Table of Contents

HashingStream Class

Definition

Namespace
Bodu.IO.Hashing
Assembly
Bodu.IO.Hashing.dll
Package
Bodu.IO.Hashing 1.0.0
Source
HashingStream.cs

Provides a pass-through Stream that feeds every byte read from or written to an inner stream into a NonCryptographicHashAlgorithm, so a digest can be computed while data is being transferred.

public sealed class HashingStream : Stream, IAsyncDisposable, IDisposable
Inheritance
HashingStream
Implements
Inherited Members
Extension Methods

Examples

// Compute a CRC-32 over a payload while streaming it to its destination - a single pass over the bytes.
using var source = File.OpenRead("payload.bin");
using var destination = File.Create("payload.copy");
using var hashing = new HashingStream(source, new Crc32());

hashing.CopyTo(destination);   // every byte read from the source is fed to the CRC-32

byte[] checksum = hashing.GetCurrentHash();

Remarks

The stream is direction-agnostic: CanRead and CanWrite delegate to the inner stream, and bytes are appended to the algorithm whichever way they flow. Reads append only the bytes actually returned by the inner stream; writes append after the inner stream has accepted the bytes.

Seeking is not supported (CanSeek is always false): repositioning would let bytes bypass or re-enter the digest, silently corrupting it.

GetCurrentHash() reports the digest of the bytes transferred so far without finalizing the ongoing computation; GetHashAndReset() additionally resets the algorithm. The algorithm instance is supplied by the caller, is not disposed by this stream, and - like all NonCryptographicHashAlgorithm instances - is not thread-safe, so concurrent reads and writes through the same HashingStream are not supported.

Constructors

HashingStream(Stream, NonCryptographicHashAlgorithm, bool)

Initializes a new instance of the HashingStream class over the specified inner stream and hash algorithm.

public HashingStream(Stream innerStream, NonCryptographicHashAlgorithm algorithm, bool leaveOpen = false)

Parameters

innerStream Stream

The stream to transfer bytes to or from. Must not be null.

algorithm NonCryptographicHashAlgorithm

The algorithm that accumulates the transferred bytes. Must not be null.

leaveOpen bool

true to leave innerStream open after this stream is disposed; otherwise, false.

Exceptions

ArgumentNullException

innerStream or algorithm is null.

Properties

Algorithm

Gets the algorithm that accumulates the bytes transferred through this stream.

public NonCryptographicHashAlgorithm Algorithm { get; }

Property Value

NonCryptographicHashAlgorithm

The NonCryptographicHashAlgorithm supplied to the constructor.

CanRead

Gets a value indicating whether the stream supports reading.

public override bool CanRead { get; }

Property Value

bool

true if this stream is not disposed and the inner stream supports reading; otherwise, false.

CanSeek

Gets a value indicating whether the stream supports seeking.

public override bool CanSeek { get; }

Property Value

bool

Always false; repositioning would corrupt the digest.

CanWrite

Gets a value indicating whether the stream supports writing.

public override bool CanWrite { get; }

Property Value

bool

true if this stream is not disposed and the inner stream supports writing; otherwise, false.

Length

Gets the length of the stream. Not supported.

public override long Length { get; }

Property Value

long

This property always throws.

Exceptions

NotSupportedException

Always thrown; the stream does not support seeking.

Position

Gets or sets the position within the stream. Not supported.

public override long Position { get; set; }

Property Value

long

This property always throws.

Exceptions

NotSupportedException

Always thrown; the stream does not support seeking.

Methods

Dispose(bool)

Releases the stream, disposing the inner stream unless leaveOpen was specified. The algorithm is never disposed.

protected override void Dispose(bool disposing)

Parameters

disposing bool

true when called from Dispose().

DisposeAsync()

Asynchronously releases the stream, disposing the inner stream unless leaveOpen was specified. The algorithm is never disposed.

public override ValueTask DisposeAsync()

Returns

ValueTask

A task representing the asynchronous dispose.

Flush()

Flushes the inner stream.

public override void Flush()

Exceptions

ObjectDisposedException

The stream has been disposed.

FlushAsync(CancellationToken)

Asynchronously flushes the inner stream.

