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