View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to you under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14   * License for the specific language governing permissions and limitations
15   * under the License.
16   */
17  package org.apache.hadoop.hbase.util;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.DataInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.math.BigDecimal;
25  import java.math.BigInteger;
26  import java.nio.ByteBuffer;
27  
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.classification.InterfaceStability;
30  import org.apache.hadoop.io.IOUtils;
31  import org.apache.hadoop.io.WritableUtils;
32  
33  import sun.nio.ch.DirectBuffer;
34  
35  /**
36   * Utility functions for working with byte buffers, such as reading/writing
37   * variable-length long numbers.
38   */
39  @InterfaceAudience.Public
40  @InterfaceStability.Evolving
41  public final class ByteBufferUtils {
42  
43    // "Compressed integer" serialization helper constants.
44    public final static int VALUE_MASK = 0x7f;
45    public final static int NEXT_BIT_SHIFT = 7;
46    public final static int NEXT_BIT_MASK = 1 << 7;
47  
48    private ByteBufferUtils() {
49    }
50  
51    /**
52     * Similar to {@link WritableUtils#writeVLong(java.io.DataOutput, long)},
53     * but writes to a {@link ByteBuffer}.
54     */
55    public static void writeVLong(ByteBuffer out, long i) {
56      if (i >= -112 && i <= 127) {
57        out.put((byte) i);
58        return;
59      }
60  
61      int len = -112;
62      if (i < 0) {
63        i ^= -1L; // take one's complement
64        len = -120;
65      }
66  
67      long tmp = i;
68      while (tmp != 0) {
69        tmp = tmp >> 8;
70        len--;
71      }
72  
73      out.put((byte) len);
74  
75      len = (len < -120) ? -(len + 120) : -(len + 112);
76  
77      for (int idx = len; idx != 0; idx--) {
78        int shiftbits = (idx - 1) * 8;
79        long mask = 0xFFL << shiftbits;
80        out.put((byte) ((i & mask) >> shiftbits));
81      }
82    }
83  
84    /**
85     * Similar to {@link WritableUtils#readVLong(DataInput)} but reads from a
86     * {@link ByteBuffer}.
87     */
88    public static long readVLong(ByteBuffer in) {
89      byte firstByte = in.get();
90      int len = WritableUtils.decodeVIntSize(firstByte);
91      if (len == 1) {
92        return firstByte;
93      }
94      long i = 0;
95      for (int idx = 0; idx < len-1; idx++) {
96        byte b = in.get();
97        i = i << 8;
98        i = i | (b & 0xFF);
99      }
100     return (WritableUtils.isNegativeVInt(firstByte) ? (i ^ -1L) : i);
101   }
102 
103 
104   /**
105    * Put in buffer integer using 7 bit encoding. For each written byte:
106    * 7 bits are used to store value
107    * 1 bit is used to indicate whether there is next bit.
108    * @param value Int to be compressed.
109    * @param out Where to put compressed data
110    * @return Number of bytes written.
111    * @throws IOException on stream error
112    */
113    public static int putCompressedInt(OutputStream out, final int value)
114       throws IOException {
115     int i = 0;
116     int tmpvalue = value;
117     do {
118       byte b = (byte) (tmpvalue & VALUE_MASK);
119       tmpvalue >>>= NEXT_BIT_SHIFT;
120       if (tmpvalue != 0) {
121         b |= (byte) NEXT_BIT_MASK;
122       }
123       out.write(b);
124       i++;
125     } while (tmpvalue != 0);
126     return i;
127   }
128 
129    /**
130     * Put in output stream 32 bit integer (Big Endian byte order).
131     * @param out Where to put integer.
132     * @param value Value of integer.
133     * @throws IOException On stream error.
134     */
135    public static void putInt(OutputStream out, final int value)
136        throws IOException {
137      for (int i = Bytes.SIZEOF_INT - 1; i >= 0; --i) {
138        out.write((byte) (value >>> (i * 8)));
139      }
140    }
141 
142   public static byte toByte(ByteBuffer buffer, int offset) {
143     if (UnsafeAccess.isAvailable()) {
144       return UnsafeAccess.toByte(buffer, offset);
145     } else {
146       return buffer.get(offset);
147     }
148   }
149 
150   /**
151    * Copy the data to the output stream and update position in buffer.
152    * @param out the stream to write bytes to
153    * @param in the buffer to read bytes from
154    * @param length the number of bytes to copy
155    */
156   public static void moveBufferToStream(OutputStream out, ByteBuffer in,
157       int length) throws IOException {
158     copyBufferToStream(out, in, in.position(), length);
159     skip(in, length);
160   }
161 
162   /**
163    * Copy data from a buffer to an output stream. Does not update the position
164    * in the buffer.
165    * @param out the stream to write bytes to
166    * @param in the buffer to read bytes from
167    * @param offset the offset in the buffer (from the buffer's array offset)
168    *      to start copying bytes from
169    * @param length the number of bytes to copy
170    */
171   public static void copyBufferToStream(OutputStream out, ByteBuffer in,
172       int offset, int length) throws IOException {
173     if (in.hasArray()) {
174       out.write(in.array(), in.arrayOffset() + offset,
175           length);
176     } else {
177       for (int i = 0; i < length; ++i) {
178         out.write(in.get(offset + i));
179       }
180     }
181   }
182 
183   public static int putLong(OutputStream out, final long value,
184       final int fitInBytes) throws IOException {
185     long tmpValue = value;
186     for (int i = 0; i < fitInBytes; ++i) {
187       out.write((byte) (tmpValue & 0xff));
188       tmpValue >>>= 8;
189     }
190     return fitInBytes;
191   }
192 
193   public static int putByte(ByteBuffer buffer, int offset, byte b) {
194     if (UnsafeAccess.isAvailable()) {
195       return UnsafeAccess.putByte(buffer, offset, b);
196     } else {
197       buffer.put(offset, b);
198       return offset + 1;
199     }
200   }
201 
202   /**
203    * Check how many bytes are required to store value.
204    * @param value Value which size will be tested.
205    * @return How many bytes are required to store value.
206    */
207   public static int longFitsIn(final long value) {
208     if (value < 0) {
209       return 8;
210     }
211 
212     if (value < (1l << 4 * 8)) {
213       // no more than 4 bytes
214       if (value < (1l << 2 * 8)) {
215         if (value < (1l << 1 * 8)) {
216           return 1;
217         }
218         return 2;
219       }
220       if (value < (1l << 3 * 8)) {
221         return 3;
222       }
223       return 4;
224     }
225     // more than 4 bytes
226     if (value < (1l << 6 * 8)) {
227       if (value < (1l << 5 * 8)) {
228         return 5;
229       }
230       return 6;
231     }
232     if (value < (1l << 7 * 8)) {
233       return 7;
234     }
235     return 8;
236   }
237 
238   /**
239    * Check how many bytes is required to store value.
240    * @param value Value which size will be tested.
241    * @return How many bytes are required to store value.
242    */
243   public static int intFitsIn(final int value) {
244     if (value < 0) {
245       return 4;
246     }
247 
248     if (value < (1 << 2 * 8)) {
249       if (value < (1 << 1 * 8)) {
250         return 1;
251       }
252       return 2;
253     }
254     if (value <= (1 << 3 * 8)) {
255       return 3;
256     }
257     return 4;
258   }
259 
260   /**
261    * Read integer from stream coded in 7 bits and increment position.
262    * @return the integer that has been read
263    * @throws IOException
264    */
265   public static int readCompressedInt(InputStream input)
266       throws IOException {
267     int result = 0;
268     int i = 0;
269     byte b;
270     do {
271       b = (byte) input.read();
272       result += (b & VALUE_MASK) << (NEXT_BIT_SHIFT * i);
273       i++;
274       if (i > Bytes.SIZEOF_INT + 1) {
275         throw new IllegalStateException(
276             "Corrupted compressed int (too long: " + (i + 1) + " bytes)");
277       }
278     } while (0 != (b & NEXT_BIT_MASK));
279     return result;
280   }
281 
282   /**
283    * Read integer from buffer coded in 7 bits and increment position.
284    * @return Read integer.
285    */
286   public static int readCompressedInt(ByteBuffer buffer) {
287     byte b = buffer.get();
288     if ((b & NEXT_BIT_MASK) != 0) {
289       return (b & VALUE_MASK) + (readCompressedInt(buffer) << NEXT_BIT_SHIFT);
290     }
291     return b & VALUE_MASK;
292   }
293 
294   /**
295    * Read long which was written to fitInBytes bytes and increment position.
296    * @param fitInBytes In how many bytes given long is stored.
297    * @return The value of parsed long.
298    * @throws IOException
299    */
300   public static long readLong(InputStream in, final int fitInBytes)
301       throws IOException {
302     long tmpLong = 0;
303     for (int i = 0; i < fitInBytes; ++i) {
304       tmpLong |= (in.read() & 0xffl) << (8 * i);
305     }
306     return tmpLong;
307   }
308 
309   /**
310    * Read long which was written to fitInBytes bytes and increment position.
311    * @param fitInBytes In how many bytes given long is stored.
312    * @return The value of parsed long.
313    */
314   public static long readLong(ByteBuffer in, final int fitInBytes) {
315     long tmpLength = 0;
316     for (int i = 0; i < fitInBytes; ++i) {
317       tmpLength |= (in.get() & 0xffl) << (8l * i);
318     }
319     return tmpLength;
320   }
321 
322   /**
323    * Copy the given number of bytes from the given stream and put it at the
324    * current position of the given buffer, updating the position in the buffer.
325    * @param out the buffer to write data to
326    * @param in the stream to read data from
327    * @param length the number of bytes to read/write
328    */
329   public static void copyFromStreamToBuffer(ByteBuffer out,
330       DataInputStream in, int length) throws IOException {
331     if (out.hasArray()) {
332       in.readFully(out.array(), out.position() + out.arrayOffset(),
333           length);
334       skip(out, length);
335     } else {
336       for (int i = 0; i < length; ++i) {
337         out.put(in.readByte());
338       }
339     }
340   }
341 
342   /**
343    * Copy from the InputStream to a new heap ByteBuffer until the InputStream is exhausted.
344    */
345   public static ByteBuffer drainInputStreamToBuffer(InputStream is) throws IOException {
346     ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
347     IOUtils.copyBytes(is, baos, 4096, true);
348     ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
349     buffer.rewind();
350     return buffer;
351   }
352 
353   /**
354    * Copy one buffer's whole data to another. Write starts at the current position of 'out' buffer.
355    * Note : This will advance the position marker of {@code out} but not change the position maker
356    * for {@code in}. The position and limit of the {@code in} buffer to be set properly by caller.
357    * @param in source buffer
358    * @param out destination buffer
359    */
360   public static void copyFromBufferToBuffer(ByteBuffer in, ByteBuffer out) {
361     if (UnsafeAccess.isAvailable()) {
362       int length = in.remaining();
363       UnsafeAccess.copy(in, in.position(), out, out.position(), length);
364       out.position(out.position() + length);
365     } else {
366       out.put(in);
367     }
368   }
369 
370   /**
371    * Copy from one buffer to another from given offset. This will be absolute positional copying and
372    * won't affect the position of any of the buffers.
373    * @param in
374    * @param out
375    * @param sourceOffset
376    * @param destinationOffset
377    * @param length
378    */
379   public static int copyFromBufferToBuffer(ByteBuffer in, ByteBuffer out, int sourceOffset,
380       int destinationOffset, int length) {
381     if (in.hasArray() && out.hasArray()) {
382       System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out.array(), out.arrayOffset()
383           + destinationOffset, length);
384     } else if (UnsafeAccess.isAvailable()) {
385       UnsafeAccess.copy(in, sourceOffset, out, destinationOffset, length);
386     } else {
387       for (int i = 0; i < length; ++i) {
388         putByte(out, destinationOffset + i, toByte(in, sourceOffset + i));
389       }
390     }
391     return destinationOffset + length;
392   }
393 
394   /**
395    * Copy from one buffer to another from given offset.
396    * <p>
397    * Note : This will advance the position marker of {@code out} but not change the position maker
398    * for {@code in}
399    * @param in source buffer
400    * @param out destination buffer
401    * @param sourceOffset offset in the source buffer
402    * @param length how many bytes to copy
403    */
404   public static void copyFromBufferToBuffer(ByteBuffer in,
405       ByteBuffer out, int sourceOffset, int length) {
406     if (in.hasArray() && out.hasArray()) {
407       System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out.array(), out.position()
408           + out.arrayOffset(), length);
409     } else if (UnsafeAccess.isAvailable()) {
410       UnsafeAccess.copy(in, sourceOffset, out, out.position(), length);
411     } else {
412       int destOffset = out.position();
413       for (int i = 0; i < length; ++i) {
414         putByte(out, destOffset + i, toByte(in, sourceOffset + i));
415       }
416     }
417     skip(out, length);
418   }
419 
420   /**
421    * Find length of common prefix of two parts in the buffer
422    * @param buffer Where parts are located.
423    * @param offsetLeft Offset of the first part.
424    * @param offsetRight Offset of the second part.
425    * @param limit Maximal length of common prefix.
426    * @return Length of prefix.
427    */
428   public static int findCommonPrefix(ByteBuffer buffer, int offsetLeft,
429       int offsetRight, int limit) {
430     int prefix = 0;
431 
432     for (; prefix < limit; ++prefix) {
433       if (buffer.get(offsetLeft + prefix) != buffer.get(offsetRight + prefix)) {
434         break;
435       }
436     }
437 
438     return prefix;
439   }
440 
441   /**
442    * Find length of common prefix in two arrays.
443    * @param left Array to be compared.
444    * @param leftOffset Offset in left array.
445    * @param leftLength Length of left array.
446    * @param right Array to be compared.
447    * @param rightOffset Offset in right array.
448    * @param rightLength Length of right array.
449    */
450   public static int findCommonPrefix(
451       byte[] left, int leftOffset, int leftLength,
452       byte[] right, int rightOffset, int rightLength) {
453     int length = Math.min(leftLength, rightLength);
454     int result = 0;
455 
456     while (result < length &&
457         left[leftOffset + result] == right[rightOffset + result]) {
458       result++;
459     }
460 
461     return result;
462   }
463 
464   /**
465    * Check whether two parts in the same buffer are equal.
466    * @param buffer In which buffer there are parts
467    * @param offsetLeft Beginning of first part.
468    * @param lengthLeft Length of the first part.
469    * @param offsetRight Beginning of the second part.
470    * @param lengthRight Length of the second part.
471    * @return True if equal
472    */
473   public static boolean arePartsEqual(ByteBuffer buffer,
474       int offsetLeft, int lengthLeft,
475       int offsetRight, int lengthRight) {
476     if (lengthLeft != lengthRight) {
477       return false;
478     }
479 
480     if (buffer.hasArray()) {
481       return 0 == Bytes.compareTo(
482           buffer.array(), buffer.arrayOffset() + offsetLeft, lengthLeft,
483           buffer.array(), buffer.arrayOffset() + offsetRight, lengthRight);
484     }
485 
486     for (int i = 0; i < lengthRight; ++i) {
487       if (buffer.get(offsetLeft + i) != buffer.get(offsetRight + i)) {
488         return false;
489       }
490     }
491     return true;
492   }
493 
494   /**
495    * Increment position in buffer.
496    * @param buffer In this buffer.
497    * @param length By that many bytes.
498    */
499   public static void skip(ByteBuffer buffer, int length) {
500     buffer.position(buffer.position() + length);
501   }
502 
503   public static void extendLimit(ByteBuffer buffer, int numBytes) {
504     buffer.limit(buffer.limit() + numBytes);
505   }
506 
507   /**
508    * Copy the bytes from position to limit into a new byte[] of the exact length and sets the
509    * position and limit back to their original values (though not thread safe).
510    * @param buffer copy from here
511    * @param startPosition put buffer.get(startPosition) into byte[0]
512    * @return a new byte[] containing the bytes in the specified range
513    */
514   public static byte[] toBytes(ByteBuffer buffer, int startPosition) {
515     int originalPosition = buffer.position();
516     byte[] output = new byte[buffer.limit() - startPosition];
517     buffer.position(startPosition);
518     buffer.get(output);
519     buffer.position(originalPosition);
520     return output;
521   }
522 
523   /**
524    * Copy the given number of bytes from specified offset into a new byte[]
525    * @param buffer
526    * @param offset
527    * @param length
528    * @return a new byte[] containing the bytes in the specified range
529    */
530   public static byte[] toBytes(ByteBuffer buffer, int offset, int length) {
531     byte[] output = new byte[length];
532     for (int i = 0; i < length; i++) {
533       output[i] = buffer.get(offset + i);
534     }
535     return output;
536   }
537 
538   public static int compareTo(ByteBuffer buf1, int o1, int l1, ByteBuffer buf2, int o2, int l2) {
539     if (UnsafeAccess.isAvailable()) {
540       long offset1Adj, offset2Adj;
541       Object refObj1 = null, refObj2 = null;
542       if (buf1.isDirect()) {
543         offset1Adj = o1 + ((DirectBuffer) buf1).address();
544       } else {
545         offset1Adj = o1 + buf1.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
546         refObj1 = buf1.array();
547       }
548       if (buf2.isDirect()) {
549         offset2Adj = o2 + ((DirectBuffer) buf2).address();
550       } else {
551         offset2Adj = o2 + buf2.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
552         refObj2 = buf2.array();
553       }
554       return compareToUnsafe(refObj1, offset1Adj, l1, refObj2, offset2Adj, l2);
555     }
556     int end1 = o1 + l1;
557     int end2 = o2 + l2;
558     for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
559       int a = buf1.get(i) & 0xFF;
560       int b = buf2.get(j) & 0xFF;
561       if (a != b) {
562         return a - b;
563       }
564     }
565     return l1 - l2;
566   }
567 
568   public static int compareTo(ByteBuffer buf1, int o1, int l1, byte[] buf2, int o2, int l2) {
569     if (UnsafeAccess.isAvailable()) {
570       long offset1Adj;
571       Object refObj1 = null;
572       if (buf1.isDirect()) {
573         offset1Adj = o1 + ((DirectBuffer) buf1).address();
574       } else {
575         offset1Adj = o1 + buf1.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
576         refObj1 = buf1.array();
577       }
578       return compareToUnsafe(refObj1, offset1Adj, l1, buf2, o2
579           + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, l2);
580     }
581     int end1 = o1 + l1;
582     int end2 = o2 + l2;
583     for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
584       int a = buf1.get(i) & 0xFF;
585       int b = buf2[j] & 0xFF;
586       if (a != b) {
587         return a - b;
588       }
589     }
590     return l1 - l2;
591   }
592 
593   static int compareToUnsafe(Object obj1, long o1, int l1, Object obj2, long o2, int l2) {
594     final int minLength = Math.min(l1, l2);
595     final int minWords = minLength / Bytes.SIZEOF_LONG;
596 
597     /*
598      * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a time is no slower than
599      * comparing 4 bytes at a time even on 32-bit. On the other hand, it is substantially faster on
600      * 64-bit.
601      */
602     int j = minWords << 3; // Same as minWords * SIZEOF_LONG
603     for (int i = 0; i < j; i += Bytes.SIZEOF_LONG) {
604       long lw = UnsafeAccess.theUnsafe.getLong(obj1, o1 + i);
605       long rw = UnsafeAccess.theUnsafe.getLong(obj2, o2 + i);
606       long diff = lw ^ rw;
607       if (diff != 0) {
608         return lessThanUnsignedLong(lw, rw) ? -1 : 1;
609       }
610     }
611     int offset = j;
612 
613     if (minLength - offset >= Bytes.SIZEOF_INT) {
614       int il = UnsafeAccess.theUnsafe.getInt(obj1, o1 + offset);
615       int ir = UnsafeAccess.theUnsafe.getInt(obj2, o2 + offset);
616       if (il != ir) {
617         return lessThanUnsignedInt(il, ir) ? -1 : 1;
618       }
619       offset += Bytes.SIZEOF_INT;
620     }
621     if (minLength - offset >= Bytes.SIZEOF_SHORT) {
622       short sl = UnsafeAccess.theUnsafe.getShort(obj1, o1 + offset);
623       short sr = UnsafeAccess.theUnsafe.getShort(obj2, o2 + offset);
624       if (sl != sr) {
625         return lessThanUnsignedShort(sl, sr) ? -1 : 1;
626       }
627       offset += Bytes.SIZEOF_SHORT;
628     }
629     if (minLength - offset == 1) {
630       int a = (UnsafeAccess.theUnsafe.getByte(obj1, o1 + offset) & 0xff);
631       int b = (UnsafeAccess.theUnsafe.getByte(obj2, o2 + offset) & 0xff);
632       if (a != b) {
633         return a - b;
634       }
635     }
636     return l1 - l2;
637   }
638 
639   /*
640    * Both values are passed as is read by Unsafe. When platform is Little Endian, have to convert
641    * to corresponding Big Endian value and then do compare. We do all writes in Big Endian format.
642    */
643   private static boolean lessThanUnsignedLong(long x1, long x2) {
644     if (UnsafeAccess.littleEndian) {
645       x1 = Long.reverseBytes(x1);
646       x2 = Long.reverseBytes(x2);
647     }
648     return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE);
649   }
650 
651   /*
652    * Both values are passed as is read by Unsafe. When platform is Little Endian, have to convert
653    * to corresponding Big Endian value and then do compare. We do all writes in Big Endian format.
654    */
655   private static boolean lessThanUnsignedInt(int x1, int x2) {
656     if (UnsafeAccess.littleEndian) {
657       x1 = Integer.reverseBytes(x1);
658       x2 = Integer.reverseBytes(x2);
659     }
660     return (x1 & 0xffffffffL) < (x2 & 0xffffffffL);
661   }
662 
663   /*
664    * Both values are passed as is read by Unsafe. When platform is Little Endian, have to convert
665    * to corresponding Big Endian value and then do compare. We do all writes in Big Endian format.
666    */
667   private static boolean lessThanUnsignedShort(short x1, short x2) {
668     if (UnsafeAccess.littleEndian) {
669       x1 = Short.reverseBytes(x1);
670       x2 = Short.reverseBytes(x2);
671     }
672     return (x1 & 0xffff) < (x2 & 0xffff);
673   }
674 
675   /**
676    * Reads a short value at the given buffer's offset.
677    * @param buffer
678    * @param offset
679    * @return short value at offset
680    */
681   public static short toShort(ByteBuffer buffer, int offset) {
682     if (UnsafeAccess.isAvailable()) {
683       return UnsafeAccess.toShort(buffer, offset);
684     } else {
685       return buffer.getShort(offset);
686     }
687   }
688 
689   /**
690    * Reads an int value at the given buffer's offset.
691    * @param buffer
692    * @param offset
693    * @return int value at offset
694    */
695   public static int toInt(ByteBuffer buffer, int offset) {
696     if (UnsafeAccess.isAvailable()) {
697       return UnsafeAccess.toInt(buffer, offset);
698     } else {
699       return buffer.getInt(offset);
700     }
701   }
702 
703   /**
704    * Reads a long value at the given buffer's offset.
705    * @param buffer
706    * @param offset
707    * @return long value at offset
708    */
709   public static long toLong(ByteBuffer buffer, int offset) {
710     if (UnsafeAccess.isAvailable()) {
711       return UnsafeAccess.toLong(buffer, offset);
712     } else {
713       return buffer.getLong(offset);
714     }
715   }
716 
717   /**
718    * Put an int value out to the given ByteBuffer's current position in big-endian format.
719    * This also advances the position in buffer by int size.
720    * @param buffer the ByteBuffer to write to
721    * @param val int to write out
722    */
723   public static void putInt(ByteBuffer buffer, int val) {
724     if (UnsafeAccess.isAvailable()) {
725       int newPos = UnsafeAccess.putInt(buffer, buffer.position(), val);
726       buffer.position(newPos);
727     } else {
728       buffer.putInt(val);
729     }
730   }
731 
732   /**
733    * Reads a double value at the given buffer's offset.
734    * @param buffer
735    * @param offset offset where double is
736    * @return double value at offset
737    */
738   public static double toDouble(ByteBuffer buffer, int offset) {
739     return Double.longBitsToDouble(toLong(buffer, offset));
740   }
741 
742   /**
743    * Reads a BigDecimal value at the given buffer's offset.
744    * @param buffer
745    * @param offset
746    * @return BigDecimal value at offset
747    */
748   public static BigDecimal toBigDecimal(ByteBuffer buffer, int offset, int length) {
749     if (buffer == null || length < Bytes.SIZEOF_INT + 1 ||
750       (offset + length > buffer.limit())) {
751       return null;
752     }
753 
754     int scale = toInt(buffer, offset);
755     byte[] tcBytes = new byte[length - Bytes.SIZEOF_INT];
756     copyFromBufferToArray(tcBytes, buffer, offset + Bytes.SIZEOF_INT, 0, length - Bytes.SIZEOF_INT);
757     return new BigDecimal(new BigInteger(tcBytes), scale);
758   }
759 
760   /**
761    * Put a short value out to the given ByteBuffer's current position in big-endian format.
762    * This also advances the position in buffer by short size.
763    * @param buffer the ByteBuffer to write to
764    * @param val short to write out
765    */
766   public static void putShort(ByteBuffer buffer, short val) {
767     if (UnsafeAccess.isAvailable()) {
768       int newPos = UnsafeAccess.putShort(buffer, buffer.position(), val);
769       buffer.position(newPos);
770     } else {
771       buffer.putShort(val);
772     }
773   }
774 
775   /**
776    * Put a long value out to the given ByteBuffer's current position in big-endian format.
777    * This also advances the position in buffer by long size.
778    * @param buffer the ByteBuffer to write to
779    * @param val long to write out
780    */
781   public static void putLong(ByteBuffer buffer, long val) {
782     if (UnsafeAccess.isAvailable()) {
783       int newPos = UnsafeAccess.putLong(buffer, buffer.position(), val);
784       buffer.position(newPos);
785     } else {
786       buffer.putLong(val);
787     }
788   }
789   /**
790    * Copies the bytes from given array's offset to length part into the given buffer. Puts the bytes
791    * to buffer's current position. This also advances the position in the 'out' buffer by 'length'
792    * @param out
793    * @param in
794    * @param inOffset
795    * @param length
796    */
797   public static void copyFromArrayToBuffer(ByteBuffer out, byte[] in, int inOffset, int length) {
798     if (out.hasArray()) {
799       System.arraycopy(in, inOffset, out.array(), out.arrayOffset() + out.position(), length);
800       // Move the position in out by length
801       out.position(out.position() + length);
802     } else if (UnsafeAccess.isAvailable()) {
803       UnsafeAccess.copy(in, inOffset, out, out.position(), length);
804       // Move the position in out by length
805       out.position(out.position() + length);
806     } else {
807       out.put(in, inOffset, length);
808     }
809   }
810 
811   /**
812    * Copies specified number of bytes from given offset of 'in' ByteBuffer to
813    * the array.
814    * @param out
815    * @param in
816    * @param sourceOffset
817    * @param destinationOffset
818    * @param length
819    */
820   public static void copyFromBufferToArray(byte[] out, ByteBuffer in, int sourceOffset,
821       int destinationOffset, int length) {
822     if (in.hasArray()) {
823       System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out, destinationOffset, length);
824     } else if (UnsafeAccess.isAvailable()) {
825       UnsafeAccess.copy(in, sourceOffset, out, destinationOffset, length);
826     } else {
827       for (int i = 0; i < length; i++) {
828         out[destinationOffset + i] = in.get(sourceOffset + i);
829       }
830     }
831   }
832 }