Skip to content

Conversation

@nickita-khylkouski
Copy link

Summary

  • Add boolean closed field to the OutputStream returned by encodingStream()
  • Make write(), flush(), and close() synchronized per @cpovirk's guidance
  • close() returns early if already closed (idempotent)
  • write()/flush() throw IOException after close, matching AppendableWriter and CharSequenceReader patterns

Motivation

BaseEncoding.encodingStream().close() violates the Closeable.close() contract which states: "If the stream is already closed then invoking this method has no effect."

Calling close() twice writes additional encoded data to the underlying Writer:

StringWriter w = new StringWriter();
OutputStream out = BaseEncoding.base64().encodingStream(w);
out.write(0);
out.close();
System.out.println(w); // "AA==" (correct)
out.close();
System.out.println(w); // "AA==A===" (wrong - extra data)

This is problematic because double-close is common due to try-with-resources blocks and wrapping streams that close their underlying streams.

Fixes #5284

Testing

  • 3 new tests in BaseEncodingTest:
    • testEncodingStreamCloseIsIdempotent — verifies close() twice doesn't change output
    • testEncodingStreamWriteAfterClose — verifies write() throws IOException after close
    • testEncodingStreamFlushAfterClose — verifies flush() throws IOException after close
  • All 43 tests in BaseEncodingTest pass (./mvnw test -pl guava-tests -Dtest=BaseEncodingTest)
  • Added IOException to ReflectionFreeAssertThrows to support the new test assertions

The OutputStream returned by encodingStream() violated the
Closeable.close() contract: calling close() multiple times would
write additional encoded data to the underlying Writer. This adds
a boolean closed field and synchronized methods to track stream
state, making close() idempotent and causing write()/flush() to
throw IOException after close.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BaseEncoding.encodingStream().close() is non-idempotent

1 participant