JDK-7073237 : ByteArrayInputStream violates contract of read(byte[])
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86
  • Submitted: 2011-07-31
  • Updated: 2012-03-20
  • Resolved: 2011-08-01
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) 64-Bit Server VM (build 21.0-b17, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7600]

A DESCRIPTION OF THE PROBLEM :
This applies to at least both of ByteArrayInputStream and StringBufferInputStream, in both Java 6 and Java 7.

Neither of these classes overrides the method "read(byte[])"  inherited from java.io.InputSream.  The documentation of the "read(byte[])" method therefore is left unmodified, and states:

"If the length of b is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at the end of the file, the value -1 is returned; otherwise, at least one byte is read and stored into b. "

Unfortunately neither ByteArrayInputStream nor StringBufferInputStream behaves in this way.  In both cases, the check for the current position is made BEFORE checking the length of the array; if an array of length zero is passed in and the stream is at EOF, the result will be -1 instead of zero.

You may ask yourself: is this really a problem?  Most definitely yes.  I discovered this because we have some unit testing code that simulates the network stack with in-memory ByteArrayInputStream containing manufactured networking frames - a pretty reasonable use case - and the unit tests fail where the production code succeeds because this behavior is different.

In an ideal world I would suggest that both ByteArrayInputStream and StringBufferInputStream should do the array length check prior to checking for EOF, as that would make them consistent with InputStream itself; however that would break binary compatibility, so I suggest as an alternative that the "read(byte[])" method be trivially overridden in both classes so that an updated and factually correct Javadoc can be placed on those methods.  As it stands right now, the Javadoc for these two classes does not correctly describe their behavior.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a byte array of size 1
2. Create a ByteArrayInputStream wrapping that byte array
3. Call the parameter-less read() method one time on the input stream to precisely consume all of the stream's data
4. Create a zero-size byte array
5. Pass this byte array to the input stream's "read(byte[])" method
6. Observe that the result is -1 instead of 0

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
read(byte[]) should return 0 when the stream is at EOF and a zero-sized array is passed in.
ACTUAL -
read(byte[]) actually returns -1 when the stream is at EOF and a zero-sized array is passed in.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.ByteArrayInputStream;

public class Test {
    public final static void main(String[] args) throws Exception {
        byte[] backingBuffer = new byte[1];
        ByteArrayInputStream stream = new ByteArrayInputStream(backingBuffer);
        stream.read();

        int numRead = stream.read(new byte[]{});
        System.out.println("numRead=" + numRead);
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Unfortunately the workaround requires adding special case logic to stop the call to read() from being made in the first place if the byte array is of size 0.  This is certainly not hard, but should not be required.

I note that the fix for this issue requires only that the documentation be updated.