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