public override Task FlushAsync(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

A token used to cancel the operation.

Returns

Task

A task representing the asynchronous flush.

Exceptions

ObjectDisposedException

The stream has been disposed.

GetCurrentHash()

Returns the digest of the bytes transferred so far without resetting the ongoing computation.

public byte[] GetCurrentHash()

Returns

byte[]

The current digest, sized per the algorithm's hash length.

Exceptions

ObjectDisposedException

The stream has been disposed.

GetHashAndReset()

Returns the digest of the bytes transferred so far and resets the algorithm to its initial state.

public byte[] GetHashAndReset()

Returns

byte[]

The digest accumulated since construction or the previous reset.

Examples

using var hashing = new HashingStream(connection, new Crc32(), leaveOpen: true);
foreach (var frame in frames)
{
    hashing.Write(frame);
    byte[] frameDigest = hashing.GetHashAndReset();   // digest of this frame only
    Send(frameDigest);
}

Remarks

Resetting between segments lets a single HashingStream produce an independent digest per framed message over one connection, without allocating a new stream for each frame.

Exceptions

ObjectDisposedException

The stream has been disposed.

Read(byte[], int, int)

Reads bytes from the inner stream into the buffer, appending the bytes actually read to the digest.

public override int Read(byte[] buffer, int offset, int count)

Parameters

buffer byte[]

The destination buffer. Must not be null.

offset int

The zero-based offset in buffer at which to begin storing data.

count int

The maximum number of bytes to read.

Returns

int

The number of bytes read, or 0 at end of stream.

Exceptions

ArgumentNullException

buffer is null.

ArgumentOutOfRangeException

offset or count is negative.

ArgumentException

offset + count exceeds the buffer length.

ObjectDisposedException

The stream has been disposed.

Read(Span<byte>)

Reads bytes from the inner stream into the span, appending the bytes actually read to the digest.

public override int Read(Span<byte> buffer)

Parameters

buffer Span<byte>

The destination span.

Returns

int

The number of bytes read, or 0 at end of stream.

Exceptions

ObjectDisposedException

The stream has been disposed.

ReadAsync(byte[], int, int, CancellationToken)

Asynchronously reads bytes from the inner stream into the buffer, appending the bytes actually read to the digest.

public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)

Parameters

buffer byte[]

The destination buffer. Must not be null.

offset int

The zero-based offset in buffer at which to begin storing data.

count int

The maximum number of bytes to read.

cancellationToken CancellationToken

A token used to cancel the operation.

Returns

Task<int>

A task producing the number of bytes read, or 0 at end of stream.

Exceptions

ArgumentNullException

buffer is null.

ArgumentOutOfRangeException

offset or count is negative.

ArgumentException

offset + count exceeds the buffer length.

ObjectDisposedException

The stream has been disposed.

ReadAsync(Memory<byte>, CancellationToken)

Asynchronously reads bytes from the inner stream into the memory region, appending the bytes actually read to the digest.

public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)

Parameters

buffer Memory<byte>

The destination memory region.

cancellationToken CancellationToken

A token used to cancel the operation.

Returns

ValueTask<int>

A task producing the number of bytes read, or 0 at end of stream.

Exceptions

ObjectDisposedException

The stream has been disposed.

ReadByte()

Reads a single byte from the inner stream, appending it to the digest when one is available.

public override int ReadByte()

Returns

int

The byte read, or -1 at end of stream.

Exceptions

ObjectDisposedException

The stream has been disposed.

Seek(long, SeekOrigin)

Seeks within the stream. Not supported.

public override long Seek(long offset, SeekOrigin origin)

Parameters

offset long

The byte offset relative to origin. Not used; the method always throws.

origin SeekOrigin

The reference point for offset. Not used; the method always throws.

Returns

long

This method always throws.

Exceptions

NotSupportedException

Always thrown; the stream does not support seeking.

SetLength(long)

Sets the length of the stream. Not supported.

public override void SetLength(long value)

Parameters

value long

Ignored.

Exceptions

NotSupportedException

Always thrown; the stream does not support seeking.

Write(byte[], int, int)

Writes bytes to the inner stream and appends them to the digest.

public override void Write(byte[] buffer, int offset, int count)

Parameters

buffer byte[]

The source buffer. Must not be null.

offset int

The zero-based offset in buffer at which to begin reading data.

count int

The number of bytes to write.

Exceptions

ArgumentNullException

buffer is null.

ArgumentOutOfRangeException

offset or count is negative.

ArgumentException

offset + count exceeds the buffer length.

ObjectDisposedException

The stream has been disposed.

Write(ReadOnlySpan<byte>)

Writes bytes to the inner stream and appends them to the digest.

public override void Write(ReadOnlySpan<byte> buffer)

Parameters

buffer ReadOnlySpan<byte>

The source span.

Remarks

Bytes are appended to the digest after the inner stream accepts the write, so a failed write does not contaminate the digest.

Exceptions

ObjectDisposedException

The stream has been disposed.

WriteAsync(byte[], int, int, CancellationToken)

Asynchronously writes bytes to the inner stream and appends them to the digest.

public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)

Parameters

buffer byte[]

The source buffer. Must not be null.

offset int

The zero-based offset in buffer at which to begin reading data.

count int

The number of bytes to write.

cancellationToken CancellationToken

A token used to cancel the operation.

Returns

Task

A task representing the asynchronous write.

Exceptions

ArgumentNullException

buffer is null.

ArgumentOutOfRangeException

offset or count is negative.

ArgumentException

offset + count exceeds the buffer length.

ObjectDisposedException

The stream has been disposed.

WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Asynchronously writes bytes to the inner stream and appends them to the digest.

public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)

Parameters

buffer ReadOnlyMemory<byte>

The source memory region.

cancellationToken CancellationToken

A token used to cancel the operation.

Returns

ValueTask

A task representing the asynchronous write.

Exceptions

ObjectDisposedException

The stream has been disposed.

WriteByte(byte)

Writes a single byte to the inner stream and appends it to the digest.

public override void WriteByte(byte value)

Parameters

value byte

The byte to write.

Exceptions

ObjectDisposedException

The stream has been disposed.

Applies to

ProductVersions
.NET8, 10