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 java.io.IOException;
021import java.io.OutputStream;
022import java.nio.ByteBuffer;
023import org.apache.hadoop.hbase.util.ByteBufferUtils;
024import org.apache.hadoop.hbase.util.Bytes;
025import org.apache.hadoop.hbase.util.ClassSize;
026import org.apache.yetus.audience.InterfaceAudience;
027
028/**
029 * This Cell is an implementation of {@link ByteBufferExtendedCell} where the data resides in off
030 * heap/ on heap ByteBuffer
031 */
032@InterfaceAudience.Private
033public class ByteBufferKeyValue extends ByteBufferExtendedCell {
034
035  protected final ByteBuffer buf;
036  protected final int offset;
037  protected final int length;
038  private long seqId = 0;
039
040  public static final int FIXED_OVERHEAD =
041    ClassSize.OBJECT + ClassSize.REFERENCE + (2 * Bytes.SIZEOF_INT) + Bytes.SIZEOF_LONG;
042
043  public ByteBufferKeyValue(ByteBuffer buf, int offset, int length, long seqId) {
044    this.buf = buf;
045    this.offset = offset;
046    this.length = length;
047    this.seqId = seqId;
048  }
049
050  public ByteBufferKeyValue(ByteBuffer buf, int offset, int length) {
051    this.buf = buf;
052    this.offset = offset;
053    this.length = length;
054  }
055
056  public ByteBuffer getBuffer() {
057    return this.buf;
058  }
059
060  public int getOffset() {
061    return this.offset;
062  }
063
064  @Override
065  public byte[] getRowArray() {
066    return CellUtil.cloneRow(this);
067  }
068
069  @Override
070  public int getRowOffset() {
071    return 0;
072  }
073
074  @Override
075  public short getRowLength() {
076    return ByteBufferUtils.toShort(this.buf, this.offset + KeyValue.ROW_OFFSET);
077  }
078
079  @Override
080  public byte[] getFamilyArray() {
081    return CellUtil.cloneFamily(this);
082  }
083
084  @Override
085  public int getFamilyOffset() {
086    return 0;
087  }
088
089  @Override
090  public byte getFamilyLength() {
091    return getFamilyLength(getFamilyLengthPosition());
092  }
093
094  int getFamilyLengthPosition() {
095    return getFamilyLengthPosition(getRowLength());
096  }
097
098  int getFamilyLengthPosition(int rowLength) {
099    return this.offset + KeyValue.ROW_KEY_OFFSET + rowLength;
100  }
101
102  byte getFamilyLength(int famLenPos) {
103    return ByteBufferUtils.toByte(this.buf, famLenPos);
104  }
105
106  @Override
107  public byte[] getQualifierArray() {
108    return CellUtil.cloneQualifier(this);
109  }
110
111  @Override
112  public int getQualifierOffset() {
113    return 0;
114  }
115
116  @Override
117  public int getQualifierLength() {
118    return getQualifierLength(getKeyLength(), getRowLength(), getFamilyLength());
119  }
120
121  int getQualifierLength(int keyLength, int rlength, int flength) {
122    return keyLength - (int) KeyValue.getKeyDataStructureSize(rlength, flength, 0);
123  }
124
125  @Override
126  public long getTimestamp() {
127    return getTimestamp(getKeyLength());
128  }
129
130  long getTimestamp(int keyLength) {
131    int offset = getTimestampOffset(keyLength);
132    return ByteBufferUtils.toLong(this.buf, offset);
133  }
134
135  int getKeyLength() {
136    return ByteBufferUtils.toInt(this.buf, this.offset);
137  }
138
139  private int getTimestampOffset(int keyLen) {
140    return this.offset + KeyValue.ROW_OFFSET + keyLen - KeyValue.TIMESTAMP_TYPE_SIZE;
141  }
142
143  @Override
144  public byte getTypeByte() {
145    return getTypeByte(getKeyLength());
146  }
147
148  byte getTypeByte(int keyLen) {
149    return ByteBufferUtils.toByte(this.buf, this.offset + keyLen - 1 + KeyValue.ROW_OFFSET);
150  }
151
152  @Override
153  public long getSequenceId() {
154    return this.seqId;
155  }
156
157  @Override
158  public void setSequenceId(long seqId) {
159    this.seqId = seqId;
160  }
161
162  @Override
163  public byte[] getValueArray() {
164    return CellUtil.cloneValue(this);
165  }
166
167  @Override
168  public int getValueOffset() {
169    return 0;
170  }
171
172  @Override
173  public int getValueLength() {
174    return ByteBufferUtils.toInt(this.buf, this.offset + Bytes.SIZEOF_INT);
175  }
176
177  @Override
178  public byte[] getTagsArray() {
179    return PrivateCellUtil.cloneTags(this);
180  }
181
182  @Override
183  public int getTagsOffset() {
184    return 0;
185  }
186
187  @Override
188  public int getTagsLength() {
189    int tagsLen =
190      this.length - (getKeyLength() + getValueLength() + KeyValue.KEYVALUE_INFRASTRUCTURE_SIZE);
191    if (tagsLen > 0) {
192      // There are some Tag bytes in the byte[]. So reduce 2 bytes which is
193      // added to denote the tags
194      // length
195      tagsLen -= KeyValue.TAGS_LENGTH_SIZE;
196    }
197    return tagsLen;
198  }
199
200  @Override
201  public ByteBuffer getRowByteBuffer() {
202    return this.buf;
203  }
204
205  @Override
206  public int getRowPosition() {
207    return this.offset + KeyValue.ROW_KEY_OFFSET;
208  }
209
210  @Override
211  public ByteBuffer getFamilyByteBuffer() {
212    return this.buf;
213  }
214
215  @Override
216  public int getFamilyPosition() {
217    return getFamilyPosition(getFamilyLengthPosition());
218  }
219
220  public int getFamilyPosition(int familyLengthPosition) {
221    return familyLengthPosition + Bytes.SIZEOF_BYTE;
222  }
223
224  @Override
225  public ByteBuffer getQualifierByteBuffer() {
226    return this.buf;
227  }
228
229  @Override
230  public int getQualifierPosition() {
231    return getQualifierPosition(getFamilyPosition(), getFamilyLength());
232  }
233
234  int getQualifierPosition(int familyPosition, int familyLength) {
235    return familyPosition + familyLength;
236  }
237
238  @Override
239  public ByteBuffer getValueByteBuffer() {
240    return this.buf;
241  }
242
243  @Override
244  public int getValuePosition() {
245    return this.offset + KeyValue.ROW_OFFSET + getKeyLength();
246  }
247
248  @Override
249  public ByteBuffer getTagsByteBuffer() {
250    return this.buf;
251  }
252
253  @Override
254  public int getTagsPosition() {
255    int tagsLen = getTagsLength();
256    if (tagsLen == 0) {
257      return this.offset + this.length;
258    }
259    return this.offset + this.length - tagsLen;
260  }
261
262  @Override
263  public long heapSize() {
264    if (this.buf.hasArray()) {
265      return ClassSize.align(FIXED_OVERHEAD + length);
266    }
267    return (long) ClassSize.align(FIXED_OVERHEAD) + this.getSerializedSize();
268  }
269
270  @Override
271  public int write(OutputStream out, boolean withTags) throws IOException {
272    int length = getSerializedSize(withTags);
273    ByteBufferUtils.copyBufferToStream(out, this.buf, this.offset, length);
274    return length;
275  }
276
277  @Override
278  public int getSerializedSize(boolean withTags) {
279    if (withTags) {
280      return this.length;
281    }
282    return getKeyLength() + this.getValueLength() + KeyValue.KEYVALUE_INFRASTRUCTURE_SIZE;
283  }
284
285  @Override
286  public int getSerializedSize() {
287    return this.length;
288  }
289
290  @Override
291  public void write(ByteBuffer buf, int offset) {
292    ByteBufferUtils.copyFromBufferToBuffer(this.buf, buf, this.offset, offset, this.length);
293  }
294
295  @Override
296  public String toString() {
297    return CellUtil.toString(this, false);
298  }
299
300  @Override
301  public void setTimestamp(long ts) throws IOException {
302    ByteBufferUtils.copyFromArrayToBuffer(this.buf, this.getTimestampOffset(), Bytes.toBytes(ts), 0,
303      Bytes.SIZEOF_LONG);
304  }
305
306  private int getTimestampOffset() {
307    return this.offset + KeyValue.KEYVALUE_INFRASTRUCTURE_SIZE + getKeyLength()
308      - KeyValue.TIMESTAMP_TYPE_SIZE;
309  }
310
311  @Override
312  public void setTimestamp(byte[] ts) throws IOException {
313    ByteBufferUtils.copyFromArrayToBuffer(this.buf, this.getTimestampOffset(), ts, 0,
314      Bytes.SIZEOF_LONG);
315  }
316
317  @Override
318  public ExtendedCell deepClone() {
319    byte[] copy = new byte[this.length];
320    ByteBufferUtils.copyFromBufferToArray(copy, this.buf, this.offset, 0, this.length);
321    KeyValue kv = new KeyValue(copy, 0, copy.length);
322    kv.setSequenceId(this.getSequenceId());
323    return kv;
324  }
325
326  /**
327   * Needed doing 'contains' on List. Only compares the key portion, not the value.
328   */
329  @Override
330  public boolean equals(Object other) {
331    if (!(other instanceof Cell)) {
332      return false;
333    }
334    return CellUtil.equals(this, (Cell) other);
335  }
336
337  /**
338   * In line with {@link #equals(Object)}, only uses the key portion, not the value.
339   */
340  @Override
341  public int hashCode() {
342    return calculateHashForKey(this);
343  }
344
345  private int calculateHashForKey(ByteBufferExtendedCell cell) {
346    int rowHash =
347      ByteBufferUtils.hashCode(cell.getRowByteBuffer(), cell.getRowPosition(), cell.getRowLength());
348    int familyHash = ByteBufferUtils.hashCode(cell.getFamilyByteBuffer(), cell.getFamilyPosition(),
349      cell.getFamilyLength());
350    int qualifierHash = ByteBufferUtils.hashCode(cell.getQualifierByteBuffer(),
351      cell.getQualifierPosition(), cell.getQualifierLength());
352
353    int hash = 31 * rowHash + familyHash;
354    hash = 31 * hash + qualifierHash;
355    hash = 31 * hash + (int) cell.getTimestamp();
356    hash = 31 * hash + cell.getTypeByte();
357    return hash;
358  }
359}