Skip to content

Core: Fix ByteBufferInputStream.read() to return -1 at EOF#16167

Open
sachinnn99 wants to merge 1 commit intoapache:mainfrom
sachinnn99:fix/16127-bytebufferinputstream-read-eof
Open

Core: Fix ByteBufferInputStream.read() to return -1 at EOF#16167
sachinnn99 wants to merge 1 commit intoapache:mainfrom
sachinnn99:fix/16127-bytebufferinputstream-read-eof

Conversation

@sachinnn99
Copy link
Copy Markdown
Contributor

Fixes #16127.

SingleBufferInputStream.read() and MultiBufferInputStream.read() throw EOFException when the stream is exhausted. This violates the java.io.InputStream contract, which requires the no-arg read() to return -1 at EOF.

The multi-byte read(byte[], int, int) in both classes already correctly returns -1 at EOF — the two overloads were inconsistent.

Changes:

  • SingleBufferInputStream.read(): return -1 instead of throwing EOFException
  • MultiBufferInputStream.read(): return -1 at both EOF entry points instead of throwing EOFException
  • Update testReadByte() to assert -1 return (including idempotency check)

Other EOFException-throwing methods (slice(), sliceBuffers(), skipFully()) are unchanged — they request specific byte counts where EOFException is the correct signal.

@github-actions github-actions Bot added the core label Apr 30, 2026
Copy link
Copy Markdown
Contributor

@steveloughran steveloughran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good for read(); looking at read(buffer[], offset, len), it seems ok too.

slice() probably needs attention at the same time

@sachinnn99
Copy link
Copy Markdown
Contributor Author

Thanks for the review! I looked at slice() — its EOFException is intentional since it's a "read exactly N bytes" operation (not part of the InputStream contract). Callers expect either the full slice or a failure, so throwing there is the correct behavior.

The fix here is scoped to the no-arg read(), which has a clear contract violation per InputStream.read() javadoc.

Copy link
Copy Markdown
Contributor

@Baunsgaard Baunsgaard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src LGTM.

But i would suggest a few additional test cases, just to ensure future coverage:

  1. invariants after the first -1 is encountered:
  2. an explicit empty case:

we can simply add a utility function to call with any stream to verify:

private static void assertAtEOF(ByteBufferInputStream stream) {
  long pos = stream.getPos();
  assertThat(stream.read()).as("read() at EOF").isEqualTo(-1);
  assertThat(stream.read()).as("read() should keep returning -1 at EOF").isEqualTo(-1);
  assertThat(stream.getPos()).as("Position should not advance past EOF").isEqualTo(pos);
  assertThat(stream.available()).as("available() should be 0 at EOF").isEqualTo(0);
}

To make full coverage of all paths you can add an additional test that calls the assertAtEOF with empty streams that are empty from the beginning.

@sachinnn99 sachinnn99 force-pushed the fix/16127-bytebufferinputstream-read-eof branch from 249ad40 to 9ea1f31 Compare May 8, 2026 05:38
@sachinnn99
Copy link
Copy Markdown
Contributor Author

Thanks for the suggestions @Baunsgaard! Added in the latest push:

  • assertAtEOF helper matching your snippet
  • Called it after EOF is first encountered in testReadAll, testSmallReads, testPartialBufferReads, and testReadByte
  • Added testEmptyStream covering single empty buffer, multiple empty buffers, and an empty buffer list

Copy link
Copy Markdown
Contributor

@Baunsgaard Baunsgaard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ByteBufferInputStream.read() throws EOFException at EOF instead of returning -1, violating InputStream contract

3 participants