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;
20  
21  import java.io.IOException;
22  import java.nio.ByteBuffer;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map.Entry;
26  import java.util.NavigableMap;
27  
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.classification.InterfaceStability;
30  import org.apache.hadoop.hbase.KeyValue.Type;
31  import org.apache.hadoop.hbase.util.ByteRange;
32  import org.apache.hadoop.hbase.util.Bytes;
33  
34  /**
35   * Utility methods helpful slinging {@link Cell} instances.
36   */
37  @InterfaceAudience.Public
38  @InterfaceStability.Evolving
39  public final class CellUtil {
40  
41    /******************* ByteRange *******************************/
42  
43    public static ByteRange fillRowRange(Cell cell, ByteRange range) {
44      return range.set(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
45    }
46  
47    public static ByteRange fillFamilyRange(Cell cell, ByteRange range) {
48      return range.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
49    }
50  
51    public static ByteRange fillQualifierRange(Cell cell, ByteRange range) {
52      return range.set(cell.getQualifierArray(), cell.getQualifierOffset(),
53        cell.getQualifierLength());
54    }
55  
56    @InterfaceStability.Unstable
57    public static ByteRange fillTagRange(Cell cell, ByteRange range) {
58      return range.set(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLengthUnsigned());
59    }
60  
61    /***************** get individual arrays for tests ************/
62  
63    public static byte[] cloneRow(Cell cell){
64      byte[] output = new byte[cell.getRowLength()];
65      copyRowTo(cell, output, 0);
66      return output;
67    }
68  
69    public static byte[] cloneFamily(Cell cell){
70      byte[] output = new byte[cell.getFamilyLength()];
71      copyFamilyTo(cell, output, 0);
72      return output;
73    }
74  
75    public static byte[] cloneQualifier(Cell cell){
76      byte[] output = new byte[cell.getQualifierLength()];
77      copyQualifierTo(cell, output, 0);
78      return output;
79    }
80  
81    public static byte[] cloneValue(Cell cell){
82      byte[] output = new byte[cell.getValueLength()];
83      copyValueTo(cell, output, 0);
84      return output;
85    }
86  
87    /**
88     * Returns tag value in a new byte array. If server-side, use
89     * {@link Tag#getBuffer()} with appropriate {@link Tag#getTagOffset()} and
90     * {@link Tag#getTagLength()} instead to save on allocations.
91     * @param cell
92     * @return tag value in a new byte array.
93     */
94    @InterfaceStability.Unstable
95    public static byte[] getTagArray(Cell cell){
96      byte[] output = new byte[cell.getTagsLengthUnsigned()];
97      copyTagTo(cell, output, 0);
98      return output;
99    }
100 
101 
102   /******************** copyTo **********************************/
103 
104   public static int copyRowTo(Cell cell, byte[] destination, int destinationOffset) {
105     System.arraycopy(cell.getRowArray(), cell.getRowOffset(), destination, destinationOffset,
106       cell.getRowLength());
107     return destinationOffset + cell.getRowLength();
108   }
109 
110   public static int copyFamilyTo(Cell cell, byte[] destination, int destinationOffset) {
111     System.arraycopy(cell.getFamilyArray(), cell.getFamilyOffset(), destination, destinationOffset,
112       cell.getFamilyLength());
113     return destinationOffset + cell.getFamilyLength();
114   }
115 
116   public static int copyQualifierTo(Cell cell, byte[] destination, int destinationOffset) {
117     System.arraycopy(cell.getQualifierArray(), cell.getQualifierOffset(), destination,
118       destinationOffset, cell.getQualifierLength());
119     return destinationOffset + cell.getQualifierLength();
120   }
121 
122   public static int copyValueTo(Cell cell, byte[] destination, int destinationOffset) {
123     System.arraycopy(cell.getValueArray(), cell.getValueOffset(), destination, destinationOffset,
124         cell.getValueLength());
125     return destinationOffset + cell.getValueLength();
126   }
127 
128   /**
129    * Copies the tags info into the tag portion of the cell
130    * @param cell
131    * @param destination
132    * @param destinationOffset
133    * @return position after tags
134    */
135   @InterfaceStability.Unstable
136   public static int copyTagTo(Cell cell, byte[] destination, int destinationOffset) {
137     System.arraycopy(cell.getTagsArray(), cell.getTagsOffset(), destination, destinationOffset,
138         cell.getTagsLengthUnsigned());
139     return destinationOffset + cell.getTagsLengthUnsigned();
140   }
141 
142   /********************* misc *************************************/
143 
144   public static byte getRowByte(Cell cell, int index) {
145     return cell.getRowArray()[cell.getRowOffset() + index];
146   }
147 
148   public static ByteBuffer getValueBufferShallowCopy(Cell cell) {
149     ByteBuffer buffer = ByteBuffer.wrap(cell.getValueArray(), cell.getValueOffset(),
150       cell.getValueLength());
151     return buffer;
152   }
153 
154   public static ByteBuffer getQualifierBufferShallowCopy(Cell cell) {
155     ByteBuffer buffer = ByteBuffer.wrap(cell.getQualifierArray(), cell.getQualifierOffset(),
156         cell.getQualifierLength());
157     return buffer;
158   }
159 
160   public static Cell createCell(final byte [] row, final byte [] family, final byte [] qualifier,
161       final long timestamp, final byte type, final byte [] value) {
162     // I need a Cell Factory here.  Using KeyValue for now. TODO.
163     // TODO: Make a new Cell implementation that just carries these
164     // byte arrays.
165     return new KeyValue(row, family, qualifier, timestamp,
166       KeyValue.Type.codeToType(type), value);
167   }
168 
169   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
170       final long timestamp, final byte type, final byte[] value, final long memstoreTS) {
171     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp,
172         KeyValue.Type.codeToType(type), value);
173     keyValue.setMvccVersion(memstoreTS);
174     return keyValue;
175   }
176 
177   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
178       final long timestamp, final byte type, final byte[] value, byte[] tags, final long memstoreTS) {
179     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp,
180         KeyValue.Type.codeToType(type), value, tags);
181     keyValue.setMvccVersion(memstoreTS);
182     return keyValue;
183   }
184 
185   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
186       final long timestamp, Type type, final byte[] value, byte[] tags) {
187     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp, type, value, tags);
188     return keyValue;
189   }
190 
191   /**
192    * @param cellScannerables
193    * @return CellScanner interface over <code>cellIterables</code>
194    */
195   public static CellScanner createCellScanner(final List<? extends CellScannable> cellScannerables) {
196     return new CellScanner() {
197       private final Iterator<? extends CellScannable> iterator = cellScannerables.iterator();
198       private CellScanner cellScanner = null;
199 
200       @Override
201       public Cell current() {
202         return this.cellScanner != null? this.cellScanner.current(): null;
203       }
204 
205       @Override
206       public boolean advance() throws IOException {
207         while (true) {
208           if (this.cellScanner == null) {
209             if (!this.iterator.hasNext()) return false;
210             this.cellScanner = this.iterator.next().cellScanner();
211           }
212           if (this.cellScanner.advance()) return true;
213           this.cellScanner = null;
214         }
215       }
216     };
217   }
218 
219   /**
220    * @param cellIterable
221    * @return CellScanner interface over <code>cellIterable</code>
222    */
223   public static CellScanner createCellScanner(final Iterable<Cell> cellIterable) {
224     if (cellIterable == null) return null;
225     return createCellScanner(cellIterable.iterator());
226   }
227 
228   /**
229    * @param cells
230    * @return CellScanner interface over <code>cellIterable</code> or null if <code>cells</code> is
231    * null
232    */
233   public static CellScanner createCellScanner(final Iterator<Cell> cells) {
234     if (cells == null) return null;
235     return new CellScanner() {
236       private final Iterator<Cell> iterator = cells;
237       private Cell current = null;
238 
239       @Override
240       public Cell current() {
241         return this.current;
242       }
243 
244       @Override
245       public boolean advance() {
246         boolean hasNext = this.iterator.hasNext();
247         this.current = hasNext? this.iterator.next(): null;
248         return hasNext;
249       }
250     };
251   }
252 
253   /**
254    * @param cellArray
255    * @return CellScanner interface over <code>cellArray</code>
256    */
257   public static CellScanner createCellScanner(final Cell[] cellArray) {
258     return new CellScanner() {
259       private final Cell [] cells = cellArray;
260       private int index = -1;
261 
262       @Override
263       public Cell current() {
264         if (cells == null) return null;
265         return (index < 0)? null: this.cells[index];
266       }
267 
268       @Override
269       public boolean advance() {
270         if (cells == null) return false;
271         return ++index < this.cells.length;
272       }
273     };
274   }
275 
276   /**
277    * Flatten the map of cells out under the CellScanner
278    * @param map Map of Cell Lists; for example, the map of families to Cells that is used
279    * inside Put, etc., keeping Cells organized by family.
280    * @return CellScanner interface over <code>cellIterable</code>
281    */
282   public static CellScanner createCellScanner(final NavigableMap<byte [], List<Cell>> map) {
283     return new CellScanner() {
284       private final Iterator<Entry<byte[], List<Cell>>> entries = map.entrySet().iterator();
285       private Iterator<Cell> currentIterator = null;
286       private Cell currentCell;
287 
288       @Override
289       public Cell current() {
290         return this.currentCell;
291       }
292 
293       @Override
294       public boolean advance() {
295         while(true) {
296           if (this.currentIterator == null) {
297             if (!this.entries.hasNext()) return false;
298             this.currentIterator = this.entries.next().getValue().iterator();
299           }
300           if (this.currentIterator.hasNext()) {
301             this.currentCell = this.currentIterator.next();
302             return true;
303           }
304           this.currentCell = null;
305           this.currentIterator = null;
306         }
307       }
308     };
309   }
310 
311   /**
312    * @param left
313    * @param right
314    * @return True if the rows in <code>left</code> and <code>right</code> Cells match
315    */
316   public static boolean matchingRow(final Cell left, final Cell right) {
317     return Bytes.equals(left.getRowArray(),  left.getRowOffset(), left.getRowLength(),
318       right.getRowArray(), right.getRowOffset(), right.getRowLength());
319   }
320 
321   public static boolean matchingRow(final Cell left, final byte[] buf) {
322     return Bytes.equals(left.getRowArray(),  left.getRowOffset(), left.getRowLength(),
323       buf, 0, buf.length);
324   }
325 
326   public static boolean matchingFamily(final Cell left, final Cell right) {
327     return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(),
328         right.getFamilyArray(), right.getFamilyOffset(), right.getFamilyLength());
329   }
330 
331   public static boolean matchingFamily(final Cell left, final byte[] buf) {
332     return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(),
333         buf, 0, buf.length);
334   }
335 
336   public static boolean matchingQualifier(final Cell left, final Cell right) {
337     return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(), left.getQualifierLength(),
338         right.getQualifierArray(), right.getQualifierOffset(), right.getQualifierLength());
339   }
340 
341   public static boolean matchingQualifier(final Cell left, final byte[] buf) {
342     return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(), left.getQualifierLength(),
343         buf, 0, buf.length);
344   }
345 
346   public static boolean matchingColumn(final Cell left, final byte[] fam, final byte[] qual) {
347     if (!matchingFamily(left, fam))
348       return false;
349     return matchingQualifier(left, qual);
350   }
351 
352   public static boolean matchingColumn(final Cell left, final Cell right) {
353     if (!matchingFamily(left, right))
354       return false;
355     return matchingQualifier(left, right);
356   }
357 
358   public static boolean matchingValue(final Cell left, final Cell right) {
359     return Bytes.equals(left.getValueArray(), left.getValueOffset(), left.getValueLength(),
360         right.getValueArray(), right.getValueOffset(), right.getValueLength());
361   }
362 
363   public static boolean matchingValue(final Cell left, final byte[] buf) {
364     return Bytes.equals(left.getValueArray(), left.getValueOffset(), left.getValueLength(),
365         buf, 0, buf.length);
366   }
367   /**
368    * @return True if a delete type, a {@link KeyValue.Type#Delete} or
369    * a {KeyValue.Type#DeleteFamily} or a {@link KeyValue.Type#DeleteColumn}
370    * KeyValue type.
371    */
372   public static boolean isDelete(final Cell cell) {
373     return KeyValue.isDelete(cell.getTypeByte());
374   }
375 
376   public static boolean isDeleteFamily(final Cell cell) {
377     return cell.getTypeByte() == Type.DeleteFamily.getCode();
378   }
379 
380   /**
381    * @param cell
382    * @return Estimate of the <code>cell</code> size in bytes.
383    */
384   public static int estimatedSizeOf(final Cell cell) {
385     // If a KeyValue, we can give a good estimate of size.
386     if (cell instanceof KeyValue) {
387       return ((KeyValue)cell).getLength() + Bytes.SIZEOF_INT;
388     }
389     // TODO: Should we add to Cell a sizeOf?  Would it help? Does it make sense if Cell is
390     // prefix encoded or compressed?
391     return cell.getRowLength() + cell.getFamilyLength() +
392       cell.getQualifierLength() +
393       cell.getValueLength() +
394       // Use the KeyValue's infrastructure size presuming that another implementation would have
395       // same basic cost.
396       KeyValue.KEY_INFRASTRUCTURE_SIZE +
397       // Serialization is probably preceded by a length (it is in the KeyValueCodec at least).
398       Bytes.SIZEOF_INT;
399   }
400   
401   
402   /********************* tags *************************************/
403   /**
404    * Util method to iterate through the tags
405    * 
406    * @param tags
407    * @param offset
408    * @param length
409    * @return iterator for the tags
410    */
411   @InterfaceStability.Unstable
412   public static Iterator<Tag> tagsIterator(final byte[] tags, final int offset, final int length) {
413     return new Iterator<Tag>() {
414       private int pos = offset;
415       private int endOffset = offset + length - 1;
416 
417       @Override
418       public boolean hasNext() {
419         return this.pos < endOffset;
420       }
421 
422       @Override
423       public Tag next() {
424         if (hasNext()) {
425           int curTagLen = Bytes.readAsInt(tags, this.pos, Tag.TAG_LENGTH_SIZE);
426           Tag tag = new Tag(tags, pos, curTagLen + Tag.TAG_LENGTH_SIZE);
427           this.pos += Bytes.SIZEOF_SHORT + curTagLen;
428           return tag;
429         }
430         return null;
431       }
432 
433       @Override
434       public void remove() {
435         throw new UnsupportedOperationException();
436       }
437     };
438   }
439 
440   public static boolean isDeleteColumns(final Cell cell) {
441     return cell.getTypeByte() == Type.DeleteColumn.getCode();
442   }
443 
444   public static boolean isDeleteColumnVersion(final Cell cell) {
445     return cell.getTypeByte() == Type.Delete.getCode();
446   }
447 
448   /**
449    * Returns true if the first range start1...end1 overlaps with the second range
450    * start2...end2, assuming the byte arrays represent row keys
451    */
452   public static boolean overlappingKeys(final byte[] start1, final byte[] end1,
453       final byte[] start2, final byte[] end2) {
454     return (end2.length == 0 || start1.length == 0 || Bytes.compareTo(start1,
455         end2) < 0)
456         && (end1.length == 0 || start2.length == 0 || Bytes.compareTo(start2,
457             end1) < 0);
458   }
459 }