VecStreamReader.cs (9287B)
1 using Std.Vec; 2 using System; 3 using System.IO; 4 using System.Threading; 5 using System.Threading.Tasks; 6 #region Namespaces 7 namespace Std.IO { 8 #region Types 9 // Read-only, non-seekable, stream based on Std.Vec.Vec<byte>. 10 // This type is an even lighter-weight alternative to the default MemoryStream and has more strictly enforced behavior. 11 // This type only implements I(Async)Disposable because it has to by virtue of being a subtype of System.IO.StreamIter. 12 // This type does not dispose of anything, however, and calling dispose only suppresses the finalizer. 13 // This type is a subtype of MemoryStream purely for performance reasons. There is a lot of code in the .NET SDK 14 // that uses reflection to detect more specific subtypes to influence the behavior of a type or function. 15 // We want this type to enjoy any potential benefits such code may have. 16 // Note this type does not use the underlying data or implementation of MemoryStream in any way with exceptions of BeginRead and EndRead. 17 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1063:Implement IDisposable Correctly", Justification = "This type should not implement IDisposable to begin with, but it has to by being a subtype of StreamIter. We have it do nothing.")] 18 public sealed class VecStreamReader: MemoryStream, IDisposable, IAsyncDisposable { 19 20 #region Type-level Constructors 21 #endregion 22 23 #region Instance Constructors 24 public VecStreamReader(Vec<byte> data, uint position) : base(Array.Empty<byte>(), 0, 0, false, false) { 25 26 if (position <= data.Len) { 27 (_buffer, _originalPosition, _pos) = (data, position, position); 28 } else { 29 throw new ArgumentOutOfRangeException(nameof(position), "Position must be less than or equal to the length of data."); 30 } 31 } 32 #endregion 33 34 #region Type-level Fields 35 #endregion 36 37 #region Instance Fields 38 readonly Vec<byte> _buffer; 39 readonly uint _originalPosition; 40 uint _pos; 41 #endregion 42 43 #region Type-level Properties 44 #endregion 45 46 #region Instance Properties 47 public sealed override bool CanRead => true; 48 public sealed override bool CanSeek => false; 49 public sealed override bool CanTimeout => false; 50 public sealed override bool CanWrite => false; 51 public sealed override int Capacity => (int)_buffer.Capacity; 52 public uint CurrentPosition => _pos; 53 public ReadOnlySpan<byte> ReadData => _buffer.AsSlice()[(int)_originalPosition..(int)_pos]; 54 public ReadOnlySpan<byte> UnreadData => _buffer.AsSlice()[(int)_pos..]; 55 public uint Len => _buffer.Len - _originalPosition; 56 public sealed override long Length => Len; 57 // The .NET SDK is littered with many terrible design decisions. 58 // Instead of having dedicated interfaces, non-sealed classes, and non-sealed records that guarantee certain invariants, 59 // many types have functions that are only sometimes valid. This in turn requires 60 // many subtypes to throw exceptions for inapplicable functions. 61 // The getter of this property should be fine to return, but the API documentation states that only seekable streams are allowed to return anything. 62 public sealed override long Position { 63 get => throw new NotSupportedException(); 64 set => throw new NotSupportedException(); 65 } 66 public sealed override int ReadTimeout => throw new InvalidOperationException(); 67 public sealed override int WriteTimeout => throw new InvalidOperationException(); 68 #endregion 69 70 #region Type-level Functions 71 public static VecStreamReader New(Vec<byte> data, uint position) => new(data, position); 72 #endregion 73 74 #region Instance Functions 75 // Due to complexity of doing this correctly, we defer to the base implementation. 76 public sealed override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => base.BeginRead(buffer, offset, count, callback, state); 77 public sealed override IAsyncResult BeginWrite(byte[] _, int _1, int _2, AsyncCallback? _3, object? _4) => throw new NotSupportedException(); 78 public sealed override void Close() => Dispose(); 79 public sealed override void CopyTo(Stream stream, int _) { 80 81 stream.Write(_buffer.AsSlice()[(int)_pos..]); 82 _pos = _buffer.Len; 83 } 84 public sealed override Task CopyToAsync(Stream stream, int bufferSize, CancellationToken _2) { CopyTo(stream, bufferSize); return Task.CompletedTask; } 85 [Obsolete("System.Threading.ManualResetEvent should be used instead.", true)] 86 protected sealed override WaitHandle CreateWaitHandle() => new ManualResetEvent(false); 87 public new void Dispose() => GC.SuppressFinalize(this); 88 [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2215:Dispose methods should call base class dispose", Justification = "We don't want Dispose to do anything.")] 89 protected sealed override void Dispose(bool disposing) { 90 // This should mean Dispose on StreamIter or MemoryStream was called, so we redirect to Dispose which suppresses the finalizer. 91 if (disposing) { 92 Dispose(); 93 } 94 } 95 [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2215:Dispose methods should call base class dispose", Justification = "We don't want DisposeAsync to do anything.")] 96 public sealed override ValueTask DisposeAsync() { 97 98 Dispose(); 99 return ValueTask.CompletedTask; 100 } 101 // Due to complexity of doing this correctly, we defer to the base implementation. 102 public sealed override int EndRead(IAsyncResult asyncResult) => base.EndRead(asyncResult); 103 public sealed override void EndWrite(IAsyncResult asyncResult) => throw new NotSupportedException(); 104 public sealed override bool Equals(object? _) => false; 105 public sealed override void Flush() { } 106 public sealed override Task FlushAsync(CancellationToken _) => Task.CompletedTask; 107 public sealed override int GetHashCode() => 0; 108 public Unit Reset() { 109 110 _pos = _originalPosition; 111 return new Unit(); 112 } 113 public Vec<byte> GetVecBuffer() => _buffer; 114 public sealed override byte[] GetBuffer() => _buffer.AsArray(); 115 [Obsolete("Platform does not support this. A System.PlatformNotSupportedException will be thrown.", true)] 116 public sealed override object InitializeLifetimeService() => throw new PlatformNotSupportedException(); 117 [Obsolete("Do not call this method.", true)] 118 protected sealed override void ObjectInvariant() { } 119 public sealed override int Read(byte[] buffer, int offset, int count) => Read(buffer.AsSpan(offset, count)); 120 public sealed override int Read(Span<byte> buffer) { 121 122 var count = (int)Math.Min(buffer.Length, _buffer.Len - _pos); 123 _buffer.AsArray().AsSpan((int)_pos, count).CopyTo(buffer); 124 _pos += (uint)count; 125 return count; 126 } 127 public sealed override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken _) => Task.FromResult(Read(buffer.AsSpan(offset, count))); 128 public sealed override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken _1) => ValueTask.FromResult(Read(buffer.Span)); 129 public sealed override int ReadByte() => _pos < _buffer.Len ? _buffer[_pos++] : -1; 130 public sealed override long Seek(long _, SeekOrigin _1) => throw new NotSupportedException(); 131 public sealed override void SetLength(long _) => throw new NotSupportedException(); 132 public sealed override byte[] ToArray() { 133 134 var arr = new byte[_buffer.Len - _originalPosition]; 135 Array.Copy(_buffer.AsArray(), _originalPosition, arr, 0, arr.Length); 136 return arr; 137 } 138 public sealed override string ToString() => "VecStreamReader"; 139 public sealed override bool TryGetBuffer(out ArraySegment<byte> buffer) { 140 141 buffer = new(_buffer.AsArray(), (int)_originalPosition, (int)(_buffer.Len - _originalPosition)); 142 return true; 143 } 144 public sealed override void Write(byte[] _, int _1, int _2) => throw new NotSupportedException(); 145 public sealed override void Write(ReadOnlySpan<byte> _) => throw new NotSupportedException(); 146 public sealed override Task WriteAsync(byte[] _, int _1, int _2, CancellationToken _3) => throw new NotSupportedException(); 147 public sealed override ValueTask WriteAsync(ReadOnlyMemory<byte> _, CancellationToken _1) => throw new NotSupportedException(); 148 public sealed override void WriteByte(byte _) => throw new NotSupportedException(); 149 public sealed override void WriteTo(Stream stream) => stream.Write(_buffer.AsSlice()[(int)_originalPosition..]); 150 #endregion 151 152 #region Operators 153 #endregion 154 155 #region Types 156 #endregion 157 } 158 #endregion 159 160 #region Namespaces 161 #endregion 162 } 163 #endregion