3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
10 ** <OWNER>[....]</OWNER>
13 ** Purpose: A Stream whose backing store is memory. Great
14 ** for temporary storage without creating a temp file. Also
15 ** lets users expose a byte[] as a stream.
18 ===========================================================*/
22 using System.Runtime.CompilerServices;
23 using System.Runtime.InteropServices;
24 using System.Diagnostics.Contracts;
26 using System.Threading;
27 using System.Threading.Tasks;
28 using System.Security.Permissions;
32 // A MemoryStream represents a Stream in memory (ie, it has no backing store).
33 // This stream may reduce the need for temporary buffers and files in
36 // There are two ways to create a MemoryStream. You can initialize one
37 // from an unsigned byte array, or you can create an empty one. Empty
38 // memory streams are resizable, while ones created with a byte array provide
39 // a stream "view" of the data.
42 public class MemoryStream : Stream
44 private byte[] _buffer; // Either allocated internally or externally.
45 private int _origin; // For user-provided arrays, start at this origin
46 private int _position; // read/write head.
47 [ContractPublicPropertyName("Length")]
48 private int _length; // Number of bytes within the memory stream
49 private int _capacity; // length of usable portion of buffer for stream
50 // Note that _capacity == _buffer.Length for non-user-provided byte[]'s
52 private bool _expandable; // User-provided buffers aren't expandable.
53 private bool _writable; // Can user write to this stream?
54 private bool _exposable; // Whether the array can be returned to the user.
55 private bool _isOpen; // Is this stream open or closed?
59 private Task<int> _lastReadTask; // The last successful task returned from ReadAsync
64 private const int MemStreamMaxLength = Int32.MaxValue;
70 public MemoryStream(int capacity) {
72 throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
74 Contract.EndContractBlock();
76 _buffer = new byte[capacity];
81 _origin = 0; // Must be 0 for byte[]'s created by MemoryStream
85 public MemoryStream(byte[] buffer)
86 : this(buffer, true) {
89 public MemoryStream(byte[] buffer, bool writable) {
90 if (buffer == null) throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
91 Contract.EndContractBlock();
93 _length = _capacity = buffer.Length;
100 public MemoryStream(byte[] buffer, int index, int count)
101 : this(buffer, index, count, true, false) {
104 public MemoryStream(byte[] buffer, int index, int count, bool writable)
105 : this(buffer, index, count, writable, false) {
108 public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) {
110 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
112 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
114 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
115 if (buffer.Length - index < count)
116 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
117 Contract.EndContractBlock();
120 _origin = _position = index;
121 _length = _capacity = index + count;
122 _writable = writable;
123 _exposable = publiclyVisible; // Can TryGetBuffer/GetBuffer return the array?
128 public override bool CanRead {
130 get { return _isOpen; }
133 public override bool CanSeek {
135 get { return _isOpen; }
138 public override bool CanWrite {
140 get { return _writable; }
143 private void EnsureWriteable() {
144 if (!CanWrite) __Error.WriteNotSupported();
147 protected override void Dispose(bool disposing)
154 // Don't set buffer to null - allow TryGetBuffer, GetBuffer & ToArray to work.
156 _lastReadTask = null;
161 // Call base.Close() to cleanup async IO resources
162 base.Dispose(disposing);
166 // returns a bool saying whether we allocated a new array.
167 private bool EnsureCapacity(int value) {
168 // Check for overflow
170 throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
171 if (value > _capacity) {
172 int newCapacity = value;
173 if (newCapacity < 256)
175 // We are ok with this overflowing since the next statement will deal
176 // with the cases where _capacity*2 overflows.
177 if (newCapacity < _capacity * 2)
178 newCapacity = _capacity * 2;
179 // We want to expand the array up to Array.MaxArrayLengthOneDimensional
180 // And we want to give the user the value that they asked for
181 if ((uint)(_capacity * 2) > Array.MaxByteArrayLength)
182 newCapacity = value > Array.MaxByteArrayLength ? value : Array.MaxByteArrayLength;
184 Capacity = newCapacity;
190 public override void Flush() {
194 [HostProtection(ExternalThreading=true)]
196 public override Task FlushAsync(CancellationToken cancellationToken) {
198 if (cancellationToken.IsCancellationRequested)
199 return Task.FromCancellation(cancellationToken);
204 return Task.CompletedTask;
206 } catch(Exception ex) {
208 return Task.FromException(ex);
211 #endif // FEATURE_ASYNC_IO
214 public virtual byte[] GetBuffer() {
216 throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_MemStreamBuffer"));
220 public virtual bool TryGetBuffer(out ArraySegment<byte> buffer) {
222 buffer = default(ArraySegment<byte>);
226 buffer = new ArraySegment<byte>(_buffer, offset:_origin, count:(_length - _origin));
230 // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) ---------------
232 // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer())
233 internal byte[] InternalGetBuffer() {
237 // PERF: Get origin and length - used in ResourceWriter.
238 [FriendAccessAllowed]
239 internal void InternalGetOriginAndLength(out int origin, out int length)
241 if (!_isOpen) __Error.StreamIsClosed();
246 // PERF: True cursor position, we don't need _origin for direct access
247 internal int InternalGetPosition() {
248 if (!_isOpen) __Error.StreamIsClosed();
252 // PERF: Takes out Int32 as fast as possible
253 internal int InternalReadInt32() {
255 __Error.StreamIsClosed();
257 int pos = (_position += 4); // use temp to avoid ----
263 return (int)(_buffer[pos-4] | _buffer[pos-3] << 8 | _buffer[pos-2] << 16 | _buffer[pos-1] << 24);
266 // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes
267 internal int InternalEmulateRead(int count) {
268 if (!_isOpen) __Error.StreamIsClosed();
270 int n = _length - _position;
271 if (n > count) n = count;
274 Contract.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
279 // Gets & sets the capacity (number of bytes allocated) for this stream.
280 // The capacity cannot be set to a value less than the current length
283 public virtual int Capacity {
285 if (!_isOpen) __Error.StreamIsClosed();
286 return _capacity - _origin;
289 // Only update the capacity if the MS is expandable and the value is different than the current capacity.
290 // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity
291 if (value < Length) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
292 Contract.Ensures(_capacity - _origin == value);
293 Contract.EndContractBlock();
295 if (!_isOpen) __Error.StreamIsClosed();
296 if (!_expandable && (value != Capacity)) __Error.MemoryStreamNotExpandable();
298 // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors)
299 if (_expandable && value != _capacity) {
301 byte[] newBuffer = new byte[value];
302 if (_length > 0) Buffer.InternalBlockCopy(_buffer, 0, newBuffer, 0, _length);
313 public override long Length {
315 if (!_isOpen) __Error.StreamIsClosed();
316 return _length - _origin;
320 public override long Position {
322 if (!_isOpen) __Error.StreamIsClosed();
323 return _position - _origin;
327 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
328 Contract.Ensures(Position == value);
329 Contract.EndContractBlock();
331 if (!_isOpen) __Error.StreamIsClosed();
333 if (value > MemStreamMaxLength)
334 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
335 _position = _origin + (int)value;
339 public override int Read([In, Out] byte[] buffer, int offset, int count) {
341 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
343 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
345 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
346 if (buffer.Length - offset < count)
347 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
348 Contract.EndContractBlock();
350 if (!_isOpen) __Error.StreamIsClosed();
352 int n = _length - _position;
353 if (n > count) n = count;
357 Contract.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
362 while (--byteCount >= 0)
363 buffer[offset + byteCount] = _buffer[_position + byteCount];
366 Buffer.InternalBlockCopy(_buffer, _position, buffer, offset, n);
373 [HostProtection(ExternalThreading = true)]
375 public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
378 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
380 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
382 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
383 if (buffer.Length - offset < count)
384 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
385 Contract.EndContractBlock(); // contract validation copied from Read(...)
387 // If cancellation was requested, bail early
388 if (cancellationToken.IsCancellationRequested)
389 return Task.FromCancellation<int>(cancellationToken);
393 int n = Read(buffer, offset, count);
394 var t = _lastReadTask;
395 Contract.Assert(t == null || t.Status == TaskStatus.RanToCompletion,
396 "Expected that a stored last task completed successfully");
397 return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<int>(n));
399 catch (OperationCanceledException oce)
401 return Task.FromCancellation<int>(oce);
403 catch (Exception exception)
405 return Task.FromException<int>(exception);
408 #endif //FEATURE_ASYNC_IO
411 public override int ReadByte() {
412 if (!_isOpen) __Error.StreamIsClosed();
414 if (_position >= _length) return -1;
416 return _buffer[_position++];
421 public override Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) {
423 // This implementation offers beter performance compared to the base class version.
425 // The parameter checks must be in [....] with the base version:
426 if (destination == null)
427 throw new ArgumentNullException("destination");
430 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
432 if (!CanRead && !CanWrite)
433 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed"));
435 if (!destination.CanRead && !destination.CanWrite)
436 throw new ObjectDisposedException("destination", Environment.GetResourceString("ObjectDisposed_StreamClosed"));
439 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream"));
441 if (!destination.CanWrite)
442 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnwritableStream"));
444 Contract.EndContractBlock();
446 // If we have been inherited into a subclass, the following implementation could be incorrect
447 // since it does not call through to Read() or Write() which a subclass might have overriden.
448 // To be safe we will only use this implementation in cases where we know it is safe to do so,
449 // and delegate to our base class (which will call into Read/Write) when we are not sure.
450 if (this.GetType() != typeof(MemoryStream))
451 return base.CopyToAsync(destination, bufferSize, cancellationToken);
453 // If cancelled - return fast:
454 if (cancellationToken.IsCancellationRequested)
455 return Task.FromCancellation(cancellationToken);
457 // Avoid copying data from this buffer into a temp buffer:
458 // (require that InternalEmulateRead does not throw,
459 // otherwise it needs to be wrapped into try-catch-Task.FromException like memStrDest.Write below)
461 Int32 pos = _position;
462 Int32 n = InternalEmulateRead(_length - _position);
464 // If destination is not a memory stream, write there asynchronously:
465 MemoryStream memStrDest = destination as MemoryStream;
466 if (memStrDest == null)
467 return destination.WriteAsync(_buffer, pos, n, cancellationToken);
471 // If destination is a MemoryStream, CopyTo synchronously:
472 memStrDest.Write(_buffer, pos, n);
473 return Task.CompletedTask;
475 } catch(Exception ex) {
476 return Task.FromException(ex);
479 #endif //FEATURE_ASYNC_IO
482 public override long Seek(long offset, SeekOrigin loc) {
483 if (!_isOpen) __Error.StreamIsClosed();
485 if (offset > MemStreamMaxLength)
486 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
488 case SeekOrigin.Begin: {
489 int tempPosition = unchecked(_origin + (int)offset);
490 if (offset < 0 || tempPosition < _origin)
491 throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
492 _position = tempPosition;
495 case SeekOrigin.Current: {
496 int tempPosition = unchecked(_position + (int)offset);
497 if (unchecked(_position + offset) < _origin || tempPosition < _origin)
498 throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
499 _position = tempPosition;
502 case SeekOrigin.End: {
503 int tempPosition = unchecked(_length + (int)offset);
504 if ( unchecked(_length + offset) < _origin || tempPosition < _origin )
505 throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
506 _position = tempPosition;
510 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin"));
513 Contract.Assert(_position >= 0, "_position >= 0");
517 // Sets the length of the stream to a given value. The new
518 // value must be nonnegative and less than the space remaining in
519 // the array, Int32.MaxValue - origin
520 // Origin is 0 in all cases other than a MemoryStream created on
521 // top of an existing array and a specific starting offset was passed
522 // into the MemoryStream constructor. The upper bounds prevents any
523 // situations where a stream may be created on top of an array then
524 // the stream is made longer than the maximum possible length of the
525 // array (Int32.MaxValue).
527 public override void SetLength(long value) {
528 if (value < 0 || value > Int32.MaxValue) {
529 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
531 Contract.Ensures(_length - _origin == value);
532 Contract.EndContractBlock();
535 // Origin wasn't publicly exposed above.
536 Contract.Assert(MemStreamMaxLength == Int32.MaxValue); // Check parameter validation logic in this method if this fails.
537 if (value > (Int32.MaxValue - _origin)) {
538 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
541 int newLength = _origin + (int)value;
542 bool allocatedNewArray = EnsureCapacity(newLength);
543 if (!allocatedNewArray && newLength > _length)
544 Array.Clear(_buffer, _length, newLength - _length);
546 if (_position > newLength) _position = newLength;
550 public virtual byte[] ToArray() {
552 // Bug fix for BCL bug when _buffer is null
553 if (_length - _origin == 0)
554 return EmptyArray<byte>.Value;
556 BCLDebug.Perf(_exposable, "MemoryStream::GetBuffer will let you avoid a copy.");
557 byte[] copy = new byte[_length - _origin];
558 Buffer.InternalBlockCopy(_buffer, _origin, copy, 0, _length - _origin);
562 public override void Write(byte[] buffer, int offset, int count) {
564 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
566 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
568 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
569 if (buffer.Length - offset < count)
570 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
571 Contract.EndContractBlock();
573 if (!_isOpen) __Error.StreamIsClosed();
576 int i = _position + count;
577 // Check for overflow
579 throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
582 bool mustZero = _position > _length;
584 bool allocatedNewArray = EnsureCapacity(i);
585 if (allocatedNewArray)
589 Array.Clear(_buffer, _length, i - _length);
592 if ((count <= 8) && (buffer != _buffer))
594 int byteCount = count;
595 while (--byteCount >= 0)
596 _buffer[_position + byteCount] = buffer[offset + byteCount];
599 Buffer.InternalBlockCopy(buffer, offset, _buffer, _position, count);
605 [HostProtection(ExternalThreading = true)]
607 public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
610 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
612 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
614 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
615 if (buffer.Length - offset < count)
616 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
617 Contract.EndContractBlock(); // contract validation copied from Write(...)
619 // If cancellation is already requested, bail early
620 if (cancellationToken.IsCancellationRequested)
621 return Task.FromCancellation(cancellationToken);
625 Write(buffer, offset, count);
626 return Task.CompletedTask;
628 catch (OperationCanceledException oce)
630 return Task.FromCancellation<VoidTaskResult>(oce);
632 catch (Exception exception)
634 return Task.FromException(exception);
637 #endif // FEATURE_ASYNC_IO
639 public override void WriteByte(byte value) {
640 if (!_isOpen) __Error.StreamIsClosed();
643 if (_position >= _length) {
644 int newLength = _position + 1;
645 bool mustZero = _position > _length;
646 if (newLength >= _capacity) {
647 bool allocatedNewArray = EnsureCapacity(newLength);
648 if (allocatedNewArray)
652 Array.Clear(_buffer, _length, _position - _length);
655 _buffer[_position++] = value;
659 // Writes this MemoryStream to another stream.
660 public virtual void WriteTo(Stream stream) {
662 throw new ArgumentNullException("stream", Environment.GetResourceString("ArgumentNull_Stream"));
663 Contract.EndContractBlock();
665 if (!_isOpen) __Error.StreamIsClosed();
666 stream.Write(_buffer, _origin, _length - _origin);
670 [ContractInvariantMethod]
671 private void ObjectInvariantMS() {
672 Contract.Invariant(_origin >= 0);
673 Contract.Invariant(_origin <= _position);
674 Contract.Invariant(_length <= _capacity);
675 // equivalent to _origin > 0 => !expandable, and using fact that _origin is non-negative.
676 Contract.Invariant(_origin == 0 || !_expandable);