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 org.apache.commons.lang3.ArrayUtils;
021import org.apache.hadoop.hbase.util.Bytes;
022import org.apache.hadoop.hbase.util.ClassSize;
023import org.apache.yetus.audience.InterfaceAudience;
024
025@InterfaceAudience.Private
026public class IndividualBytesFieldCell implements ExtendedCell, Cloneable {
027  // do alignment(padding gap)
028  private static final long FIXED_OVERHEAD = ClassSize.align(ClassSize.OBJECT // object header
029    // timestamp and type
030    + KeyValue.TIMESTAMP_TYPE_SIZE
031    // sequence id
032    + Bytes.SIZEOF_LONG
033    // references to all byte arrays: row, family, qualifier, value, tags
034    + 5 * ClassSize.REFERENCE);
035
036  // The following fields are backed by individual byte arrays
037  private final byte[] row;
038  private final int rOffset;
039  private final int rLength;
040  private final byte[] family;
041  private final int fOffset;
042  private final int fLength;
043  private final byte[] qualifier;
044  private final int qOffset;
045  private final int qLength;
046  private final byte[] value;
047  private final int vOffset;
048  private final int vLength;
049  private final byte[] tags; // A byte array, rather than an array of org.apache.hadoop.hbase.Tag
050  private final int tagsOffset;
051  private final int tagsLength;
052
053  // Other fields
054  private long timestamp;
055  private final byte type; // A byte, rather than org.apache.hadoop.hbase.KeyValue.Type
056  private long seqId;
057
058  public IndividualBytesFieldCell(byte[] row, byte[] family, byte[] qualifier, long timestamp,
059    KeyValue.Type type, byte[] value) {
060    this(row, family, qualifier, timestamp, type, 0L /* sequence id */, value, null /* tags */);
061  }
062
063  public IndividualBytesFieldCell(byte[] row, byte[] family, byte[] qualifier, long timestamp,
064    KeyValue.Type type, long seqId, byte[] value, byte[] tags) {
065    this(row, 0, ArrayUtils.getLength(row), family, 0, ArrayUtils.getLength(family), qualifier, 0,
066      ArrayUtils.getLength(qualifier), timestamp, type, seqId, value, 0,
067      ArrayUtils.getLength(value), tags, 0, ArrayUtils.getLength(tags));
068  }
069
070  public IndividualBytesFieldCell(byte[] row, int rOffset, int rLength, byte[] family, int fOffset,
071    int fLength, byte[] qualifier, int qOffset, int qLength, long timestamp, KeyValue.Type type,
072    long seqId, byte[] value, int vOffset, int vLength, byte[] tags, int tagsOffset,
073    int tagsLength) {
074    // Check row, family, qualifier and value
075    KeyValue.checkParameters(row, rLength, // row and row length
076      family, fLength, // family and family length
077      qLength, // qualifier length
078      vLength); // value length
079
080    // Check timestamp
081    if (timestamp < 0) {
082      throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
083    }
084
085    // Check tags
086    RawCell.checkForTagsLength(tagsLength);
087    checkArrayBounds(row, rOffset, rLength);
088    checkArrayBounds(family, fOffset, fLength);
089    checkArrayBounds(qualifier, qOffset, qLength);
090    checkArrayBounds(value, vOffset, vLength);
091    checkArrayBounds(tags, tagsOffset, tagsLength);
092    // No local copy is made, but reference to the input directly
093    this.row = row;
094    this.rOffset = rOffset;
095    this.rLength = rLength;
096    this.family = family;
097    this.fOffset = fOffset;
098    this.fLength = fLength;
099    this.qualifier = qualifier;
100    this.qOffset = qOffset;
101    this.qLength = qLength;
102    this.value = value;
103    this.vOffset = vOffset;
104    this.vLength = vLength;
105    this.tags = tags;
106    this.tagsOffset = tagsOffset;
107    this.tagsLength = tagsLength;
108
109    // Set others
110    this.timestamp = timestamp;
111    this.type = type.getCode();
112    this.seqId = seqId;
113  }
114
115  private void checkArrayBounds(byte[] bytes, int offset, int length) {
116    if (offset < 0 || length < 0) {
117      throw new IllegalArgumentException(
118        "Negative number! offset=" + offset + "and length=" + length);
119    }
120    if (bytes == null && (offset != 0 || length != 0)) {
121      throw new IllegalArgumentException(
122        "Null bytes array but offset=" + offset + "and length=" + length);
123    }
124    if (bytes != null && bytes.length < offset + length) {
125      throw new IllegalArgumentException("Out of bounds! bytes.length=" + bytes.length + ", offset="
126        + offset + ", length=" + length);
127    }
128  }
129
130  private long heapOverhead() {
131    return FIXED_OVERHEAD + ClassSize.ARRAY // row , can not be null
132      + ((family == null) ? 0 : ClassSize.ARRAY) // family , can be null
133      + ((qualifier == null) ? 0 : ClassSize.ARRAY) // qualifier, can be null
134      + ((value == null) ? 0 : ClassSize.ARRAY) // value , can be null
135      + ((tags == null) ? 0 : ClassSize.ARRAY); // tags , can be null
136  }
137
138  /**
139   * Implement Cell interface
140   */
141  // 1) Row
142  @Override
143  public byte[] getRowArray() {
144    // If row is null, the constructor will reject it, by {@link KeyValue#checkParameters()},
145    // so it is safe to return row without checking.
146    return row;
147  }
148
149  @Override
150  public int getRowOffset() {
151    return rOffset;
152  }
153
154  @Override
155  public short getRowLength() {
156    // If row is null or rLength is invalid, the constructor will reject it, by
157    // {@link KeyValue#checkParameters()}, so it is safe to call rLength and make the type
158    // conversion.
159    return (short) rLength;
160  }
161
162  // 2) Family
163  @Override
164  public byte[] getFamilyArray() {
165    // Family could be null
166    return (family == null) ? HConstants.EMPTY_BYTE_ARRAY : family;
167  }
168
169  @Override
170  public int getFamilyOffset() {
171    return fOffset;
172  }
173
174  @Override
175  public byte getFamilyLength() {
176    // If fLength is invalid, the constructor will reject it, by {@link KeyValue#checkParameters()},
177    // so it is safe to make the type conversion.
178    return (byte) fLength;
179  }
180
181  // 3) Qualifier
182  @Override
183  public byte[] getQualifierArray() {
184    // Qualifier could be null
185    return (qualifier == null) ? HConstants.EMPTY_BYTE_ARRAY : qualifier;
186  }
187
188  @Override
189  public int getQualifierOffset() {
190    return qOffset;
191  }
192
193  @Override
194  public int getQualifierLength() {
195    return qLength;
196  }
197
198  // 4) Timestamp
199  @Override
200  public long getTimestamp() {
201    return timestamp;
202  }
203
204  // 5) Type
205  @Override
206  public byte getTypeByte() {
207    return type;
208  }
209
210  // 6) Sequence id
211  @Override
212  public long getSequenceId() {
213    return seqId;
214  }
215
216  // 7) Value
217  @Override
218  public byte[] getValueArray() {
219    // Value could be null
220    return (value == null) ? HConstants.EMPTY_BYTE_ARRAY : value;
221  }
222
223  @Override
224  public int getValueOffset() {
225    return vOffset;
226  }
227
228  @Override
229  public int getValueLength() {
230    return vLength;
231  }
232
233  // 8) Tags
234  @Override
235  public byte[] getTagsArray() {
236    // Tags can could null
237    return (tags == null) ? HConstants.EMPTY_BYTE_ARRAY : tags;
238  }
239
240  @Override
241  public int getTagsOffset() {
242    return tagsOffset;
243  }
244
245  @Override
246  public int getTagsLength() {
247    return tagsLength;
248  }
249
250  /**
251   * Implement HeapSize interface
252   */
253  @Override
254  public long heapSize() {
255    // Size of array headers are already included into overhead, so do not need to include it for
256    // each byte array
257    return heapOverhead() // overhead, with array headers included
258      + ClassSize.align(getRowLength()) // row
259      + ClassSize.align(getFamilyLength()) // family
260      + ClassSize.align(getQualifierLength()) // qualifier
261      + ClassSize.align(getValueLength()) // value
262      + ClassSize.align(getTagsLength()); // tags
263  }
264
265  /**
266   * Implement Cloneable interface
267   */
268  @Override
269  public Object clone() throws CloneNotSupportedException {
270    return super.clone(); // only a shadow copy
271  }
272
273  @Override
274  public void setSequenceId(long seqId) {
275    if (seqId < 0) {
276      throw new IllegalArgumentException("Sequence Id cannot be negative. ts=" + seqId);
277    }
278    this.seqId = seqId;
279  }
280
281  @Override
282  public void setTimestamp(long ts) {
283    if (ts < 0) {
284      throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
285    }
286    this.timestamp = ts;
287  }
288
289  @Override
290  public void setTimestamp(byte[] ts) {
291    setTimestamp(Bytes.toLong(ts, 0));
292  }
293
294  @Override
295  public String toString() {
296    return CellUtil.toString(this, false);
297  }
298}