Class TailStrippingInputStream
- All Implemented Interfaces:
Closeable, AutoCloseable
- Direct Known Subclasses:
SignatureTrailerInputStream
Overview
This stream is useful when the final bytes carry out-of-band metadata such as checksums, MAC tags, or trailers that must not appear in the payload. During normal reads it buffers enough data to ensure the lasttailLen bytes are never exposed. When the upstream reaches
EOF, the accumulated tail is passed to processTail(byte[]) exactly
once.
How it works
- A sliding window of size
tailLen + ioBufferis maintained. - While reading, any overflow beyond
tailLenis immediately emitted to the caller; the finaltailLenbytes are withheld in the window. - On EOF, the remaining bytes in the window (length
<= tailLen) are delivered toprocessTail(byte[])and never returned fromread().
Behavioral notes
- If the entire stream is shorter than
tailLen, no payload bytes are ever emitted; the complete content is provided toprocessTail(byte[]). close()drains the upstream to ensure tail processing happens even if the caller stops reading early.- Mark/reset is not supported.
Typical use
InputStream raw = ...; // e.g., file or network
int tagBytes = 16; // withhold a 16-byte authentication tag at the end
TailStrippingInputStream in = new TailStrippingInputStream(raw, tagBytes, 4096) {
@Override
protected void processTail(byte[] tail) throws IOException {
// verify MAC, parse footer, etc.
}
};
// Read payload only (tag withheld and never exposed here)
byte[] buf = in.readAllBytes();
in.close(); // ensures processTail(...) is invoked if not already
Thread-safety
Instances are not thread-safe. Use a separate instance per stream.- Since:
- 1.0
-
Constructor Summary
ConstructorsConstructorDescriptionTailStrippingInputStream(InputStream upstream, int tailLen, int ioBuffer) Creates a new tail-stripping stream around an upstream source. -
Method Summary
Modifier and TypeMethodDescriptionintReturns an estimate of the number of bytes that can be read without blocking.voidclose()Closes this stream and the underlying upstream stream.booleanIndicates that mark/reset is not supported.protected abstract voidprocessTail(byte[] tail) Hook invoked exactly once at end of stream with the withheld tail bytes.intread()Reads and returns a single byte of payload data.intread(byte[] b, int off, int len) Reads up tolenbytes of payload into the provided buffer.voidreset()Unsupported operation; always throws.intReturns the number of bytes stripped from the end of the stream.Methods inherited from class InputStream
mark, nullInputStream, read, readAllBytes, readNBytes, readNBytes, skip, skipNBytes, transferTo
-
Constructor Details
-
TailStrippingInputStream
Creates a new tail-stripping stream around an upstream source.The internal sliding window is allocated with capacity
tailLen + max(1024, ioBuffer). The parameterioBuffercontrols incremental emission granularity;tailLencontrols how many bytes are withheld until EOF.- Parameters:
upstream- non-null source streamtailLen- number of bytes to strip and deliver toprocessTail(byte[]); must be >= 1ioBuffer- additional working buffer size; must be >= 1 (actual allocation ismax(1024, ioBuffer))- Throws:
NullPointerException- ifupstreamis nullIllegalArgumentException- iftailLen < 1orioBuffer < 1
-
-
Method Details
-
processTail
Hook invoked exactly once at end of stream with the withheld tail bytes.The provided array contains the last
min(totalBytesRead, tailLen)bytes of the upstream stream. For short streams, this may be smaller thantailLenand can even contain the entire stream contents. Implementations typically verify or parse the trailer here.This method is called from the reading thread either upon the first read that observes upstream EOF or during
close()if the stream was not fully consumed. It is never called more than once.- Parameters:
tail- the withheld tail bytes (never null; length in [0, tailLen])- Throws:
IOException- if processing the tail fails
-
read
Reads and returns a single byte of payload data.This method never returns bytes belonging to the tail. On end of stream,
-1is returned andprocessTail(byte[])is invoked if not already processed.- Specified by:
readin classInputStream- Returns:
- the next payload byte (0..255) or
-1if no more payload is available - Throws:
IOException- if an I/O error occurs, if the stream is closed, or if tail processing fails
-
read
Reads up tolenbytes of payload into the provided buffer.The bytes returned exclude the last
tailLenbytes of the upstream stream. When EOF is reached, this method returns-1and ensuresprocessTail(byte[])is invoked exactly once with the withheld tail. If the stream is shorter thantailLen, this method returns-1immediately once EOF is known and the entire content is reported viaprocessTail.- Overrides:
readin classInputStream- Parameters:
b- destination buffer; must not be nulloff- start offset inblen- maximum number of bytes to read- Returns:
- the number of payload bytes read, or
-1if the payload is finished - Throws:
NullPointerException- ifbis nullIndexOutOfBoundsException- ifofforlenare invalid forbIOException- if an I/O error occurs or if the stream is closed
-
close
Closes this stream and the underlying upstream stream.If the upstream has not yet reached EOF, this method drains it to force tail detection and ensures
processTail(byte[])is invoked exactly once. Subsequent calls are ignored.- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable- Overrides:
closein classInputStream- Throws:
IOException- if an I/O error occurs while draining or closing
-
available
Returns an estimate of the number of bytes that can be read without blocking.This includes any previously prepared payload bytes and the upstream's reported availability. Note that upstream availability may over-report relative to payload because the last
tailLenbytes are withheld.- Overrides:
availablein classInputStream- Returns:
- the number of bytes that can be read without blocking
- Throws:
IOException- if an I/O error occurs in the upstream
-
markSupported
public boolean markSupported()Indicates that mark/reset is not supported.- Overrides:
markSupportedin classInputStream- Returns:
- always
false
-
reset
Unsupported operation; always throws.- Overrides:
resetin classInputStream- Throws:
IOException- always thrown because mark/reset is not supported
-
tailLength
public int tailLength()Returns the number of bytes stripped from the end of the stream.- Returns:
- the configured tail length in bytes
-