This commit is contained in:
Sunil Ramchandra Pawar 2026-08-01 14:09:48 +03:00 committed by GitHub
commit 266a21aa38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 193 additions and 22 deletions

View File

@ -186,6 +186,8 @@ public class FastByteOperations
*/ */
static final long BYTE_ARRAY_BASE_OFFSET; static final long BYTE_ARRAY_BASE_OFFSET;
static final long DIRECT_BUFFER_ADDRESS_OFFSET; static final long DIRECT_BUFFER_ADDRESS_OFFSET;
static final long HEAP_HB_FIELD_OFFSET;
static final long HEAP_ARRAY_FIELD_OFFSET;
static static
{ {
@ -218,6 +220,8 @@ public class FastByteOperations
{ {
BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);
DIRECT_BUFFER_ADDRESS_OFFSET = theUnsafe.objectFieldOffset(Buffer.class.getDeclaredField("address")); DIRECT_BUFFER_ADDRESS_OFFSET = theUnsafe.objectFieldOffset(Buffer.class.getDeclaredField("address"));
HEAP_HB_FIELD_OFFSET = theUnsafe.objectFieldOffset(ByteBuffer.class.getDeclaredField("hb"));
HEAP_ARRAY_FIELD_OFFSET = theUnsafe.objectFieldOffset(ByteBuffer.class.getDeclaredField("offset"));
} }
catch (Exception e) catch (Exception e)
{ {
@ -286,15 +290,24 @@ public class FastByteOperations
{ {
Object obj1; Object obj1;
long offset1; long offset1;
if (buffer1.hasArray())
// Direct ByteBuffer
if (buffer1.isDirect())
{
obj1 = null;
offset1 = theUnsafe.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET) + position1;
}
// Heap ByteBuffer (Mutable)
else if (buffer1.hasArray())
{ {
obj1 = buffer1.array(); obj1 = buffer1.array();
offset1 = BYTE_ARRAY_BASE_OFFSET + buffer1.arrayOffset() + position1; offset1 = BYTE_ARRAY_BASE_OFFSET + buffer1.arrayOffset() + position1;
} }
// Read-Only Heap ByteBuffer (Still has hb but read-only)
else else
{ {
obj1 = null; obj1 = theUnsafe.getObject(buffer1, HEAP_HB_FIELD_OFFSET);
offset1 = theUnsafe.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET) + position1; offset1 = BYTE_ARRAY_BASE_OFFSET + position1;
} }
return compareTo(obj1, offset1, length1, buffer2, BYTE_ARRAY_BASE_OFFSET + offset2, length2); return compareTo(obj1, offset1, length1, buffer2, BYTE_ARRAY_BASE_OFFSET + offset2, length2);
@ -330,16 +343,28 @@ public class FastByteOperations
{ {
Object src; Object src;
long srcOffset; long srcOffset;
if (srcBuf.hasArray())
{ // Direct ByteBuffer
src = srcBuf.array(); if (srcBuf.isDirect())
srcOffset = BYTE_ARRAY_BASE_OFFSET + srcBuf.arrayOffset();
}
else
{ {
src = null; src = null;
srcOffset = theUnsafe.getLong(srcBuf, DIRECT_BUFFER_ADDRESS_OFFSET); srcOffset = theUnsafe.getLong(srcBuf, DIRECT_BUFFER_ADDRESS_OFFSET);
} }
// Heap ByteBuffer (Mutable)
else if (srcBuf.hasArray())
{
src = srcBuf.array();
srcOffset = BYTE_ARRAY_BASE_OFFSET + srcBuf.arrayOffset();
}
// Read-Only Heap ByteBuffer (Still has hb but read-only)
else
{
src = theUnsafe.getObject(srcBuf, HEAP_HB_FIELD_OFFSET);
int arrayOffset = theUnsafe.getInt(srcBuf, HEAP_ARRAY_FIELD_OFFSET);
srcOffset = BYTE_ARRAY_BASE_OFFSET + arrayOffset ;
}
// Direct ByteBuffer
copy(src, srcOffset + srcPosition, trgBuf, trgPosition, length); copy(src, srcOffset + srcPosition, trgBuf, trgPosition, length);
} }
@ -387,16 +412,26 @@ public class FastByteOperations
Object obj1; Object obj1;
long offset1; long offset1;
int length1; int length1;
if (buffer1.hasArray())
{ // Direct ByteBuffer
obj1 = buffer1.array(); if (buffer1.isDirect())
offset1 = BYTE_ARRAY_BASE_OFFSET + buffer1.arrayOffset();
}
else
{ {
obj1 = null; obj1 = null;
offset1 = theUnsafe.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET); offset1 = theUnsafe.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET);
} }
// Heap ByteBuffer (Mutable)
else if (buffer1.hasArray())
{
obj1 = buffer1.array();
offset1 = BYTE_ARRAY_BASE_OFFSET + buffer1.arrayOffset();
}
// Read-Only Heap ByteBuffer (Still has hb but read-only)
else
{
obj1 = theUnsafe.getObject(buffer1, HEAP_HB_FIELD_OFFSET);
offset1 = BYTE_ARRAY_BASE_OFFSET;
}
offset1 += buffer1.position(); offset1 += buffer1.position();
length1 = buffer1.remaining(); length1 = buffer1.remaining();
return compareTo(obj1, offset1, length1, buffer2); return compareTo(obj1, offset1, length1, buffer2);
@ -410,16 +445,26 @@ public class FastByteOperations
int position = buffer.position(); int position = buffer.position();
int limit = buffer.limit(); int limit = buffer.limit();
if (buffer.hasArray())
{ // Direct ByteBuffer
obj2 = buffer.array(); if (buffer.isDirect())
offset2 = BYTE_ARRAY_BASE_OFFSET + buffer.arrayOffset();
}
else
{ {
obj2 = null; obj2 = null;
offset2 = theUnsafe.getLong(buffer, DIRECT_BUFFER_ADDRESS_OFFSET); offset2 = theUnsafe.getLong(buffer, DIRECT_BUFFER_ADDRESS_OFFSET);
} }
// Heap ByteBuffer (Mutable)
else if (buffer.hasArray())
{
obj2 = buffer.array();
offset2 = BYTE_ARRAY_BASE_OFFSET + buffer.arrayOffset();
}
// Read-Only Heap ByteBuffer (Still has hb but read-only)
else
{
obj2 = theUnsafe.getObject(buffer, HEAP_HB_FIELD_OFFSET);
offset2 = BYTE_ARRAY_BASE_OFFSET;
}
int length2 = limit - position; int length2 = limit - position;
offset2 += position; offset2 += position;

View File

@ -20,6 +20,7 @@ package org.apache.cassandra.utils;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import org.junit.Test; import org.junit.Test;
@ -50,6 +51,60 @@ public class FastByteOperationsTest
testCopy(bytes1, wrap1(bytes1, false), wrap2(empty, false), UO); 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);
}
@Test
public void testFastByteCopyWithPoistion()
{
byte[] bytes1 = new byte[128];
byte[] empty = new byte[128];
rand.nextBytes(bytes1);
int randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, true, randomPos).asReadOnlyBuffer(), wrapR1(empty, true, randomPos), PJO, randomPos);
randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, true, randomPos).asReadOnlyBuffer(), wrapR1(empty, false, randomPos), PJO, randomPos);
randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, false, randomPos).asReadOnlyBuffer(), wrapR1(empty, true, randomPos), PJO, randomPos);
randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, false, randomPos).asReadOnlyBuffer(), wrapR1(empty, false, randomPos), PJO, randomPos);
randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, true, randomPos).asReadOnlyBuffer(), wrapR1(empty, true, randomPos), UO, randomPos);
randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, true, randomPos).asReadOnlyBuffer(), wrapR1(empty, false, randomPos), UO, randomPos);
randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, false, randomPos).asReadOnlyBuffer(), wrapR1(empty, true, randomPos), UO, randomPos);
randomPos = ThreadLocalRandom.current().nextInt(0, 101);
testCopyWithPosition(bytes1, wrapR(bytes1, false, randomPos).asReadOnlyBuffer(), wrapR1(empty, false, randomPos), UO, randomPos);
}
private void testCopyWithPosition(byte[] canon, ByteBuffer src, ByteBuffer trg, FastByteOperations.ByteOperations ops, int position)
{
byte[] result = new byte[src.remaining()];
byte[] canonSubArray = Arrays.copyOfRange(canon, position, canon.length);
ops.copy(src, src.position(), trg, trg.position(), src.remaining());
ops.copy(trg, trg.position(), result, 0, trg.remaining());
assert firstdiff(canonSubArray, result) < 0;
}
private void testCopy(byte[] canon, ByteBuffer src, ByteBuffer trg, FastByteOperations.ByteOperations ops) private void testCopy(byte[] canon, ByteBuffer src, ByteBuffer trg, FastByteOperations.ByteOperations ops)
{ {
byte[] result = new byte[src.remaining()]; byte[] result = new byte[src.remaining()];
@ -86,6 +141,38 @@ public class FastByteOperationsTest
} }
} }
@Test
public void testFastByteCompareRandomRead()
{
byte[] bytes1 = new byte[128];
for (int i = 0 ; i < 1000 ; i++)
{
rand.nextBytes(bytes1);
for (int j = 0 ; j < 16 ; j++)
{
byte[] bytes2 = Arrays.copyOf(bytes1, bytes1.length - j);
testTwiddleOneByteComparisonsReadOnly(bytes1, bytes2, 16, true, 1);
testTwiddleOneByteComparisonsReadOnly(bytes1, bytes2, 16, true, -1);
testTwiddleOneByteComparisonsReadOnly(bytes1, bytes2, 16, false, 1);
testTwiddleOneByteComparisonsReadOnly(bytes1, bytes2, 16, false, -1);
testTwiddleOneByteComparisonsReadOnly(bytes1, bytes2, 16, true, 128);
testTwiddleOneByteComparisonsReadOnly(bytes1, bytes2, 16, false, 128);
}
}
}
private void testTwiddleOneByteComparisonsReadOnly(byte[] bytes1, byte[] bytes2, int count, boolean start, int inc)
{
for (int j = 0 ; j < count ; j++)
{
int index = start ? j : bytes2.length - (j + 1);
bytes2[index] += inc;
testComparisonsReadOnly(bytes1, bytes2);
bytes2[index] -= inc;
}
}
private void testTwiddleOneByteComparisons(byte[] bytes1, byte[] bytes2, int count, boolean start, int inc) private void testTwiddleOneByteComparisons(byte[] bytes1, byte[] bytes2, int count, boolean start, int inc)
{ {
for (int j = 0 ; j < count ; j++) for (int j = 0 ; j < count ; j++)
@ -116,6 +203,45 @@ public class FastByteOperationsTest
return buf; return buf;
} }
private static ByteBuffer wrapR(byte[] bytes, boolean direct, int position)
{
return sliceR(bytes, direct ? dbuf1 : hbuf1, position);
}
private static ByteBuffer wrapR1(byte[] bytes, boolean direct, int position)
{
return sliceR(bytes, direct ? dbuf2 : hbuf2, position);
}
private static ByteBuffer sliceR(byte[] bytes, ByteBuffer buf, int position)
{
buf = buf.duplicate();
buf.put(bytes);
buf.position(position);
buf = buf.slice();
return buf;
}
private void testComparisonsReadOnly(byte[] bytes1, byte[] bytes2)
{
testComparison(bytes1, bytes2);
testComparison(bytes2, bytes1);
testComparison(wrap1(bytes1, false).asReadOnlyBuffer(), bytes2);
testComparison(wrap2(bytes2, false).asReadOnlyBuffer(), bytes1);
testComparison(wrap1(bytes1, false).asReadOnlyBuffer(), wrap2(bytes2, false));
testComparison(wrap2(bytes2, false).asReadOnlyBuffer(), wrap1(bytes1, false));
testComparison(wrap1(bytes1, true).asReadOnlyBuffer(), bytes2);
testComparison(wrap2(bytes2, true).asReadOnlyBuffer(), bytes1);
testComparison(wrap1(bytes1, true).asReadOnlyBuffer(), wrap2(bytes2, true));
testComparison(wrap2(bytes2, true).asReadOnlyBuffer(), wrap1(bytes1, true));
testComparison(wrap1(bytes1, true).asReadOnlyBuffer(), wrap2(bytes2, false));
testComparison(wrap1(bytes1, false).asReadOnlyBuffer(), wrap2(bytes2, true));
testComparison(wrap2(bytes2, true).asReadOnlyBuffer(), wrap1(bytes1, false));
testComparison(wrap2(bytes2, false).asReadOnlyBuffer(), wrap1(bytes1, true));
}
private void testComparisons(byte[] bytes1, byte[] bytes2) private void testComparisons(byte[] bytes1, byte[] bytes2)
{ {
testComparison(bytes1, bytes2); testComparison(bytes1, bytes2);

View File

@ -370,7 +370,7 @@ public final class Generators
public static Gen<ByteBuffer> bytes(int min, int max) public static Gen<ByteBuffer> bytes(int min, int max)
{ {
return bytes(min, max, SourceDSL.arbitrary().constant(BBCases.HEAP)); return bytes(min, max, SourceDSL.arbitrary().pick(BBCases.HEAP, BBCases.READ_ONLY_HEAP));
} }
public static Gen<ByteBuffer> directBytes(int min, int max) public static Gen<ByteBuffer> directBytes(int min, int max)