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