001/**
002 * Copyright The Apache Software Foundation
003 *
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *     http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020package org.apache.hadoop.hbase;
021
022import java.nio.ByteBuffer;
023
024import org.apache.hadoop.hbase.util.Bytes;
025import org.apache.yetus.audience.InterfaceAudience;
026import org.apache.yetus.audience.InterfaceStability;
027
028/**
029 * This is a {@link Tag} implementation in which value is backed by an on heap byte array.
030 */
031@InterfaceAudience.Private
032@InterfaceStability.Evolving
033public class ArrayBackedTag implements Tag {
034  private final byte type;// TODO  extra type state needed?
035  private final byte[] bytes;
036  private int offset = 0;
037  private int length = 0;
038
039  /**
040   * The special tag will write the length of each tag and that will be
041   * followed by the type and then the actual tag.
042   * So every time the length part is parsed we need to add + 1 byte to it to
043   * get the type and then get the actual tag.
044   */
045  public ArrayBackedTag(byte tagType, String tag) {
046    this(tagType, Bytes.toBytes(tag));
047  }
048
049  /**
050   * Format for a tag :
051   * {@code <length of tag - 2 bytes><type code - 1 byte><tag>} tag length is serialized
052   * using 2 bytes only but as this will be unsigned, we can have max tag length of
053   * (Short.MAX_SIZE * 2) +1. It includes 1 byte type length and actual tag bytes length.
054   */
055  public ArrayBackedTag(byte tagType, byte[] tag) {
056    int tagLength = tag.length + TYPE_LENGTH_SIZE;
057    if (tagLength > MAX_TAG_LENGTH) {
058      throw new IllegalArgumentException(
059          "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
060    }
061    length = TAG_LENGTH_SIZE + tagLength;
062    bytes = new byte[length];
063    int pos = Bytes.putAsShort(bytes, 0, tagLength);
064    pos = Bytes.putByte(bytes, pos, tagType);
065    Bytes.putBytes(bytes, pos, tag, 0, tag.length);
066    this.type = tagType;
067  }
068
069  /**
070   * Creates a Tag from the specified byte array and offset. Presumes
071   * <code>bytes</code> content starting at <code>offset</code> is formatted as
072   * a Tag blob.
073   * The bytes to include the tag type, tag length and actual tag bytes.
074   * @param offset offset to start of Tag
075   */
076  public ArrayBackedTag(byte[] bytes, int offset) {
077    this(bytes, offset, getLength(bytes, offset));
078  }
079
080  private static int getLength(byte[] bytes, int offset) {
081    return TAG_LENGTH_SIZE + Bytes.readAsInt(bytes, offset, TAG_LENGTH_SIZE);
082  }
083
084  /**
085   * Creates a Tag from the specified byte array, starting at offset, and for length
086   * <code>length</code>. Presumes <code>bytes</code> content starting at <code>offset</code> is
087   * formatted as a Tag blob.
088   */
089  public ArrayBackedTag(byte[] bytes, int offset, int length) {
090    if (length > MAX_TAG_LENGTH) {
091      throw new IllegalArgumentException(
092          "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
093    }
094    this.bytes = bytes;
095    this.offset = offset;
096    this.length = length;
097    this.type = bytes[offset + TAG_LENGTH_SIZE];
098  }
099
100  /**
101   * @return The byte array backing this Tag.
102   */
103  @Override
104  public byte[] getValueArray() {
105    return this.bytes;
106  }
107
108  /**
109   * @return the tag type
110   */
111  @Override
112  public byte getType() {
113    return this.type;
114  }
115
116  /**
117   * @return Length of actual tag bytes within the backed buffer
118   */
119  @Override
120  public int getValueLength() {
121    return this.length - INFRASTRUCTURE_SIZE;
122  }
123
124  /**
125   * @return Offset of actual tag bytes within the backed buffer
126   */
127  @Override
128  public int getValueOffset() {
129    return this.offset + INFRASTRUCTURE_SIZE;
130  }
131
132  @Override
133  public boolean hasArray() {
134    return true;
135  }
136
137  @Override
138  public ByteBuffer getValueByteBuffer() {
139    return ByteBuffer.wrap(bytes);
140  }
141
142  @Override
143  public String toString() {
144    return "[Tag type : " + this.type + ", value : "
145        + Bytes.toStringBinary(bytes, getValueOffset(), getValueLength()) + "]";
146  }
147}