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