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