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