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