1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.hbase.codec.prefixtree.decode; 20 21 import org.apache.hadoop.hbase.classification.InterfaceAudience; 22 import org.apache.hadoop.hbase.Cell; 23 import org.apache.hadoop.hbase.CellComparator; 24 import org.apache.hadoop.hbase.CellScanner; 25 import org.apache.hadoop.hbase.codec.prefixtree.PrefixTreeBlockMeta; 26 import org.apache.hadoop.hbase.codec.prefixtree.decode.column.ColumnReader; 27 import org.apache.hadoop.hbase.codec.prefixtree.decode.row.RowNodeReader; 28 import org.apache.hadoop.hbase.codec.prefixtree.decode.timestamp.MvccVersionDecoder; 29 import org.apache.hadoop.hbase.codec.prefixtree.decode.timestamp.TimestampDecoder; 30 import org.apache.hadoop.hbase.codec.prefixtree.encode.other.ColumnNodeType; 31 32 /** 33 * Extends PtCell and manipulates its protected fields. Could alternatively contain a PtCell and 34 * call get/set methods. 35 * 36 * This is an "Array" scanner to distinguish from a future "ByteBuffer" scanner. This 37 * implementation requires that the bytes be in a normal java byte[] for performance. The 38 * alternative ByteBuffer implementation would allow for accessing data in an off-heap ByteBuffer 39 * without copying the whole buffer on-heap. 40 */ 41 @InterfaceAudience.Private 42 public class PrefixTreeArrayScanner extends PrefixTreeCell implements CellScanner { 43 44 /***************** fields ********************************/ 45 46 protected PrefixTreeBlockMeta blockMeta; 47 48 protected boolean beforeFirst; 49 protected boolean afterLast; 50 51 protected RowNodeReader[] rowNodes; 52 protected int rowNodeStackIndex; 53 54 protected RowNodeReader currentRowNode; 55 protected ColumnReader familyReader; 56 protected ColumnReader qualifierReader; 57 protected ColumnReader tagsReader; 58 protected TimestampDecoder timestampDecoder; 59 protected MvccVersionDecoder mvccVersionDecoder; 60 61 protected boolean nubCellsRemain; 62 protected int currentCellIndex; 63 64 65 /*********************** construct ******************************/ 66 67 // pass in blockMeta so we can initialize buffers big enough for all cells in the block PrefixTreeArrayScanner(PrefixTreeBlockMeta blockMeta, int rowTreeDepth, int rowBufferLength, int qualifierBufferLength, int tagsBufferLength)68 public PrefixTreeArrayScanner(PrefixTreeBlockMeta blockMeta, int rowTreeDepth, 69 int rowBufferLength, int qualifierBufferLength, int tagsBufferLength) { 70 this.rowNodes = new RowNodeReader[rowTreeDepth]; 71 for (int i = 0; i < rowNodes.length; ++i) { 72 rowNodes[i] = new RowNodeReader(); 73 } 74 this.rowBuffer = new byte[rowBufferLength]; 75 this.familyBuffer = new byte[PrefixTreeBlockMeta.MAX_FAMILY_LENGTH]; 76 this.familyReader = new ColumnReader(familyBuffer, ColumnNodeType.FAMILY); 77 this.qualifierBuffer = new byte[qualifierBufferLength]; 78 this.tagsBuffer = new byte[tagsBufferLength]; 79 this.qualifierReader = new ColumnReader(qualifierBuffer, ColumnNodeType.QUALIFIER); 80 this.tagsReader = new ColumnReader(tagsBuffer, ColumnNodeType.TAGS); 81 this.timestampDecoder = new TimestampDecoder(); 82 this.mvccVersionDecoder = new MvccVersionDecoder(); 83 } 84 85 86 /**************** init helpers ***************************************/ 87 88 /** 89 * Call when first accessing a block. 90 * @return entirely new scanner if false 91 */ areBuffersBigEnough()92 public boolean areBuffersBigEnough() { 93 if (rowNodes.length < blockMeta.getRowTreeDepth()) { 94 return false; 95 } 96 if (rowBuffer.length < blockMeta.getMaxRowLength()) { 97 return false; 98 } 99 if (qualifierBuffer.length < blockMeta.getMaxQualifierLength()) { 100 return false; 101 } 102 if(tagsBuffer.length < blockMeta.getMaxTagsLength()) { 103 return false; 104 } 105 return true; 106 } 107 initOnBlock(PrefixTreeBlockMeta blockMeta, byte[] block, boolean includeMvccVersion)108 public void initOnBlock(PrefixTreeBlockMeta blockMeta, byte[] block, 109 boolean includeMvccVersion) { 110 this.block = block; 111 this.blockMeta = blockMeta; 112 this.familyOffset = familyBuffer.length; 113 this.familyReader.initOnBlock(blockMeta, block); 114 this.qualifierOffset = qualifierBuffer.length; 115 this.qualifierReader.initOnBlock(blockMeta, block); 116 this.tagsOffset = tagsBuffer.length; 117 this.tagsReader.initOnBlock(blockMeta, block); 118 this.timestampDecoder.initOnBlock(blockMeta, block); 119 this.mvccVersionDecoder.initOnBlock(blockMeta, block); 120 this.includeMvccVersion = includeMvccVersion; 121 resetToBeforeFirstEntry(); 122 } 123 124 // Does this have to be in the CellScanner Interface? TODO resetToBeforeFirstEntry()125 public void resetToBeforeFirstEntry() { 126 beforeFirst = true; 127 afterLast = false; 128 rowNodeStackIndex = -1; 129 currentRowNode = null; 130 rowLength = 0; 131 familyOffset = familyBuffer.length; 132 familyLength = 0; 133 qualifierOffset = blockMeta.getMaxQualifierLength(); 134 qualifierLength = 0; 135 nubCellsRemain = false; 136 currentCellIndex = -1; 137 timestamp = -1L; 138 type = DEFAULT_TYPE; 139 absoluteValueOffset = 0;//use 0 vs -1 so the cell is valid when value hasn't been initialized 140 valueLength = 0;// had it at -1, but that causes null Cell to add up to the wrong length 141 tagsOffset = blockMeta.getMaxTagsLength(); 142 tagsLength = 0; 143 } 144 145 /** 146 * Call this before putting the scanner back into a pool so it doesn't hold the last used block 147 * in memory. 148 */ releaseBlockReference()149 public void releaseBlockReference(){ 150 block = null; 151 } 152 153 154 /********************** CellScanner **********************/ 155 156 @Override current()157 public Cell current() { 158 if(isOutOfBounds()){ 159 return null; 160 } 161 return (Cell)this; 162 } 163 164 /******************* Object methods ************************/ 165 166 @Override equals(Object obj)167 public boolean equals(Object obj) { 168 //trivial override to confirm intent (findbugs) 169 return super.equals(obj); 170 } 171 172 @Override hashCode()173 public int hashCode() { 174 return super.hashCode(); 175 } 176 177 /** 178 * Override PrefixTreeCell.toString() with a check to see if the current cell is valid. 179 */ 180 @Override toString()181 public String toString() { 182 Cell currentCell = current(); 183 if(currentCell==null){ 184 return "null"; 185 } 186 return ((PrefixTreeCell)currentCell).getKeyValueString(); 187 } 188 189 190 /******************* advance ***************************/ 191 positionAtFirstCell()192 public boolean positionAtFirstCell() { 193 reInitFirstNode(); 194 return advance(); 195 } 196 197 @Override advance()198 public boolean advance() { 199 if (afterLast) { 200 return false; 201 } 202 if (!hasOccurrences()) { 203 resetToBeforeFirstEntry(); 204 } 205 if (beforeFirst || isLastCellInRow()) { 206 nextRow(); 207 if (afterLast) { 208 return false; 209 } 210 } else { 211 ++currentCellIndex; 212 } 213 214 populateNonRowFields(currentCellIndex); 215 return true; 216 } 217 218 nextRow()219 public boolean nextRow() { 220 nextRowInternal(); 221 if (afterLast) { 222 return false; 223 } 224 populateNonRowFields(currentCellIndex); 225 return true; 226 } 227 228 229 /** 230 * This method is safe to call when the scanner is not on a fully valid row node, as in the case 231 * of a row token miss in the Searcher 232 * @return true if we are positioned on a valid row, false if past end of block 233 */ nextRowInternal()234 protected boolean nextRowInternal() { 235 if (afterLast) { 236 return false; 237 } 238 if (beforeFirst) { 239 initFirstNode(); 240 if (currentRowNode.hasOccurrences()) { 241 if (currentRowNode.isNub()) { 242 nubCellsRemain = true; 243 } 244 currentCellIndex = 0; 245 return true; 246 } 247 } 248 if (currentRowNode.isLeaf()) { 249 discardCurrentRowNode(true); 250 } 251 while (!afterLast) { 252 if (nubCellsRemain) { 253 nubCellsRemain = false; 254 } 255 if (currentRowNode.hasMoreFanNodes()) { 256 followNextFan(); 257 if (currentRowNode.hasOccurrences()) { 258 // found some values 259 currentCellIndex = 0; 260 return true; 261 } 262 } else { 263 discardCurrentRowNode(true); 264 } 265 } 266 return false;// went past the end 267 } 268 269 270 /**************** secondary traversal methods ******************************/ 271 reInitFirstNode()272 protected void reInitFirstNode() { 273 resetToBeforeFirstEntry(); 274 initFirstNode(); 275 } 276 initFirstNode()277 protected void initFirstNode() { 278 int offsetIntoUnderlyingStructure = blockMeta.getAbsoluteRowOffset(); 279 rowNodeStackIndex = 0; 280 currentRowNode = rowNodes[0]; 281 currentRowNode.initOnBlock(blockMeta, block, offsetIntoUnderlyingStructure); 282 appendCurrentTokenToRowBuffer(); 283 beforeFirst = false; 284 } 285 followFirstFan()286 protected void followFirstFan() { 287 followFan(0); 288 } 289 followPreviousFan()290 protected void followPreviousFan() { 291 int nextFanPosition = currentRowNode.getFanIndex() - 1; 292 followFan(nextFanPosition); 293 } 294 followCurrentFan()295 protected void followCurrentFan() { 296 int currentFanPosition = currentRowNode.getFanIndex(); 297 followFan(currentFanPosition); 298 } 299 followNextFan()300 protected void followNextFan() { 301 int nextFanPosition = currentRowNode.getFanIndex() + 1; 302 followFan(nextFanPosition); 303 } 304 followLastFan()305 protected void followLastFan() { 306 followFan(currentRowNode.getLastFanIndex()); 307 } 308 followFan(int fanIndex)309 protected void followFan(int fanIndex) { 310 currentRowNode.setFanIndex(fanIndex); 311 appendToRowBuffer(currentRowNode.getFanByte(fanIndex)); 312 313 int nextOffsetIntoUnderlyingStructure = currentRowNode.getOffset() 314 + currentRowNode.getNextNodeOffset(fanIndex, blockMeta); 315 ++rowNodeStackIndex; 316 317 currentRowNode = rowNodes[rowNodeStackIndex]; 318 currentRowNode.initOnBlock(blockMeta, block, nextOffsetIntoUnderlyingStructure); 319 320 //TODO getToken is spewing garbage 321 appendCurrentTokenToRowBuffer(); 322 if (currentRowNode.isNub()) { 323 nubCellsRemain = true; 324 } 325 currentCellIndex = 0; 326 } 327 328 /** 329 * @param forwards which marker to set if we overflow 330 */ discardCurrentRowNode(boolean forwards)331 protected void discardCurrentRowNode(boolean forwards) { 332 RowNodeReader rowNodeBeingPopped = currentRowNode; 333 --rowNodeStackIndex;// pop it off the stack 334 if (rowNodeStackIndex < 0) { 335 currentRowNode = null; 336 if (forwards) { 337 markAfterLast(); 338 } else { 339 markBeforeFirst(); 340 } 341 return; 342 } 343 popFromRowBuffer(rowNodeBeingPopped); 344 currentRowNode = rowNodes[rowNodeStackIndex]; 345 } 346 markBeforeFirst()347 protected void markBeforeFirst() { 348 beforeFirst = true; 349 afterLast = false; 350 currentRowNode = null; 351 } 352 markAfterLast()353 protected void markAfterLast() { 354 beforeFirst = false; 355 afterLast = true; 356 currentRowNode = null; 357 } 358 359 360 /***************** helper methods **************************/ 361 appendCurrentTokenToRowBuffer()362 protected void appendCurrentTokenToRowBuffer() { 363 System.arraycopy(block, currentRowNode.getTokenArrayOffset(), rowBuffer, rowLength, 364 currentRowNode.getTokenLength()); 365 rowLength += currentRowNode.getTokenLength(); 366 } 367 appendToRowBuffer(byte b)368 protected void appendToRowBuffer(byte b) { 369 rowBuffer[rowLength] = b; 370 ++rowLength; 371 } 372 popFromRowBuffer(RowNodeReader rowNodeBeingPopped)373 protected void popFromRowBuffer(RowNodeReader rowNodeBeingPopped) { 374 rowLength -= rowNodeBeingPopped.getTokenLength(); 375 --rowLength; // pop the parent's fan byte 376 } 377 hasOccurrences()378 protected boolean hasOccurrences() { 379 return currentRowNode != null && currentRowNode.hasOccurrences(); 380 } 381 isBranch()382 protected boolean isBranch() { 383 return currentRowNode != null && !currentRowNode.hasOccurrences() 384 && currentRowNode.hasChildren(); 385 } 386 isNub()387 protected boolean isNub() { 388 return currentRowNode != null && currentRowNode.hasOccurrences() 389 && currentRowNode.hasChildren(); 390 } 391 isLeaf()392 protected boolean isLeaf() { 393 return currentRowNode != null && currentRowNode.hasOccurrences() 394 && !currentRowNode.hasChildren(); 395 } 396 397 //TODO expose this in a PrefixTreeScanner interface isBeforeFirst()398 public boolean isBeforeFirst(){ 399 return beforeFirst; 400 } 401 isAfterLast()402 public boolean isAfterLast(){ 403 return afterLast; 404 } 405 isOutOfBounds()406 protected boolean isOutOfBounds(){ 407 return beforeFirst || afterLast; 408 } 409 isFirstCellInRow()410 protected boolean isFirstCellInRow() { 411 return currentCellIndex == 0; 412 } 413 isLastCellInRow()414 protected boolean isLastCellInRow() { 415 return currentCellIndex == currentRowNode.getLastCellIndex(); 416 } 417 418 419 /********************* fill in family/qualifier/ts/type/value ************/ 420 populateNonRowFieldsAndCompareTo(int cellNum, Cell key)421 protected int populateNonRowFieldsAndCompareTo(int cellNum, Cell key) { 422 populateNonRowFields(cellNum); 423 return CellComparator.compare(this, key, true); 424 } 425 populateFirstNonRowFields()426 protected void populateFirstNonRowFields() { 427 populateNonRowFields(0); 428 } 429 populatePreviousNonRowFields()430 protected void populatePreviousNonRowFields() { 431 populateNonRowFields(currentCellIndex - 1); 432 } 433 populateLastNonRowFields()434 protected void populateLastNonRowFields() { 435 populateNonRowFields(currentRowNode.getLastCellIndex()); 436 } 437 populateNonRowFields(int cellIndex)438 protected void populateNonRowFields(int cellIndex) { 439 currentCellIndex = cellIndex; 440 populateFamily(); 441 populateQualifier(); 442 // Read tags only if there are tags in the meta 443 if(blockMeta.getNumTagsBytes() != 0) { 444 populateTag(); 445 } 446 populateTimestamp(); 447 populateMvccVersion(); 448 populateType(); 449 populateValueOffsets(); 450 } 451 populateFamily()452 protected void populateFamily() { 453 int familyTreeIndex = currentRowNode.getFamilyOffset(currentCellIndex, blockMeta); 454 familyOffset = familyReader.populateBuffer(familyTreeIndex).getColumnOffset(); 455 familyLength = familyReader.getColumnLength(); 456 } 457 populateQualifier()458 protected void populateQualifier() { 459 int qualifierTreeIndex = currentRowNode.getColumnOffset(currentCellIndex, blockMeta); 460 qualifierOffset = qualifierReader.populateBuffer(qualifierTreeIndex).getColumnOffset(); 461 qualifierLength = qualifierReader.getColumnLength(); 462 } 463 populateTag()464 protected void populateTag() { 465 int tagTreeIndex = currentRowNode.getTagOffset(currentCellIndex, blockMeta); 466 tagsOffset = tagsReader.populateBuffer(tagTreeIndex).getColumnOffset(); 467 tagsLength = tagsReader.getColumnLength(); 468 } 469 populateTimestamp()470 protected void populateTimestamp() { 471 if (blockMeta.isAllSameTimestamp()) { 472 timestamp = blockMeta.getMinTimestamp(); 473 } else { 474 int timestampIndex = currentRowNode.getTimestampIndex(currentCellIndex, blockMeta); 475 timestamp = timestampDecoder.getLong(timestampIndex); 476 } 477 } 478 populateMvccVersion()479 protected void populateMvccVersion() { 480 if (blockMeta.isAllSameMvccVersion()) { 481 mvccVersion = blockMeta.getMinMvccVersion(); 482 } else { 483 int mvccVersionIndex = currentRowNode.getMvccVersionIndex(currentCellIndex, 484 blockMeta); 485 mvccVersion = mvccVersionDecoder.getMvccVersion(mvccVersionIndex); 486 } 487 } 488 populateType()489 protected void populateType() { 490 int typeInt; 491 if (blockMeta.isAllSameType()) { 492 typeInt = blockMeta.getAllTypes(); 493 } else { 494 typeInt = currentRowNode.getType(currentCellIndex, blockMeta); 495 } 496 type = PrefixTreeCell.TYPES[typeInt]; 497 } 498 populateValueOffsets()499 protected void populateValueOffsets() { 500 int offsetIntoValueSection = currentRowNode.getValueOffset(currentCellIndex, blockMeta); 501 absoluteValueOffset = blockMeta.getAbsoluteValueOffset() + offsetIntoValueSection; 502 valueLength = currentRowNode.getValueLength(currentCellIndex, blockMeta); 503 } 504 505 /**************** getters ***************************/ 506 getTreeBytes()507 public byte[] getTreeBytes() { 508 return block; 509 } 510 getBlockMeta()511 public PrefixTreeBlockMeta getBlockMeta() { 512 return blockMeta; 513 } 514 getMaxRowTreeStackNodes()515 public int getMaxRowTreeStackNodes() { 516 return rowNodes.length; 517 } 518 getRowBufferLength()519 public int getRowBufferLength() { 520 return rowBuffer.length; 521 } 522 getQualifierBufferLength()523 public int getQualifierBufferLength() { 524 return qualifierBuffer.length; 525 } 526 getTagBufferLength()527 public int getTagBufferLength() { 528 return tagsBuffer.length; 529 } 530 } 531