Skip to content

Instantly share code, notes, and snippets.

@Jozkee
Created December 6, 2022 20:35
Show Gist options
  • Save Jozkee/27aa6ab757e8d87dc3ef27d7be69d9d3 to your computer and use it in GitHub Desktop.
Save Jozkee/27aa6ab757e8d87dc3ef27d7be69d9d3 to your computer and use it in GitHub Desktop.
Implementation of NewStream with Template Method Pattern
namespace System.IO;
public abstract class NewStream : Stream
{
private bool _disposed;
public sealed override int Read(byte[] buffer, int offset, int count)
{
ValidateBufferArguments(buffer, offset, count);
return Read(buffer.AsSpan(offset, count));
}
public sealed override int Read(Span<byte> buffer)
{
EnsureCanRead();
EnsureNotDisposed();
return ReadCore(buffer);
}
protected abstract int ReadCore(Span<byte> buffer);
public sealed override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
ValidateBufferArguments(buffer, offset, count);
return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
}
public sealed override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
EnsureCanRead();
EnsureNotDisposed();
return base.ReadAsync(buffer, cancellationToken);
}
public sealed override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
=> TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state);
public override int EndRead(IAsyncResult asyncResult)
{
EnsureNotDisposed();
return TaskToApm.End<int>(asyncResult);
}
protected abstract ValueTask<int> ReadCoreAsync(Memory<byte> buffer, CancellationToken cancellationToken);
public sealed override void Write(byte[] buffer, int offset, int count)
{
ValidateBufferArguments(buffer, offset, count);
Read(buffer.AsSpan(offset, count));
}
public sealed override void Write(ReadOnlySpan<byte> buffer)
{
EnsureCanWrite();
EnsureNotDisposed();
WriteCore(buffer);
}
protected abstract void WriteCore(ReadOnlySpan<byte> buffer);
public sealed override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
ValidateBufferArguments(buffer, offset, count);
return WriteAsync(buffer.AsMemory(offset, count)).AsTask();
}
public sealed override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
EnsureCanWrite();
EnsureNotDisposed();
return WriteCoreAsync(buffer, cancellationToken);
}
public sealed override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
=> TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state);
public override void EndWrite(IAsyncResult asyncResult)
{
EnsureNotDisposed();
TaskToApm.End(asyncResult);
}
protected abstract ValueTask WriteCoreAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken);
public override long Seek(long offset, SeekOrigin origin)
{
EnsureNotDisposed();
EnsureCanSeek();
long pos = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.Current => Position + offset,
SeekOrigin.End => Length + offset,
_ => throw new ArgumentException("Invalid seek origin", nameof(origin))
};
if (pos < 0)
{
throw new ArgumentOutOfRangeException();
}
return SeekCore(offset, origin);
}
protected abstract long SeekCore(long offset, SeekOrigin origin);
public override void SetLength(long value)
{
EnsureNotDisposed();
EnsureCanSeek();
EnsureCanWrite();
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
SetLengthCore(value);
}
protected abstract void SetLengthCore(long value);
protected override void Dispose(bool disposing)
{
_disposed = true;
base.Dispose(disposing);
}
private void EnsureCanRead()
{
if (!CanRead) throw new InvalidOperationException("Stream is unreadable.");
}
private void EnsureCanWrite()
{
if (!CanWrite) throw new InvalidOperationException("Stream is unwrittable.");
}
private void EnsureCanSeek()
{
if (!CanSeek) throw new InvalidOperationException("Stream is unseekable.");
}
private void EnsureNotDisposed() => ObjectDisposedException.ThrowIf(_disposed, this);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment