| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Threading; |
| | | 5 | | using System.Threading.Tasks; |
| | | 6 | | |
| | | 7 | | namespace System.IO |
| | | 8 | | { |
| | | 9 | | /// <summary>Provides a <see cref="Stream"/> for the contents of a <see cref="ReadOnlyMemory{Byte}"/>.</summary> |
| | | 10 | | internal sealed class ReadOnlyMemoryStream : Stream |
| | | 11 | | { |
| | | 12 | | private ReadOnlyMemory<byte> _content; |
| | | 13 | | private int _position; |
| | | 14 | | private bool _isOpen; |
| | | 15 | | |
| | 0 | 16 | | public ReadOnlyMemoryStream(ReadOnlyMemory<byte> content) |
| | 0 | 17 | | { |
| | 0 | 18 | | _content = content; |
| | 0 | 19 | | _isOpen = true; |
| | 0 | 20 | | } |
| | | 21 | | |
| | 0 | 22 | | public override bool CanRead => _isOpen; |
| | 0 | 23 | | public override bool CanSeek => _isOpen; |
| | 0 | 24 | | public override bool CanWrite => false; |
| | | 25 | | |
| | | 26 | | private void EnsureNotClosed() |
| | 0 | 27 | | { |
| | 0 | 28 | | if (!_isOpen) |
| | 0 | 29 | | { |
| | 0 | 30 | | throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); |
| | | 31 | | } |
| | 0 | 32 | | } |
| | | 33 | | |
| | | 34 | | public override long Length |
| | | 35 | | { |
| | | 36 | | get |
| | 0 | 37 | | { |
| | 0 | 38 | | EnsureNotClosed(); |
| | 0 | 39 | | return _content.Length; |
| | 0 | 40 | | } |
| | | 41 | | } |
| | | 42 | | |
| | | 43 | | public override long Position |
| | | 44 | | { |
| | | 45 | | get |
| | 0 | 46 | | { |
| | 0 | 47 | | EnsureNotClosed(); |
| | 0 | 48 | | return _position; |
| | 0 | 49 | | } |
| | | 50 | | set |
| | 0 | 51 | | { |
| | 0 | 52 | | EnsureNotClosed(); |
| | 0 | 53 | | if (value < 0 || value > int.MaxValue) |
| | 0 | 54 | | { |
| | 0 | 55 | | throw new ArgumentOutOfRangeException(nameof(value)); |
| | | 56 | | } |
| | 0 | 57 | | _position = (int)value; |
| | 0 | 58 | | } |
| | | 59 | | } |
| | | 60 | | |
| | | 61 | | public override long Seek(long offset, SeekOrigin origin) |
| | 0 | 62 | | { |
| | 0 | 63 | | EnsureNotClosed(); |
| | | 64 | | |
| | 0 | 65 | | long pos = |
| | 0 | 66 | | origin == SeekOrigin.Begin ? offset : |
| | 0 | 67 | | origin == SeekOrigin.Current ? _position + offset : |
| | 0 | 68 | | origin == SeekOrigin.End ? _content.Length + offset : |
| | 0 | 69 | | throw new ArgumentOutOfRangeException(nameof(origin)); |
| | | 70 | | |
| | 0 | 71 | | if (pos > int.MaxValue) |
| | 0 | 72 | | { |
| | 0 | 73 | | throw new ArgumentOutOfRangeException(nameof(offset)); |
| | | 74 | | } |
| | 0 | 75 | | else if (pos < 0) |
| | 0 | 76 | | { |
| | 0 | 77 | | throw new IOException(SR.IO_SeekBeforeBegin); |
| | | 78 | | } |
| | | 79 | | |
| | 0 | 80 | | _position = (int)pos; |
| | 0 | 81 | | return _position; |
| | 0 | 82 | | } |
| | | 83 | | |
| | | 84 | | public override int ReadByte() |
| | 0 | 85 | | { |
| | 0 | 86 | | EnsureNotClosed(); |
| | | 87 | | |
| | 0 | 88 | | ReadOnlySpan<byte> s = _content.Span; |
| | 0 | 89 | | return _position < s.Length ? s[_position++] : -1; |
| | 0 | 90 | | } |
| | | 91 | | |
| | | 92 | | public override int Read(byte[] buffer, int offset, int count) |
| | 0 | 93 | | { |
| | 0 | 94 | | ValidateBufferArguments(buffer, offset, count); |
| | 0 | 95 | | return ReadBuffer(new Span<byte>(buffer, offset, count)); |
| | 0 | 96 | | } |
| | | 97 | | |
| | | 98 | | #if !NETFRAMEWORK && !NETSTANDARD2_0 |
| | 0 | 99 | | public override int Read(Span<byte> buffer) => ReadBuffer(buffer); |
| | | 100 | | #endif |
| | | 101 | | |
| | | 102 | | private int ReadBuffer(Span<byte> buffer) |
| | 0 | 103 | | { |
| | 0 | 104 | | EnsureNotClosed(); |
| | | 105 | | |
| | 0 | 106 | | int remaining = _content.Length - _position; |
| | | 107 | | |
| | 0 | 108 | | if (remaining <= 0 || buffer.Length == 0) |
| | 0 | 109 | | { |
| | 0 | 110 | | return 0; |
| | | 111 | | } |
| | 0 | 112 | | else if (remaining <= buffer.Length) |
| | 0 | 113 | | { |
| | 0 | 114 | | _content.Span.Slice(_position).CopyTo(buffer); |
| | 0 | 115 | | _position = _content.Length; |
| | 0 | 116 | | return remaining; |
| | | 117 | | } |
| | | 118 | | else |
| | 0 | 119 | | { |
| | 0 | 120 | | _content.Span.Slice(_position, buffer.Length).CopyTo(buffer); |
| | 0 | 121 | | _position += buffer.Length; |
| | 0 | 122 | | return buffer.Length; |
| | | 123 | | } |
| | 0 | 124 | | } |
| | | 125 | | |
| | | 126 | | public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) |
| | 0 | 127 | | { |
| | 0 | 128 | | ValidateBufferArguments(buffer, offset, count); |
| | 0 | 129 | | EnsureNotClosed(); |
| | 0 | 130 | | return cancellationToken.IsCancellationRequested ? |
| | 0 | 131 | | Task.FromCanceled<int>(cancellationToken) : |
| | 0 | 132 | | Task.FromResult(ReadBuffer(new Span<byte>(buffer, offset, count))); |
| | 0 | 133 | | } |
| | | 134 | | |
| | | 135 | | #if !NETFRAMEWORK && !NETSTANDARD2_0 |
| | | 136 | | public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default(Canc |
| | 0 | 137 | | { |
| | 0 | 138 | | EnsureNotClosed(); |
| | 0 | 139 | | return cancellationToken.IsCancellationRequested ? |
| | 0 | 140 | | ValueTask.FromCanceled<int>(cancellationToken) : |
| | 0 | 141 | | new ValueTask<int>(ReadBuffer(buffer.Span)); |
| | 0 | 142 | | } |
| | | 143 | | #endif |
| | | 144 | | |
| | | 145 | | public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? st |
| | 0 | 146 | | TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count), callback, state); |
| | | 147 | | |
| | | 148 | | public override int EndRead(IAsyncResult asyncResult) |
| | 0 | 149 | | { |
| | 0 | 150 | | EnsureNotClosed(); |
| | 0 | 151 | | return TaskToAsyncResult.End<int>(asyncResult); |
| | 0 | 152 | | } |
| | | 153 | | |
| | | 154 | | #if !NETFRAMEWORK && !NETSTANDARD2_0 |
| | | 155 | | public override void CopyTo(Stream destination, int bufferSize) |
| | 0 | 156 | | { |
| | 0 | 157 | | ValidateCopyToArguments(destination, bufferSize); |
| | 0 | 158 | | EnsureNotClosed(); |
| | 0 | 159 | | if (_content.Length > _position) |
| | 0 | 160 | | { |
| | 0 | 161 | | destination.Write(_content.Span.Slice(_position)); |
| | 0 | 162 | | _position = _content.Length; |
| | 0 | 163 | | } |
| | 0 | 164 | | } |
| | | 165 | | |
| | | 166 | | public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) |
| | 0 | 167 | | { |
| | 0 | 168 | | ValidateCopyToArguments(destination, bufferSize); |
| | 0 | 169 | | EnsureNotClosed(); |
| | 0 | 170 | | if (_content.Length > _position) |
| | 0 | 171 | | { |
| | 0 | 172 | | ReadOnlyMemory<byte> content = _content.Slice(_position); |
| | 0 | 173 | | _position = _content.Length; |
| | 0 | 174 | | return destination.WriteAsync(content, cancellationToken).AsTask(); |
| | | 175 | | } |
| | | 176 | | else |
| | 0 | 177 | | { |
| | 0 | 178 | | return Task.CompletedTask; |
| | | 179 | | } |
| | 0 | 180 | | } |
| | | 181 | | #endif |
| | | 182 | | |
| | 0 | 183 | | public override void Flush() { } |
| | | 184 | | |
| | 0 | 185 | | public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; |
| | | 186 | | |
| | 0 | 187 | | public override void SetLength(long value) => throw new NotSupportedException(); |
| | | 188 | | |
| | 0 | 189 | | public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); |
| | | 190 | | |
| | | 191 | | protected override void Dispose(bool disposing) |
| | 0 | 192 | | { |
| | 0 | 193 | | _isOpen = false; |
| | 0 | 194 | | _content = default; |
| | 0 | 195 | | base.Dispose(disposing); |
| | 0 | 196 | | } |
| | | 197 | | |
| | | 198 | | #if NETFRAMEWORK || NETSTANDARD2_0 |
| | | 199 | | private static void ValidateBufferArguments(byte[] buffer, int offset, int count) |
| | | 200 | | { |
| | | 201 | | ArgumentNullException.ThrowIfNull(buffer); |
| | | 202 | | |
| | | 203 | | if (offset < 0) |
| | | 204 | | { |
| | | 205 | | throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); |
| | | 206 | | } |
| | | 207 | | |
| | | 208 | | if ((uint)count > buffer.Length - offset) |
| | | 209 | | { |
| | | 210 | | throw new ArgumentOutOfRangeException(nameof(count), SR.Argument_InvalidOffLen); |
| | | 211 | | } |
| | | 212 | | } |
| | | 213 | | #endif |
| | | 214 | | } |
| | | 215 | | } |