diff --git a/CHANGES.txt b/CHANGES.txt index 5df559acd..a16f955e1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ 0.5.0 ----- + * Fix FastByteOperations Silently Falling Back to Pure-Java Comparator (CASSANALYTICS-187) * AbstractSchemaBuilder.build() drops the cdc flag, silently disabling CDC on all tables (CASSANALYTICS-186) * Fix total timeout calculation for getAllNodeSettings (CASSANALYTICS-179) * Support vector data type (CASSANALYTICS-26) diff --git a/cassandra-analytics-core/src/main/java/org/apache/cassandra/spark/bulkwriter/util/FastByteOperations.java b/cassandra-analytics-core/src/main/java/org/apache/cassandra/spark/bulkwriter/util/FastByteOperations.java index 12a158a8d..3bae5dd61 100644 --- a/cassandra-analytics-core/src/main/java/org/apache/cassandra/spark/bulkwriter/util/FastByteOperations.java +++ b/cassandra-analytics-core/src/main/java/org/apache/cassandra/spark/bulkwriter/util/FastByteOperations.java @@ -28,7 +28,6 @@ import com.google.common.primitives.Longs; import com.google.common.primitives.UnsignedBytes; -import com.google.common.primitives.UnsignedLongs; import sun.misc.Unsafe; // CHECKSTYLE IGNORE: Used with care to speed byte operations up @@ -105,43 +104,55 @@ static ByteOperations getBest() } + @SuppressWarnings("unused") // used via reflection public static final class UnsafeOperations implements ByteOperations { - static final Unsafe UNSAFE; + static final Unsafe theUnsafe; /** - * The offset to the first element in a byte array + * The offset to the first element in a byte array. */ static final long BYTE_ARRAY_BASE_OFFSET; static final long DIRECT_BUFFER_ADDRESS_OFFSET; static { - UNSAFE = (Unsafe) AccessController.doPrivileged((PrivilegedAction) () -> { - try - { - Field field = Unsafe.class.getDeclaredField("UNSAFE"); - field.setAccessible(true); - return field.get(null); - } - catch (NoSuchFieldException | IllegalAccessException exception) - { - // It doesn't matter what we throw; it's swallowed in getBest() - throw new Error(); - } - }); + theUnsafe = (Unsafe) AccessController.doPrivileged( + new PrivilegedAction() + { + @Override + public Object run() + { + try + { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return f.get(null); + } + catch (NoSuchFieldException e) + { + // It doesn't matter what we throw; + // it's swallowed in getBest(). + throw new Error(); + } + catch (IllegalAccessException e) + { + throw new Error(); + } + } + }); try { - BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); - DIRECT_BUFFER_ADDRESS_OFFSET = UNSAFE.objectFieldOffset(Buffer.class.getDeclaredField("address")); + BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + DIRECT_BUFFER_ADDRESS_OFFSET = theUnsafe.objectFieldOffset(Buffer.class.getDeclaredField("address")); } - catch (Exception exception) + catch (Exception e) { - throw new AssertionError(exception); + throw new AssertionError(e); } - // Sanity check - this should never fail - if (UNSAFE.arrayIndexScale(byte[].class) != 1) + // sanity check - this should never fail + if (theUnsafe.arrayIndexScale(byte[].class) != 1) { throw new AssertionError(); } @@ -154,63 +165,62 @@ public int compare(ByteBuffer buffer1, ByteBuffer buffer2) return compareTo(buffer1, buffer2); } - static int compareTo(ByteBuffer buffer1, ByteBuffer buffer2) + public static int compareTo(ByteBuffer buffer1, ByteBuffer buffer2) { - Object object1; + Object obj1; long offset1; int length1; if (buffer1.hasArray()) { - object1 = buffer1.array(); + obj1 = buffer1.array(); offset1 = BYTE_ARRAY_BASE_OFFSET + buffer1.arrayOffset(); } else { - object1 = null; - offset1 = UNSAFE.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET); + obj1 = null; + offset1 = theUnsafe.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET); } offset1 += buffer1.position(); length1 = buffer1.remaining(); - return compareTo(object1, offset1, length1, buffer2); + return compareTo(obj1, offset1, length1, buffer2); } - static int compareTo(Object buffer1, long offset1, int length1, ByteBuffer buffer2) + public static int compareTo(Object buffer1, long offset1, int length1, ByteBuffer buffer) { - Object object2; + Object obj2; long offset2; - int position = buffer2.position(); - int limit = buffer2.limit(); - if (buffer2.hasArray()) + int position = buffer.position(); + int limit = buffer.limit(); + if (buffer.hasArray()) { - object2 = buffer2.array(); - offset2 = BYTE_ARRAY_BASE_OFFSET + buffer2.arrayOffset(); + obj2 = buffer.array(); + offset2 = BYTE_ARRAY_BASE_OFFSET + buffer.arrayOffset(); } else { - object2 = null; - offset2 = UNSAFE.getLong(buffer2, DIRECT_BUFFER_ADDRESS_OFFSET); + obj2 = null; + offset2 = theUnsafe.getLong(buffer, DIRECT_BUFFER_ADDRESS_OFFSET); } int length2 = limit - position; offset2 += position; - return compareTo(buffer1, offset1, length1, object2, offset2, length2); + return compareTo(buffer1, offset1, length1, obj2, offset2, length2); } /** - * Lexicographically compare two arrays + * Lexicographically compare two arrays. * * @param buffer1 left operand: a byte[] or null * @param buffer2 right operand: a byte[] or null - * @param offset1 Where to start comparing in the left buffer - * (pure memory address if buffer1 is null, or relative otherwise) - * @param offset2 Where to start comparing in the right buffer - * (pure memory address if buffer1 is null, or relative otherwise) + * @param memoryOffset1 Where to start comparing in the left buffer (pure memory address if buffer1 is null, or relative otherwise) + * @param memoryOffset2 Where to start comparing in the right buffer (pure memory address if buffer1 is null, or relative otherwise) * @param length1 How much to compare from the left buffer * @param length2 How much to compare from the right buffer - * @return 0 if equal, < 0 if left is less than right, etc. + * @return 0 if equal, {@code < 0} if left is less than right, etc. */ - static int compareTo(Object buffer1, long offset1, int length1, Object buffer2, long offset2, int length2) + public static int compareTo(Object buffer1, long memoryOffset1, int length1, + Object buffer2, long memoryOffset2, int length2) { int minLength = Math.min(length1, length2); @@ -220,25 +230,29 @@ static int compareTo(Object buffer1, long offset1, int length1, Object buffer2, * On the other hand, it is substantially faster on 64-bit. */ int wordComparisons = minLength & ~7; - for (int index = 0; index < wordComparisons; index += Longs.BYTES) + for (int i = 0; i < wordComparisons; i += Longs.BYTES) { - long long1 = UNSAFE.getLong(buffer1, offset1 + index); - long long2 = UNSAFE.getLong(buffer2, offset2 + index); + long lw = theUnsafe.getLong(buffer1, memoryOffset1 + i); + long rw = theUnsafe.getLong(buffer2, memoryOffset2 + i); - if (long1 != long2) + if (lw != rw) { - return BIG_ENDIAN ? UnsignedLongs.compare(long1, long2) - : UnsignedLongs.compare(Long.reverseBytes(long1), Long.reverseBytes(long2)); + if (BIG_ENDIAN) + { + return Long.compareUnsigned(lw, rw); + } + + return Long.compareUnsigned(Long.reverseBytes(lw), Long.reverseBytes(rw)); } } - for (int index = wordComparisons; index < minLength; index++) + for (int i = wordComparisons; i < minLength; i++) { - int byte1 = UNSAFE.getByte(buffer1, offset1 + index) & 0xFF; - int byte2 = UNSAFE.getByte(buffer2, offset2 + index) & 0xFF; - if (byte1 != byte2) + int b1 = theUnsafe.getByte(buffer1, memoryOffset1 + i) & 0xFF; + int b2 = theUnsafe.getByte(buffer2, memoryOffset2 + i) & 0xFF; + if (b1 != b2) { - return byte1 - byte2; + return b1 - b2; } } @@ -246,19 +260,20 @@ static int compareTo(Object buffer1, long offset1, int length1, Object buffer2, } } + @SuppressWarnings("unused") public static final class PureJavaOperations implements ByteOperations { public int compare(ByteBuffer buffer1, ByteBuffer buffer2) { int end1 = buffer1.limit(); int end2 = buffer2.limit(); - for (int index1 = buffer1.position(), index2 = buffer2.position(); index1 < end1 && index2 < end2; index1++, index2++) + for (int i = buffer1.position(), j = buffer2.position(); i < end1 && j < end2; i++, j++) { - int byte1 = buffer1.get(index1) & 0xFF; - int byte2 = buffer2.get(index2) & 0xFF; - if (byte1 != byte2) + int a = buffer1.get(i) & 0xff; + int b = buffer2.get(j) & 0xff; + if (a != b) { - return byte1 - byte2; + return a - b; } } return buffer1.remaining() - buffer2.remaining();