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