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.nio.ByteBuffer;
021import org.apache.hadoop.hbase.util.Bytes;
022import org.apache.yetus.audience.InterfaceAudience;
023import org.apache.yetus.audience.InterfaceStability;
024
025/**
026 * This is a {@link Tag} implementation in which value is backed by an on heap byte array.
027 */
028@InterfaceAudience.Private
029@InterfaceStability.Evolving
030public class ArrayBackedTag implements Tag {
031  private final byte type;// TODO extra type state needed?
032  private final byte[] bytes;
033  private int offset = 0;
034  private int length = 0;
035
036  /**
037   * The special tag will write the length of each tag and that will be followed by the type and
038   * then the actual tag. So every time the length part is parsed we need to add + 1 byte to it to
039   * get the type and then get the actual tag.
040   */
041  public ArrayBackedTag(byte tagType, String tag) {
042    this(tagType, Bytes.toBytes(tag));
043  }
044
045  /**
046   * Format for a tag : {@code <length of tag - 2 bytes><type code - 1 byte><tag>} tag length is
047   * serialized using 2 bytes only but as this will be unsigned, we can have max tag length of
048   * (Short.MAX_SIZE * 2) +1. It includes 1 byte type length and actual tag bytes length.
049   */
050  public ArrayBackedTag(byte tagType, byte[] tag) {
051    int tagLength = tag.length + TYPE_LENGTH_SIZE;
052    if (tagLength > MAX_TAG_LENGTH) {
053      throw new IllegalArgumentException(
054        "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
055    }
056    length = TAG_LENGTH_SIZE + tagLength;
057    bytes = new byte[length];
058    int pos = Bytes.putAsShort(bytes, 0, tagLength);
059    pos = Bytes.putByte(bytes, pos, tagType);
060    Bytes.putBytes(bytes, pos, tag, 0, tag.length);
061    this.type = tagType;
062  }
063
064  /**
065   * Creates a Tag from the specified byte array and offset. Presumes <code>bytes</code> content
066   * starting at <code>offset</code> is formatted as a Tag blob. The bytes to include the tag type,
067   * tag length and actual tag bytes.
068   * @param offset offset to start of Tag
069   */
070  public ArrayBackedTag(byte[] bytes, int offset) {
071    this(bytes, offset, getLength(bytes, offset));
072  }
073
074  private static int getLength(byte[] bytes, int offset) {
075    return TAG_LENGTH_SIZE + Bytes.readAsInt(bytes, offset, TAG_LENGTH_SIZE);
076  }
077
078  /**
079   * Creates a Tag from the specified byte array, starting at offset, and for length
080   * <code>length</code>. Presumes <code>bytes</code> content starting at <code>offset</code> is
081   * formatted as a Tag blob.
082   */
083  public ArrayBackedTag(byte[] bytes, int offset, int length) {
084    if (length > MAX_TAG_LENGTH) {
085      throw new IllegalArgumentException(
086        "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
087    }
088    this.bytes = bytes;
089    this.offset = offset;
090    this.length = length;
091    this.type = bytes[offset + TAG_LENGTH_SIZE];
092  }
093
094  /** Returns The byte array backing this Tag. */
095  @Override
096  public byte[] getValueArray() {
097    return this.bytes;
098  }
099
100  /** Returns the tag type */
101  @Override
102  public byte getType() {
103    return this.type;
104  }
105
106  /** Returns Length of actual tag bytes within the backed buffer */
107  @Override
108  public int getValueLength() {
109    return this.length - INFRASTRUCTURE_SIZE;
110  }
111
112  /** Returns Offset of actual tag bytes within the backed buffer */
113  @Override
114  public int getValueOffset() {
115    return this.offset + INFRASTRUCTURE_SIZE;
116  }
117
118  @Override
119  public boolean hasArray() {
120    return true;
121  }
122
123  @Override
124  public ByteBuffer getValueByteBuffer() {
125    return ByteBuffer.wrap(bytes);
126  }
127
128  @Override
129  public String toString() {
130    return "[Tag type : " + this.type + ", value : "
131      + Bytes.toStringBinary(bytes, getValueOffset(), getValueLength()) + "]";
132  }
133}