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.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.nio.ByteBuffer;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.apache.hadoop.hbase.KeyValue.Type;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  import org.apache.hadoop.hbase.io.util.StreamUtils;
33  import org.apache.hadoop.hbase.util.ByteBufferUtils;
34  import org.apache.hadoop.hbase.util.Bytes;
35  import org.apache.hadoop.hbase.util.IterableUtils;
36  import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
37  import org.apache.hadoop.io.IOUtils;
38  import org.apache.hadoop.io.WritableUtils;
39  
40  import com.google.common.base.Function;
41  import com.google.common.collect.Lists;
42  
43  /**
44   * static convenience methods for dealing with KeyValues and collections of KeyValues
45   */
46  @InterfaceAudience.Private
47  public class KeyValueUtil {
48  
49    /**************** length *********************/
50  
51    /**
52     * Returns number of bytes this cell would have been used if serialized as in {@link KeyValue}
53     * @param cell
54     * @return the length
55     */
56    public static int length(final Cell cell) {
57      return length(cell.getRowLength(), cell.getFamilyLength(), cell.getQualifierLength(),
58          cell.getValueLength(), cell.getTagsLength(), true);
59    }
60  
61    public static int length(short rlen, byte flen, int qlen, int vlen, int tlen, boolean withTags) {
62      if (withTags) {
63        return (int) (KeyValue.getKeyValueDataStructureSize(rlen, flen, qlen, vlen, tlen));
64      }
65      return (int) (KeyValue.getKeyValueDataStructureSize(rlen, flen, qlen, vlen));
66    }
67  
68    /**
69     * Returns number of bytes this cell's key part would have been used if serialized as in
70     * {@link KeyValue}. Key includes rowkey, family, qualifier, timestamp and type.
71     * @param cell
72     * @return the key length
73     */
74    public static int keyLength(final Cell cell) {
75      return keyLength(cell.getRowLength(), cell.getFamilyLength(), cell.getQualifierLength());
76    }
77  
78    private static int keyLength(short rlen, byte flen, int qlen) {
79      return (int) KeyValue.getKeyDataStructureSize(rlen, flen, qlen);
80    }
81  
82    public static int lengthWithMvccVersion(final KeyValue kv, final boolean includeMvccVersion) {
83      int length = kv.getLength();
84      if (includeMvccVersion) {
85        length += WritableUtils.getVIntSize(kv.getMvccVersion());
86      }
87      return length;
88    }
89  
90    public static int totalLengthWithMvccVersion(final Iterable<? extends KeyValue> kvs,
91        final boolean includeMvccVersion) {
92      int length = 0;
93      for (KeyValue kv : IterableUtils.nullSafe(kvs)) {
94        length += lengthWithMvccVersion(kv, includeMvccVersion);
95      }
96      return length;
97    }
98  
99  
100   /**************** copy key only *********************/
101 
102   public static KeyValue copyToNewKeyValue(final Cell cell) {
103     byte[] bytes = copyToNewByteArray(cell);
104     KeyValue kvCell = new KeyValue(bytes, 0, bytes.length);
105     kvCell.setSequenceId(cell.getMvccVersion());
106     return kvCell;
107   }
108 
109   /**
110    * The position will be set to the beginning of the new ByteBuffer
111    * @param cell
112    * @return the Bytebuffer containing the key part of the cell
113    */
114   public static ByteBuffer copyKeyToNewByteBuffer(final Cell cell) {
115     byte[] bytes = new byte[keyLength(cell)];
116     appendKeyTo(cell, bytes, 0);
117     ByteBuffer buffer = ByteBuffer.wrap(bytes);
118     return buffer;
119   }
120 
121   public static byte[] copyToNewByteArray(final Cell cell) {
122     int v1Length = length(cell);
123     byte[] backingBytes = new byte[v1Length];
124     appendToByteArray(cell, backingBytes, 0);
125     return backingBytes;
126   }
127 
128   public static int appendKeyTo(final Cell cell, final byte[] output,
129       final int offset) {
130     int nextOffset = offset;
131     nextOffset = Bytes.putShort(output, nextOffset, cell.getRowLength());
132     nextOffset = CellUtil.copyRowTo(cell, output, nextOffset);
133     nextOffset = Bytes.putByte(output, nextOffset, cell.getFamilyLength());
134     nextOffset = CellUtil.copyFamilyTo(cell, output, nextOffset);
135     nextOffset = CellUtil.copyQualifierTo(cell, output, nextOffset);
136     nextOffset = Bytes.putLong(output, nextOffset, cell.getTimestamp());
137     nextOffset = Bytes.putByte(output, nextOffset, cell.getTypeByte());
138     return nextOffset;
139   }
140 
141 
142   /**************** copy key and value *********************/
143 
144   public static int appendToByteArray(final Cell cell, final byte[] output, final int offset) {
145     // TODO when cell instance of KV we can bypass all steps and just do backing single array
146     // copy(?)
147     int pos = offset;
148     pos = Bytes.putInt(output, pos, keyLength(cell));
149     pos = Bytes.putInt(output, pos, cell.getValueLength());
150     pos = appendKeyTo(cell, output, pos);
151     pos = CellUtil.copyValueTo(cell, output, pos);
152     if ((cell.getTagsLength() > 0)) {
153       pos = Bytes.putAsShort(output, pos, cell.getTagsLength());
154       pos = CellUtil.copyTagTo(cell, output, pos);
155     }
156     return pos;
157   }
158 
159   /**
160    * The position will be set to the beginning of the new ByteBuffer
161    * @param cell
162    * @return the ByteBuffer containing the cell
163    */
164   public static ByteBuffer copyToNewByteBuffer(final Cell cell) {
165     byte[] bytes = new byte[length(cell)];
166     appendToByteArray(cell, bytes, 0);
167     ByteBuffer buffer = ByteBuffer.wrap(bytes);
168     return buffer;
169   }
170 
171   public static void appendToByteBuffer(final ByteBuffer bb, final KeyValue kv,
172       final boolean includeMvccVersion) {
173     // keep pushing the limit out. assume enough capacity
174     bb.limit(bb.position() + kv.getLength());
175     bb.put(kv.getBuffer(), kv.getOffset(), kv.getLength());
176     if (includeMvccVersion) {
177       int numMvccVersionBytes = WritableUtils.getVIntSize(kv.getMvccVersion());
178       ByteBufferUtils.extendLimit(bb, numMvccVersionBytes);
179       ByteBufferUtils.writeVLong(bb, kv.getMvccVersion());
180     }
181   }
182 
183 
184   /**************** iterating *******************************/
185 
186   /**
187    * Creates a new KeyValue object positioned in the supplied ByteBuffer and sets the ByteBuffer's
188    * position to the start of the next KeyValue. Does not allocate a new array or copy data.
189    * @param bb
190    * @param includesMvccVersion
191    * @param includesTags 
192    */
193   public static KeyValue nextShallowCopy(final ByteBuffer bb, final boolean includesMvccVersion,
194       boolean includesTags) {
195     if (bb.isDirect()) {
196       throw new IllegalArgumentException("only supports heap buffers");
197     }
198     if (bb.remaining() < 1) {
199       return null;
200     }
201     KeyValue keyValue = null;
202     int underlyingArrayOffset = bb.arrayOffset() + bb.position();
203     int keyLength = bb.getInt();
204     int valueLength = bb.getInt();
205     ByteBufferUtils.skip(bb, keyLength + valueLength);
206     int tagsLength = 0;
207     if (includesTags) {
208       // Read short as unsigned, high byte first
209       tagsLength = ((bb.get() & 0xff) << 8) ^ (bb.get() & 0xff);
210       ByteBufferUtils.skip(bb, tagsLength);
211     }
212     int kvLength = (int) KeyValue.getKeyValueDataStructureSize(keyLength, valueLength, tagsLength);
213     keyValue = new KeyValue(bb.array(), underlyingArrayOffset, kvLength);
214     if (includesMvccVersion) {
215       long mvccVersion = ByteBufferUtils.readVLong(bb);
216       keyValue.setSequenceId(mvccVersion);
217     }
218     return keyValue;
219   }
220 
221 
222   /*************** next/previous **********************************/
223 
224   /**
225    * Append single byte 0x00 to the end of the input row key
226    */
227   public static KeyValue createFirstKeyInNextRow(final Cell in){
228     byte[] nextRow = new byte[in.getRowLength() + 1];
229     System.arraycopy(in.getRowArray(), in.getRowOffset(), nextRow, 0, in.getRowLength());
230     nextRow[nextRow.length - 1] = 0;//maybe not necessary
231     return createFirstOnRow(nextRow);
232   }
233 
234   /**
235    * Increment the row bytes and clear the other fields
236    */
237   public static KeyValue createFirstKeyInIncrementedRow(final Cell in){
238     byte[] thisRow = new SimpleMutableByteRange(in.getRowArray(), in.getRowOffset(),
239         in.getRowLength()).deepCopyToNewArray();
240     byte[] nextRow = Bytes.unsignedCopyAndIncrement(thisRow);
241     return createFirstOnRow(nextRow);
242   }
243 
244   /**
245    * Decrement the timestamp.  For tests (currently wasteful)
246    *
247    * Remember timestamps are sorted reverse chronologically.
248    * @param in
249    * @return previous key
250    */
251   public static KeyValue previousKey(final KeyValue in) {
252     return createFirstOnRow(CellUtil.cloneRow(in), CellUtil.cloneFamily(in),
253       CellUtil.cloneQualifier(in), in.getTimestamp() - 1);
254   }
255   
256 
257   /**
258    * Create a KeyValue for the specified row, family and qualifier that would be
259    * larger than or equal to all other possible KeyValues that have the same
260    * row, family, qualifier. Used for reseeking.
261    *
262    * @param row
263    *          row key
264    * @param roffset
265    *         row offset
266    * @param rlength
267    *         row length
268    * @param family
269    *         family name
270    * @param foffset
271    *         family offset
272    * @param flength
273    *         family length
274    * @param qualifier
275    *        column qualifier
276    * @param qoffset
277    *        qualifier offset
278    * @param qlength
279    *        qualifier length
280    * @return Last possible key on passed row, family, qualifier.
281    */
282   public static KeyValue createLastOnRow(final byte[] row, final int roffset, final int rlength,
283       final byte[] family, final int foffset, final int flength, final byte[] qualifier,
284       final int qoffset, final int qlength) {
285     return new KeyValue(row, roffset, rlength, family, foffset, flength, qualifier, qoffset,
286         qlength, HConstants.OLDEST_TIMESTAMP, Type.Minimum, null, 0, 0);
287   }
288   
289   /**
290    * Creates a keyValue for the specified keyvalue larger than or equal to all other possible
291    * KeyValues that have the same row, family, qualifer.  Used for reseeking
292    * @param kv
293    * @return KeyValue
294    */
295   public static KeyValue createLastOnRow(Cell kv) {
296     return createLastOnRow(kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(), null, 0, 0,
297         null, 0, 0);
298   }
299 
300   /**
301    * Similar to
302    * {@link #createLastOnRow(byte[], int, int, byte[], int, int, byte[], int, int)}
303    * but creates the last key on the row/column of this KV (the value part of
304    * the returned KV is always empty). Used in creating "fake keys" for the
305    * multi-column Bloom filter optimization to skip the row/column we already
306    * know is not in the file.
307    * 
308    * @param kv - cell
309    * @return the last key on the row/column of the given key-value pair
310    */
311   public static KeyValue createLastOnRowCol(Cell kv) {
312     return new KeyValue(kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
313         kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(), kv.getQualifierArray(),
314         kv.getQualifierOffset(), kv.getQualifierLength(), HConstants.OLDEST_TIMESTAMP,
315         Type.Minimum, null, 0, 0);
316   }
317 
318   /**
319    * Creates the first KV with the row/family/qualifier of this KV and the given
320    * timestamp. Uses the "maximum" KV type that guarantees that the new KV is
321    * the lowest possible for this combination of row, family, qualifier, and
322    * timestamp. This KV's own timestamp is ignored. While this function copies
323    * the value from this KV, it is normally used on key-only KVs.
324    * 
325    * @param kv - cell
326    * @param ts
327    */
328   public static KeyValue createFirstOnRowColTS(Cell kv, long ts) {
329     return new KeyValue(kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
330         kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(), kv.getQualifierArray(),
331         kv.getQualifierOffset(), kv.getQualifierLength(), ts, Type.Maximum, kv.getValueArray(),
332         kv.getValueOffset(), kv.getValueLength());
333   }
334   
335   /**
336    * Create a KeyValue that is smaller than all other possible KeyValues
337    * for the given row. That is any (valid) KeyValue on 'row' would sort
338    * _after_ the result.
339    *
340    * @param row - row key (arbitrary byte array)
341    * @return First possible KeyValue on passed <code>row</code>
342    */
343   public static KeyValue createFirstOnRow(final byte [] row, int roffset, short rlength) {
344     return new KeyValue(row, roffset, rlength,
345         null, 0, 0, null, 0, 0, HConstants.LATEST_TIMESTAMP, Type.Maximum, null, 0, 0);
346   }
347   
348 
349   /**
350    * Creates a KeyValue that is last on the specified row id. That is,
351    * every other possible KeyValue for the given row would compareTo()
352    * less than the result of this call.
353    * @param row row key
354    * @return Last possible KeyValue on passed <code>row</code>
355    */
356   public static KeyValue createLastOnRow(final byte[] row) {
357     return new KeyValue(row, null, null, HConstants.LATEST_TIMESTAMP, Type.Minimum);
358   }
359 
360   /**
361    * Create a KeyValue that is smaller than all other possible KeyValues
362    * for the given row. That is any (valid) KeyValue on 'row' would sort
363    * _after_ the result.
364    *
365    * @param row - row key (arbitrary byte array)
366    * @return First possible KeyValue on passed <code>row</code>
367    */
368   public static KeyValue createFirstOnRow(final byte [] row) {
369     return createFirstOnRow(row, HConstants.LATEST_TIMESTAMP);
370   }
371 
372   /**
373    * Creates a KeyValue that is smaller than all other KeyValues that
374    * are older than the passed timestamp.
375    * @param row - row key (arbitrary byte array)
376    * @param ts - timestamp
377    * @return First possible key on passed <code>row</code> and timestamp.
378    */
379   public static KeyValue createFirstOnRow(final byte [] row,
380       final long ts) {
381     return new KeyValue(row, null, null, ts, Type.Maximum);
382   }
383 
384   /**
385    * Create a KeyValue for the specified row, family and qualifier that would be
386    * smaller than all other possible KeyValues that have the same row,family,qualifier.
387    * Used for seeking.
388    * @param row - row key (arbitrary byte array)
389    * @param family - family name
390    * @param qualifier - column qualifier
391    * @return First possible key on passed <code>row</code>, and column.
392    */
393   public static KeyValue createFirstOnRow(final byte [] row, final byte [] family,
394       final byte [] qualifier) {
395     return new KeyValue(row, family, qualifier, HConstants.LATEST_TIMESTAMP, Type.Maximum);
396   }
397 
398   /**
399    * Create a Delete Family KeyValue for the specified row and family that would
400    * be smaller than all other possible Delete Family KeyValues that have the
401    * same row and family.
402    * Used for seeking.
403    * @param row - row key (arbitrary byte array)
404    * @param family - family name
405    * @return First Delete Family possible key on passed <code>row</code>.
406    */
407   public static KeyValue createFirstDeleteFamilyOnRow(final byte [] row,
408       final byte [] family) {
409     return new KeyValue(row, family, null, HConstants.LATEST_TIMESTAMP,
410         Type.DeleteFamily);
411   }
412 
413   /**
414    * @param row - row key (arbitrary byte array)
415    * @param f - family name
416    * @param q - column qualifier
417    * @param ts - timestamp
418    * @return First possible key on passed <code>row</code>, column and timestamp
419    */
420   public static KeyValue createFirstOnRow(final byte [] row, final byte [] f,
421       final byte [] q, final long ts) {
422     return new KeyValue(row, f, q, ts, Type.Maximum);
423   }
424 
425   /**
426    * Create a KeyValue for the specified row, family and qualifier that would be
427    * smaller than all other possible KeyValues that have the same row,
428    * family, qualifier.
429    * Used for seeking.
430    * @param row row key
431    * @param roffset row offset
432    * @param rlength row length
433    * @param family family name
434    * @param foffset family offset
435    * @param flength family length
436    * @param qualifier column qualifier
437    * @param qoffset qualifier offset
438    * @param qlength qualifier length
439    * @return First possible key on passed Row, Family, Qualifier.
440    */
441   public static KeyValue createFirstOnRow(final byte [] row,
442       final int roffset, final int rlength, final byte [] family,
443       final int foffset, final int flength, final byte [] qualifier,
444       final int qoffset, final int qlength) {
445     return new KeyValue(row, roffset, rlength, family,
446         foffset, flength, qualifier, qoffset, qlength,
447         HConstants.LATEST_TIMESTAMP, Type.Maximum, null, 0, 0);
448   }
449 
450   /**
451    * Create a KeyValue for the specified row, family and qualifier that would be
452    * smaller than all other possible KeyValues that have the same row,
453    * family, qualifier.
454    * Used for seeking.
455    *
456    * @param buffer the buffer to use for the new <code>KeyValue</code> object
457    * @param row the value key
458    * @param family family name
459    * @param qualifier column qualifier
460    *
461    * @return First possible key on passed Row, Family, Qualifier.
462    *
463    * @throws IllegalArgumentException The resulting <code>KeyValue</code> object would be larger
464    * than the provided buffer or than <code>Integer.MAX_VALUE</code>
465    */
466   public static KeyValue createFirstOnRow(byte [] buffer, final byte [] row,
467       final byte [] family, final byte [] qualifier)
468           throws IllegalArgumentException {
469     return createFirstOnRow(buffer, 0, row, 0, row.length,
470         family, 0, family.length,
471         qualifier, 0, qualifier.length);
472   }
473 
474   /**
475    * Create a KeyValue for the specified row, family and qualifier that would be
476    * smaller than all other possible KeyValues that have the same row,
477    * family, qualifier.
478    * Used for seeking.
479    *
480    * @param buffer the buffer to use for the new <code>KeyValue</code> object
481    * @param boffset buffer offset
482    * @param row the value key
483    * @param roffset row offset
484    * @param rlength row length
485    * @param family family name
486    * @param foffset family offset
487    * @param flength family length
488    * @param qualifier column qualifier
489    * @param qoffset qualifier offset
490    * @param qlength qualifier length
491    *
492    * @return First possible key on passed Row, Family, Qualifier.
493    *
494    * @throws IllegalArgumentException The resulting <code>KeyValue</code> object would be larger
495    * than the provided buffer or than <code>Integer.MAX_VALUE</code>
496    */
497   public static KeyValue createFirstOnRow(byte[] buffer, final int boffset, final byte[] row,
498       final int roffset, final int rlength, final byte[] family, final int foffset,
499       final int flength, final byte[] qualifier, final int qoffset, final int qlength)
500       throws IllegalArgumentException {
501 
502     long lLength = KeyValue.getKeyValueDataStructureSize(rlength, flength, qlength, 0);
503 
504     if (lLength > Integer.MAX_VALUE) {
505       throw new IllegalArgumentException("KeyValue length " + lLength + " > " + Integer.MAX_VALUE);
506     }
507     int iLength = (int) lLength;
508     if (buffer.length - boffset < iLength) {
509       throw new IllegalArgumentException("Buffer size " + (buffer.length - boffset) + " < "
510           + iLength);
511     }
512 
513     int len = KeyValue.writeByteArray(buffer, boffset, row, roffset, rlength, family, foffset,
514         flength, qualifier, qoffset, qlength, HConstants.LATEST_TIMESTAMP, KeyValue.Type.Maximum,
515         null, 0, 0, null);
516     return new KeyValue(buffer, boffset, len);
517   }
518 
519   /**
520    * Creates the first KV with the row/family/qualifier of this KV and the
521    * given timestamp. Uses the "maximum" KV type that guarantees that the new
522    * KV is the lowest possible for this combination of row, family, qualifier,
523    * and timestamp. This KV's own timestamp is ignored. While this function
524    * copies the value from this KV, it is normally used on key-only KVs.
525    */
526   public static KeyValue createFirstOnRowColTS(KeyValue kv, long ts) {
527     return new KeyValue(
528         kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
529         kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
530         kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength(),
531         ts, Type.Maximum, kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
532   }
533 
534   /*************** misc **********************************/
535   /**
536    * @param cell
537    * @return <code>cell</code> if it is an instance of {@link KeyValue} else we will return a
538    * new {@link KeyValue} instance made from <code>cell</code>
539    * @deprecated without any replacement.
540    */
541   @Deprecated
542   public static KeyValue ensureKeyValue(final Cell cell) {
543     if (cell == null) return null;
544     return cell instanceof KeyValue? (KeyValue)cell: copyToNewKeyValue(cell);
545   }
546 
547   @Deprecated
548   public static List<KeyValue> ensureKeyValues(List<Cell> cells) {
549     List<KeyValue> lazyList = Lists.transform(cells, new Function<Cell, KeyValue>() {
550       public KeyValue apply(Cell arg0) {
551         return KeyValueUtil.ensureKeyValue(arg0);
552       }
553     });
554     return new ArrayList<KeyValue>(lazyList);
555   }
556   /**
557    * Write out a KeyValue in the manner in which we used to when KeyValue was a
558    * Writable.
559    *
560    * @param kv
561    * @param out
562    * @return Length written on stream
563    * @throws IOException
564    * @see #create(DataInput) for the inverse function
565    */
566   public static long write(final KeyValue kv, final DataOutput out) throws IOException {
567     // This is how the old Writables write used to serialize KVs. Need to figure
568     // way to make it
569     // work for all implementations.
570     int length = kv.getLength();
571     out.writeInt(length);
572     out.write(kv.getBuffer(), kv.getOffset(), length);
573     return length + Bytes.SIZEOF_INT;
574   }
575 
576   /**
577    * Create a KeyValue reading from the raw InputStream. Named
578    * <code>iscreate</code> so doesn't clash with {@link #create(DataInput)}
579    *
580    * @param in
581    * @param withTags whether the keyvalue should include tags are not
582    * @return Created KeyValue OR if we find a length of zero, we will return
583    *         null which can be useful marking a stream as done.
584    * @throws IOException
585    */
586   public static KeyValue iscreate(final InputStream in, boolean withTags) throws IOException {
587     byte[] intBytes = new byte[Bytes.SIZEOF_INT];
588     int bytesRead = 0;
589     while (bytesRead < intBytes.length) {
590       int n = in.read(intBytes, bytesRead, intBytes.length - bytesRead);
591       if (n < 0) {
592         if (bytesRead == 0)
593           return null; // EOF at start is ok
594         throw new IOException("Failed read of int, read " + bytesRead + " bytes");
595       }
596       bytesRead += n;
597     }
598     // TODO: perhaps some sanity check is needed here.
599     byte[] bytes = new byte[Bytes.toInt(intBytes)];
600     IOUtils.readFully(in, bytes, 0, bytes.length);
601     if (withTags) {
602       return new KeyValue(bytes, 0, bytes.length);
603     } else {
604       return new NoTagsKeyValue(bytes, 0, bytes.length);
605     }
606   }
607 
608   /**
609    * @param b
610    * @return A KeyValue made of a byte array that holds the key-only part.
611    *         Needed to convert hfile index members to KeyValues.
612    */
613   public static KeyValue createKeyValueFromKey(final byte[] b) {
614     return createKeyValueFromKey(b, 0, b.length);
615   }
616 
617   /**
618    * @param bb
619    * @return A KeyValue made of a byte buffer that holds the key-only part.
620    *         Needed to convert hfile index members to KeyValues.
621    */
622   public static KeyValue createKeyValueFromKey(final ByteBuffer bb) {
623     return createKeyValueFromKey(bb.array(), bb.arrayOffset(), bb.limit());
624   }
625 
626   /**
627    * @param b
628    * @param o
629    * @param l
630    * @return A KeyValue made of a byte array that holds the key-only part.
631    *         Needed to convert hfile index members to KeyValues.
632    */
633   public static KeyValue createKeyValueFromKey(final byte[] b, final int o, final int l) {
634     byte[] newb = new byte[l + KeyValue.ROW_OFFSET];
635     System.arraycopy(b, o, newb, KeyValue.ROW_OFFSET, l);
636     Bytes.putInt(newb, 0, l);
637     Bytes.putInt(newb, Bytes.SIZEOF_INT, 0);
638     return new KeyValue(newb);
639   }
640 
641   /**
642    * @param in
643    *          Where to read bytes from. Creates a byte array to hold the
644    *          KeyValue backing bytes copied from the steam.
645    * @return KeyValue created by deserializing from <code>in</code> OR if we
646    *         find a length of zero, we will return null which can be useful
647    *         marking a stream as done.
648    * @throws IOException
649    */
650   public static KeyValue create(final DataInput in) throws IOException {
651     return create(in.readInt(), in);
652   }
653 
654   /**
655    * Create a KeyValue reading <code>length</code> from <code>in</code>
656    * 
657    * @param length
658    * @param in
659    * @return Created KeyValue OR if we find a length of zero, we will return
660    *         null which can be useful marking a stream as done.
661    * @throws IOException
662    */
663   public static KeyValue create(int length, final DataInput in) throws IOException {
664 
665     if (length <= 0) {
666       if (length == 0)
667         return null;
668       throw new IOException("Failed read " + length + " bytes, stream corrupt?");
669     }
670 
671     // This is how the old Writables.readFrom used to deserialize. Didn't even
672     // vint.
673     byte[] bytes = new byte[length];
674     in.readFully(bytes);
675     return new KeyValue(bytes, 0, length);
676   }
677 
678   public static void oswrite(final Cell cell, final OutputStream out, final boolean withTags)
679       throws IOException {
680     if (cell instanceof Streamable) {
681       ((Streamable)cell).write(out, withTags);
682     } else {
683       short rlen = cell.getRowLength();
684       byte flen = cell.getFamilyLength();
685       int qlen = cell.getQualifierLength();
686       int vlen = cell.getValueLength();
687       int tlen = cell.getTagsLength();
688 
689       // write total length
690       KeyValue.writeInt(out, length(rlen, flen, qlen, vlen, tlen, withTags));
691       // write key length
692       KeyValue.writeInt(out, keyLength(rlen, flen, qlen));
693       // write value length
694       KeyValue.writeInt(out, vlen);
695       // Write rowkey - 2 bytes rk length followed by rowkey bytes
696       StreamUtils.writeShort(out, rlen);
697       out.write(cell.getRowArray(), cell.getRowOffset(), rlen);
698       // Write cf - 1 byte of cf length followed by the family bytes
699       out.write(flen);
700       out.write(cell.getFamilyArray(), cell.getFamilyOffset(), flen);
701       // write qualifier
702       out.write(cell.getQualifierArray(), cell.getQualifierOffset(), qlen);
703       // write timestamp
704       StreamUtils.writeLong(out, cell.getTimestamp());
705       // write the type
706       out.write(cell.getTypeByte());
707       // write value
708       out.write(cell.getValueArray(), cell.getValueOffset(), vlen);
709       // write tags if we have to
710       if (withTags && tlen > 0) {
711         // 2 bytes tags length followed by tags bytes
712         // tags length is serialized with 2 bytes only(short way) even if the
713         // type is int. As this
714         // is non -ve numbers, we save the sign bit. See HBASE-11437
715         out.write((byte) (0xff & (tlen >> 8)));
716         out.write((byte) (0xff & tlen));
717         out.write(cell.getTagsArray(), cell.getTagsOffset(), tlen);
718       }
719     }
720   }
721 }