From 6f413eef873d530139faa7160974a93a8f7de191 Mon Sep 17 00:00:00 2001 From: Sunil Ramchandra Pawar Date: Thu, 27 Mar 2025 13:04:59 +0530 Subject: [PATCH] Adding a case to handle read-only BB. --- .../cassandra/utils/FastByteOperations.java | 24 ++++++++++++++++--- .../utils/FastByteOperationsTest.java | 16 +++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/java/org/apache/cassandra/utils/FastByteOperations.java b/src/java/org/apache/cassandra/utils/FastByteOperations.java index 2a86712951..b1514543d2 100644 --- a/src/java/org/apache/cassandra/utils/FastByteOperations.java +++ b/src/java/org/apache/cassandra/utils/FastByteOperations.java @@ -163,6 +163,7 @@ public class FastByteOperations */ static final long BYTE_ARRAY_BASE_OFFSET; static final long DIRECT_BUFFER_ADDRESS_OFFSET; + static final long HEAP_HB_FIELD; static { @@ -195,6 +196,8 @@ public class FastByteOperations { BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); DIRECT_BUFFER_ADDRESS_OFFSET = theUnsafe.objectFieldOffset(Buffer.class.getDeclaredField("address")); + + HEAP_HB_FIELD = theUnsafe.objectFieldOffset(ByteBuffer.class.getDeclaredField("hb")); } catch (Exception e) { @@ -269,16 +272,31 @@ public class FastByteOperations { Object src; long srcOffset; - if (srcBuf.hasArray()) + + // Heap ByteBuffer (Mutable) + if (srcBuf.hasArray() && !srcBuf.isReadOnly()) { - src = srcBuf.array(); - srcOffset = BYTE_ARRAY_BASE_OFFSET + srcBuf.arrayOffset(); + src = theUnsafe.getObject(srcBuf, HEAP_HB_FIELD); + srcOffset = BYTE_ARRAY_BASE_OFFSET; } + // Read-Only Heap ByteBuffer (Still has hb but read-only) + else if (srcBuf.isReadOnly() && !srcBuf.isDirect()) + { + src = theUnsafe.getObject(srcBuf, HEAP_HB_FIELD); + srcOffset = BYTE_ARRAY_BASE_OFFSET; + + if (src == null) + { + throw new IllegalArgumentException("Unsupported ByteBuffer type: No backing array and not direct."); + } + } + // Direct ByteBuffer else { src = null; srcOffset = theUnsafe.getLong(srcBuf, DIRECT_BUFFER_ADDRESS_OFFSET); } + copy(src, srcOffset + srcPosition, trgBuf, trgPosition, length); } diff --git a/test/unit/org/apache/cassandra/utils/FastByteOperationsTest.java b/test/unit/org/apache/cassandra/utils/FastByteOperationsTest.java index 4f6d6523f8..e2bc4d7d71 100644 --- a/test/unit/org/apache/cassandra/utils/FastByteOperationsTest.java +++ b/test/unit/org/apache/cassandra/utils/FastByteOperationsTest.java @@ -50,6 +50,22 @@ public class FastByteOperationsTest testCopy(bytes1, wrap1(bytes1, false), wrap2(empty, false), UO); } + @Test + public void testFastByteCopyReadOnly() + { + byte[] bytes1 = new byte[128]; + byte[] empty = new byte[128]; + rand.nextBytes(bytes1); + testCopy(bytes1, wrap1(bytes1, true).asReadOnlyBuffer(), wrap2(empty, true), PJO); + testCopy(bytes1, wrap1(bytes1, true).asReadOnlyBuffer(), wrap2(empty, false), PJO); + testCopy(bytes1, wrap1(bytes1, false).asReadOnlyBuffer(), wrap2(empty, true), PJO); + testCopy(bytes1, wrap1(bytes1, false).asReadOnlyBuffer(), wrap2(empty, false), PJO); + testCopy(bytes1, wrap1(bytes1, true).asReadOnlyBuffer(), wrap2(empty, true), UO); + testCopy(bytes1, wrap1(bytes1, true).asReadOnlyBuffer(), wrap2(empty, false), UO); + testCopy(bytes1, wrap1(bytes1, false).asReadOnlyBuffer(), wrap2(empty, true), UO); + testCopy(bytes1, wrap1(bytes1, false).asReadOnlyBuffer(), wrap2(empty, false), UO); + } + private void testCopy(byte[] canon, ByteBuffer src, ByteBuffer trg, FastByteOperations.ByteOperations ops) { byte[] result = new byte[src.remaining()];