From b972b9a2d95c44bf6cd63963978726ec192d545f Mon Sep 17 00:00:00 2001 From: Sahil Aggarwal Date: Tue, 9 Oct 2018 22:41:36 +0530 Subject: [PATCH] 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. --- .../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(-) 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 714c550ddd8..88cad0c263b 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 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 11745a4d96d..deb667b6727 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 + @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); } /** 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 faaab6f86c3..9c3049fe71b 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 { 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 { 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 { 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 { 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 { 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 { 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 { ); } + 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 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 + @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}.