View Javadoc

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