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.SimpleByteRange;
28  import org.apache.hadoop.hbase.util.Bytes;
29  import org.apache.hadoop.hbase.util.IterableUtils;
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(), cell.getFamilyLength(),
45        cell.getQualifierLength(), cell.getValueLength());
46    }
47  
48    protected static int keyLength(final Cell cell) {
49      return (int)KeyValue.getKeyDataStructureSize(cell.getRowLength(), cell.getFamilyLength(),
50        cell.getQualifierLength());
51    }
52  
53    public static int lengthWithMvccVersion(final KeyValue kv, final boolean includeMvccVersion) {
54      int length = kv.getLength();
55      if (includeMvccVersion) {
56        length += WritableUtils.getVIntSize(kv.getMvccVersion());
57      }
58      return length;
59    }
60  
61    public static int totalLengthWithMvccVersion(final Iterable<? extends KeyValue> kvs,
62        final boolean includeMvccVersion) {
63      int length = 0;
64      for (KeyValue kv : IterableUtils.nullSafe(kvs)) {
65        length += lengthWithMvccVersion(kv, includeMvccVersion);
66      }
67      return length;
68    }
69  
70  
71    /**************** copy key only *********************/
72  
73    public static KeyValue copyToNewKeyValue(final Cell cell) {
74      KeyValue kvCell = new KeyValue(copyToNewByteArray(cell));
75      kvCell.setMvccVersion(cell.getMvccVersion());
76      return kvCell;
77    }
78  
79    public static ByteBuffer copyKeyToNewByteBuffer(final Cell cell) {
80      byte[] bytes = new byte[keyLength(cell)];
81      appendKeyToByteArrayWithoutValue(cell, bytes, 0);
82      ByteBuffer buffer = ByteBuffer.wrap(bytes);
83      buffer.position(buffer.limit());//make it look as if each field were appended
84      return buffer;
85    }
86  
87    public static byte[] copyToNewByteArray(final Cell cell) {
88      int v1Length = length(cell);
89      byte[] backingBytes = new byte[v1Length];
90      appendToByteArray(cell, backingBytes, 0);
91      return backingBytes;
92    }
93  
94    protected static int appendKeyToByteArrayWithoutValue(final Cell cell, final byte[] output,
95        final int offset) {
96      int nextOffset = offset;
97      nextOffset = Bytes.putShort(output, nextOffset, cell.getRowLength());
98      nextOffset = CellUtil.copyRowTo(cell, output, nextOffset);
99      nextOffset = Bytes.putByte(output, nextOffset, cell.getFamilyLength());
100     nextOffset = CellUtil.copyFamilyTo(cell, output, nextOffset);
101     nextOffset = CellUtil.copyQualifierTo(cell, output, nextOffset);
102     nextOffset = Bytes.putLong(output, nextOffset, cell.getTimestamp());
103     nextOffset = Bytes.putByte(output, nextOffset, cell.getTypeByte());
104     return nextOffset;
105   }
106 
107 
108   /**************** copy key and value *********************/
109 
110   public static int appendToByteArray(final Cell cell, final byte[] output, final int offset) {
111     int pos = offset;
112     pos = Bytes.putInt(output, pos, keyLength(cell));
113     pos = Bytes.putInt(output, pos, cell.getValueLength());
114     pos = appendKeyToByteArrayWithoutValue(cell, output, pos);
115     CellUtil.copyValueTo(cell, output, pos);
116     return pos + cell.getValueLength();
117   }
118 
119   public static ByteBuffer copyToNewByteBuffer(final Cell cell) {
120     byte[] bytes = new byte[length(cell)];
121     appendToByteArray(cell, bytes, 0);
122     ByteBuffer buffer = ByteBuffer.wrap(bytes);
123     buffer.position(buffer.limit());//make it look as if each field were appended
124     return buffer;
125   }
126 
127   public static void appendToByteBuffer(final ByteBuffer bb, final KeyValue kv,
128       final boolean includeMvccVersion) {
129     // keep pushing the limit out. assume enough capacity
130     bb.limit(bb.position() + kv.getLength());
131     bb.put(kv.getBuffer(), kv.getOffset(), kv.getLength());
132     if (includeMvccVersion) {
133       int numMvccVersionBytes = WritableUtils.getVIntSize(kv.getMvccVersion());
134       ByteBufferUtils.extendLimit(bb, numMvccVersionBytes);
135       ByteBufferUtils.writeVLong(bb, kv.getMvccVersion());
136     }
137   }
138 
139 
140   /**************** iterating *******************************/
141 
142   /**
143    * Creates a new KeyValue object positioned in the supplied ByteBuffer and sets the ByteBuffer's
144    * position to the start of the next KeyValue. Does not allocate a new array or copy data.
145    */
146   public static KeyValue nextShallowCopy(final ByteBuffer bb, final boolean includesMvccVersion) {
147     if (bb.isDirect()) {
148       throw new IllegalArgumentException("only supports heap buffers");
149     }
150     if (bb.remaining() < 1) {
151       return null;
152     }
153     int underlyingArrayOffset = bb.arrayOffset() + bb.position();
154     int keyLength = bb.getInt();
155     int valueLength = bb.getInt();
156     int kvLength = KeyValue.KEYVALUE_INFRASTRUCTURE_SIZE + keyLength + valueLength;
157     KeyValue keyValue = new KeyValue(bb.array(), underlyingArrayOffset, kvLength);
158     ByteBufferUtils.skip(bb, keyLength + valueLength);
159     if (includesMvccVersion) {
160       long mvccVersion = ByteBufferUtils.readVLong(bb);
161       keyValue.setMvccVersion(mvccVersion);
162     }
163     return keyValue;
164   }
165 
166 
167   /*************** next/previous **********************************/
168 
169   /**
170    * Append single byte 0x00 to the end of the input row key
171    */
172   public static KeyValue createFirstKeyInNextRow(final Cell in){
173     byte[] nextRow = new byte[in.getRowLength() + 1];
174     System.arraycopy(in.getRowArray(), in.getRowOffset(), nextRow, 0, in.getRowLength());
175     nextRow[nextRow.length - 1] = 0;//maybe not necessary
176     return KeyValue.createFirstOnRow(nextRow);
177   }
178 
179   /**
180    * Increment the row bytes and clear the other fields
181    */
182   public static KeyValue createFirstKeyInIncrementedRow(final Cell in){
183     byte[] thisRow = new SimpleByteRange(in.getRowArray(), in.getRowOffset(), in.getRowLength())
184         .deepCopyToNewArray();
185     byte[] nextRow = Bytes.unsignedCopyAndIncrement(thisRow);
186     return KeyValue.createFirstOnRow(nextRow);
187   }
188 
189   /**
190    * Decrement the timestamp.  For tests (currently wasteful)
191    *
192    * Remember timestamps are sorted reverse chronologically.
193    * @param in
194    * @return previous key
195    */
196   public static KeyValue previousKey(final KeyValue in) {
197     return KeyValue.createFirstOnRow(CellUtil.cloneRow(in), CellUtil.cloneFamily(in),
198       CellUtil.cloneQualifier(in), in.getTimestamp() - 1);
199   }
200 
201   /*************** misc **********************************/
202   /**
203    * @param cell
204    * @return <code>cell<code> if it is an instance of {@link KeyValue} else we will return a
205    * new {@link KeyValue} instance made from <code>cell</code>
206    */
207   public static KeyValue ensureKeyValue(final Cell cell) {
208     if (cell == null) return null;
209     return cell instanceof KeyValue? (KeyValue)cell: copyToNewKeyValue(cell);
210   }
211 
212   public static List<KeyValue> ensureKeyValues(List<Cell> cells) {
213     List<KeyValue> lazyList = Lists.transform(cells, new Function<Cell, KeyValue>() {
214       public KeyValue apply(Cell arg0) {
215         return KeyValueUtil.ensureKeyValue(arg0);
216       }
217     });
218     return new ArrayList<KeyValue>(lazyList);
219   }
220 }