JDK-6231529 : (bf) ByteBuffer.reset throws InvalidMarkException if ByteBuffer.duplicate called when mark == 0
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2005-02-22
  • Updated: 2010-07-29
  • Resolved: 2005-07-22
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.
Other JDK 6
1.4.2_14Fixed 6 b45Fixed
Description
Error scenario:
A ByteBuffer has its mark currently set at position 0.
ByteBuffer.duplicate is used to create a duplicate ByteBuffer
from the original.  If the reset method is called on the
duplicate ByteBuffer, it throws InvalidMarkException, implying
that the mark was not set for the duplicate ByteBuffer.

Note that this problem only seems to occur if the mark was set
for position 0 when the original ByteBuffer was duplicated.

This was tested with every version of Java 1.4 that I could find
with the same results.  This includes using build 1.4.2_07-b05.

The problem could also be reproduced for 5.0 and 6.0, on both
Windows and Solaris; no attempts were made to test on other OS's
but the problem is probably OS-independent.


// ******************** START JAVA TEST CODE ********************
/**
 * Class to test using ByteBuffers.
 */
 
import java.nio.*;

class ByteBufferTest {
  
  public static void main(String args[]) {
    
    int setPosition = 0;
    ByteBuffer buf = ByteBuffer.allocateDirect(10);
    
    if (args.length > 0)
        try {setPosition = Integer.parseInt(args[0]);} catch (Exception e) {}

    System.out.println("Initializing position and mark to " + setPosition);
    buf.position(setPosition);
    buf.mark();

    ByteBuffer dup = buf.duplicate();

    try {
        buf.reset();
        System.out.println("Reset worked correctly for original buffer.");
    } catch (InvalidMarkException e) {
        System.out.println("Reset failed for original buffer.");        
    }

    try {
        dup.reset();
        System.out.println("Reset worked correctly for duplicate buffer.");
    } catch (InvalidMarkException e) {
        System.out.println("Reset failed for duplicate buffer.");        
    }

    System.out.println();
  }
}
// ******************** END JAVA TEST CODE ********************


REM ******************** START TEST BAT FILE ********************
@echo off

set JAVA_HOME=C:\j2sdk1.4.2_07

echo Compiling ...
%JAVA_HOME%\bin\javac ByteBufferTest.java


echo.
echo Running ...
%JAVA_HOME%\jre\bin\java -cp . ByteBufferTest 0
%JAVA_HOME%\jre\bin\java -cp . ByteBufferTest 1
%JAVA_HOME%\jre\bin\java -cp . ByteBufferTest 2


pause
REM ******************** END TEST BAT FILE ********************


When running the test bat file, the following output is produced:
Compiling ...

Running ...
Initializing position and mark to 0
Reset worked correctly for original buffer.
Reset failed for duplicate buffer.

Initializing position and mark to 1
Reset worked correctly for original buffer.
Reset worked correctly for duplicate buffer.

Initializing position and mark to 2
Reset worked correctly for original buffer.
Reset worked correctly for duplicate buffer.

Press any key to continue . . .
###@###.### 2005-2-22 05:31:11 GMT

Comments
EVALUATION I believe that a similar problem will be observed with X-Buffer.asReadOnlyBuffer when the buffer isn't read-only. From the spec of ByteBuffer.duplicate: The new buffer's capacity, limit, position, and mark values will be identical to those of this buffer. When calling ByteBuffer.duplicate, we eventually end up calling the package-private constructor in Buffer.java. From the Buffer.java class javadoc: The following invariant holds for the mark, position, limit, and capacity values: 0 <= mark <= position <= limit <= capacity A newly-created buffer always has a position of zero and a mark that is undefined. It looks like a minor modification is needed to the constructor in Buffer.java to handle the case of mark == 0: Buffer(int mark, int pos, int lim, int cap) { [ ... ] if (mark > 0) { // change to (mark >= 0) if (mark > pos) throw new IllegalArgumentException(); this.mark = mark; } } I don't believe that 0 is ever used in the buffer code to indicate an error condition or an unset mark, but close examination of all of the code in java.nio.*Buffer* should be made before this change is applied. ###@###.### 2005-2-22 21:12:25 GMT
22-02-2005