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 // See the LICENSE file in the project root for more information.
5 /*============================================================
11 ** Purpose: A Stream whose backing store is memory. Great
12 ** for temporary storage without creating a temp file. Also
13 ** lets users expose a byte[] as a stream.
16 ===========================================================*/
20 using System.Runtime.CompilerServices;
21 using System.Runtime.InteropServices;
22 using System.Diagnostics;
23 using System.Threading;
24 using System.Threading.Tasks;
28 // A MemoryStream represents a Stream in memory (ie, it has no backing store).
29 // This stream may reduce the need for temporary buffers and files in
32 // There are two ways to create a MemoryStream. You can initialize one
33 // from an unsigned byte array, or you can create an empty one. Empty
34 // memory streams are resizable, while ones created with a byte array provide
35 // a stream "view" of the data.
36 public class MemoryStream : Stream
38 private byte[] _buffer; // Either allocated internally or externally.
39 private int _origin; // For user-provided arrays, start at this origin
40 private int _position; // read/write head.
41 private int _length; // Number of bytes within the memory stream
42 private int _capacity; // length of usable portion of buffer for stream
43 // Note that _capacity == _buffer.Length for non-user-provided byte[]'s
45 private bool _expandable; // User-provided buffers aren't expandable.
46 private bool _writable; // Can user write to this stream?
47 private bool _exposable; // Whether the array can be returned to the user.
48 private bool _isOpen; // Is this stream open or closed?
50 private Task<int> _lastReadTask; // The last successful task returned from ReadAsync
52 private const int MemStreamMaxLength = Int32.MaxValue;
59 public MemoryStream(int capacity)
63 throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity);
66 _buffer = capacity != 0 ? new byte[capacity] : Array.Empty<byte>();
71 _origin = 0; // Must be 0 for byte[]'s created by MemoryStream
75 public MemoryStream(byte[] buffer)
80 public MemoryStream(byte[] buffer, bool writable)
82 if (buffer == null) throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
84 _length = _capacity = buffer.Length;
91 public MemoryStream(byte[] buffer, int index, int count)
92 : this(buffer, index, count, true, false)
96 public MemoryStream(byte[] buffer, int index, int count, bool writable)
97 : this(buffer, index, count, writable, false)
101 public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
104 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
106 throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
108 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
109 if (buffer.Length - index < count)
110 throw new ArgumentException(SR.Argument_InvalidOffLen);
113 _origin = _position = index;
114 _length = _capacity = index + count;
115 _writable = writable;
116 _exposable = publiclyVisible; // Can TryGetBuffer/GetBuffer return the array?
121 public override bool CanRead
123 get { return _isOpen; }
126 public override bool CanSeek
128 get { return _isOpen; }
131 public override bool CanWrite
133 get { return _writable; }
136 private void EnsureWriteable()
138 if (!CanWrite) __Error.WriteNotSupported();
141 protected override void Dispose(bool disposing)
150 // Don't set buffer to null - allow TryGetBuffer, GetBuffer & ToArray to work.
151 _lastReadTask = null;
156 // Call base.Close() to cleanup async IO resources
157 base.Dispose(disposing);
161 // returns a bool saying whether we allocated a new array.
162 private bool EnsureCapacity(int value)
164 // Check for overflow
166 throw new IOException(SR.IO_StreamTooLong);
167 if (value > _capacity)
169 int newCapacity = value;
170 if (newCapacity < 256)
172 // We are ok with this overflowing since the next statement will deal
173 // with the cases where _capacity*2 overflows.
174 if (newCapacity < _capacity * 2)
175 newCapacity = _capacity * 2;
176 // We want to expand the array up to Array.MaxArrayLengthOneDimensional
177 // And we want to give the user the value that they asked for
178 if ((uint)(_capacity * 2) > Array.MaxByteArrayLength)
179 newCapacity = value > Array.MaxByteArrayLength ? value : Array.MaxByteArrayLength;
181 Capacity = newCapacity;
187 public override void Flush()
191 public override Task FlushAsync(CancellationToken cancellationToken)
193 if (cancellationToken.IsCancellationRequested)
194 return Task.FromCanceled(cancellationToken);
199 return Task.CompletedTask;
203 return Task.FromException(ex);
208 public virtual byte[] GetBuffer()
211 throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer);
215 public virtual bool TryGetBuffer(out ArraySegment<byte> buffer)
219 buffer = default(ArraySegment<byte>);
223 buffer = new ArraySegment<byte>(_buffer, offset: _origin, count: (_length - _origin));
227 // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) ---------------
229 // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer())
230 internal byte[] InternalGetBuffer()
235 // PERF: True cursor position, we don't need _origin for direct access
236 internal int InternalGetPosition()
238 if (!_isOpen) __Error.StreamIsClosed();
242 // PERF: Takes out Int32 as fast as possible
243 internal int InternalReadInt32()
246 __Error.StreamIsClosed();
248 int pos = (_position += 4); // use temp to avoid a race condition
254 return (int)(_buffer[pos - 4] | _buffer[pos - 3] << 8 | _buffer[pos - 2] << 16 | _buffer[pos - 1] << 24);
257 // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes
258 internal int InternalEmulateRead(int count)
260 if (!_isOpen) __Error.StreamIsClosed();
262 int n = _length - _position;
263 if (n > count) n = count;
266 Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
271 // Gets & sets the capacity (number of bytes allocated) for this stream.
272 // The capacity cannot be set to a value less than the current length
275 public virtual int Capacity
279 if (!_isOpen) __Error.StreamIsClosed();
280 return _capacity - _origin;
284 // Only update the capacity if the MS is expandable and the value is different than the current capacity.
285 // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity
286 if (value < Length) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
288 if (!_isOpen) __Error.StreamIsClosed();
289 if (!_expandable && (value != Capacity)) __Error.MemoryStreamNotExpandable();
291 // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors)
292 if (_expandable && value != _capacity)
296 byte[] newBuffer = new byte[value];
297 if (_length > 0) Buffer.InternalBlockCopy(_buffer, 0, newBuffer, 0, _length);
309 public override long Length
313 if (!_isOpen) __Error.StreamIsClosed();
314 return _length - _origin;
318 public override long Position
322 if (!_isOpen) __Error.StreamIsClosed();
323 return _position - _origin;
328 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
330 if (!_isOpen) __Error.StreamIsClosed();
332 if (value > MemStreamMaxLength)
333 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
334 _position = _origin + (int)value;
338 public override int Read([In, Out] byte[] buffer, int offset, int count)
341 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
343 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
345 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
346 if (buffer.Length - offset < count)
347 throw new ArgumentException(SR.Argument_InvalidOffLen);
349 if (!_isOpen) __Error.StreamIsClosed();
351 int n = _length - _position;
352 if (n > count) n = count;
356 Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
361 while (--byteCount >= 0)
362 buffer[offset + byteCount] = _buffer[_position + byteCount];
365 Buffer.InternalBlockCopy(_buffer, _position, buffer, offset, n);
371 public override int Read(Span<byte> destination)
373 if (GetType() != typeof(MemoryStream))
375 // MemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior
376 // to this Read(Span<byte>) overload being introduced. In that case, this Read(Span<byte>) overload
377 // should use the behavior of Read(byte[],int,int) overload.
378 return base.Read(destination);
383 __Error.StreamIsClosed();
386 int n = Math.Min(_length - _position, destination.Length);
392 // TODO https://github.com/dotnet/corefx/issues/22388:
393 // Read(byte[], int, int) has an n <= 8 optimization, presumably based
394 // on benchmarking. Determine if/where such a cut-off is here and add
395 // an equivalent optimization if necessary.
396 new Span<byte>(_buffer, _position, n).CopyTo(destination);
402 public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
405 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
407 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
409 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
410 if (buffer.Length - offset < count)
411 throw new ArgumentException(SR.Argument_InvalidOffLen);
413 // If cancellation was requested, bail early
414 if (cancellationToken.IsCancellationRequested)
415 return Task.FromCanceled<int>(cancellationToken);
419 int n = Read(buffer, offset, count);
420 var t = _lastReadTask;
421 Debug.Assert(t == null || t.Status == TaskStatus.RanToCompletion,
422 "Expected that a stored last task completed successfully");
423 return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<int>(n));
425 catch (OperationCanceledException oce)
427 return Task.FromCancellation<int>(oce);
429 catch (Exception exception)
431 return Task.FromException<int>(exception);
435 public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
437 if (cancellationToken.IsCancellationRequested)
439 return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
444 // ReadAsync(Memory<byte>,...) needs to delegate to an existing virtual to do the work, in case an existing derived type
445 // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to
446 // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate
447 // a Task<int> for the return value, so we want to delegate to one of the synchronous methods. We could always
448 // delegate to the Read(Span<byte>) method, and that's the most efficient solution when dealing with a concrete
449 // MemoryStream, but if we're dealing with a type derived from MemoryStream, Read(Span<byte>) will end up delegating
450 // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the
451 // very common case of the Memory<byte> wrapping an array: if it does, we delegate to Read(byte[], ...) with it,
452 // as that will be efficient in both cases, and we fall back to Read(Span<byte>) if the Memory<byte> wrapped something
453 // else; if this is a concrete MemoryStream, that'll be efficient, and only in the case where the Memory<byte> wrapped
454 // something other than an array and this is a MemoryStream-derived type that doesn't override Read(Span<byte>) will
455 // it then fall back to doing the ArrayPool/copy behavior.
456 return new ValueTask<int>(
457 destination.TryGetArray(out ArraySegment<byte> destinationArray) ?
458 Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) :
459 Read(destination.Span));
461 catch (OperationCanceledException oce)
463 return new ValueTask<int>(Task.FromCancellation<int>(oce));
465 catch (Exception exception)
467 return new ValueTask<int>(Task.FromException<int>(exception));
472 public override int ReadByte()
474 if (!_isOpen) __Error.StreamIsClosed();
476 if (_position >= _length) return -1;
478 return _buffer[_position++];
481 public override void CopyTo(Stream destination, int bufferSize)
483 // Since we did not originally override this method, validate the arguments
484 // the same way Stream does for back-compat.
485 StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
487 // If we have been inherited into a subclass, the following implementation could be incorrect
488 // since it does not call through to Read() which a subclass might have overridden.
489 // To be safe we will only use this implementation in cases where we know it is safe to do so,
490 // and delegate to our base class (which will call into Read) when we are not sure.
491 if (GetType() != typeof(MemoryStream))
493 base.CopyTo(destination, bufferSize);
497 int originalPosition = _position;
499 // Seek to the end of the MemoryStream.
500 int remaining = InternalEmulateRead(_length - originalPosition);
502 // If we were already at or past the end, there's no copying to do so just quit.
505 // Call Write() on the other Stream, using our internal buffer and avoiding any
506 // intermediary allocations.
507 destination.Write(_buffer, originalPosition, remaining);
511 public override Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
513 // This implementation offers beter performance compared to the base class version.
515 StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
517 // If we have been inherited into a subclass, the following implementation could be incorrect
518 // since it does not call through to ReadAsync() which a subclass might have overridden.
519 // To be safe we will only use this implementation in cases where we know it is safe to do so,
520 // and delegate to our base class (which will call into ReadAsync) when we are not sure.
521 if (this.GetType() != typeof(MemoryStream))
522 return base.CopyToAsync(destination, bufferSize, cancellationToken);
524 // If cancelled - return fast:
525 if (cancellationToken.IsCancellationRequested)
526 return Task.FromCanceled(cancellationToken);
528 // Avoid copying data from this buffer into a temp buffer:
529 // (require that InternalEmulateRead does not throw,
530 // otherwise it needs to be wrapped into try-catch-Task.FromException like memStrDest.Write below)
532 Int32 pos = _position;
533 Int32 n = InternalEmulateRead(_length - _position);
535 // If we were already at or past the end, there's no copying to do so just quit.
537 return Task.CompletedTask;
539 // If destination is not a memory stream, write there asynchronously:
540 MemoryStream memStrDest = destination as MemoryStream;
541 if (memStrDest == null)
542 return destination.WriteAsync(_buffer, pos, n, cancellationToken);
546 // If destination is a MemoryStream, CopyTo synchronously:
547 memStrDest.Write(_buffer, pos, n);
548 return Task.CompletedTask;
552 return Task.FromException(ex);
557 public override long Seek(long offset, SeekOrigin loc)
559 if (!_isOpen) __Error.StreamIsClosed();
561 if (offset > MemStreamMaxLength)
562 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_StreamLength);
565 case SeekOrigin.Begin:
567 int tempPosition = unchecked(_origin + (int)offset);
568 if (offset < 0 || tempPosition < _origin)
569 throw new IOException(SR.IO_SeekBeforeBegin);
570 _position = tempPosition;
573 case SeekOrigin.Current:
575 int tempPosition = unchecked(_position + (int)offset);
576 if (unchecked(_position + offset) < _origin || tempPosition < _origin)
577 throw new IOException(SR.IO_SeekBeforeBegin);
578 _position = tempPosition;
583 int tempPosition = unchecked(_length + (int)offset);
584 if (unchecked(_length + offset) < _origin || tempPosition < _origin)
585 throw new IOException(SR.IO_SeekBeforeBegin);
586 _position = tempPosition;
590 throw new ArgumentException(SR.Argument_InvalidSeekOrigin);
593 Debug.Assert(_position >= 0, "_position >= 0");
597 // Sets the length of the stream to a given value. The new
598 // value must be nonnegative and less than the space remaining in
599 // the array, Int32.MaxValue - origin
600 // Origin is 0 in all cases other than a MemoryStream created on
601 // top of an existing array and a specific starting offset was passed
602 // into the MemoryStream constructor. The upper bounds prevents any
603 // situations where a stream may be created on top of an array then
604 // the stream is made longer than the maximum possible length of the
605 // array (Int32.MaxValue).
607 public override void SetLength(long value)
609 if (value < 0 || value > Int32.MaxValue)
611 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
615 // Origin wasn't publicly exposed above.
616 Debug.Assert(MemStreamMaxLength == Int32.MaxValue); // Check parameter validation logic in this method if this fails.
617 if (value > (Int32.MaxValue - _origin))
619 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
622 int newLength = _origin + (int)value;
623 bool allocatedNewArray = EnsureCapacity(newLength);
624 if (!allocatedNewArray && newLength > _length)
625 Array.Clear(_buffer, _length, newLength - _length);
627 if (_position > newLength) _position = newLength;
630 public virtual byte[] ToArray()
632 byte[] copy = new byte[_length - _origin];
633 Buffer.InternalBlockCopy(_buffer, _origin, copy, 0, _length - _origin);
637 public override void Write(byte[] buffer, int offset, int count)
640 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
642 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
644 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
645 if (buffer.Length - offset < count)
646 throw new ArgumentException(SR.Argument_InvalidOffLen);
648 if (!_isOpen) __Error.StreamIsClosed();
651 int i = _position + count;
652 // Check for overflow
654 throw new IOException(SR.IO_StreamTooLong);
658 bool mustZero = _position > _length;
661 bool allocatedNewArray = EnsureCapacity(i);
662 if (allocatedNewArray)
666 Array.Clear(_buffer, _length, i - _length);
669 if ((count <= 8) && (buffer != _buffer))
671 int byteCount = count;
672 while (--byteCount >= 0)
673 _buffer[_position + byteCount] = buffer[offset + byteCount];
676 Buffer.InternalBlockCopy(buffer, offset, _buffer, _position, count);
680 public override void Write(ReadOnlySpan<byte> source)
682 if (GetType() != typeof(MemoryStream))
684 // MemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior
685 // to this Write(Span<byte>) overload being introduced. In that case, this Write(Span<byte>) overload
686 // should use the behavior of Write(byte[],int,int) overload.
693 __Error.StreamIsClosed();
697 // Check for overflow
698 int i = _position + source.Length;
701 throw new IOException(SR.IO_StreamTooLong);
706 bool mustZero = _position > _length;
709 bool allocatedNewArray = EnsureCapacity(i);
710 if (allocatedNewArray)
717 Array.Clear(_buffer, _length, i - _length);
722 source.CopyTo(new Span<byte>(_buffer, _position, source.Length));
726 public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
729 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
731 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
733 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
734 if (buffer.Length - offset < count)
735 throw new ArgumentException(SR.Argument_InvalidOffLen);
737 // If cancellation is already requested, bail early
738 if (cancellationToken.IsCancellationRequested)
739 return Task.FromCanceled(cancellationToken);
743 Write(buffer, offset, count);
744 return Task.CompletedTask;
746 catch (OperationCanceledException oce)
748 return Task.FromCancellation<VoidTaskResult>(oce);
750 catch (Exception exception)
752 return Task.FromException(exception);
756 public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
758 if (cancellationToken.IsCancellationRequested)
760 return Task.FromCanceled(cancellationToken);
765 // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan<byte>).
766 // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency.
767 if (source.DangerousTryGetArray(out ArraySegment<byte> sourceArray))
769 Write(sourceArray.Array, sourceArray.Offset, sourceArray.Count);
775 return Task.CompletedTask;
777 catch (OperationCanceledException oce)
779 return Task.FromCancellation<VoidTaskResult>(oce);
781 catch (Exception exception)
783 return Task.FromException(exception);
787 public override void WriteByte(byte value)
789 if (!_isOpen) __Error.StreamIsClosed();
792 if (_position >= _length)
794 int newLength = _position + 1;
795 bool mustZero = _position > _length;
796 if (newLength >= _capacity)
798 bool allocatedNewArray = EnsureCapacity(newLength);
799 if (allocatedNewArray)
803 Array.Clear(_buffer, _length, _position - _length);
806 _buffer[_position++] = value;
809 // Writes this MemoryStream to another stream.
810 public virtual void WriteTo(Stream stream)
813 throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream);
815 if (!_isOpen) __Error.StreamIsClosed();
816 stream.Write(_buffer, _origin, _length - _origin);