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.math.BigDecimal;
26  import java.nio.ByteBuffer;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map.Entry;
30  import java.util.NavigableMap;
31  
32  import org.apache.hadoop.hbase.KeyValue.Type;
33  import org.apache.hadoop.hbase.classification.InterfaceAudience;
34  import org.apache.hadoop.hbase.classification.InterfaceStability;
35  import org.apache.hadoop.hbase.io.HeapSize;
36  import org.apache.hadoop.hbase.util.ByteBufferUtils;
37  import org.apache.hadoop.hbase.util.ByteRange;
38  import org.apache.hadoop.hbase.util.Bytes;
39  
40  /**
41   * Utility methods helpful slinging {@link Cell} instances.
42   * Some methods below are for internal use only and are marked InterfaceAudience.Private at the
43   * method level.
44   */
45  @InterfaceAudience.Public
46  @InterfaceStability.Evolving
47  public final class CellUtil {
48  
49    /**
50     * Private constructor to keep this class from being instantiated.
51     */
52    private CellUtil(){}
53  
54    /******************* ByteRange *******************************/
55  
56    public static ByteRange fillRowRange(Cell cell, ByteRange range) {
57      return range.set(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
58    }
59  
60    public static ByteRange fillFamilyRange(Cell cell, ByteRange range) {
61      return range.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
62    }
63  
64    public static ByteRange fillQualifierRange(Cell cell, ByteRange range) {
65      return range.set(cell.getQualifierArray(), cell.getQualifierOffset(),
66        cell.getQualifierLength());
67    }
68  
69    public static ByteRange fillValueRange(Cell cell, ByteRange range) {
70      return range.set(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
71    }
72  
73    public static ByteRange fillTagRange(Cell cell, ByteRange range) {
74      return range.set(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
75    }
76  
77    /***************** get individual arrays for tests ************/
78  
79    public static byte[] cloneRow(Cell cell){
80      byte[] output = new byte[cell.getRowLength()];
81      copyRowTo(cell, output, 0);
82      return output;
83    }
84  
85    public static byte[] cloneFamily(Cell cell){
86      byte[] output = new byte[cell.getFamilyLength()];
87      copyFamilyTo(cell, output, 0);
88      return output;
89    }
90  
91    public static byte[] cloneQualifier(Cell cell){
92      byte[] output = new byte[cell.getQualifierLength()];
93      copyQualifierTo(cell, output, 0);
94      return output;
95    }
96  
97    public static byte[] cloneValue(Cell cell){
98      byte[] output = new byte[cell.getValueLength()];
99      copyValueTo(cell, output, 0);
100     return output;
101   }
102 
103   public static byte[] cloneTags(Cell cell) {
104     byte[] output = new byte[cell.getTagsLength()];
105     copyTagTo(cell, output, 0);
106     return output;
107   }
108 
109   /**
110    * Returns tag value in a new byte array. If server-side, use
111    * {@link Tag#getBuffer()} with appropriate {@link Tag#getTagOffset()} and
112    * {@link Tag#getTagLength()} instead to save on allocations.
113    * @param cell
114    * @return tag value in a new byte array.
115    */
116   public static byte[] getTagArray(Cell cell){
117     byte[] output = new byte[cell.getTagsLength()];
118     copyTagTo(cell, output, 0);
119     return output;
120   }
121 
122 
123   /******************** copyTo **********************************/
124 
125   public static int copyRowTo(Cell cell, byte[] destination, int destinationOffset) {
126     short rowLen = cell.getRowLength();
127     if (cell instanceof ByteBufferedCell) {
128       ByteBufferUtils.copyFromBufferToArray(destination,
129           ((ByteBufferedCell) cell).getRowByteBuffer(),
130           ((ByteBufferedCell) cell).getRowPositionInByteBuffer(), destinationOffset, rowLen);
131     } else {
132       System.arraycopy(cell.getRowArray(), cell.getRowOffset(), destination, destinationOffset,
133           rowLen);
134     }
135     return destinationOffset + rowLen;
136   }
137 
138   public static int copyFamilyTo(Cell cell, byte[] destination, int destinationOffset) {
139     byte fLen = cell.getFamilyLength();
140     if (cell instanceof ByteBufferedCell) {
141       ByteBufferUtils.copyFromBufferToArray(destination,
142           ((ByteBufferedCell) cell).getFamilyByteBuffer(),
143           ((ByteBufferedCell) cell).getFamilyPositionInByteBuffer(), destinationOffset, fLen);
144     } else {
145       System.arraycopy(cell.getFamilyArray(), cell.getFamilyOffset(), destination,
146           destinationOffset, fLen);
147     }
148     return destinationOffset + fLen;
149   }
150 
151   public static int copyQualifierTo(Cell cell, byte[] destination, int destinationOffset) {
152     int qlen = cell.getQualifierLength();
153     if (cell instanceof ByteBufferedCell) {
154       ByteBufferUtils.copyFromBufferToArray(destination,
155           ((ByteBufferedCell) cell).getQualifierByteBuffer(),
156           ((ByteBufferedCell) cell).getQualifierPositionInByteBuffer(), destinationOffset, qlen);
157     } else {
158       System.arraycopy(cell.getQualifierArray(), cell.getQualifierOffset(), destination,
159           destinationOffset, qlen);
160     }
161     return destinationOffset + qlen;
162   }
163 
164   public static int copyValueTo(Cell cell, byte[] destination, int destinationOffset) {
165     int vlen = cell.getValueLength();
166     if (cell instanceof ByteBufferedCell) {
167       ByteBufferUtils.copyFromBufferToArray(destination,
168           ((ByteBufferedCell) cell).getValueByteBuffer(),
169           ((ByteBufferedCell) cell).getValuePositionInByteBuffer(), destinationOffset, vlen);
170     } else {
171       System.arraycopy(cell.getValueArray(), cell.getValueOffset(), destination, destinationOffset,
172           vlen);
173     }
174     return destinationOffset + vlen;
175   }
176 
177   /**
178    * Copies the tags info into the tag portion of the cell
179    * @param cell
180    * @param destination
181    * @param destinationOffset
182    * @return position after tags
183    */
184   public static int copyTagTo(Cell cell, byte[] destination, int destinationOffset) {
185     int tlen = cell.getTagsLength();
186     if (cell instanceof ByteBufferedCell) {
187       ByteBufferUtils.copyFromBufferToArray(destination,
188           ((ByteBufferedCell) cell).getTagsByteBuffer(),
189           ((ByteBufferedCell) cell).getTagsPositionInByteBuffer(), destinationOffset, tlen);
190     } else {
191       System.arraycopy(cell.getTagsArray(), cell.getTagsOffset(), destination, destinationOffset,
192           tlen);
193     }
194     return destinationOffset + tlen;
195   }
196 
197   /********************* misc *************************************/
198 
199   public static byte getRowByte(Cell cell, int index) {
200     if (cell instanceof ByteBufferedCell) {
201       return ((ByteBufferedCell) cell).getRowByteBuffer().get(
202           ((ByteBufferedCell) cell).getRowPositionInByteBuffer() + index);
203     }
204     return cell.getRowArray()[cell.getRowOffset() + index];
205   }
206 
207   public static ByteBuffer getValueBufferShallowCopy(Cell cell) {
208     ByteBuffer buffer = ByteBuffer.wrap(cell.getValueArray(), cell.getValueOffset(),
209       cell.getValueLength());
210     return buffer;
211   }
212 
213   /**
214    * @param cell
215    * @return cell's qualifier wrapped into a ByteBuffer.
216    * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
217    */
218   @Deprecated
219   public static ByteBuffer getQualifierBufferShallowCopy(Cell cell) {
220     // No usage of this in code.
221     ByteBuffer buffer = ByteBuffer.wrap(cell.getQualifierArray(), cell.getQualifierOffset(),
222         cell.getQualifierLength());
223     return buffer;
224   }
225 
226   public static Cell createCell(final byte [] row, final byte [] family, final byte [] qualifier,
227       final long timestamp, final byte type, final byte [] value) {
228     // I need a Cell Factory here.  Using KeyValue for now. TODO.
229     // TODO: Make a new Cell implementation that just carries these
230     // byte arrays.
231     // TODO: Call factory to create Cell
232     return new KeyValue(row, family, qualifier, timestamp, KeyValue.Type.codeToType(type), value);
233   }
234 
235   public static Cell createCell(final byte [] rowArray, final int rowOffset, final int rowLength,
236       final byte [] familyArray, final int familyOffset, final int familyLength,
237       final byte [] qualifierArray, final int qualifierOffset, final int qualifierLength) {
238     // See createCell(final byte [] row, final byte [] value) for why we default Maximum type.
239     return new KeyValue(rowArray, rowOffset, rowLength,
240         familyArray, familyOffset, familyLength,
241         qualifierArray, qualifierOffset, qualifierLength,
242         HConstants.LATEST_TIMESTAMP,
243         KeyValue.Type.Maximum,
244         HConstants.EMPTY_BYTE_ARRAY, 0, HConstants.EMPTY_BYTE_ARRAY.length);
245   }
246 
247   /**
248    * Marked as audience Private as of 1.2.0.
249    * Creating a Cell with a memstoreTS/mvcc is an internal implementation detail not for
250    * public use.
251    */
252   @InterfaceAudience.Private
253   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
254       final long timestamp, final byte type, final byte[] value, final long memstoreTS) {
255     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp,
256         KeyValue.Type.codeToType(type), value);
257     keyValue.setSequenceId(memstoreTS);
258     return keyValue;
259   }
260 
261   /**
262    * Marked as audience Private as of 1.2.0.
263    * Creating a Cell with tags and a memstoreTS/mvcc is an internal implementation detail not for
264    * public use.
265    */
266   @InterfaceAudience.Private
267   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
268       final long timestamp, final byte type, final byte[] value, byte[] tags,
269       final long memstoreTS) {
270     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp,
271         KeyValue.Type.codeToType(type), value, tags);
272     keyValue.setSequenceId(memstoreTS);
273     return keyValue;
274   }
275 
276   /**
277    * Marked as audience Private as of 1.2.0.
278    * Creating a Cell with tags is an internal implementation detail not for
279    * public use.
280    */
281   @InterfaceAudience.Private
282   public static Cell createCell(final byte[] row, final byte[] family, final byte[] qualifier,
283       final long timestamp, Type type, final byte[] value, byte[] tags) {
284     KeyValue keyValue = new KeyValue(row, family, qualifier, timestamp, type, value, tags);
285     return keyValue;
286   }
287 
288   /**
289    * Create a Cell with specific row.  Other fields defaulted.
290    * @param row
291    * @return Cell with passed row but all other fields are arbitrary
292    */
293   public static Cell createCell(final byte [] row) {
294     return createCell(row, HConstants.EMPTY_BYTE_ARRAY);
295   }
296 
297   /**
298    * Create a Cell with specific row and value.  Other fields are defaulted.
299    * @param row
300    * @param value
301    * @return Cell with passed row and value but all other fields are arbitrary
302    */
303   public static Cell createCell(final byte [] row, final byte [] value) {
304     // An empty family + empty qualifier + Type.Minimum is used as flag to indicate last on row.
305     // See the CellComparator and KeyValue comparator.  Search for compareWithoutRow.
306     // Lets not make a last-on-row key as default but at same time, if you are making a key
307     // without specifying type, etc., flag it as weird by setting type to be Maximum.
308     return createCell(row, HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY,
309       HConstants.LATEST_TIMESTAMP, KeyValue.Type.Maximum.getCode(), value);
310   }
311 
312   /**
313    * Create a Cell with specific row.  Other fields defaulted.
314    * @param row
315    * @param family
316    * @param qualifier
317    * @return Cell with passed row but all other fields are arbitrary
318    */
319   public static Cell createCell(final byte [] row, final byte [] family, final byte [] qualifier) {
320     // See above in createCell(final byte [] row, final byte [] value) why we set type to Maximum.
321     return createCell(row, family, qualifier,
322         HConstants.LATEST_TIMESTAMP, KeyValue.Type.Maximum.getCode(), HConstants.EMPTY_BYTE_ARRAY);
323   }
324 
325   /**
326    * @param cellScannerables
327    * @return CellScanner interface over <code>cellIterables</code>
328    */
329   public static CellScanner createCellScanner(
330       final List<? extends CellScannable> cellScannerables) {
331     return new CellScanner() {
332       private final Iterator<? extends CellScannable> iterator = cellScannerables.iterator();
333       private CellScanner cellScanner = null;
334 
335       @Override
336       public Cell current() {
337         return this.cellScanner != null? this.cellScanner.current(): null;
338       }
339 
340       @Override
341       public boolean advance() throws IOException {
342         while (true) {
343           if (this.cellScanner == null) {
344             if (!this.iterator.hasNext()) return false;
345             this.cellScanner = this.iterator.next().cellScanner();
346           }
347           if (this.cellScanner.advance()) return true;
348           this.cellScanner = null;
349         }
350       }
351     };
352   }
353 
354   /**
355    * @param cellIterable
356    * @return CellScanner interface over <code>cellIterable</code>
357    */
358   public static CellScanner createCellScanner(final Iterable<Cell> cellIterable) {
359     if (cellIterable == null) return null;
360     return createCellScanner(cellIterable.iterator());
361   }
362 
363   /**
364    * @param cells
365    * @return CellScanner interface over <code>cellIterable</code> or null if <code>cells</code> is
366    * null
367    */
368   public static CellScanner createCellScanner(final Iterator<Cell> cells) {
369     if (cells == null) return null;
370     return new CellScanner() {
371       private final Iterator<Cell> iterator = cells;
372       private Cell current = null;
373 
374       @Override
375       public Cell current() {
376         return this.current;
377       }
378 
379       @Override
380       public boolean advance() {
381         boolean hasNext = this.iterator.hasNext();
382         this.current = hasNext? this.iterator.next(): null;
383         return hasNext;
384       }
385     };
386   }
387 
388   /**
389    * @param cellArray
390    * @return CellScanner interface over <code>cellArray</code>
391    */
392   public static CellScanner createCellScanner(final Cell[] cellArray) {
393     return new CellScanner() {
394       private final Cell [] cells = cellArray;
395       private int index = -1;
396 
397       @Override
398       public Cell current() {
399         if (cells == null) return null;
400         return (index < 0)? null: this.cells[index];
401       }
402 
403       @Override
404       public boolean advance() {
405         if (cells == null) return false;
406         return ++index < this.cells.length;
407       }
408     };
409   }
410 
411   /**
412    * Flatten the map of cells out under the CellScanner
413    * @param map Map of Cell Lists; for example, the map of families to Cells that is used
414    * inside Put, etc., keeping Cells organized by family.
415    * @return CellScanner interface over <code>cellIterable</code>
416    */
417   public static CellScanner createCellScanner(final NavigableMap<byte [], List<Cell>> map) {
418     return new CellScanner() {
419       private final Iterator<Entry<byte[], List<Cell>>> entries = map.entrySet().iterator();
420       private Iterator<Cell> currentIterator = null;
421       private Cell currentCell;
422 
423       @Override
424       public Cell current() {
425         return this.currentCell;
426       }
427 
428       @Override
429       public boolean advance() {
430         while(true) {
431           if (this.currentIterator == null) {
432             if (!this.entries.hasNext()) return false;
433             this.currentIterator = this.entries.next().getValue().iterator();
434           }
435           if (this.currentIterator.hasNext()) {
436             this.currentCell = this.currentIterator.next();
437             return true;
438           }
439           this.currentCell = null;
440           this.currentIterator = null;
441         }
442       }
443     };
444   }
445 
446   /**
447    * @param left
448    * @param right
449    * @return True if the rows in <code>left</code> and <code>right</code> Cells match
450    * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
451    *             Instead use {@link #matchingRows(Cell, Cell)}
452    */
453   @Deprecated
454   public static boolean matchingRow(final Cell left, final Cell right) {
455     return matchingRows(left, right);
456   }
457 
458   public static boolean matchingRow(final Cell left, final byte[] buf) {
459     if (buf == null) {
460       return left.getQualifierLength() == 0;
461     }
462     return matchingRow(left, buf, 0, buf.length);
463   }
464 
465   public static boolean matchingRow(final Cell left, final byte[] buf, final int offset,
466       final int length) {
467     if (left instanceof ByteBufferedCell) {
468       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getRowByteBuffer(),
469           ((ByteBufferedCell) left).getRowPositionInByteBuffer(), left.getRowLength(), buf, offset,
470           length) == 0;
471     }
472     return Bytes.equals(left.getRowArray(), left.getRowOffset(), left.getRowLength(), buf, offset,
473         length);
474   }
475 
476   public static boolean matchingFamily(final Cell left, final Cell right) {
477     byte lfamlength = left.getFamilyLength();
478     byte rfamlength = right.getFamilyLength();
479     if (lfamlength != rfamlength) return false;
480     if (left instanceof ByteBufferedCell && right instanceof ByteBufferedCell) {
481       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getFamilyByteBuffer(),
482           ((ByteBufferedCell) left).getFamilyPositionInByteBuffer(), lfamlength,
483           ((ByteBufferedCell) right).getFamilyByteBuffer(),
484           ((ByteBufferedCell) right).getFamilyPositionInByteBuffer(), rfamlength) == 0;
485     }
486     if (left instanceof ByteBufferedCell) {
487       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getFamilyByteBuffer(),
488           ((ByteBufferedCell) left).getFamilyPositionInByteBuffer(), lfamlength,
489           right.getFamilyArray(), right.getFamilyOffset(), rfamlength) == 0;
490     }
491     if (right instanceof ByteBufferedCell) {
492       return ByteBufferUtils.compareTo(((ByteBufferedCell) right).getFamilyByteBuffer(),
493           ((ByteBufferedCell) right).getFamilyPositionInByteBuffer(), rfamlength,
494           left.getFamilyArray(), left.getFamilyOffset(), lfamlength) == 0;
495     }
496     return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(),
497         right.getFamilyArray(), right.getFamilyOffset(), right.getFamilyLength());
498   }
499 
500   public static boolean matchingFamily(final Cell left, final byte[] buf) {
501     if (buf == null) {
502       return left.getFamilyLength() == 0;
503     }
504     return matchingFamily(left, buf, 0, buf.length);
505   }
506 
507   public static boolean matchingFamily(final Cell left, final byte[] buf, final int offset,
508       final int length) {
509     if (left instanceof ByteBufferedCell) {
510       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getFamilyByteBuffer(),
511           ((ByteBufferedCell) left).getFamilyPositionInByteBuffer(), left.getFamilyLength(), buf,
512           offset, length) == 0;
513     }
514     return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(), buf,
515         offset, length);
516   }
517 
518   public static boolean matchingQualifier(final Cell left, final Cell right) {
519     int lqlength = left.getQualifierLength();
520     int rqlength = right.getQualifierLength();
521     if (lqlength != rqlength) return false;
522     if (left instanceof ByteBufferedCell && right instanceof ByteBufferedCell) {
523       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getQualifierByteBuffer(),
524           ((ByteBufferedCell) left).getQualifierPositionInByteBuffer(), lqlength,
525           ((ByteBufferedCell) right).getQualifierByteBuffer(),
526           ((ByteBufferedCell) right).getQualifierPositionInByteBuffer(), rqlength) == 0;
527     }
528     if (left instanceof ByteBufferedCell) {
529       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getQualifierByteBuffer(),
530           ((ByteBufferedCell) left).getQualifierPositionInByteBuffer(), lqlength,
531           right.getQualifierArray(), right.getQualifierOffset(), rqlength) == 0;
532     }
533     if (right instanceof ByteBufferedCell) {
534       return ByteBufferUtils.compareTo(((ByteBufferedCell) right).getQualifierByteBuffer(),
535           ((ByteBufferedCell) right).getQualifierPositionInByteBuffer(), rqlength,
536           left.getQualifierArray(), left.getQualifierOffset(), lqlength) == 0;
537     }
538     return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(),
539         left.getQualifierLength(), right.getQualifierArray(), right.getQualifierOffset(),
540         right.getQualifierLength());
541   }
542 
543   /**
544    * Finds if the qualifier part of the cell and the KV serialized
545    * byte[] are equal
546    * @param left
547    * @param buf the serialized keyvalue format byte[]
548    * @return true if the qualifier matches, false otherwise
549    */
550   public static boolean matchingQualifier(final Cell left, final byte[] buf) {
551     if (buf == null) {
552       return left.getQualifierLength() == 0;
553     }
554     return matchingQualifier(left, buf, 0, buf.length);
555   }
556 
557   /**
558    * Finds if the qualifier part of the cell and the KV serialized
559    * byte[] are equal
560    * @param left
561    * @param buf the serialized keyvalue format byte[]
562    * @param offset the offset of the qualifier in the byte[]
563    * @param length the length of the qualifier in the byte[]
564    * @return true if the qualifier matches, false otherwise
565    */
566   public static boolean matchingQualifier(final Cell left, final byte[] buf, final int offset,
567       final int length) {
568     if (buf == null) {
569       return left.getQualifierLength() == 0;
570     }
571     if (left instanceof ByteBufferedCell) {
572       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getQualifierByteBuffer(),
573           ((ByteBufferedCell) left).getQualifierPositionInByteBuffer(), left.getQualifierLength(),
574           buf, offset, length) == 0;
575     }
576     return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(),
577         left.getQualifierLength(), buf, offset, length);
578   }
579 
580   public static boolean matchingColumn(final Cell left, final byte[] fam, final byte[] qual) {
581     if (!matchingFamily(left, fam))
582       return false;
583     return matchingQualifier(left, qual);
584   }
585 
586   public static boolean matchingColumn(final Cell left, final byte[] fam, final int foffset,
587       final int flength, final byte[] qual, final int qoffset, final int qlength) {
588     if (!matchingFamily(left, fam, foffset, flength))
589       return false;
590     return matchingQualifier(left, qual, qoffset, qlength);
591   }
592 
593   public static boolean matchingColumn(final Cell left, final Cell right) {
594     if (!matchingFamily(left, right))
595       return false;
596     return matchingQualifier(left, right);
597   }
598 
599   public static boolean matchingValue(final Cell left, final Cell right) {
600     int lvlength = left.getValueLength();
601     int rvlength = right.getValueLength();
602     if (lvlength != rvlength) return false;
603     if (left instanceof ByteBufferedCell && right instanceof ByteBufferedCell) {
604       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getValueByteBuffer(),
605           ((ByteBufferedCell) left).getValuePositionInByteBuffer(), lvlength,
606           ((ByteBufferedCell) right).getValueByteBuffer(),
607           ((ByteBufferedCell) right).getValuePositionInByteBuffer(), rvlength) == 0;
608     }
609     if (left instanceof ByteBufferedCell) {
610       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getValueByteBuffer(),
611           ((ByteBufferedCell) left).getValuePositionInByteBuffer(), lvlength,
612           right.getValueArray(), right.getValueOffset(), rvlength) == 0;
613     }
614     if (right instanceof ByteBufferedCell) {
615       return ByteBufferUtils.compareTo(((ByteBufferedCell) right).getValueByteBuffer(),
616           ((ByteBufferedCell) right).getValuePositionInByteBuffer(), rvlength,
617           left.getValueArray(), left.getValueOffset(), lvlength) == 0;
618     }
619     return Bytes.equals(left.getValueArray(), left.getValueOffset(), lvlength,
620         right.getValueArray(), right.getValueOffset(), rvlength);
621   }
622 
623   public static boolean matchingValue(final Cell left, final byte[] buf) {
624     if (left instanceof ByteBufferedCell) {
625       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getValueByteBuffer(),
626           ((ByteBufferedCell) left).getValuePositionInByteBuffer(), left.getValueLength(), buf, 0,
627           buf.length) == 0;
628     }
629     return Bytes.equals(left.getValueArray(), left.getValueOffset(), left.getValueLength(), buf, 0,
630         buf.length);
631   }
632 
633   /**
634    * @return True if a delete type, a {@link KeyValue.Type#Delete} or a
635    *         {KeyValue.Type#DeleteFamily} or a
636    *         {@link KeyValue.Type#DeleteColumn} KeyValue type.
637    */
638   public static boolean isDelete(final Cell cell) {
639     return isDelete(cell.getTypeByte());
640   }
641 
642   /**
643    * @return True if a delete type, a {@link KeyValue.Type#Delete} or a
644    *         {KeyValue.Type#DeleteFamily} or a
645    *         {@link KeyValue.Type#DeleteColumn} KeyValue type.
646    */
647   public static boolean isDelete(final byte type) {
648     return Type.Delete.getCode() <= type
649         && type <= Type.DeleteFamily.getCode();
650   }
651 
652   /**
653    * @return True if this cell is a {@link KeyValue.Type#Delete} type.
654    */
655   public static boolean isDeleteType(Cell cell) {
656     return cell.getTypeByte() == Type.Delete.getCode();
657   }
658 
659   public static boolean isDeleteFamily(final Cell cell) {
660     return cell.getTypeByte() == Type.DeleteFamily.getCode();
661   }
662 
663   public static boolean isDeleteFamilyVersion(final Cell cell) {
664     return cell.getTypeByte() == Type.DeleteFamilyVersion.getCode();
665   }
666 
667   public static boolean isDeleteColumns(final Cell cell) {
668     return cell.getTypeByte() == Type.DeleteColumn.getCode();
669   }
670 
671   public static boolean isDeleteColumnVersion(final Cell cell) {
672     return cell.getTypeByte() == Type.Delete.getCode();
673   }
674 
675   /**
676    *
677    * @return True if this cell is a delete family or column type.
678    */
679   public static boolean isDeleteColumnOrFamily(Cell cell) {
680     int t = cell.getTypeByte();
681     return t == Type.DeleteColumn.getCode() || t == Type.DeleteFamily.getCode();
682   }
683 
684   /**
685    * @param cell
686    * @return Estimate of the <code>cell</code> size in bytes.
687    */
688   public static int estimatedSerializedSizeOf(final Cell cell) {
689     // If a KeyValue, we can give a good estimate of size.
690     if (cell instanceof KeyValue) {
691       return ((KeyValue)cell).getLength() + Bytes.SIZEOF_INT;
692     }
693     // TODO: Should we add to Cell a sizeOf?  Would it help? Does it make sense if Cell is
694     // prefix encoded or compressed?
695     return getSumOfCellElementLengths(cell) +
696       // Use the KeyValue's infrastructure size presuming that another implementation would have
697       // same basic cost.
698       KeyValue.KEY_INFRASTRUCTURE_SIZE +
699       // Serialization is probably preceded by a length (it is in the KeyValueCodec at least).
700       Bytes.SIZEOF_INT;
701   }
702 
703   /**
704    * @param cell
705    * @return Sum of the lengths of all the elements in a Cell; does not count in any infrastructure
706    */
707   private static int getSumOfCellElementLengths(final Cell cell) {
708     return getSumOfCellKeyElementLengths(cell) + cell.getValueLength() + cell.getTagsLength();
709   }
710 
711   /**
712    * @param cell
713    * @return Sum of all elements that make up a key; does not include infrastructure, tags or
714    * values.
715    */
716   private static int getSumOfCellKeyElementLengths(final Cell cell) {
717     return cell.getRowLength() + cell.getFamilyLength() +
718     cell.getQualifierLength() +
719     KeyValue.TIMESTAMP_TYPE_SIZE;
720   }
721 
722   public static int estimatedSerializedSizeOfKey(final Cell cell) {
723     if (cell instanceof KeyValue) return ((KeyValue)cell).getKeyLength();
724     // This will be a low estimate.  Will do for now.
725     return getSumOfCellKeyElementLengths(cell);
726   }
727 
728   /**
729    * This is an estimate of the heap space occupied by a cell. When the cell is of type
730    * {@link HeapSize} we call {@link HeapSize#heapSize()} so cell can give a correct value. In other
731    * cases we just consider the bytes occupied by the cell components ie. row, CF, qualifier,
732    * timestamp, type, value and tags.
733    * @param cell
734    * @return estimate of the heap space
735    */
736   public static long estimatedHeapSizeOf(final Cell cell) {
737     if (cell instanceof HeapSize) {
738       return ((HeapSize) cell).heapSize();
739     }
740     // TODO: Add sizing of references that hold the row, family, etc., arrays.
741     return estimatedSerializedSizeOf(cell);
742   }
743 
744   /********************* tags *************************************/
745   /**
746    * Util method to iterate through the tags
747    *
748    * @param tags
749    * @param offset
750    * @param length
751    * @return iterator for the tags
752    */
753   public static Iterator<Tag> tagsIterator(final byte[] tags, final int offset, final int length) {
754     return new Iterator<Tag>() {
755       private int pos = offset;
756       private int endOffset = offset + length - 1;
757 
758       @Override
759       public boolean hasNext() {
760         return this.pos < endOffset;
761       }
762 
763       @Override
764       public Tag next() {
765         if (hasNext()) {
766           int curTagLen = Bytes.readAsInt(tags, this.pos, Tag.TAG_LENGTH_SIZE);
767           Tag tag = new Tag(tags, pos, curTagLen + Tag.TAG_LENGTH_SIZE);
768           this.pos += Bytes.SIZEOF_SHORT + curTagLen;
769           return tag;
770         }
771         return null;
772       }
773 
774       @Override
775       public void remove() {
776         throw new UnsupportedOperationException();
777       }
778     };
779   }
780 
781   /**
782    * Returns true if the first range start1...end1 overlaps with the second range
783    * start2...end2, assuming the byte arrays represent row keys
784    */
785   public static boolean overlappingKeys(final byte[] start1, final byte[] end1,
786       final byte[] start2, final byte[] end2) {
787     return (end2.length == 0 || start1.length == 0 || Bytes.compareTo(start1,
788         end2) < 0)
789         && (end1.length == 0 || start2.length == 0 || Bytes.compareTo(start2,
790             end1) < 0);
791   }
792 
793   /**
794    * Sets the given seqId to the cell.
795    * Marked as audience Private as of 1.2.0.
796    * Setting a Cell sequenceid is an internal implementation detail not for general public use.
797    * @param cell
798    * @param seqId
799    * @throws IOException when the passed cell is not of type {@link SettableSequenceId}
800    */
801   @InterfaceAudience.Private
802   public static void setSequenceId(Cell cell, long seqId) throws IOException {
803     if (cell instanceof SettableSequenceId) {
804       ((SettableSequenceId) cell).setSequenceId(seqId);
805     } else {
806       throw new IOException(new UnsupportedOperationException("Cell is not of type "
807           + SettableSequenceId.class.getName()));
808     }
809   }
810 
811   /**
812    * Sets the given timestamp to the cell.
813    * @param cell
814    * @param ts
815    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
816    */
817   public static void setTimestamp(Cell cell, long ts) throws IOException {
818     if (cell instanceof SettableTimestamp) {
819       ((SettableTimestamp) cell).setTimestamp(ts);
820     } else {
821       throw new IOException(new UnsupportedOperationException("Cell is not of type "
822           + SettableTimestamp.class.getName()));
823     }
824   }
825 
826   /**
827    * Sets the given timestamp to the cell.
828    * @param cell
829    * @param ts buffer containing the timestamp value
830    * @param tsOffset offset to the new timestamp
831    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
832    */
833   public static void setTimestamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
834     if (cell instanceof SettableTimestamp) {
835       ((SettableTimestamp) cell).setTimestamp(ts, tsOffset);
836     } else {
837       throw new IOException(new UnsupportedOperationException("Cell is not of type "
838           + SettableTimestamp.class.getName()));
839     }
840   }
841 
842   /**
843    * Sets the given timestamp to the cell iff current timestamp is
844    * {@link HConstants#LATEST_TIMESTAMP}.
845    * @param cell
846    * @param ts
847    * @return True if cell timestamp is modified.
848    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
849    */
850   public static boolean updateLatestStamp(Cell cell, long ts) throws IOException {
851     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
852       setTimestamp(cell, ts);
853       return true;
854     }
855     return false;
856   }
857 
858   /**
859    * Sets the given timestamp to the cell iff current timestamp is
860    * {@link HConstants#LATEST_TIMESTAMP}.
861    * @param cell
862    * @param ts buffer containing the timestamp value
863    * @param tsOffset offset to the new timestamp
864    * @return True if cell timestamp is modified.
865    * @throws IOException when the passed cell is not of type {@link SettableTimestamp}
866    */
867   public static boolean updateLatestStamp(Cell cell, byte[] ts, int tsOffset) throws IOException {
868     if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
869       setTimestamp(cell, ts, tsOffset);
870       return true;
871     }
872     return false;
873   }
874 
875   /**
876    * Writes the Cell's key part as it would have serialized in a KeyValue. The format is &lt;2 bytes
877    * rk len&gt;&lt;rk&gt;&lt;1 byte cf len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes
878    * timestamp&gt;&lt;1 byte type&gt;
879    * @param cell
880    * @param out
881    * @throws IOException
882    */
883   public static void writeFlatKey(Cell cell, DataOutputStream out) throws IOException {
884     short rowLen = cell.getRowLength();
885     out.writeShort(rowLen);
886     out.write(cell.getRowArray(), cell.getRowOffset(), rowLen);
887     byte fLen = cell.getFamilyLength();
888     out.writeByte(fLen);
889     out.write(cell.getFamilyArray(), cell.getFamilyOffset(), fLen);
890     out.write(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
891     out.writeLong(cell.getTimestamp());
892     out.writeByte(cell.getTypeByte());
893   }
894 
895   /**
896    * @param cell
897    * @return The Key portion of the passed <code>cell</code> as a String.
898    */
899   public static String getCellKeyAsString(Cell cell) {
900     StringBuilder sb = new StringBuilder(Bytes.toStringBinary(
901       cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
902     sb.append('/');
903     sb.append(cell.getFamilyLength() == 0? "":
904       Bytes.toStringBinary(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()));
905     // KeyValue only added ':' if family is non-null.  Do same.
906     if (cell.getFamilyLength() > 0) sb.append(':');
907     sb.append(cell.getQualifierLength() == 0? "":
908       Bytes.toStringBinary(cell.getQualifierArray(), cell.getQualifierOffset(),
909         cell.getQualifierLength()));
910     sb.append('/');
911     sb.append(KeyValue.humanReadableTimestamp(cell.getTimestamp()));
912     sb.append('/');
913     sb.append(Type.codeToType(cell.getTypeByte()));
914     if (!(cell instanceof KeyValue.KeyOnlyKeyValue)) {
915       sb.append("/vlen=");
916       sb.append(cell.getValueLength());
917     }
918     sb.append("/seqid=");
919     sb.append(cell.getSequenceId());
920     return sb.toString();
921   }
922 
923   /**
924    * This method exists just to encapsulate how we serialize keys.  To be replaced by a factory
925    * that we query to figure what the Cell implementation is and then, what serialization engine
926    * to use and further, how to serialize the key for inclusion in hfile index. TODO.
927    * @param cell
928    * @return The key portion of the Cell serialized in the old-school KeyValue way or null if
929    * passed a null <code>cell</code>
930    */
931   public static byte [] getCellKeySerializedAsKeyValueKey(final Cell cell) {
932     if (cell == null) return null;
933     byte [] b = new byte[KeyValueUtil.keyLength(cell)];
934     KeyValueUtil.appendKeyTo(cell, b, 0);
935     return b;
936   }
937 
938   /**
939    * Write rowkey excluding the common part.
940    * @param cell
941    * @param rLen
942    * @param commonPrefix
943    * @param out
944    * @throws IOException
945    */
946   public static void writeRowKeyExcludingCommon(Cell cell, short rLen, int commonPrefix,
947       DataOutputStream out) throws IOException {
948     if (commonPrefix == 0) {
949       out.writeShort(rLen);
950     } else if (commonPrefix == 1) {
951       out.writeByte((byte) rLen);
952       commonPrefix--;
953     } else {
954       commonPrefix -= KeyValue.ROW_LENGTH_SIZE;
955     }
956     if (rLen > commonPrefix) {
957       out.write(cell.getRowArray(), cell.getRowOffset() + commonPrefix, rLen - commonPrefix);
958     }
959   }
960 
961   /**
962    * Find length of common prefix in keys of the cells, considering key as byte[] if serialized in
963    * {@link KeyValue}. The key format is &lt;2 bytes rk len&gt;&lt;rk&gt;&lt;1 byte cf
964    * len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes timestamp&gt;&lt;1 byte type&gt;
965    * @param c1
966    *          the cell
967    * @param c2
968    *          the cell
969    * @param bypassFamilyCheck
970    *          when true assume the family bytes same in both cells. Pass it as true when dealing
971    *          with Cells in same CF so as to avoid some checks
972    * @param withTsType
973    *          when true check timestamp and type bytes also.
974    * @return length of common prefix
975    */
976   public static int findCommonPrefixInFlatKey(Cell c1, Cell c2, boolean bypassFamilyCheck,
977       boolean withTsType) {
978     // Compare the 2 bytes in RK length part
979     short rLen1 = c1.getRowLength();
980     short rLen2 = c2.getRowLength();
981     int commonPrefix = KeyValue.ROW_LENGTH_SIZE;
982     if (rLen1 != rLen2) {
983       // early out when the RK length itself is not matching
984       return ByteBufferUtils.findCommonPrefix(Bytes.toBytes(rLen1), 0, KeyValue.ROW_LENGTH_SIZE,
985           Bytes.toBytes(rLen2), 0, KeyValue.ROW_LENGTH_SIZE);
986     }
987     // Compare the RKs
988     int rkCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getRowArray(), c1.getRowOffset(),
989         rLen1, c2.getRowArray(), c2.getRowOffset(), rLen2);
990     commonPrefix += rkCommonPrefix;
991     if (rkCommonPrefix != rLen1) {
992       // Early out when RK is not fully matching.
993       return commonPrefix;
994     }
995     // Compare 1 byte CF length part
996     byte fLen1 = c1.getFamilyLength();
997     if (bypassFamilyCheck) {
998       // This flag will be true when caller is sure that the family will be same for both the cells
999       // Just make commonPrefix to increment by the family part
1000       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE + fLen1;
1001     } else {
1002       byte fLen2 = c2.getFamilyLength();
1003       if (fLen1 != fLen2) {
1004         // early out when the CF length itself is not matching
1005         return commonPrefix;
1006       }
1007       // CF lengths are same so there is one more byte common in key part
1008       commonPrefix += KeyValue.FAMILY_LENGTH_SIZE;
1009       // Compare the CF names
1010       int fCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getFamilyArray(),
1011           c1.getFamilyOffset(), fLen1, c2.getFamilyArray(), c2.getFamilyOffset(), fLen2);
1012       commonPrefix += fCommonPrefix;
1013       if (fCommonPrefix != fLen1) {
1014         return commonPrefix;
1015       }
1016     }
1017     // Compare the Qualifiers
1018     int qLen1 = c1.getQualifierLength();
1019     int qLen2 = c2.getQualifierLength();
1020     int qCommon = ByteBufferUtils.findCommonPrefix(c1.getQualifierArray(), c1.getQualifierOffset(),
1021         qLen1, c2.getQualifierArray(), c2.getQualifierOffset(), qLen2);
1022     commonPrefix += qCommon;
1023     if (!withTsType || Math.max(qLen1, qLen2) != qCommon) {
1024       return commonPrefix;
1025     }
1026     // Compare the timestamp parts
1027     int tsCommonPrefix = ByteBufferUtils.findCommonPrefix(Bytes.toBytes(c1.getTimestamp()), 0,
1028         KeyValue.TIMESTAMP_SIZE, Bytes.toBytes(c2.getTimestamp()), 0, KeyValue.TIMESTAMP_SIZE);
1029     commonPrefix += tsCommonPrefix;
1030     if (tsCommonPrefix != KeyValue.TIMESTAMP_SIZE) {
1031       return commonPrefix;
1032     }
1033     // Compare the type
1034     if (c1.getTypeByte() == c2.getTypeByte()) {
1035       commonPrefix += KeyValue.TYPE_SIZE;
1036     }
1037     return commonPrefix;
1038   }
1039 
1040   /** Returns a string representation of the cell */
1041   public static String toString(Cell cell, boolean verbose) {
1042     if (cell == null) {
1043       return "";
1044     }
1045     StringBuilder builder = new StringBuilder();
1046     String keyStr = getCellKeyAsString(cell);
1047 
1048     String tag = null;
1049     String value = null;
1050     if (verbose) {
1051       // TODO: pretty print tags as well
1052       if (cell.getTagsLength() > 0) {
1053         tag = Bytes.toStringBinary(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
1054       }
1055       if (!(cell instanceof KeyValue.KeyOnlyKeyValue)) {
1056         value = Bytes.toStringBinary(cell.getValueArray(), cell.getValueOffset(),
1057             cell.getValueLength());
1058       }
1059     }
1060 
1061     builder
1062       .append(keyStr);
1063     if (tag != null && !tag.isEmpty()) {
1064       builder.append("/").append(tag);
1065     }
1066     if (value != null) {
1067       builder.append("/").append(value);
1068     }
1069 
1070     return builder.toString();
1071   }
1072 
1073   /***************** special cases ****************************/
1074 
1075   /**
1076    * special case for Cell.equals
1077    */
1078   public static boolean equalsIgnoreMvccVersion(Cell a, Cell b) {
1079     // row
1080     boolean res = matchingRow(a, b);
1081     if (!res)
1082       return res;
1083 
1084     // family
1085     res = matchingColumn(a, b);
1086     if (!res)
1087       return res;
1088 
1089     // timestamp: later sorts first
1090     if (!matchingTimestamp(a, b))
1091       return false;
1092 
1093     // type
1094     int c = (0xff & b.getTypeByte()) - (0xff & a.getTypeByte());
1095     if (c != 0)
1096       return false;
1097     else return true;
1098   }
1099 
1100   /**************** equals ****************************/
1101 
1102   public static boolean equals(Cell a, Cell b) {
1103     return matchingRow(a, b) && matchingFamily(a, b) && matchingQualifier(a, b)
1104         && matchingTimestamp(a, b) && matchingType(a, b);
1105   }
1106 
1107   public static boolean matchingTimestamp(Cell a, Cell b) {
1108     return CellComparator.compareTimestamps(a.getTimestamp(), b.getTimestamp()) == 0;
1109   }
1110 
1111   public static boolean matchingType(Cell a, Cell b) {
1112     return a.getTypeByte() == b.getTypeByte();
1113   }
1114 
1115   /**
1116    * Compares the row of two keyvalues for equality
1117    * 
1118    * @param left
1119    * @param right
1120    * @return True if rows match.
1121    */
1122   public static boolean matchingRows(final Cell left, final Cell right) {
1123     short lrowlength = left.getRowLength();
1124     short rrowlength = right.getRowLength();
1125     if (lrowlength != rrowlength) return false;
1126     if (left instanceof ByteBufferedCell && right instanceof ByteBufferedCell) {
1127       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getRowByteBuffer(),
1128           ((ByteBufferedCell) left).getRowPositionInByteBuffer(), lrowlength,
1129           ((ByteBufferedCell) right).getRowByteBuffer(),
1130           ((ByteBufferedCell) right).getRowPositionInByteBuffer(), rrowlength) == 0;
1131     }
1132     if (left instanceof ByteBufferedCell) {
1133       return ByteBufferUtils.compareTo(((ByteBufferedCell) left).getRowByteBuffer(),
1134           ((ByteBufferedCell) left).getRowPositionInByteBuffer(), lrowlength, right.getRowArray(),
1135           right.getRowOffset(), rrowlength) == 0;
1136     }
1137     if (right instanceof ByteBufferedCell) {
1138       return ByteBufferUtils.compareTo(((ByteBufferedCell) right).getRowByteBuffer(),
1139           ((ByteBufferedCell) right).getRowPositionInByteBuffer(), rrowlength, left.getRowArray(),
1140           left.getRowOffset(), lrowlength) == 0;
1141     }
1142     return Bytes.equals(left.getRowArray(), left.getRowOffset(), left.getRowLength(),
1143         right.getRowArray(), right.getRowOffset(), right.getRowLength());
1144   }
1145 
1146   /**
1147    * Compares the row and column of two keyvalues for equality
1148    *
1149    * @param left
1150    * @param right
1151    * @return True if same row and column.
1152    */
1153   public static boolean matchingRowColumn(final Cell left, final Cell right) {
1154     if ((left.getRowLength() + left.getFamilyLength() + left.getQualifierLength()) != (right
1155         .getRowLength() + right.getFamilyLength() + right.getQualifierLength())) {
1156       return false;
1157     }
1158 
1159     if (!matchingRows(left, right)) {
1160       return false;
1161     }
1162     return matchingColumn(left, right);
1163   }
1164 
1165   /**
1166    * Converts the rowkey bytes of the given cell into an int value
1167    *
1168    * @param cell
1169    * @return rowkey as int
1170    */
1171   public static int getRowAsInt(Cell cell) {
1172     if (cell instanceof ByteBufferedCell) {
1173       return ByteBufferUtils.toInt(((ByteBufferedCell) cell).getRowByteBuffer(),
1174           ((ByteBufferedCell) cell).getRowPositionInByteBuffer());
1175     }
1176     return Bytes.toInt(cell.getRowArray(), cell.getRowOffset());
1177   }
1178 
1179   /**
1180    * Converts the value bytes of the given cell into a long value
1181    *
1182    * @param cell
1183    * @return value as long
1184    */
1185   public static long getValueAsLong(Cell cell) {
1186     if (cell instanceof ByteBufferedCell) {
1187       return ByteBufferUtils.toLong(((ByteBufferedCell) cell).getValueByteBuffer(),
1188           ((ByteBufferedCell) cell).getValuePositionInByteBuffer());
1189     }
1190     return Bytes.toLong(cell.getValueArray(), cell.getValueOffset());
1191   }
1192 
1193   /**
1194    * Converts the value bytes of the given cell into a double value
1195    *
1196    * @param cell
1197    * @return value as double
1198    */
1199   public static double getValueAsDouble(Cell cell) {
1200     if (cell instanceof ByteBufferedCell) {
1201       return ByteBufferUtils.toDouble(((ByteBufferedCell) cell).getValueByteBuffer(),
1202           ((ByteBufferedCell) cell).getValuePositionInByteBuffer());
1203     }
1204     return Bytes.toDouble(cell.getValueArray(), cell.getValueOffset());
1205   }
1206 
1207   /**
1208    * Converts the value bytes of the given cell into a BigDecimal
1209    *
1210    * @param cell
1211    * @return value as BigDecimal
1212    */
1213   public static BigDecimal getValueAsBigDecimal(Cell cell) {
1214     if (cell instanceof ByteBufferedCell) {
1215       return ByteBufferUtils.toBigDecimal(((ByteBufferedCell) cell).getValueByteBuffer(),
1216           ((ByteBufferedCell) cell).getValuePositionInByteBuffer(), cell.getValueLength());
1217     }
1218     return Bytes.toBigDecimal(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
1219   }
1220 
1221   /**
1222    * Create a Cell that is smaller than all other possible Cells for the given Cell's row.
1223    *
1224    * @param cell
1225    * @return First possible Cell on passed Cell's row.
1226    */
1227   public static Cell createFirstOnRow(final Cell cell) {
1228     return new FirstOnRowFakeCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
1229   }
1230 
1231   /**
1232    * Create a Cell that is smaller than all other possible Cells for the given Cell row's next row.
1233    * Makes the next row's rowkey by appending single byte 0x00 to the end of current row key.
1234    */
1235   public static Cell createFirstOnNextRow(final Cell cell) {
1236     byte[] nextRow = new byte[cell.getRowLength() + 1];
1237     copyRowTo(cell, nextRow, 0);
1238     nextRow[nextRow.length - 1] = 0;// maybe not necessary
1239     return new FirstOnRowFakeCell(nextRow, 0, (short) nextRow.length);
1240   }
1241 
1242   /**
1243    * Create a Cell that is smaller than all other possible Cells for the given Cell's rk:cf and
1244    * passed qualifier.
1245    *
1246    * @param cell
1247    * @param qArray
1248    * @param qoffest
1249    * @param qlength
1250    * @return Last possible Cell on passed Cell's rk:cf and passed qualifier.
1251    */
1252   public static Cell createFirstOnRowCol(final Cell cell, byte[] qArray, int qoffest, int qlength) {
1253     return new FirstOnRowColumnFakeCell(cell.getRowArray(), cell.getRowOffset(),
1254         cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
1255         qArray, qoffest, qlength);
1256   }
1257 
1258   /**
1259    * Creates the first cell with the row/family/qualifier of this cell and the given timestamp.
1260    * Uses the "maximum" type that guarantees that the new cell is the lowest possible for this
1261    * combination of row, family, qualifier, and timestamp. This cell's own timestamp is ignored.
1262    *
1263    * @param cell - cell
1264    * @param ts
1265    */
1266   public static Cell createFirstOnRowColTS(Cell cell, long ts) {
1267     return new FirstOnRowColumnTSFakeCell(cell.getRowArray(), cell.getRowOffset(),
1268         cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
1269         cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(), ts);
1270   }
1271 
1272   /**
1273    * Create a Cell that is larger than all other possible Cells for the given Cell's row.
1274    *
1275    * @param cell
1276    * @return Last possible Cell on passed Cell's row.
1277    */
1278   public static Cell createLastOnRow(final Cell cell) {
1279     return new LastOnRowFakeCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
1280   }
1281 
1282   /**
1283    * Create a Cell that is larger than all other possible Cells for the given Cell's rk:cf:q. Used
1284    * in creating "fake keys" for the multi-column Bloom filter optimization to skip the row/column
1285    * we already know is not in the file.
1286    *
1287    * @param cell
1288    * @return Last possible Cell on passed Cell's rk:cf:q.
1289    */
1290   public static Cell createLastOnRowCol(final Cell cell) {
1291     return new LastOnRowColumnFakeCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
1292         cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
1293         cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
1294   }
1295 
1296   /**
1297    * Create a Delete Family Cell for the specified row and family that would
1298    * be smaller than all other possible Delete Family KeyValues that have the
1299    * same row and family.
1300    * Used for seeking.
1301    * @param row - row key (arbitrary byte array)
1302    * @param fam - family name
1303    * @return First Delete Family possible key on passed <code>row</code>.
1304    */
1305   public static Cell createFirstDeleteFamilyCellOnRow(final byte[] row, final byte[] fam) {
1306     return new FirstOnRowDeleteFamilyCell(row, fam);
1307   }
1308 
1309   @InterfaceAudience.Private
1310   private static abstract class FakeCell implements Cell {
1311 
1312     @Override
1313     public byte[] getRowArray() {
1314       return EMPTY_BYTE_ARRAY;
1315     }
1316 
1317     @Override
1318     public int getRowOffset() {
1319       return 0;
1320     }
1321 
1322     @Override
1323     public short getRowLength() {
1324       return 0;
1325     }
1326 
1327     @Override
1328     public byte[] getFamilyArray() {
1329       return EMPTY_BYTE_ARRAY;
1330     }
1331 
1332     @Override
1333     public int getFamilyOffset() {
1334       return 0;
1335     }
1336 
1337     @Override
1338     public byte getFamilyLength() {
1339       return 0;
1340     }
1341 
1342     @Override
1343     public byte[] getQualifierArray() {
1344       return EMPTY_BYTE_ARRAY;
1345     }
1346 
1347     @Override
1348     public int getQualifierOffset() {
1349       return 0;
1350     }
1351 
1352     @Override
1353     public int getQualifierLength() {
1354       return 0;
1355     }
1356 
1357     @Override
1358     public long getSequenceId() {
1359       return 0;
1360     }
1361 
1362     @Override
1363     public byte[] getValueArray() {
1364       return EMPTY_BYTE_ARRAY;
1365     }
1366 
1367     @Override
1368     public int getValueOffset() {
1369       return 0;
1370     }
1371 
1372     @Override
1373     public int getValueLength() {
1374       return 0;
1375     }
1376 
1377     @Override
1378     public byte[] getTagsArray() {
1379       return EMPTY_BYTE_ARRAY;
1380     }
1381 
1382     @Override
1383     public int getTagsOffset() {
1384       return 0;
1385     }
1386 
1387     @Override
1388     public int getTagsLength() {
1389       return 0;
1390     }
1391   }
1392 
1393   @InterfaceAudience.Private
1394   private static class FirstOnRowFakeCell extends FakeCell {
1395     private final byte[] rowArray;
1396     private final int roffset;
1397     private final short rlength;
1398 
1399     public FirstOnRowFakeCell(final byte[] row, int roffset, short rlength) {
1400       this.rowArray = row;
1401       this.roffset = roffset;
1402       this.rlength = rlength;
1403     }
1404 
1405     @Override
1406     public byte[] getRowArray() {
1407       return this.rowArray;
1408     }
1409 
1410     @Override
1411     public int getRowOffset() {
1412       return this.roffset;
1413     }
1414 
1415     @Override
1416     public short getRowLength() {
1417       return this.rlength;
1418     }
1419 
1420     @Override
1421     public long getTimestamp() {
1422       return HConstants.LATEST_TIMESTAMP;
1423     }
1424 
1425     @Override
1426     public byte getTypeByte() {
1427       return Type.Maximum.getCode();
1428     }
1429   }
1430 
1431   @InterfaceAudience.Private
1432   private static class FirstOnRowColumnFakeCell extends FirstOnRowFakeCell {
1433     private final byte[] fArray;
1434     private final int foffset;
1435     private final byte flength;
1436     private final byte[] qArray;
1437     private final int qoffset;
1438     private final int qlength;
1439 
1440     public FirstOnRowColumnFakeCell(byte[] rArray, int roffset, short rlength, byte[] fArray,
1441         int foffset, byte flength, byte[] qArray, int qoffset, int qlength) {
1442       super(rArray, roffset, rlength);
1443       this.fArray = fArray;
1444       this.foffset = foffset;
1445       this.flength = flength;
1446       this.qArray = qArray;
1447       this.qoffset = qoffset;
1448       this.qlength = qlength;
1449     }
1450 
1451     @Override
1452     public byte[] getFamilyArray() {
1453       return this.fArray;
1454     }
1455 
1456     @Override
1457     public int getFamilyOffset() {
1458       return this.foffset;
1459     }
1460 
1461     @Override
1462     public byte getFamilyLength() {
1463       return this.flength;
1464     }
1465 
1466     @Override
1467     public byte[] getQualifierArray() {
1468       return this.qArray;
1469     }
1470 
1471     @Override
1472     public int getQualifierOffset() {
1473       return this.qoffset;
1474     }
1475 
1476     @Override
1477     public int getQualifierLength() {
1478       return this.qlength;
1479     }
1480   }
1481 
1482   @InterfaceAudience.Private
1483   private static class FirstOnRowColumnTSFakeCell extends FirstOnRowColumnFakeCell {
1484 
1485     private long ts;
1486 
1487     public FirstOnRowColumnTSFakeCell(byte[] rArray, int roffset, short rlength, byte[] fArray,
1488         int foffset, byte flength, byte[] qArray, int qoffset, int qlength, long ts) {
1489       super(rArray, roffset, rlength, fArray, foffset, flength, qArray, qoffset, qlength);
1490       this.ts = ts;
1491     }
1492 
1493     @Override
1494     public long getTimestamp() {
1495       return this.ts;
1496     }
1497   }
1498 
1499   @InterfaceAudience.Private
1500   private static class LastOnRowFakeCell extends FakeCell {
1501     private final byte[] rowArray;
1502     private final int roffset;
1503     private final short rlength;
1504 
1505     public LastOnRowFakeCell(byte[] row, int roffset, short rlength) {
1506       this.rowArray = row;
1507       this.roffset = roffset;
1508       this.rlength = rlength;
1509     }
1510 
1511     @Override
1512     public byte[] getRowArray() {
1513       return this.rowArray;
1514     }
1515 
1516     @Override
1517     public int getRowOffset() {
1518       return this.roffset;
1519     }
1520 
1521     @Override
1522     public short getRowLength() {
1523       return this.rlength;
1524     }
1525 
1526     @Override
1527     public long getTimestamp() {
1528       return HConstants.OLDEST_TIMESTAMP;
1529     }
1530 
1531     @Override
1532     public byte getTypeByte() {
1533       return Type.Minimum.getCode();
1534     }
1535   }
1536 
1537   @InterfaceAudience.Private
1538   private static class LastOnRowColumnFakeCell extends LastOnRowFakeCell {
1539     private final byte[] fArray;
1540     private final int foffset;
1541     private final byte flength;
1542     private final byte[] qArray;
1543     private final int qoffset;
1544     private final int qlength;
1545 
1546     public LastOnRowColumnFakeCell(byte[] rArray, int roffset, short rlength, byte[] fArray,
1547         int foffset, byte flength, byte[] qArray, int qoffset, int qlength) {
1548       super(rArray, roffset, rlength);
1549       this.fArray = fArray;
1550       this.foffset = foffset;
1551       this.flength = flength;
1552       this.qArray = qArray;
1553       this.qoffset = qoffset;
1554       this.qlength = qlength;
1555     }
1556 
1557     @Override
1558     public byte[] getFamilyArray() {
1559       return this.fArray;
1560     }
1561 
1562     @Override
1563     public int getFamilyOffset() {
1564       return this.foffset;
1565     }
1566 
1567     @Override
1568     public byte getFamilyLength() {
1569       return this.flength;
1570     }
1571 
1572     @Override
1573     public byte[] getQualifierArray() {
1574       return this.qArray;
1575     }
1576 
1577     @Override
1578     public int getQualifierOffset() {
1579       return this.qoffset;
1580     }
1581 
1582     @Override
1583     public int getQualifierLength() {
1584       return this.qlength;
1585     }
1586   }
1587 
1588   @InterfaceAudience.Private
1589   private static class FirstOnRowDeleteFamilyCell extends FakeCell {
1590     private final byte[] row;
1591     private final byte[] fam;
1592 
1593     public FirstOnRowDeleteFamilyCell(byte[] row, byte[] fam) {
1594       this.row = row;
1595       this.fam = fam;
1596     }
1597 
1598     @Override
1599     public byte[] getRowArray() {
1600       return this.row;
1601     }
1602 
1603     @Override
1604     public short getRowLength() {
1605       return (short) this.row.length;
1606     }
1607 
1608     @Override
1609     public byte[] getFamilyArray() {
1610       return this.fam;
1611     }
1612 
1613     @Override
1614     public byte getFamilyLength() {
1615       return (byte) this.fam.length;
1616     }
1617 
1618     @Override
1619     public long getTimestamp() {
1620       return HConstants.LATEST_TIMESTAMP;
1621     }
1622 
1623     @Override
1624     public byte getTypeByte() {
1625       return Type.DeleteFamily.getCode();
1626     }
1627   }
1628 }