001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import static org.apache.hadoop.hbase.HConstants.EMPTY_BYTE_ARRAY;
021import static org.apache.hadoop.hbase.Tag.TAG_LENGTH_SIZE;
022
023import java.io.DataOutput;
024import java.io.DataOutputStream;
025import java.io.IOException;
026import java.io.OutputStream;
027import java.math.BigDecimal;
028import java.nio.ByteBuffer;
029import java.util.ArrayList;
030import java.util.Iterator;
031import java.util.List;
032import java.util.Map.Entry;
033import java.util.NavigableMap;
034import java.util.Optional;
035import org.apache.hadoop.hbase.filter.ByteArrayComparable;
036import org.apache.hadoop.hbase.io.TagCompressionContext;
037import org.apache.hadoop.hbase.io.util.Dictionary;
038import org.apache.hadoop.hbase.io.util.StreamUtils;
039import org.apache.hadoop.hbase.util.ByteBufferUtils;
040import org.apache.hadoop.hbase.util.ByteRange;
041import org.apache.hadoop.hbase.util.Bytes;
042import org.apache.hadoop.hbase.util.ClassSize;
043import org.apache.yetus.audience.InterfaceAudience;
044
045import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
046
047/**
048 * Utility methods helpful slinging {@link Cell} instances. It has more powerful and rich set of
049 * APIs than those in {@link CellUtil} for internal usage.
050 */
051@InterfaceAudience.Private
052public final class PrivateCellUtil {
053
054  /**
055   * Private constructor to keep this class from being instantiated.
056   */
057  private PrivateCellUtil() {
058  }
059
060  /******************* ByteRange *******************************/
061
062  public static ByteRange fillRowRange(Cell cell, ByteRange range) {
063    return range.set(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
064  }
065
066  public static ByteRange fillFamilyRange(Cell cell, ByteRange range) {
067    return range.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
068  }
069
070  public static ByteRange fillQualifierRange(Cell cell, ByteRange range) {
071    return range.set(cell.getQualifierArray(), cell.getQualifierOffset(),
072      cell.getQualifierLength());
073  }
074
075  public static ByteRange fillValueRange(Cell cell, ByteRange range) {
076    return range.set(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
077  }
078
079  public static ByteRange fillTagRange(ExtendedCell cell, ByteRange range) {
080    return range.set(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
081  }
082
083  /********************* misc *************************************/
084
085  public static byte getRowByte(Cell cell, int index) {
086    if (cell instanceof ByteBufferExtendedCell) {
087      return ((ByteBufferExtendedCell) cell).getRowByteBuffer()
088        .get(((ByteBufferExtendedCell) cell).getRowPosition() + index);
089    }
090    return cell.getRowArray()[cell.getRowOffset() + index];
091  }
092
093  public static byte getQualifierByte(Cell cell, int index) {
094    if (cell instanceof ByteBufferExtendedCell) {
095      return ((ByteBufferExtendedCell) cell).getQualifierByteBuffer()
096        .get(((ByteBufferExtendedCell) cell).getQualifierPosition() + index);
097    }
098    return cell.getQualifierArray()[cell.getQualifierOffset() + index];
099  }
100
101  public static ByteBuffer getValueBufferShallowCopy(Cell cell) {
102    ByteBuffer buffer =
103      ByteBuffer.wrap(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
104    return buffer;
105  }
106
107  /** Returns A new cell which is having the extra tags also added to it. */
108  public static ExtendedCell createCell(ExtendedCell cell, List<Tag> tags) {
109    return createCell(cell, TagUtil.fromList(tags));
110  }
111
112  /** Returns A new cell which is having the extra tags also added to it. */
113  public static ExtendedCell createCell(ExtendedCell cell, byte[] tags) {
114    if (cell instanceof ByteBufferExtendedCell) {
115      return new TagRewriteByteBufferExtendedCell((ByteBufferExtendedCell) cell, tags);
116    }
117    return new TagRewriteCell(cell, tags);
118  }
119
120  public static ExtendedCell createCell(ExtendedCell cell, byte[] value, byte[] tags) {
121    if (cell instanceof ByteBufferExtendedCell) {
122      return new ValueAndTagRewriteByteBufferExtendedCell((ByteBufferExtendedCell) cell, value,
123        tags);
124    }
125    return new ValueAndTagRewriteCell(cell, value, tags);
126  }
127
128  /**
129   * This can be used when a Cell has to change with addition/removal of one or more tags. This is
130   * an efficient way to do so in which only the tags bytes part need to recreated and copied. All
131   * other parts, refer to the original Cell.
132   */
133  static class TagRewriteCell implements ExtendedCell {
134    protected ExtendedCell cell;
135    protected byte[] tags;
136    private static final int HEAP_SIZE_OVERHEAD = ClassSize.OBJECT + 2 * ClassSize.REFERENCE;
137
138    /**
139     * Construct a TagRewriteCell
140     * @param cell The original Cell which it rewrites
141     * @param tags the tags bytes. The array suppose to contain the tags bytes alone.
142     */
143    public TagRewriteCell(ExtendedCell cell, byte[] tags) {
144      assert tags != null;
145      this.cell = cell;
146      this.tags = tags;
147      // tag offset will be treated as 0 and length this.tags.length
148      if (this.cell instanceof TagRewriteCell) {
149        // Cleaning the ref so that the byte[] can be GCed
150        ((TagRewriteCell) this.cell).tags = null;
151      }
152    }
153
154    @Override
155    public byte[] getRowArray() {
156      return cell.getRowArray();
157    }
158
159    @Override
160    public int getRowOffset() {
161      return cell.getRowOffset();
162    }
163
164    @Override
165    public short getRowLength() {
166      return cell.getRowLength();
167    }
168
169    @Override
170    public byte[] getFamilyArray() {
171      return cell.getFamilyArray();
172    }
173
174    @Override
175    public int getFamilyOffset() {
176      return cell.getFamilyOffset();
177    }
178
179    @Override
180    public byte getFamilyLength() {
181      return cell.getFamilyLength();
182    }
183
184    @Override
185    public byte[] getQualifierArray() {
186      return cell.getQualifierArray();
187    }
188
189    @Override
190    public int getQualifierOffset() {
191      return cell.getQualifierOffset();
192    }
193
194    @Override
195    public int getQualifierLength() {
196      return cell.getQualifierLength();
197    }
198
199    @Override
200    public long getTimestamp() {
201      return cell.getTimestamp();
202    }
203
204    @Override
205    public byte getTypeByte() {
206      return cell.getTypeByte();
207    }
208
209    @Override
210    public long getSequenceId() {
211      return cell.getSequenceId();
212    }
213
214    @Override
215    public byte[] getValueArray() {
216      return cell.getValueArray();
217    }
218
219    @Override
220    public int getValueOffset() {
221      return cell.getValueOffset();
222    }
223
224    @Override
225    public int getValueLength() {
226      return cell.getValueLength();
227    }
228
229    @Override
230    public byte[] getTagsArray() {
231      return this.tags;
232    }
233
234    @Override
235    public int getTagsOffset() {
236      return 0;
237    }
238
239    @Override
240    public int getTagsLength() {
241      if (null == this.tags) {
242        // Nulled out tags array optimization in constructor
243        return 0;
244      }
245      return this.tags.length;
246    }
247
248    @Override
249    public long heapSize() {
250      long sum = HEAP_SIZE_OVERHEAD + cell.heapSize();
251      if (this.tags != null) {
252        sum += ClassSize.sizeOf(this.tags);
253      }
254      return sum;
255    }
256
257    @Override
258    public void setTimestamp(long ts) throws IOException {
259      // The incoming cell is supposed to be ExtendedCell type.
260      PrivateCellUtil.setTimestamp(cell, ts);
261    }
262
263    @Override
264    public void setTimestamp(byte[] ts) throws IOException {
265      // The incoming cell is supposed to be ExtendedCell type.
266      PrivateCellUtil.setTimestamp(cell, ts);
267    }
268
269    @Override
270    public void setSequenceId(long seqId) throws IOException {
271      // The incoming cell is supposed to be ExtendedCell type.
272      PrivateCellUtil.setSequenceId(cell, seqId);
273    }
274
275    @Override
276    public int write(OutputStream out, boolean withTags) throws IOException {
277      int len = ((ExtendedCell) this.cell).write(out, false);
278      if (withTags && this.tags != null) {
279        // Write the tagsLength 2 bytes
280        out.write((byte) (0xff & (this.tags.length >> 8)));
281        out.write((byte) (0xff & this.tags.length));
282        out.write(this.tags);
283        len += KeyValue.TAGS_LENGTH_SIZE + this.tags.length;
284      }
285      return len;
286    }
287
288    @Override
289    public int getSerializedSize(boolean withTags) {
290      int len = ((ExtendedCell) this.cell).getSerializedSize(false);
291      if (withTags && this.tags != null) {
292        len += KeyValue.TAGS_LENGTH_SIZE + this.tags.length;
293      }
294      return len;
295    }
296
297    @Override
298    public void write(ByteBuffer buf, int offset) {
299      offset = KeyValueUtil.appendTo(this.cell, buf, offset, false);
300      int tagsLen = this.tags == null ? 0 : this.tags.length;
301      if (tagsLen > 0) {
302        offset = ByteBufferUtils.putAsShort(buf, offset, tagsLen);
303        ByteBufferUtils.copyFromArrayToBuffer(buf, offset, this.tags, 0, tagsLen);
304      }
305    }
306
307    @Override
308    public ExtendedCell deepClone() {
309      ExtendedCell clonedBaseCell = this.cell.deepClone();
310      return new TagRewriteCell(clonedBaseCell, this.tags);
311    }
312  }
313
314  static class TagRewriteByteBufferExtendedCell extends ByteBufferExtendedCell {
315
316    protected ByteBufferExtendedCell cell;
317    protected byte[] tags;
318    private static final int HEAP_SIZE_OVERHEAD = ClassSize.OBJECT + 2 * ClassSize.REFERENCE;
319
320    /**
321     * @param cell The original ByteBufferExtendedCell which it rewrites
322     * @param tags the tags bytes. The array suppose to contain the tags bytes alone.
323     */
324    public TagRewriteByteBufferExtendedCell(ByteBufferExtendedCell cell, byte[] tags) {
325      assert tags != null;
326      this.cell = cell;
327      this.tags = tags;
328      // tag offset will be treated as 0 and length this.tags.length
329      if (this.cell instanceof TagRewriteByteBufferExtendedCell) {
330        // Cleaning the ref so that the byte[] can be GCed
331        ((TagRewriteByteBufferExtendedCell) this.cell).tags = null;
332      }
333    }
334
335    @Override
336    public byte[] getRowArray() {
337      return this.cell.getRowArray();
338    }
339
340    @Override
341    public int getRowOffset() {
342      return this.cell.getRowOffset();
343    }
344
345    @Override
346    public short getRowLength() {
347      return this.cell.getRowLength();
348    }
349
350    @Override
351    public byte[] getFamilyArray() {
352      return this.cell.getFamilyArray();
353    }
354
355    @Override
356    public int getFamilyOffset() {
357      return this.cell.getFamilyOffset();
358    }
359
360    @Override
361    public byte getFamilyLength() {
362      return this.cell.getFamilyLength();
363    }
364
365    @Override
366    public byte[] getQualifierArray() {
367      return this.cell.getQualifierArray();
368    }
369
370    @Override
371    public int getQualifierOffset() {
372      return this.cell.getQualifierOffset();
373    }
374
375    @Override
376    public int getQualifierLength() {
377      return this.cell.getQualifierLength();
378    }
379
380    @Override
381    public long getTimestamp() {
382      return this.cell.getTimestamp();
383    }
384
385    @Override
386    public byte getTypeByte() {
387      return this.cell.getTypeByte();
388    }
389
390    @Override
391    public long getSequenceId() {
392      return this.cell.getSequenceId();
393    }
394
395    @Override
396    public byte[] getValueArray() {
397      return this.cell.getValueArray();
398    }
399
400    @Override
401    public int getValueOffset() {
402      return this.cell.getValueOffset();
403    }
404
405    @Override
406    public int getValueLength() {
407      return this.cell.getValueLength();
408    }
409
410    @Override
411    public byte[] getTagsArray() {
412      return this.tags;
413    }
414
415    @Override
416    public int getTagsOffset() {
417      return 0;
418    }
419
420    @Override
421    public int getTagsLength() {
422      if (null == this.tags) {
423        // Nulled out tags array optimization in constructor
424        return 0;
425      }
426      return this.tags.length;
427    }
428
429    @Override
430    public void setSequenceId(long seqId) throws IOException {
431      PrivateCellUtil.setSequenceId(this.cell, seqId);
432    }
433
434    @Override
435    public void setTimestamp(long ts) throws IOException {
436      PrivateCellUtil.setTimestamp(this.cell, ts);
437    }
438
439    @Override
440    public void setTimestamp(byte[] ts) throws IOException {
441      PrivateCellUtil.setTimestamp(this.cell, ts);
442    }
443
444    @Override
445    public long heapSize() {
446      long sum = HEAP_SIZE_OVERHEAD + cell.heapSize();
447      // this.tags is on heap byte[]
448      if (this.tags != null) {
449        sum += ClassSize.sizeOf(this.tags);
450      }
451      return sum;
452    }
453
454    @Override
455    public int write(OutputStream out, boolean withTags) throws IOException {
456      int len = ((ExtendedCell) this.cell).write(out, false);
457      if (withTags && this.tags != null) {
458        // Write the tagsLength 2 bytes
459        out.write((byte) (0xff & (this.tags.length >> 8)));
460        out.write((byte) (0xff & this.tags.length));
461        out.write(this.tags);
462        len += KeyValue.TAGS_LENGTH_SIZE + this.tags.length;
463      }
464      return len;
465    }
466
467    @Override
468    public int getSerializedSize(boolean withTags) {
469      int len = ((ExtendedCell) this.cell).getSerializedSize(false);
470      if (withTags && this.tags != null) {
471        len += KeyValue.TAGS_LENGTH_SIZE + this.tags.length;
472      }
473      return len;
474    }
475
476    @Override
477    public void write(ByteBuffer buf, int offset) {
478      offset = KeyValueUtil.appendTo(this.cell, buf, offset, false);
479      int tagsLen = this.tags == null ? 0 : this.tags.length;
480      if (tagsLen > 0) {
481        offset = ByteBufferUtils.putAsShort(buf, offset, tagsLen);
482        ByteBufferUtils.copyFromArrayToBuffer(buf, offset, this.tags, 0, tagsLen);
483      }
484    }
485
486    @Override
487    public ExtendedCell deepClone() {
488      ExtendedCell clonedBaseCell = this.cell.deepClone();
489      if (clonedBaseCell instanceof ByteBufferExtendedCell) {
490        return new TagRewriteByteBufferExtendedCell((ByteBufferExtendedCell) clonedBaseCell,
491          this.tags);
492      }
493      return new TagRewriteCell(clonedBaseCell, this.tags);
494    }
495
496    @Override
497    public ByteBuffer getRowByteBuffer() {
498      return this.cell.getRowByteBuffer();
499    }
500
501    @Override
502    public int getRowPosition() {
503      return this.cell.getRowPosition();
504    }
505
506    @Override
507    public ByteBuffer getFamilyByteBuffer() {
508      return this.cell.getFamilyByteBuffer();
509    }
510
511    @Override
512    public int getFamilyPosition() {
513      return this.cell.getFamilyPosition();
514    }
515
516    @Override
517    public ByteBuffer getQualifierByteBuffer() {
518      return this.cell.getQualifierByteBuffer();
519    }
520
521    @Override
522    public int getQualifierPosition() {
523      return this.cell.getQualifierPosition();
524    }
525
526    @Override
527    public ByteBuffer getValueByteBuffer() {
528      return this.cell.getValueByteBuffer();
529    }
530
531    @Override
532    public int getValuePosition() {
533      return this.cell.getValuePosition();
534    }
535
536    @Override
537    public ByteBuffer getTagsByteBuffer() {
538      return this.tags == null ? HConstants.EMPTY_BYTE_BUFFER : ByteBuffer.wrap(this.tags);
539    }
540
541    @Override
542    public int getTagsPosition() {
543      return 0;
544    }
545  }
546
547  static class ValueAndTagRewriteCell extends TagRewriteCell {
548
549    protected byte[] value;
550
551    public ValueAndTagRewriteCell(ExtendedCell cell, byte[] value, byte[] tags) {
552      super(cell, tags);
553      this.value = value;
554    }
555
556    @Override
557    public byte[] getValueArray() {
558      return this.value;
559    }
560
561    @Override
562    public int getValueOffset() {
563      return 0;
564    }
565
566    @Override
567    public int getValueLength() {
568      return this.value == null ? 0 : this.value.length;
569    }
570
571    @Override
572    public long heapSize() {
573      long sum = ClassSize.REFERENCE + super.heapSize();
574      if (this.value != null) {
575        sum += ClassSize.sizeOf(this.value);
576      }
577      return sum;
578    }
579
580    @Override
581    public int write(OutputStream out, boolean withTags) throws IOException {
582      return write(out, withTags, this.cell, this.value, this.tags);
583    }
584
585    /**
586     * Made into a static method so as to reuse the logic within
587     * ValueAndTagRewriteByteBufferExtendedCell
588     */
589    static int write(OutputStream out, boolean withTags, ExtendedCell cell, byte[] value,
590      byte[] tags) throws IOException {
591      int valLen = value == null ? 0 : value.length;
592      ByteBufferUtils.putInt(out, KeyValueUtil.keyLength(cell));// Key length
593      ByteBufferUtils.putInt(out, valLen);// Value length
594      int len = 2 * Bytes.SIZEOF_INT;
595      len += writeFlatKey(cell, out);// Key
596      if (valLen > 0) {
597        out.write(value);// Value
598      }
599      len += valLen;
600      if (withTags && tags != null) {
601        // Write the tagsLength 2 bytes
602        out.write((byte) (0xff & (tags.length >> 8)));
603        out.write((byte) (0xff & tags.length));
604        out.write(tags);
605        len += KeyValue.TAGS_LENGTH_SIZE + tags.length;
606      }
607      return len;
608    }
609
610    @Override
611    public int getSerializedSize(boolean withTags) {
612      return super.getSerializedSize(withTags) - this.cell.getValueLength() + this.value.length;
613    }
614
615    @Override
616    public void write(ByteBuffer buf, int offset) {
617      write(buf, offset, this.cell, this.value, this.tags);
618    }
619
620    /**
621     * Made into a static method so as to reuse the logic within
622     * ValueAndTagRewriteByteBufferExtendedCell
623     */
624    static void write(ByteBuffer buf, int offset, ExtendedCell cell, byte[] value, byte[] tags) {
625      offset = ByteBufferUtils.putInt(buf, offset, KeyValueUtil.keyLength(cell));// Key length
626      offset = ByteBufferUtils.putInt(buf, offset, value.length);// Value length
627      offset = KeyValueUtil.appendKeyTo(cell, buf, offset);
628      ByteBufferUtils.copyFromArrayToBuffer(buf, offset, value, 0, value.length);
629      offset += value.length;
630      int tagsLen = tags == null ? 0 : tags.length;
631      if (tagsLen > 0) {
632        offset = ByteBufferUtils.putAsShort(buf, offset, tagsLen);
633        ByteBufferUtils.copyFromArrayToBuffer(buf, offset, tags, 0, tagsLen);
634      }
635    }
636
637    @Override
638    public ExtendedCell deepClone() {
639      ExtendedCell clonedBaseCell = this.cell.deepClone();
640      return new ValueAndTagRewriteCell(clonedBaseCell, this.value, this.tags);
641    }
642  }
643
644  static class ValueAndTagRewriteByteBufferExtendedCell extends TagRewriteByteBufferExtendedCell {
645
646    protected byte[] value;
647
648    public ValueAndTagRewriteByteBufferExtendedCell(ByteBufferExtendedCell cell, byte[] value,
649      byte[] tags) {
650      super(cell, tags);
651      this.value = value;
652    }
653
654    @Override
655    public byte[] getValueArray() {
656      return this.value;
657    }
658
659    @Override
660    public int getValueOffset() {
661      return 0;
662    }
663
664    @Override
665    public int getValueLength() {
666      return this.value == null ? 0 : this.value.length;
667    }
668
669    @Override
670    public ByteBuffer getValueByteBuffer() {
671      return ByteBuffer.wrap(this.value);
672    }
673
674    @Override
675    public int getValuePosition() {
676      return 0;
677    }
678
679    @Override
680    public long heapSize() {
681      long sum = ClassSize.REFERENCE + super.heapSize();
682      if (this.value != null) {
683        sum += ClassSize.sizeOf(this.value);
684      }
685      return sum;
686    }
687
688    @Override
689    public int write(OutputStream out, boolean withTags) throws IOException {
690      return ValueAndTagRewriteCell.write(out, withTags, this.cell, this.value, this.tags);
691    }
692
693    @Override
694    public int getSerializedSize(boolean withTags) {
695      return super.getSerializedSize(withTags) - this.cell.getValueLength() + this.value.length;
696    }
697
698    @Override
699    public void write(ByteBuffer buf, int offset) {
700      ValueAndTagRewriteCell.write(buf, offset, this.cell, this.value, this.tags);
701    }
702
703    @Override
704    public ExtendedCell deepClone() {
705      ExtendedCell clonedBaseCell = this.cell.deepClone();
706      if (clonedBaseCell instanceof ByteBufferExtendedCell) {
707        return new ValueAndTagRewriteByteBufferExtendedCell((ByteBufferExtendedCell) clonedBaseCell,
708          this.value, this.tags);
709      }
710      return new ValueAndTagRewriteCell(clonedBaseCell, this.value, this.tags);
711    }
712  }
713
714  public static boolean matchingRows(final Cell left, final byte[] buf, final int offset,
715    final int length) {
716    if (left instanceof ByteBufferExtendedCell) {
717      return ByteBufferUtils.equals(((ByteBufferExtendedCell) left).getRowByteBuffer(),
718        ((ByteBufferExtendedCell) left).getRowPosition(), left.getRowLength(), buf, offset, length);
719    }
720    return Bytes.equals(left.getRowArray(), left.getRowOffset(), left.getRowLength(), buf, offset,
721      length);
722  }
723
724  public static boolean matchingFamily(final Cell left, final byte[] buf, final int offset,
725    final int length) {
726    if (left instanceof ByteBufferExtendedCell) {
727      return ByteBufferUtils.equals(((ByteBufferExtendedCell) left).getFamilyByteBuffer(),
728        ((ByteBufferExtendedCell) left).getFamilyPosition(), left.getFamilyLength(), buf, offset,
729        length);
730    }
731    return Bytes.equals(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(), buf,
732      offset, length);
733  }
734
735  /**
736   * Finds if the qualifier part of the cell and the KV serialized byte[] are equal
737   * @param left   the cell with which we need to match the qualifier
738   * @param buf    the serialized keyvalue format byte[]
739   * @param offset the offset of the qualifier in the byte[]
740   * @param length the length of the qualifier in the byte[]
741   * @return true if the qualifier matches, false otherwise
742   */
743  public static boolean matchingQualifier(final Cell left, final byte[] buf, final int offset,
744    final int length) {
745    if (buf == null) {
746      return left.getQualifierLength() == 0;
747    }
748    if (left instanceof ByteBufferExtendedCell) {
749      return ByteBufferUtils.equals(((ByteBufferExtendedCell) left).getQualifierByteBuffer(),
750        ((ByteBufferExtendedCell) left).getQualifierPosition(), left.getQualifierLength(), buf,
751        offset, length);
752    }
753    return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(),
754      left.getQualifierLength(), buf, offset, length);
755  }
756
757  /**
758   * Finds if the start of the qualifier part of the Cell matches <code>buf</code>
759   * @param left       the cell with which we need to match the qualifier
760   * @param startsWith the serialized keyvalue format byte[]
761   * @return true if the qualifier have same staring characters, false otherwise
762   */
763  public static boolean qualifierStartsWith(final Cell left, final byte[] startsWith) {
764    if (startsWith == null || startsWith.length == 0) {
765      throw new IllegalArgumentException("Cannot pass an empty startsWith");
766    }
767    if (left.getQualifierLength() < startsWith.length) {
768      return false;
769    }
770    if (left instanceof ByteBufferExtendedCell) {
771      return ByteBufferUtils.equals(((ByteBufferExtendedCell) left).getQualifierByteBuffer(),
772        ((ByteBufferExtendedCell) left).getQualifierPosition(), startsWith.length, startsWith, 0,
773        startsWith.length);
774    }
775    return Bytes.equals(left.getQualifierArray(), left.getQualifierOffset(), startsWith.length,
776      startsWith, 0, startsWith.length);
777  }
778
779  public static boolean matchingColumn(final Cell left, final byte[] fam, final int foffset,
780    final int flength, final byte[] qual, final int qoffset, final int qlength) {
781    if (!matchingFamily(left, fam, foffset, flength)) {
782      return false;
783    }
784    return matchingQualifier(left, qual, qoffset, qlength);
785  }
786
787  public static boolean matchingValue(final Cell left, final Cell right, int lvlength,
788    int rvlength) {
789    if (left instanceof ByteBufferExtendedCell && right instanceof ByteBufferExtendedCell) {
790      return ByteBufferUtils.equals(((ByteBufferExtendedCell) left).getValueByteBuffer(),
791        ((ByteBufferExtendedCell) left).getValuePosition(), lvlength,
792        ((ByteBufferExtendedCell) right).getValueByteBuffer(),
793        ((ByteBufferExtendedCell) right).getValuePosition(), rvlength);
794    }
795    if (left instanceof ByteBufferExtendedCell) {
796      return ByteBufferUtils.equals(((ByteBufferExtendedCell) left).getValueByteBuffer(),
797        ((ByteBufferExtendedCell) left).getValuePosition(), lvlength, right.getValueArray(),
798        right.getValueOffset(), rvlength);
799    }
800    if (right instanceof ByteBufferExtendedCell) {
801      return ByteBufferUtils.equals(((ByteBufferExtendedCell) right).getValueByteBuffer(),
802        ((ByteBufferExtendedCell) right).getValuePosition(), rvlength, left.getValueArray(),
803        left.getValueOffset(), lvlength);
804    }
805    return Bytes.equals(left.getValueArray(), left.getValueOffset(), lvlength,
806      right.getValueArray(), right.getValueOffset(), rvlength);
807  }
808
809  public static boolean matchingType(ExtendedCell a, ExtendedCell b) {
810    return a.getTypeByte() == b.getTypeByte();
811  }
812
813  public static boolean matchingTags(final ExtendedCell left, final ExtendedCell right) {
814    return matchingTags(left, right, left.getTagsLength(), right.getTagsLength());
815  }
816
817  public static boolean matchingTags(final ExtendedCell left, final ExtendedCell right, int llength,
818    int rlength) {
819    if (left instanceof ByteBufferExtendedCell && right instanceof ByteBufferExtendedCell) {
820      ByteBufferExtendedCell leftBBCell = (ByteBufferExtendedCell) left;
821      ByteBufferExtendedCell rightBBCell = (ByteBufferExtendedCell) right;
822      return ByteBufferUtils.equals(leftBBCell.getTagsByteBuffer(), leftBBCell.getTagsPosition(),
823        llength, rightBBCell.getTagsByteBuffer(), rightBBCell.getTagsPosition(), rlength);
824    }
825    if (left instanceof ByteBufferExtendedCell) {
826      ByteBufferExtendedCell leftBBCell = (ByteBufferExtendedCell) left;
827      return ByteBufferUtils.equals(leftBBCell.getTagsByteBuffer(), leftBBCell.getTagsPosition(),
828        llength, right.getTagsArray(), right.getTagsOffset(), rlength);
829    }
830    if (right instanceof ByteBufferExtendedCell) {
831      ByteBufferExtendedCell rightBBCell = (ByteBufferExtendedCell) right;
832      return ByteBufferUtils.equals(rightBBCell.getTagsByteBuffer(), rightBBCell.getTagsPosition(),
833        rlength, left.getTagsArray(), left.getTagsOffset(), llength);
834    }
835    return Bytes.equals(left.getTagsArray(), left.getTagsOffset(), llength, right.getTagsArray(),
836      right.getTagsOffset(), rlength);
837  }
838
839  /**
840   * Return true if a delete type, a {@link KeyValue.Type#Delete} or a {KeyValue.Type#DeleteFamily}
841   * or a {@link KeyValue.Type#DeleteColumn} KeyValue type.
842   */
843  public static boolean isDelete(final byte type) {
844    return KeyValue.Type.Delete.getCode() <= type && type <= KeyValue.Type.DeleteFamily.getCode();
845  }
846
847  /** Returns True if this cell is a {@link KeyValue.Type#Delete} type. */
848  public static boolean isDeleteType(ExtendedCell cell) {
849    return cell.getTypeByte() == KeyValue.Type.Delete.getCode();
850  }
851
852  public static boolean isDeleteFamily(final ExtendedCell cell) {
853    return cell.getTypeByte() == KeyValue.Type.DeleteFamily.getCode();
854  }
855
856  public static boolean isDeleteFamilyVersion(final ExtendedCell cell) {
857    return cell.getTypeByte() == KeyValue.Type.DeleteFamilyVersion.getCode();
858  }
859
860  public static boolean isDeleteColumns(final ExtendedCell cell) {
861    return cell.getTypeByte() == KeyValue.Type.DeleteColumn.getCode();
862  }
863
864  public static boolean isDeleteColumnVersion(final ExtendedCell cell) {
865    return cell.getTypeByte() == KeyValue.Type.Delete.getCode();
866  }
867
868  /** Returns True if this cell is a delete family or column type. */
869  public static boolean isDeleteColumnOrFamily(ExtendedCell cell) {
870    int t = cell.getTypeByte();
871    return t == KeyValue.Type.DeleteColumn.getCode() || t == KeyValue.Type.DeleteFamily.getCode();
872  }
873
874  public static byte[] cloneTags(ExtendedCell cell) {
875    byte[] output = new byte[cell.getTagsLength()];
876    copyTagsTo(cell, output, 0);
877    return output;
878  }
879
880  /** Copies the tags info into the tag portion of the cell */
881  public static int copyTagsTo(ExtendedCell cell, byte[] destination, int destinationOffset) {
882    int tlen = cell.getTagsLength();
883    if (cell instanceof ByteBufferExtendedCell) {
884      ByteBufferUtils.copyFromBufferToArray(destination,
885        ((ByteBufferExtendedCell) cell).getTagsByteBuffer(),
886        ((ByteBufferExtendedCell) cell).getTagsPosition(), destinationOffset, tlen);
887    } else {
888      System.arraycopy(cell.getTagsArray(), cell.getTagsOffset(), destination, destinationOffset,
889        tlen);
890    }
891    return destinationOffset + tlen;
892  }
893
894  /** Copies the tags info into the tag portion of the cell */
895  public static int copyTagsTo(ExtendedCell cell, ByteBuffer destination, int destinationOffset) {
896    int tlen = cell.getTagsLength();
897    if (cell instanceof ByteBufferExtendedCell) {
898      ByteBufferUtils.copyFromBufferToBuffer(((ByteBufferExtendedCell) cell).getTagsByteBuffer(),
899        destination, ((ByteBufferExtendedCell) cell).getTagsPosition(), destinationOffset, tlen);
900    } else {
901      ByteBufferUtils.copyFromArrayToBuffer(destination, destinationOffset, cell.getTagsArray(),
902        cell.getTagsOffset(), tlen);
903    }
904    return destinationOffset + tlen;
905  }
906
907  /**
908   * Return tags in the given Cell as a List
909   * @param cell The Cell
910   * @return Tags in the given Cell as a List
911   */
912  public static List<Tag> getTags(ExtendedCell cell) {
913    List<Tag> tags = new ArrayList<>();
914    Iterator<Tag> tagsItr = tagsIterator(cell);
915    while (tagsItr.hasNext()) {
916      tags.add(tagsItr.next());
917    }
918    return tags;
919  }
920
921  /**
922   * Retrieve Cell's first tag, matching the passed in type
923   * @param cell The Cell
924   * @param type Type of the Tag to retrieve
925   * @return Optional, empty if there is no tag of the passed in tag type
926   */
927  public static Optional<Tag> getTag(ExtendedCell cell, byte type) {
928    boolean bufferBacked = cell instanceof ByteBufferExtendedCell;
929    int length = cell.getTagsLength();
930    int offset =
931      bufferBacked ? ((ByteBufferExtendedCell) cell).getTagsPosition() : cell.getTagsOffset();
932    int pos = offset;
933    while (pos < offset + length) {
934      int tagLen;
935      if (bufferBacked) {
936        ByteBuffer tagsBuffer = ((ByteBufferExtendedCell) cell).getTagsByteBuffer();
937        tagLen = ByteBufferUtils.readAsInt(tagsBuffer, pos, TAG_LENGTH_SIZE);
938        if (ByteBufferUtils.toByte(tagsBuffer, pos + TAG_LENGTH_SIZE) == type) {
939          return Optional.of(new ByteBufferTag(tagsBuffer, pos, tagLen + TAG_LENGTH_SIZE));
940        }
941      } else {
942        tagLen = Bytes.readAsInt(cell.getTagsArray(), pos, TAG_LENGTH_SIZE);
943        if (cell.getTagsArray()[pos + TAG_LENGTH_SIZE] == type) {
944          return Optional
945            .of(new ArrayBackedTag(cell.getTagsArray(), pos, tagLen + TAG_LENGTH_SIZE));
946        }
947      }
948      pos += TAG_LENGTH_SIZE + tagLen;
949    }
950    return Optional.empty();
951  }
952
953  /**
954   * Utility method to iterate through the tags in the given cell.
955   * @param cell The Cell over which tags iterator is needed.
956   * @return iterator for the tags
957   */
958  public static Iterator<Tag> tagsIterator(final ExtendedCell cell) {
959    final int tagsLength = cell.getTagsLength();
960    // Save an object allocation where we can
961    if (tagsLength == 0) {
962      return TagUtil.EMPTY_TAGS_ITR;
963    }
964    if (cell instanceof ByteBufferExtendedCell) {
965      return tagsIterator(((ByteBufferExtendedCell) cell).getTagsByteBuffer(),
966        ((ByteBufferExtendedCell) cell).getTagsPosition(), tagsLength);
967    }
968
969    return new Iterator<Tag>() {
970      private int offset = cell.getTagsOffset();
971      private int pos = offset;
972      private int endOffset = offset + cell.getTagsLength() - 1;
973
974      @Override
975      public boolean hasNext() {
976        return this.pos < endOffset;
977      }
978
979      @Override
980      public Tag next() {
981        if (hasNext()) {
982          byte[] tags = cell.getTagsArray();
983          int curTagLen = Bytes.readAsInt(tags, this.pos, Tag.TAG_LENGTH_SIZE);
984          Tag tag = new ArrayBackedTag(tags, pos, curTagLen + TAG_LENGTH_SIZE);
985          this.pos += Bytes.SIZEOF_SHORT + curTagLen;
986          return tag;
987        }
988        return null;
989      }
990
991      @Override
992      public void remove() {
993        throw new UnsupportedOperationException();
994      }
995    };
996  }
997
998  public static Iterator<Tag> tagsIterator(final ByteBuffer tags, final int offset,
999    final int length) {
1000    return new Iterator<Tag>() {
1001      private int pos = offset;
1002      private int endOffset = offset + length - 1;
1003
1004      @Override
1005      public boolean hasNext() {
1006        return this.pos < endOffset;
1007      }
1008
1009      @Override
1010      public Tag next() {
1011        if (hasNext()) {
1012          int curTagLen = ByteBufferUtils.readAsInt(tags, this.pos, Tag.TAG_LENGTH_SIZE);
1013          Tag tag = new ByteBufferTag(tags, pos, curTagLen + Tag.TAG_LENGTH_SIZE);
1014          this.pos += Bytes.SIZEOF_SHORT + curTagLen;
1015          return tag;
1016        }
1017        return null;
1018      }
1019
1020      @Override
1021      public void remove() {
1022        throw new UnsupportedOperationException();
1023      }
1024    };
1025  }
1026
1027  /**
1028   * Returns true if the first range start1...end1 overlaps with the second range start2...end2,
1029   * assuming the byte arrays represent row keys
1030   */
1031  public static boolean overlappingKeys(final byte[] start1, final byte[] end1, final byte[] start2,
1032    final byte[] end2) {
1033    return (end2.length == 0 || start1.length == 0 || Bytes.compareTo(start1, end2) < 0)
1034      && (end1.length == 0 || start2.length == 0 || Bytes.compareTo(start2, end1) < 0);
1035  }
1036
1037  /** Write rowkey excluding the common part. */
1038  public static void writeRowKeyExcludingCommon(Cell cell, short rLen, int commonPrefix,
1039    DataOutputStream out) throws IOException {
1040    if (commonPrefix == 0) {
1041      out.writeShort(rLen);
1042    } else if (commonPrefix == 1) {
1043      out.writeByte((byte) rLen);
1044      commonPrefix--;
1045    } else {
1046      commonPrefix -= KeyValue.ROW_LENGTH_SIZE;
1047    }
1048    if (rLen > commonPrefix) {
1049      writeRowSkippingBytes(out, cell, rLen, commonPrefix);
1050    }
1051  }
1052
1053  /**
1054   * Writes the row from the given cell to the output stream excluding the common prefix
1055   * @param out     The dataoutputstream to which the data has to be written
1056   * @param cell    The cell whose contents has to be written
1057   * @param rlength the row length
1058   */
1059  public static void writeRowSkippingBytes(DataOutputStream out, Cell cell, short rlength,
1060    int commonPrefix) throws IOException {
1061    if (cell instanceof ByteBufferExtendedCell) {
1062      ByteBufferUtils.copyBufferToStream((DataOutput) out,
1063        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
1064        ((ByteBufferExtendedCell) cell).getRowPosition() + commonPrefix, rlength - commonPrefix);
1065    } else {
1066      out.write(cell.getRowArray(), cell.getRowOffset() + commonPrefix, rlength - commonPrefix);
1067    }
1068  }
1069
1070  /**
1071   * Find length of common prefix in keys of the cells, considering key as byte[] if serialized in
1072   * {@link KeyValue}. The key format is &lt;2 bytes rk len&gt;&lt;rk&gt;&lt;1 byte cf
1073   * len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes timestamp&gt;&lt;1 byte type&gt;
1074   * @param c1                the cell
1075   * @param c2                the cell
1076   * @param bypassFamilyCheck when true assume the family bytes same in both cells. Pass it as true
1077   *                          when dealing with Cells in same CF so as to avoid some checks
1078   * @param withTsType        when true check timestamp and type bytes also.
1079   * @return length of common prefix
1080   */
1081  public static int findCommonPrefixInFlatKey(ExtendedCell c1, ExtendedCell c2,
1082    boolean bypassFamilyCheck, boolean withTsType) {
1083    // Compare the 2 bytes in RK length part
1084    short rLen1 = c1.getRowLength();
1085    short rLen2 = c2.getRowLength();
1086    int commonPrefix = KeyValue.ROW_LENGTH_SIZE;
1087    if (rLen1 != rLen2) {
1088      // early out when the RK length itself is not matching
1089      return ByteBufferUtils.findCommonPrefix(Bytes.toBytes(rLen1), 0, KeyValue.ROW_LENGTH_SIZE,
1090        Bytes.toBytes(rLen2), 0, KeyValue.ROW_LENGTH_SIZE);
1091    }
1092    // Compare the RKs
1093    int rkCommonPrefix = 0;
1094    if (c1 instanceof ByteBufferExtendedCell && c2 instanceof ByteBufferExtendedCell) {
1095      rkCommonPrefix =
1096        ByteBufferUtils.findCommonPrefix(((ByteBufferExtendedCell) c1).getRowByteBuffer(),
1097          ((ByteBufferExtendedCell) c1).getRowPosition(), rLen1,
1098          ((ByteBufferExtendedCell) c2).getRowByteBuffer(),
1099          ((ByteBufferExtendedCell) c2).getRowPosition(), rLen2);
1100    } else {
1101      // There cannot be a case where one cell is BBCell and other is KeyValue. This flow comes
1102      // either
1103      // in flush or compactions. In flushes both cells are KV and in case of compaction it will be
1104      // either
1105      // KV or BBCell
1106      rkCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getRowArray(), c1.getRowOffset(), rLen1,
1107        c2.getRowArray(), c2.getRowOffset(), rLen2);
1108    }
1109    commonPrefix += rkCommonPrefix;
1110    if (rkCommonPrefix != rLen1) {
1111      // Early out when RK is not fully matching.
1112      return commonPrefix;
1113    }
1114    // Compare 1 byte CF length part
1115    byte fLen1 = c1.getFamilyLength();
1116    if (bypassFamilyCheck) {
1117      // This flag will be true when caller is sure that the family will be same for both the cells
1118      // Just make commonPrefix to increment by the family part
1119      commonPrefix += KeyValue.FAMILY_LENGTH_SIZE + fLen1;
1120    } else {
1121      byte fLen2 = c2.getFamilyLength();
1122      if (fLen1 != fLen2) {
1123        // early out when the CF length itself is not matching
1124        return commonPrefix;
1125      }
1126      // CF lengths are same so there is one more byte common in key part
1127      commonPrefix += KeyValue.FAMILY_LENGTH_SIZE;
1128      // Compare the CF names
1129      int fCommonPrefix;
1130      if (c1 instanceof ByteBufferExtendedCell && c2 instanceof ByteBufferExtendedCell) {
1131        fCommonPrefix =
1132          ByteBufferUtils.findCommonPrefix(((ByteBufferExtendedCell) c1).getFamilyByteBuffer(),
1133            ((ByteBufferExtendedCell) c1).getFamilyPosition(), fLen1,
1134            ((ByteBufferExtendedCell) c2).getFamilyByteBuffer(),
1135            ((ByteBufferExtendedCell) c2).getFamilyPosition(), fLen2);
1136      } else {
1137        fCommonPrefix = ByteBufferUtils.findCommonPrefix(c1.getFamilyArray(), c1.getFamilyOffset(),
1138          fLen1, c2.getFamilyArray(), c2.getFamilyOffset(), fLen2);
1139      }
1140      commonPrefix += fCommonPrefix;
1141      if (fCommonPrefix != fLen1) {
1142        return commonPrefix;
1143      }
1144    }
1145    // Compare the Qualifiers
1146    int qLen1 = c1.getQualifierLength();
1147    int qLen2 = c2.getQualifierLength();
1148    int qCommon;
1149    if (c1 instanceof ByteBufferExtendedCell && c2 instanceof ByteBufferExtendedCell) {
1150      qCommon =
1151        ByteBufferUtils.findCommonPrefix(((ByteBufferExtendedCell) c1).getQualifierByteBuffer(),
1152          ((ByteBufferExtendedCell) c1).getQualifierPosition(), qLen1,
1153          ((ByteBufferExtendedCell) c2).getQualifierByteBuffer(),
1154          ((ByteBufferExtendedCell) c2).getQualifierPosition(), qLen2);
1155    } else {
1156      qCommon = ByteBufferUtils.findCommonPrefix(c1.getQualifierArray(), c1.getQualifierOffset(),
1157        qLen1, c2.getQualifierArray(), c2.getQualifierOffset(), qLen2);
1158    }
1159    commonPrefix += qCommon;
1160    if (!withTsType || Math.max(qLen1, qLen2) != qCommon) {
1161      return commonPrefix;
1162    }
1163    // Compare the timestamp parts
1164    int tsCommonPrefix = ByteBufferUtils.findCommonPrefix(Bytes.toBytes(c1.getTimestamp()), 0,
1165      KeyValue.TIMESTAMP_SIZE, Bytes.toBytes(c2.getTimestamp()), 0, KeyValue.TIMESTAMP_SIZE);
1166    commonPrefix += tsCommonPrefix;
1167    if (tsCommonPrefix != KeyValue.TIMESTAMP_SIZE) {
1168      return commonPrefix;
1169    }
1170    // Compare the type
1171    if (c1.getTypeByte() == c2.getTypeByte()) {
1172      commonPrefix += KeyValue.TYPE_SIZE;
1173    }
1174    return commonPrefix;
1175  }
1176
1177  /**
1178   * Used to compare two cells based on the column hint provided. This is specifically used when we
1179   * need to optimize the seeks based on the next indexed key. This is an advanced usage API
1180   * specifically needed for some optimizations.
1181   * @param nextIndexedCell the next indexed cell
1182   * @param currentCell     the cell to be compared
1183   * @param foff            the family offset of the currentCell
1184   * @param flen            the family length of the currentCell
1185   * @param colHint         the column hint provided - could be null
1186   * @param coff            the offset of the column hint if provided, if not offset of the
1187   *                        currentCell's qualifier
1188   * @param clen            the length of the column hint if provided, if not length of the
1189   *                        currentCell's qualifier
1190   * @param ts              the timestamp to be seeked
1191   * @param type            the type to be seeked
1192   * @return an int based on the given column hint TODO : To be moved out of here because this is a
1193   *         special API used in scan optimization.
1194   */
1195  // compare a key against row/fam/qual/ts/type
1196  public static final int compareKeyBasedOnColHint(CellComparator comparator,
1197    ExtendedCell nextIndexedCell, ExtendedCell currentCell, int foff, int flen, byte[] colHint,
1198    int coff, int clen, long ts, byte type) {
1199    int compare = comparator.compareRows(nextIndexedCell, currentCell);
1200    if (compare != 0) {
1201      return compare;
1202    }
1203    // If the column is not specified, the "minimum" key type appears the
1204    // latest in the sorted order, regardless of the timestamp. This is used
1205    // for specifying the last key/value in a given row, because there is no
1206    // "lexicographically last column" (it would be infinitely long). The
1207    // "maximum" key type does not need this behavior.
1208    if (
1209      nextIndexedCell.getFamilyLength() + nextIndexedCell.getQualifierLength() == 0
1210        && nextIndexedCell.getTypeByte() == KeyValue.Type.Minimum.getCode()
1211    ) {
1212      // left is "bigger", i.e. it appears later in the sorted order
1213      return 1;
1214    }
1215    if (flen + clen == 0 && type == KeyValue.Type.Minimum.getCode()) {
1216      return -1;
1217    }
1218
1219    compare = comparator.compareFamilies(nextIndexedCell, currentCell);
1220    if (compare != 0) {
1221      return compare;
1222    }
1223    if (colHint == null) {
1224      compare = comparator.compareQualifiers(nextIndexedCell, currentCell);
1225    } else {
1226      compare = CellUtil.compareQualifiers(nextIndexedCell, colHint, coff, clen);
1227    }
1228    if (compare != 0) {
1229      return compare;
1230    }
1231    // Next compare timestamps.
1232    compare = comparator.compareTimestamps(nextIndexedCell.getTimestamp(), ts);
1233    if (compare != 0) {
1234      return compare;
1235    }
1236
1237    // Compare types. Let the delete types sort ahead of puts; i.e. types
1238    // of higher numbers sort before those of lesser numbers. Maximum (255)
1239    // appears ahead of everything, and minimum (0) appears after
1240    // everything.
1241    return (0xff & type) - (0xff & nextIndexedCell.getTypeByte());
1242  }
1243
1244  /**
1245   * Compares only the key portion of a cell. It does not include the sequence id/mvcc of the cell
1246   * @return an int greater than 0 if left &gt; than right lesser than 0 if left &lt; than right
1247   *         equal to 0 if left is equal to right
1248   */
1249  public static final int compareKeyIgnoresMvcc(CellComparator comparator, Cell left, Cell right) {
1250    return ((CellComparatorImpl) comparator).compare(left, right, true);
1251  }
1252
1253  /**
1254   * Compare cell's row against given comparator
1255   * @param cell       the cell to use for comparison
1256   * @param comparator the {@link CellComparator} to use for comparison
1257   * @return result comparing cell's row
1258   */
1259  public static int compareRow(Cell cell, ByteArrayComparable comparator) {
1260    if (cell instanceof ByteBufferExtendedCell) {
1261      return comparator.compareTo(((ByteBufferExtendedCell) cell).getRowByteBuffer(),
1262        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength());
1263    }
1264    return comparator.compareTo(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
1265  }
1266
1267  /**
1268   * Compare cell's column family against given comparator
1269   * @param cell       the cell to use for comparison
1270   * @param comparator the {@link CellComparator} to use for comparison
1271   * @return result comparing cell's column family
1272   */
1273  public static int compareFamily(Cell cell, ByteArrayComparable comparator) {
1274    if (cell instanceof ByteBufferExtendedCell) {
1275      return comparator.compareTo(((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
1276        ((ByteBufferExtendedCell) cell).getFamilyPosition(), cell.getFamilyLength());
1277    }
1278    return comparator.compareTo(cell.getFamilyArray(), cell.getFamilyOffset(),
1279      cell.getFamilyLength());
1280  }
1281
1282  /**
1283   * Compare cell's qualifier against given comparator
1284   * @param cell       the cell to use for comparison
1285   * @param comparator the {@link CellComparator} to use for comparison
1286   * @return result comparing cell's qualifier
1287   */
1288  public static int compareQualifier(Cell cell, ByteArrayComparable comparator) {
1289    if (cell instanceof ByteBufferExtendedCell) {
1290      return comparator.compareTo(((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
1291        ((ByteBufferExtendedCell) cell).getQualifierPosition(), cell.getQualifierLength());
1292    }
1293    return comparator.compareTo(cell.getQualifierArray(), cell.getQualifierOffset(),
1294      cell.getQualifierLength());
1295  }
1296
1297  public static Cell.Type toType(byte type) {
1298    KeyValue.Type codeToType = KeyValue.Type.codeToType(type);
1299    switch (codeToType) {
1300      case Put:
1301        return Cell.Type.Put;
1302      case Delete:
1303        return Cell.Type.Delete;
1304      case DeleteColumn:
1305        return Cell.Type.DeleteColumn;
1306      case DeleteFamily:
1307        return Cell.Type.DeleteFamily;
1308      case DeleteFamilyVersion:
1309        return Cell.Type.DeleteFamilyVersion;
1310      default:
1311        throw new UnsupportedOperationException("Invalid type of cell " + type);
1312    }
1313  }
1314
1315  public static KeyValue.Type toTypeByte(Cell.Type type) {
1316    switch (type) {
1317      case Put:
1318        return KeyValue.Type.Put;
1319      case Delete:
1320        return KeyValue.Type.Delete;
1321      case DeleteColumn:
1322        return KeyValue.Type.DeleteColumn;
1323      case DeleteFamilyVersion:
1324        return KeyValue.Type.DeleteFamilyVersion;
1325      case DeleteFamily:
1326        return KeyValue.Type.DeleteFamily;
1327      default:
1328        throw new UnsupportedOperationException("Unsupported data type:" + type);
1329    }
1330  }
1331
1332  /**
1333   * Compare cell's value against given comparator
1334   * @param cell       the cell to use for comparison
1335   * @param comparator the {@link CellComparator} to use for comparison
1336   * @return result comparing cell's value
1337   */
1338  public static int compareValue(Cell cell, ByteArrayComparable comparator) {
1339    if (cell instanceof ByteBufferExtendedCell) {
1340      return comparator.compareTo(((ByteBufferExtendedCell) cell).getValueByteBuffer(),
1341        ((ByteBufferExtendedCell) cell).getValuePosition(), cell.getValueLength());
1342    }
1343    return comparator.compareTo(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
1344  }
1345
1346  /**
1347   * These cells are used in reseeks/seeks to improve the read performance. They are not real cells
1348   * that are returned back to the clients
1349   */
1350  private static abstract class EmptyCell implements ExtendedCell {
1351
1352    @Override
1353    public void setSequenceId(long seqId) {
1354      // Fake cells don't need seqId, so leaving it as a noop.
1355    }
1356
1357    @Override
1358    public void setTimestamp(long ts) {
1359      // Fake cells can't be changed timestamp, so leaving it as a noop.
1360    }
1361
1362    @Override
1363    public void setTimestamp(byte[] ts) {
1364      // Fake cells can't be changed timestamp, so leaving it as a noop.
1365    }
1366
1367    @Override
1368    public byte[] getRowArray() {
1369      return EMPTY_BYTE_ARRAY;
1370    }
1371
1372    @Override
1373    public int getRowOffset() {
1374      return 0;
1375    }
1376
1377    @Override
1378    public short getRowLength() {
1379      return 0;
1380    }
1381
1382    @Override
1383    public byte[] getFamilyArray() {
1384      return EMPTY_BYTE_ARRAY;
1385    }
1386
1387    @Override
1388    public int getFamilyOffset() {
1389      return 0;
1390    }
1391
1392    @Override
1393    public byte getFamilyLength() {
1394      return 0;
1395    }
1396
1397    @Override
1398    public byte[] getQualifierArray() {
1399      return EMPTY_BYTE_ARRAY;
1400    }
1401
1402    @Override
1403    public int getQualifierOffset() {
1404      return 0;
1405    }
1406
1407    @Override
1408    public int getQualifierLength() {
1409      return 0;
1410    }
1411
1412    @Override
1413    public long getSequenceId() {
1414      return 0;
1415    }
1416
1417    @Override
1418    public byte[] getValueArray() {
1419      return EMPTY_BYTE_ARRAY;
1420    }
1421
1422    @Override
1423    public int getValueOffset() {
1424      return 0;
1425    }
1426
1427    @Override
1428    public int getValueLength() {
1429      return 0;
1430    }
1431
1432    @Override
1433    public byte[] getTagsArray() {
1434      return EMPTY_BYTE_ARRAY;
1435    }
1436
1437    @Override
1438    public int getTagsOffset() {
1439      return 0;
1440    }
1441
1442    @Override
1443    public int getTagsLength() {
1444      return 0;
1445    }
1446  }
1447
1448  /**
1449   * These cells are used in reseeks/seeks to improve the read performance. They are not real cells
1450   * that are returned back to the clients
1451   */
1452  private static abstract class EmptyByteBufferExtendedCell extends ByteBufferExtendedCell {
1453
1454    @Override
1455    public void setSequenceId(long seqId) {
1456      // Fake cells don't need seqId, so leaving it as a noop.
1457    }
1458
1459    @Override
1460    public void setTimestamp(long ts) {
1461      // Fake cells can't be changed timestamp, so leaving it as a noop.
1462    }
1463
1464    @Override
1465    public void setTimestamp(byte[] ts) {
1466      // Fake cells can't be changed timestamp, so leaving it as a noop.
1467    }
1468
1469    @Override
1470    public byte[] getRowArray() {
1471      return CellUtil.cloneRow(this);
1472    }
1473
1474    @Override
1475    public int getRowOffset() {
1476      return 0;
1477    }
1478
1479    @Override
1480    public short getRowLength() {
1481      return 0;
1482    }
1483
1484    @Override
1485    public byte[] getFamilyArray() {
1486      return CellUtil.cloneFamily(this);
1487    }
1488
1489    @Override
1490    public int getFamilyOffset() {
1491      return 0;
1492    }
1493
1494    @Override
1495    public byte getFamilyLength() {
1496      return 0;
1497    }
1498
1499    @Override
1500    public byte[] getQualifierArray() {
1501      return CellUtil.cloneQualifier(this);
1502    }
1503
1504    @Override
1505    public int getQualifierOffset() {
1506      return 0;
1507    }
1508
1509    @Override
1510    public int getQualifierLength() {
1511      return 0;
1512    }
1513
1514    @Override
1515    public long getSequenceId() {
1516      return 0;
1517    }
1518
1519    @Override
1520    public byte[] getValueArray() {
1521      return CellUtil.cloneValue(this);
1522    }
1523
1524    @Override
1525    public int getValueOffset() {
1526      return 0;
1527    }
1528
1529    @Override
1530    public int getValueLength() {
1531      return 0;
1532    }
1533
1534    @Override
1535    public byte[] getTagsArray() {
1536      return PrivateCellUtil.cloneTags(this);
1537    }
1538
1539    @Override
1540    public int getTagsOffset() {
1541      return 0;
1542    }
1543
1544    @Override
1545    public int getTagsLength() {
1546      return 0;
1547    }
1548
1549    @Override
1550    public ByteBuffer getRowByteBuffer() {
1551      return HConstants.EMPTY_BYTE_BUFFER;
1552    }
1553
1554    @Override
1555    public int getRowPosition() {
1556      return 0;
1557    }
1558
1559    @Override
1560    public ByteBuffer getFamilyByteBuffer() {
1561      return HConstants.EMPTY_BYTE_BUFFER;
1562    }
1563
1564    @Override
1565    public int getFamilyPosition() {
1566      return 0;
1567    }
1568
1569    @Override
1570    public ByteBuffer getQualifierByteBuffer() {
1571      return HConstants.EMPTY_BYTE_BUFFER;
1572    }
1573
1574    @Override
1575    public int getQualifierPosition() {
1576      return 0;
1577    }
1578
1579    @Override
1580    public ByteBuffer getTagsByteBuffer() {
1581      return HConstants.EMPTY_BYTE_BUFFER;
1582    }
1583
1584    @Override
1585    public int getTagsPosition() {
1586      return 0;
1587    }
1588
1589    @Override
1590    public ByteBuffer getValueByteBuffer() {
1591      return HConstants.EMPTY_BYTE_BUFFER;
1592    }
1593
1594    @Override
1595    public int getValuePosition() {
1596      return 0;
1597    }
1598  }
1599
1600  private static class FirstOnRowCell extends EmptyCell {
1601    // @formatter:off
1602    private static final int FIXED_HEAPSIZE =
1603        ClassSize.OBJECT // object
1604      + ClassSize.REFERENCE // row array
1605      + Bytes.SIZEOF_INT // row offset
1606      + Bytes.SIZEOF_SHORT;  // row length
1607    // @formatter:on
1608    private final byte[] rowArray;
1609    private final int roffset;
1610    private final short rlength;
1611
1612    public FirstOnRowCell(final byte[] row, int roffset, short rlength) {
1613      this.rowArray = row;
1614      this.roffset = roffset;
1615      this.rlength = rlength;
1616    }
1617
1618    @Override
1619    public long heapSize() {
1620      return ClassSize.align(FIXED_HEAPSIZE)
1621        // array overhead
1622        + (rlength == 0 ? ClassSize.sizeOfByteArray(rlength) : rlength);
1623    }
1624
1625    @Override
1626    public byte[] getRowArray() {
1627      return this.rowArray;
1628    }
1629
1630    @Override
1631    public int getRowOffset() {
1632      return this.roffset;
1633    }
1634
1635    @Override
1636    public short getRowLength() {
1637      return this.rlength;
1638    }
1639
1640    @Override
1641    public long getTimestamp() {
1642      return HConstants.LATEST_TIMESTAMP;
1643    }
1644
1645    @Override
1646    public byte getTypeByte() {
1647      return KeyValue.Type.Maximum.getCode();
1648    }
1649
1650    @Override
1651    public Cell.Type getType() {
1652      throw new UnsupportedOperationException();
1653    }
1654  }
1655
1656  private static class FirstOnRowByteBufferExtendedCell extends EmptyByteBufferExtendedCell {
1657    // @formatter:off
1658    private static final int FIXED_OVERHEAD =
1659        ClassSize.OBJECT // object
1660        + ClassSize.REFERENCE // row buffer
1661        + Bytes.SIZEOF_INT // row offset
1662        + Bytes.SIZEOF_SHORT; // row length
1663    // @formatter:on
1664    private final ByteBuffer rowBuff;
1665    private final int roffset;
1666    private final short rlength;
1667
1668    public FirstOnRowByteBufferExtendedCell(final ByteBuffer row, int roffset, short rlength) {
1669      this.rowBuff = row;
1670      this.roffset = roffset;
1671      this.rlength = rlength;
1672    }
1673
1674    @Override
1675    public long heapSize() {
1676      if (this.rowBuff.hasArray()) {
1677        return ClassSize.align(FIXED_OVERHEAD + rlength);
1678      }
1679      return ClassSize.align(FIXED_OVERHEAD);
1680    }
1681
1682    @Override
1683    public ByteBuffer getRowByteBuffer() {
1684      return this.rowBuff;
1685    }
1686
1687    @Override
1688    public int getRowPosition() {
1689      return this.roffset;
1690    }
1691
1692    @Override
1693    public short getRowLength() {
1694      return this.rlength;
1695    }
1696
1697    @Override
1698    public long getTimestamp() {
1699      return HConstants.LATEST_TIMESTAMP;
1700    }
1701
1702    @Override
1703    public byte getTypeByte() {
1704      return KeyValue.Type.Maximum.getCode();
1705    }
1706
1707    @Override
1708    public Type getType() {
1709      throw new UnsupportedOperationException();
1710    }
1711  }
1712
1713  private static class LastOnRowByteBufferExtendedCell extends EmptyByteBufferExtendedCell {
1714    // @formatter:off
1715    private static final int FIXED_OVERHEAD = ClassSize.OBJECT // object
1716      + ClassSize.REFERENCE // rowBuff
1717      + Bytes.SIZEOF_INT // roffset
1718      + Bytes.SIZEOF_SHORT; // rlength
1719    // @formatter:on
1720    private final ByteBuffer rowBuff;
1721    private final int roffset;
1722    private final short rlength;
1723
1724    public LastOnRowByteBufferExtendedCell(final ByteBuffer row, int roffset, short rlength) {
1725      this.rowBuff = row;
1726      this.roffset = roffset;
1727      this.rlength = rlength;
1728    }
1729
1730    @Override
1731    public long heapSize() {
1732      if (this.rowBuff.hasArray()) {
1733        return ClassSize.align(FIXED_OVERHEAD + rlength);
1734      }
1735      return ClassSize.align(FIXED_OVERHEAD);
1736    }
1737
1738    @Override
1739    public ByteBuffer getRowByteBuffer() {
1740      return this.rowBuff;
1741    }
1742
1743    @Override
1744    public int getRowPosition() {
1745      return this.roffset;
1746    }
1747
1748    @Override
1749    public short getRowLength() {
1750      return this.rlength;
1751    }
1752
1753    @Override
1754    public long getTimestamp() {
1755      return PrivateConstants.OLDEST_TIMESTAMP;
1756    }
1757
1758    @Override
1759    public byte getTypeByte() {
1760      return KeyValue.Type.Minimum.getCode();
1761    }
1762
1763    @Override
1764    public Type getType() {
1765      throw new UnsupportedOperationException();
1766    }
1767  }
1768
1769  private static class FirstOnRowColByteBufferExtendedCell
1770    extends FirstOnRowByteBufferExtendedCell {
1771    // @formatter:off
1772    private static final int FIXED_OVERHEAD = FirstOnRowByteBufferExtendedCell.FIXED_OVERHEAD
1773      + ClassSize.REFERENCE * 2 // family buffer and column buffer
1774      + Bytes.SIZEOF_INT * 3 // famOffset, colOffset, colLength
1775      + Bytes.SIZEOF_BYTE; // famLength
1776    // @formatter:on
1777    private final ByteBuffer famBuff;
1778    private final int famOffset;
1779    private final byte famLength;
1780    private final ByteBuffer colBuff;
1781    private final int colOffset;
1782    private final int colLength;
1783
1784    public FirstOnRowColByteBufferExtendedCell(final ByteBuffer row, int roffset, short rlength,
1785      final ByteBuffer famBuff, final int famOffset, final byte famLength, final ByteBuffer col,
1786      final int colOffset, final int colLength) {
1787      super(row, roffset, rlength);
1788      this.famBuff = famBuff;
1789      this.famOffset = famOffset;
1790      this.famLength = famLength;
1791      this.colBuff = col;
1792      this.colOffset = colOffset;
1793      this.colLength = colLength;
1794    }
1795
1796    @Override
1797    public long heapSize() {
1798      if (famBuff.hasArray() && colBuff.hasArray()) {
1799        return ClassSize.align(FIXED_OVERHEAD + famLength + colLength);
1800      } else if (famBuff.hasArray()) {
1801        return ClassSize.align(FIXED_OVERHEAD + famLength);
1802      } else if (colBuff.hasArray()) {
1803        return ClassSize.align(FIXED_OVERHEAD + colLength);
1804      } else {
1805        return ClassSize.align(FIXED_OVERHEAD);
1806      }
1807    }
1808
1809    @Override
1810    public ByteBuffer getFamilyByteBuffer() {
1811      return this.famBuff;
1812    }
1813
1814    @Override
1815    public int getFamilyPosition() {
1816      return this.famOffset;
1817    }
1818
1819    @Override
1820    public byte getFamilyLength() {
1821      return famLength;
1822    }
1823
1824    @Override
1825    public ByteBuffer getQualifierByteBuffer() {
1826      return this.colBuff;
1827    }
1828
1829    @Override
1830    public int getQualifierPosition() {
1831      return this.colOffset;
1832    }
1833
1834    @Override
1835    public int getQualifierLength() {
1836      return this.colLength;
1837    }
1838  }
1839
1840  private static class FirstOnRowColCell extends FirstOnRowCell {
1841    // @formatter:off
1842    private static final long FIXED_HEAPSIZE = (long) FirstOnRowCell.FIXED_HEAPSIZE
1843      + Bytes.SIZEOF_BYTE // flength
1844      + Bytes.SIZEOF_INT * 3 // foffset, qoffset, qlength
1845      + ClassSize.REFERENCE * 2; // fArray, qArray
1846    // @formatter:on
1847    private final byte[] fArray;
1848    private final int foffset;
1849    private final byte flength;
1850    private final byte[] qArray;
1851    private final int qoffset;
1852    private final int qlength;
1853
1854    public FirstOnRowColCell(byte[] rArray, int roffset, short rlength, byte[] fArray, int foffset,
1855      byte flength, byte[] qArray, int qoffset, int qlength) {
1856      super(rArray, roffset, rlength);
1857      this.fArray = fArray;
1858      this.foffset = foffset;
1859      this.flength = flength;
1860      this.qArray = qArray;
1861      this.qoffset = qoffset;
1862      this.qlength = qlength;
1863    }
1864
1865    @Override
1866    public long heapSize() {
1867      return ClassSize.align(FIXED_HEAPSIZE)
1868        // array overhead
1869        + (flength == 0 ? ClassSize.sizeOfByteArray(flength) : flength)
1870        + (qlength == 0 ? ClassSize.sizeOfByteArray(qlength) : qlength);
1871    }
1872
1873    @Override
1874    public byte[] getFamilyArray() {
1875      return this.fArray;
1876    }
1877
1878    @Override
1879    public int getFamilyOffset() {
1880      return this.foffset;
1881    }
1882
1883    @Override
1884    public byte getFamilyLength() {
1885      return this.flength;
1886    }
1887
1888    @Override
1889    public byte[] getQualifierArray() {
1890      return this.qArray;
1891    }
1892
1893    @Override
1894    public int getQualifierOffset() {
1895      return this.qoffset;
1896    }
1897
1898    @Override
1899    public int getQualifierLength() {
1900      return this.qlength;
1901    }
1902  }
1903
1904  private static class FirstOnRowColTSCell extends FirstOnRowColCell {
1905    // @formatter:off
1906    private static final long FIXED_HEAPSIZE = FirstOnRowColCell.FIXED_HEAPSIZE
1907      + Bytes.SIZEOF_LONG; // ts
1908    private long ts;
1909    // @formatter:on
1910
1911    public FirstOnRowColTSCell(byte[] rArray, int roffset, short rlength, byte[] fArray,
1912      int foffset, byte flength, byte[] qArray, int qoffset, int qlength, long ts) {
1913      super(rArray, roffset, rlength, fArray, foffset, flength, qArray, qoffset, qlength);
1914      this.ts = ts;
1915    }
1916
1917    @Override
1918    public long getTimestamp() {
1919      return this.ts;
1920    }
1921
1922    @Override
1923    public long heapSize() {
1924      return ClassSize.align(FIXED_HEAPSIZE);
1925    }
1926  }
1927
1928  private static class FirstOnRowColTSByteBufferExtendedCell
1929    extends FirstOnRowColByteBufferExtendedCell {
1930    // @formatter:off
1931    private static final int FIXED_OVERHEAD = FirstOnRowColByteBufferExtendedCell.FIXED_OVERHEAD
1932      + Bytes.SIZEOF_LONG; // ts
1933    private long ts;
1934    // @formatter:on
1935
1936    public FirstOnRowColTSByteBufferExtendedCell(ByteBuffer rBuffer, int roffset, short rlength,
1937      ByteBuffer fBuffer, int foffset, byte flength, ByteBuffer qBuffer, int qoffset, int qlength,
1938      long ts) {
1939      super(rBuffer, roffset, rlength, fBuffer, foffset, flength, qBuffer, qoffset, qlength);
1940      this.ts = ts;
1941    }
1942
1943    @Override
1944    public long getTimestamp() {
1945      return this.ts;
1946    }
1947
1948    @Override
1949    public long heapSize() {
1950      return ClassSize.align(FIXED_OVERHEAD + super.heapSize());
1951    }
1952  }
1953
1954  private static class LastOnRowCell extends EmptyCell {
1955    // @formatter:off
1956    private static final int FIXED_OVERHEAD = ClassSize.OBJECT // object
1957      + ClassSize.REFERENCE // row array
1958      + Bytes.SIZEOF_INT // row offset
1959      + Bytes.SIZEOF_SHORT; // row length
1960    // @formatter:on
1961    private final byte[] rowArray;
1962    private final int roffset;
1963    private final short rlength;
1964
1965    public LastOnRowCell(byte[] row, int roffset, short rlength) {
1966      this.rowArray = row;
1967      this.roffset = roffset;
1968      this.rlength = rlength;
1969    }
1970
1971    @Override
1972    public long heapSize() {
1973      return ClassSize.align(FIXED_OVERHEAD)
1974        // array overhead
1975        + (rlength == 0 ? ClassSize.sizeOfByteArray(rlength) : rlength);
1976    }
1977
1978    @Override
1979    public byte[] getRowArray() {
1980      return this.rowArray;
1981    }
1982
1983    @Override
1984    public int getRowOffset() {
1985      return this.roffset;
1986    }
1987
1988    @Override
1989    public short getRowLength() {
1990      return this.rlength;
1991    }
1992
1993    @Override
1994    public long getTimestamp() {
1995      return PrivateConstants.OLDEST_TIMESTAMP;
1996    }
1997
1998    @Override
1999    public byte getTypeByte() {
2000      return KeyValue.Type.Minimum.getCode();
2001    }
2002
2003    @Override
2004    public Type getType() {
2005      throw new UnsupportedOperationException();
2006    }
2007  }
2008
2009  private static class LastOnRowColCell extends LastOnRowCell {
2010    // @formatter:off
2011    private static final long FIXED_OVERHEAD = (long) LastOnRowCell.FIXED_OVERHEAD
2012      + ClassSize.REFERENCE * 2 // fArray and qArray
2013      + Bytes.SIZEOF_INT * 3 // foffset, qoffset, qlength
2014      + Bytes.SIZEOF_BYTE; // flength
2015    // @formatter:on
2016    private final byte[] fArray;
2017    private final int foffset;
2018    private final byte flength;
2019    private final byte[] qArray;
2020    private final int qoffset;
2021    private final int qlength;
2022
2023    public LastOnRowColCell(byte[] rArray, int roffset, short rlength, byte[] fArray, int foffset,
2024      byte flength, byte[] qArray, int qoffset, int qlength) {
2025      super(rArray, roffset, rlength);
2026      this.fArray = fArray;
2027      this.foffset = foffset;
2028      this.flength = flength;
2029      this.qArray = qArray;
2030      this.qoffset = qoffset;
2031      this.qlength = qlength;
2032    }
2033
2034    @Override
2035    public long heapSize() {
2036      return ClassSize.align(FIXED_OVERHEAD)
2037        // array overhead
2038        + (flength == 0 ? ClassSize.sizeOfByteArray(flength) : flength)
2039        + (qlength == 0 ? ClassSize.sizeOfByteArray(qlength) : qlength);
2040    }
2041
2042    @Override
2043    public byte[] getFamilyArray() {
2044      return this.fArray;
2045    }
2046
2047    @Override
2048    public int getFamilyOffset() {
2049      return this.foffset;
2050    }
2051
2052    @Override
2053    public byte getFamilyLength() {
2054      return this.flength;
2055    }
2056
2057    @Override
2058    public byte[] getQualifierArray() {
2059      return this.qArray;
2060    }
2061
2062    @Override
2063    public int getQualifierOffset() {
2064      return this.qoffset;
2065    }
2066
2067    @Override
2068    public int getQualifierLength() {
2069      return this.qlength;
2070    }
2071  }
2072
2073  private static class LastOnRowColByteBufferExtendedCell extends LastOnRowByteBufferExtendedCell {
2074    // @formatter:off
2075    private static final int FIXED_OVERHEAD = LastOnRowByteBufferExtendedCell.FIXED_OVERHEAD
2076      + ClassSize.REFERENCE * 2 // fBuffer and qBuffer
2077      + Bytes.SIZEOF_INT * 3 // foffset, qoffset, qlength
2078      + Bytes.SIZEOF_BYTE; // flength
2079    // @formatter:on
2080    private final ByteBuffer fBuffer;
2081    private final int foffset;
2082    private final byte flength;
2083    private final ByteBuffer qBuffer;
2084    private final int qoffset;
2085    private final int qlength;
2086
2087    public LastOnRowColByteBufferExtendedCell(ByteBuffer rBuffer, int roffset, short rlength,
2088      ByteBuffer fBuffer, int foffset, byte flength, ByteBuffer qBuffer, int qoffset, int qlength) {
2089      super(rBuffer, roffset, rlength);
2090      this.fBuffer = fBuffer;
2091      this.foffset = foffset;
2092      this.flength = flength;
2093      this.qBuffer = qBuffer;
2094      this.qoffset = qoffset;
2095      this.qlength = qlength;
2096    }
2097
2098    @Override
2099    public long heapSize() {
2100      if (fBuffer.hasArray() && qBuffer.hasArray()) {
2101        return ClassSize.align(FIXED_OVERHEAD + flength + qlength);
2102      } else if (fBuffer.hasArray()) {
2103        return ClassSize.align(FIXED_OVERHEAD + flength);
2104      } else if (qBuffer.hasArray()) {
2105        return ClassSize.align(FIXED_OVERHEAD + qlength);
2106      } else {
2107        return ClassSize.align(FIXED_OVERHEAD);
2108      }
2109    }
2110
2111    @Override
2112    public ByteBuffer getFamilyByteBuffer() {
2113      return this.fBuffer;
2114    }
2115
2116    @Override
2117    public int getFamilyPosition() {
2118      return this.foffset;
2119    }
2120
2121    @Override
2122    public byte getFamilyLength() {
2123      return this.flength;
2124    }
2125
2126    @Override
2127    public ByteBuffer getQualifierByteBuffer() {
2128      return this.qBuffer;
2129    }
2130
2131    @Override
2132    public int getQualifierPosition() {
2133      return this.qoffset;
2134    }
2135
2136    @Override
2137    public int getQualifierLength() {
2138      return this.qlength;
2139    }
2140  }
2141
2142  private static class FirstOnRowDeleteFamilyCell extends EmptyCell {
2143    // @formatter:off
2144    private static final int FIXED_OVERHEAD = ClassSize.OBJECT // object
2145      + ClassSize.REFERENCE * 2 // fBuffer and qBuffer
2146      + Bytes.SIZEOF_INT * 3 // foffset, qoffset, qlength
2147      + Bytes.SIZEOF_BYTE; // flength
2148    // @formatter:on
2149    private final byte[] row;
2150    private final byte[] fam;
2151
2152    public FirstOnRowDeleteFamilyCell(byte[] row, byte[] fam) {
2153      this.row = row;
2154      this.fam = fam;
2155    }
2156
2157    @Override
2158    public long heapSize() {
2159      return ClassSize.align(FIXED_OVERHEAD)
2160        // array overhead
2161        + (getRowLength() == 0 ? ClassSize.sizeOfByteArray(getRowLength()) : getRowLength())
2162        + (getFamilyLength() == 0
2163          ? ClassSize.sizeOfByteArray(getFamilyLength())
2164          : getFamilyLength());
2165    }
2166
2167    @Override
2168    public byte[] getRowArray() {
2169      return this.row;
2170    }
2171
2172    @Override
2173    public short getRowLength() {
2174      return (short) this.row.length;
2175    }
2176
2177    @Override
2178    public byte[] getFamilyArray() {
2179      return this.fam;
2180    }
2181
2182    @Override
2183    public byte getFamilyLength() {
2184      return (byte) this.fam.length;
2185    }
2186
2187    @Override
2188    public long getTimestamp() {
2189      return HConstants.LATEST_TIMESTAMP;
2190    }
2191
2192    @Override
2193    public byte getTypeByte() {
2194      return KeyValue.Type.DeleteFamily.getCode();
2195    }
2196
2197    @Override
2198    public Type getType() {
2199      return Type.DeleteFamily;
2200    }
2201  }
2202
2203  /**
2204   * Writes the Cell's key part as it would have serialized in a KeyValue. The format is &lt;2 bytes
2205   * rk len&gt;&lt;rk&gt;&lt;1 byte cf len&gt;&lt;cf&gt;&lt;qualifier&gt;&lt;8 bytes
2206   * timestamp&gt;&lt;1 byte type&gt;
2207   */
2208  public static void writeFlatKey(ExtendedCell cell, DataOutput out) throws IOException {
2209    short rowLen = cell.getRowLength();
2210    byte fLen = cell.getFamilyLength();
2211    int qLen = cell.getQualifierLength();
2212    // Using just one if/else loop instead of every time checking before writing every
2213    // component of cell
2214    if (cell instanceof ByteBufferExtendedCell) {
2215      out.writeShort(rowLen);
2216      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2217        ((ByteBufferExtendedCell) cell).getRowPosition(), rowLen);
2218      out.writeByte(fLen);
2219      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2220        ((ByteBufferExtendedCell) cell).getFamilyPosition(), fLen);
2221      ByteBufferUtils.copyBufferToStream(out,
2222        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2223        ((ByteBufferExtendedCell) cell).getQualifierPosition(), qLen);
2224    } else {
2225      out.writeShort(rowLen);
2226      out.write(cell.getRowArray(), cell.getRowOffset(), rowLen);
2227      out.writeByte(fLen);
2228      out.write(cell.getFamilyArray(), cell.getFamilyOffset(), fLen);
2229      out.write(cell.getQualifierArray(), cell.getQualifierOffset(), qLen);
2230    }
2231    out.writeLong(cell.getTimestamp());
2232    out.writeByte(cell.getTypeByte());
2233  }
2234
2235  public static int writeFlatKey(ExtendedCell cell, OutputStream out) throws IOException {
2236    short rowLen = cell.getRowLength();
2237    byte fLen = cell.getFamilyLength();
2238    int qLen = cell.getQualifierLength();
2239    // Using just one if/else loop instead of every time checking before writing every
2240    // component of cell
2241    if (cell instanceof ByteBufferExtendedCell) {
2242      StreamUtils.writeShort(out, rowLen);
2243      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2244        ((ByteBufferExtendedCell) cell).getRowPosition(), rowLen);
2245      out.write(fLen);
2246      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2247        ((ByteBufferExtendedCell) cell).getFamilyPosition(), fLen);
2248      ByteBufferUtils.copyBufferToStream(out,
2249        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2250        ((ByteBufferExtendedCell) cell).getQualifierPosition(), qLen);
2251    } else {
2252      StreamUtils.writeShort(out, rowLen);
2253      out.write(cell.getRowArray(), cell.getRowOffset(), rowLen);
2254      out.write(fLen);
2255      out.write(cell.getFamilyArray(), cell.getFamilyOffset(), fLen);
2256      out.write(cell.getQualifierArray(), cell.getQualifierOffset(), qLen);
2257    }
2258    StreamUtils.writeLong(out, cell.getTimestamp());
2259    out.write(cell.getTypeByte());
2260    return Bytes.SIZEOF_SHORT + rowLen + Bytes.SIZEOF_BYTE + fLen + qLen + Bytes.SIZEOF_LONG
2261      + Bytes.SIZEOF_BYTE;
2262  }
2263
2264  /**
2265   * Sets the given seqId to the cell. Marked as audience Private as of 1.2.0. Setting a Cell
2266   * sequenceid is an internal implementation detail not for general public use.
2267   * @throws IOException when the passed cell is not of type {@link ExtendedCell}
2268   */
2269  public static void setSequenceId(Cell cell, long seqId) throws IOException {
2270    if (cell instanceof ExtendedCell) {
2271      ((ExtendedCell) cell).setSequenceId(seqId);
2272    } else {
2273      throw new IOException(
2274        new UnsupportedOperationException("Cell is not of type " + ExtendedCell.class.getName()));
2275    }
2276  }
2277
2278  /**
2279   * Sets the given timestamp to the cell.
2280   * @throws IOException when the passed cell is not of type {@link ExtendedCell}
2281   */
2282  public static void setTimestamp(Cell cell, long ts) throws IOException {
2283    if (cell instanceof ExtendedCell) {
2284      ((ExtendedCell) cell).setTimestamp(ts);
2285    } else {
2286      throw new IOException(
2287        new UnsupportedOperationException("Cell is not of type " + ExtendedCell.class.getName()));
2288    }
2289  }
2290
2291  /**
2292   * Sets the given timestamp to the cell.
2293   * @throws IOException when the passed cell is not of type {@link ExtendedCell}
2294   */
2295  public static void setTimestamp(Cell cell, byte[] ts) throws IOException {
2296    if (cell instanceof ExtendedCell) {
2297      ((ExtendedCell) cell).setTimestamp(ts);
2298    } else {
2299      throw new IOException(
2300        new UnsupportedOperationException("Cell is not of type " + ExtendedCell.class.getName()));
2301    }
2302  }
2303
2304  /**
2305   * Sets the given timestamp to the cell iff current timestamp is
2306   * {@link HConstants#LATEST_TIMESTAMP}.
2307   * @return True if cell timestamp is modified.
2308   * @throws IOException when the passed cell is not of type {@link ExtendedCell}
2309   */
2310  public static boolean updateLatestStamp(ExtendedCell cell, long ts) throws IOException {
2311    if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
2312      cell.setTimestamp(ts);
2313      return true;
2314    }
2315    return false;
2316  }
2317
2318  /**
2319   * Sets the given timestamp to the cell iff current timestamp is
2320   * {@link HConstants#LATEST_TIMESTAMP}.
2321   * @return True if cell timestamp is modified.
2322   * @throws IOException when the passed cell is not of type {@link ExtendedCell}
2323   */
2324  public static boolean updateLatestStamp(Cell cell, byte[] ts) throws IOException {
2325    if (cell.getTimestamp() == HConstants.LATEST_TIMESTAMP) {
2326      setTimestamp(cell, ts);
2327      return true;
2328    }
2329    return false;
2330  }
2331
2332  /**
2333   * Writes the row from the given cell to the output stream
2334   * @param out     The outputstream to which the data has to be written
2335   * @param cell    The cell whose contents has to be written
2336   * @param rlength the row length
2337   */
2338  public static void writeRow(OutputStream out, Cell cell, short rlength) throws IOException {
2339    if (cell instanceof ByteBufferExtendedCell) {
2340      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2341        ((ByteBufferExtendedCell) cell).getRowPosition(), rlength);
2342    } else {
2343      out.write(cell.getRowArray(), cell.getRowOffset(), rlength);
2344    }
2345  }
2346
2347  /**
2348   * Writes the family from the given cell to the output stream
2349   * @param out     The outputstream to which the data has to be written
2350   * @param cell    The cell whose contents has to be written
2351   * @param flength the family length
2352   */
2353  public static void writeFamily(OutputStream out, Cell cell, byte flength) throws IOException {
2354    if (cell instanceof ByteBufferExtendedCell) {
2355      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2356        ((ByteBufferExtendedCell) cell).getFamilyPosition(), flength);
2357    } else {
2358      out.write(cell.getFamilyArray(), cell.getFamilyOffset(), flength);
2359    }
2360  }
2361
2362  /**
2363   * Writes the qualifier from the given cell to the output stream
2364   * @param out     The outputstream to which the data has to be written
2365   * @param cell    The cell whose contents has to be written
2366   * @param qlength the qualifier length
2367   */
2368  public static void writeQualifier(OutputStream out, Cell cell, int qlength) throws IOException {
2369    if (cell instanceof ByteBufferExtendedCell) {
2370      ByteBufferUtils.copyBufferToStream(out,
2371        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2372        ((ByteBufferExtendedCell) cell).getQualifierPosition(), qlength);
2373    } else {
2374      out.write(cell.getQualifierArray(), cell.getQualifierOffset(), qlength);
2375    }
2376  }
2377
2378  /**
2379   * Writes the qualifier from the given cell to the output stream excluding the common prefix
2380   * @param out     The dataoutputstream to which the data has to be written
2381   * @param cell    The cell whose contents has to be written
2382   * @param qlength the qualifier length
2383   */
2384  public static void writeQualifierSkippingBytes(DataOutputStream out, Cell cell, int qlength,
2385    int commonPrefix) throws IOException {
2386    if (cell instanceof ByteBufferExtendedCell) {
2387      ByteBufferUtils.copyBufferToStream((DataOutput) out,
2388        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2389        ((ByteBufferExtendedCell) cell).getQualifierPosition() + commonPrefix,
2390        qlength - commonPrefix);
2391    } else {
2392      out.write(cell.getQualifierArray(), cell.getQualifierOffset() + commonPrefix,
2393        qlength - commonPrefix);
2394    }
2395  }
2396
2397  /**
2398   * Writes the value from the given cell to the output stream
2399   * @param out     The outputstream to which the data has to be written
2400   * @param cell    The cell whose contents has to be written
2401   * @param vlength the value length
2402   */
2403  public static void writeValue(OutputStream out, ExtendedCell cell, int vlength)
2404    throws IOException {
2405    if (cell instanceof ByteBufferExtendedCell) {
2406      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getValueByteBuffer(),
2407        ((ByteBufferExtendedCell) cell).getValuePosition(), vlength);
2408    } else {
2409      out.write(cell.getValueArray(), cell.getValueOffset(), vlength);
2410    }
2411  }
2412
2413  /**
2414   * Writes the tag from the given cell to the output stream
2415   * @param out        The outputstream to which the data has to be written
2416   * @param cell       The cell whose contents has to be written
2417   * @param tagsLength the tag length
2418   */
2419  public static void writeTags(OutputStream out, ExtendedCell cell, int tagsLength)
2420    throws IOException {
2421    if (cell instanceof ByteBufferExtendedCell) {
2422      ByteBufferUtils.copyBufferToStream(out, ((ByteBufferExtendedCell) cell).getTagsByteBuffer(),
2423        ((ByteBufferExtendedCell) cell).getTagsPosition(), tagsLength);
2424    } else {
2425      out.write(cell.getTagsArray(), cell.getTagsOffset(), tagsLength);
2426    }
2427  }
2428
2429  /**
2430   * special case for Cell.equals
2431   */
2432  public static boolean equalsIgnoreMvccVersion(ExtendedCell a, ExtendedCell b) {
2433    // row
2434    boolean res = CellUtil.matchingRows(a, b);
2435    if (!res) {
2436      return res;
2437    }
2438
2439    // family
2440    res = CellUtil.matchingColumn(a, b);
2441    if (!res) {
2442      return res;
2443    }
2444
2445    // timestamp: later sorts first
2446    if (!CellUtil.matchingTimestamp(a, b)) {
2447      return false;
2448    }
2449
2450    // type
2451    int c = (0xff & b.getTypeByte()) - (0xff & a.getTypeByte());
2452    if (c != 0) {
2453      return false;
2454    } else {
2455      return true;
2456    }
2457  }
2458
2459  /**
2460   * Converts the rowkey bytes of the given cell into an int value
2461   * @return rowkey as int
2462   */
2463  public static int getRowAsInt(Cell cell) {
2464    if (cell instanceof ByteBufferExtendedCell) {
2465      return ByteBufferUtils.toInt(((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2466        ((ByteBufferExtendedCell) cell).getRowPosition());
2467    }
2468    return Bytes.toInt(cell.getRowArray(), cell.getRowOffset());
2469  }
2470
2471  /**
2472   * Converts the value bytes of the given cell into a long value
2473   * @return value as long
2474   */
2475  public static long getValueAsLong(Cell cell) {
2476    if (cell instanceof ByteBufferExtendedCell) {
2477      return ByteBufferUtils.toLong(((ByteBufferExtendedCell) cell).getValueByteBuffer(),
2478        ((ByteBufferExtendedCell) cell).getValuePosition());
2479    }
2480    return Bytes.toLong(cell.getValueArray(), cell.getValueOffset());
2481  }
2482
2483  /**
2484   * Converts the value bytes of the given cell into a int value
2485   * @return value as int
2486   */
2487  public static int getValueAsInt(Cell cell) {
2488    if (cell instanceof ByteBufferExtendedCell) {
2489      return ByteBufferUtils.toInt(((ByteBufferExtendedCell) cell).getValueByteBuffer(),
2490        ((ByteBufferExtendedCell) cell).getValuePosition());
2491    }
2492    return Bytes.toInt(cell.getValueArray(), cell.getValueOffset());
2493  }
2494
2495  /**
2496   * Converts the value bytes of the given cell into a double value
2497   * @return value as double
2498   */
2499  public static double getValueAsDouble(Cell cell) {
2500    if (cell instanceof ByteBufferExtendedCell) {
2501      return ByteBufferUtils.toDouble(((ByteBufferExtendedCell) cell).getValueByteBuffer(),
2502        ((ByteBufferExtendedCell) cell).getValuePosition());
2503    }
2504    return Bytes.toDouble(cell.getValueArray(), cell.getValueOffset());
2505  }
2506
2507  /**
2508   * Converts the value bytes of the given cell into a BigDecimal
2509   * @return value as BigDecimal
2510   */
2511  public static BigDecimal getValueAsBigDecimal(Cell cell) {
2512    if (cell instanceof ByteBufferExtendedCell) {
2513      return ByteBufferUtils.toBigDecimal(((ByteBufferExtendedCell) cell).getValueByteBuffer(),
2514        ((ByteBufferExtendedCell) cell).getValuePosition(), cell.getValueLength());
2515    }
2516    return Bytes.toBigDecimal(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
2517  }
2518
2519  /**
2520   * Compresses the tags to the given outputstream using the TagcompressionContext
2521   * @param out                   the outputstream to which the compression should happen
2522   * @param cell                  the cell which has tags
2523   * @param tagCompressionContext the TagCompressionContext
2524   * @throws IOException can throw IOException if the compression encounters issue
2525   */
2526  public static void compressTags(OutputStream out, ExtendedCell cell,
2527    TagCompressionContext tagCompressionContext) throws IOException {
2528    if (cell instanceof ByteBufferExtendedCell) {
2529      tagCompressionContext.compressTags(out, ((ByteBufferExtendedCell) cell).getTagsByteBuffer(),
2530        ((ByteBufferExtendedCell) cell).getTagsPosition(), cell.getTagsLength());
2531    } else {
2532      tagCompressionContext.compressTags(out, cell.getTagsArray(), cell.getTagsOffset(),
2533        cell.getTagsLength());
2534    }
2535  }
2536
2537  public static void compressRow(OutputStream out, Cell cell, Dictionary dict) throws IOException {
2538    if (cell instanceof ByteBufferExtendedCell) {
2539      Dictionary.write(out, ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2540        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength(), dict);
2541    } else {
2542      Dictionary.write(out, cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(), dict);
2543    }
2544  }
2545
2546  public static void compressFamily(OutputStream out, Cell cell, Dictionary dict)
2547    throws IOException {
2548    if (cell instanceof ByteBufferExtendedCell) {
2549      Dictionary.write(out, ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2550        ((ByteBufferExtendedCell) cell).getFamilyPosition(), cell.getFamilyLength(), dict);
2551    } else {
2552      Dictionary.write(out, cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
2553        dict);
2554    }
2555  }
2556
2557  public static void compressQualifier(OutputStream out, Cell cell, Dictionary dict)
2558    throws IOException {
2559    if (cell instanceof ByteBufferExtendedCell) {
2560      Dictionary.write(out, ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2561        ((ByteBufferExtendedCell) cell).getQualifierPosition(), cell.getQualifierLength(), dict);
2562    } else {
2563      Dictionary.write(out, cell.getQualifierArray(), cell.getQualifierOffset(),
2564        cell.getQualifierLength(), dict);
2565    }
2566  }
2567
2568  /**
2569   * Used when a cell needs to be compared with a key byte[] such as cases of finding the index from
2570   * the index block, bloom keys from the bloom blocks This byte[] is expected to be serialized in
2571   * the KeyValue serialization format If the KeyValue (Cell's) serialization format changes this
2572   * method cannot be used.
2573   * @param comparator the {@link CellComparator} to use for comparison
2574   * @param left       the cell to be compared
2575   * @param key        the serialized key part of a KeyValue
2576   * @param offset     the offset in the key byte[]
2577   * @param length     the length of the key byte[]
2578   * @return an int greater than 0 if left is greater than right lesser than 0 if left is lesser
2579   *         than right equal to 0 if left is equal to right
2580   */
2581  public static final int compare(CellComparator comparator, ExtendedCell left, byte[] key,
2582    int offset, int length) {
2583    // row
2584    short rrowlength = Bytes.toShort(key, offset);
2585    int c = comparator.compareRows(left, key, offset + Bytes.SIZEOF_SHORT, rrowlength);
2586    if (c != 0) return c;
2587
2588    // Compare the rest of the two KVs without making any assumptions about
2589    // the common prefix. This function will not compare rows anyway, so we
2590    // don't need to tell it that the common prefix includes the row.
2591    return compareWithoutRow(comparator, left, key, offset, length, rrowlength);
2592  }
2593
2594  /**
2595   * Compare columnFamily, qualifier, timestamp, and key type (everything except the row). This
2596   * method is used both in the normal comparator and the "same-prefix" comparator. Note that we are
2597   * assuming that row portions of both KVs have already been parsed and found identical, and we
2598   * don't validate that assumption here.
2599   * @param comparator the {@link CellComparator} to use for comparison
2600   * @param left       the cell to be compared
2601   * @param right      the serialized key part of a key-value
2602   * @param roffset    the offset in the key byte[]
2603   * @param rlength    the length of the key byte[]
2604   * @param rowlength  the row length
2605   * @return greater than 0 if left cell is bigger, less than 0 if right cell is bigger, 0 if both
2606   *         cells are equal
2607   */
2608  static final int compareWithoutRow(CellComparator comparator, ExtendedCell left, byte[] right,
2609    int roffset, int rlength, short rowlength) {
2610    /***
2611     * KeyValue Format and commonLength:
2612     * |_keyLen_|_valLen_|_rowLen_|_rowKey_|_famiLen_|_fami_|_Quali_|....
2613     * ------------------|-------commonLength--------|--------------
2614     */
2615    int commonLength = KeyValue.ROW_LENGTH_SIZE + KeyValue.FAMILY_LENGTH_SIZE + rowlength;
2616
2617    // commonLength + TIMESTAMP_TYPE_SIZE
2618    int commonLengthWithTSAndType = KeyValue.TIMESTAMP_TYPE_SIZE + commonLength;
2619    // ColumnFamily + Qualifier length.
2620    int lcolumnlength = left.getFamilyLength() + left.getQualifierLength();
2621    int rcolumnlength = rlength - commonLengthWithTSAndType;
2622
2623    byte ltype = left.getTypeByte();
2624    byte rtype = right[roffset + (rlength - 1)];
2625
2626    // If the column is not specified, the "minimum" key type appears the
2627    // latest in the sorted order, regardless of the timestamp. This is used
2628    // for specifying the last key/value in a given row, because there is no
2629    // "lexicographically last column" (it would be infinitely long). The
2630    // "maximum" key type does not need this behavior.
2631    if (lcolumnlength == 0 && ltype == KeyValue.Type.Minimum.getCode()) {
2632      // left is "bigger", i.e. it appears later in the sorted order
2633      return 1;
2634    }
2635    if (rcolumnlength == 0 && rtype == KeyValue.Type.Minimum.getCode()) {
2636      return -1;
2637    }
2638
2639    int rfamilyoffset = commonLength + roffset;
2640
2641    // Column family length.
2642    int lfamilylength = left.getFamilyLength();
2643    int rfamilylength = right[rfamilyoffset - 1];
2644    // If left family size is not equal to right family size, we need not
2645    // compare the qualifiers.
2646    boolean sameFamilySize = (lfamilylength == rfamilylength);
2647    if (!sameFamilySize) {
2648      // comparing column family is enough.
2649      return CellUtil.compareFamilies(left, right, rfamilyoffset, rfamilylength);
2650    }
2651    // Compare family & qualifier together.
2652    // Families are same. Compare on qualifiers.
2653    int comparison = CellUtil.compareColumns(left, right, rfamilyoffset, rfamilylength,
2654      rfamilyoffset + rfamilylength, (rcolumnlength - rfamilylength));
2655    if (comparison != 0) {
2656      return comparison;
2657    }
2658
2659    // //
2660    // Next compare timestamps.
2661    long rtimestamp = Bytes.toLong(right, roffset + (rlength - KeyValue.TIMESTAMP_TYPE_SIZE));
2662    int compare = comparator.compareTimestamps(left.getTimestamp(), rtimestamp);
2663    if (compare != 0) {
2664      return compare;
2665    }
2666
2667    // Compare types. Let the delete types sort ahead of puts; i.e. types
2668    // of higher numbers sort before those of lesser numbers. Maximum (255)
2669    // appears ahead of everything, and minimum (0) appears after
2670    // everything.
2671    return (0xff & rtype) - (0xff & ltype);
2672  }
2673
2674  /**
2675   * Return a new cell is located following input cell. If both of type and timestamp are minimum,
2676   * the input cell will be returned directly.
2677   */
2678  public static ExtendedCell createNextOnRowCol(ExtendedCell cell) {
2679    long ts = cell.getTimestamp();
2680    byte type = cell.getTypeByte();
2681    if (type != KeyValue.Type.Minimum.getCode()) {
2682      type = KeyValue.Type.values()[KeyValue.Type.codeToType(type).ordinal() - 1].getCode();
2683    } else if (ts != PrivateConstants.OLDEST_TIMESTAMP) {
2684      ts = ts - 1;
2685      type = KeyValue.Type.Maximum.getCode();
2686    } else {
2687      return cell;
2688    }
2689    return createNextOnRowCol(cell, ts, type);
2690  }
2691
2692  static ExtendedCell createNextOnRowCol(ExtendedCell cell, long ts, byte type) {
2693    if (cell instanceof ByteBufferExtendedCell) {
2694      return new LastOnRowColByteBufferExtendedCell(
2695        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2696        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength(),
2697        ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2698        ((ByteBufferExtendedCell) cell).getFamilyPosition(), cell.getFamilyLength(),
2699        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2700        ((ByteBufferExtendedCell) cell).getQualifierPosition(), cell.getQualifierLength()) {
2701        @Override
2702        public long getTimestamp() {
2703          return ts;
2704        }
2705
2706        @Override
2707        public byte getTypeByte() {
2708          return type;
2709        }
2710      };
2711    }
2712    return new LastOnRowColCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
2713      cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
2714      cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()) {
2715      @Override
2716      public long getTimestamp() {
2717        return ts;
2718      }
2719
2720      @Override
2721      public byte getTypeByte() {
2722        return type;
2723      }
2724    };
2725  }
2726
2727  /**
2728   * Estimate based on keyvalue's serialization format in the RPC layer. Note that there is an extra
2729   * SIZEOF_INT added to the size here that indicates the actual length of the cell for cases where
2730   * cell's are serialized in a contiguous format (For eg in RPCs).
2731   * @return Estimate of the <code>cell</code> size in bytes plus an extra SIZEOF_INT indicating the
2732   *         actual cell length.
2733   */
2734  public static int estimatedSerializedSizeOf(final Cell cell) {
2735    return cell.getSerializedSize() + Bytes.SIZEOF_INT;
2736  }
2737
2738  /**
2739   * Calculates the serialized key size. We always serialize in the KeyValue's serialization format.
2740   * @param cell the cell for which the key size has to be calculated.
2741   * @return the key size
2742   */
2743  public static int estimatedSerializedSizeOfKey(final Cell cell) {
2744    if (cell instanceof KeyValue) return ((KeyValue) cell).getKeyLength();
2745    return cell.getRowLength() + cell.getFamilyLength() + cell.getQualifierLength()
2746      + KeyValue.KEY_INFRASTRUCTURE_SIZE;
2747  }
2748
2749  /**
2750   * This method exists just to encapsulate how we serialize keys. To be replaced by a factory that
2751   * we query to figure what the Cell implementation is and then, what serialization engine to use
2752   * and further, how to serialize the key for inclusion in hfile index. TODO.
2753   * @return The key portion of the Cell serialized in the old-school KeyValue way or null if passed
2754   *         a null <code>cell</code>
2755   */
2756  public static byte[] getCellKeySerializedAsKeyValueKey(final ExtendedCell cell) {
2757    if (cell == null) return null;
2758    byte[] b = new byte[KeyValueUtil.keyLength(cell)];
2759    KeyValueUtil.appendKeyTo(cell, b, 0);
2760    return b;
2761  }
2762
2763  /**
2764   * Create a Cell that is smaller than all other possible Cells for the given Cell's row.
2765   * @return First possible Cell on passed Cell's row.
2766   */
2767  public static ExtendedCell createFirstOnRow(final Cell cell) {
2768    if (cell instanceof ByteBufferExtendedCell) {
2769      return new FirstOnRowByteBufferExtendedCell(
2770        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2771        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength());
2772    }
2773    return new FirstOnRowCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
2774  }
2775
2776  public static ExtendedCell createFirstOnRow(final byte[] row, int roffset, short rlength) {
2777    return new FirstOnRowCell(row, roffset, rlength);
2778  }
2779
2780  public static ExtendedCell createFirstOnRow(final byte[] row, final byte[] family,
2781    final byte[] col) {
2782    return createFirstOnRow(row, 0, (short) row.length, family, 0, (byte) family.length, col, 0,
2783      col.length);
2784  }
2785
2786  public static ExtendedCell createFirstOnRow(final byte[] row, int roffset, short rlength,
2787    final byte[] family, int foffset, byte flength, final byte[] col, int coffset, int clength) {
2788    return new FirstOnRowColCell(row, roffset, rlength, family, foffset, flength, col, coffset,
2789      clength);
2790  }
2791
2792  public static ExtendedCell createFirstOnRow(final byte[] row) {
2793    return createFirstOnRow(row, 0, (short) row.length);
2794  }
2795
2796  public static ExtendedCell createFirstOnRowFamily(Cell cell, byte[] fArray, int foff, int flen) {
2797    if (cell instanceof ByteBufferExtendedCell) {
2798      return new FirstOnRowColByteBufferExtendedCell(
2799        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2800        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength(),
2801        ByteBuffer.wrap(fArray), foff, (byte) flen, HConstants.EMPTY_BYTE_BUFFER, 0, 0);
2802    }
2803    return new FirstOnRowColCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
2804      fArray, foff, (byte) flen, HConstants.EMPTY_BYTE_ARRAY, 0, 0);
2805  }
2806
2807  public static ExtendedCell createFirstOnRowCol(final Cell cell) {
2808    if (cell instanceof ByteBufferExtendedCell) {
2809      return new FirstOnRowColByteBufferExtendedCell(
2810        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2811        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength(),
2812        HConstants.EMPTY_BYTE_BUFFER, 0, (byte) 0,
2813        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2814        ((ByteBufferExtendedCell) cell).getQualifierPosition(), cell.getQualifierLength());
2815    }
2816    return new FirstOnRowColCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
2817      HConstants.EMPTY_BYTE_ARRAY, 0, (byte) 0, cell.getQualifierArray(), cell.getQualifierOffset(),
2818      cell.getQualifierLength());
2819  }
2820
2821  public static ExtendedCell createFirstOnNextRow(final Cell cell) {
2822    byte[] nextRow = new byte[cell.getRowLength() + 1];
2823    CellUtil.copyRowTo(cell, nextRow, 0);
2824    nextRow[nextRow.length - 1] = 0;// maybe not necessary
2825    return new FirstOnRowCell(nextRow, 0, (short) nextRow.length);
2826  }
2827
2828  /**
2829   * Create a Cell that is smaller than all other possible Cells for the given Cell's rk:cf and
2830   * passed qualifier.
2831   * @return Last possible Cell on passed Cell's rk:cf and passed qualifier.
2832   */
2833  public static ExtendedCell createFirstOnRowCol(final Cell cell, byte[] qArray, int qoffest,
2834    int qlength) {
2835    if (cell instanceof ByteBufferExtendedCell) {
2836      return new FirstOnRowColByteBufferExtendedCell(
2837        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2838        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength(),
2839        ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2840        ((ByteBufferExtendedCell) cell).getFamilyPosition(), cell.getFamilyLength(),
2841        ByteBuffer.wrap(qArray), qoffest, qlength);
2842    }
2843    return new FirstOnRowColCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
2844      cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(), qArray, qoffest,
2845      qlength);
2846  }
2847
2848  /**
2849   * Creates the first cell with the row/family/qualifier of this cell and the given timestamp. Uses
2850   * the "maximum" type that guarantees that the new cell is the lowest possible for this
2851   * combination of row, family, qualifier, and timestamp. This cell's own timestamp is ignored.
2852   * @param cell - cell
2853   */
2854  public static ExtendedCell createFirstOnRowColTS(Cell cell, long ts) {
2855    if (cell instanceof ByteBufferExtendedCell) {
2856      return new FirstOnRowColTSByteBufferExtendedCell(
2857        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2858        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength(),
2859        ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2860        ((ByteBufferExtendedCell) cell).getFamilyPosition(), cell.getFamilyLength(),
2861        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2862        ((ByteBufferExtendedCell) cell).getQualifierPosition(), cell.getQualifierLength(), ts);
2863    }
2864    return new FirstOnRowColTSCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
2865      cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
2866      cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(), ts);
2867  }
2868
2869  /**
2870   * Create a Cell that is larger than all other possible Cells for the given Cell's row.
2871   * @return Last possible Cell on passed Cell's row.
2872   */
2873  public static ExtendedCell createLastOnRow(final Cell cell) {
2874    if (cell instanceof ByteBufferExtendedCell) {
2875      return new LastOnRowByteBufferExtendedCell(((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2876        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength());
2877    }
2878    return new LastOnRowCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
2879  }
2880
2881  public static ExtendedCell createLastOnRow(final byte[] row) {
2882    return new LastOnRowCell(row, 0, (short) row.length);
2883  }
2884
2885  /**
2886   * Create a Cell that is larger than all other possible Cells for the given Cell's rk:cf:q. Used
2887   * in creating "fake keys" for the multi-column Bloom filter optimization to skip the row/column
2888   * we already know is not in the file.
2889   * @return Last possible Cell on passed Cell's rk:cf:q.
2890   */
2891  public static ExtendedCell createLastOnRowCol(final Cell cell) {
2892    if (cell instanceof ByteBufferExtendedCell) {
2893      return new LastOnRowColByteBufferExtendedCell(
2894        ((ByteBufferExtendedCell) cell).getRowByteBuffer(),
2895        ((ByteBufferExtendedCell) cell).getRowPosition(), cell.getRowLength(),
2896        ((ByteBufferExtendedCell) cell).getFamilyByteBuffer(),
2897        ((ByteBufferExtendedCell) cell).getFamilyPosition(), cell.getFamilyLength(),
2898        ((ByteBufferExtendedCell) cell).getQualifierByteBuffer(),
2899        ((ByteBufferExtendedCell) cell).getQualifierPosition(), cell.getQualifierLength());
2900    }
2901    return new LastOnRowColCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
2902      cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
2903      cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
2904  }
2905
2906  /**
2907   * Create a Delete Family Cell for the specified row and family that would be smaller than all
2908   * other possible Delete Family KeyValues that have the same row and family. Used for seeking.
2909   * @param row - row key (arbitrary byte array)
2910   * @param fam - family name
2911   * @return First Delete Family possible key on passed <code>row</code>.
2912   */
2913  public static ExtendedCell createFirstDeleteFamilyCellOnRow(final byte[] row, final byte[] fam) {
2914    return new FirstOnRowDeleteFamilyCell(row, fam);
2915  }
2916
2917  /**
2918   * In fact, in HBase, all {@link Cell}s are {@link ExtendedCell}s. We do not expect users to
2919   * implement their own {@link Cell} types, except some special projects like Phoenix, where they
2920   * just use {@link org.apache.hadoop.hbase.KeyValue} and {@link ExtendedCell} directly.
2921   * @return the original {@code cell} which has already been cast to an {@link ExtendedCell}.
2922   * @throws IllegalArgumentException if the given {@code cell} is not an {@link ExtendedCell}.
2923   */
2924  public static ExtendedCell ensureExtendedCell(Cell cell) {
2925    if (cell == null) {
2926      return null;
2927    }
2928    Preconditions.checkArgument(cell instanceof ExtendedCell, "Unsupported cell type: %s",
2929      cell.getClass().getName());
2930    return (ExtendedCell) cell;
2931  }
2932
2933  public static boolean equals(ExtendedCell a, ExtendedCell b) {
2934    return CellUtil.matchingRows(a, b) && CellUtil.matchingFamily(a, b)
2935      && CellUtil.matchingQualifier(a, b) && CellUtil.matchingTimestamp(a, b)
2936      && PrivateCellUtil.matchingType(a, b);
2937  }
2938
2939  /** Returns ExtendedCellScanner interface over <code>cellIterables</code> */
2940  public static ExtendedCellScanner
2941    createExtendedCellScanner(final List<? extends ExtendedCellScannable> cellScannerables) {
2942    return new ExtendedCellScanner() {
2943      private final Iterator<? extends ExtendedCellScannable> iterator =
2944        cellScannerables.iterator();
2945      private ExtendedCellScanner cellScanner = null;
2946
2947      @Override
2948      public ExtendedCell current() {
2949        return this.cellScanner != null ? this.cellScanner.current() : null;
2950      }
2951
2952      @Override
2953      public boolean advance() throws IOException {
2954        while (true) {
2955          if (this.cellScanner == null) {
2956            if (!this.iterator.hasNext()) {
2957              return false;
2958            }
2959            this.cellScanner = this.iterator.next().cellScanner();
2960          }
2961          if (this.cellScanner.advance()) {
2962            return true;
2963          }
2964          this.cellScanner = null;
2965        }
2966      }
2967    };
2968  }
2969
2970  /**
2971   * Flatten the map of cells out under the ExtendedCellScanner
2972   * @param map Map of Cell Lists; for example, the map of families to ExtendedCells that is used
2973   *            inside Put, etc., keeping Cells organized by family.
2974   * @return ExtendedCellScanner interface over <code>cellIterable</code>
2975   */
2976  public static ExtendedCellScanner
2977    createExtendedCellScanner(final NavigableMap<byte[], List<ExtendedCell>> map) {
2978    return new ExtendedCellScanner() {
2979      private final Iterator<Entry<byte[], List<ExtendedCell>>> entries = map.entrySet().iterator();
2980      private Iterator<ExtendedCell> currentIterator = null;
2981      private ExtendedCell currentCell;
2982
2983      @Override
2984      public ExtendedCell current() {
2985        return this.currentCell;
2986      }
2987
2988      @Override
2989      public boolean advance() {
2990        while (true) {
2991          if (this.currentIterator == null) {
2992            if (!this.entries.hasNext()) {
2993              return false;
2994            }
2995            this.currentIterator = this.entries.next().getValue().iterator();
2996          }
2997          if (this.currentIterator.hasNext()) {
2998            this.currentCell = this.currentIterator.next();
2999            return true;
3000          }
3001          this.currentCell = null;
3002          this.currentIterator = null;
3003        }
3004      }
3005    };
3006  }
3007
3008  /** Returns CellScanner interface over <code>cellArray</code> */
3009  public static ExtendedCellScanner createExtendedCellScanner(final ExtendedCell[] cellArray) {
3010    return new ExtendedCellScanner() {
3011      private final ExtendedCell[] cells = cellArray;
3012      private int index = -1;
3013
3014      @Override
3015      public ExtendedCell current() {
3016        if (cells == null) {
3017          return null;
3018        }
3019        return (index < 0) ? null : this.cells[index];
3020      }
3021
3022      @Override
3023      public boolean advance() {
3024        if (cells == null) {
3025          return false;
3026        }
3027        return ++index < this.cells.length;
3028      }
3029    };
3030  }
3031
3032  /** Returns ExtendedCellScanner interface over <code>cellIterable</code> */
3033  public static ExtendedCellScanner
3034    createExtendedCellScanner(final Iterable<ExtendedCell> cellIterable) {
3035    if (cellIterable == null) {
3036      return null;
3037    }
3038    return createExtendedCellScanner(cellIterable.iterator());
3039  }
3040
3041  /**
3042   * Returns ExtendedCellScanner interface over <code>cellIterable</code> or null if
3043   * <code>cells</code> is null
3044   */
3045  public static ExtendedCellScanner createExtendedCellScanner(final Iterator<ExtendedCell> cells) {
3046    if (cells == null) {
3047      return null;
3048    }
3049    return new ExtendedCellScanner() {
3050      private final Iterator<ExtendedCell> iterator = cells;
3051      private ExtendedCell current = null;
3052
3053      @Override
3054      public ExtendedCell current() {
3055        return this.current;
3056      }
3057
3058      @Override
3059      public boolean advance() {
3060        boolean hasNext = this.iterator.hasNext();
3061        this.current = hasNext ? this.iterator.next() : null;
3062        return hasNext;
3063      }
3064    };
3065  }
3066
3067  private static final Cell.Type[] CELL_TYPE_CODE_ARRAY = new Cell.Type[256];
3068
3069  static {
3070    for (Cell.Type t : Cell.Type.values()) {
3071      CELL_TYPE_CODE_ARRAY[t.getCode() & 0xff] = t;
3072    }
3073  }
3074
3075  public static Cell.Type code2Type(byte code) {
3076    Cell.Type t = CELL_TYPE_CODE_ARRAY[code & 0xff];
3077    if (t != null) {
3078      return t;
3079    }
3080    throw new UnsupportedOperationException("Invalid type of cell " + code);
3081  }
3082
3083  public static byte getTypeByte(Cell c) {
3084    if (c instanceof ExtendedCell) {
3085      return ((ExtendedCell) c).getTypeByte();
3086    } else {
3087      return c.getType().getCode();
3088    }
3089  }
3090
3091  public static long getSequenceId(Cell c) {
3092    if (c instanceof ExtendedCell) {
3093      return ((ExtendedCell) c).getSequenceId();
3094    } else {
3095      return HConstants.NO_SEQNUM;
3096    }
3097  }
3098}