All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hbase.KeyValue Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta-1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.hbase;

import static org.apache.hadoop.hbase.util.Bytes.len;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.hbase.util.ByteBufferUtils;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.ClassSize;
import org.apache.hadoop.io.RawComparator;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An HBase Key/Value. This is the fundamental HBase Type.
 * 

* HBase applications and users should use the Cell interface and avoid directly using KeyValue and * member functions not defined in Cell. *

* If being used client-side, the primary methods to access individual fields are * {@link #getRowArray()}, {@link #getFamilyArray()}, {@link #getQualifierArray()}, * {@link #getTimestamp()}, and {@link #getValueArray()}. These methods allocate new byte arrays and * return copies. Avoid their use server-side. *

* Instances of this class are immutable. They do not implement Comparable but Comparators are * provided. Comparators change with context, whether user table or a catalog table comparison. Its * critical you use the appropriate comparator. There are Comparators for normal HFiles, Meta's * Hfiles, and bloom filter keys. *

* KeyValue wraps a byte array and takes offsets and lengths into passed array at where to start * interpreting the content as KeyValue. The KeyValue format inside a byte array is: * <keylength> <valuelength> <key> <value> Key is further * decomposed as: <rowlength> <row> <columnfamilylength> * <columnfamily> <columnqualifier> * <timestamp> <keytype> The rowlength maximum is * Short.MAX_SIZE, column family length maximum is Byte.MAX_SIZE, and * column qualifier + key length must be < Integer.MAX_SIZE. The column does not * contain the family/qualifier delimiter, {@link #COLUMN_FAMILY_DELIMITER}
* KeyValue can optionally contain Tags. When it contains tags, it is added in the byte array after * the value part. The format for this part is: <tagslength><tagsbytes>. * tagslength maximum is Short.MAX_SIZE. The tagsbytes * contain one or more tags where as each tag is of the form * <taglength><tagtype><tagbytes>. tagtype is one byte * and taglength maximum is Short.MAX_SIZE and it includes 1 byte type * length and actual tag bytes length. */ @InterfaceAudience.Private public class KeyValue implements ExtendedCell, Cloneable { private static final Logger LOG = LoggerFactory.getLogger(KeyValue.class); public static final int FIXED_OVERHEAD = ClassSize.OBJECT + // the KeyValue object itself ClassSize.REFERENCE + // pointer to "bytes" 2 * Bytes.SIZEOF_INT + // offset, length Bytes.SIZEOF_LONG;// memstoreTS /** * Colon character in UTF-8 */ public static final char COLUMN_FAMILY_DELIMITER = ':'; public static final byte[] COLUMN_FAMILY_DELIM_ARRAY = new byte[] { COLUMN_FAMILY_DELIMITER }; /** * Comparator for plain key/values; i.e. non-catalog table key/values. Works on Key portion of * KeyValue only. * @deprecated Use {@link CellComparator#getInstance()} instead. Deprecated for hbase 2.0, remove * for hbase 3.0. */ @Deprecated public static final KVComparator COMPARATOR = new KVComparator(); /** * A {@link KVComparator} for hbase:meta catalog table {@link KeyValue}s. * @deprecated Use {@link MetaCellComparator#META_COMPARATOR} instead. Deprecated for hbase 2.0, * remove for hbase 3.0. */ @Deprecated public static final KVComparator META_COMPARATOR = new MetaComparator(); /** Size of the key length field in bytes */ public static final int KEY_LENGTH_SIZE = Bytes.SIZEOF_INT; /** Size of the key type field in bytes */ public static final int TYPE_SIZE = Bytes.SIZEOF_BYTE; /** Size of the row length field in bytes */ public static final int ROW_LENGTH_SIZE = Bytes.SIZEOF_SHORT; /** Size of the family length field in bytes */ public static final int FAMILY_LENGTH_SIZE = Bytes.SIZEOF_BYTE; /** Size of the timestamp field in bytes */ public static final int TIMESTAMP_SIZE = Bytes.SIZEOF_LONG; // Size of the timestamp and type byte on end of a key -- a long + a byte. public static final int TIMESTAMP_TYPE_SIZE = TIMESTAMP_SIZE + TYPE_SIZE; // Size of the length shorts and bytes in key. public static final int KEY_INFRASTRUCTURE_SIZE = ROW_LENGTH_SIZE + FAMILY_LENGTH_SIZE + TIMESTAMP_TYPE_SIZE; // How far into the key the row starts at. First thing to read is the short // that says how long the row is. public static final int ROW_OFFSET = Bytes.SIZEOF_INT /* keylength */ + Bytes.SIZEOF_INT /* valuelength */; public static final int ROW_KEY_OFFSET = ROW_OFFSET + ROW_LENGTH_SIZE; // Size of the length ints in a KeyValue datastructure. public static final int KEYVALUE_INFRASTRUCTURE_SIZE = ROW_OFFSET; /** Size of the tags length field in bytes */ public static final int TAGS_LENGTH_SIZE = Bytes.SIZEOF_SHORT; public static final int KEYVALUE_WITH_TAGS_INFRASTRUCTURE_SIZE = ROW_OFFSET + TAGS_LENGTH_SIZE; /** * Computes the number of bytes that a KeyValue instance with the provided * characteristics would take up for its underlying data structure. * @param rlength row length * @param flength family length * @param qlength qualifier length * @param vlength value length * @return the KeyValue data structure length */ public static long getKeyValueDataStructureSize(int rlength, int flength, int qlength, int vlength) { return KeyValue.KEYVALUE_INFRASTRUCTURE_SIZE + getKeyDataStructureSize(rlength, flength, qlength) + vlength; } /** * Computes the number of bytes that a KeyValue instance with the provided * characteristics would take up for its underlying data structure. * @param rlength row length * @param flength family length * @param qlength qualifier length * @param vlength value length * @param tagsLength total length of the tags * @return the KeyValue data structure length */ public static long getKeyValueDataStructureSize(int rlength, int flength, int qlength, int vlength, int tagsLength) { if (tagsLength == 0) { return getKeyValueDataStructureSize(rlength, flength, qlength, vlength); } return KeyValue.KEYVALUE_WITH_TAGS_INFRASTRUCTURE_SIZE + getKeyDataStructureSize(rlength, flength, qlength) + vlength + tagsLength; } /** * Computes the number of bytes that a KeyValue instance with the provided * characteristics would take up for its underlying data structure. * @param klength key length * @param vlength value length * @param tagsLength total length of the tags * @return the KeyValue data structure length */ public static long getKeyValueDataStructureSize(int klength, int vlength, int tagsLength) { if (tagsLength == 0) { return (long) KeyValue.KEYVALUE_INFRASTRUCTURE_SIZE + klength + vlength; } return (long) KeyValue.KEYVALUE_WITH_TAGS_INFRASTRUCTURE_SIZE + klength + vlength + tagsLength; } /** * Computes the number of bytes that a KeyValue instance with the provided * characteristics would take up in its underlying data structure for the key. * @param rlength row length * @param flength family length * @param qlength qualifier length * @return the key data structure length */ public static long getKeyDataStructureSize(int rlength, int flength, int qlength) { return (long) KeyValue.KEY_INFRASTRUCTURE_SIZE + rlength + flength + qlength; } /** * Key type. Has space for other key types to be added later. Cannot rely on enum ordinals . They * change if item is removed or moved. Do our own codes. */ public static enum Type { Minimum((byte) 0), Put((byte) 4), Delete((byte) 8), DeleteFamilyVersion((byte) 10), DeleteColumn((byte) 12), DeleteFamily((byte) 14), // Maximum is used when searching; you look from maximum on down. Maximum((byte) 255); private final byte code; Type(final byte c) { this.code = c; } public byte getCode() { return this.code; } private static Type[] codeArray = new Type[256]; static { for (Type t : Type.values()) { codeArray[t.code & 0xff] = t; } } /** * True to indicate that the byte b is a valid type. * @param b byte to check * @return true or false */ static boolean isValidType(byte b) { return codeArray[b & 0xff] != null; } /** * Cannot rely on enum ordinals . They change if item is removed or moved. Do our own codes. * @param b the kv serialized byte[] to process * @return Type associated with passed code. */ public static Type codeToType(final byte b) { Type t = codeArray[b & 0xff]; if (t != null) { return t; } throw new RuntimeException("Unknown code " + b); } } /** * Lowest possible key. Makes a Key with highest possible Timestamp, empty row and column. No key * can be equal or lower than this one in memstore or in store file. */ public static final KeyValue LOWESTKEY = new KeyValue(HConstants.EMPTY_BYTE_ARRAY, HConstants.LATEST_TIMESTAMP); //// // KeyValue core instance fields. protected byte[] bytes = null; // an immutable byte array that contains the KV protected int offset = 0; // offset into bytes buffer KV starts at protected int length = 0; // length of the KV starting from offset. /** Here be dragons **/ /** * used to achieve atomic operations in the memstore. */ @Override public long getSequenceId() { return seqId; } @Override public void setSequenceId(long seqId) { this.seqId = seqId; } // multi-version concurrency control version. default value is 0, aka do not care. private long seqId = 0; /** Dragon time over, return to normal business */ /** Writable Constructor -- DO NOT USE */ public KeyValue() { } /** * Creates a KeyValue from the start of the specified byte array. Presumes bytes * content is formatted as a KeyValue blob. * @param bytes byte array */ public KeyValue(final byte[] bytes) { this(bytes, 0); } /** * Creates a KeyValue from the specified byte array and offset. Presumes bytes * content starting at offset is formatted as a KeyValue blob. * @param bytes byte array * @param offset offset to start of KeyValue */ public KeyValue(final byte[] bytes, final int offset) { this(bytes, offset, getLength(bytes, offset)); } /** * Creates a KeyValue from the specified byte array, starting at offset, and for length * length. * @param bytes byte array * @param offset offset to start of the KeyValue * @param length length of the KeyValue */ public KeyValue(final byte[] bytes, final int offset, final int length) { KeyValueUtil.checkKeyValueBytes(bytes, offset, length, true); this.bytes = bytes; this.offset = offset; this.length = length; } /** * Creates a KeyValue from the specified byte array, starting at offset, and for length * length. * @param bytes byte array * @param offset offset to start of the KeyValue * @param length length of the KeyValue * @param ts timestamp */ public KeyValue(final byte[] bytes, final int offset, final int length, long ts) { this(bytes, offset, length, null, 0, 0, null, 0, 0, ts, Type.Maximum, null, 0, 0, null); } /** Constructors that build a new backing byte array from fields */ /** * Constructs KeyValue structure filled with null value. Sets type to * {@link KeyValue.Type#Maximum} * @param row - row key (arbitrary byte array) * @param timestamp version timestamp */ public KeyValue(final byte[] row, final long timestamp) { this(row, null, null, timestamp, Type.Maximum, null); } /** * Constructs KeyValue structure filled with null value. * @param row - row key (arbitrary byte array) * @param timestamp version timestamp */ public KeyValue(final byte[] row, final long timestamp, Type type) { this(row, null, null, timestamp, type, null); } /** * Constructs KeyValue structure filled with null value. Sets type to * {@link KeyValue.Type#Maximum} * @param row - row key (arbitrary byte array) * @param family family name * @param qualifier column qualifier */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier) { this(row, family, qualifier, HConstants.LATEST_TIMESTAMP, Type.Maximum); } /** * Constructs KeyValue structure as a put filled with specified values and LATEST_TIMESTAMP. * @param row - row key (arbitrary byte array) * @param family family name * @param qualifier column qualifier */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final byte[] value) { this(row, family, qualifier, HConstants.LATEST_TIMESTAMP, Type.Put, value); } /** * Constructs KeyValue structure filled with specified values. * @param row row key * @param family family name * @param qualifier column qualifier * @param timestamp version timestamp * @param type key type * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final long timestamp, Type type) { this(row, family, qualifier, timestamp, type, null); } /** * Constructs KeyValue structure filled with specified values. * @param row row key * @param family family name * @param qualifier column qualifier * @param timestamp version timestamp * @param value column value * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final long timestamp, final byte[] value) { this(row, family, qualifier, timestamp, Type.Put, value); } /** * Constructs KeyValue structure filled with specified values. * @param row row key * @param family family name * @param qualifier column qualifier * @param timestamp version timestamp * @param value column value * @param tags tags * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final long timestamp, final byte[] value, final Tag[] tags) { this(row, family, qualifier, timestamp, value, tags != null ? Arrays.asList(tags) : null); } /** * Constructs KeyValue structure filled with specified values. * @param row row key * @param family family name * @param qualifier column qualifier * @param timestamp version timestamp * @param value column value * @param tags tags non-empty list of tags or null * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final long timestamp, final byte[] value, final List tags) { this(row, 0, row == null ? 0 : row.length, family, 0, family == null ? 0 : family.length, qualifier, 0, qualifier == null ? 0 : qualifier.length, timestamp, Type.Put, value, 0, value == null ? 0 : value.length, tags); } /** * Constructs KeyValue structure filled with specified values. * @param row row key * @param family family name * @param qualifier column qualifier * @param timestamp version timestamp * @param type key type * @param value column value * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final long timestamp, Type type, final byte[] value) { this(row, 0, len(row), family, 0, len(family), qualifier, 0, len(qualifier), timestamp, type, value, 0, len(value)); } /** * Constructs KeyValue structure filled with specified values. *

* Column is split into two fields, family and qualifier. * @param row row key * @param family family name * @param qualifier column qualifier * @param timestamp version timestamp * @param type key type * @param value column value * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final long timestamp, Type type, final byte[] value, final List tags) { this(row, family, qualifier, 0, qualifier == null ? 0 : qualifier.length, timestamp, type, value, 0, value == null ? 0 : value.length, tags); } /** * Constructs KeyValue structure filled with specified values. * @param row row key * @param family family name * @param qualifier column qualifier * @param timestamp version timestamp * @param type key type * @param value column value * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final byte[] family, final byte[] qualifier, final long timestamp, Type type, final byte[] value, final byte[] tags) { this(row, family, qualifier, 0, qualifier == null ? 0 : qualifier.length, timestamp, type, value, 0, value == null ? 0 : value.length, tags); } /** * Constructs KeyValue structure filled with specified values. * @param row row key * @param family family name * @param qualifier column qualifier * @param qoffset qualifier offset * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param value column value * @param voffset value offset * @param vlength value length * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(byte[] row, byte[] family, byte[] qualifier, int qoffset, int qlength, long timestamp, Type type, byte[] value, int voffset, int vlength, List tags) { this(row, 0, row == null ? 0 : row.length, family, 0, family == null ? 0 : family.length, qualifier, qoffset, qlength, timestamp, type, value, voffset, vlength, tags); } /** * @param row row key * @param family family name * @param qualifier qualifier name * @param qoffset qualifier offset * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param value column value * @param voffset value offset * @param vlength value length * @param tags tags */ public KeyValue(byte[] row, byte[] family, byte[] qualifier, int qoffset, int qlength, long timestamp, Type type, byte[] value, int voffset, int vlength, byte[] tags) { this(row, 0, row == null ? 0 : row.length, family, 0, family == null ? 0 : family.length, qualifier, qoffset, qlength, timestamp, type, value, voffset, vlength, tags, 0, tags == null ? 0 : tags.length); } /** * Constructs KeyValue structure filled with specified values. *

* Column is split into two fields, family and qualifier. * @param row row key * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final int roffset, final int rlength, final byte[] family, final int foffset, final int flength, final byte[] qualifier, final int qoffset, final int qlength, final long timestamp, final Type type, final byte[] value, final int voffset, final int vlength) { this(row, roffset, rlength, family, foffset, flength, qualifier, qoffset, qlength, timestamp, type, value, voffset, vlength, null); } /** * Constructs KeyValue structure filled with specified values. Uses the provided buffer as the * data buffer. *

* Column is split into two fields, family and qualifier. * @param buffer the bytes buffer to use * @param boffset buffer offset * @param row row key * @param roffset row offset * @param rlength row length * @param family family name * @param foffset family offset * @param flength family length * @param qualifier column qualifier * @param qoffset qualifier offset * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param value column value * @param voffset value offset * @param vlength value length * @param tags non-empty list of tags or null * @throws IllegalArgumentException an illegal value was passed or there is insufficient space * remaining in the buffer */ public KeyValue(byte[] buffer, final int boffset, final byte[] row, final int roffset, final int rlength, final byte[] family, final int foffset, final int flength, final byte[] qualifier, final int qoffset, final int qlength, final long timestamp, final Type type, final byte[] value, final int voffset, final int vlength, final Tag[] tags) { this.bytes = buffer; this.length = writeByteArray(buffer, boffset, row, roffset, rlength, family, foffset, flength, qualifier, qoffset, qlength, timestamp, type, value, voffset, vlength, tags); this.offset = boffset; } /** * Constructs KeyValue structure filled with specified values. *

* Column is split into two fields, family and qualifier. * @param row row key * @param roffset row offset * @param rlength row length * @param family family name * @param foffset family offset * @param flength family length * @param qualifier column qualifier * @param qoffset qualifier offset * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param value column value * @param voffset value offset * @param vlength value length * @param tags tags * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final byte[] row, final int roffset, final int rlength, final byte[] family, final int foffset, final int flength, final byte[] qualifier, final int qoffset, final int qlength, final long timestamp, final Type type, final byte[] value, final int voffset, final int vlength, final List tags) { this.bytes = createByteArray(row, roffset, rlength, family, foffset, flength, qualifier, qoffset, qlength, timestamp, type, value, voffset, vlength, tags); this.length = bytes.length; this.offset = 0; } /** * @param row row key * @param roffset row offset * @param rlength row length * @param family family name * @param foffset fammily offset * @param flength family length * @param qualifier column qualifier * @param qoffset qualifier offset * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param value column value * @param voffset value offset * @param vlength value length * @param tags input tags */ public KeyValue(final byte[] row, final int roffset, final int rlength, final byte[] family, final int foffset, final int flength, final byte[] qualifier, final int qoffset, final int qlength, final long timestamp, final Type type, final byte[] value, final int voffset, final int vlength, final byte[] tags, final int tagsOffset, final int tagsLength) { this.bytes = createByteArray(row, roffset, rlength, family, foffset, flength, qualifier, qoffset, qlength, timestamp, type, value, voffset, vlength, tags, tagsOffset, tagsLength); this.length = bytes.length; this.offset = 0; } /** * Constructs an empty KeyValue structure, with specified sizes. This can be used to partially * fill up KeyValues. *

* Column is split into two fields, family and qualifier. * @param rlength row length * @param flength family length * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param vlength value length * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final int rlength, final int flength, final int qlength, final long timestamp, final Type type, final int vlength) { this(rlength, flength, qlength, timestamp, type, vlength, 0); } /** * Constructs an empty KeyValue structure, with specified sizes. This can be used to partially * fill up KeyValues. *

* Column is split into two fields, family and qualifier. * @param rlength row length * @param flength family length * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param vlength value length * @param tagsLength length of the tags * @throws IllegalArgumentException an illegal value was passed */ public KeyValue(final int rlength, final int flength, final int qlength, final long timestamp, final Type type, final int vlength, final int tagsLength) { this.bytes = createEmptyByteArray(rlength, flength, qlength, timestamp, type, vlength, tagsLength); this.length = bytes.length; this.offset = 0; } public KeyValue(byte[] row, int roffset, int rlength, byte[] family, int foffset, int flength, ByteBuffer qualifier, long ts, Type type, ByteBuffer value, List tags) { this.bytes = createByteArray(row, roffset, rlength, family, foffset, flength, qualifier, 0, qualifier == null ? 0 : qualifier.remaining(), ts, type, value, 0, value == null ? 0 : value.remaining(), tags); this.length = bytes.length; this.offset = 0; } public KeyValue(Cell c) { this(c.getRowArray(), c.getRowOffset(), c.getRowLength(), c.getFamilyArray(), c.getFamilyOffset(), c.getFamilyLength(), c.getQualifierArray(), c.getQualifierOffset(), c.getQualifierLength(), c.getTimestamp(), Type.codeToType(c.getTypeByte()), c.getValueArray(), c.getValueOffset(), c.getValueLength(), c.getTagsArray(), c.getTagsOffset(), c.getTagsLength()); this.seqId = c.getSequenceId(); } /** * Create an empty byte[] representing a KeyValue All lengths are preset and can be filled in * later. * @param rlength row length * @param flength family length * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param vlength value length * @return The newly created byte array. */ private static byte[] createEmptyByteArray(final int rlength, int flength, int qlength, final long timestamp, final Type type, int vlength, int tagsLength) { if (rlength > Short.MAX_VALUE) { throw new IllegalArgumentException("Row > " + Short.MAX_VALUE); } if (flength > Byte.MAX_VALUE) { throw new IllegalArgumentException("Family > " + Byte.MAX_VALUE); } // Qualifier length if (qlength > Integer.MAX_VALUE - rlength - flength) { throw new IllegalArgumentException("Qualifier > " + Integer.MAX_VALUE); } RawCell.checkForTagsLength(tagsLength); // Key length long longkeylength = getKeyDataStructureSize(rlength, flength, qlength); if (longkeylength > Integer.MAX_VALUE) { throw new IllegalArgumentException("keylength " + longkeylength + " > " + Integer.MAX_VALUE); } int keylength = (int) longkeylength; // Value length if (vlength > HConstants.MAXIMUM_VALUE_LENGTH) { // FindBugs INT_VACUOUS_COMPARISON throw new IllegalArgumentException("Valuer > " + HConstants.MAXIMUM_VALUE_LENGTH); } // Allocate right-sized byte array. byte[] bytes = new byte[(int) getKeyValueDataStructureSize(rlength, flength, qlength, vlength, tagsLength)]; // Write the correct size markers int pos = 0; pos = Bytes.putInt(bytes, pos, keylength); pos = Bytes.putInt(bytes, pos, vlength); pos = Bytes.putShort(bytes, pos, (short) (rlength & 0x0000ffff)); pos += rlength; pos = Bytes.putByte(bytes, pos, (byte) (flength & 0x0000ff)); pos += flength + qlength; pos = Bytes.putLong(bytes, pos, timestamp); pos = Bytes.putByte(bytes, pos, type.getCode()); pos += vlength; if (tagsLength > 0) { pos = Bytes.putAsShort(bytes, pos, tagsLength); } return bytes; } /** * Checks the parameters passed to a constructor. * @param row row key * @param rlength row length * @param family family name * @param flength family length * @param qlength qualifier length * @param vlength value length * @throws IllegalArgumentException an illegal value was passed */ static void checkParameters(final byte[] row, final int rlength, final byte[] family, int flength, int qlength, int vlength) throws IllegalArgumentException { if (rlength > Short.MAX_VALUE) { throw new IllegalArgumentException("Row > " + Short.MAX_VALUE); } if (row == null) { throw new IllegalArgumentException("Row is null"); } // Family length flength = family == null ? 0 : flength; if (flength > Byte.MAX_VALUE) { throw new IllegalArgumentException("Family > " + Byte.MAX_VALUE); } // Qualifier length if (qlength > Integer.MAX_VALUE - rlength - flength) { throw new IllegalArgumentException("Qualifier > " + Integer.MAX_VALUE); } // Key length long longKeyLength = getKeyDataStructureSize(rlength, flength, qlength); if (longKeyLength > Integer.MAX_VALUE) { throw new IllegalArgumentException("keylength " + longKeyLength + " > " + Integer.MAX_VALUE); } // Value length if (vlength > HConstants.MAXIMUM_VALUE_LENGTH) { // FindBugs INT_VACUOUS_COMPARISON throw new IllegalArgumentException( "Value length " + vlength + " > " + HConstants.MAXIMUM_VALUE_LENGTH); } } /** * Write KeyValue format into the provided byte array. * @param buffer the bytes buffer to use * @param boffset buffer offset * @param row row key * @param roffset row offset * @param rlength row length * @param family family name * @param foffset family offset * @param flength family length * @param qualifier column qualifier * @param qoffset qualifier offset * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param value column value * @param voffset value offset * @param vlength value length * @return The number of useful bytes in the buffer. * @throws IllegalArgumentException an illegal value was passed or there is insufficient space * remaining in the buffer */ public static int writeByteArray(byte[] buffer, final int boffset, final byte[] row, final int roffset, final int rlength, final byte[] family, final int foffset, int flength, final byte[] qualifier, final int qoffset, int qlength, final long timestamp, final Type type, final byte[] value, final int voffset, int vlength, Tag[] tags) { checkParameters(row, rlength, family, flength, qlength, vlength); // Calculate length of tags area int tagsLength = 0; if (tags != null && tags.length > 0) { for (Tag t : tags) { tagsLength += t.getValueLength() + Tag.INFRASTRUCTURE_SIZE; } } RawCell.checkForTagsLength(tagsLength); int keyLength = (int) getKeyDataStructureSize(rlength, flength, qlength); int keyValueLength = (int) getKeyValueDataStructureSize(rlength, flength, qlength, vlength, tagsLength); if (keyValueLength > buffer.length - boffset) { throw new IllegalArgumentException( "Buffer size " + (buffer.length - boffset) + " < " + keyValueLength); } // Write key, value and key row length. int pos = boffset; pos = Bytes.putInt(buffer, pos, keyLength); pos = Bytes.putInt(buffer, pos, vlength); pos = Bytes.putShort(buffer, pos, (short) (rlength & 0x0000ffff)); pos = Bytes.putBytes(buffer, pos, row, roffset, rlength); pos = Bytes.putByte(buffer, pos, (byte) (flength & 0x0000ff)); if (flength != 0) { pos = Bytes.putBytes(buffer, pos, family, foffset, flength); } if (qlength != 0) { pos = Bytes.putBytes(buffer, pos, qualifier, qoffset, qlength); } pos = Bytes.putLong(buffer, pos, timestamp); pos = Bytes.putByte(buffer, pos, type.getCode()); if (value != null && value.length > 0) { pos = Bytes.putBytes(buffer, pos, value, voffset, vlength); } // Write the number of tags. If it is 0 then it means there are no tags. if (tagsLength > 0) { pos = Bytes.putAsShort(buffer, pos, tagsLength); for (Tag t : tags) { int tlen = t.getValueLength(); pos = Bytes.putAsShort(buffer, pos, tlen + Tag.TYPE_LENGTH_SIZE); pos = Bytes.putByte(buffer, pos, t.getType()); Tag.copyValueTo(t, buffer, pos); pos += tlen; } } return keyValueLength; } /** * Write KeyValue format into a byte array. * @param row row key * @param roffset row offset * @param rlength row length * @param family family name * @param foffset family offset * @param flength family length * @param qualifier column qualifier * @param qoffset qualifier offset * @param qlength qualifier length * @param timestamp version timestamp * @param type key type * @param value column value * @param voffset value offset * @param vlength value length * @return The newly created byte array. */ private static byte[] createByteArray(final byte[] row, final int roffset, final int rlength, final byte[] family, final int foffset, int flength, final byte[] qualifier, final int qoffset, int qlength, final long timestamp, final Type type, final byte[] value, final int voffset, int vlength, byte[] tags, int tagsOffset, int tagsLength) { checkParameters(row, rlength, family, flength, qlength, vlength); RawCell.checkForTagsLength(tagsLength); // Allocate right-sized byte array. int keyLength = (int) getKeyDataStructureSize(rlength, flength, qlength); byte[] bytes = new byte[(int) getKeyValueDataStructureSize(rlength, flength, qlength, vlength, tagsLength)]; // Write key, value and key row length. int pos = 0; pos = Bytes.putInt(bytes, pos, keyLength); pos = Bytes.putInt(bytes, pos, vlength); pos = Bytes.putShort(bytes, pos, (short) (rlength & 0x0000ffff)); pos = Bytes.putBytes(bytes, pos, row, roffset, rlength); pos = Bytes.putByte(bytes, pos, (byte) (flength & 0x0000ff)); if (flength != 0) { pos = Bytes.putBytes(bytes, pos, family, foffset, flength); } if (qlength != 0) { pos = Bytes.putBytes(bytes, pos, qualifier, qoffset, qlength); } pos = Bytes.putLong(bytes, pos, timestamp); pos = Bytes.putByte(bytes, pos, type.getCode()); if (value != null && value.length > 0) { pos = Bytes.putBytes(bytes, pos, value, voffset, vlength); } // Add the tags after the value part if (tagsLength > 0) { pos = Bytes.putAsShort(bytes, pos, tagsLength); pos = Bytes.putBytes(bytes, pos, tags, tagsOffset, tagsLength); } return bytes; } /** * @param qualifier can be a ByteBuffer or a byte[], or null. * @param value can be a ByteBuffer or a byte[], or null. */ private static byte[] createByteArray(final byte[] row, final int roffset, final int rlength, final byte[] family, final int foffset, int flength, final Object qualifier, final int qoffset, int qlength, final long timestamp, final Type type, final Object value, final int voffset, int vlength, List tags) { checkParameters(row, rlength, family, flength, qlength, vlength); // Calculate length of tags area int tagsLength = 0; if (tags != null && !tags.isEmpty()) { for (Tag t : tags) { tagsLength += t.getValueLength() + Tag.INFRASTRUCTURE_SIZE; } } RawCell.checkForTagsLength(tagsLength); // Allocate right-sized byte array. int keyLength = (int) getKeyDataStructureSize(rlength, flength, qlength); byte[] bytes = new byte[(int) getKeyValueDataStructureSize(rlength, flength, qlength, vlength, tagsLength)]; // Write key, value and key row length. int pos = 0; pos = Bytes.putInt(bytes, pos, keyLength); pos = Bytes.putInt(bytes, pos, vlength); pos = Bytes.putShort(bytes, pos, (short) (rlength & 0x0000ffff)); pos = Bytes.putBytes(bytes, pos, row, roffset, rlength); pos = Bytes.putByte(bytes, pos, (byte) (flength & 0x0000ff)); if (flength != 0) { pos = Bytes.putBytes(bytes, pos, family, foffset, flength); } if (qlength > 0) { if (qualifier instanceof ByteBuffer) { pos = Bytes.putByteBuffer(bytes, pos, (ByteBuffer) qualifier); } else { pos = Bytes.putBytes(bytes, pos, (byte[]) qualifier, qoffset, qlength); } } pos = Bytes.putLong(bytes, pos, timestamp); pos = Bytes.putByte(bytes, pos, type.getCode()); if (vlength > 0) { if (value instanceof ByteBuffer) { pos = Bytes.putByteBuffer(bytes, pos, (ByteBuffer) value); } else { pos = Bytes.putBytes(bytes, pos, (byte[]) value, voffset, vlength); } } // Add the tags after the value part if (tagsLength > 0) { pos = Bytes.putAsShort(bytes, pos, tagsLength); for (Tag t : tags) { int tlen = t.getValueLength(); pos = Bytes.putAsShort(bytes, pos, tlen + Tag.TYPE_LENGTH_SIZE); pos = Bytes.putByte(bytes, pos, t.getType()); Tag.copyValueTo(t, bytes, pos); pos += tlen; } } return bytes; } /** * Needed doing 'contains' on List. Only compares the key portion, not the value. */ @Override public boolean equals(Object other) { if (!(other instanceof Cell)) { return false; } return CellUtil.equals(this, (Cell) other); } /** * In line with {@link #equals(Object)}, only uses the key portion, not the value. */ @Override public int hashCode() { return calculateHashForKey(this); } private int calculateHashForKey(Cell cell) { // pre-calculate the 3 hashes made of byte ranges int rowHash = Bytes.hashCode(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()); int familyHash = Bytes.hashCode(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()); int qualifierHash = Bytes.hashCode(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); // combine the 6 sub-hashes int hash = 31 * rowHash + familyHash; hash = 31 * hash + qualifierHash; hash = 31 * hash + (int) cell.getTimestamp(); hash = 31 * hash + cell.getTypeByte(); return hash; } // --------------------------------------------------------------------------- // // KeyValue cloning // // --------------------------------------------------------------------------- /** * Clones a KeyValue. This creates a copy, re-allocating the buffer. * @return Fully copied clone of this KeyValue * @throws CloneNotSupportedException if cloning of keyValue is not supported */ @Override public KeyValue clone() throws CloneNotSupportedException { KeyValue ret = (KeyValue) super.clone(); ret.bytes = Arrays.copyOf(this.bytes, this.bytes.length); ret.offset = 0; ret.length = ret.bytes.length; // Important to clone the memstoreTS as well - otherwise memstore's // update-in-place methods (eg increment) will end up creating // new entries ret.setSequenceId(seqId); return ret; } /** * Creates a shallow copy of this KeyValue, reusing the data byte buffer. * http://en.wikipedia.org/wiki/Object_copy * @return Shallow copy of this KeyValue */ public KeyValue shallowCopy() { KeyValue shallowCopy = new KeyValue(this.bytes, this.offset, this.length); shallowCopy.setSequenceId(this.seqId); return shallowCopy; } // --------------------------------------------------------------------------- // // String representation // // --------------------------------------------------------------------------- @Override public String toString() { if (this.bytes == null || this.bytes.length == 0) { return "empty"; } return keyToString(this.bytes, this.offset + ROW_OFFSET, getKeyLength()) + "/vlen=" + getValueLength() + "/seqid=" + seqId; } /** Return key as a String, empty string if k is null. */ public static String keyToString(final byte[] k) { if (k == null) { return ""; } return keyToString(k, 0, k.length); } /** * Produces a string map for this key/value pair. Useful for programmatic use and manipulation of * the data stored in an WALKey, for example, printing as JSON. Values are left out due to their * tendency to be large. If needed, they can be added manually. * @return the Map<String,?> containing data from this key */ public Map toStringMap() { Map stringMap = new HashMap<>(); stringMap.put("row", Bytes.toStringBinary(getRowArray(), getRowOffset(), getRowLength())); stringMap.put("family", Bytes.toStringBinary(getFamilyArray(), getFamilyOffset(), getFamilyLength())); stringMap.put("qualifier", Bytes.toStringBinary(getQualifierArray(), getQualifierOffset(), getQualifierLength())); stringMap.put("timestamp", getTimestamp()); stringMap.put("vlen", getValueLength()); Iterator tags = getTags(); if (tags != null) { List tagsString = new ArrayList(); while (tags.hasNext()) { tagsString.add(tags.next().toString()); } stringMap.put("tag", tagsString); } return stringMap; } /** * Use for logging. * @param b Key portion of a KeyValue. * @param o Offset to start of key * @param l Length of key. * @return Key as a String. */ public static String keyToString(final byte[] b, final int o, final int l) { if (b == null) { return ""; } int rowlength = Bytes.toShort(b, o); String row = Bytes.toStringBinary(b, o + Bytes.SIZEOF_SHORT, rowlength); int columnoffset = o + Bytes.SIZEOF_SHORT + 1 + rowlength; int familylength = b[columnoffset - 1]; int columnlength = l - ((columnoffset - o) + TIMESTAMP_TYPE_SIZE); String family = familylength == 0 ? "" : Bytes.toStringBinary(b, columnoffset, familylength); String qualifier = columnlength == 0 ? "" : Bytes.toStringBinary(b, columnoffset + familylength, columnlength - familylength); long timestamp = Bytes.toLong(b, o + (l - TIMESTAMP_TYPE_SIZE)); String timestampStr = humanReadableTimestamp(timestamp); byte type = b[o + l - 1]; return row + "/" + family + (family != null && family.length() > 0 ? ":" : "") + qualifier + "/" + timestampStr + "/" + Type.codeToType(type); } public static String humanReadableTimestamp(final long timestamp) { if (timestamp == HConstants.LATEST_TIMESTAMP) { return "LATEST_TIMESTAMP"; } if (timestamp == PrivateConstants.OLDEST_TIMESTAMP) { return "OLDEST_TIMESTAMP"; } return String.valueOf(timestamp); } // --------------------------------------------------------------------------- // // Public Member Accessors // // --------------------------------------------------------------------------- /** * To be used only in tests where the Cells are clearly assumed to be of type KeyValue and that we * need access to the backing array to do some test case related assertions. * @return The byte array backing this KeyValue. */ public byte[] getBuffer() { return this.bytes; } /** Returns Offset into {@link #getBuffer()} at which this KeyValue starts. */ public int getOffset() { return this.offset; } /** Returns Length of bytes this KeyValue occupies in {@link #getBuffer()}. */ public int getLength() { return length; } // --------------------------------------------------------------------------- // // Length and Offset Calculators // // --------------------------------------------------------------------------- /** * Determines the total length of the KeyValue stored in the specified byte array and offset. * Includes all headers. * @param bytes byte array * @param offset offset to start of the KeyValue * @return length of entire KeyValue, in bytes */ private static int getLength(byte[] bytes, int offset) { int klength = ROW_OFFSET + Bytes.toInt(bytes, offset); int vlength = Bytes.toInt(bytes, offset + Bytes.SIZEOF_INT); return klength + vlength; } /** Returns Key offset in backing buffer.. */ public int getKeyOffset() { return this.offset + ROW_OFFSET; } public String getKeyString() { return Bytes.toStringBinary(getBuffer(), getKeyOffset(), getKeyLength()); } /** Returns Length of key portion. */ public int getKeyLength() { return Bytes.toInt(this.bytes, this.offset); } /** * Returns the backing array of the entire KeyValue (all KeyValue fields are in a single array) */ @Override public byte[] getValueArray() { return bytes; } /** Returns the value offset */ @Override public int getValueOffset() { int voffset = getKeyOffset() + getKeyLength(); return voffset; } /** Returns Value length */ @Override public int getValueLength() { int vlength = Bytes.toInt(this.bytes, this.offset + Bytes.SIZEOF_INT); return vlength; } /** * Returns the backing array of the entire KeyValue (all KeyValue fields are in a single array) */ @Override public byte[] getRowArray() { return bytes; } /** Returns Row offset */ @Override public int getRowOffset() { return this.offset + ROW_KEY_OFFSET; } /** Returns Row length */ @Override public short getRowLength() { return Bytes.toShort(this.bytes, getKeyOffset()); } /** * Returns the backing array of the entire KeyValue (all KeyValue fields are in a single array) */ @Override public byte[] getFamilyArray() { return bytes; } /** Returns Family offset */ @Override public int getFamilyOffset() { return getFamilyOffset(getFamilyLengthPosition(getRowLength())); } /** Returns Family offset */ int getFamilyOffset(int familyLenPosition) { return familyLenPosition + Bytes.SIZEOF_BYTE; } /** Returns Family length */ @Override public byte getFamilyLength() { return getFamilyLength(getFamilyLengthPosition(getRowLength())); } /** Returns Family length */ public byte getFamilyLength(int famLenPos) { return this.bytes[famLenPos]; } int getFamilyLengthPosition(int rowLength) { return this.offset + KeyValue.ROW_KEY_OFFSET + rowLength; } /** * Returns the backing array of the entire KeyValue (all KeyValue fields are in a single array) */ @Override public byte[] getQualifierArray() { return bytes; } /** Returns Qualifier offset */ @Override public int getQualifierOffset() { return getQualifierOffset(getFamilyOffset()); } /** Returns Qualifier offset */ private int getQualifierOffset(int foffset) { return getQualifierOffset(foffset, getFamilyLength()); } /** Returns Qualifier offset */ int getQualifierOffset(int foffset, int flength) { return foffset + flength; } /** Returns Qualifier length */ @Override public int getQualifierLength() { return getQualifierLength(getRowLength(), getFamilyLength()); } /** Returns Qualifier length */ private int getQualifierLength(int rlength, int flength) { return getQualifierLength(getKeyLength(), rlength, flength); } /** Returns Qualifier length */ int getQualifierLength(int keyLength, int rlength, int flength) { return keyLength - (int) getKeyDataStructureSize(rlength, flength, 0); } /** Returns Timestamp offset */ public int getTimestampOffset() { return getTimestampOffset(getKeyLength()); } /** Return the timestamp offset */ private int getTimestampOffset(final int keylength) { return getKeyOffset() + keylength - TIMESTAMP_TYPE_SIZE; } /** Returns True if this KeyValue has a LATEST_TIMESTAMP timestamp. */ public boolean isLatestTimestamp() { return Bytes.equals(getBuffer(), getTimestampOffset(), Bytes.SIZEOF_LONG, HConstants.LATEST_TIMESTAMP_BYTES, 0, Bytes.SIZEOF_LONG); } /** * Update the timestamp. * @param now Time to set into this IFF timestamp == * {@link HConstants#LATEST_TIMESTAMP} (else, its a noop). * @return True is we modified this. */ public boolean updateLatestStamp(final byte[] now) { if (this.isLatestTimestamp()) { int tsOffset = getTimestampOffset(); System.arraycopy(now, 0, this.bytes, tsOffset, Bytes.SIZEOF_LONG); // clear cache or else getTimestamp() possibly returns an old value return true; } return false; } @Override public void setTimestamp(long ts) { Bytes.putBytes(this.bytes, this.getTimestampOffset(), Bytes.toBytes(ts), 0, Bytes.SIZEOF_LONG); } @Override public void setTimestamp(byte[] ts) { Bytes.putBytes(this.bytes, this.getTimestampOffset(), ts, 0, Bytes.SIZEOF_LONG); } // --------------------------------------------------------------------------- // // Methods that return copies of fields // // --------------------------------------------------------------------------- /** * Do not use unless you have to. Used internally for compacting and testing. Use * {@link #getRowArray()}, {@link #getFamilyArray()}, {@link #getQualifierArray()}, and * {@link #getValueArray()} if accessing a KeyValue client-side. * @return Copy of the key portion only. */ public byte[] getKey() { int keylength = getKeyLength(); byte[] key = new byte[keylength]; System.arraycopy(getBuffer(), getKeyOffset(), key, 0, keylength); return key; } /** Return the timestamp. */ @Override public long getTimestamp() { return getTimestamp(getKeyLength()); } /** Return the timestamp. */ long getTimestamp(final int keylength) { int tsOffset = getTimestampOffset(keylength); return Bytes.toLong(this.bytes, tsOffset); } /** Returns KeyValue.TYPE byte representation */ @Override public byte getTypeByte() { return getTypeByte(getKeyLength()); } /** Return the KeyValue.TYPE byte representation */ byte getTypeByte(int keyLength) { return this.bytes[this.offset + keyLength - 1 + ROW_OFFSET]; } /** Return the offset where the tag data starts. */ @Override public int getTagsOffset() { int tagsLen = getTagsLength(); if (tagsLen == 0) { return this.offset + this.length; } return this.offset + this.length - tagsLen; } /** Return the total length of the tag bytes */ @Override public int getTagsLength() { int tagsLen = this.length - (getKeyLength() + getValueLength() + KEYVALUE_INFRASTRUCTURE_SIZE); if (tagsLen > 0) { // There are some Tag bytes in the byte[]. So reduce 2 bytes which is added to denote the tags // length tagsLen -= TAGS_LENGTH_SIZE; } return tagsLen; } /** * Returns the backing array of the entire KeyValue (all KeyValue fields are in a single array) */ @Override public byte[] getTagsArray() { return bytes; } /** * Creates a new KeyValue that only contains the key portion (the value is set to be null). TODO * only used by KeyOnlyFilter -- move there. * @param lenAsVal replace value with the actual value length (false=empty) */ public KeyValue createKeyOnly(boolean lenAsVal) { // KV format: // Rebuild as: <0:4> int dataLen = lenAsVal ? Bytes.SIZEOF_INT : 0; byte[] newBuffer = new byte[getKeyLength() + ROW_OFFSET + dataLen]; System.arraycopy(this.bytes, this.offset, newBuffer, 0, Math.min(newBuffer.length, this.length)); Bytes.putInt(newBuffer, Bytes.SIZEOF_INT, dataLen); if (lenAsVal) { Bytes.putInt(newBuffer, newBuffer.length - dataLen, this.getValueLength()); } return new KeyValue(newBuffer); } /** * Find index of passed delimiter walking from start of buffer forwards. * @param b the kv serialized byte[] to process * @param delimiter input delimeter to fetch index from start * @return Index of delimiter having started from start of b moving rightward. */ public static int getDelimiter(final byte[] b, int offset, final int length, final int delimiter) { if (b == null) { throw new IllegalArgumentException("Passed buffer is null"); } int result = -1; for (int i = offset; i < length + offset; i++) { if (b[i] == delimiter) { result = i; break; } } return result; } /** * Find index of passed delimiter walking from end of buffer backwards. * @param b the kv serialized byte[] to process * @param offset the offset in the byte[] * @param length the length in the byte[] * @param delimiter input delimeter to fetch index from end * @return Index of delimiter */ public static int getDelimiterInReverse(final byte[] b, final int offset, final int length, final int delimiter) { if (b == null) { throw new IllegalArgumentException("Passed buffer is null"); } int result = -1; for (int i = (offset + length) - 1; i >= offset; i--) { if (b[i] == delimiter) { result = i; break; } } return result; } /** * A {@link KVComparator} for hbase:meta catalog table {@link KeyValue}s. * @deprecated : {@link MetaCellComparator#META_COMPARATOR} to be used. Deprecated for hbase 2.0, * remove for hbase 3.0. */ @Deprecated public static class MetaComparator extends KVComparator { /** * Compare key portion of a {@link KeyValue} for keys in hbase:meta table. */ @Override public int compare(final Cell left, final Cell right) { return PrivateCellUtil.compareKeyIgnoresMvcc(MetaCellComparator.META_COMPARATOR, left, right); } @Override public int compareOnlyKeyPortion(Cell left, Cell right) { return compare(left, right); } @Override public int compareRows(byte[] left, int loffset, int llength, byte[] right, int roffset, int rlength) { int leftDelimiter = getDelimiter(left, loffset, llength, HConstants.DELIMITER); int rightDelimiter = getDelimiter(right, roffset, rlength, HConstants.DELIMITER); // Compare up to the delimiter int lpart = (leftDelimiter < 0 ? llength : leftDelimiter - loffset); int rpart = (rightDelimiter < 0 ? rlength : rightDelimiter - roffset); int result = Bytes.compareTo(left, loffset, lpart, right, roffset, rpart); if (result != 0) { return result; } else { if (leftDelimiter < 0 && rightDelimiter >= 0) { return -1; } else if (rightDelimiter < 0 && leftDelimiter >= 0) { return 1; } else if (leftDelimiter < 0 && rightDelimiter < 0) { return 0; } } // Compare middle bit of the row. // Move past delimiter leftDelimiter++; rightDelimiter++; int leftFarDelimiter = getDelimiterInReverse(left, leftDelimiter, llength - (leftDelimiter - loffset), HConstants.DELIMITER); int rightFarDelimiter = getDelimiterInReverse(right, rightDelimiter, rlength - (rightDelimiter - roffset), HConstants.DELIMITER); // Now compare middlesection of row. lpart = (leftFarDelimiter < 0 ? llength + loffset : leftFarDelimiter) - leftDelimiter; rpart = (rightFarDelimiter < 0 ? rlength + roffset : rightFarDelimiter) - rightDelimiter; result = super.compareRows(left, leftDelimiter, lpart, right, rightDelimiter, rpart); if (result != 0) { return result; } else { if (leftDelimiter < 0 && rightDelimiter >= 0) { return -1; } else if (rightDelimiter < 0 && leftDelimiter >= 0) { return 1; } else if (leftDelimiter < 0 && rightDelimiter < 0) { return 0; } } // Compare last part of row, the rowid. leftFarDelimiter++; rightFarDelimiter++; result = Bytes.compareTo(left, leftFarDelimiter, llength - (leftFarDelimiter - loffset), right, rightFarDelimiter, rlength - (rightFarDelimiter - roffset)); return result; } /** * Don't do any fancy Block Index splitting tricks. */ @Override public byte[] getShortMidpointKey(final byte[] leftKey, final byte[] rightKey) { return Arrays.copyOf(rightKey, rightKey.length); } /** * The HFileV2 file format's trailer contains this class name. We reinterpret this and * instantiate the appropriate comparator. TODO: With V3 consider removing this. * @return legacy class name for FileFileTrailer#comparatorClassName */ @Override public String getLegacyKeyComparatorName() { return "org.apache.hadoop.hbase.KeyValue$MetaKeyComparator"; } @Override protected MetaComparator clone() throws CloneNotSupportedException { return (MetaComparator) super.clone(); } /** * Override the row key comparison to parse and compare the meta row key parts. */ @Override protected int compareRowKey(final Cell l, final Cell r) { byte[] left = l.getRowArray(); int loffset = l.getRowOffset(); int llength = l.getRowLength(); byte[] right = r.getRowArray(); int roffset = r.getRowOffset(); int rlength = r.getRowLength(); return compareRows(left, loffset, llength, right, roffset, rlength); } } /** * Compare KeyValues. When we compare KeyValues, we only compare the Key portion. This means two * KeyValues with same Key but different Values are considered the same as far as this Comparator * is concerned. * @deprecated : Use {@link CellComparatorImpl}. Deprecated for hbase 2.0, remove for hbase 3.0. */ @Deprecated public static class KVComparator implements RawComparator, SamePrefixComparator { /** * The HFileV2 file format's trailer contains this class name. We reinterpret this and * instantiate the appropriate comparator. TODO: With V3 consider removing this. * @return legacy class name for FileFileTrailer#comparatorClassName */ public String getLegacyKeyComparatorName() { return "org.apache.hadoop.hbase.KeyValue$KeyComparator"; } @Override // RawComparator public int compare(byte[] l, int loff, int llen, byte[] r, int roff, int rlen) { return compareFlatKey(l, loff, llen, r, roff, rlen); } /** * Compares the only the user specified portion of a Key. This is overridden by MetaComparator. * @param left left cell to compare row key * @param right right cell to compare row key * @return 0 if equal, <0 if left smaller, >0 if right smaller */ protected int compareRowKey(final Cell left, final Cell right) { return CellComparatorImpl.COMPARATOR.compareRows(left, right); } /** * Compares left to right assuming that left,loffset,llength and right,roffset,rlength are full * KVs laid out in a flat byte[]s. * @param left the left kv serialized byte[] to be compared with * @param loffset the offset in the left byte[] * @param llength the length in the left byte[] * @param right the right kv serialized byte[] to be compared with * @param roffset the offset in the right byte[] * @param rlength the length in the right byte[] * @return 0 if equal, <0 if left smaller, >0 if right smaller */ public int compareFlatKey(byte[] left, int loffset, int llength, byte[] right, int roffset, int rlength) { // Compare row short lrowlength = Bytes.toShort(left, loffset); short rrowlength = Bytes.toShort(right, roffset); int compare = compareRows(left, loffset + Bytes.SIZEOF_SHORT, lrowlength, right, roffset + Bytes.SIZEOF_SHORT, rrowlength); if (compare != 0) { return compare; } // Compare the rest of the two KVs without making any assumptions about // the common prefix. This function will not compare rows anyway, so we // don't need to tell it that the common prefix includes the row. return compareWithoutRow(0, left, loffset, llength, right, roffset, rlength, rrowlength); } public int compareFlatKey(byte[] left, byte[] right) { return compareFlatKey(left, 0, left.length, right, 0, right.length); } // compare a key against row/fam/qual/ts/type public int compareKey(Cell cell, byte[] row, int roff, int rlen, byte[] fam, int foff, int flen, byte[] col, int coff, int clen, long ts, byte type) { int compare = compareRows(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(), row, roff, rlen); if (compare != 0) { return compare; } // If the column is not specified, the "minimum" key type appears the // latest in the sorted order, regardless of the timestamp. This is used // for specifying the last key/value in a given row, because there is no // "lexicographically last column" (it would be infinitely long). The // "maximum" key type does not need this behavior. if ( cell.getFamilyLength() + cell.getQualifierLength() == 0 && cell.getTypeByte() == Type.Minimum.getCode() ) { // left is "bigger", i.e. it appears later in the sorted order return 1; } if (flen + clen == 0 && type == Type.Minimum.getCode()) { return -1; } compare = compareFamilies(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(), fam, foff, flen); if (compare != 0) { return compare; } compare = compareColumns(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(), col, coff, clen); if (compare != 0) { return compare; } // Next compare timestamps. compare = compareTimestamps(cell.getTimestamp(), ts); if (compare != 0) { return compare; } // Compare types. Let the delete types sort ahead of puts; i.e. types // of higher numbers sort before those of lesser numbers. Maximum (255) // appears ahead of everything, and minimum (0) appears after // everything. return (0xff & type) - (0xff & cell.getTypeByte()); } public int compareOnlyKeyPortion(Cell left, Cell right) { return PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, right); } /** * Compares the Key of a cell -- with fields being more significant in this order: rowkey, * colfam/qual, timestamp, type, mvcc */ @Override public int compare(final Cell left, final Cell right) { int compare = CellComparatorImpl.COMPARATOR.compare(left, right); return compare; } public int compareTimestamps(final Cell left, final Cell right) { return CellComparatorImpl.COMPARATOR.compareTimestamps(left, right); } /** * Compares the rows of a cell * @param left left cell to compare rows for * @param right right cell to compare rows for * @return Result comparing rows. */ public int compareRows(final Cell left, final Cell right) { return compareRows(left.getRowArray(), left.getRowOffset(), left.getRowLength(), right.getRowArray(), right.getRowOffset(), right.getRowLength()); } /** * Get the b[],o,l for left and right rowkey portions and compare. * @param left the left kv serialized byte[] to be compared with * @param loffset the offset in the left byte[] * @param llength the length in the left byte[] * @param right the right kv serialized byte[] to be compared with * @param roffset the offset in the right byte[] * @param rlength the length in the right byte[] * @return 0 if equal, <0 if left smaller, >0 if right smaller */ public int compareRows(byte[] left, int loffset, int llength, byte[] right, int roffset, int rlength) { return Bytes.compareTo(left, loffset, llength, right, roffset, rlength); } int compareColumns(final Cell left, final short lrowlength, final Cell right, final short rrowlength) { return CellComparatorImpl.COMPARATOR.compareColumns(left, right); } protected int compareColumns(byte[] left, int loffset, int llength, final int lfamilylength, byte[] right, int roffset, int rlength, final int rfamilylength) { // Compare family portion first. int diff = Bytes.compareTo(left, loffset, lfamilylength, right, roffset, rfamilylength); if (diff != 0) { return diff; } // Compare qualifier portion return Bytes.compareTo(left, loffset + lfamilylength, llength - lfamilylength, right, roffset + rfamilylength, rlength - rfamilylength); } static int compareTimestamps(final long ltimestamp, final long rtimestamp) { // The below older timestamps sorting ahead of newer timestamps looks // wrong but it is intentional. This way, newer timestamps are first // found when we iterate over a memstore and newer versions are the // first we trip over when reading from a store file. if (ltimestamp < rtimestamp) { return 1; } else if (ltimestamp > rtimestamp) { return -1; } return 0; } /** * Overridden * @param commonPrefix location of expected common prefix * @param left the left kv serialized byte[] to be compared with * @param loffset the offset in the left byte[] * @param llength the length in the left byte[] * @param right the right kv serialized byte[] to be compared with * @param roffset the offset in the byte[] * @param rlength the length in the right byte[] * @return 0 if equal, <0 if left smaller, >0 if right smaller */ @Override // SamePrefixComparator public int compareIgnoringPrefix(int commonPrefix, byte[] left, int loffset, int llength, byte[] right, int roffset, int rlength) { // Compare row short lrowlength = Bytes.toShort(left, loffset); short rrowlength; int comparisonResult = 0; if (commonPrefix < ROW_LENGTH_SIZE) { // almost nothing in common rrowlength = Bytes.toShort(right, roffset); comparisonResult = compareRows(left, loffset + ROW_LENGTH_SIZE, lrowlength, right, roffset + ROW_LENGTH_SIZE, rrowlength); } else { // the row length is the same rrowlength = lrowlength; if (commonPrefix < ROW_LENGTH_SIZE + rrowlength) { // The rows are not the same. Exclude the common prefix and compare // the rest of the two rows. int common = commonPrefix - ROW_LENGTH_SIZE; comparisonResult = compareRows(left, loffset + common + ROW_LENGTH_SIZE, lrowlength - common, right, roffset + common + ROW_LENGTH_SIZE, rrowlength - common); } } if (comparisonResult != 0) { return comparisonResult; } assert lrowlength == rrowlength; return compareWithoutRow(commonPrefix, left, loffset, llength, right, roffset, rlength, lrowlength); } /** * Compare columnFamily, qualifier, timestamp, and key type (everything except the row). This * method is used both in the normal comparator and the "same-prefix" comparator. Note that we * are assuming that row portions of both KVs have already been parsed and found identical, and * we don't validate that assumption here. the length of the common prefix of the two key-values * being compared, including row length and row */ private int compareWithoutRow(int commonPrefix, byte[] left, int loffset, int llength, byte[] right, int roffset, int rlength, short rowlength) { /*** * KeyValue Format and commonLength: * |_keyLen_|_valLen_|_rowLen_|_rowKey_|_famiLen_|_fami_|_Quali_|.... * ------------------|-------commonLength--------|-------------- */ int commonLength = ROW_LENGTH_SIZE + FAMILY_LENGTH_SIZE + rowlength; // commonLength + TIMESTAMP_TYPE_SIZE int commonLengthWithTSAndType = TIMESTAMP_TYPE_SIZE + commonLength; // ColumnFamily + Qualifier length. int lcolumnlength = llength - commonLengthWithTSAndType; int rcolumnlength = rlength - commonLengthWithTSAndType; byte ltype = left[loffset + (llength - 1)]; byte rtype = right[roffset + (rlength - 1)]; // If the column is not specified, the "minimum" key type appears the // latest in the sorted order, regardless of the timestamp. This is used // for specifying the last key/value in a given row, because there is no // "lexicographically last column" (it would be infinitely long). The // "maximum" key type does not need this behavior. if (lcolumnlength == 0 && ltype == Type.Minimum.getCode()) { // left is "bigger", i.e. it appears later in the sorted order return 1; } if (rcolumnlength == 0 && rtype == Type.Minimum.getCode()) { return -1; } int lfamilyoffset = commonLength + loffset; int rfamilyoffset = commonLength + roffset; // Column family length. int lfamilylength = left[lfamilyoffset - 1]; int rfamilylength = right[rfamilyoffset - 1]; // If left family size is not equal to right family size, we need not // compare the qualifiers. boolean sameFamilySize = (lfamilylength == rfamilylength); int common = 0; if (commonPrefix > 0) { common = Math.max(0, commonPrefix - commonLength); if (!sameFamilySize) { // Common should not be larger than Math.min(lfamilylength, // rfamilylength). common = Math.min(common, Math.min(lfamilylength, rfamilylength)); } else { common = Math.min(common, Math.min(lcolumnlength, rcolumnlength)); } } if (!sameFamilySize) { // comparing column family is enough. return Bytes.compareTo(left, lfamilyoffset + common, lfamilylength - common, right, rfamilyoffset + common, rfamilylength - common); } // Compare family & qualifier together. final int comparison = Bytes.compareTo(left, lfamilyoffset + common, lcolumnlength - common, right, rfamilyoffset + common, rcolumnlength - common); if (comparison != 0) { return comparison; } //// // Next compare timestamps. long ltimestamp = Bytes.toLong(left, loffset + (llength - TIMESTAMP_TYPE_SIZE)); long rtimestamp = Bytes.toLong(right, roffset + (rlength - TIMESTAMP_TYPE_SIZE)); int compare = compareTimestamps(ltimestamp, rtimestamp); if (compare != 0) { return compare; } // Compare types. Let the delete types sort ahead of puts; i.e. types // of higher numbers sort before those of lesser numbers. Maximum (255) // appears ahead of everything, and minimum (0) appears after // everything. return (0xff & rtype) - (0xff & ltype); } protected int compareFamilies(final byte[] left, final int loffset, final int lfamilylength, final byte[] right, final int roffset, final int rfamilylength) { int diff = Bytes.compareTo(left, loffset, lfamilylength, right, roffset, rfamilylength); return diff; } protected int compareColumns(final byte[] left, final int loffset, final int lquallength, final byte[] right, final int roffset, final int rquallength) { int diff = Bytes.compareTo(left, loffset, lquallength, right, roffset, rquallength); return diff; } /** * Compares the row and column of two keyvalues for equality * @param left left cell to compare row and column * @param right right cell to compare row and column * @return True if same row and column. */ public boolean matchingRowColumn(final Cell left, final Cell right) { short lrowlength = left.getRowLength(); short rrowlength = right.getRowLength(); // TsOffset = end of column data. just comparing Row+CF length of each if ( (left.getRowLength() + left.getFamilyLength() + left.getQualifierLength()) != (right.getRowLength() + right.getFamilyLength() + right.getQualifierLength()) ) { return false; } if (!matchingRows(left, lrowlength, right, rrowlength)) { return false; } int lfoffset = left.getFamilyOffset(); int rfoffset = right.getFamilyOffset(); int lclength = left.getQualifierLength(); int rclength = right.getQualifierLength(); int lfamilylength = left.getFamilyLength(); int rfamilylength = right.getFamilyLength(); int diff = compareFamilies(left.getFamilyArray(), lfoffset, lfamilylength, right.getFamilyArray(), rfoffset, rfamilylength); if (diff != 0) { return false; } else { diff = compareColumns(left.getQualifierArray(), left.getQualifierOffset(), lclength, right.getQualifierArray(), right.getQualifierOffset(), rclength); return diff == 0; } } /** * Compares the row of two keyvalues for equality * @param left left cell to compare row * @param right right cell to compare row * @return True if rows match. */ public boolean matchingRows(final Cell left, final Cell right) { short lrowlength = left.getRowLength(); short rrowlength = right.getRowLength(); return matchingRows(left, lrowlength, right, rrowlength); } /** * Compares the row of two keyvalues for equality * @param left left cell to compare row * @param lrowlength left row length * @param right right cell to compare row * @param rrowlength right row length * @return True if rows match. */ private boolean matchingRows(final Cell left, final short lrowlength, final Cell right, final short rrowlength) { return lrowlength == rrowlength && matchingRows(left.getRowArray(), left.getRowOffset(), lrowlength, right.getRowArray(), right.getRowOffset(), rrowlength); } /** * Compare rows. Just calls Bytes.equals, but it's good to have this encapsulated. * @param left Left row array. * @param loffset Left row offset. * @param llength Left row length. * @param right Right row array. * @param roffset Right row offset. * @param rlength Right row length. * @return Whether rows are the same row. */ public boolean matchingRows(final byte[] left, final int loffset, final int llength, final byte[] right, final int roffset, final int rlength) { return Bytes.equals(left, loffset, llength, right, roffset, rlength); } public byte[] calcIndexKey(byte[] lastKeyOfPreviousBlock, byte[] firstKeyInBlock) { byte[] fakeKey = getShortMidpointKey(lastKeyOfPreviousBlock, firstKeyInBlock); if (compareFlatKey(fakeKey, firstKeyInBlock) > 0) { LOG.error("Unexpected getShortMidpointKey result, fakeKey:" + Bytes.toStringBinary(fakeKey) + ", firstKeyInBlock:" + Bytes.toStringBinary(firstKeyInBlock)); return firstKeyInBlock; } if (lastKeyOfPreviousBlock != null && compareFlatKey(lastKeyOfPreviousBlock, fakeKey) >= 0) { LOG.error("Unexpected getShortMidpointKey result, lastKeyOfPreviousBlock:" + Bytes.toStringBinary(lastKeyOfPreviousBlock) + ", fakeKey:" + Bytes.toStringBinary(fakeKey)); return firstKeyInBlock; } return fakeKey; } /** * This is a HFile block index key optimization. * @param leftKey byte array for left Key * @param rightKey byte array for right Key * @return 0 if equal, <0 if left smaller, >0 if right smaller * @deprecated Since 0.99.2; */ @Deprecated public byte[] getShortMidpointKey(final byte[] leftKey, final byte[] rightKey) { if (rightKey == null) { throw new IllegalArgumentException("rightKey can not be null"); } if (leftKey == null) { return Arrays.copyOf(rightKey, rightKey.length); } if (compareFlatKey(leftKey, rightKey) >= 0) { throw new IllegalArgumentException("Unexpected input, leftKey:" + Bytes.toString(leftKey) + ", rightKey:" + Bytes.toString(rightKey)); } short leftRowLength = Bytes.toShort(leftKey, 0); short rightRowLength = Bytes.toShort(rightKey, 0); int leftCommonLength = ROW_LENGTH_SIZE + FAMILY_LENGTH_SIZE + leftRowLength; int rightCommonLength = ROW_LENGTH_SIZE + FAMILY_LENGTH_SIZE + rightRowLength; int leftCommonLengthWithTSAndType = TIMESTAMP_TYPE_SIZE + leftCommonLength; int rightCommonLengthWithTSAndType = TIMESTAMP_TYPE_SIZE + rightCommonLength; int leftColumnLength = leftKey.length - leftCommonLengthWithTSAndType; int rightColumnLength = rightKey.length - rightCommonLengthWithTSAndType; // rows are equal if ( leftRowLength == rightRowLength && compareRows(leftKey, ROW_LENGTH_SIZE, leftRowLength, rightKey, ROW_LENGTH_SIZE, rightRowLength) == 0 ) { // Compare family & qualifier together. int comparison = Bytes.compareTo(leftKey, leftCommonLength, leftColumnLength, rightKey, rightCommonLength, rightColumnLength); // same with "row + family + qualifier", return rightKey directly if (comparison == 0) { return Arrays.copyOf(rightKey, rightKey.length); } // "family + qualifier" are different, generate a faked key per rightKey byte[] newKey = Arrays.copyOf(rightKey, rightKey.length); Bytes.putLong(newKey, rightKey.length - TIMESTAMP_TYPE_SIZE, HConstants.LATEST_TIMESTAMP); Bytes.putByte(newKey, rightKey.length - TYPE_SIZE, Type.Maximum.getCode()); return newKey; } // rows are different short minLength = leftRowLength < rightRowLength ? leftRowLength : rightRowLength; short diffIdx = 0; while ( diffIdx < minLength && leftKey[ROW_LENGTH_SIZE + diffIdx] == rightKey[ROW_LENGTH_SIZE + diffIdx] ) { diffIdx++; } byte[] newRowKey = null; if (diffIdx >= minLength) { // leftKey's row is prefix of rightKey's. newRowKey = new byte[diffIdx + 1]; System.arraycopy(rightKey, ROW_LENGTH_SIZE, newRowKey, 0, diffIdx + 1); } else { int diffByte = leftKey[ROW_LENGTH_SIZE + diffIdx]; if ( (0xff & diffByte) < 0xff && (diffByte + 1) < (rightKey[ROW_LENGTH_SIZE + diffIdx] & 0xff) ) { newRowKey = new byte[diffIdx + 1]; System.arraycopy(leftKey, ROW_LENGTH_SIZE, newRowKey, 0, diffIdx); newRowKey[diffIdx] = (byte) (diffByte + 1); } else { newRowKey = new byte[diffIdx + 1]; System.arraycopy(rightKey, ROW_LENGTH_SIZE, newRowKey, 0, diffIdx + 1); } } return new KeyValue(newRowKey, null, null, HConstants.LATEST_TIMESTAMP, Type.Maximum) .getKey(); } @Override protected KVComparator clone() throws CloneNotSupportedException { return (KVComparator) super.clone(); } } /** * Create a KeyValue reading from in * @param in Where to read bytes from. Creates a byte array to hold the KeyValue backing bytes * copied from the steam. * @return KeyValue created by deserializing from in OR if we find a length of zero, * we will return null which can be useful marking a stream as done. * @throws IOException if any IO error happen */ public static KeyValue create(final DataInput in) throws IOException { return create(in.readInt(), in); } /** * Create a KeyValue reading length from in * @param length length of the Key * @param in Input to read from * @return Created KeyValue OR if we find a length of zero, we will return null which can be * useful marking a stream as done. * @throws IOException if any IO error happen */ public static KeyValue create(int length, final DataInput in) throws IOException { if (length <= 0) { if (length == 0) { return null; } throw new IOException("Failed read " + length + " bytes, stream corrupt?"); } // This is how the old Writables.readFrom used to deserialize. Didn't even vint. byte[] bytes = new byte[length]; in.readFully(bytes); return new KeyValue(bytes, 0, length); } /** * Write out a KeyValue in the manner in which we used to when KeyValue was a Writable. * @param kv the KeyValue on which write is being requested * @param out OutputStream to write keyValue to * @return Length written on stream * @throws IOException if any IO error happen * @see #create(DataInput) for the inverse function */ public static long write(final KeyValue kv, final DataOutput out) throws IOException { // This is how the old Writables write used to serialize KVs. Need to figure way to make it // work for all implementations. int length = kv.getLength(); out.writeInt(length); out.write(kv.getBuffer(), kv.getOffset(), length); return (long) length + Bytes.SIZEOF_INT; } /** * Write out a KeyValue in the manner in which we used to when KeyValue was a Writable but do not * require a {@link DataOutput}, just take plain {@link OutputStream} Named oswrite * so does not clash with {@link #write(KeyValue, DataOutput)} * @param kv the KeyValue on which write is being requested * @param out OutputStream to write keyValue to * @param withTags boolean value indicating write is with Tags or not * @return Length written on stream * @throws IOException if any IO error happen * @see #create(DataInput) for the inverse function * @see #write(KeyValue, DataOutput) * @see KeyValueUtil#oswrite(Cell, OutputStream, boolean) * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0. Instead use * {@link #write(OutputStream, boolean)} */ @Deprecated public static long oswrite(final KeyValue kv, final OutputStream out, final boolean withTags) throws IOException { ByteBufferUtils.putInt(out, kv.getSerializedSize(withTags)); return (long) kv.write(out, withTags) + Bytes.SIZEOF_INT; } @Override public int write(OutputStream out, boolean withTags) throws IOException { int len = getSerializedSize(withTags); out.write(this.bytes, this.offset, len); return len; } @Override public int getSerializedSize(boolean withTags) { if (withTags) { return this.length; } return this.getKeyLength() + this.getValueLength() + KEYVALUE_INFRASTRUCTURE_SIZE; } @Override public int getSerializedSize() { return this.length; } @Override public void write(ByteBuffer buf, int offset) { ByteBufferUtils.copyFromArrayToBuffer(buf, offset, this.bytes, this.offset, this.length); } /** * Avoids redundant comparisons for better performance. TODO get rid of this wart */ public interface SamePrefixComparator { /** * Compare two keys assuming that the first n bytes are the same. * @param commonPrefix How many bytes are the same. */ int compareIgnoringPrefix(int commonPrefix, byte[] left, int loffset, int llength, byte[] right, int roffset, int rlength); } /** * HeapSize implementation *

* We do not count the bytes in the rowCache because it should be empty for a KeyValue in the * MemStore. */ @Override public long heapSize() { // Deep object overhead for this KV consists of two parts. The first part is the KV object // itself, while the second part is the backing byte[]. We will only count the array overhead // from the byte[] only if this is the first KV in there. int fixed = ClassSize.align(FIXED_OVERHEAD); if (offset == 0) { // count both length and object overhead return fixed + ClassSize.sizeOfByteArray(length); } else { // only count the number of bytes return (long) fixed + length; } } /** * A simple form of KeyValue that creates a keyvalue with only the key part of the byte[] Mainly * used in places where we need to compare two cells. Avoids copying of bytes In places like block * index keys, we need to compare the key byte[] with a cell. Hence create a Keyvalue(aka Cell) * that would help in comparing as two cells */ public static class KeyOnlyKeyValue extends KeyValue { private short rowLen = -1; public KeyOnlyKeyValue() { } public KeyOnlyKeyValue(byte[] b) { this(b, 0, b.length); } public KeyOnlyKeyValue(byte[] b, int offset, int length) { this.bytes = b; this.length = length; this.offset = offset; this.rowLen = Bytes.toShort(this.bytes, this.offset); } public void set(KeyOnlyKeyValue keyOnlyKeyValue) { this.bytes = keyOnlyKeyValue.bytes; this.length = keyOnlyKeyValue.length; this.offset = keyOnlyKeyValue.offset; this.rowLen = keyOnlyKeyValue.rowLen; } public void clear() { rowLen = -1; bytes = null; offset = 0; length = 0; } @Override public int getKeyOffset() { return this.offset; } /** * A setter that helps to avoid object creation every time and whenever there is a need to * create new KeyOnlyKeyValue. * @param key Key to set * @param offset Offset of the Key * @param length length of the Key */ public void setKey(byte[] key, int offset, int length) { this.bytes = key; this.offset = offset; this.length = length; this.rowLen = Bytes.toShort(this.bytes, this.offset); } @Override public byte[] getKey() { int keylength = getKeyLength(); byte[] key = new byte[keylength]; System.arraycopy(this.bytes, getKeyOffset(), key, 0, keylength); return key; } @Override public byte[] getRowArray() { return bytes; } @Override public int getRowOffset() { return getKeyOffset() + Bytes.SIZEOF_SHORT; } @Override public byte[] getFamilyArray() { return bytes; } @Override public byte getFamilyLength() { return this.bytes[getFamilyOffset() - 1]; } @Override int getFamilyLengthPosition(int rowLength) { return this.offset + Bytes.SIZEOF_SHORT + rowLength; } @Override public int getFamilyOffset() { return this.offset + Bytes.SIZEOF_SHORT + getRowLength() + Bytes.SIZEOF_BYTE; } @Override public byte[] getQualifierArray() { return bytes; } @Override public int getQualifierLength() { return getQualifierLength(getRowLength(), getFamilyLength()); } @Override public int getQualifierOffset() { return getFamilyOffset() + getFamilyLength(); } @Override public int getKeyLength() { return length; } @Override public short getRowLength() { return rowLen; } @Override public byte getTypeByte() { return getTypeByte(getKeyLength()); } @Override byte getTypeByte(int keyLength) { return this.bytes[this.offset + keyLength - 1]; } private int getQualifierLength(int rlength, int flength) { return getKeyLength() - (int) getKeyDataStructureSize(rlength, flength, 0); } @Override public long getTimestamp() { int tsOffset = getTimestampOffset(); return Bytes.toLong(this.bytes, tsOffset); } @Override public int getTimestampOffset() { return getKeyOffset() + getKeyLength() - TIMESTAMP_TYPE_SIZE; } @Override public byte[] getTagsArray() { return HConstants.EMPTY_BYTE_ARRAY; } @Override public int getTagsOffset() { return 0; } @Override public byte[] getValueArray() { throw new IllegalArgumentException("KeyOnlyKeyValue does not work with values."); } @Override public int getValueOffset() { throw new IllegalArgumentException("KeyOnlyKeyValue does not work with values."); } @Override public int getValueLength() { throw new IllegalArgumentException("KeyOnlyKeyValue does not work with values."); } @Override public int getTagsLength() { return 0; } @Override public String toString() { if (this.bytes == null || this.bytes.length == 0) { return "empty"; } return keyToString(this.bytes, this.offset, getKeyLength()) + "/vlen=0/mvcc=0"; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object other) { return super.equals(other); } @Override public long heapSize() { return super.heapSize() + Bytes.SIZEOF_SHORT; } @Override public int write(OutputStream out, boolean withTags) throws IOException { // This type of Cell is used only to maintain some internal states. We never allow this type // of Cell to be returned back over the RPC throw new IllegalStateException("A reader should never return this type of a Cell"); } } @Override public ExtendedCell deepClone() { byte[] copy = Bytes.copy(this.bytes, this.offset, this.length); KeyValue kv = new KeyValue(copy, 0, copy.length); kv.setSequenceId(this.getSequenceId()); return kv; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy