Repository: hbase
Updated Branches:
  refs/heads/branch-2.1 c744dd84c -> b972b9a2d


HBASE-20716: Changes the bytes[] conversion done in Bytes and ByteBufferUtils. 
Instead of doing check unsafe_aligned available everytime, choose the best 
converter at startup.


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/b972b9a2
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/b972b9a2
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/b972b9a2

Branch: refs/heads/branch-2.1
Commit: b972b9a2d95c44bf6cd63963978726ec192d545f
Parents: c744dd8
Author: Sahil Aggarwal <[email protected]>
Authored: Tue Oct 9 22:41:36 2018 +0530
Committer: Michael Stack <[email protected]>
Committed: Wed Oct 17 21:03:39 2018 -0700

----------------------------------------------------------------------
 .../hadoop/hbase/filter/FuzzyRowFilter.java     |  19 +-
 .../hadoop/hbase/util/ByteBufferUtils.java      | 403 +++++++++++++------
 .../org/apache/hadoop/hbase/util/Bytes.java     | 213 +++++++---
 3 files changed, 439 insertions(+), 196 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/b972b9a2/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FuzzyRowFilter.java
----------------------------------------------------------------------
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FuzzyRowFilter.java 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FuzzyRowFilter.java
index 714c550..88cad0c 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FuzzyRowFilter.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FuzzyRowFilter.java
@@ -34,7 +34,6 @@ import 
org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.BytesBytesPair;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.Pair;
-import org.apache.hadoop.hbase.util.UnsafeAccess;
 import org.apache.hadoop.hbase.util.UnsafeAvailChecker;
 
 import 
org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
@@ -351,9 +350,9 @@ public class FuzzyRowFilter extends FilterBase {
     int j = numWords << 3; // numWords * SIZEOF_LONG;
 
     for (int i = 0; i < j; i += Bytes.SIZEOF_LONG) {
-      long fuzzyBytes = UnsafeAccess.toLong(fuzzyKeyBytes, i);
-      long fuzzyMeta = UnsafeAccess.toLong(fuzzyKeyMeta, i);
-      long rowValue = UnsafeAccess.toLong(row, offset + i);
+      long fuzzyBytes = Bytes.toLong(fuzzyKeyBytes, i);
+      long fuzzyMeta = Bytes.toLong(fuzzyKeyMeta, i);
+      long rowValue = Bytes.toLong(row, offset + i);
       if ((rowValue & fuzzyMeta) != (fuzzyBytes)) {
         // We always return NEXT_EXISTS
         return SatisfiesCode.NEXT_EXISTS;
@@ -363,9 +362,9 @@ public class FuzzyRowFilter extends FilterBase {
     int off = j;
 
     if (length - off >= Bytes.SIZEOF_INT) {
-      int fuzzyBytes = UnsafeAccess.toInt(fuzzyKeyBytes, off);
-      int fuzzyMeta = UnsafeAccess.toInt(fuzzyKeyMeta, off);
-      int rowValue = UnsafeAccess.toInt(row, offset + off);
+      int fuzzyBytes = Bytes.toInt(fuzzyKeyBytes, off);
+      int fuzzyMeta = Bytes.toInt(fuzzyKeyMeta, off);
+      int rowValue = Bytes.toInt(row, offset + off);
       if ((rowValue & fuzzyMeta) != (fuzzyBytes)) {
         // We always return NEXT_EXISTS
         return SatisfiesCode.NEXT_EXISTS;
@@ -374,9 +373,9 @@ public class FuzzyRowFilter extends FilterBase {
     }
 
     if (length - off >= Bytes.SIZEOF_SHORT) {
-      short fuzzyBytes = UnsafeAccess.toShort(fuzzyKeyBytes, off);
-      short fuzzyMeta = UnsafeAccess.toShort(fuzzyKeyMeta, off);
-      short rowValue = UnsafeAccess.toShort(row, offset + off);
+      short fuzzyBytes = Bytes.toShort(fuzzyKeyBytes, off);
+      short fuzzyMeta = Bytes.toShort(fuzzyKeyMeta, off);
+      short rowValue = Bytes.toShort(row, offset + off);
       if ((rowValue & fuzzyMeta) != (fuzzyBytes)) {
         // We always return NEXT_EXISTS
         // even if it does not (in this case getNextForFuzzyRule

http://git-wip-us.apache.org/repos/asf/hbase/blob/b972b9a2/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java
----------------------------------------------------------------------
diff --git 
a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java 
b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java
index 11745a4..deb667b 100644
--- 
a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java
+++ 
b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java
@@ -55,7 +55,267 @@ public final class ByteBufferUtils {
   private ByteBufferUtils() {
   }
 
-  /**
+
+  static abstract class Comparer {
+    abstract int compareTo(byte [] buf1, int o1, int l1, ByteBuffer buf2, int 
o2, int l2);
+    abstract int compareTo(ByteBuffer buf1, int o1, int l1, ByteBuffer buf2, 
int o2, int l2);
+  }
+
+  static abstract class Converter {
+    abstract short toShort(ByteBuffer buffer, int offset);
+    abstract int toInt(ByteBuffer buffer);
+    abstract int toInt(ByteBuffer buffer, int offset);
+    abstract long toLong(ByteBuffer buffer, int offset);
+    abstract void putInt(ByteBuffer buffer, int val);
+    abstract int putInt(ByteBuffer buffer, int index, int val);
+    abstract void putShort(ByteBuffer buffer, short val);
+    abstract int putShort(ByteBuffer buffer, int index, short val);
+    abstract void putLong(ByteBuffer buffer, long val);
+    abstract int putLong(ByteBuffer buffer, int index, long val);
+  }
+
+  static class ComparerHolder {
+    static final String UNSAFE_COMPARER_NAME = ComparerHolder.class.getName() 
+ "$UnsafeComparer";
+
+    static final Comparer BEST_COMPARER = getBestComparer();
+
+    static Comparer getBestComparer() {
+      try {
+        Class<?> theClass = Class.forName(UNSAFE_COMPARER_NAME);
+
+        @SuppressWarnings("unchecked")
+        Comparer comparer = (Comparer) theClass.getConstructor().newInstance();
+        return comparer;
+      } catch (Throwable t) { // ensure we really catch *everything*
+        return PureJavaComparer.INSTANCE;
+      }
+    }
+
+    static final class PureJavaComparer extends Comparer {
+      static final PureJavaComparer INSTANCE = new PureJavaComparer();
+
+      private PureJavaComparer() {}
+
+      @Override
+      public int compareTo(byte [] buf1, int o1, int l1, ByteBuffer buf2, int 
o2, int l2) {
+        int end1 = o1 + l1;
+        int end2 = o2 + l2;
+        for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
+          int a = buf1[i] & 0xFF;
+          int b = buf2.get(j) & 0xFF;
+          if (a != b) {
+            return a - b;
+          }
+        }
+        return l1 - l2;
+      }
+
+      @Override
+      public int compareTo(ByteBuffer buf1, int o1, int l1, ByteBuffer buf2, 
int o2, int l2) {
+        int end1 = o1 + l1;
+        int end2 = o2 + l2;
+        for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
+          int a = buf1.get(i) & 0xFF;
+          int b = buf2.get(j) & 0xFF;
+          if (a != b) {
+            return a - b;
+          }
+        }
+        return l1 - l2;
+      }
+    }
+
+    static final class UnsafeComparer extends Comparer {
+
+      public UnsafeComparer() {}
+
+      static {
+        if(!UNSAFE_UNALIGNED) {
+          throw new Error();
+        }
+      }
+
+      @Override
+      public int compareTo(byte[] buf1, int o1, int l1, ByteBuffer buf2, int 
o2, int l2) {
+        long offset2Adj;
+        Object refObj2 = null;
+        if (buf2.isDirect()) {
+          offset2Adj = o2 + ((DirectBuffer)buf2).address();
+        } else {
+          offset2Adj = o2 + buf2.arrayOffset() + 
UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
+          refObj2 = buf2.array();
+        }
+        return compareToUnsafe(buf1, o1 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, 
l1,
+                refObj2, offset2Adj, l2);
+      }
+
+      @Override
+      public int compareTo(ByteBuffer buf1, int o1, int l1, ByteBuffer buf2, 
int o2, int l2) {
+        long offset1Adj, offset2Adj;
+        Object refObj1 = null, refObj2 = null;
+        if (buf1.isDirect()) {
+          offset1Adj = o1 + ((DirectBuffer) buf1).address();
+        } else {
+          offset1Adj = o1 + buf1.arrayOffset() + 
UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
+          refObj1 = buf1.array();
+        }
+        if (buf2.isDirect()) {
+          offset2Adj = o2 + ((DirectBuffer) buf2).address();
+        } else {
+          offset2Adj = o2 + buf2.arrayOffset() + 
UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
+          refObj2 = buf2.array();
+        }
+        return compareToUnsafe(refObj1, offset1Adj, l1, refObj2, offset2Adj, 
l2);
+      }
+    }
+  }
+
+
+  static class ConverterHolder {
+    static final String UNSAFE_CONVERTER_NAME =
+            ConverterHolder.class.getName() + "$UnsafeConverter";
+    static final Converter BEST_CONVERTER = getBestConverter();
+
+    static Converter getBestConverter() {
+      try {
+        Class<?> theClass = Class.forName(UNSAFE_CONVERTER_NAME);
+
+        // yes, UnsafeComparer does implement Comparer<byte[]>
+        @SuppressWarnings("unchecked")
+        Converter converter = (Converter) 
theClass.getConstructor().newInstance();
+        return converter;
+      } catch (Throwable t) { // ensure we really catch *everything*
+        return PureJavaConverter.INSTANCE;
+      }
+    }
+
+    static final class PureJavaConverter extends Converter {
+      static final PureJavaConverter INSTANCE = new PureJavaConverter();
+
+      private PureJavaConverter() {}
+
+      @Override
+      short toShort(ByteBuffer buffer, int offset) {
+        return buffer.getShort(offset);
+      }
+
+      @Override
+      int toInt(ByteBuffer buffer) {
+        return buffer.getInt();
+      }
+
+      @Override
+      int toInt(ByteBuffer buffer, int offset) {
+        return buffer.getInt(offset);
+      }
+
+      @Override
+      long toLong(ByteBuffer buffer, int offset) {
+        return buffer.getLong(offset);
+      }
+
+      @Override
+      void putInt(ByteBuffer buffer, int val) {
+        buffer.putInt(val);
+      }
+
+      @Override
+      int putInt(ByteBuffer buffer, int index, int val) {
+        buffer.putInt(index, val);
+        return index + Bytes.SIZEOF_INT;
+      }
+
+      @Override
+      void putShort(ByteBuffer buffer, short val) {
+        buffer.putShort(val);
+      }
+
+      @Override
+      int putShort(ByteBuffer buffer, int index, short val) {
+        buffer.putShort(index, val);
+        return index + Bytes.SIZEOF_SHORT;
+      }
+
+      @Override
+      void putLong(ByteBuffer buffer, long val) {
+        buffer.putLong(val);
+      }
+
+      @Override
+      int putLong(ByteBuffer buffer, int index, long val) {
+        buffer.putLong(index, val);
+        return index + Bytes.SIZEOF_LONG;
+      }
+    }
+
+    static final class UnsafeConverter extends Converter {
+
+      public UnsafeConverter() {}
+
+      static {
+        if(!UNSAFE_UNALIGNED) {
+          throw new Error();
+        }
+      }
+
+      @Override
+      short toShort(ByteBuffer buffer, int offset) {
+        return UnsafeAccess.toShort(buffer, offset);
+      }
+
+      @Override
+      int toInt(ByteBuffer buffer) {
+        int i = UnsafeAccess.toInt(buffer, buffer.position());
+        buffer.position(buffer.position() + Bytes.SIZEOF_INT);
+        return i;
+      }
+
+      @Override
+      int toInt(ByteBuffer buffer, int offset) {
+        return UnsafeAccess.toInt(buffer, offset);
+      }
+
+      @Override
+      long toLong(ByteBuffer buffer, int offset) {
+        return UnsafeAccess.toLong(buffer, offset);
+      }
+
+      @Override
+      void putInt(ByteBuffer buffer, int val) {
+        int newPos = UnsafeAccess.putInt(buffer, buffer.position(), val);
+        buffer.position(newPos);
+      }
+
+      @Override
+      int putInt(ByteBuffer buffer, int index, int val) {
+        return UnsafeAccess.putInt(buffer, index, val);
+      }
+
+      @Override
+      void putShort(ByteBuffer buffer, short val) {
+        int newPos = UnsafeAccess.putShort(buffer, buffer.position(), val);
+        buffer.position(newPos);
+      }
+
+      @Override
+      int putShort(ByteBuffer buffer, int index, short val) {
+        return UnsafeAccess.putShort(buffer, index, val);
+      }
+
+      @Override
+      void putLong(ByteBuffer buffer, long val) {
+        int newPos = UnsafeAccess.putLong(buffer, buffer.position(), val);
+        buffer.position(newPos);
+      }
+
+      @Override
+      int putLong(ByteBuffer buffer, int index, long val) {
+        return UnsafeAccess.putLong(buffer, index, val);
+      }
+    }
+  }
+
+    /**
    * Similar to {@link WritableUtils#writeVLong(java.io.DataOutput, long)},
    * but writes to a {@link ByteBuffer}.
    */
@@ -629,35 +889,7 @@ public final class ByteBufferUtils {
   }
 
   public static int compareTo(ByteBuffer buf1, int o1, int l1, ByteBuffer 
buf2, int o2, int l2) {
-    // NOTE: This method is copied over in BBKVComparator!!!!! For perf 
reasons. If you make
-    // changes here, make them there too!!!!
-    if (UNSAFE_UNALIGNED) {
-      long offset1Adj, offset2Adj;
-      Object refObj1 = null, refObj2 = null;
-      if (buf1.isDirect()) {
-        offset1Adj = o1 + ((DirectBuffer) buf1).address();
-      } else {
-        offset1Adj = o1 + buf1.arrayOffset() + 
UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
-        refObj1 = buf1.array();
-      }
-      if (buf2.isDirect()) {
-        offset2Adj = o2 + ((DirectBuffer) buf2).address();
-      } else {
-        offset2Adj = o2 + buf2.arrayOffset() + 
UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
-        refObj2 = buf2.array();
-      }
-      return compareToUnsafe(refObj1, offset1Adj, l1, refObj2, offset2Adj, l2);
-    }
-    int end1 = o1 + l1;
-    int end2 = o2 + l2;
-    for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
-      int a = buf1.get(i) & 0xFF;
-      int b = buf2.get(j) & 0xFF;
-      if (a != b) {
-        return a - b;
-      }
-    }
-    return l1 - l2;
+    return ComparerHolder.BEST_COMPARER.compareTo(buf1, o1, l1, buf2, o2, l2);
   }
 
   public static boolean equals(ByteBuffer buf1, int o1, int l1, byte[] buf2, 
int o2, int l2) {
@@ -678,53 +910,11 @@ public final class ByteBufferUtils {
   // of compiled code via  jitwatch).
 
   public static int compareTo(byte [] buf1, int o1, int l1, ByteBuffer buf2, 
int o2, int l2) {
-    if (UNSAFE_UNALIGNED) {
-      long offset2Adj;
-      Object refObj2 = null;
-      if (buf2.isDirect()) {
-        offset2Adj = o2 + ((DirectBuffer)buf2).address();
-      } else {
-        offset2Adj = o2 + buf2.arrayOffset() + 
UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
-        refObj2 = buf2.array();
-      }
-      return compareToUnsafe(buf1, o1 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, 
l1,
-          refObj2, offset2Adj, l2);
-    }
-    int end1 = o1 + l1;
-    int end2 = o2 + l2;
-    for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
-      int a = buf1[i] & 0xFF;
-      int b = buf2.get(j) & 0xFF;
-      if (a != b) {
-        return a - b;
-      }
-    }
-    return l1 - l2;
+    return ComparerHolder.BEST_COMPARER.compareTo(buf1, o1, l1, buf2, o2, l2);
   }
 
   public static int compareTo(ByteBuffer buf1, int o1, int l1, byte[] buf2, 
int o2, int l2) {
-    if (UNSAFE_UNALIGNED) {
-      long offset1Adj;
-      Object refObj1 = null;
-      if (buf1.isDirect()) {
-        offset1Adj = o1 + ((DirectBuffer) buf1).address();
-      } else {
-        offset1Adj = o1 + buf1.arrayOffset() + 
UnsafeAccess.BYTE_ARRAY_BASE_OFFSET;
-        refObj1 = buf1.array();
-      }
-      return compareToUnsafe(refObj1, offset1Adj, l1,
-          buf2, o2 + UnsafeAccess.BYTE_ARRAY_BASE_OFFSET, l2);
-    }
-    int end1 = o1 + l1;
-    int end2 = o2 + l2;
-    for (int i = o1, j = o2; i < end1 && j < end2; i++, j++) {
-      int a = buf1.get(i) & 0xFF;
-      int b = buf2[j] & 0xFF;
-      if (a != b) {
-        return a - b;
-      }
-    }
-    return l1 - l2;
+    return compareTo(buf2, o2, l2, buf1, o1, l1)*-1;
   }
 
   static int compareToUnsafe(Object obj1, long o1, int l1, Object obj2, long 
o2, int l2) {
@@ -777,24 +967,14 @@ public final class ByteBufferUtils {
    * @return short value at offset
    */
   public static short toShort(ByteBuffer buffer, int offset) {
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.toShort(buffer, offset);
-    } else {
-      return buffer.getShort(offset);
-    }
+    return ConverterHolder.BEST_CONVERTER.toShort(buffer, offset);
   }
 
   /**
    * Reads an int value at the given buffer's current position. Also advances 
the buffer's position
    */
   public static int toInt(ByteBuffer buffer) {
-    if (UNSAFE_UNALIGNED) {
-      int i = UnsafeAccess.toInt(buffer, buffer.position());
-      buffer.position(buffer.position() + Bytes.SIZEOF_INT);
-      return i;
-    } else {
-      return buffer.getInt();
-    }
+    return ConverterHolder.BEST_CONVERTER.toInt(buffer);
   }
 
   /**
@@ -804,11 +984,7 @@ public final class ByteBufferUtils {
    * @return int value at offset
    */
   public static int toInt(ByteBuffer buffer, int offset) {
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.toInt(buffer, offset);
-    } else {
-      return buffer.getInt(offset);
-    }
+    return ConverterHolder.BEST_CONVERTER.toInt(buffer, offset);
   }
 
   /**
@@ -841,11 +1017,7 @@ public final class ByteBufferUtils {
    * @return long value at offset
    */
   public static long toLong(ByteBuffer buffer, int offset) {
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.toLong(buffer, offset);
-    } else {
-      return buffer.getLong(offset);
-    }
+    return ConverterHolder.BEST_CONVERTER.toLong(buffer, offset);
   }
 
   /**
@@ -855,20 +1027,11 @@ public final class ByteBufferUtils {
    * @param val int to write out
    */
   public static void putInt(ByteBuffer buffer, int val) {
-    if (UNSAFE_UNALIGNED) {
-      int newPos = UnsafeAccess.putInt(buffer, buffer.position(), val);
-      buffer.position(newPos);
-    } else {
-      buffer.putInt(val);
-    }
+    ConverterHolder.BEST_CONVERTER.putInt(buffer, val);
   }
 
   public static int putInt(ByteBuffer buffer, int index, int val) {
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.putInt(buffer, index, val);
-    }
-    buffer.putInt(index, val);
-    return index + Bytes.SIZEOF_INT;
+    return ConverterHolder.BEST_CONVERTER.putInt(buffer, index, val);
   }
 
   /**
@@ -906,20 +1069,11 @@ public final class ByteBufferUtils {
    * @param val short to write out
    */
   public static void putShort(ByteBuffer buffer, short val) {
-    if (UNSAFE_UNALIGNED) {
-      int newPos = UnsafeAccess.putShort(buffer, buffer.position(), val);
-      buffer.position(newPos);
-    } else {
-      buffer.putShort(val);
-    }
+    ConverterHolder.BEST_CONVERTER.putShort(buffer, val);
   }
 
   public static int putShort(ByteBuffer buffer, int index, short val) {
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.putShort(buffer, index, val);
-    }
-    buffer.putShort(index, val);
-    return index + Bytes.SIZEOF_SHORT;
+    return ConverterHolder.BEST_CONVERTER.putShort(buffer, index, val);
   }
 
   public static int putAsShort(ByteBuffer buf, int index, int val) {
@@ -936,20 +1090,11 @@ public final class ByteBufferUtils {
    * @param val long to write out
    */
   public static void putLong(ByteBuffer buffer, long val) {
-    if (UNSAFE_UNALIGNED) {
-      int newPos = UnsafeAccess.putLong(buffer, buffer.position(), val);
-      buffer.position(newPos);
-    } else {
-      buffer.putLong(val);
-    }
+    ConverterHolder.BEST_CONVERTER.putLong(buffer, val);
   }
 
   public static int putLong(ByteBuffer buffer, int index, long val) {
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.putLong(buffer, index, val);
-    }
-    buffer.putLong(index, val);
-    return index + Bytes.SIZEOF_LONG;
+    return ConverterHolder.BEST_CONVERTER.putLong(buffer, index, val);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/hbase/blob/b972b9a2/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java 
b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
index faaab6f..9c3049f 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
@@ -812,16 +812,7 @@ public class Bytes implements Comparable<Bytes> {
     if (length != SIZEOF_LONG || offset + length > bytes.length) {
       throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_LONG);
     }
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.toLong(bytes, offset);
-    } else {
-      long l = 0;
-      for(int i = offset; i < offset + length; i++) {
-        l <<= 8;
-        l ^= bytes[i] & 0xFF;
-      }
-      return l;
-    }
+    return ConverterHolder.BEST_CONVERTER.toLong(bytes, offset, length);
   }
 
   private static IllegalArgumentException
@@ -853,16 +844,7 @@ public class Bytes implements Comparable<Bytes> {
       throw new IllegalArgumentException("Not enough room to put a long at"
           + " offset " + offset + " in a " + bytes.length + " byte array");
     }
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.putLong(bytes, offset, val);
-    } else {
-      for(int i = offset + 7; i > offset; i--) {
-        bytes[i] = (byte) val;
-        val >>>= 8;
-      }
-      bytes[offset] = (byte) val;
-      return offset + SIZEOF_LONG;
-    }
+    return ConverterHolder.BEST_CONVERTER.putLong(bytes, offset, val);
   }
 
   /**
@@ -1004,16 +986,7 @@ public class Bytes implements Comparable<Bytes> {
     if (length != SIZEOF_INT || offset + length > bytes.length) {
       throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_INT);
     }
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.toInt(bytes, offset);
-    } else {
-      int n = 0;
-      for(int i = offset; i < (offset + length); i++) {
-        n <<= 8;
-        n ^= bytes[i] & 0xFF;
-      }
-      return n;
-    }
+    return ConverterHolder.BEST_CONVERTER.toInt(bytes, offset, length);
   }
 
   /**
@@ -1088,16 +1061,7 @@ public class Bytes implements Comparable<Bytes> {
       throw new IllegalArgumentException("Not enough room to put an int at"
           + " offset " + offset + " in a " + bytes.length + " byte array");
     }
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.putInt(bytes, offset, val);
-    } else {
-      for(int i= offset + 3; i > offset; i--) {
-        bytes[i] = (byte) val;
-        val >>>= 8;
-      }
-      bytes[offset] = (byte) val;
-      return offset + SIZEOF_INT;
-    }
+    return ConverterHolder.BEST_CONVERTER.putInt(bytes, offset, val);
   }
 
   /**
@@ -1158,15 +1122,7 @@ public class Bytes implements Comparable<Bytes> {
     if (length != SIZEOF_SHORT || offset + length > bytes.length) {
       throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_SHORT);
     }
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.toShort(bytes, offset);
-    } else {
-      short n = 0;
-      n = (short) (n ^ (bytes[offset] & 0xFF));
-      n = (short) (n << 8);
-      n = (short) (n ^ (bytes[offset + 1] & 0xFF));
-      return n;
-   }
+    return ConverterHolder.BEST_CONVERTER.toShort(bytes, offset, length);
   }
 
   /**
@@ -1196,14 +1152,7 @@ public class Bytes implements Comparable<Bytes> {
       throw new IllegalArgumentException("Not enough room to put a short at"
           + " offset " + offset + " in a " + bytes.length + " byte array");
     }
-    if (UNSAFE_UNALIGNED) {
-      return UnsafeAccess.putShort(bytes, offset, val);
-    } else {
-      bytes[offset+1] = (byte) val;
-      val >>= 8;
-      bytes[offset] = (byte) val;
-      return offset + SIZEOF_SHORT;
-    }
+    return ConverterHolder.BEST_CONVERTER.putShort(bytes, offset, val);
   }
 
   /**
@@ -1432,11 +1381,161 @@ public class Bytes implements Comparable<Bytes> {
     );
   }
 
+  static abstract class Converter {
+    abstract long toLong(byte[] bytes, int offset, int length);
+    abstract int putLong(byte[] bytes, int offset, long val);
+
+    abstract int toInt(byte[] bytes, int offset, final int length);
+    abstract int putInt(byte[] bytes, int offset, int val);
+
+    abstract short toShort(byte[] bytes, int offset, final int length);
+    abstract int putShort(byte[] bytes, int offset, short val);
+
+  }
+
   @VisibleForTesting
   static Comparer<byte[]> lexicographicalComparerJavaImpl() {
     return LexicographicalComparerHolder.PureJavaComparer.INSTANCE;
   }
 
+  static class ConverterHolder {
+    static final String UNSAFE_CONVERTER_NAME =
+            ConverterHolder.class.getName() + "$UnsafeConverter";
+
+    static final Converter BEST_CONVERTER = getBestConverter();
+    /**
+     * Returns the Unsafe-using Converter, or falls back to the pure-Java
+     * implementation if unable to do so.
+     */
+    static Converter getBestConverter() {
+      try {
+        Class<?> theClass = Class.forName(UNSAFE_CONVERTER_NAME);
+
+        // yes, UnsafeComparer does implement Comparer<byte[]>
+        @SuppressWarnings("unchecked")
+        Converter converter = (Converter) 
theClass.getConstructor().newInstance();
+        return converter;
+      } catch (Throwable t) { // ensure we really catch *everything*
+        return PureJavaConverter.INSTANCE;
+      }
+    }
+
+    protected static final class PureJavaConverter extends Converter {
+      static final PureJavaConverter INSTANCE = new PureJavaConverter();
+
+      private PureJavaConverter() {}
+
+      @Override
+      long toLong(byte[] bytes, int offset, int length) {
+        long l = 0;
+        for(int i = offset; i < offset + length; i++) {
+          l <<= 8;
+          l ^= bytes[i] & 0xFF;
+        }
+        return l;
+      }
+
+      @Override
+      int putLong(byte[] bytes, int offset, long val) {
+        for(int i = offset + 7; i > offset; i--) {
+          bytes[i] = (byte) val;
+          val >>>= 8;
+        }
+        bytes[offset] = (byte) val;
+        return offset + SIZEOF_LONG;
+      }
+
+      @Override
+      int toInt(byte[] bytes, int offset, int length) {
+        int n = 0;
+        for(int i = offset; i < (offset + length); i++) {
+          n <<= 8;
+          n ^= bytes[i] & 0xFF;
+        }
+        return n;
+      }
+
+      @Override
+      int putInt(byte[] bytes, int offset, int val) {
+        for(int i= offset + 3; i > offset; i--) {
+          bytes[i] = (byte) val;
+          val >>>= 8;
+        }
+        bytes[offset] = (byte) val;
+        return offset + SIZEOF_INT;
+      }
+
+      @Override
+      short toShort(byte[] bytes, int offset, int length) {
+        short n = 0;
+        n = (short) ((n ^ bytes[offset]) & 0xFF);
+        n = (short) (n << 8);
+        n ^= (short) (bytes[offset+1] & 0xFF);
+        return n;
+      }
+
+      @Override
+      int putShort(byte[] bytes, int offset, short val) {
+        bytes[offset+1] = (byte) val;
+        val >>= 8;
+        bytes[offset] = (byte) val;
+        return offset + SIZEOF_SHORT;
+      }
+    }
+
+    protected static final class UnsafeConverter extends Converter {
+
+      static final Unsafe theUnsafe;
+
+      public UnsafeConverter() {}
+
+      static {
+        if (UNSAFE_UNALIGNED) {
+          theUnsafe = UnsafeAccess.theUnsafe;
+        } else {
+          // It doesn't matter what we throw;
+          // it's swallowed in getBestComparer().
+          throw new Error();
+        }
+
+        // sanity check - this should never fail
+        if (theUnsafe.arrayIndexScale(byte[].class) != 1) {
+          throw new AssertionError();
+        }
+      }
+
+      @Override
+      long toLong(byte[] bytes, int offset, int length) {
+        return UnsafeAccess.toLong(bytes, offset);
+      }
+
+      @Override
+      int putLong(byte[] bytes, int offset, long val) {
+        return UnsafeAccess.putLong(bytes, offset, val);
+      }
+
+      @Override
+      int toInt(byte[] bytes, int offset, int length) {
+        return UnsafeAccess.toInt(bytes, offset);
+      }
+
+      @Override
+      int putInt(byte[] bytes, int offset, int val) {
+        return UnsafeAccess.putInt(bytes, offset, val);
+      }
+
+      @Override
+      short toShort(byte[] bytes, int offset, int length) {
+        return UnsafeAccess.toShort(bytes, offset);
+      }
+
+      @Override
+      int putShort(byte[] bytes, int offset, short val) {
+        return UnsafeAccess.putShort(bytes, offset, val);
+      }
+    }
+  }
+
   /**
    * Provides a lexicographical comparer implementation; either a Java
    * implementation or a faster implementation based on {@link Unsafe}.

Reply via email to