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<Tag> 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}