Skip to content

Streams

The library provides two Stream implementations for working with byte sequences, plus extension methods on Stream for typed reading and writing. See the Reading and Writing page for full details of the typed Stream extension methods.

ReadOnlyListStream

ReadOnlyListStream wraps an IReadOnlyList<byte> as a seekable, read-only Stream. It adapts any list of bytes for use with APIs that consume a Stream, without copying the underlying data.

IReadOnlyList<byte> bytes = new List<byte> { 0x01, 0x00, 0x00, 0x00 };
using var stream = new ReadOnlyListStream(bytes);

int value = stream.ReadInt32OrThrow(); // 1

The stream supports seeking via Seek and the Position property. CanRead and CanSeek return true while the stream is open. CanWrite always returns false; any attempt to write or resize the stream throws NotSupportedException.

PeekableStream

PeekableStream wraps any readable Stream and adds the ability to inspect the next byte without consuming it. This is useful for parsers that need to look ahead one byte before deciding how to proceed.

using var inner = new MemoryStream(new byte[] { 0x01, 0x02, 0x03 });
using var stream = new PeekableStream(inner);

int next = stream.Peek(); // 1, position stays at 0
int read = stream.ReadByte(); // 1, position advances to 1

Peek() returns the next byte as an integer in the range 0..255, or -1 if the end of the stream has been reached. Calling Peek() multiple times without reading returns the same byte each time. Any read or seek operation that moves the position will reset the peeked value.

The EndOfStream property is a convenience that calls Peek() and returns true if the result is -1:

while (!stream.EndOfStream)
{
    byte b = stream.ReadByteOrThrow();
    // process b
}

Construction and Ownership

The constructor accepts a leaveOpen parameter (default true) that controls whether the underlying stream is disposed when the PeekableStream is disposed. Set it to false to transfer ownership:

// The underlying stream will be disposed when the PeekableStream is disposed.
using var stream = new PeekableStream(inner, leaveOpen: false);

The stream must be readable; passing a non-readable stream throws ArgumentException.

CanRead and CanSeek reflect the underlying stream's capabilities while the wrapper is open. CanWrite always returns false.

StreamExtensions

StreamExtensions adds typed read and write methods directly to Stream, covering all the multi-byte primitive types supported by the library. See Reading and Writing: Stream for full details and examples.

The methods available are:

Reading (synchronous): ReadByteOrThrow, ReadExactly, ReadAllBytes, ReadInt16OrThrow, ReadUInt16OrThrow, ReadUInt24OrThrow, ReadInt32OrThrow, ReadUInt32OrThrow, ReadInt64OrThrow, ReadUInt64OrThrow.

Writing (synchronous): WriteInt16, WriteUInt16, WriteUInt24, WriteInt32, WriteUInt32, WriteInt64, WriteUInt64.

Reading (asynchronous): ReadByteOrThrowAsync, ReadExactlyAsync, ReadAllBytesAsync, ReadInt16OrThrowAsync, ReadUInt16OrThrowAsync, ReadUInt24OrThrowAsync, ReadInt32OrThrowAsync, ReadUInt32OrThrowAsync, ReadInt64OrThrowAsync, ReadUInt64OrThrowAsync.

Writing (asynchronous): WriteInt16Async, WriteUInt16Async, WriteUInt24Async, WriteInt32Async, WriteUInt32Async, WriteInt64Async, WriteUInt64Async.