Class AbstractPassthroughInputStream

java.lang.Object
java.io.InputStream
java.io.FilterInputStream
zeroecho.core.io.AbstractPassthroughInputStream
All Implemented Interfaces:
Closeable, AutoCloseable

public abstract class AbstractPassthroughInputStream extends FilterInputStream
Passthrough InputStream that forwards body bytes and optionally emits a trailer at EOF.

This abstract helper implements a two-phase read pipeline suitable for tag-producing or verifying stages. During the BODY phase, bytes are pulled from the wrapped upstream stream and immediately exposed to the caller while update(byte[], int, int) is invoked so subclasses can maintain running state (for example, hash, MAC, or signature update). After upstream EOF, the TRAILER phase runs once: produceTrailer(byte[]) may write a finite trailer into an internal buffer, and onCompleted() is called exactly once.

Phases

Subclassing contract

  • update(byte[], int, int) must be side-effect free with respect to the bytes already returned to the caller; it is for state maintenance only.
  • produceTrailer(byte[]) must write at most buf.length bytes and return the count, or return 0 to produce no trailer. If more space is required, it must throw IOException.
  • onCompleted() is invoked exactly once after produceTrailer returns, regardless of whether a trailer was produced; it should release resources and may perform final checks. It must not attempt to emit more bytes.

Thread-safety

Instances are not thread-safe. Use a separate instance per stream pipeline.

Example


final class DigestingStream extends AbstractPassthroughInputStream {
    private final MessageDigest md;
    DigestingStream(InputStream upstream, MessageDigest md) {
        super(upstream, 8192);
        this.md = md;
    }
    @Override protected void update(byte[] buf, int off, int len) { md.update(buf, off, len); }
    @Override protected int produceTrailer(byte[] buf) {
        byte[] tag = md.digest();
        System.arraycopy(tag, 0, buf, 0, tag.length);
        return tag.length; // append digest at EOF
    }
    @Override protected void onCompleted() { /* publish metrics, close resources, etc. *\/ }
}

  • Field Details

    • inBuf

      protected final byte[] inBuf
      Scratch buffer used to read from FilterInputStream.in. Also used once to hold the trailer, if any.
  • Constructor Details

    • AbstractPassthroughInputStream

      protected AbstractPassthroughInputStream(InputStream upstream, int bodyBufSize)
      Constructs a new passthrough stream with an internal buffer sized for body reads.

      The buffer is allocated as the maximum of 1024 and the requested bodyBufSize, ensuring a reasonable minimum size even if the caller specifies a very small value. The buffer is reused both for normal body reads and, if applicable, for the single trailer emission.

      Parameters:
      upstream - source input stream to wrap
      bodyBufSize - requested size of the body buffer, in bytes
      Throws:
      NullPointerException - if upstream is null
  • Method Details

    • update

      protected abstract void update(byte[] buf, int off, int len) throws IOException
      Called for each body chunk just read from the upstream.

      Implementations may update running state (e.g., hash or MAC) using the bytes in buf[off..off+len). Implementations must not modify the visible output of the current chunk.

      Parameters:
      buf - buffer containing the newly read bytes
      off - start offset
      len - number of bytes read
      Throws:
      IOException - if processing the chunk fails
    • produceTrailer

      protected abstract int produceTrailer(byte[] buf) throws IOException
      Produces an optional trailer after upstream EOF.

      This method is called at most once, after the BODY phase has ended. Implementations should write the trailer bytes (if any) into the provided buffer and return the number of bytes written. A return value of 0 means no trailer will be emitted.

      Contract: This method must be side-effect free with respect to subsequent reads; it is followed immediately by a call to onCompleted(). If a trailer cannot fit into buf, implementations should throw IOException to avoid truncation.

      Parameters:
      buf - destination buffer to receive the trailer
      Returns:
      number of bytes written to buf; 0 to emit no trailer
      Throws:
      IOException - if trailer production fails or the trailer does not fit into buf
    • onCompleted

      protected abstract void onCompleted() throws IOException
      Notifies that the stream has fully completed.

      This method is invoked exactly once, immediately after produceTrailer(byte[]) returns, regardless of whether a trailer was produced. Implementations typically perform final verification, publish flags, and/or release resources here. This method should be idempotent and must not attempt to write further bytes.

      Throws:
      IOException - if final verification or resource release fails
    • read

      public int read() throws IOException
      Reads the next byte from this stream.
      Overrides:
      read in class FilterInputStream
      Returns:
      next byte as an unsigned int (0..255), or -1 if no more data is available
      Throws:
      IOException - if an I/O error occurs
    • read

      public int read(byte[] b, int off, int len) throws IOException
      Reads some bytes from this stream into the specified array.
      Overrides:
      read in class FilterInputStream
      Parameters:
      b - target buffer; must not be null
      off - start offset within b
      len - maximum number of bytes to read
      Returns:
      number of bytes read, or -1 if end-of-stream is reached
      Throws:
      IOException - if an I/O error occurs
      NullPointerException - if b is null
      IndexOutOfBoundsException - if off or len are invalid for b
    • close

      public void close() throws IOException
      Drains remaining data (including any trailer) and then closes the upstream stream.

      This method transfers all remaining bytes to OutputStream.nullOutputStream(), which guarantees that the trailer (if any) is produced and onCompleted() is invoked, and finally calls super.close().

      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable
      Overrides:
      close in class FilterInputStream
      Throws:
      IOException - if an I/O error occurs while draining or closing