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
86 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
88 public MemoryStream(byte[] buffer
)
89 : this(buffer
, true) {
92 public MemoryStream(byte[] buffer
, bool writable
) {
93 if (buffer
== null) throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
94 Contract
.EndContractBlock();
96 _length
= _capacity
= buffer
.Length
;
103 public MemoryStream(byte[] buffer
, int index
, int count
)
104 : this(buffer
, index
, count
, true, false) {
107 public MemoryStream(byte[] buffer
, int index
, int count
, bool writable
)
108 : this(buffer
, index
, count
, writable
, false) {
111 public MemoryStream(byte[] buffer
, int index
, int count
, bool writable
, bool publiclyVisible
) {
113 throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
115 throw new ArgumentOutOfRangeException("index", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
117 throw new ArgumentOutOfRangeException("count", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
118 if (buffer
.Length
- index
< count
)
119 throw new ArgumentException(Environment
.GetResourceString("Argument_InvalidOffLen"));
120 Contract
.EndContractBlock();
123 _origin
= _position
= index
;
124 _length
= _capacity
= index
+ count
;
125 _writable
= writable
;
126 _exposable
= publiclyVisible
; // Can GetBuffer return the array?
131 public override bool CanRead
{
133 get { return _isOpen; }
136 public override bool CanSeek
{
138 get { return _isOpen; }
141 public override bool CanWrite
{
143 get { return _writable; }
146 private void EnsureWriteable() {
147 // Previously, instead of calling CanWrite we just checked the _writable field directly, and some code
148 // took a dependency on that behavior.
150 if (IsAppEarlierThanSl4
) {
151 if (!_writable
) __Error
.WriteNotSupported();
153 if (!CanWrite
) __Error
.WriteNotSupported();
156 if (!CanWrite
) __Error
.WriteNotSupported();
160 protected override void Dispose(bool disposing
)
167 // Don't set buffer to null - allow GetBuffer & ToArray to work.
169 _lastReadTask
= null;
174 // Call base.Close() to cleanup async IO resources
175 base.Dispose(disposing
);
179 // returns a bool saying whether we allocated a new array.
180 private bool EnsureCapacity(int value) {
181 // Check for overflow
183 throw new IOException(Environment
.GetResourceString("IO.IO_StreamTooLong"));
184 if (value > _capacity
) {
185 int newCapacity
= value;
186 if (newCapacity
< 256)
188 if (newCapacity
< _capacity
* 2)
189 newCapacity
= _capacity
* 2;
190 Capacity
= newCapacity
;
196 public override void Flush() {
200 [HostProtection(ExternalThreading
=true)]
202 public override Task
FlushAsync(CancellationToken cancellationToken
) {
204 if (cancellationToken
.IsCancellationRequested
)
205 return Task
.FromCancellation(cancellationToken
);
210 return Task
.CompletedTask
;
212 } catch(Exception ex
) {
214 return Task
.FromException(ex
);
217 #endif // FEATURE_ASYNC_IO
221 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
223 public virtual byte[] GetBuffer() {
225 throw new UnauthorizedAccessException(Environment
.GetResourceString("UnauthorizedAccess_MemStreamBuffer"));
229 // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) ---------------
231 // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer())
233 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
235 internal byte[] InternalGetBuffer() {
239 // PERF: Get origin and length - used in ResourceWriter.
241 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
243 [FriendAccessAllowed
]
244 internal void InternalGetOriginAndLength(out int origin
, out int length
)
246 if (!_isOpen
) __Error
.StreamIsClosed();
251 // PERF: True cursor position, we don't need _origin for direct access
253 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
255 internal int InternalGetPosition() {
256 if (!_isOpen
) __Error
.StreamIsClosed();
260 // PERF: Takes out Int32 as fast as possible
261 internal int InternalReadInt32() {
263 __Error
.StreamIsClosed();
265 int pos
= (_position
+= 4); // use temp to avoid ----
271 return (int)(_buffer
[pos
-4] | _buffer
[pos
-3] << 8 | _buffer
[pos
-2] << 16 | _buffer
[pos
-1] << 24);
274 // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes
275 internal int InternalEmulateRead(int count
) {
276 if (!_isOpen
) __Error
.StreamIsClosed();
278 int n
= _length
- _position
;
279 if (n
> count
) n
= count
;
282 Contract
.Assert(_position
+ n
>= 0, "_position + n >= 0"); // len is less than 2^31 -1.
287 // Gets & sets the capacity (number of bytes allocated) for this stream.
288 // The capacity cannot be set to a value less than the current length
291 public virtual int Capacity
{
293 if (!_isOpen
) __Error
.StreamIsClosed();
294 return _capacity
- _origin
;
297 // Only update the capacity if the MS is expandable and the value is different than the current capacity.
298 // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity
300 if (value < Length
) throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
302 Contract
.Ensures(_capacity
- _origin
== value);
303 Contract
.EndContractBlock();
306 if (IsAppEarlierThanSl4
) {
307 if (value < _length
) throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
309 if (value < Length
) throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
313 if (!_isOpen
) __Error
.StreamIsClosed();
314 if (!_expandable
&& (value != Capacity
)) __Error
.MemoryStreamNotExpandable();
316 // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors)
317 if (_expandable
&& value != _capacity
) {
319 byte[] newBuffer
= new byte[value];
320 if (_length
> 0) Buffer
.InternalBlockCopy(_buffer
, 0, newBuffer
, 0, _length
);
331 public override long Length
{
333 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
336 if (!_isOpen
) __Error
.StreamIsClosed();
337 return _length
- _origin
;
341 public override long Position
{
343 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
346 if (!_isOpen
) __Error
.StreamIsClosed();
347 return _position
- _origin
;
351 throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
352 Contract
.Ensures(Position
== value);
353 Contract
.EndContractBlock();
355 if (!_isOpen
) __Error
.StreamIsClosed();
357 if (value > MemStreamMaxLength
)
358 throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_StreamLength"));
359 _position
= _origin
+ (int)value;
364 private static bool IsAppEarlierThanSl4
{
366 return CompatibilitySwitches
.IsAppEarlierThanSilverlight4
;
371 public override int Read([In
, Out
] byte[] buffer
, int offset
, int count
) {
373 throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
375 throw new ArgumentOutOfRangeException("offset", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
377 throw new ArgumentOutOfRangeException("count", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
378 if (buffer
.Length
- offset
< count
)
379 throw new ArgumentException(Environment
.GetResourceString("Argument_InvalidOffLen"));
380 Contract
.EndContractBlock();
382 if (!_isOpen
) __Error
.StreamIsClosed();
384 int n
= _length
- _position
;
385 if (n
> count
) n
= count
;
389 Contract
.Assert(_position
+ n
>= 0, "_position + n >= 0"); // len is less than 2^31 -1.
394 while (--byteCount
>= 0)
395 buffer
[offset
+ byteCount
] = _buffer
[_position
+ byteCount
];
398 Buffer
.InternalBlockCopy(_buffer
, _position
, buffer
, offset
, n
);
405 [HostProtection(ExternalThreading
= true)]
407 public override Task
<int> ReadAsync(Byte
[] buffer
, int offset
, int count
, CancellationToken cancellationToken
)
410 throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
412 throw new ArgumentOutOfRangeException("offset", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
414 throw new ArgumentOutOfRangeException("count", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
415 if (buffer
.Length
- offset
< count
)
416 throw new ArgumentException(Environment
.GetResourceString("Argument_InvalidOffLen"));
417 Contract
.EndContractBlock(); // contract validation copied from Read(...)
419 // If cancellation was requested, bail early
420 if (cancellationToken
.IsCancellationRequested
)
421 return Task
.FromCancellation
<int>(cancellationToken
);
425 int n
= Read(buffer
, offset
, count
);
426 var t
= _lastReadTask
;
427 Contract
.Assert(t
== null || t
.Status
== TaskStatus
.RanToCompletion
,
428 "Expected that a stored last task completed successfully");
429 return (t
!= null && t
.Result
== n
) ? t
: (_lastReadTask
= Task
.FromResult
<int>(n
));
431 catch (OperationCanceledException oce
)
433 return Task
.FromCancellation
<int>(oce
);
435 catch (Exception exception
)
437 return Task
.FromException
<int>(exception
);
440 #endif //FEATURE_ASYNC_IO
443 public override int ReadByte() {
444 if (!_isOpen
) __Error
.StreamIsClosed();
446 if (_position
>= _length
) return -1;
448 return _buffer
[_position
++];
453 public override Task
CopyToAsync(Stream destination
, Int32 bufferSize
, CancellationToken cancellationToken
) {
455 // This implementation offers beter performance compared to the base class version.
457 // The parameter checks must be in [....] with the base version:
458 if (destination
== null)
459 throw new ArgumentNullException("destination");
462 throw new ArgumentOutOfRangeException("bufferSize", Environment
.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
464 if (!CanRead
&& !CanWrite
)
465 throw new ObjectDisposedException(null, Environment
.GetResourceString("ObjectDisposed_StreamClosed"));
467 if (!destination
.CanRead
&& !destination
.CanWrite
)
468 throw new ObjectDisposedException("destination", Environment
.GetResourceString("ObjectDisposed_StreamClosed"));
471 throw new NotSupportedException(Environment
.GetResourceString("NotSupported_UnreadableStream"));
473 if (!destination
.CanWrite
)
474 throw new NotSupportedException(Environment
.GetResourceString("NotSupported_UnwritableStream"));
476 Contract
.EndContractBlock();
478 // If we have been inherited into a subclass, the following implementation could be incorrect
479 // since it does not call through to Read() or Write() which a subclass might have overriden.
480 // To be safe we will only use this implementation in cases where we know it is safe to do so,
481 // and delegate to our base class (which will call into Read/Write) when we are not sure.
482 if (this.GetType() != typeof(MemoryStream
))
483 return base.CopyToAsync(destination
, bufferSize
, cancellationToken
);
485 // If cancelled - return fast:
486 if (cancellationToken
.IsCancellationRequested
)
487 return Task
.FromCancellation(cancellationToken
);
489 // Avoid copying data from this buffer into a temp buffer:
490 // (require that InternalEmulateRead does not throw,
491 // otherwise it needs to be wrapped into try-catch-Task.FromException like memStrDest.Write below)
493 Int32 pos
= _position
;
494 Int32 n
= InternalEmulateRead(_length
- _position
);
496 // If destination is not a memory stream, write there asynchronously:
497 MemoryStream memStrDest
= destination
as MemoryStream
;
498 if (memStrDest
== null)
499 return destination
.WriteAsync(_buffer
, pos
, n
, cancellationToken
);
503 // If destination is a MemoryStream, CopyTo synchronously:
504 memStrDest
.Write(_buffer
, pos
, n
);
505 return Task
.CompletedTask
;
507 } catch(Exception ex
) {
508 return Task
.FromException(ex
);
511 #endif //FEATURE_ASYNC_IO
514 public override long Seek(long offset
, SeekOrigin loc
) {
515 if (!_isOpen
) __Error
.StreamIsClosed();
517 if (offset
> MemStreamMaxLength
)
518 throw new ArgumentOutOfRangeException("offset", Environment
.GetResourceString("ArgumentOutOfRange_StreamLength"));
520 case SeekOrigin
.Begin
: {
521 int tempPosition
= unchecked(_origin
+ (int)offset
);
522 if (offset
< 0 || tempPosition
< _origin
)
523 throw new IOException(Environment
.GetResourceString("IO.IO_SeekBeforeBegin"));
524 _position
= tempPosition
;
527 case SeekOrigin
.Current
: {
528 int tempPosition
= unchecked(_position
+ (int)offset
);
529 if (unchecked(_position
+ offset
) < _origin
|| tempPosition
< _origin
)
530 throw new IOException(Environment
.GetResourceString("IO.IO_SeekBeforeBegin"));
531 _position
= tempPosition
;
534 case SeekOrigin
.End
: {
535 int tempPosition
= unchecked(_length
+ (int)offset
);
536 if ( unchecked(_length
+ offset
) < _origin
|| tempPosition
< _origin
)
537 throw new IOException(Environment
.GetResourceString("IO.IO_SeekBeforeBegin"));
538 _position
= tempPosition
;
542 throw new ArgumentException(Environment
.GetResourceString("Argument_InvalidSeekOrigin"));
545 Contract
.Assert(_position
>= 0, "_position >= 0");
549 // Sets the length of the stream to a given value. The new
550 // value must be nonnegative and less than the space remaining in
551 // the array, Int32.MaxValue - origin
552 // Origin is 0 in all cases other than a MemoryStream created on
553 // top of an existing array and a specific starting offset was passed
554 // into the MemoryStream constructor. The upper bounds prevents any
555 // situations where a stream may be created on top of an array then
556 // the stream is made longer than the maximum possible length of the
557 // array (Int32.MaxValue).
559 public override void SetLength(long value) {
560 if (value < 0 || value > Int32
.MaxValue
) {
561 throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_StreamLength"));
563 Contract
.Ensures(_length
- _origin
== value);
564 Contract
.EndContractBlock();
567 // Origin wasn't publicly exposed above.
568 Contract
.Assert(MemStreamMaxLength
== Int32
.MaxValue
); // Check parameter validation logic in this method if this fails.
569 if (value > (Int32
.MaxValue
- _origin
)) {
570 throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_StreamLength"));
573 int newLength
= _origin
+ (int)value;
574 bool allocatedNewArray
= EnsureCapacity(newLength
);
575 if (!allocatedNewArray
&& newLength
> _length
)
576 Array
.Clear(_buffer
, _length
, newLength
- _length
);
578 if (_position
> newLength
) _position
= newLength
;
582 public virtual byte[] ToArray() {
584 // Bug fix for BCL bug when _buffer is null
585 if (_length
- _origin
== 0)
586 return EmptyArray
<byte>.Value
;
588 BCLDebug
.Perf(_exposable
, "MemoryStream::GetBuffer will let you avoid a copy.");
589 byte[] copy
= new byte[_length
- _origin
];
590 Buffer
.InternalBlockCopy(_buffer
, _origin
, copy
, 0, _length
- _origin
);
594 public override void Write(byte[] buffer
, int offset
, int count
) {
596 throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
598 throw new ArgumentOutOfRangeException("offset", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
600 throw new ArgumentOutOfRangeException("count", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
601 if (buffer
.Length
- offset
< count
)
602 throw new ArgumentException(Environment
.GetResourceString("Argument_InvalidOffLen"));
603 Contract
.EndContractBlock();
605 if (!_isOpen
) __Error
.StreamIsClosed();
608 int i
= _position
+ count
;
609 // Check for overflow
611 throw new IOException(Environment
.GetResourceString("IO.IO_StreamTooLong"));
614 bool mustZero
= _position
> _length
;
616 bool allocatedNewArray
= EnsureCapacity(i
);
617 if (allocatedNewArray
)
621 Array
.Clear(_buffer
, _length
, i
- _length
);
624 if ((count
<= 8) && (buffer
!= _buffer
))
626 int byteCount
= count
;
627 while (--byteCount
>= 0)
628 _buffer
[_position
+ byteCount
] = buffer
[offset
+ byteCount
];
631 Buffer
.InternalBlockCopy(buffer
, offset
, _buffer
, _position
, count
);
637 [HostProtection(ExternalThreading
= true)]
639 public override Task
WriteAsync(Byte
[] buffer
, int offset
, int count
, CancellationToken cancellationToken
)
642 throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
644 throw new ArgumentOutOfRangeException("offset", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
646 throw new ArgumentOutOfRangeException("count", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
647 if (buffer
.Length
- offset
< count
)
648 throw new ArgumentException(Environment
.GetResourceString("Argument_InvalidOffLen"));
649 Contract
.EndContractBlock(); // contract validation copied from Write(...)
651 // If cancellation is already requested, bail early
652 if (cancellationToken
.IsCancellationRequested
)
653 return Task
.FromCancellation(cancellationToken
);
657 Write(buffer
, offset
, count
);
658 return Task
.CompletedTask
;
660 catch (OperationCanceledException oce
)
662 return Task
.FromCancellation
<VoidTaskResult
>(oce
);
664 catch (Exception exception
)
666 return Task
.FromException(exception
);
669 #endif // FEATURE_ASYNC_IO
671 public override void WriteByte(byte value) {
672 if (!_isOpen
) __Error
.StreamIsClosed();
675 if (_position
>= _length
) {
676 int newLength
= _position
+ 1;
677 bool mustZero
= _position
> _length
;
678 if (newLength
>= _capacity
) {
679 bool allocatedNewArray
= EnsureCapacity(newLength
);
680 if (allocatedNewArray
)
684 Array
.Clear(_buffer
, _length
, _position
- _length
);
687 _buffer
[_position
++] = value;
691 // Writes this MemoryStream to another stream.
692 public virtual void WriteTo(Stream stream
) {
694 throw new ArgumentNullException("stream", Environment
.GetResourceString("ArgumentNull_Stream"));
695 Contract
.EndContractBlock();
697 if (!_isOpen
) __Error
.StreamIsClosed();
698 stream
.Write(_buffer
, _origin
, _length
- _origin
);
702 [ContractInvariantMethod
]
703 private void ObjectInvariantMS() {
704 Contract
.Invariant(_origin
>= 0);
705 Contract
.Invariant(_origin
<= _position
);
706 Contract
.Invariant(_length
<= _capacity
);
707 // equivalent to _origin > 0 => !expandable, and using fact that _origin is non-negative.
708 Contract
.Invariant(_origin
== 0 || !_expandable
);