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