Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java InputStream short reads: assemble a complete record header

Last updated: 5 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

InputStream.read(byte[]) may return fewer bytes than requested even when the stream has not ended.

Treat the return count as the contract

A read call reports how many bytes it placed into the destination, or -1 at end of stream. A network stream, decompressor, or wrapper can deliver only part of a header. Parse only after the declared header width is present. A short positive read is progress, not proof of truncation.

The loop below reads a four-byte message length from a stream that deliberately releases one byte per call. It rejects EOF before four bytes arrive. Follow the length with a separate payload boundary; a valid header never proves that the body is complete.

Keep parser state local

Do not publish a partially filled header to downstream code. The caller receives a length only after all four bytes are assembled. A real framing protocol should also reject negative lengths and cap allocations before reading its payload; byte-stream copying covers the general read loop.

Working program

Java
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;

public class InventoryFrameHeader {
    static int readLength(InputStream source) throws IOException {
        byte[] header = new byte[4];
        int received = 0;
        while (received < header.length) {
            int count = source.read(header, received, header.length - received);
            if (count == -1) throw new EOFException("short header");
            if (count == 0) continue;
            received += count;
        }
        return ((header[0] & 255) << 24) | ((header[1] & 255) << 16)
                | ((header[2] & 255) << 8) | (header[3] & 255);
    }

    public static void main(String[] args) throws IOException {
        byte[] wire = {0, 0, 0, 47};
        InputStream throttled = new ByteArrayInputStream(wire) {
            @Override public synchronized int read(byte[] target, int offset, int length) {
                return super.read(target, offset, Math.min(length, 1));
            }
        };
        System.out.println(readLength(throttled));
        try { readLength(new ByteArrayInputStream(new byte[] {0, 0, 0})); }
        catch (EOFException truncated) { System.out.println(truncated.getMessage()); }
    }
}

Output

Output
47
short header

Cost and ownership

The header uses four bytes of fixed storage and O(4) read work, although a slow source can require four separate calls. A production loop also needs a deadline or cancellation policy for sources that can stall; the byte count alone does not impose one.

Common Mistakes

  • Do not assume one read fills the requested array.
  • Do not parse a header after a positive but short read.
  • Do not allocate a body directly from an unchecked length field.

Read next

readnbytes truncated frame, datainputstream readfully eof, Java byte streams: partial reads and bounded copying, inputstream available not length.

java
io
inputstream-short-read-loop
Storage details