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.io.IOException;
021import java.util.ArrayList;
022import java.util.Iterator;
023import java.util.List;
024
025import org.apache.hadoop.hbase.io.util.StreamUtils;
026import org.apache.hadoop.hbase.util.ByteBufferUtils;
027import org.apache.hadoop.hbase.util.Bytes;
028import org.apache.hadoop.hbase.util.Pair;
029import org.apache.yetus.audience.InterfaceAudience;
030
031@InterfaceAudience.Private
032public final class TagUtil {
033
034  private TagUtil(){}
035
036  /**
037   * Creates list of tags from given byte array, expected that it is in the expected tag format.
038   * @param b The byte array
039   * @param offset The offset in array where tag bytes begin
040   * @param length Total length of all tags bytes
041   * @return List of tags
042   */
043  public static List<Tag> asList(byte[] b, int offset, int length) {
044    List<Tag> tags = new ArrayList<>();
045    int pos = offset;
046    while (pos < offset + length) {
047      int tagLen = Bytes.readAsInt(b, pos, Tag.TAG_LENGTH_SIZE);
048      tags.add(new ArrayBackedTag(b, pos, tagLen + Tag.TAG_LENGTH_SIZE));
049      pos += Tag.TAG_LENGTH_SIZE + tagLen;
050    }
051    return tags;
052  }
053
054  /**
055   * Reads an int value stored as a VInt at tag's given offset.
056   * @param tag The Tag
057   * @param offset The offset where VInt bytes begin
058   * @return A pair of the int value and number of bytes taken to store VInt
059   * @throws IOException When varint is malformed and not able to be read correctly
060   */
061  public static Pair<Integer, Integer> readVIntValuePart(Tag tag, int offset) throws IOException {
062    if (tag.hasArray()) {
063      return StreamUtils.readRawVarint32(tag.getValueArray(), offset);
064    }
065    return StreamUtils.readRawVarint32(tag.getValueByteBuffer(), offset);
066  }
067
068  /**
069   * @return A List&lt;Tag&gt; of any Tags found in <code>cell</code> else null.
070   */
071  public static List<Tag> carryForwardTags(final Cell cell) {
072    return carryForwardTags(null, cell);
073  }
074
075  /**
076   * Add to <code>tagsOrNull</code> any Tags <code>cell</code> is carrying or null if none.
077   */
078  public static List<Tag> carryForwardTags(final List<Tag> tagsOrNull, final Cell cell) {
079    Iterator<Tag> itr = PrivateCellUtil.tagsIterator(cell);
080    if (itr == EMPTY_TAGS_ITR) {
081      // If no Tags, return early.
082      return tagsOrNull;
083    }
084    List<Tag> tags = tagsOrNull;
085    if (tags == null) {
086      tags = new ArrayList<>();
087    }
088    while (itr.hasNext()) {
089      tags.add(itr.next());
090    }
091    return tags;
092  }
093
094  public static byte[] concatTags(byte[] tags, Cell cell) {
095    int cellTagsLen = cell.getTagsLength();
096    if (cellTagsLen == 0) {
097      // If no Tags, return early.
098      return tags;
099    }
100    byte[] b = new byte[tags.length + cellTagsLen];
101    int pos = Bytes.putBytes(b, 0, tags, 0, tags.length);
102    if (cell instanceof ByteBufferExtendedCell) {
103      ByteBufferUtils.copyFromBufferToArray(b, ((ByteBufferExtendedCell) cell).getTagsByteBuffer(),
104          ((ByteBufferExtendedCell) cell).getTagsPosition(), pos, cellTagsLen);
105    } else {
106      Bytes.putBytes(b, pos, cell.getTagsArray(), cell.getTagsOffset(), cellTagsLen);
107    }
108    return b;
109  }
110
111  /**
112   * @return Carry forward the TTL tag.
113   */
114  public static List<Tag> carryForwardTTLTag(final List<Tag> tagsOrNull, final long ttl) {
115    if (ttl == Long.MAX_VALUE) {
116      return tagsOrNull;
117    }
118    List<Tag> tags = tagsOrNull;
119    // If we are making the array in here, given we are the last thing checked, we'll be only thing
120    // in the array so set its size to '1' (I saw this being done in earlier version of
121    // tag-handling).
122    if (tags == null) {
123      tags = new ArrayList<>(1);
124    } else {
125      // Remove existing TTL tags if any
126      Iterator<Tag> tagsItr = tags.iterator();
127      while (tagsItr.hasNext()) {
128        Tag tag = tagsItr.next();
129        if (tag.getType() == TagType.TTL_TAG_TYPE) {
130          tagsItr.remove();
131          break;
132        }
133      }
134    }
135    tags.add(new ArrayBackedTag(TagType.TTL_TAG_TYPE, Bytes.toBytes(ttl)));
136    return tags;
137  }
138
139  /**
140   * Write a list of tags into a byte array
141   * Note : these are all purely internal APIs. It helps in
142   * cases where we have set of tags and we would want to create a cell out of it. Say in Mobs we
143   * create a reference tags to indicate the presence of mob data. Also note that these are not
144   * exposed to CPs also
145   * @param tags The list of tags
146   * @return the serialized tag data as bytes
147   */
148  public static byte[] fromList(List<Tag> tags) {
149    if (tags == null || tags.isEmpty()) {
150      return HConstants.EMPTY_BYTE_ARRAY;
151    }
152    int length = 0;
153    for (Tag tag : tags) {
154      length += tag.getValueLength() + Tag.INFRASTRUCTURE_SIZE;
155    }
156    byte[] b = new byte[length];
157    int pos = 0;
158    int tlen;
159    for (Tag tag : tags) {
160      tlen = tag.getValueLength();
161      pos = Bytes.putAsShort(b, pos, tlen + Tag.TYPE_LENGTH_SIZE);
162      pos = Bytes.putByte(b, pos, tag.getType());
163      if (tag.hasArray()) {
164        pos = Bytes.putBytes(b, pos, tag.getValueArray(), tag.getValueOffset(), tlen);
165      } else {
166        ByteBufferUtils.copyFromBufferToArray(b, tag.getValueByteBuffer(), tag.getValueOffset(),
167          pos, tlen);
168        pos += tlen;
169      }
170    }
171    return b;
172  }
173
174  /**
175   * Iterator returned when no Tags. Used by CellUtil too.
176   */
177  static final Iterator<Tag> EMPTY_TAGS_ITR = new Iterator<Tag>() {
178    @Override
179    public boolean hasNext() {
180      return false;
181    }
182
183    @Override
184    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IT_NO_SUCH_ELEMENT",
185      justification="Intentional")
186    public Tag next() {
187      return null;
188    }
189
190    @Override
191    public void remove() {
192      throw new UnsupportedOperationException();
193    }
194  };
195}