View Javadoc

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
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     */
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 
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
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    */
149   public void releaseBlockReference(){
150     block = null;
151   }
152 
153 
154   /********************** CellScanner **********************/
155 
156   @Override
157   public Cell current() {
158     if(isOutOfBounds()){
159       return null;
160     }
161     return (Cell)this;
162   }
163 
164   /******************* Object methods ************************/
165 
166   @Override
167   public boolean equals(Object obj) {
168     //trivial override to confirm intent (findbugs)
169     return super.equals(obj);
170   }
171 
172   @Override
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
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 
192   public boolean positionAtFirstCell() {
193     reInitFirstNode();
194     return advance();
195   }
196 
197   @Override
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 
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    */
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 
272   protected void reInitFirstNode() {
273     resetToBeforeFirstEntry();
274     initFirstNode();
275   }
276 
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 
286   protected void followFirstFan() {
287     followFan(0);
288   }
289 
290   protected void followPreviousFan() {
291     int nextFanPosition = currentRowNode.getFanIndex() - 1;
292     followFan(nextFanPosition);
293   }
294 
295   protected void followCurrentFan() {
296     int currentFanPosition = currentRowNode.getFanIndex();
297     followFan(currentFanPosition);
298   }
299 
300   protected void followNextFan() {
301     int nextFanPosition = currentRowNode.getFanIndex() + 1;
302     followFan(nextFanPosition);
303   }
304 
305   protected void followLastFan() {
306     followFan(currentRowNode.getLastFanIndex());
307   }
308 
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    */
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 
347   protected void markBeforeFirst() {
348     beforeFirst = true;
349     afterLast = false;
350     currentRowNode = null;
351   }
352 
353   protected void markAfterLast() {
354     beforeFirst = false;
355     afterLast = true;
356     currentRowNode = null;
357   }
358 
359 
360   /***************** helper methods **************************/
361 
362   protected void appendCurrentTokenToRowBuffer() {
363     System.arraycopy(block, currentRowNode.getTokenArrayOffset(), rowBuffer, rowLength,
364       currentRowNode.getTokenLength());
365     rowLength += currentRowNode.getTokenLength();
366   }
367 
368   protected void appendToRowBuffer(byte b) {
369     rowBuffer[rowLength] = b;
370     ++rowLength;
371   }
372 
373   protected void popFromRowBuffer(RowNodeReader rowNodeBeingPopped) {
374     rowLength -= rowNodeBeingPopped.getTokenLength();
375     --rowLength; // pop the parent's fan byte
376   }
377 
378   protected boolean hasOccurrences() {
379     return currentRowNode != null && currentRowNode.hasOccurrences();
380   }
381 
382   protected boolean isBranch() {
383     return currentRowNode != null && !currentRowNode.hasOccurrences()
384         && currentRowNode.hasChildren();
385   }
386 
387   protected boolean isNub() {
388     return currentRowNode != null && currentRowNode.hasOccurrences()
389         && currentRowNode.hasChildren();
390   }
391 
392   protected boolean isLeaf() {
393     return currentRowNode != null && currentRowNode.hasOccurrences()
394         && !currentRowNode.hasChildren();
395   }
396 
397   //TODO expose this in a PrefixTreeScanner interface
398   public boolean isBeforeFirst(){
399     return beforeFirst;
400   }
401 
402   public boolean isAfterLast(){
403     return afterLast;
404   }
405 
406   protected boolean isOutOfBounds(){
407     return beforeFirst || afterLast;
408   }
409 
410   protected boolean isFirstCellInRow() {
411     return currentCellIndex == 0;
412   }
413 
414   protected boolean isLastCellInRow() {
415     return currentCellIndex == currentRowNode.getLastCellIndex();
416   }
417 
418 
419   /********************* fill in family/qualifier/ts/type/value ************/
420 
421   protected int populateNonRowFieldsAndCompareTo(int cellNum, Cell key) {
422     populateNonRowFields(cellNum);
423     return CellComparator.compare(this, key, true);
424   }
425 
426   protected void populateFirstNonRowFields() {
427     populateNonRowFields(0);
428   }
429 
430   protected void populatePreviousNonRowFields() {
431     populateNonRowFields(currentCellIndex - 1);
432   }
433 
434   protected void populateLastNonRowFields() {
435     populateNonRowFields(currentRowNode.getLastCellIndex());
436   }
437 
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 
452   protected void populateFamily() {
453     int familyTreeIndex = currentRowNode.getFamilyOffset(currentCellIndex, blockMeta);
454     familyOffset = familyReader.populateBuffer(familyTreeIndex).getColumnOffset();
455     familyLength = familyReader.getColumnLength();
456   }
457 
458   protected void populateQualifier() {
459     int qualifierTreeIndex = currentRowNode.getColumnOffset(currentCellIndex, blockMeta);
460     qualifierOffset = qualifierReader.populateBuffer(qualifierTreeIndex).getColumnOffset();
461     qualifierLength = qualifierReader.getColumnLength();
462   }
463 
464   protected void populateTag() {
465     int tagTreeIndex = currentRowNode.getTagOffset(currentCellIndex, blockMeta);
466     tagsOffset = tagsReader.populateBuffer(tagTreeIndex).getColumnOffset();
467     tagsLength = tagsReader.getColumnLength();
468   }
469 
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 
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 
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 
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 
507   public byte[] getTreeBytes() {
508     return block;
509   }
510 
511   public PrefixTreeBlockMeta getBlockMeta() {
512     return blockMeta;
513   }
514 
515   public int getMaxRowTreeStackNodes() {
516     return rowNodes.length;
517   }
518 
519   public int getRowBufferLength() {
520     return rowBuffer.length;
521   }
522 
523   public int getQualifierBufferLength() {
524     return qualifierBuffer.length;
525   }
526 
527   public int getTagBufferLength() {
528     return tagsBuffer.length;
529   }
530 }