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