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.DataInput;
21  import java.io.DataInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.math.BigDecimal;
26  import java.math.BigInteger;
27  import java.nio.ByteBuffer;
28  import java.util.Arrays;
29
30  import org.apache.hadoop.hbase.classification.InterfaceAudience;
31  import org.apache.hadoop.hbase.classification.InterfaceStability;
32  import org.apache.hadoop.hbase.io.ByteBufferSupportOutputStream;
33  import org.apache.hadoop.hbase.io.util.StreamUtils;
34  import org.apache.hadoop.io.IOUtils;
35  import org.apache.hadoop.io.WritableUtils;
36
37  import sun.nio.ch.DirectBuffer;
38
39  /**
40   * Utility functions for working with byte buffers, such as reading/writing
41   * variable-length long numbers.
42   */
43  @SuppressWarnings("restriction")
44  @InterfaceAudience.Public
45  @InterfaceStability.Evolving
46  public final class ByteBufferUtils {
47
48    // "Compressed integer" serialization helper constants.
49    public final static int VALUE_MASK = 0x7f;
50    public final static int NEXT_BIT_SHIFT = 7;
51    public final static int NEXT_BIT_MASK = 1 << 7;
52    private static final boolean UNSAFE_AVAIL = UnsafeAvailChecker.isAvailable();
53    private static final boolean UNSAFE_UNALIGNED = UnsafeAvailChecker.unaligned();
54
55    private ByteBufferUtils() {
56    }
57
58    /**
59     * Similar to {@link WritableUtils#writeVLong(java.io.DataOutput, long)},
60     * but writes to a {@link ByteBuffer}.
61     */
62    public static void writeVLong(ByteBuffer out, long i) {
63      if (i >= -112 && i <= 127) {
64        out.put((byte) i);
65        return;
66      }
67
68      int len = -112;
69      if (i < 0) {
70        i ^= -1L; // take one's complement
71        len = -120;
72      }
73
74      long tmp = i;
75      while (tmp != 0) {
76        tmp = tmp >> 8;
77        len--;
78      }
79
80      out.put((byte) len);
81
82      len = (len < -120) ? -(len + 120) : -(len + 112);
83
84      for (int idx = len; idx != 0; idx--) {
85        int shiftbits = (idx - 1) * 8;
86        long mask = 0xFFL << shiftbits;
87        out.put((byte) ((i & mask) >> shiftbits));
88      }
89    }
90
91    /**
92     * Similar to {@link WritableUtils#readVLong(DataInput)} but reads from a
93     * {@link ByteBuffer}.
94     */
95    public static long readVLong(ByteBuffer in) {
96      byte firstByte = in.get();
97      int len = WritableUtils.decodeVIntSize(firstByte);
98      if (len == 1) {
99        return firstByte;
100     }
101     long i = 0;
102     for (int idx = 0; idx < len-1; idx++) {
103       byte b = in.get();
104       i = i << 8;
105       i = i | (b & 0xFF);
106     }
107     return (WritableUtils.isNegativeVInt(firstByte) ? (i ^ -1L) : i);
108   }
109
110
111   /**
112    * Put in buffer integer using 7 bit encoding. For each written byte:
113    * 7 bits are used to store value
114    * 1 bit is used to indicate whether there is next bit.
115    * @param value Int to be compressed.
116    * @param out Where to put compressed data
117    * @return Number of bytes written.
118    * @throws IOException on stream error
119    */
120    public static int putCompressedInt(OutputStream out, final int value)
121       throws IOException {
122     int i = 0;
123     int tmpvalue = value;
124     do {
125       byte b = (byte) (tmpvalue & VALUE_MASK);
126       tmpvalue >>>= NEXT_BIT_SHIFT;
127       if (tmpvalue != 0) {
128         b |= (byte) NEXT_BIT_MASK;
129       }
130       out.write(b);
131       i++;
132     } while (tmpvalue != 0);
133     return i;
134   }
135
136    /**
137     * Put in output stream 32 bit integer (Big Endian byte order).
138     * @param out Where to put integer.
139     * @param value Value of integer.
140     * @throws IOException On stream error.
141     */
142    public static void putInt(OutputStream out, final int value)
143        throws IOException {
144      // We have writeInt in ByteBufferOutputStream so that it can directly write
145      // int to underlying
146      // ByteBuffer in one step.
147      if (out instanceof ByteBufferSupportOutputStream) {
148        ((ByteBufferSupportOutputStream) out).writeInt(value);
149      } else {
150        StreamUtils.writeInt(out, value);
151      }
152    }
153
154   public static byte toByte(ByteBuffer buffer, int offset) {
155     if (UNSAFE_AVAIL) {
156       return UnsafeAccess.toByte(buffer, offset);
157     } else {
158       return buffer.get(offset);
159     }
160   }
161
162   /**
163    * Copy the data to the output stream and update position in buffer.
164    * @param out the stream to write bytes to
165    * @param in the buffer to read bytes from
166    * @param length the number of bytes to copy
167    */
168   public static void moveBufferToStream(OutputStream out, ByteBuffer in,
169       int length) throws IOException {
170     copyBufferToStream(out, in, in.position(), length);
171     skip(in, length);
172   }
173
174   /**
175    * Copy data from a buffer to an output stream. Does not update the position
176    * in the buffer.
177    * @param out the stream to write bytes to
178    * @param in the buffer to read bytes from
179    * @param offset the offset in the buffer (from the buffer's array offset)
180    *      to start copying bytes from
181    * @param length the number of bytes to copy
182    */
183   public static void copyBufferToStream(OutputStream out, ByteBuffer in,
184       int offset, int length) throws IOException {
185     if (out instanceof ByteBufferSupportOutputStream) {
186       ((ByteBufferSupportOutputStream) out).write(in, offset, length);
187     } else if (in.hasArray()) {
188       out.write(in.array(), in.arrayOffset() + offset, length);
189     } else {
190       for (int i = 0; i < length; ++i) {
191         out.write(toByte(in, offset + i));
192       }
193     }
194   }
195
196   public static int putLong(OutputStream out, final long value,
197       final int fitInBytes) throws IOException {
198     long tmpValue = value;
199     for (int i = 0; i < fitInBytes; ++i) {
200       out.write((byte) (tmpValue & 0xff));
201       tmpValue >>>= 8;
202     }
203     return fitInBytes;
204   }
205
206   public static int putByte(ByteBuffer buffer, int offset, byte b) {
207     if (UNSAFE_AVAIL) {
208       return UnsafeAccess.putByte(buffer, offset, b);
209     } else {
210       buffer.put(offset, b);
211       return offset + 1;
212     }
213   }
214
215   /**
216    * Check how many bytes are required to store value.
217    * @param value Value which size will be tested.
218    * @return How many bytes are required to store value.
219    */
220   public static int longFitsIn(final long value) {
221     if (value < 0) {
222       return 8;
223     }
224
225     if (value < (1l << 4 * 8)) {
226       // no more than 4 bytes
227       if (value < (1l << 2 * 8)) {
228         if (value < (1l << 1 * 8)) {
229           return 1;
230         }
231         return 2;
232       }
233       if (value < (1l << 3 * 8)) {
234         return 3;
235       }
236       return 4;
237     }
238     // more than 4 bytes
239     if (value < (1l << 6 * 8)) {
240       if (value < (1l << 5 * 8)) {
241         return 5;
242       }
243       return 6;
244     }
245     if (value < (1l << 7 * 8)) {
246       return 7;
247     }
248     return 8;
249   }
250
251   /**
252    * Check how many bytes is required to store value.
253    * @param value Value which size will be tested.
254    * @return How many bytes are required to store value.
255    */
256   public static int intFitsIn(final int value) {
257     if (value < 0) {
258       return 4;
259     }
260
261     if (value < (1 << 2 * 8)) {
262       if (value < (1 << 1 * 8)) {
263         return 1;
264       }
265       return 2;
266     }
267     if (value <= (1 << 3 * 8)) {
268       return 3;
269     }
270     return 4;
271   }
272
273   /**
274    * Read integer from stream coded in 7 bits and increment position.
275    * @return the integer that has been read
276    * @throws IOException
277    */
278   public static int readCompressedInt(InputStream input)
279       throws IOException {
280     int result = 0;
281     int i = 0;
282     byte b;
283     do {
284       b = (byte) input.read();
285       result += (b & VALUE_MASK) << (NEXT_BIT_SHIFT * i);
286       i++;
287       if (i > Bytes.SIZEOF_INT + 1) {
288         throw new IllegalStateException(
289             "Corrupted compressed int (too long: " + (i + 1) + " bytes)");
290       }
291     } while (0 != (b & NEXT_BIT_MASK));
292     return result;
293   }
294
295   /**
296    * Read integer from buffer coded in 7 bits and increment position.
297    * @return Read integer.
298    */
299   public static int readCompressedInt(ByteBuffer buffer) {
300     byte b = buffer.get();
301     if ((b & NEXT_BIT_MASK) != 0) {
302       return (b & VALUE_MASK) + (readCompressedInt(buffer) << NEXT_BIT_SHIFT);
303     }
304     return b & VALUE_MASK;
305   }
306
307   /**
308    * Read long which was written to fitInBytes bytes and increment position.
309    * @param fitInBytes In how many bytes given long is stored.
310    * @return The value of parsed long.
311    * @throws IOException
312    */
313   public static long readLong(InputStream in, final int fitInBytes)
314       throws IOException {
315     long tmpLong = 0;
316     for (int i = 0; i < fitInBytes; ++i) {
317       tmpLong |= (in.read() & 0xffl) << (8 * i);
318     }
319     return tmpLong;
320   }
321
322   /**
323    * Read long which was written to fitInBytes bytes and increment position.
324    * @param fitInBytes In how many bytes given long is stored.
325    * @return The value of parsed long.
326    */
327   public static long readLong(ByteBuffer in, final int fitInBytes) {
328     long tmpLength = 0;
329     for (int i = 0; i < fitInBytes; ++i) {
330       tmpLength |= (in.get() & 0xffl) << (8l * i);
331     }
332     return tmpLength;
333   }
334
335   /**
336    * Copy the given number of bytes from the given stream and put it at the
337    * current position of the given buffer, updating the position in the buffer.
338    * @param out the buffer to write data to
339    * @param in the stream to read data from
340    * @param length the number of bytes to read/write
341    */
342   public static void copyFromStreamToBuffer(ByteBuffer out,
343       DataInputStream in, int length) throws IOException {
344     if (out.hasArray()) {
345       in.readFully(out.array(), out.position() + out.arrayOffset(),
346           length);
347       skip(out, length);
348     } else {
349       for (int i = 0; i < length; ++i) {
350         out.put(in.readByte());
351       }
352     }
353   }
354
355   /**
356    * Copy from the InputStream to a new heap ByteBuffer until the InputStream is exhausted.
357    */
358   public static ByteBuffer drainInputStreamToBuffer(InputStream is) throws IOException {
359     ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
360     IOUtils.copyBytes(is, baos, 4096, true);
361     ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
362     buffer.rewind();
363     return buffer;
364   }
365
366   /**
367    * Copy one buffer's whole data to another. Write starts at the current position of 'out' buffer.
368    * Note : This will advance the position marker of {@code out} but not change the position maker
369    * for {@code in}. The position and limit of the {@code in} buffer to be set properly by caller.
370    * @param in source buffer
371    * @param out destination buffer
372    */
373   public static void copyFromBufferToBuffer(ByteBuffer in, ByteBuffer out) {
374     if (UNSAFE_AVAIL) {
375       int length = in.remaining();
376       UnsafeAccess.copy(in, in.position(), out, out.position(), length);
377       out.position(out.position() + length);
378     } else {
379       out.put(in);
380     }
381   }
382
383   /**
384    * Copy from one buffer to another from given offset. This will be absolute positional copying and
385    * won't affect the position of any of the buffers.
386    * @param in
387    * @param out
388    * @param sourceOffset
389    * @param destinationOffset
390    * @param length
391    */
392   public static int copyFromBufferToBuffer(ByteBuffer in, ByteBuffer out, int sourceOffset,
393       int destinationOffset, int length) {
394     if (in.hasArray() && out.hasArray()) {
395       System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out.array(), out.arrayOffset()
396           + destinationOffset, length);
397     } else if (UNSAFE_AVAIL) {
398       UnsafeAccess.copy(in, sourceOffset, out, destinationOffset, length);
399     } else {
400       int outOldPos = out.position();
401       out.position(destinationOffset);
402       ByteBuffer inDup = in.duplicate();
403       inDup.position(sourceOffset).limit(sourceOffset + length);
404       out.put(inDup);
405       out.position(outOldPos);
406     }
407     return destinationOffset + length;
408   }
409
410   /**
411    * Copy from one buffer to another from given offset.
412    * <p>
413    * Note : This will advance the position marker of {@code out} but not change the position maker
414    * for {@code in}
415    * @param in source buffer
416    * @param out destination buffer
417    * @param sourceOffset offset in the source buffer
418    * @param length how many bytes to copy
419    */
420   public static void copyFromBufferToBuffer(ByteBuffer in,
421       ByteBuffer out, int sourceOffset, int length) {
422     if (in.hasArray() && out.hasArray()) {
423       System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out.array(), out.position()
424           + out.arrayOffset(), length);
425       skip(out, length);
426     } else if (UNSAFE_AVAIL) {
427       UnsafeAccess.copy(in, sourceOffset, out, out.position(), length);
428       skip(out, length);
429     } else {
430       ByteBuffer inDup = in.duplicate();
431       inDup.position(sourceOffset).limit(sourceOffset + length);
432       out.put(inDup);
433     }
434   }
435
436   /**
437    * Find length of common prefix of two parts in the buffer
438    * @param buffer Where parts are located.
439    * @param offsetLeft Offset of the first part.
440    * @param offsetRight Offset of the second part.
441    * @param limit Maximal length of common prefix.
442    * @return Length of prefix.
443    */
444   public static int findCommonPrefix(ByteBuffer buffer, int offsetLeft,
445       int offsetRight, int limit) {
446     int prefix = 0;
447
448     for (; prefix < limit; ++prefix) {
449       if (buffer.get(offsetLeft + prefix) != buffer.get(offsetRight + prefix)) {
450         break;
451       }
452     }
453
454     return prefix;
455   }
456
457   /**
458    * Find length of common prefix in two arrays.
459    * @param left Array to be compared.
460    * @param leftOffset Offset in left array.
461    * @param leftLength Length of left array.
462    * @param right Array to be compared.
463    * @param rightOffset Offset in right array.
464    * @param rightLength Length of right array.
465    */
466   public static int findCommonPrefix(
467       byte[] left, int leftOffset, int leftLength,
468       byte[] right, int rightOffset, int rightLength) {
469     int length = Math.min(leftLength, rightLength);
470     int result = 0;
471
472     while (result < length &&
473         left[leftOffset + result] == right[rightOffset + result]) {
474       result++;
475     }
476
477     return result;
478   }
479
480   /**
481    * Find length of common prefix in two arrays.
482    * @param left ByteBuffer to be compared.
483    * @param leftOffset Offset in left ByteBuffer.
484    * @param leftLength Length of left ByteBuffer.
485    * @param right ByteBuffer to be compared.
486    * @param rightOffset Offset in right ByteBuffer.
487    * @param rightLength Length of right ByteBuffer.
488    */
489   public static int findCommonPrefix(ByteBuffer left, int leftOffset, int leftLength,
490       ByteBuffer right, int rightOffset, int rightLength) {
491     int length = Math.min(leftLength, rightLength);
492     int result = 0;
493
494     while (result < length && ByteBufferUtils.toByte(left, leftOffset + result) == ByteBufferUtils
495         .toByte(right, rightOffset + result)) {
496       result++;
497     }
498
499     return result;
500   }
501
502   /**
503    * Check whether two parts in the same buffer are equal.
504    * @param buffer In which buffer there are parts
505    * @param offsetLeft Beginning of first part.
506    * @param lengthLeft Length of the first part.
507    * @param offsetRight Beginning of the second part.
508    * @param lengthRight Length of the second part.
509    * @return True if equal
510    */
511   public static boolean arePartsEqual(ByteBuffer buffer,
512       int offsetLeft, int lengthLeft,
513       int offsetRight, int lengthRight) {
514     if (lengthLeft != lengthRight) {
515       return false;
516     }
517
518     if (buffer.hasArray()) {
519       return 0 == Bytes.compareTo(
520           buffer.array(), buffer.arrayOffset() + offsetLeft, lengthLeft,
521           buffer.array(), buffer.arrayOffset() + offsetRight, lengthRight);
522     }
523
524     for (int i = 0; i < lengthRight; ++i) {
525       if (buffer.get(offsetLeft + i) != buffer.get(offsetRight + i)) {
526         return false;
527       }
528     }
529     return true;
530   }
531
532   /**
533    * Increment position in buffer.
534    * @param buffer In this buffer.
535    * @param length By that many bytes.
536    */
537   public static void skip(ByteBuffer buffer, int length) {
538     buffer.position(buffer.position() + length);
539   }
540
541   public static void extendLimit(ByteBuffer buffer, int numBytes) {
542     buffer.limit(buffer.limit() + numBytes);
543   }
544
545   /**
546    * Copy the bytes from position to limit into a new byte[] of the exact length and sets the
547    * position and limit back to their original values (though not thread safe).
548    * @param buffer copy from here
549    * @param startPosition put buffer.get(startPosition) into byte[0]
550    * @return a new byte[] containing the bytes in the specified range
551    */
552   public static byte[] toBytes(ByteBuffer buffer, int startPosition) {
553     int originalPosition = buffer.position();
554     byte[] output = new byte[buffer.limit() - startPosition];
555     buffer.position(startPosition);
556     buffer.get(output);
557     buffer.position(originalPosition);
558     return output;
559   }
560
561   /**
562    * Copy the given number of bytes from specified offset into a new byte[]
563    * @param buffer
564    * @param offset
565    * @param length
566    * @return a new byte[] containing the bytes in the specified range
567    */
568   public static byte[] toBytes(ByteBuffer buffer, int offset, int length) {
569     byte[] output = new byte[length];
570     for (int i = 0; i < length; i++) {
571       output[i] = buffer.get(offset + i);
572     }
573     return output;
574   }
575
576   public static boolean equals(ByteBuffer buf1, int o1, int l1, ByteBuffer buf2, int o2, int l2) {
577     if ((l1 == 0) || (l2 == 0)) {
578       // both 0 length, return true, or else false
579       return l1 == l2;
580     }
581     // Since we're often comparing adjacent sorted data,
582     // it's usual to have equal arrays except for the very last byte
583     // so check that first
584     if (toByte(buf1, o1 + l1 - 1) != toByte(buf2, o2 + l2 - 1)) return false;
585     return compareTo(buf1, o1, l1, buf2, o2, l2) == 0;
586   }
587
588   /**
589    * @param buf
590    *          ByteBuffer to hash
591    * @param offset
592    *          offset to start from
593    * @param length
594    *          length to hash
595    */
596   public static int hashCode(ByteBuffer buf, int offset, int length) {
597     int hash = 1;
598     for (int i = offset; i < offset + length; i++) {
599       hash = (31 * hash) + (int) toByte(buf, i);
600     }
601     return hash;
602   }
603
604   public static int compareTo(ByteBuffer buf1, int o1, int l1, ByteBuffer buf2, int o2, int l2) {
605     if (UNSAFE_UNALIGNED) {
606       long offset1Adj, offset2Adj;
607       Object refObj1 = null, refObj2 = null;
608       if (buf1.isDirect()) {
609         offset1Adj = o1 + ((DirectBuffer) buf1).address();
610       } else {
611         offset1Adj = o1 + buf1.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
612         refObj1 = buf1.array();
613       }
614       if (buf2.isDirect()) {
615         offset2Adj = o2 + ((DirectBuffer) buf2).address();
616       } else {
617         offset2Adj = o2 + buf2.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
618         refObj2 = buf2.array();
619       }
620       return compareToUnsafe(refObj1, offset1Adj, l1, refObj2, offset2Adj, l2);
621     }
622     int end1 = o1 + l1;
623     int end2 = o2 + l2;
624     for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
625       int a = buf1.get(i) & 0xFF;
626       int b = buf2.get(j) & 0xFF;
627       if (a != b) {
628         return a - b;
629       }
630     }
631     return l1 - l2;
632   }
633
634   public static boolean equals(ByteBuffer buf1, int o1, int l1, byte[] buf2, int o2, int l2) {
635     if ((l1 == 0) || (l2 == 0)) {
636       // both 0 length, return true, or else false
637       return l1 == l2;
638     }
639     // Since we're often comparing adjacent sorted data,
640     // it's usual to have equal arrays except for the very last byte
641     // so check that first
642     if (toByte(buf1, o1 + l1 - 1) != buf2[o2 + l2 - 1]) return false;
643     return compareTo(buf1, o1, l1, buf2, o2, l2) == 0;
644   }
645
646   public static int compareTo(byte [] buf1, int o1, int l1, ByteBuffer buf2, int o2, int l2) {
647     // This method is nearly same as the compareTo that follows but hard sharing code given
648     // byte array and bytebuffer types and this is a hot code path
649     if (UNSAFE_UNALIGNED) {
650       long offset2Adj;
651       Object refObj2 = null;
652       if (buf2.isDirect()) {
653         offset2Adj = o2 + ((DirectBuffer)buf2).address();
654       } else {
655         offset2Adj = o2 + buf2.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
656         refObj2 = buf2.array();
657       }
658       return compareToUnsafe(buf1, o1 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, l1,
659           refObj2, offset2Adj, l2);
660     }
661     int end1 = o1 + l1;
662     int end2 = o2 + l2;
663     for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
664       int a = buf1[i] & 0xFF;
665       int b = buf2.get(i) & 0xFF;
666       if (a != b) {
667         return a - b;
668       }
669     }
670     return l1 - l2;
671   }
672
673   public static int compareTo(ByteBuffer buf1, int o1, int l1, byte[] buf2, int o2, int l2) {
674     if (UNSAFE_UNALIGNED) {
675       long offset1Adj;
676       Object refObj1 = null;
677       if (buf1.isDirect()) {
678         offset1Adj = o1 + ((DirectBuffer) buf1).address();
679       } else {
680         offset1Adj = o1 + buf1.arrayOffset() + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
681         refObj1 = buf1.array();
682       }
683       return compareToUnsafe(refObj1, offset1Adj, l1,
684           buf2, o2 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, l2);
685     }
686     int end1 = o1 + l1;
687     int end2 = o2 + l2;
688     for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
689       int a = buf1.get(i) & 0xFF;
690       int b = buf2[j] & 0xFF;
691       if (a != b) {
692         return a - b;
693       }
694     }
695     return l1 - l2;
696   }
697
698   static int compareToUnsafe(Object obj1, long o1, int l1, Object obj2, long o2, int l2) {
699     final int minLength = Math.min(l1, l2);
700     final int minWords = minLength / Bytes.SIZEOF_LONG;
701
702     /*
703      * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a time is no slower than
704      * comparing 4 bytes at a time even on 32-bit. On the other hand, it is substantially faster on
705      * 64-bit.
706      */
707     int j = minWords << 3; // Same as minWords * SIZEOF_LONG
708     for (int i = 0; i < j; i += Bytes.SIZEOF_LONG) {
709       long lw = UnsafeAccess.theUnsafe.getLong(obj1, o1 + i);
710       long rw = UnsafeAccess.theUnsafe.getLong(obj2, o2 + i);
711       long diff = lw ^ rw;
712       if (diff != 0) {
713         return lessThanUnsignedLong(lw, rw) ? -1 : 1;
714       }
715     }
716     int offset = j;
717
718     if (minLength - offset >= Bytes.SIZEOF_INT) {
719       int il = UnsafeAccess.theUnsafe.getInt(obj1, o1 + offset);
720       int ir = UnsafeAccess.theUnsafe.getInt(obj2, o2 + offset);
721       if (il != ir) {
722         return lessThanUnsignedInt(il, ir) ? -1 : 1;
723       }
724       offset += Bytes.SIZEOF_INT;
725     }
726     if (minLength - offset >= Bytes.SIZEOF_SHORT) {
727       short sl = UnsafeAccess.theUnsafe.getShort(obj1, o1 + offset);
728       short sr = UnsafeAccess.theUnsafe.getShort(obj2, o2 + offset);
729       if (sl != sr) {
730         return lessThanUnsignedShort(sl, sr) ? -1 : 1;
731       }
732       offset += Bytes.SIZEOF_SHORT;
733     }
734     if (minLength - offset == 1) {
735       int a = (UnsafeAccess.theUnsafe.getByte(obj1, o1 + offset) & 0xff);
736       int b = (UnsafeAccess.theUnsafe.getByte(obj2, o2 + offset) & 0xff);
737       if (a != b) {
738         return a - b;
739       }
740     }
741     return l1 - l2;
742   }
743
744   /*
745    * Both values are passed as is read by Unsafe. When platform is Little Endian, have to convert
746    * to corresponding Big Endian value and then do compare. We do all writes in Big Endian format.
747    */
748   private static boolean lessThanUnsignedLong(long x1, long x2) {
749     if (UnsafeAccess.littleEndian) {
750       x1 = Long.reverseBytes(x1);
751       x2 = Long.reverseBytes(x2);
752     }
753     return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE);
754   }
755
756   /*
757    * Both values are passed as is read by Unsafe. When platform is Little Endian, have to convert
758    * to corresponding Big Endian value and then do compare. We do all writes in Big Endian format.
759    */
760   private static boolean lessThanUnsignedInt(int x1, int x2) {
761     if (UnsafeAccess.littleEndian) {
762       x1 = Integer.reverseBytes(x1);
763       x2 = Integer.reverseBytes(x2);
764     }
765     return (x1 & 0xffffffffL) < (x2 & 0xffffffffL);
766   }
767
768   /*
769    * Both values are passed as is read by Unsafe. When platform is Little Endian, have to convert
770    * to corresponding Big Endian value and then do compare. We do all writes in Big Endian format.
771    */
772   private static boolean lessThanUnsignedShort(short x1, short x2) {
773     if (UnsafeAccess.littleEndian) {
774       x1 = Short.reverseBytes(x1);
775       x2 = Short.reverseBytes(x2);
776     }
777     return (x1 & 0xffff) < (x2 & 0xffff);
778   }
779
780   /**
781    * Reads a short value at the given buffer's offset.
782    * @param buffer
783    * @param offset
784    * @return short value at offset
785    */
786   public static short toShort(ByteBuffer buffer, int offset) {
787     if (UNSAFE_UNALIGNED) {
788       return UnsafeAccess.toShort(buffer, offset);
789     } else {
790       return buffer.getShort(offset);
791     }
792   }
793
794   /**
795    * Reads an int value at the given buffer's current position. Also advances the buffer's position
796    */
797   public static int toInt(ByteBuffer buffer) {
798     if (UNSAFE_UNALIGNED) {
799       int i = UnsafeAccess.toInt(buffer, buffer.position());
800       buffer.position(buffer.position() + Bytes.SIZEOF_INT);
801       return i;
802     } else {
803       return buffer.getInt();
804     }
805   }
806
807   /**
808    * Reads an int value at the given buffer's offset.
809    * @param buffer
810    * @param offset
811    * @return int value at offset
812    */
813   public static int toInt(ByteBuffer buffer, int offset) {
814     if (UNSAFE_UNALIGNED) {
815       return UnsafeAccess.toInt(buffer, offset);
816     } else {
817       return buffer.getInt(offset);
818     }
819   }
820
821   /**
822    * Converts a ByteBuffer to an int value
823    *
824    * @param buf The ByteBuffer
825    * @param offset Offset to int value
826    * @param length Number of bytes used to store the int value.
827    * @return the int value
828    * @throws IllegalArgumentException
829    *           if there's not enough bytes left in the buffer after the given offset
830    */
831   public static int readAsInt(ByteBuffer buf, int offset, final int length) {
832     if (offset + length > buf.limit()) {
833       throw new IllegalArgumentException("offset (" + offset + ") + length (" + length
834           + ") exceed the" + " limit of the buffer: " + buf.limit());
835     }
836     int n = 0;
837     for(int i = offset; i < (offset + length); i++) {
838       n <<= 8;
839       n ^= toByte(buf, i) & 0xFF;
840     }
841     return n;
842   }
843
844   /**
845    * Reads a long value at the given buffer's offset.
846    * @param buffer
847    * @param offset
848    * @return long value at offset
849    */
850   public static long toLong(ByteBuffer buffer, int offset) {
851     if (UNSAFE_UNALIGNED) {
852       return UnsafeAccess.toLong(buffer, offset);
853     } else {
854       return buffer.getLong(offset);
855     }
856   }
857
858   /**
859    * Put an int value out to the given ByteBuffer's current position in big-endian format.
860    * This also advances the position in buffer by int size.
861    * @param buffer the ByteBuffer to write to
862    * @param val int to write out
863    */
864   public static void putInt(ByteBuffer buffer, int val) {
865     if (UNSAFE_UNALIGNED) {
866       int newPos = UnsafeAccess.putInt(buffer, buffer.position(), val);
867       buffer.position(newPos);
868     } else {
869       buffer.putInt(val);
870     }
871   }
872
873   /**
874    * Reads a double value at the given buffer's offset.
875    * @param buffer
876    * @param offset offset where double is
877    * @return double value at offset
878    */
879   public static double toDouble(ByteBuffer buffer, int offset) {
880     return Double.longBitsToDouble(toLong(buffer, offset));
881   }
882
883   /**
884    * Reads a BigDecimal value at the given buffer's offset.
885    * @param buffer
886    * @param offset
887    * @return BigDecimal value at offset
888    */
889   public static BigDecimal toBigDecimal(ByteBuffer buffer, int offset, int length) {
890     if (buffer == null || length < Bytes.SIZEOF_INT + 1 ||
891       (offset + length > buffer.limit())) {
892       return null;
893     }
894
895     int scale = toInt(buffer, offset);
896     byte[] tcBytes = new byte[length - Bytes.SIZEOF_INT];
897     copyFromBufferToArray(tcBytes, buffer, offset + Bytes.SIZEOF_INT, 0, length - Bytes.SIZEOF_INT);
898     return new BigDecimal(new BigInteger(tcBytes), scale);
899   }
900
901   /**
902    * Put a short value out to the given ByteBuffer's current position in big-endian format.
903    * This also advances the position in buffer by short size.
904    * @param buffer the ByteBuffer to write to
905    * @param val short to write out
906    */
907   public static void putShort(ByteBuffer buffer, short val) {
908     if (UNSAFE_UNALIGNED) {
909       int newPos = UnsafeAccess.putShort(buffer, buffer.position(), val);
910       buffer.position(newPos);
911     } else {
912       buffer.putShort(val);
913     }
914   }
915
916   /**
917    * Put a long value out to the given ByteBuffer's current position in big-endian format.
918    * This also advances the position in buffer by long size.
919    * @param buffer the ByteBuffer to write to
920    * @param val long to write out
921    */
922   public static void putLong(ByteBuffer buffer, long val) {
923     if (UNSAFE_UNALIGNED) {
924       int newPos = UnsafeAccess.putLong(buffer, buffer.position(), val);
925       buffer.position(newPos);
926     } else {
927       buffer.putLong(val);
928     }
929   }
930   /**
931    * Copies the bytes from given array's offset to length part into the given buffer. Puts the bytes
932    * to buffer's current position. This also advances the position in the 'out' buffer by 'length'
933    * @param out
934    * @param in
935    * @param inOffset
936    * @param length
937    */
938   public static void copyFromArrayToBuffer(ByteBuffer out, byte[] in, int inOffset, int length) {
939     if (out.hasArray()) {
940       System.arraycopy(in, inOffset, out.array(), out.arrayOffset() + out.position(), length);
941       // Move the position in out by length
942       out.position(out.position() + length);
943     } else if (UNSAFE_AVAIL) {
944       UnsafeAccess.copy(in, inOffset, out, out.position(), length);
945       // Move the position in out by length
946       out.position(out.position() + length);
947     } else {
948       out.put(in, inOffset, length);
949     }
950   }
951
952   /**
953    * Copies bytes from given array's offset to length part into the given buffer. Puts the bytes
954    * to buffer's given position.
955    * @param out
956    * @param in
957    * @param inOffset
958    * @param length
959    */
960   public static void copyFromArrayToBuffer(ByteBuffer out, int outOffset, byte[] in, int inOffset,
961       int length) {
962     if (out.hasArray()) {
963       System.arraycopy(in, inOffset, out.array(), out.arrayOffset() + outOffset, length);
964     } else if (UNSAFE_AVAIL) {
965       UnsafeAccess.copy(in, inOffset, out, outOffset, length);
966     } else {
967       int oldPos = out.position();
968       out.position(outOffset);
969       out.put(in, inOffset, length);
970       out.position(oldPos);
971     }
972   }
973
974   /**
975    * Copies specified number of bytes from given offset of 'in' ByteBuffer to
976    * the array.
977    * @param out
978    * @param in
979    * @param sourceOffset
980    * @param destinationOffset
981    * @param length
982    */
983   public static void copyFromBufferToArray(byte[] out, ByteBuffer in, int sourceOffset,
984       int destinationOffset, int length) {
985     if (in.hasArray()) {
986       System.arraycopy(in.array(), sourceOffset + in.arrayOffset(), out, destinationOffset, length);
987     } else if (UNSAFE_AVAIL) {
988       UnsafeAccess.copy(in, sourceOffset, out, destinationOffset, length);
989     } else {
990       int oldPos = in.position();
991       in.position(sourceOffset);
992       in.get(out, destinationOffset, length);
993       in.position(oldPos);
994     }
995   }
996
997   /**
998    * Similar to  {@link Arrays#copyOfRange(byte[], int, int)}
999    * @param original the buffer from which the copy has to happen
1000    * @param from the starting index
1001    * @param to the ending index
1002    * @return a byte[] created out of the copy
1003    */
1004   public static byte[] copyOfRange(ByteBuffer original, int from, int to) {
1005     int newLength = to - from;
1006     if (newLength < 0) throw new IllegalArgumentException(from + " > " + to);
1007     byte[] copy = new byte[newLength];
1008     ByteBufferUtils.copyFromBufferToArray(copy, original, from, 0, newLength);
1009     return copy;
1010   }
1011
1012   // For testing purpose
1013   public static String toStringBinary(final ByteBuffer b, int off, int len) {
1014     StringBuilder result = new StringBuilder();
1015     // Just in case we are passed a 'len' that is > buffer length...
1016     if (off >= b.capacity())
1017       return result.toString();
1018     if (off + len > b.capacity())
1019       len = b.capacity() - off;
1020     for (int i = off; i < off + len; ++i) {
1021       int ch = b.get(i) & 0xFF;
1022       if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
1023           || " `~!@#$%^&*()-_=+[]{}|;:'\",.<>/?".indexOf(ch) >= 0) {
1024         result.append((char) ch);
1025       } else {
1026         result.append(String.format("\\x%02X", ch));
1027       }
1028     }
1029     return result.toString();
1030   }
1031
1032   public static String toStringBinary(final ByteBuffer b) {
1033     return toStringBinary(b, 0, b.capacity());
1034   }
1035 }