// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.IO; using System.Threading; using System.Threading.Tasks; #pragma warning disable CA1710 namespace EpicGames.Core { /// /// implementation that wraps another stream, allowing derived classes to override some methods of their chosing. /// public class StreamWrapper : Stream { readonly Stream _inner; /// /// Accessor for the wrapped stream /// protected Stream Inner => _inner; /// public override bool CanRead => _inner.CanRead; /// public override bool CanSeek => _inner.CanSeek; /// public override bool CanTimeout => _inner.CanTimeout; /// public override bool CanWrite => _inner.CanWrite; /// public override long Length => _inner.Length; /// public override long Position { get => _inner.Position; set => _inner.Position = value; } /// public override int ReadTimeout { get => _inner.ReadTimeout; set => _inner.ReadTimeout = value; } /// public override int WriteTimeout { get => _inner.WriteTimeout; set => _inner.WriteTimeout = value; } /// /// Constructor /// /// The stream to wrap public StreamWrapper(Stream inner) => _inner = inner; /// protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { _inner.Dispose(); } } /// public override async ValueTask DisposeAsync() { await base.DisposeAsync(); await _inner.DisposeAsync(); GC.SuppressFinalize(this); } /// public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => _inner.BeginRead(buffer, offset, count, callback, state); /// public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => _inner.BeginWrite(buffer, offset, count, callback, state); /// public override void Close() => _inner.Close(); /// public override void CopyTo(Stream destination, int bufferSize) => _inner.CopyTo(destination, bufferSize); /// public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _inner.CopyToAsync(destination, bufferSize, cancellationToken); /// public override int EndRead(IAsyncResult asyncResult) => _inner.EndRead(asyncResult); /// public override void EndWrite(IAsyncResult asyncResult) => _inner.EndWrite(asyncResult); /// public override void Flush() => _inner.Flush(); /// public override Task FlushAsync(CancellationToken cancellationToken) => _inner.FlushAsync(cancellationToken); /// public override int Read(byte[] buffer, int offset, int count) => _inner.Read(buffer, offset, count); /// public override int Read(Span buffer) => _inner.Read(buffer); /// public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => _inner.ReadAsync(buffer, offset, count, cancellationToken); /// public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => _inner.ReadAsync(buffer, cancellationToken); /// public override int ReadByte() => _inner.ReadByte(); /// public override long Seek(long offset, SeekOrigin origin) => _inner.Seek(offset, origin); /// public override void SetLength(long value) => _inner.SetLength(value); /// public override void Write(byte[] buffer, int offset, int count) => _inner.Write(buffer, offset, count); /// public override void Write(ReadOnlySpan buffer) => _inner.Write(buffer); /// public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => _inner.WriteAsync(buffer, offset, count, cancellationToken); /// public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => _inner.WriteAsync(buffer, cancellationToken); /// public override void WriteByte(byte value) => _inner.WriteByte(value); } /// /// Extension methods for /// public static class StreamWrapperExtensions { class StreamResourceOwner : StreamWrapper { IDisposable? _resource; public StreamResourceOwner(Stream inner, IDisposable resource) : base(inner) { _resource = resource; } /// protected override void Dispose(bool disposing) { if (disposing) { DisposeResource(); } base.Dispose(disposing); } /// public override async ValueTask DisposeAsync() { DisposeResource(); await base.DisposeAsync(); } void DisposeResource() { if (_resource != null) { _resource.Dispose(); _resource = null; } } } /// /// Wraps ownership of another resource with a stream /// /// Stream to wrap /// Additional resource to control ownership of /// New stream which will dispose of the given resource when it is disposed public static Stream WrapOwnership(this Stream stream, IDisposable resource) => new StreamResourceOwner(stream, resource); } }