This is an automated email from the ASF dual-hosted git repository.

yifan-c pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-analytics.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 98ca34c9 CASSANALYTICS-187: Fix FastByteOperations Silently Falling 
Back to Pure-Java Comparator (#228)
98ca34c9 is described below

commit 98ca34c95a2b29cbcd4e09f50f30f0e836364e03
Author: Yifan Cai <[email protected]>
AuthorDate: Mon Aug 3 16:01:58 2026 -0700

    CASSANALYTICS-187: Fix FastByteOperations Silently Falling Back to 
Pure-Java Comparator (#228)
    
    FastByteOperations.java is copied from Cassandra codebase, but
    introduced a regression that fails to load theUnsafe singleton from
    Unsafe, failing back to the PureJavaOperations unexpectedly.
    
    Patch by Yifan Cai; Reviewed by Francisco Guerrero, Saranya Krishnakumar  
for CASSANALYTICS-187
---
 CHANGES.txt                                        |   1 +
 .../spark/bulkwriter/util/FastByteOperations.java  | 137 ++++++++++++---------
 2 files changed, 77 insertions(+), 61 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 5df559ac..a16f955e 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 12a158a8..3bae5dd6 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 java.security.PrivilegedAction;
 
 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 @@ public final class FastByteOperations
 
     }
 
+    @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<Object>) () -> {
-                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<Object>()
+                      {
+                          @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 final class FastByteOperations
             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 @@ public final class FastByteOperations
              * 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 @@ public final class FastByteOperations
         }
     }
 
+    @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();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to