ADDITIONAL SYSTEM INFORMATION :
$ java -version
java version "15.0.1" 2020-10-20
$ uname -a
Linux [redacted] 5.9.15-100.fc32.x86_64 #1 SMP Wed Dec 16 16:49:20 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
All the DirectXBuffer[US] types (beyond ByteBuffer) have a broken slice(int,int) method that fails to adjust the 'index' parameter by the primitive byte size. This leads to the wrong region being returned, and can read misaligned chars, shorts, ints, floats, doubles.
For example:
return new DirectCharBufferU(this,
-1,
0,
length,
length,
index, segment);
The penultimate argument, 'index', is the 'off' parameter of the constructor, and is measured in bytes.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a ByteBuffer, obtain a ShortBuffer view using asShortBuffer, create a slice using slice(int,int). See the example below showing the error.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The slice starts at the correct offset.
ACTUAL -
The slice starts at a partial offset.
---------- BEGIN SOURCE ----------
public class BufferSliceBug
{
public static void main( String[] args )
{
class BufferKind< T extends Buffer >
{
String name;
Function< ByteBuffer, T > asTypedBuffer;
ToIntFunction< T > get;
ObjIntConsumer< T > put;
BufferKind( String name, Function< ByteBuffer, T > asTypedBuffer, ToIntFunction< T > get, ObjIntConsumer< T > put )
{
this.name = name;
this.asTypedBuffer = asTypedBuffer;
this.get = get;
this.put = put;
}
void heapLE()
{
test( this.asTypedBuffer.apply( ByteBuffer.allocate( 32 ).order( ByteOrder.LITTLE_ENDIAN ) ) );
}
void heapBE()
{
test( this.asTypedBuffer.apply( ByteBuffer.allocate( 32 ).order( ByteOrder.BIG_ENDIAN ) ) );
}
void directLE()
{
test( this.asTypedBuffer.apply( ByteBuffer.allocateDirect( 32 ).order( ByteOrder.LITTLE_ENDIAN ) ) );
}
void directBE()
{
test( this.asTypedBuffer.apply( ByteBuffer.allocateDirect( 32 ).order( ByteOrder.BIG_ENDIAN ) ) );
}
private void test( T buffer )
{
// Fill buffer with primitives of values [0..]
fill( buffer );
// Take a slice and verify.
T slice = (T) buffer.slice( 1, buffer.capacity() - 1 );
verify( slice, 1 );
}
private void fill( T buffer )
{
int i = 0;
while ( buffer.hasRemaining() )
{
this.put.accept( buffer, i++ );
}
buffer.clear();
}
private void verify( T buffer, int from )
{
int i = from;
while ( buffer.hasRemaining() )
{
if ( this.get.applyAsInt( buffer ) != i )
{
System.out.println( "Broken : " + buffer.getClass().getSimpleName() );
return;
}
i++;
}
System.out.println( "Correct : " + buffer.getClass().getSimpleName() );
}
}
BufferKind< ? >[] kinds =
{
new BufferKind<>( "ByteBuffer", ByteBuffer::duplicate, ByteBuffer::get, ( buffer, value ) -> buffer.put( (byte) value ) ),
new BufferKind<>( "ShortBuffer", ByteBuffer::asShortBuffer, ShortBuffer::get, ( buffer, value ) -> buffer.put( (short) value ) ),
new BufferKind<>( "IntBuffer", ByteBuffer::asIntBuffer, IntBuffer::get, IntBuffer::put ),
new BufferKind<>( "LongBuffer", ByteBuffer::asLongBuffer, buffer -> (int) buffer.get(), LongBuffer::put ),
new BufferKind<>( "FloatBuffer", ByteBuffer::asFloatBuffer, buffer -> (int) buffer.get(), FloatBuffer::put ),
new BufferKind<>( "DoubleBuffer", ByteBuffer::asDoubleBuffer, buffer -> (int) buffer.get(), DoubleBuffer::put ),
new BufferKind<>( "CharBuffer", ByteBuffer::asCharBuffer, CharBuffer::get, ( buffer, value ) -> buffer.put( (char) value ) ),
};
for ( BufferKind< ? > kind : kinds )
{
System.out.println( kind.name );
kind.heapLE();
kind.heapBE();
kind.directLE();
kind.directBE();
System.out.println();
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use Buffer.position(), Buffer.limit(), Buffer.slice() and then reset the Buffer to its original position/limit.
FREQUENCY : always