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