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.Iterator;
24  import java.util.List;
25  
26  import org.apache.hadoop.hbase.classification.InterfaceAudience;
27  import org.apache.hadoop.hbase.classification.InterfaceStability;
28  import org.apache.hadoop.hbase.util.Bytes;
29  /**
30   * Tags are part of cells and helps to add metadata about the KVs.
31   * Metadata could be ACLs per cells, visibility labels, etc.
32   */
33  @InterfaceAudience.Private
34  @InterfaceStability.Evolving
35  public class Tag {
36    public final static int TYPE_LENGTH_SIZE = Bytes.SIZEOF_BYTE;
37    public final static int TAG_LENGTH_SIZE = Bytes.SIZEOF_SHORT;
38    public final static int INFRASTRUCTURE_SIZE = TYPE_LENGTH_SIZE + TAG_LENGTH_SIZE;
39    public static final int MAX_TAG_LENGTH = (2 * Short.MAX_VALUE) + 1 - TAG_LENGTH_SIZE;
40  
41    private final byte type;
42    private final byte[] bytes;
43    private int offset = 0;
44    private int length = 0;
45  
46    // The special tag will write the length of each tag and that will be
47    // followed by the type and then the actual tag.
48    // So every time the length part is parsed we need to add + 1 byte to it to
49    // get the type and then get the actual tag.
50    public Tag(byte tagType, String tag) {
51      this(tagType, Bytes.toBytes(tag));
52    }
53  
54    /**
55     * @param tagType
56     * @param tag
57     */
58    public Tag(byte tagType, byte[] tag) {
59      /**
60       * Format for a tag : <length of tag - 2 bytes><type code - 1 byte><tag> taglength is serialized
61       * using 2 bytes only but as this will be unsigned, we can have max taglength of
62       * (Short.MAX_SIZE * 2) +1. It includes 1 byte type length and actual tag bytes length.
63       */
64      int tagLength = tag.length + TYPE_LENGTH_SIZE;
65      if (tagLength > MAX_TAG_LENGTH) {
66        throw new IllegalArgumentException(
67            "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
68      }
69      length = TAG_LENGTH_SIZE + tagLength;
70      bytes = new byte[length];
71      int pos = Bytes.putAsShort(bytes, 0, tagLength);
72      pos = Bytes.putByte(bytes, pos, tagType);
73      Bytes.putBytes(bytes, pos, tag, 0, tag.length);
74      this.type = tagType;
75    }
76  
77    /**
78     * Creates a Tag from the specified byte array and offset. Presumes
79     * <code>bytes</code> content starting at <code>offset</code> is formatted as
80     * a Tag blob.
81     * The bytes to include the tag type, tag length and actual tag bytes.
82     * @param bytes
83     *          byte array
84     * @param offset
85     *          offset to start of Tag
86     */
87    public Tag(byte[] bytes, int offset) {
88      this(bytes, offset, getLength(bytes, offset));
89    }
90  
91    private static int getLength(byte[] bytes, int offset) {
92      return TAG_LENGTH_SIZE + Bytes.readAsInt(bytes, offset, TAG_LENGTH_SIZE);
93    }
94  
95    /**
96     * Creates a Tag from the specified byte array, starting at offset, and for length
97     * <code>length</code>. Presumes <code>bytes</code> content starting at <code>offset</code> is
98     * formatted as a Tag blob.
99     * @param bytes
100    *          byte array
101    * @param offset
102    *          offset to start of the Tag
103    * @param length
104    *          length of the Tag
105    */
106   public Tag(byte[] bytes, int offset, int length) {
107     if (length > MAX_TAG_LENGTH) {
108       throw new IllegalArgumentException(
109           "Invalid tag data being passed. Its length can not exceed " + MAX_TAG_LENGTH);
110     }
111     this.bytes = bytes;
112     this.offset = offset;
113     this.length = length;
114     this.type = bytes[offset + TAG_LENGTH_SIZE];
115   }
116 
117   /**
118    * @return The byte array backing this Tag.
119    */
120   public byte[] getBuffer() {
121     return this.bytes;
122   }
123 
124   /**
125    * @return the tag type
126    */
127   public byte getType() {
128     return this.type;
129   }
130 
131   /**
132    * @return Length of actual tag bytes within the backed buffer
133    */
134   public int getTagLength() {
135     return this.length - INFRASTRUCTURE_SIZE;
136   }
137 
138   /**
139    * @return Offset of actual tag bytes within the backed buffer
140    */
141   public int getTagOffset() {
142     return this.offset + INFRASTRUCTURE_SIZE;
143   }
144 
145   /**
146    * Returns tag value in a new byte array.
147    * Primarily for use client-side. If server-side, use
148    * {@link #getBuffer()} with appropriate {@link #getTagOffset()} and {@link #getTagLength()}
149    * instead to save on allocations.
150    * @return tag value in a new byte array.
151    */
152   public byte[] getValue() {
153     int tagLength = getTagLength();
154     byte[] tag = new byte[tagLength];
155     Bytes.putBytes(tag, 0, bytes, getTagOffset(), tagLength);
156     return tag;
157   }
158 
159   /**
160    * Creates the list of tags from the byte array b. Expected that b is in the
161    * expected tag format
162    * @param b
163    * @param offset
164    * @param length
165    * @return List of tags
166    */
167   public static List<Tag> asList(byte[] b, int offset, int length) {
168     List<Tag> tags = new ArrayList<Tag>();
169     int pos = offset;
170     while (pos < offset + length) {
171       int tagLen = Bytes.readAsInt(b, pos, TAG_LENGTH_SIZE);
172       tags.add(new Tag(b, pos, tagLen + TAG_LENGTH_SIZE));
173       pos += TAG_LENGTH_SIZE + tagLen;
174     }
175     return tags;
176   }
177 
178   /**
179    * Write a list of tags into a byte array
180    * @param tags
181    * @return the serialized tag data as bytes
182    */
183   public static byte[] fromList(List<Tag> tags) {
184     if (tags == null || tags.size() <= 0) return null;
185     int length = 0;
186     for (Tag tag: tags) {
187       length += tag.length;
188     }
189     byte[] b = new byte[length];
190     int pos = 0;
191     for (Tag tag: tags) {
192       System.arraycopy(tag.bytes, tag.offset, b, pos, tag.length);
193       pos += tag.length;
194     }
195     return b;
196   }
197 
198   /**
199    * Retrieve the first tag from the tags byte array matching the passed in tag type
200    * @param b
201    * @param offset
202    * @param length
203    * @param type
204    * @return null if there is no tag of the passed in tag type
205    */
206   public static Tag getTag(byte[] b, int offset, int length, byte type) {
207     int pos = offset;
208     while (pos < offset + length) {
209       int tagLen = Bytes.readAsInt(b, pos, TAG_LENGTH_SIZE);
210       if(b[pos + TAG_LENGTH_SIZE] == type) {
211         return new Tag(b, pos, tagLen + TAG_LENGTH_SIZE);
212       }
213       pos += TAG_LENGTH_SIZE + tagLen;
214     }
215     return null;
216   }
217 
218   /**
219    * Returns the total length of the entire tag entity
220    */
221   int getLength() {
222     return this.length;
223   }
224 
225   /**
226    * Returns the offset of the entire tag entity
227    */
228   int getOffset() {
229     return this.offset;
230   }
231 
232 
233   /**
234    * @return A List<Tag> of any Tags found in <code>cell</code> else null.
235    */
236   public static List<Tag> carryForwardTags(final Cell cell) {
237     return carryForwardTags(null, cell);
238   }
239 
240   /**
241    * @return Add to <code>tagsOrNull</code> any Tags <code>cell</code> is carrying or null if
242    * it is carrying no Tags AND the passed in <code>tagsOrNull</code> is null (else we return new
243    * List<Tag> with Tags found).
244    */
245   public static List<Tag> carryForwardTags(final List<Tag> tagsOrNull, final Cell cell) {
246     List<Tag> tags = tagsOrNull;
247     if (cell.getTagsLength() <= 0) return tags;
248     Iterator<Tag> itr =
249         CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength());
250     if (tags == null) tags = new ArrayList<Tag>();
251     while (itr.hasNext()) {
252       tags.add(itr.next());
253     }
254     return tags;
255   }
256 }