JDK-8258955 : (bf) slice(int, int) on view buffers fails to adjust index according to primitive size
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 13,15,16
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2020-12-24
  • Updated: 2021-01-20
  • Resolved: 2021-01-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 16 JDK 17
16 b31Fixed 17Fixed
Related Reports
Relates :  
Description
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



Comments
Verification from the submitter: Yes, I have checked with the latest JDK 16 build, and it works correctly now.
15-01-2021

Requested the submitter verify the fix with latest version of JDK 16.
13-01-2021

Changeset: 73f54153 Author: Chris Hegarty <chegar@openjdk.org> Date: 2021-01-02 19:29:50 +0000 URL: https://git.openjdk.java.net/jdk16/commit/73f54153
02-01-2021

An oversight in the original implementation when it was added in Java 13, this might be a bug to fix in JDK 16 or 16.0.1.
30-12-2020

The observations on Windows 10: JDK 15: Failed, got incorrect results JDK 16: Failed.
26-12-2020