View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase;
20  
21  import java.nio.ByteBuffer;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.hadoop.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.util.ByteBufferUtils;
27  import org.apache.hadoop.hbase.util.Bytes;
28  import org.apache.hadoop.hbase.util.IterableUtils;
29  import org.apache.hadoop.hbase.util.SimpleByteRange;
30  import org.apache.hadoop.io.WritableUtils;
31  
32  import com.google.common.base.Function;
33  import com.google.common.collect.Lists;
34  
35  /**
36   * static convenience methods for dealing with KeyValues and collections of KeyValues
37   */
38  @InterfaceAudience.Private
39  public class KeyValueUtil {
40  
41    /**************** length *********************/
42  
43    public static int length(final Cell cell) {
44      return (int) (KeyValue.getKeyValueDataStructureSize(cell.getRowLength(),
45          cell.getFamilyLength(), cell.getQualifierLength(), cell.getValueLength(),
46          cell.getTagsLength()));
47    }
48  
49    protected static int keyLength(final Cell cell) {
50      return (int)KeyValue.getKeyDataStructureSize(cell.getRowLength(), cell.getFamilyLength(),
51        cell.getQualifierLength());
52    }
53  
54    public static int lengthWithMvccVersion(final KeyValue kv, final boolean includeMvccVersion) {
55      int length = kv.getLength();
56      if (includeMvccVersion) {
57        length += WritableUtils.getVIntSize(kv.getMvccVersion());
58      }
59      return length;
60    }
61  
62    public static int totalLengthWithMvccVersion(final Iterable<? extends KeyValue> kvs,
63        final boolean includeMvccVersion) {
64      int length = 0;
65      for (KeyValue kv : IterableUtils.nullSafe(kvs)) {
66        length += lengthWithMvccVersion(kv, includeMvccVersion);
67      }
68      return length;
69    }
70  
71  
72    /**************** copy key only *********************/
73  
74    public static KeyValue copyToNewKeyValue(final Cell cell) {
75      byte[] bytes = copyToNewByteArray(cell);
76      KeyValue kvCell = new KeyValue(bytes, 0, bytes.length);
77      kvCell.setMvccVersion(cell.getMvccVersion());
78      return kvCell;
79    }
80  
81    public static ByteBuffer copyKeyToNewByteBuffer(final Cell cell) {
82      byte[] bytes = new byte[keyLength(cell)];
83      appendKeyToByteArrayWithoutValue(cell, bytes, 0);
84      ByteBuffer buffer = ByteBuffer.wrap(bytes);
85      buffer.position(buffer.limit());//make it look as if each field were appended
86      return buffer;
87    }
88  
89    public static byte[] copyToNewByteArray(final Cell cell) {
90      int v1Length = length(cell);
91      byte[] backingBytes = new byte[v1Length];
92      appendToByteArray(cell, backingBytes, 0);
93      return backingBytes;
94    }
95  
96    protected static int appendKeyToByteArrayWithoutValue(final Cell cell, final byte[] output,
97        final int offset) {
98      int nextOffset = offset;
99      nextOffset = Bytes.putShort(output, nextOffset, cell.getRowLength());
100     nextOffset = CellUtil.copyRowTo(cell, output, nextOffset);
101     nextOffset = Bytes.putByte(output, nextOffset, cell.getFamilyLength());
102     nextOffset = CellUtil.copyFamilyTo(cell, output, nextOffset);
103     nextOffset = CellUtil.copyQualifierTo(cell, output, nextOffset);
104     nextOffset = Bytes.putLong(output, nextOffset, cell.getTimestamp());
105     nextOffset = Bytes.putByte(output, nextOffset, cell.getTypeByte());
106     return nextOffset;
107   }
108 
109 
110   /**************** copy key and value *********************/
111 
112   public static int appendToByteArray(final Cell cell, final byte[] output, final int offset) {
113     int pos = offset;
114     pos = Bytes.putInt(output, pos, keyLength(cell));
115     pos = Bytes.putInt(output, pos, cell.getValueLength());
116     pos = appendKeyToByteArrayWithoutValue(cell, output, pos);
117     pos = CellUtil.copyValueTo(cell, output, pos);
118     if ((cell.getTagsLength() > 0)) {
119       pos = Bytes.putShort(output, pos, cell.getTagsLength());
120       pos = CellUtil.copyTagTo(cell, output, pos);
121     }
122     return pos;
123   }
124 
125   public static ByteBuffer copyToNewByteBuffer(final Cell cell) {
126     byte[] bytes = new byte[length(cell)];
127     appendToByteArray(cell, bytes, 0);
128     ByteBuffer buffer = ByteBuffer.wrap(bytes);
129     buffer.position(buffer.limit());//make it look as if each field were appended
130     return buffer;
131   }
132 
133   public static void appendToByteBuffer(final ByteBuffer bb, final KeyValue kv,
134       final boolean includeMvccVersion) {
135     // keep pushing the limit out. assume enough capacity
136     bb.limit(bb.position() + kv.getLength());
137     bb.put(kv.getBuffer(), kv.getOffset(), kv.getLength());
138     if (includeMvccVersion) {
139       int numMvccVersionBytes = WritableUtils.getVIntSize(kv.getMvccVersion());
140       ByteBufferUtils.extendLimit(bb, numMvccVersionBytes);
141       ByteBufferUtils.writeVLong(bb, kv.getMvccVersion());
142     }
143   }
144 
145 
146   /**************** iterating *******************************/
147 
148   /**
149    * Creates a new KeyValue object positioned in the supplied ByteBuffer and sets the ByteBuffer's
150    * position to the start of the next KeyValue. Does not allocate a new array or copy data.
151    * @param bb
152    * @param includesMvccVersion
153    * @param includesTags 
154    */
155   public static KeyValue nextShallowCopy(final ByteBuffer bb, final boolean includesMvccVersion,
156       boolean includesTags) {
157     if (bb.isDirect()) {
158       throw new IllegalArgumentException("only supports heap buffers");
159     }
160     if (bb.remaining() < 1) {
161       return null;
162     }
163     KeyValue keyValue = null;
164     int underlyingArrayOffset = bb.arrayOffset() + bb.position();
165     int keyLength = bb.getInt();
166     int valueLength = bb.getInt();
167     ByteBufferUtils.skip(bb, keyLength + valueLength);
168     short tagsLength = 0;
169     if (includesTags) {
170       tagsLength = bb.getShort();
171       ByteBufferUtils.skip(bb, tagsLength);
172     }
173     int kvLength = (int) KeyValue.getKeyValueDataStructureSize(keyLength, valueLength, tagsLength);
174     keyValue = new KeyValue(bb.array(), underlyingArrayOffset, kvLength);
175     if (includesMvccVersion) {
176       long mvccVersion = ByteBufferUtils.readVLong(bb);
177       keyValue.setMvccVersion(mvccVersion);
178     }
179     return keyValue;
180   }
181 
182 
183   /*************** next/previous **********************************/
184 
185   /**
186    * Append single byte 0x00 to the end of the input row key
187    */
188   public static KeyValue createFirstKeyInNextRow(final Cell in){
189     byte[] nextRow = new byte[in.getRowLength() + 1];
190     System.arraycopy(in.getRowArray(), in.getRowOffset(), nextRow, 0, in.getRowLength());
191     nextRow[nextRow.length - 1] = 0;//maybe not necessary
192     return KeyValue.createFirstOnRow(nextRow);
193   }
194 
195   /**
196    * Increment the row bytes and clear the other fields
197    */
198   public static KeyValue createFirstKeyInIncrementedRow(final Cell in){
199     byte[] thisRow = new SimpleByteRange(in.getRowArray(), in.getRowOffset(), in.getRowLength())
200         .deepCopyToNewArray();
201     byte[] nextRow = Bytes.unsignedCopyAndIncrement(thisRow);
202     return KeyValue.createFirstOnRow(nextRow);
203   }
204 
205   /**
206    * Decrement the timestamp.  For tests (currently wasteful)
207    *
208    * Remember timestamps are sorted reverse chronologically.
209    * @param in
210    * @return previous key
211    */
212   public static KeyValue previousKey(final KeyValue in) {
213     return KeyValue.createFirstOnRow(CellUtil.cloneRow(in), CellUtil.cloneFamily(in),
214       CellUtil.cloneQualifier(in), in.getTimestamp() - 1);
215   }
216 
217   /*************** misc **********************************/
218   /**
219    * @param cell
220    * @return <code>cell<code> if it is an instance of {@link KeyValue} else we will return a
221    * new {@link KeyValue} instance made from <code>cell</code>
222    */
223   public static KeyValue ensureKeyValue(final Cell cell) {
224     if (cell == null) return null;
225     return cell instanceof KeyValue? (KeyValue)cell: copyToNewKeyValue(cell);
226   }
227 
228   public static List<KeyValue> ensureKeyValues(List<Cell> cells) {
229     List<KeyValue> lazyList = Lists.transform(cells, new Function<Cell, KeyValue>() {
230       public KeyValue apply(Cell arg0) {
231         return KeyValueUtil.ensureKeyValue(arg0);
232       }
233     });
234     return new ArrayList<KeyValue>(lazyList);
235   }
236 }