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.row;
20 
21 import org.apache.hadoop.hbase.classification.InterfaceAudience;
22 import org.apache.hadoop.hbase.codec.prefixtree.PrefixTreeBlockMeta;
23 import org.apache.hadoop.hbase.util.Bytes;
24 import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
25 import org.apache.hadoop.hbase.util.vint.UFIntTool;
26 import org.apache.hadoop.hbase.util.vint.UVIntTool;
27 
28 /**
29  * Position one of these appropriately in the data block and you can call its methods to retrieve
30  * information necessary to decode the cells in the row.
31  */
32 @InterfaceAudience.Private
33 public class RowNodeReader {
34 
35   /************* fields ***********************************/
36 
37   protected byte[] block;
38   protected int offset;
39   protected int fanIndex;
40 
41   protected int numCells;
42 
43   protected int tokenOffset;
44   protected int tokenLength;
45   protected int fanOffset;
46   protected int fanOut;
47 
48   protected int familyOffsetsOffset;
49   protected int qualifierOffsetsOffset;
50   protected int timestampIndexesOffset;
51   protected int mvccVersionIndexesOffset;
52   protected int operationTypesOffset;
53   protected int valueOffsetsOffset;
54   protected int valueLengthsOffset;
55   protected int tagOffsetsOffset;
56   protected int nextNodeOffsetsOffset;
57 
58 
59   /******************* construct **************************/
60 
initOnBlock(PrefixTreeBlockMeta blockMeta, byte[] block, int offset)61   public void initOnBlock(PrefixTreeBlockMeta blockMeta, byte[] block, int offset) {
62     this.block = block;
63 
64     this.offset = offset;
65     resetFanIndex();
66 
67     this.tokenLength = UVIntTool.getInt(block, offset);
68     this.tokenOffset = offset + UVIntTool.numBytes(tokenLength);
69 
70     this.fanOut = UVIntTool.getInt(block, tokenOffset + tokenLength);
71     this.fanOffset = tokenOffset + tokenLength + UVIntTool.numBytes(fanOut);
72 
73     this.numCells = UVIntTool.getInt(block, fanOffset + fanOut);
74 
75     this.familyOffsetsOffset = fanOffset + fanOut + UVIntTool.numBytes(numCells);
76     this.qualifierOffsetsOffset = familyOffsetsOffset + numCells * blockMeta.getFamilyOffsetWidth();
77     this.tagOffsetsOffset = this.qualifierOffsetsOffset + numCells * blockMeta.getQualifierOffsetWidth();
78     // TODO : This code may not be needed now..As we always consider tags to be present
79     if(blockMeta.getTagsOffsetWidth() == 0) {
80       // Make both of them same so that we know that there are no tags
81       this.tagOffsetsOffset = this.qualifierOffsetsOffset;
82       this.timestampIndexesOffset = qualifierOffsetsOffset + numCells * blockMeta.getQualifierOffsetWidth();
83     } else {
84       this.timestampIndexesOffset = tagOffsetsOffset + numCells * blockMeta.getTagsOffsetWidth();
85     }
86     this.mvccVersionIndexesOffset = timestampIndexesOffset + numCells
87         * blockMeta.getTimestampIndexWidth();
88     this.operationTypesOffset = mvccVersionIndexesOffset + numCells
89         * blockMeta.getMvccVersionIndexWidth();
90     this.valueOffsetsOffset = operationTypesOffset + numCells * blockMeta.getKeyValueTypeWidth();
91     this.valueLengthsOffset = valueOffsetsOffset + numCells * blockMeta.getValueOffsetWidth();
92     this.nextNodeOffsetsOffset = valueLengthsOffset + numCells * blockMeta.getValueLengthWidth();
93   }
94 
95 
96   /******************** methods ****************************/
97 
isLeaf()98   public boolean isLeaf() {
99     return fanOut == 0;
100   }
101 
isNub()102   public boolean isNub() {
103     return fanOut > 0 && numCells > 0;
104   }
105 
isBranch()106   public boolean isBranch() {
107     return fanOut > 0 && numCells == 0;
108   }
109 
hasOccurrences()110   public boolean hasOccurrences() {
111     return numCells > 0;
112   }
113 
getTokenArrayOffset()114   public int getTokenArrayOffset(){
115     return tokenOffset;
116   }
117 
getTokenLength()118   public int getTokenLength() {
119     return tokenLength;
120   }
121 
getFanByte(int i)122   public byte getFanByte(int i) {
123     return block[fanOffset + i];
124   }
125 
126   /**
127    * for debugging
128    */
getFanByteReadable(int i)129   protected String getFanByteReadable(int i){
130     return Bytes.toStringBinary(block, fanOffset + i, 1);
131   }
132 
getFamilyOffset(int index, PrefixTreeBlockMeta blockMeta)133   public int getFamilyOffset(int index, PrefixTreeBlockMeta blockMeta) {
134     int fIntWidth = blockMeta.getFamilyOffsetWidth();
135     int startIndex = familyOffsetsOffset + fIntWidth * index;
136     return (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
137   }
138 
getColumnOffset(int index, PrefixTreeBlockMeta blockMeta)139   public int getColumnOffset(int index, PrefixTreeBlockMeta blockMeta) {
140     int fIntWidth = blockMeta.getQualifierOffsetWidth();
141     int startIndex = qualifierOffsetsOffset + fIntWidth * index;
142     return (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
143   }
144 
getTagOffset(int index, PrefixTreeBlockMeta blockMeta)145   public int getTagOffset(int index, PrefixTreeBlockMeta blockMeta) {
146     int fIntWidth = blockMeta.getTagsOffsetWidth();
147     int startIndex = tagOffsetsOffset + fIntWidth * index;
148     return (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
149   }
150 
getTimestampIndex(int index, PrefixTreeBlockMeta blockMeta)151   public int getTimestampIndex(int index, PrefixTreeBlockMeta blockMeta) {
152     int fIntWidth = blockMeta.getTimestampIndexWidth();
153     int startIndex = timestampIndexesOffset + fIntWidth * index;
154     return (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
155   }
156 
getMvccVersionIndex(int index, PrefixTreeBlockMeta blockMeta)157   public int getMvccVersionIndex(int index, PrefixTreeBlockMeta blockMeta) {
158     int fIntWidth = blockMeta.getMvccVersionIndexWidth();
159     int startIndex = mvccVersionIndexesOffset + fIntWidth * index;
160     return (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
161   }
162 
getType(int index, PrefixTreeBlockMeta blockMeta)163   public int getType(int index, PrefixTreeBlockMeta blockMeta) {
164     if (blockMeta.isAllSameType()) {
165       return blockMeta.getAllTypes();
166     }
167     return block[operationTypesOffset + index];
168   }
169 
getValueOffset(int index, PrefixTreeBlockMeta blockMeta)170   public int getValueOffset(int index, PrefixTreeBlockMeta blockMeta) {
171     int fIntWidth = blockMeta.getValueOffsetWidth();
172     int startIndex = valueOffsetsOffset + fIntWidth * index;
173     int offset = (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
174     return offset;
175   }
176 
getValueLength(int index, PrefixTreeBlockMeta blockMeta)177   public int getValueLength(int index, PrefixTreeBlockMeta blockMeta) {
178     int fIntWidth = blockMeta.getValueLengthWidth();
179     int startIndex = valueLengthsOffset + fIntWidth * index;
180     int length = (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
181     return length;
182   }
183 
getNextNodeOffset(int index, PrefixTreeBlockMeta blockMeta)184   public int getNextNodeOffset(int index, PrefixTreeBlockMeta blockMeta) {
185     int fIntWidth = blockMeta.getNextNodeOffsetWidth();
186     int startIndex = nextNodeOffsetsOffset + fIntWidth * index;
187     return (int) UFIntTool.fromBytes(block, startIndex, fIntWidth);
188   }
189 
getBranchNubLeafIndicator()190   public String getBranchNubLeafIndicator() {
191     if (isNub()) {
192       return "N";
193     }
194     return isBranch() ? "B" : "L";
195   }
196 
hasChildren()197   public boolean hasChildren() {
198     return fanOut > 0;
199   }
200 
getLastFanIndex()201   public int getLastFanIndex() {
202     return fanOut - 1;
203   }
204 
getLastCellIndex()205   public int getLastCellIndex() {
206     return numCells - 1;
207   }
208 
getNumCells()209   public int getNumCells() {
210     return numCells;
211   }
212 
getFanOut()213   public int getFanOut() {
214     return fanOut;
215   }
216 
getToken()217   public byte[] getToken() {
218     // TODO pass in reusable ByteRange
219     return new SimpleMutableByteRange(block, tokenOffset, tokenLength).deepCopyToNewArray();
220   }
221 
getOffset()222   public int getOffset() {
223     return offset;
224   }
225 
whichFanNode(byte searchForByte)226   public int whichFanNode(byte searchForByte) {
227     if( ! hasFan()){
228       throw new IllegalStateException("This row node has no fan, so can't search it");
229     }
230     int fanIndexInBlock = Bytes.unsignedBinarySearch(block, fanOffset, fanOffset + fanOut,
231       searchForByte);
232     if (fanIndexInBlock >= 0) {// found it, but need to adjust for position of fan in overall block
233       return fanIndexInBlock - fanOffset;
234     }
235     return fanIndexInBlock + fanOffset;// didn't find it, so compensate in reverse
236   }
237 
resetFanIndex()238   public void resetFanIndex() {
239     fanIndex = -1;// just the way the logic currently works
240   }
241 
getFanIndex()242   public int getFanIndex() {
243     return fanIndex;
244   }
245 
setFanIndex(int fanIndex)246   public void setFanIndex(int fanIndex) {
247     this.fanIndex = fanIndex;
248   }
249 
hasFan()250   public boolean hasFan(){
251     return fanOut > 0;
252   }
253 
hasPreviousFanNodes()254   public boolean hasPreviousFanNodes() {
255     return fanOut > 0 && fanIndex > 0;
256   }
257 
hasMoreFanNodes()258   public boolean hasMoreFanNodes() {
259     return fanIndex < getLastFanIndex();
260   }
261 
isOnLastFanNode()262   public boolean isOnLastFanNode() {
263     return !hasMoreFanNodes();
264   }
265 
266 
267   /*************** standard methods **************************/
268 
269   @Override
toString()270   public String toString() {
271     StringBuilder sb = new StringBuilder();
272     sb.append("fan:" + Bytes.toStringBinary(block, fanOffset, fanOut));
273     sb.append(",token:" + Bytes.toStringBinary(block, tokenOffset, tokenLength));
274     sb.append(",numCells:" + numCells);
275     sb.append(",fanIndex:"+fanIndex);
276     if(fanIndex>=0){
277       sb.append("("+getFanByteReadable(fanIndex)+")");
278     }
279     return sb.toString();
280   }
281 }
282