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