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 8e55b63c5dc..765f51bb9c0 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 @@ -129,7 +129,8 @@ public class Bytes implements Comparable { // SizeOf which uses java.lang.instrument says 24 bytes. (3 longs?) public static final int ESTIMATED_HEAP_TAX = 16; - private static final boolean UNSAFE_UNALIGNED = UnsafeAvailChecker.unaligned(); + @VisibleForTesting + static final boolean UNSAFE_UNALIGNED = UnsafeAvailChecker.unaligned(); /** * Returns length of the byte array, returning 0 if the array is null. @@ -1082,7 +1083,15 @@ public class Bytes implements Comparable { if (length != SIZEOF_SHORT || offset + length > bytes.length) { throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_SHORT); } - return ConverterHolder.BEST_CONVERTER.toShort(bytes, offset, length); + if (UNSAFE_UNALIGNED) { + return ConverterHolder.BEST_CONVERTER.toShort(bytes, offset, length); + } else { + short n = 0; + n = (short) (n ^ (bytes[offset] & 0xFF)); + n = (short) (n << 8); + n = (short) (n ^ (bytes[offset + 1] & 0xFF)); + return n; + } } /** diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java index 7771f8773e8..2e19ddd86a2 100644 --- a/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestBytes.java @@ -22,6 +22,8 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -39,6 +41,49 @@ import org.junit.experimental.categories.Category; @Category(SmallTests.class) public class TestBytes extends TestCase { + + private static void setUnsafe(boolean value) throws Exception { + Field field = Bytes.class.getDeclaredField("UNSAFE_UNALIGNED"); + field.setAccessible(true); + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + int oldModifiers = field.getModifiers(); + modifiersField.setInt(field, oldModifiers & ~Modifier.FINAL); + try { + field.set(null, value); + } finally { + modifiersField.setInt(field, oldModifiers); + } + assertEquals(Bytes.UNSAFE_UNALIGNED, value); + } + + public void testShort() throws Exception { + testShort(false); + } + + public void testShortUnsafe() throws Exception { + testShort(true); + } + + private static void testShort(boolean unsafe) throws Exception { + setUnsafe(unsafe); + try { + for (short n : Arrays.asList( + Short.MIN_VALUE, + (short) -100, + (short) -1, + (short) 0, + (short) 1, + (short) 300, + Short.MAX_VALUE)) { + byte[] bytes = Bytes.toBytes(n); + assertEquals(Bytes.toShort(bytes, 0, bytes.length), n); + } + } finally { + setUnsafe(UnsafeAvailChecker.unaligned()); + } + } + public void testNullHashCode() { byte [] b = null; Exception ee = null;