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   /**
572    * This is a hack that should be removed once we don't care about matching
573    * up client- and server-side estimations of cell size. It needed to be
574    * backwards compatible with estimations done by older clients. We need to
575    * pretend that tags never exist and cells aren't serialized with tag
576    * length included. See HBASE-13262 and HBASE-13303
577    */
578   @Deprecated
579   public static long estimatedHeapSizeOfWithoutTags(final Cell cell) {
580     if (cell instanceof KeyValue) {
581       return ((KeyValue)cell).heapSizeWithoutTags();
582     }
583     return getSumOfCellKeyElementLengths(cell) + cell.getValueLength();
584   }
585 
586   /********************* tags *************************************/
587   /**
588    * Util method to iterate through the tags
589    *
590    * @param tags
591    * @param offset
592    * @param length
593    * @return iterator for the tags
594    */
595   public static Iterator<Tag> tagsIterator(final byte[] tags, final int offset, final int length) {
596     return new Iterator<Tag>() {
597       private int pos = offset;
598       private int endOffset = offset + length - 1;
599 
600       @Override
601       public boolean hasNext() {
602         return this.pos < endOffset;
603       }
604 
605       @Override
606       public Tag next() {
607         if (hasNext()) {
608           int curTagLen = Bytes.readAsInt(tags, this.pos, Tag.TAG_LENGTH_SIZE);
609           Tag tag = new Tag(tags, pos, curTagLen + Tag.TAG_LENGTH_SIZE);
610           this.pos += Bytes.SIZEOF_SHORT + curTagLen;
611           return tag;
612         }
613         return null;
614       }
615 
616       @Override
617       public void remove() {
618         throw new UnsupportedOperationException();
619       }
620     };
621   }
622 
623   /**
624    * Returns true if the first range start1...end1 overlaps with the second range
625    * start2...end2, assuming the byte arrays represent row keys
626    */
627   public static boolean overlappingKeys(final byte[] start1, final byte[] end1,
628       final byte[] start2, final byte[] end2) {
629     return (end2.length == 0 || start1.length == 0 || Bytes.compareTo(start1,
630         end2) < 0)
631         && (end1.length == 0 || start2.length == 0 || Bytes.compareTo(start2,
632             end1) < 0);
633   }
634 
635   /**
636    * Sets the given seqId to the cell.
637    * @param cell
638    * @param seqId
639    * @throws IOException when the passed cell is not of type {@link SettableSequenceId}
640    */
641   public static void setSequenceId(Cell cell, long seqId) throws IOException {
642     if (cell instanceof SettableSequenceId) {
643       ((SettableSequenceId) cell).setSequenceId(seqId);
644     } else {
645       throw new IOException(new UnsupportedOperationException("Cell is not of type "
646           + SettableSequenceId.class.getName()));
647     }
648   }
649 
650   /**
651    * Sets the given timestamp to the cell.
652    * @param cell
653    * @param ts
654    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
655    */
656   public static void setTimestamp(Cell cell, long ts) throws IOException {
657     if (cell instanceof SettableTimestamp) {
658       ((SettableTimestamp) cell).setTimestamp(ts);
659     } else {
660       throw new IOException(new UnsupportedOperationException("Cell is not of type "
661           + SettableTimestamp.class.getName()));
662     }
663   }
664 
665   /**
666    * Sets the given timestamp to the cell.
667    * @param cell
668    * @param ts buffer containing the timestamp value
669    * @param tsOffset offset to the new timestamp
670    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
671    */
672   public static void setTimestamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
673     if (cell instanceof SettableTimestamp) {
674       ((SettableTimestamp) cell).setTimestamp(ts, tsOffset);
675     } else {
676       throw new IOException(new UnsupportedOperationException("Cell is not of type "
677           + SettableTimestamp.class.getName()));
678     }
679   }
680 
681   /**
682    * Sets the given timestamp to the cell iff current timestamp is
683    * {@link HConstants#LATEST_TIMESTAMP}.
684    * @param cell
685    * @param ts
686    * @return True if cell timestamp is modified.
687    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
688    */
689   public static boolean updateLatestStamp(Cell cell, long ts) throws IOException {
690     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
691       setTimestamp(cell, ts);
692       return true;
693     }
694     return false;
695   }
696 
697   /**
698    * Sets the given timestamp to the cell iff current timestamp is
699    * {@link HConstants#LATEST_TIMESTAMP}.
700    * @param cell
701    * @param ts buffer containing the timestamp value
702    * @param tsOffset offset to the new timestamp
703    * @return True if cell timestamp is modified.
704    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
705    */
706   public static boolean updateLatestStamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
707     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
708       setTimestamp(cell, ts, tsOffset);
709       return true;
710     }
711     return false;
712   }
713 
714   /**
715    * Writes the Cell's key part as it would have serialized in a KeyValue. The format is &lt;2 bytes
716    * rk len&gt;&lt;rk&gt;&lt;1 byte cf len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes
717    * timestamp&gt;&lt;1 byte type&gt;
718    * @param cell
719    * @param out
720    * @throws IOException
721    */
722   public static void writeFlatKey(Cell cell, DataOutputStream out) throws IOException {
723     short rowLen = cell.getRowLength();
724     out.writeShort(rowLen);
725     out.write(cell.getRowArray(), cell.getRowOffset(), rowLen);
726     byte fLen = cell.getFamilyLength();
727     out.writeByte(fLen);
728     out.write(cell.getFamilyArray(), cell.getFamilyOffset(), fLen);
729     out.write(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
730     out.writeLong(cell.getTimestamp());
731     out.writeByte(cell.getTypeByte());
732   }
733 
734   /**
735    * @param cell
736    * @return The Key portion of the passed <code>cell</code> as a String.
737    */
738   public static String getCellKeyAsString(Cell cell) {
739     StringBuilder sb = new StringBuilder(Bytes.toStringBinary(
740       cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
741     sb.append('/');
742     sb.append(cell.getFamilyLength() == 0? "":
743       Bytes.toStringBinary(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()));
744     // KeyValue only added ':' if family is non-null.  Do same.
745     if (cell.getFamilyLength() > 0) sb.append(':');
746     sb.append(cell.getQualifierLength() == 0? "":
747       Bytes.toStringBinary(cell.getQualifierArray(), cell.getQualifierOffset(),
748         cell.getQualifierLength()));
749     sb.append('/');
750     sb.append(KeyValue.humanReadableTimestamp(cell.getTimestamp()));
751     sb.append('/');
752     sb.append(Type.codeToType(cell.getTypeByte()));
753     sb.append("/vlen=");
754     sb.append(cell.getValueLength());
755     sb.append("/seqid=");
756     sb.append(cell.getSequenceId());
757     return sb.toString();
758   }
759 
760   /**
761    * This method exists just to encapsulate how we serialize keys.  To be replaced by a factory
762    * that we query to figure what the Cell implementation is and then, what serialization engine
763    * to use and further, how to serialize the key for inclusion in hfile index. TODO.
764    * @param cell
765    * @return The key portion of the Cell serialized in the old-school KeyValue way or null if
766    * passed a null <code>cell</code>
767    */
768   public static byte [] getCellKeySerializedAsKeyValueKey(final Cell cell) {
769     if (cell == null) return null;
770     byte [] b = new byte[KeyValueUtil.keyLength(cell)];
771     KeyValueUtil.appendKeyTo(cell, b, 0);
772     return b;
773   }
774 
775   /**
776    * Write rowkey excluding the common part.
777    * @param cell
778    * @param rLen
779    * @param commonPrefix
780    * @param out
781    * @throws IOException
782    */
783   public static void writeRowKeyExcludingCommon(Cell cell, short rLen, int commonPrefix,
784       DataOutputStream out) throws IOException {
785     if (commonPrefix == 0) {
786       out.writeShort(rLen);
787     } else if (commonPrefix == 1) {
788       out.writeByte((byte) rLen);
789       commonPrefix--;
790     } else {
791       commonPrefix -= KeyValue.ROW_LENGTH_SIZE;
792     }
793     if (rLen > commonPrefix) {
794       out.write(cell.getRowArray(), cell.getRowOffset() + commonPrefix, rLen - commonPrefix);
795     }
796   }
797 
798   /**
799    * Find length of common prefix in keys of the cells, considering key as byte[] if serialized in
800    * {@link KeyValue}. The key format is &lt;2 bytes rk len&gt;&lt;rk&gt;&lt;1 byte cf
801    * len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes timestamp&gt;&lt;1 byte type&gt;
802    * @param c1
803    *          the cell
804    * @param c2
805    *          the cell
806    * @param bypassFamilyCheck
807    *          when true assume the family bytes same in both cells. Pass it as true when dealing
808    *          with Cells in same CF so as to avoid some checks
809    * @param withTsType
810    *          when true check timestamp and type bytes also.
811    * @return length of common prefix
812    */
813   public static int findCommonPrefixInFlatKey(Cell c1, Cell c2, boolean bypassFamilyCheck,
814       boolean withTsType) {
815     // Compare the 2 bytes in RK length part
816     short rLen1 = c1.getRowLength();
817     short rLen2 = c2.getRowLength();
818     int commonPrefix = KeyValue.ROW_LENGTH_SIZE;
819     if (rLen1 != rLen2) {
820       // early out when the RK length itself is not matching
821       return ByteBufferUtils.findCommonPrefix(Bytes.toBytes(rLen1), 0, KeyValue.ROW_LENGTH_SIZE,
822           Bytes.toBytes(rLen2), 0, KeyValue.ROW_LENGTH_SIZE);
823     }
824     // Compare the RKs
825     int rkCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getRowArray(), c1.getRowOffset(),
826         rLen1, c2.getRowArray(), c2.getRowOffset(), rLen2);
827     commonPrefix += rkCommonPrefix;
828     if (rkCommonPrefix != rLen1) {
829       // Early out when RK is not fully matching.
830       return commonPrefix;
831     }
832     // Compare 1 byte CF length part
833     byte fLen1 = c1.getFamilyLength();
834     if (bypassFamilyCheck) {
835       // This flag will be true when caller is sure that the family will be same for both the cells
836       // Just make commonPrefix to increment by the family part
837       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE + fLen1;
838     } else {
839       byte fLen2 = c2.getFamilyLength();
840       if (fLen1 != fLen2) {
841         // early out when the CF length itself is not matching
842         return commonPrefix;
843       }
844       // CF lengths are same so there is one more byte common in key part
845       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE;
846       // Compare the CF names
847       int fCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getFamilyArray(),
848           c1.getFamilyOffset(), fLen1, c2.getFamilyArray(), c2.getFamilyOffset(), fLen2);
849       commonPrefix += fCommonPrefix;
850       if (fCommonPrefix != fLen1) {
851         return commonPrefix;
852       }
853     }
854     // Compare the Qualifiers
855     int qLen1 = c1.getQualifierLength();
856     int qLen2 = c2.getQualifierLength();
857     int qCommon = ByteBufferUtils.findCommonPrefix(c1.getQualifierArray(), c1.getQualifierOffset(),
858         qLen1, c2.getQualifierArray(), c2.getQualifierOffset(), qLen2);
859     commonPrefix += qCommon;
860     if (!withTsType || Math.max(qLen1, qLen2) != qCommon) {
861       return commonPrefix;
862     }
863     // Compare the timestamp parts
864     int tsCommonPrefix = ByteBufferUtils.findCommonPrefix(Bytes.toBytes(c1.getTimestamp()), 0,
865         KeyValue.TIMESTAMP_SIZE, Bytes.toBytes(c2.getTimestamp()), 0, KeyValue.TIMESTAMP_SIZE);
866     commonPrefix += tsCommonPrefix;
867     if (tsCommonPrefix != KeyValue.TIMESTAMP_SIZE) {
868       return commonPrefix;
869     }
870     // Compare the type
871     if (c1.getTypeByte() == c2.getTypeByte()) {
872       commonPrefix += KeyValue.TYPE_SIZE;
873     }
874     return commonPrefix;
875   }
876 
877   /** Returns a string representation of the cell */
878   public static String toString(Cell cell, boolean verbose) {
879     if (cell == null) {
880       return "";
881     }
882     StringBuilder builder = new StringBuilder();
883     String keyStr = getCellKeyAsString(cell);
884 
885     String tag = null;
886     String value = null;
887     if (verbose) {
888       // TODO: pretty print tags as well
889       tag = Bytes.toStringBinary(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
890       value = Bytes.toStringBinary(cell.getValueArray(), cell.getValueOffset(),
891         cell.getValueLength());
892     }
893 
894     builder
895       .append(keyStr);
896     if (tag != null && !tag.isEmpty()) {
897       builder.append("/").append(tag);
898     }
899     if (value != null) {
900       builder.append("/").append(value);
901     }
902 
903     return builder.toString();
904   }
905 }