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  /**
095   * @return The byte array backing this Tag.
096   */
097  @Override
098  public byte[] getValueArray() {
099    return this.bytes;
100  }
101
102  /**
103   * @return the tag type
104   */
105  @Override
106  public byte getType() {
107    return this.type;
108  }
109
110  /**
111   * @return Length of actual tag bytes within the backed buffer
112   */
113  @Override
114  public int getValueLength() {
115    return this.length - INFRASTRUCTURE_SIZE;
116  }
117
118  /**
119   * @return Offset of actual tag bytes within the backed buffer
120   */
121  @Override
122  public int getValueOffset() {
123    return this.offset + INFRASTRUCTURE_SIZE;
124  }
125
126  @Override
127  public boolean hasArray() {
128    return true;
129  }
130
131  @Override
132  public ByteBuffer getValueByteBuffer() {
133    return ByteBuffer.wrap(bytes);
134  }
135
136  @Override
137  public String toString() {
138    return "[Tag type : " + this.type + ", value : "
139      + Bytes.toStringBinary(bytes, getValueOffset(), getValueLength()) + "]";
140  }
141}