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   /*
474     Compares tags in both cells.
475    */
476   public static boolean matchingTags(final Cell left, final Cell right) {
477     return Bytes.equals(left.getTagsArray(), left.getTagsOffset(), left.getTagsLength(),
478         right.getTagsArray(), right.getTagsOffset(), right.getTagsLength());
479   }
480 
481   public static boolean matchingTimestamp(Cell a, Cell b) {
482     return CellComparator.compareTimestamps(a, b) == 0;
483   }
484 
485   /**
486    * Compares the row and column of two keyvalues for equality
487    * @param left
488    * @param right
489    * @return True if same row and column.
490    */
491   public static boolean matchingRowColumn(final Cell left, final Cell right) {
492     if ((left.getRowLength() + left.getFamilyLength() + left.getQualifierLength()) != (right
493         .getRowLength() + right.getFamilyLength() + right.getQualifierLength())) {
494       return false;
495     }
496 
497     if (!matchingRow(left, right)) {
498       return false;
499     }
500     return matchingColumn(left, right);
501   }
502 
503   /**
504    * @return True if a delete type, a {@link KeyValue.Type#Delete} or a
505    *         {KeyValue.Type#DeleteFamily} or a
506    *         {@link KeyValue.Type#DeleteColumn} KeyValue type.
507    */
508   public static boolean isDelete(final Cell cell) {
509     return isDelete(cell.getTypeByte());
510   }
511 
512   /**
513    * @return True if a delete type, a {@link KeyValue.Type#Delete} or a
514    *         {KeyValue.Type#DeleteFamily} or a
515    *         {@link KeyValue.Type#DeleteColumn} KeyValue type.
516    */
517   public static boolean isDelete(final byte type) {
518     return Type.Delete.getCode() <= type
519         && type <= Type.DeleteFamily.getCode();
520   }
521 
522   /**
523    * @return True if this cell is a {@link KeyValue.Type#Delete} type.
524    */
525   public static boolean isDeleteType(Cell cell) {
526     return cell.getTypeByte() == Type.Delete.getCode();
527   }
528 
529   public static boolean isDeleteFamily(final Cell cell) {
530     return cell.getTypeByte() == Type.DeleteFamily.getCode();
531   }
532 
533   public static boolean isDeleteFamilyVersion(final Cell cell) {
534     return cell.getTypeByte() == Type.DeleteFamilyVersion.getCode();
535   }
536 
537   public static boolean isDeleteColumns(final Cell cell) {
538     return cell.getTypeByte() == Type.DeleteColumn.getCode();
539   }
540 
541   public static boolean isDeleteColumnVersion(final Cell cell) {
542     return cell.getTypeByte() == Type.Delete.getCode();
543   }
544 
545   /**
546    *
547    * @return True if this cell is a delete family or column type.
548    */
549   public static boolean isDeleteColumnOrFamily(Cell cell) {
550     int t = cell.getTypeByte();
551     return t == Type.DeleteColumn.getCode() || t == Type.DeleteFamily.getCode();
552   }
553 
554   /**
555    * Estimate based on keyvalue's serialization format.
556    * @param cell
557    * @return Estimate of the <code>cell</code> size in bytes.
558    * @deprecated please use estimatedSerializedSizeOf(Cell)
559    */
560   @Deprecated
561   public static int estimatedSizeOf(final Cell cell) {
562     return estimatedSerializedSizeOf(cell);
563   }
564 
565   /**
566    * @param cell
567    * @return Estimate of the <code>cell</code> size in bytes.
568    */
569   public static int estimatedSerializedSizeOf(final Cell cell) {
570     // If a KeyValue, we can give a good estimate of size.
571     if (cell instanceof KeyValue) {
572       return ((KeyValue)cell).getLength() + Bytes.SIZEOF_INT;
573     }
574     // TODO: Should we add to Cell a sizeOf?  Would it help? Does it make sense if Cell is
575     // prefix encoded or compressed?
576     return getSumOfCellElementLengths(cell) +
577       // Use the KeyValue's infrastructure size presuming that another implementation would have
578       // same basic cost.
579       KeyValue.ROW_LENGTH_SIZE + KeyValue.FAMILY_LENGTH_SIZE +
580       // Serialization is probably preceded by a length (it is in the KeyValueCodec at least).
581       Bytes.SIZEOF_INT;
582   }
583 
584   /**
585    * @param cell
586    * @return Sum of the lengths of all the elements in a Cell; does not count in any infrastructure
587    */
588   private static int getSumOfCellElementLengths(final Cell cell) {
589     return getSumOfCellKeyElementLengths(cell) + cell.getValueLength() + cell.getTagsLength();
590   }
591 
592   /**
593    * @param cell
594    * @return Sum of all elements that make up a key; does not include infrastructure, tags or
595    * values.
596    */
597   private static int getSumOfCellKeyElementLengths(final Cell cell) {
598     return cell.getRowLength() + cell.getFamilyLength() +
599     cell.getQualifierLength() +
600     KeyValue.TIMESTAMP_TYPE_SIZE;
601   }
602 
603   /**
604    * Calculates the serialized key size. We always serialize in the KeyValue's serialization
605    * format.
606    * @param cell the cell for which the key size has to be calculated.
607    * @return the key size
608    */
609   public static int estimatedSerializedSizeOfKey(final Cell cell) {
610     if (cell instanceof KeyValue) return ((KeyValue)cell).getKeyLength();
611     return cell.getRowLength() + cell.getFamilyLength() +
612         cell.getQualifierLength() +
613         KeyValue.KEY_INFRASTRUCTURE_SIZE;
614   }
615 
616   /**
617    * This is an estimate of the heap space occupied by a cell. When the cell is of type
618    * {@link HeapSize} we call {@link HeapSize#heapSize()} so cell can give a correct value. In other
619    * cases we just consider the byte occupied by the cell components ie. row, CF, qualifier,
620    * timestamp, type, value and tags.
621    * @param cell
622    * @return estimate of the heap space
623    */
624   public static long estimatedHeapSizeOf(final Cell cell) {
625     if (cell instanceof HeapSize) {
626       return ((HeapSize) cell).heapSize();
627     }
628     // TODO: Add sizing of references that hold the row, family, etc., arrays.
629     return estimatedSerializedSizeOf(cell);
630   }
631 
632   /**
633    * This is a hack that should be removed once we don't care about matching
634    * up client- and server-side estimations of cell size. It needed to be
635    * backwards compatible with estimations done by older clients. We need to
636    * pretend that tags never exist and cells aren't serialized with tag
637    * length included. See HBASE-13262 and HBASE-13303
638    */
639   @Deprecated
640   public static long estimatedHeapSizeOfWithoutTags(final Cell cell) {
641     if (cell instanceof KeyValue) {
642       return ((KeyValue)cell).heapSizeWithoutTags();
643     }
644     return (long) getSumOfCellKeyElementLengths(cell) + cell.getValueLength();
645   }
646 
647   /********************* tags *************************************/
648   /**
649    * Util method to iterate through the tags
650    *
651    * @param tags
652    * @param offset
653    * @param length
654    * @return iterator for the tags
655    */
656   public static Iterator<Tag> tagsIterator(final byte[] tags, final int offset, final int length) {
657     return new Iterator<Tag>() {
658       private int pos = offset;
659       private int endOffset = offset + length - 1;
660 
661       @Override
662       public boolean hasNext() {
663         return this.pos < endOffset;
664       }
665 
666       @Override
667       public Tag next() {
668         if (hasNext()) {
669           int curTagLen = Bytes.readAsInt(tags, this.pos, Tag.TAG_LENGTH_SIZE);
670           Tag tag = new Tag(tags, pos, curTagLen + Tag.TAG_LENGTH_SIZE);
671           this.pos += Bytes.SIZEOF_SHORT + curTagLen;
672           return tag;
673         }
674         return null;
675       }
676 
677       @Override
678       public void remove() {
679         throw new UnsupportedOperationException();
680       }
681     };
682   }
683 
684   public static byte[] cloneTags(Cell cell) {
685     byte[] output = new byte[cell.getTagsLength()];
686     copyTagsTo(cell, output, 0);
687     return output;
688   }
689 
690   /**
691    * Copies the tags info into the tag portion of the cell
692    * @param cell the cell
693    * @param destination byte array that will receive tag data
694    * @param destinationOffset start offset in byte array that will receive tag data
695    * @return position after tags
696    */
697   public static int copyTagsTo(Cell cell, byte[] destination, int destinationOffset) {
698     int tlen = cell.getTagsLength();
699     System.arraycopy(cell.getTagsArray(), cell.getTagsOffset(), destination,
700       destinationOffset, tlen);
701     return destinationOffset + tlen;
702   }
703 
704   /**
705    * Returns true if the first range start1...end1 overlaps with the second range
706    * start2...end2, assuming the byte arrays represent row keys
707    */
708   public static boolean overlappingKeys(final byte[] start1, final byte[] end1,
709       final byte[] start2, final byte[] end2) {
710     return (end2.length == 0 || start1.length == 0 || Bytes.compareTo(start1,
711         end2) < 0)
712         && (end1.length == 0 || start2.length == 0 || Bytes.compareTo(start2,
713             end1) < 0);
714   }
715 
716   /**
717    * Sets the given seqId to the cell.
718    * Marked as audience Private as of 1.2.0.
719    * Setting a Cell sequenceid is an internal implementation detail not for general public use.
720    * @param cell
721    * @param seqId
722    * @throws IOException when the passed cell is not of type {@link SettableSequenceId}
723    */
724   @InterfaceAudience.Private
725   public static void setSequenceId(Cell cell, long seqId) throws IOException {
726     if (cell instanceof SettableSequenceId) {
727       ((SettableSequenceId) cell).setSequenceId(seqId);
728     } else {
729       throw new IOException(new UnsupportedOperationException("Cell is not of type "
730           + SettableSequenceId.class.getName()));
731     }
732   }
733 
734   /**
735    * Sets the given timestamp to the cell.
736    * @param cell
737    * @param ts
738    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
739    */
740   public static void setTimestamp(Cell cell, long ts) throws IOException {
741     if (cell instanceof SettableTimestamp) {
742       ((SettableTimestamp) cell).setTimestamp(ts);
743     } else {
744       throw new IOException(new UnsupportedOperationException("Cell is not of type "
745           + SettableTimestamp.class.getName()));
746     }
747   }
748 
749   /**
750    * Sets the given timestamp to the cell.
751    * @param cell
752    * @param ts buffer containing the timestamp value
753    * @param tsOffset offset to the new timestamp
754    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
755    */
756   public static void setTimestamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
757     if (cell instanceof SettableTimestamp) {
758       ((SettableTimestamp) cell).setTimestamp(ts, tsOffset);
759     } else {
760       throw new IOException(new UnsupportedOperationException("Cell is not of type "
761           + SettableTimestamp.class.getName()));
762     }
763   }
764 
765   /**
766    * Sets the given timestamp to the cell iff current timestamp is
767    * {@link HConstants#LATEST_TIMESTAMP}.
768    * @param cell
769    * @param ts
770    * @return True if cell timestamp is modified.
771    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
772    */
773   public static boolean updateLatestStamp(Cell cell, long ts) throws IOException {
774     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
775       setTimestamp(cell, ts);
776       return true;
777     }
778     return false;
779   }
780 
781   /**
782    * Sets the given timestamp to the cell iff current timestamp is
783    * {@link HConstants#LATEST_TIMESTAMP}.
784    * @param cell
785    * @param ts buffer containing the timestamp value
786    * @param tsOffset offset to the new timestamp
787    * @return True if cell timestamp is modified.
788    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
789    */
790   public static boolean updateLatestStamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
791     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
792       setTimestamp(cell, ts, tsOffset);
793       return true;
794     }
795     return false;
796   }
797 
798   /**
799    * Writes the Cell's key part as it would have serialized in a KeyValue. The format is &lt;2 bytes
800    * rk len&gt;&lt;rk&gt;&lt;1 byte cf len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes
801    * timestamp&gt;&lt;1 byte type&gt;
802    * @param cell
803    * @param out
804    * @throws IOException
805    */
806   public static void writeFlatKey(Cell cell, DataOutputStream out) throws IOException {
807     short rowLen = cell.getRowLength();
808     out.writeShort(rowLen);
809     out.write(cell.getRowArray(), cell.getRowOffset(), rowLen);
810     byte fLen = cell.getFamilyLength();
811     out.writeByte(fLen);
812     out.write(cell.getFamilyArray(), cell.getFamilyOffset(), fLen);
813     out.write(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
814     out.writeLong(cell.getTimestamp());
815     out.writeByte(cell.getTypeByte());
816   }
817 
818   /**
819    * @param cell
820    * @return The Key portion of the passed <code>cell</code> as a String.
821    */
822   public static String getCellKeyAsString(Cell cell) {
823     StringBuilder sb = new StringBuilder(Bytes.toStringBinary(
824       cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
825     sb.append('/');
826     sb.append(cell.getFamilyLength() == 0? "":
827       Bytes.toStringBinary(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()));
828     // KeyValue only added ':' if family is non-null.  Do same.
829     if (cell.getFamilyLength() > 0) sb.append(':');
830     sb.append(cell.getQualifierLength() == 0? "":
831       Bytes.toStringBinary(cell.getQualifierArray(), cell.getQualifierOffset(),
832         cell.getQualifierLength()));
833     sb.append('/');
834     sb.append(KeyValue.humanReadableTimestamp(cell.getTimestamp()));
835     sb.append('/');
836     sb.append(Type.codeToType(cell.getTypeByte()));
837     sb.append("/vlen=");
838     sb.append(cell.getValueLength());
839     sb.append("/seqid=");
840     sb.append(cell.getSequenceId());
841     return sb.toString();
842   }
843 
844   /**
845    * This method exists just to encapsulate how we serialize keys.  To be replaced by a factory
846    * that we query to figure what the Cell implementation is and then, what serialization engine
847    * to use and further, how to serialize the key for inclusion in hfile index. TODO.
848    * @param cell
849    * @return The key portion of the Cell serialized in the old-school KeyValue way or null if
850    * passed a null <code>cell</code>
851    */
852   public static byte [] getCellKeySerializedAsKeyValueKey(final Cell cell) {
853     if (cell == null) return null;
854     byte [] b = new byte[KeyValueUtil.keyLength(cell)];
855     KeyValueUtil.appendKeyTo(cell, b, 0);
856     return b;
857   }
858 
859   /**
860    * Write rowkey excluding the common part.
861    * @param cell
862    * @param rLen
863    * @param commonPrefix
864    * @param out
865    * @throws IOException
866    */
867   public static void writeRowKeyExcludingCommon(Cell cell, short rLen, int commonPrefix,
868       DataOutputStream out) throws IOException {
869     if (commonPrefix == 0) {
870       out.writeShort(rLen);
871     } else if (commonPrefix == 1) {
872       out.writeByte((byte) rLen);
873       commonPrefix--;
874     } else {
875       commonPrefix -= KeyValue.ROW_LENGTH_SIZE;
876     }
877     if (rLen > commonPrefix) {
878       out.write(cell.getRowArray(), cell.getRowOffset() + commonPrefix, rLen - commonPrefix);
879     }
880   }
881 
882   /**
883    * Find length of common prefix in keys of the cells, considering key as byte[] if serialized in
884    * {@link KeyValue}. The key format is &lt;2 bytes rk len&gt;&lt;rk&gt;&lt;1 byte cf
885    * len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes timestamp&gt;&lt;1 byte type&gt;
886    * @param c1
887    *          the cell
888    * @param c2
889    *          the cell
890    * @param bypassFamilyCheck
891    *          when true assume the family bytes same in both cells. Pass it as true when dealing
892    *          with Cells in same CF so as to avoid some checks
893    * @param withTsType
894    *          when true check timestamp and type bytes also.
895    * @return length of common prefix
896    */
897   public static int findCommonPrefixInFlatKey(Cell c1, Cell c2, boolean bypassFamilyCheck,
898       boolean withTsType) {
899     // Compare the 2 bytes in RK length part
900     short rLen1 = c1.getRowLength();
901     short rLen2 = c2.getRowLength();
902     int commonPrefix = KeyValue.ROW_LENGTH_SIZE;
903     if (rLen1 != rLen2) {
904       // early out when the RK length itself is not matching
905       return ByteBufferUtils.findCommonPrefix(Bytes.toBytes(rLen1), 0, KeyValue.ROW_LENGTH_SIZE,
906           Bytes.toBytes(rLen2), 0, KeyValue.ROW_LENGTH_SIZE);
907     }
908     // Compare the RKs
909     int rkCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getRowArray(), c1.getRowOffset(),
910         rLen1, c2.getRowArray(), c2.getRowOffset(), rLen2);
911     commonPrefix += rkCommonPrefix;
912     if (rkCommonPrefix != rLen1) {
913       // Early out when RK is not fully matching.
914       return commonPrefix;
915     }
916     // Compare 1 byte CF length part
917     byte fLen1 = c1.getFamilyLength();
918     if (bypassFamilyCheck) {
919       // This flag will be true when caller is sure that the family will be same for both the cells
920       // Just make commonPrefix to increment by the family part
921       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE + fLen1;
922     } else {
923       byte fLen2 = c2.getFamilyLength();
924       if (fLen1 != fLen2) {
925         // early out when the CF length itself is not matching
926         return commonPrefix;
927       }
928       // CF lengths are same so there is one more byte common in key part
929       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE;
930       // Compare the CF names
931       int fCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getFamilyArray(),
932           c1.getFamilyOffset(), fLen1, c2.getFamilyArray(), c2.getFamilyOffset(), fLen2);
933       commonPrefix += fCommonPrefix;
934       if (fCommonPrefix != fLen1) {
935         return commonPrefix;
936       }
937     }
938     // Compare the Qualifiers
939     int qLen1 = c1.getQualifierLength();
940     int qLen2 = c2.getQualifierLength();
941     int qCommon = ByteBufferUtils.findCommonPrefix(c1.getQualifierArray(), c1.getQualifierOffset(),
942         qLen1, c2.getQualifierArray(), c2.getQualifierOffset(), qLen2);
943     commonPrefix += qCommon;
944     if (!withTsType || Math.max(qLen1, qLen2) != qCommon) {
945       return commonPrefix;
946     }
947     // Compare the timestamp parts
948     int tsCommonPrefix = ByteBufferUtils.findCommonPrefix(Bytes.toBytes(c1.getTimestamp()), 0,
949         KeyValue.TIMESTAMP_SIZE, Bytes.toBytes(c2.getTimestamp()), 0, KeyValue.TIMESTAMP_SIZE);
950     commonPrefix += tsCommonPrefix;
951     if (tsCommonPrefix != KeyValue.TIMESTAMP_SIZE) {
952       return commonPrefix;
953     }
954     // Compare the type
955     if (c1.getTypeByte() == c2.getTypeByte()) {
956       commonPrefix += KeyValue.TYPE_SIZE;
957     }
958     return commonPrefix;
959   }
960 
961   /** Returns a string representation of the cell */
962   public static String toString(Cell cell, boolean verbose) {
963     if (cell == null) {
964       return "";
965     }
966     StringBuilder builder = new StringBuilder();
967     String keyStr = getCellKeyAsString(cell);
968 
969     String tag = null;
970     String value = null;
971     if (verbose) {
972       // TODO: pretty print tags as well
973       tag = Bytes.toStringBinary(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
974       value = Bytes.toStringBinary(cell.getValueArray(), cell.getValueOffset(),
975         cell.getValueLength());
976     }
977 
978     builder
979       .append(keyStr);
980     if (tag != null && !tag.isEmpty()) {
981       builder.append("/").append(tag);
982     }
983     if (value != null) {
984       builder.append("/").append(value);
985     }
986 
987     return builder.toString();
988   }
989 
990   /**************** equals ****************************/
991 
992   public static boolean equals(Cell a, Cell b) {
993     return matchingRow(a, b) && matchingFamily(a, b) && matchingQualifier(a, b)
994         && matchingTimestamp(a, b) && matchingType(a, b);
995   }
996 
997   public static boolean matchingType(Cell a, Cell b) {
998     return a.getTypeByte() == b.getTypeByte();
999   }
1000 }