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.DataOutputStream;
22  import java.io.IOException;
23  import java.nio.ByteBuffer;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map.Entry;
27  import java.util.NavigableMap;
28  
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  import org.apache.hadoop.hbase.classification.InterfaceStability;
31  import org.apache.hadoop.hbase.KeyValue.Type;
32  import org.apache.hadoop.hbase.io.HeapSize;
33  import org.apache.hadoop.hbase.util.ByteBufferUtils;
34  import org.apache.hadoop.hbase.util.ByteRange;
35  import org.apache.hadoop.hbase.util.Bytes;
36  
37  /**
38   * Utility methods helpful slinging {@link Cell} instances.
39   * Some methods below are for internal use only and are marked InterfaceAudience.Private at the
40   * method level.
41   */
42  @InterfaceAudience.Public
43  @InterfaceStability.Evolving
44  public final class CellUtil {
45  
46    /******************* ByteRange *******************************/
47  
48    public static ByteRange fillRowRange(Cell cell, ByteRange range) {
49      return range.set(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
50    }
51  
52    public static ByteRange fillFamilyRange(Cell cell, ByteRange range) {
53      return range.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
54    }
55  
56    public static ByteRange fillQualifierRange(Cell cell, ByteRange range) {
57      return range.set(cell.getQualifierArray(), cell.getQualifierOffset(),
58        cell.getQualifierLength());
59    }
60  
61    public static ByteRange fillValueRange(Cell cell, ByteRange range) {
62      return range.set(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
63    }
64  
65    public static ByteRange fillTagRange(Cell cell, ByteRange range) {
66      return range.set(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
67    }
68  
69    /***************** get individual arrays for tests ************/
70  
71    public static byte[] cloneRow(Cell cell){
72      byte[] output = new byte[cell.getRowLength()];
73      copyRowTo(cell, output, 0);
74      return output;
75    }
76  
77    public static byte[] cloneFamily(Cell cell){
78      byte[] output = new byte[cell.getFamilyLength()];
79      copyFamilyTo(cell, output, 0);
80      return output;
81    }
82  
83    public static byte[] cloneQualifier(Cell cell){
84      byte[] output = new byte[cell.getQualifierLength()];
85      copyQualifierTo(cell, output, 0);
86      return output;
87    }
88  
89    public static byte[] cloneValue(Cell cell){
90      byte[] output = new byte[cell.getValueLength()];
91      copyValueTo(cell, output, 0);
92      return output;
93    }
94  
95    /**
96     * Returns tag value in a new byte array. If server-side, use
97     * {@link Tag#getBuffer()} with appropriate {@link Tag#getTagOffset()} and
98     * {@link Tag#getTagLength()} instead to save on allocations.
99     * @param cell
100    * @return tag value in a new byte array.
101    */
102   public static byte[] getTagArray(Cell cell){
103     byte[] output = new byte[cell.getTagsLength()];
104     copyTagTo(cell, output, 0);
105     return output;
106   }
107 
108 
109   /******************** copyTo **********************************/
110 
111   public static int copyRowTo(Cell cell, byte[] destination, int destinationOffset) {
112     System.arraycopy(cell.getRowArray(), cell.getRowOffset(), destination, destinationOffset,
113       cell.getRowLength());
114     return destinationOffset + cell.getRowLength();
115   }
116 
117   public static int copyFamilyTo(Cell cell, byte[] destination, int destinationOffset) {
118     System.arraycopy(cell.getFamilyArray(), cell.getFamilyOffset(), destination, destinationOffset,
119       cell.getFamilyLength());
120     return destinationOffset + cell.getFamilyLength();
121   }
122 
123   public static int copyQualifierTo(Cell cell, byte[] destination, int destinationOffset) {
124     System.arraycopy(cell.getQualifierArray(), cell.getQualifierOffset(), destination,
125       destinationOffset, cell.getQualifierLength());
126     return destinationOffset + cell.getQualifierLength();
127   }
128 
129   public static int copyValueTo(Cell cell, byte[] destination, int destinationOffset) {
130     System.arraycopy(cell.getValueArray(), cell.getValueOffset(), destination, destinationOffset,
131         cell.getValueLength());
132     return destinationOffset + cell.getValueLength();
133   }
134 
135   /**
136    * Copies the tags info into the tag portion of the cell
137    * @param cell
138    * @param destination
139    * @param destinationOffset
140    * @return position after tags
141    */
142   public static int copyTagTo(Cell cell, byte[] destination, int destinationOffset) {
143     System.arraycopy(cell.getTagsArray(), cell.getTagsOffset(), destination, destinationOffset,
144         cell.getTagsLength());
145     return destinationOffset + cell.getTagsLength();
146   }
147 
148   /********************* misc *************************************/
149 
150   public static byte getRowByte(Cell cell, int index) {
151     return cell.getRowArray()[cell.getRowOffset() + index];
152   }
153 
154   public static ByteBuffer getValueBufferShallowCopy(Cell cell) {
155     ByteBuffer buffer = ByteBuffer.wrap(cell.getValueArray(), cell.getValueOffset(),
156       cell.getValueLength());
157     return buffer;
158   }
159 
160   public static ByteBuffer getQualifierBufferShallowCopy(Cell cell) {
161     ByteBuffer buffer = ByteBuffer.wrap(cell.getQualifierArray(), cell.getQualifierOffset(),
162         cell.getQualifierLength());
163     return buffer;
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) {
168     // I need a Cell Factory here.  Using KeyValue for now. TODO.
169     // TODO: Make a new Cell implementation that just carries these
170     // byte arrays.
171     // TODO: Call factory to create Cell
172     return new KeyValue(row, family, qualifier, timestamp, KeyValue.Type.codeToType(type), value);
173   }
174 
175   public static Cell createCell(final byte [] rowArray, final int rowOffset, final int rowLength,
176       final byte [] familyArray, final int familyOffset, final int familyLength,
177       final byte [] qualifierArray, final int qualifierOffset, final int qualifierLength) {
178     // See createCell(final byte [] row, final byte [] value) for why we default Maximum type.
179     return new KeyValue(rowArray, rowOffset, rowLength,
180         familyArray, familyOffset, familyLength,
181         qualifierArray, qualifierOffset, qualifierLength,
182         HConstants.LATEST_TIMESTAMP,
183         KeyValue.Type.Maximum,
184         HConstants.EMPTY_BYTE_ARRAY, 0, HConstants.EMPTY_BYTE_ARRAY.length);
185   }
186 
187   /**
188    * Marked as audience Private as of 1.2.0.
189    * Creating a Cell with a memstoreTS/mvcc is an internal implementation detail not for
190    * public use.
191    */
192   @InterfaceAudience.Private
193   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
194       final long timestamp, final byte type, final byte[] value, final long memstoreTS) {
195     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp,
196         KeyValue.Type.codeToType(type), value);
197     keyValue.setSequenceId(memstoreTS);
198     return keyValue;
199   }
200 
201   /**
202    * Marked as audience Private as of 1.2.0.
203    * Creating a Cell with tags and a memstoreTS/mvcc is an internal implementation detail not for
204    * public use.
205    */
206   @InterfaceAudience.Private
207   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
208       final long timestamp, final byte type, final byte[] value, byte[] tags, final long memstoreTS) {
209     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp,
210         KeyValue.Type.codeToType(type), value, tags);
211     keyValue.setSequenceId(memstoreTS);
212     return keyValue;
213   }
214 
215   /**
216    * Marked as audience Private as of 1.2.0.
217    * Creating a Cell with tags is an internal implementation detail not for
218    * public use.
219    */
220   @InterfaceAudience.Private
221   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
222       final long timestamp, Type type, final byte[] value, byte[] tags) {
223     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp, type, value, tags);
224     return keyValue;
225   }
226 
227   /**
228    * Create a Cell with specific row.  Other fields defaulted.
229    * @param row
230    * @return Cell with passed row but all other fields are arbitrary
231    */
232   public static Cell createCell(final byte [] row) {
233     return createCell(row, HConstants.EMPTY_BYTE_ARRAY);
234   }
235 
236   /**
237    * Create a Cell with specific row and value.  Other fields are defaulted.
238    * @param row
239    * @param value
240    * @return Cell with passed row and value but all other fields are arbitrary
241    */
242   public static Cell createCell(final byte [] row, final byte [] value) {
243     // An empty family + empty qualifier + Type.Minimum is used as flag to indicate last on row.
244     // See the CellComparator and KeyValue comparator.  Search for compareWithoutRow.
245     // Lets not make a last-on-row key as default but at same time, if you are making a key
246     // without specifying type, etc., flag it as weird by setting type to be Maximum.
247     return createCell(row, HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY,
248       HConstants.LATEST_TIMESTAMP, KeyValue.Type.Maximum.getCode(), value);
249   }
250 
251   /**
252    * Create a Cell with specific row.  Other fields defaulted.
253    * @param row
254    * @param family
255    * @param qualifier
256    * @return Cell with passed row but all other fields are arbitrary
257    */
258   public static Cell createCell(final byte [] row, final byte [] family, final byte [] qualifier) {
259     // See above in createCell(final byte [] row, final byte [] value) why we set type to Maximum.
260     return createCell(row, family, qualifier,
261         HConstants.LATEST_TIMESTAMP, KeyValue.Type.Maximum.getCode(), HConstants.EMPTY_BYTE_ARRAY);
262   }
263 
264   /**
265    * @param cellScannerables
266    * @return CellScanner interface over <code>cellIterables</code>
267    */
268   public static CellScanner createCellScanner(final List<? extends CellScannable> cellScannerables) {
269     return new CellScanner() {
270       private final Iterator<? extends CellScannable> iterator = cellScannerables.iterator();
271       private CellScanner cellScanner = null;
272 
273       @Override
274       public Cell current() {
275         return this.cellScanner != null? this.cellScanner.current(): null;
276       }
277 
278       @Override
279       public boolean advance() throws IOException {
280         while (true) {
281           if (this.cellScanner == null) {
282             if (!this.iterator.hasNext()) return false;
283             this.cellScanner = this.iterator.next().cellScanner();
284           }
285           if (this.cellScanner.advance()) return true;
286           this.cellScanner = null;
287         }
288       }
289     };
290   }
291 
292   /**
293    * @param cellIterable
294    * @return CellScanner interface over <code>cellIterable</code>
295    */
296   public static CellScanner createCellScanner(final Iterable<Cell> cellIterable) {
297     if (cellIterable == null) return null;
298     return createCellScanner(cellIterable.iterator());
299   }
300 
301   /**
302    * @param cells
303    * @return CellScanner interface over <code>cellIterable</code> or null if <code>cells</code> is
304    * null
305    */
306   public static CellScanner createCellScanner(final Iterator<Cell> cells) {
307     if (cells == null) return null;
308     return new CellScanner() {
309       private final Iterator<Cell> iterator = cells;
310       private Cell current = null;
311 
312       @Override
313       public Cell current() {
314         return this.current;
315       }
316 
317       @Override
318       public boolean advance() {
319         boolean hasNext = this.iterator.hasNext();
320         this.current = hasNext? this.iterator.next(): null;
321         return hasNext;
322       }
323     };
324   }
325 
326   /**
327    * @param cellArray
328    * @return CellScanner interface over <code>cellArray</code>
329    */
330   public static CellScanner createCellScanner(final Cell[] cellArray) {
331     return new CellScanner() {
332       private final Cell [] cells = cellArray;
333       private int index = -1;
334 
335       @Override
336       public Cell current() {
337         if (cells == null) return null;
338         return (index < 0)? null: this.cells[index];
339       }
340 
341       @Override
342       public boolean advance() {
343         if (cells == null) return false;
344         return ++index < this.cells.length;
345       }
346     };
347   }
348 
349   /**
350    * Flatten the map of cells out under the CellScanner
351    * @param map Map of Cell Lists; for example, the map of families to Cells that is used
352    * inside Put, etc., keeping Cells organized by family.
353    * @return CellScanner interface over <code>cellIterable</code>
354    */
355   public static CellScanner createCellScanner(final NavigableMap<byte [], List<Cell>> map) {
356     return new CellScanner() {
357       private final Iterator<Entry<byte[], List<Cell>>> entries = map.entrySet().iterator();
358       private Iterator<Cell> currentIterator = null;
359       private Cell currentCell;
360 
361       @Override
362       public Cell current() {
363         return this.currentCell;
364       }
365 
366       @Override
367       public boolean advance() {
368         while(true) {
369           if (this.currentIterator == null) {
370             if (!this.entries.hasNext()) return false;
371             this.currentIterator = this.entries.next().getValue().iterator();
372           }
373           if (this.currentIterator.hasNext()) {
374             this.currentCell = this.currentIterator.next();
375             return true;
376           }
377           this.currentCell = null;
378           this.currentIterator = null;
379         }
380       }
381     };
382   }
383 
384   /**
385    * @param left
386    * @param right
387    * @return True if the rows in <code>left</code> and <code>right</code> Cells match
388    */
389   public static boolean matchingRow(final Cell left, final Cell right) {
390     return Bytes.equals(left.getRowArray(), left.getRowOffset(), left.getRowLength(),
391         right.getRowArray(), right.getRowOffset(), right.getRowLength());
392   }
393 
394   public static boolean matchingRow(final Cell left, final byte[] buf) {
395     return Bytes.equals(left.getRowArray(), left.getRowOffset(), left.getRowLength(), buf, 0,
396         buf.length);
397   }
398 
399   public static boolean matchingRow(final Cell left, final byte[] buf, final int offset,
400       final int length) {
401     return Bytes.equals(left.getRowArray(), left.getRowOffset(), left.getRowLength(), buf, offset,
402         length);
403   }
404 
405   public static boolean matchingFamily(final Cell left, final Cell right) {
406     return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(),
407         right.getFamilyArray(), right.getFamilyOffset(), right.getFamilyLength());
408   }
409 
410   public static boolean matchingFamily(final Cell left, final byte[] buf) {
411     return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(), buf,
412         0, buf.length);
413   }
414 
415   public static boolean matchingFamily(final Cell left, final byte[] buf, final int offset,
416       final int length) {
417     return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(), buf,
418         offset, length);
419   }
420 
421   public static boolean matchingQualifier(final Cell left, final Cell right) {
422     return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(),
423         left.getQualifierLength(), right.getQualifierArray(), right.getQualifierOffset(),
424         right.getQualifierLength());
425   }
426 
427   public static boolean matchingQualifier(final Cell left, final byte[] buf) {
428     if (buf == null) {
429       return left.getQualifierLength() == 0;
430     }
431     return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(),
432         left.getQualifierLength(), buf, 0, buf.length);
433   }
434 
435   public static boolean matchingQualifier(final Cell left, final byte[] buf, final int offset,
436       final int length) {
437     if (buf == null) {
438       return left.getQualifierLength() == 0;
439     }
440     return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(),
441         left.getQualifierLength(), buf, offset, length);
442   }
443 
444   public static boolean matchingColumn(final Cell left, final byte[] fam, final byte[] qual) {
445     if (!matchingFamily(left, fam))
446       return false;
447     return matchingQualifier(left, qual);
448   }
449 
450   public static boolean matchingColumn(final Cell left, final byte[] fam, final int foffset,
451       final int flength, final byte[] qual, final int qoffset, final int qlength) {
452     if (!matchingFamily(left, fam, foffset, flength))
453       return false;
454     return matchingQualifier(left, qual, qoffset, qlength);
455   }
456 
457   public static boolean matchingColumn(final Cell left, final Cell right) {
458     if (!matchingFamily(left, right))
459       return false;
460     return matchingQualifier(left, right);
461   }
462 
463   public static boolean matchingValue(final Cell left, final Cell right) {
464     return Bytes.equals(left.getValueArray(), left.getValueOffset(), left.getValueLength(),
465         right.getValueArray(), right.getValueOffset(), right.getValueLength());
466   }
467 
468   public static boolean matchingValue(final Cell left, final byte[] buf) {
469     return Bytes.equals(left.getValueArray(), left.getValueOffset(), left.getValueLength(), buf, 0,
470         buf.length);
471   }
472 
473   public static boolean matchingTimestamp(Cell a, Cell b) {
474     return CellComparator.compareTimestamps(a, b) == 0;
475   }
476 
477   /**
478    * Compares the row and column of two keyvalues for equality
479    * @param left
480    * @param right
481    * @return True if same row and column.
482    */
483   public static boolean matchingRowColumn(final Cell left, final Cell right) {
484     if ((left.getRowLength() + left.getFamilyLength() + left.getQualifierLength()) != (right
485         .getRowLength() + right.getFamilyLength() + right.getQualifierLength())) {
486       return false;
487     }
488 
489     if (!matchingRow(left, right)) {
490       return false;
491     }
492     return matchingColumn(left, right);
493   }
494 
495   /**
496    * @return True if a delete type, a {@link KeyValue.Type#Delete} or a
497    *         {KeyValue.Type#DeleteFamily} or a
498    *         {@link KeyValue.Type#DeleteColumn} KeyValue type.
499    */
500   public static boolean isDelete(final Cell cell) {
501     return isDelete(cell.getTypeByte());
502   }
503 
504   /**
505    * @return True if a delete type, a {@link KeyValue.Type#Delete} or a
506    *         {KeyValue.Type#DeleteFamily} or a
507    *         {@link KeyValue.Type#DeleteColumn} KeyValue type.
508    */
509   public static boolean isDelete(final byte type) {
510     return Type.Delete.getCode() <= type
511         && type <= Type.DeleteFamily.getCode();
512   }
513 
514   /**
515    * @return True if this cell is a {@link KeyValue.Type#Delete} type.
516    */
517   public static boolean isDeleteType(Cell cell) {
518     return cell.getTypeByte() == Type.Delete.getCode();
519   }
520 
521   public static boolean isDeleteFamily(final Cell cell) {
522     return cell.getTypeByte() == Type.DeleteFamily.getCode();
523   }
524 
525   public static boolean isDeleteFamilyVersion(final Cell cell) {
526     return cell.getTypeByte() == Type.DeleteFamilyVersion.getCode();
527   }
528 
529   public static boolean isDeleteColumns(final Cell cell) {
530     return cell.getTypeByte() == Type.DeleteColumn.getCode();
531   }
532 
533   public static boolean isDeleteColumnVersion(final Cell cell) {
534     return cell.getTypeByte() == Type.Delete.getCode();
535   }
536 
537   /**
538    *
539    * @return True if this cell is a delete family or column type.
540    */
541   public static boolean isDeleteColumnOrFamily(Cell cell) {
542     int t = cell.getTypeByte();
543     return t == Type.DeleteColumn.getCode() || t == Type.DeleteFamily.getCode();
544   }
545 
546   /**
547    * Estimate based on keyvalue's serialization format. 
548    * @param cell
549    * @return Estimate of the <code>cell</code> size in bytes.
550    * @deprecated please use estimatedSerializedSizeOf(Cell)
551    */
552   @Deprecated
553   public static int estimatedSizeOf(final Cell cell) {
554     return estimatedSerializedSizeOf(cell);
555   }
556 
557   /**
558    * @param cell
559    * @return Estimate of the <code>cell</code> size in bytes.
560    */
561   public static int estimatedSerializedSizeOf(final Cell cell) {
562     // If a KeyValue, we can give a good estimate of size.
563     if (cell instanceof KeyValue) {
564       return ((KeyValue)cell).getLength() + Bytes.SIZEOF_INT;
565     }
566     // TODO: Should we add to Cell a sizeOf?  Would it help? Does it make sense if Cell is
567     // prefix encoded or compressed?
568     return getSumOfCellElementLengths(cell) +
569       // Use the KeyValue's infrastructure size presuming that another implementation would have
570       // same basic cost.
571       KeyValue.ROW_LENGTH_SIZE + KeyValue.FAMILY_LENGTH_SIZE +
572       // Serialization is probably preceded by a length (it is in the KeyValueCodec at least).
573       Bytes.SIZEOF_INT;
574   }
575 
576   /**
577    * @param cell
578    * @return Sum of the lengths of all the elements in a Cell; does not count in any infrastructure
579    */
580   private static int getSumOfCellElementLengths(final Cell cell) {
581     return getSumOfCellKeyElementLengths(cell) + cell.getValueLength() + cell.getTagsLength();
582   }
583 
584   /**
585    * @param cell
586    * @return Sum of all elements that make up a key; does not include infrastructure, tags or
587    * values.
588    */
589   private static int getSumOfCellKeyElementLengths(final Cell cell) {
590     return cell.getRowLength() + cell.getFamilyLength() +
591     cell.getQualifierLength() +
592     KeyValue.TIMESTAMP_TYPE_SIZE;
593   }
594 
595   /**
596    * Calculates the serialized key size. We always serialize in the KeyValue's serialization
597    * format.
598    * @param cell the cell for which the key size has to be calculated.
599    * @return the key size
600    */
601   public static int estimatedSerializedSizeOfKey(final Cell cell) {
602     if (cell instanceof KeyValue) return ((KeyValue)cell).getKeyLength();
603     return cell.getRowLength() + cell.getFamilyLength() +
604         cell.getQualifierLength() +
605         KeyValue.KEY_INFRASTRUCTURE_SIZE;
606   }
607 
608   /**
609    * This is an estimate of the heap space occupied by a cell. When the cell is of type
610    * {@link HeapSize} we call {@link HeapSize#heapSize()} so cell can give a correct value. In other
611    * cases we just consider the byte occupied by the cell components ie. row, CF, qualifier,
612    * timestamp, type, value and tags.
613    * @param cell
614    * @return estimate of the heap space
615    */
616   public static long estimatedHeapSizeOf(final Cell cell) {
617     if (cell instanceof HeapSize) {
618       return ((HeapSize) cell).heapSize();
619     }
620     // TODO: Add sizing of references that hold the row, family, etc., arrays.
621     return estimatedSerializedSizeOf(cell);
622   }
623 
624   /**
625    * This is a hack that should be removed once we don't care about matching
626    * up client- and server-side estimations of cell size. It needed to be
627    * backwards compatible with estimations done by older clients. We need to
628    * pretend that tags never exist and cells aren't serialized with tag
629    * length included. See HBASE-13262 and HBASE-13303
630    */
631   @Deprecated
632   public static long estimatedHeapSizeOfWithoutTags(final Cell cell) {
633     if (cell instanceof KeyValue) {
634       return ((KeyValue)cell).heapSizeWithoutTags();
635     }
636     return (long) getSumOfCellKeyElementLengths(cell) + cell.getValueLength();
637   }
638 
639   /********************* tags *************************************/
640   /**
641    * Util method to iterate through the tags
642    *
643    * @param tags
644    * @param offset
645    * @param length
646    * @return iterator for the tags
647    */
648   public static Iterator<Tag> tagsIterator(final byte[] tags, final int offset, final int length) {
649     return new Iterator<Tag>() {
650       private int pos = offset;
651       private int endOffset = offset + length - 1;
652 
653       @Override
654       public boolean hasNext() {
655         return this.pos < endOffset;
656       }
657 
658       @Override
659       public Tag next() {
660         if (hasNext()) {
661           int curTagLen = Bytes.readAsInt(tags, this.pos, Tag.TAG_LENGTH_SIZE);
662           Tag tag = new Tag(tags, pos, curTagLen + Tag.TAG_LENGTH_SIZE);
663           this.pos += Bytes.SIZEOF_SHORT + curTagLen;
664           return tag;
665         }
666         return null;
667       }
668 
669       @Override
670       public void remove() {
671         throw new UnsupportedOperationException();
672       }
673     };
674   }
675 
676   /**
677    * Returns true if the first range start1...end1 overlaps with the second range
678    * start2...end2, assuming the byte arrays represent row keys
679    */
680   public static boolean overlappingKeys(final byte[] start1, final byte[] end1,
681       final byte[] start2, final byte[] end2) {
682     return (end2.length == 0 || start1.length == 0 || Bytes.compareTo(start1,
683         end2) < 0)
684         && (end1.length == 0 || start2.length == 0 || Bytes.compareTo(start2,
685             end1) < 0);
686   }
687 
688   /**
689    * Sets the given seqId to the cell.
690    * Marked as audience Private as of 1.2.0.
691    * Setting a Cell sequenceid is an internal implementation detail not for general public use.
692    * @param cell
693    * @param seqId
694    * @throws IOException when the passed cell is not of type {@link SettableSequenceId}
695    */
696   @InterfaceAudience.Private
697   public static void setSequenceId(Cell cell, long seqId) throws IOException {
698     if (cell instanceof SettableSequenceId) {
699       ((SettableSequenceId) cell).setSequenceId(seqId);
700     } else {
701       throw new IOException(new UnsupportedOperationException("Cell is not of type "
702           + SettableSequenceId.class.getName()));
703     }
704   }
705 
706   /**
707    * Sets the given timestamp to the cell.
708    * @param cell
709    * @param ts
710    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
711    */
712   public static void setTimestamp(Cell cell, long ts) throws IOException {
713     if (cell instanceof SettableTimestamp) {
714       ((SettableTimestamp) cell).setTimestamp(ts);
715     } else {
716       throw new IOException(new UnsupportedOperationException("Cell is not of type "
717           + SettableTimestamp.class.getName()));
718     }
719   }
720 
721   /**
722    * Sets the given timestamp to the cell.
723    * @param cell
724    * @param ts buffer containing the timestamp value
725    * @param tsOffset offset to the new timestamp
726    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
727    */
728   public static void setTimestamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
729     if (cell instanceof SettableTimestamp) {
730       ((SettableTimestamp) cell).setTimestamp(ts, tsOffset);
731     } else {
732       throw new IOException(new UnsupportedOperationException("Cell is not of type "
733           + SettableTimestamp.class.getName()));
734     }
735   }
736 
737   /**
738    * Sets the given timestamp to the cell iff current timestamp is
739    * {@link HConstants#LATEST_TIMESTAMP}.
740    * @param cell
741    * @param ts
742    * @return True if cell timestamp is modified.
743    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
744    */
745   public static boolean updateLatestStamp(Cell cell, long ts) throws IOException {
746     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
747       setTimestamp(cell, ts);
748       return true;
749     }
750     return false;
751   }
752 
753   /**
754    * Sets the given timestamp to the cell iff current timestamp is
755    * {@link HConstants#LATEST_TIMESTAMP}.
756    * @param cell
757    * @param ts buffer containing the timestamp value
758    * @param tsOffset offset to the new timestamp
759    * @return True if cell timestamp is modified.
760    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
761    */
762   public static boolean updateLatestStamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
763     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
764       setTimestamp(cell, ts, tsOffset);
765       return true;
766     }
767     return false;
768   }
769 
770   /**
771    * Writes the Cell's key part as it would have serialized in a KeyValue. The format is &lt;2 bytes
772    * rk len&gt;&lt;rk&gt;&lt;1 byte cf len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes
773    * timestamp&gt;&lt;1 byte type&gt;
774    * @param cell
775    * @param out
776    * @throws IOException
777    */
778   public static void writeFlatKey(Cell cell, DataOutputStream out) throws IOException {
779     short rowLen = cell.getRowLength();
780     out.writeShort(rowLen);
781     out.write(cell.getRowArray(), cell.getRowOffset(), rowLen);
782     byte fLen = cell.getFamilyLength();
783     out.writeByte(fLen);
784     out.write(cell.getFamilyArray(), cell.getFamilyOffset(), fLen);
785     out.write(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
786     out.writeLong(cell.getTimestamp());
787     out.writeByte(cell.getTypeByte());
788   }
789 
790   /**
791    * @param cell
792    * @return The Key portion of the passed <code>cell</code> as a String.
793    */
794   public static String getCellKeyAsString(Cell cell) {
795     StringBuilder sb = new StringBuilder(Bytes.toStringBinary(
796       cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
797     sb.append('/');
798     sb.append(cell.getFamilyLength() == 0? "":
799       Bytes.toStringBinary(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()));
800     // KeyValue only added ':' if family is non-null.  Do same.
801     if (cell.getFamilyLength() > 0) sb.append(':');
802     sb.append(cell.getQualifierLength() == 0? "":
803       Bytes.toStringBinary(cell.getQualifierArray(), cell.getQualifierOffset(),
804         cell.getQualifierLength()));
805     sb.append('/');
806     sb.append(KeyValue.humanReadableTimestamp(cell.getTimestamp()));
807     sb.append('/');
808     sb.append(Type.codeToType(cell.getTypeByte()));
809     sb.append("/vlen=");
810     sb.append(cell.getValueLength());
811     sb.append("/seqid=");
812     sb.append(cell.getSequenceId());
813     return sb.toString();
814   }
815 
816   /**
817    * This method exists just to encapsulate how we serialize keys.  To be replaced by a factory
818    * that we query to figure what the Cell implementation is and then, what serialization engine
819    * to use and further, how to serialize the key for inclusion in hfile index. TODO.
820    * @param cell
821    * @return The key portion of the Cell serialized in the old-school KeyValue way or null if
822    * passed a null <code>cell</code>
823    */
824   public static byte [] getCellKeySerializedAsKeyValueKey(final Cell cell) {
825     if (cell == null) return null;
826     byte [] b = new byte[KeyValueUtil.keyLength(cell)];
827     KeyValueUtil.appendKeyTo(cell, b, 0);
828     return b;
829   }
830 
831   /**
832    * Write rowkey excluding the common part.
833    * @param cell
834    * @param rLen
835    * @param commonPrefix
836    * @param out
837    * @throws IOException
838    */
839   public static void writeRowKeyExcludingCommon(Cell cell, short rLen, int commonPrefix,
840       DataOutputStream out) throws IOException {
841     if (commonPrefix == 0) {
842       out.writeShort(rLen);
843     } else if (commonPrefix == 1) {
844       out.writeByte((byte) rLen);
845       commonPrefix--;
846     } else {
847       commonPrefix -= KeyValue.ROW_LENGTH_SIZE;
848     }
849     if (rLen > commonPrefix) {
850       out.write(cell.getRowArray(), cell.getRowOffset() + commonPrefix, rLen - commonPrefix);
851     }
852   }
853 
854   /**
855    * Find length of common prefix in keys of the cells, considering key as byte[] if serialized in
856    * {@link KeyValue}. The key format is &lt;2 bytes rk len&gt;&lt;rk&gt;&lt;1 byte cf
857    * len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes timestamp&gt;&lt;1 byte type&gt;
858    * @param c1
859    *          the cell
860    * @param c2
861    *          the cell
862    * @param bypassFamilyCheck
863    *          when true assume the family bytes same in both cells. Pass it as true when dealing
864    *          with Cells in same CF so as to avoid some checks
865    * @param withTsType
866    *          when true check timestamp and type bytes also.
867    * @return length of common prefix
868    */
869   public static int findCommonPrefixInFlatKey(Cell c1, Cell c2, boolean bypassFamilyCheck,
870       boolean withTsType) {
871     // Compare the 2 bytes in RK length part
872     short rLen1 = c1.getRowLength();
873     short rLen2 = c2.getRowLength();
874     int commonPrefix = KeyValue.ROW_LENGTH_SIZE;
875     if (rLen1 != rLen2) {
876       // early out when the RK length itself is not matching
877       return ByteBufferUtils.findCommonPrefix(Bytes.toBytes(rLen1), 0, KeyValue.ROW_LENGTH_SIZE,
878           Bytes.toBytes(rLen2), 0, KeyValue.ROW_LENGTH_SIZE);
879     }
880     // Compare the RKs
881     int rkCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getRowArray(), c1.getRowOffset(),
882         rLen1, c2.getRowArray(), c2.getRowOffset(), rLen2);
883     commonPrefix += rkCommonPrefix;
884     if (rkCommonPrefix != rLen1) {
885       // Early out when RK is not fully matching.
886       return commonPrefix;
887     }
888     // Compare 1 byte CF length part
889     byte fLen1 = c1.getFamilyLength();
890     if (bypassFamilyCheck) {
891       // This flag will be true when caller is sure that the family will be same for both the cells
892       // Just make commonPrefix to increment by the family part
893       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE + fLen1;
894     } else {
895       byte fLen2 = c2.getFamilyLength();
896       if (fLen1 != fLen2) {
897         // early out when the CF length itself is not matching
898         return commonPrefix;
899       }
900       // CF lengths are same so there is one more byte common in key part
901       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE;
902       // Compare the CF names
903       int fCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getFamilyArray(),
904           c1.getFamilyOffset(), fLen1, c2.getFamilyArray(), c2.getFamilyOffset(), fLen2);
905       commonPrefix += fCommonPrefix;
906       if (fCommonPrefix != fLen1) {
907         return commonPrefix;
908       }
909     }
910     // Compare the Qualifiers
911     int qLen1 = c1.getQualifierLength();
912     int qLen2 = c2.getQualifierLength();
913     int qCommon = ByteBufferUtils.findCommonPrefix(c1.getQualifierArray(), c1.getQualifierOffset(),
914         qLen1, c2.getQualifierArray(), c2.getQualifierOffset(), qLen2);
915     commonPrefix += qCommon;
916     if (!withTsType || Math.max(qLen1, qLen2) != qCommon) {
917       return commonPrefix;
918     }
919     // Compare the timestamp parts
920     int tsCommonPrefix = ByteBufferUtils.findCommonPrefix(Bytes.toBytes(c1.getTimestamp()), 0,
921         KeyValue.TIMESTAMP_SIZE, Bytes.toBytes(c2.getTimestamp()), 0, KeyValue.TIMESTAMP_SIZE);
922     commonPrefix += tsCommonPrefix;
923     if (tsCommonPrefix != KeyValue.TIMESTAMP_SIZE) {
924       return commonPrefix;
925     }
926     // Compare the type
927     if (c1.getTypeByte() == c2.getTypeByte()) {
928       commonPrefix += KeyValue.TYPE_SIZE;
929     }
930     return commonPrefix;
931   }
932 
933   /** Returns a string representation of the cell */
934   public static String toString(Cell cell, boolean verbose) {
935     if (cell == null) {
936       return "";
937     }
938     StringBuilder builder = new StringBuilder();
939     String keyStr = getCellKeyAsString(cell);
940 
941     String tag = null;
942     String value = null;
943     if (verbose) {
944       // TODO: pretty print tags as well
945       tag = Bytes.toStringBinary(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
946       value = Bytes.toStringBinary(cell.getValueArray(), cell.getValueOffset(),
947         cell.getValueLength());
948     }
949 
950     builder
951       .append(keyStr);
952     if (tag != null && !tag.isEmpty()) {
953       builder.append("/").append(tag);
954     }
955     if (value != null) {
956       builder.append("/").append(value);
957     }
958 
959     return builder.toString();
960   }
961 
962   /**************** equals ****************************/
963 
964   public static boolean equals(Cell a, Cell b) {
965     return matchingRow(a, b) && matchingFamily(a, b) && matchingQualifier(a, b)
966         && matchingTimestamp(a, b) && matchingType(a, b);
967   }
968 
969   public static boolean matchingType(Cell a, Cell b) {
970     return a.getTypeByte() == b.getTypeByte();
971   }
972 }