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