View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.hadoop.classification.InterfaceAudience;
26  import org.apache.hadoop.classification.InterfaceStability;
27  import org.apache.hadoop.hbase.util.Bytes;
28  /**
29   * Tags are part of cells and helps to add metadata about the KVs.
30   * Metadata could be ACLs per cells, visibility labels, etc.
31   */
32  @InterfaceAudience.Private
33  @InterfaceStability.Evolving
34  public class Tag {
35    public final static int TYPE_LENGTH_SIZE = Bytes.SIZEOF_BYTE;
36    public final static int TAG_LENGTH_SIZE = Bytes.SIZEOF_SHORT;
37    public final static int INFRASTRUCTURE_SIZE = TYPE_LENGTH_SIZE + TAG_LENGTH_SIZE;
38    private static final int MAX_TAG_LENGTH = (2 * Short.MAX_VALUE) + 1 - TAG_LENGTH_SIZE;
39  
40    private final byte type;
41    private final byte[] bytes;
42    private int offset = 0;
43    private int length = 0;
44  
45    // The special tag will write the length of each tag and that will be
46    // followed by the type and then the actual tag.
47    // So every time the length part is parsed we need to add + 1 byte to it to
48    // get the type and then get the actual tag.
49    public Tag(byte tagType, String tag) {
50      this(tagType, Bytes.toBytes(tag));
51    }
52  
53    /**
54     * @param tagType
55     * @param tag
56     */
57    public Tag(byte tagType, byte[] tag) {
58      /**
59       * Format for a tag : <length of tag - 2 bytes><type code - 1 byte><tag> taglength is serialized
60       * using 2 bytes only but as this will be unsigned, we can have max taglength of
61       * (Short.MAX_SIZE * 2) +1. It includes 1 byte type length and actual tag bytes length.
62       */
63      int tagLength = tag.length + TYPE_LENGTH_SIZE;
64      if (tagLength > MAX_TAG_LENGTH) {
65        throw new IllegalArgumentException(
66            "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
67      }
68      length = TAG_LENGTH_SIZE + tagLength;
69      bytes = new byte[length];
70      int pos = Bytes.putAsShort(bytes, 0, tagLength);
71      pos = Bytes.putByte(bytes, pos, tagType);
72      Bytes.putBytes(bytes, pos, tag, 0, tag.length);
73      this.type = tagType;
74    }
75  
76    /**
77     * Creates a Tag from the specified byte array and offset. Presumes
78     * <code>bytes</code> content starting at <code>offset</code> is formatted as
79     * a Tag blob.
80     * The bytes to include the tag type, tag length and actual tag bytes.
81     * @param bytes
82     *          byte array
83     * @param offset
84     *          offset to start of Tag
85     */
86    public Tag(byte[] bytes, int offset) {
87      this(bytes, offset, getLength(bytes, offset));
88    }
89  
90    private static int getLength(byte[] bytes, int offset) {
91      return TAG_LENGTH_SIZE + Bytes.readAsInt(bytes, offset, TAG_LENGTH_SIZE);
92    }
93  
94    /**
95     * Creates a Tag from the specified byte array, starting at offset, and for length
96     * <code>length</code>. Presumes <code>bytes</code> content starting at <code>offset</code> is
97     * formatted as a Tag blob.
98     * @param bytes
99     *          byte array
100    * @param offset
101    *          offset to start of the Tag
102    * @param length
103    *          length of the Tag
104    */
105   public Tag(byte[] bytes, int offset, int length) {
106     if (length > MAX_TAG_LENGTH) {
107       throw new IllegalArgumentException(
108           "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
109     }
110     this.bytes = bytes;
111     this.offset = offset;
112     this.length = length;
113     this.type = bytes[offset + TAG_LENGTH_SIZE];
114   }
115 
116   /**
117    * @return The byte array backing this Tag.
118    */
119   public byte[] getBuffer() {
120     return this.bytes;
121   }
122 
123   /**
124    * @return the tag type
125    */
126   public byte getType() {
127     return this.type;
128   }
129 
130   /**
131    * @return Length of actual tag bytes within the backed buffer
132    */
133   public int getTagLength() {
134     return this.length - INFRASTRUCTURE_SIZE;
135   }
136 
137   /**
138    * @return Offset of actual tag bytes within the backed buffer
139    */
140   public int getTagOffset() {
141     return this.offset + INFRASTRUCTURE_SIZE;
142   }
143 
144   /**
145    * Returns tag value in a new byte array.
146    * Primarily for use client-side. If server-side, use
147    * {@link #getBuffer()} with appropriate {@link #getTagOffset()} and {@link #getTagLength()}
148    * instead to save on allocations.
149    * @return tag value in a new byte array.
150    */
151   public byte[] getValue() {
152     int tagLength = getTagLength();
153     byte[] tag = new byte[tagLength];
154     Bytes.putBytes(tag, 0, bytes, getTagOffset(), tagLength);
155     return tag;
156   }
157 
158   /**
159    * Creates the list of tags from the byte array b. Expected that b is in the
160    * expected tag format
161    * @param b
162    * @param offset
163    * @param length
164    * @return List of tags
165    */
166   public static List<Tag> asList(byte[] b, int offset, int length) {
167     List<Tag> tags = new ArrayList<Tag>();
168     int pos = offset;
169     while (pos < offset + length) {
170       int tagLen = Bytes.readAsInt(b, pos, TAG_LENGTH_SIZE);
171       tags.add(new Tag(b, pos, tagLen + TAG_LENGTH_SIZE));
172       pos += TAG_LENGTH_SIZE + tagLen;
173     }
174     return tags;
175   }
176   
177   /**
178    * Retrieve the first tag from the tags byte array matching the passed in tag type
179    * @param b
180    * @param offset
181    * @param length
182    * @param type
183    * @return null if there is no tag of the passed in tag type
184    */
185   public static Tag getTag(byte[] b, int offset, int length, byte type) {
186     int pos = offset;
187     while (pos < offset + length) {
188       int tagLen = Bytes.readAsInt(b, pos, TAG_LENGTH_SIZE);
189       if(b[pos + TAG_LENGTH_SIZE] == type) {
190         return new Tag(b, pos, tagLen + TAG_LENGTH_SIZE);
191       }
192       pos += TAG_LENGTH_SIZE + tagLen;
193     }
194     return null;
195   }
196 
197   /**
198    * Returns the total length of the entire tag entity
199    */
200   int getLength() {
201     return this.length;
202   }
203 
204   /**
205    * Returns the offset of the entire tag entity
206    */
207   int getOffset() {
208     return this.offset;
209   }
210 }