Delete FriendAccessAllowedAttribute and associated dead code (#15101)
[platform/upstream/coreclr.git] / src / mscorlib / src / System / IO / MemoryStream.cs
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.
4
5 /*============================================================
6 **
7 ** 
8 ** 
9 **
10 **
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.
14 **
15 **
16 ===========================================================*/
17
18 using System;
19 using System.Runtime;
20 using System.Runtime.CompilerServices;
21 using System.Runtime.InteropServices;
22 using System.Diagnostics;
23 using System.Threading;
24 using System.Threading.Tasks;
25
26 namespace System.IO
27 {
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 
30     // an application.  
31     // 
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
37     {
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
44
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?
49
50         private Task<int> _lastReadTask; // The last successful task returned from ReadAsync
51
52         private const int MemStreamMaxLength = Int32.MaxValue;
53
54         public MemoryStream()
55             : this(0)
56         {
57         }
58
59         public MemoryStream(int capacity)
60         {
61             if (capacity < 0)
62             {
63                 throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity);
64             }
65
66             _buffer = capacity != 0 ? new byte[capacity] : Array.Empty<byte>();
67             _capacity = capacity;
68             _expandable = true;
69             _writable = true;
70             _exposable = true;
71             _origin = 0;      // Must be 0 for byte[]'s created by MemoryStream
72             _isOpen = true;
73         }
74
75         public MemoryStream(byte[] buffer)
76             : this(buffer, true)
77         {
78         }
79
80         public MemoryStream(byte[] buffer, bool writable)
81         {
82             if (buffer == null) throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
83             _buffer = buffer;
84             _length = _capacity = buffer.Length;
85             _writable = writable;
86             _exposable = false;
87             _origin = 0;
88             _isOpen = true;
89         }
90
91         public MemoryStream(byte[] buffer, int index, int count)
92             : this(buffer, index, count, true, false)
93         {
94         }
95
96         public MemoryStream(byte[] buffer, int index, int count, bool writable)
97             : this(buffer, index, count, writable, false)
98         {
99         }
100
101         public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
102         {
103             if (buffer == null)
104                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
105             if (index < 0)
106                 throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
107             if (count < 0)
108                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
109             if (buffer.Length - index < count)
110                 throw new ArgumentException(SR.Argument_InvalidOffLen);
111
112             _buffer = buffer;
113             _origin = _position = index;
114             _length = _capacity = index + count;
115             _writable = writable;
116             _exposable = publiclyVisible;  // Can TryGetBuffer/GetBuffer return the array?
117             _expandable = false;
118             _isOpen = true;
119         }
120
121         public override bool CanRead
122         {
123             get { return _isOpen; }
124         }
125
126         public override bool CanSeek
127         {
128             get { return _isOpen; }
129         }
130
131         public override bool CanWrite
132         {
133             get { return _writable; }
134         }
135
136         private void EnsureWriteable()
137         {
138             if (!CanWrite) __Error.WriteNotSupported();
139         }
140
141         protected override void Dispose(bool disposing)
142         {
143             try
144             {
145                 if (disposing)
146                 {
147                     _isOpen = false;
148                     _writable = false;
149                     _expandable = false;
150                     // Don't set buffer to null - allow TryGetBuffer, GetBuffer & ToArray to work.
151                     _lastReadTask = null;
152                 }
153             }
154             finally
155             {
156                 // Call base.Close() to cleanup async IO resources
157                 base.Dispose(disposing);
158             }
159         }
160
161         // returns a bool saying whether we allocated a new array.
162         private bool EnsureCapacity(int value)
163         {
164             // Check for overflow
165             if (value < 0)
166                 throw new IOException(SR.IO_StreamTooLong);
167             if (value > _capacity)
168             {
169                 int newCapacity = value;
170                 if (newCapacity < 256)
171                     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;
180
181                 Capacity = newCapacity;
182                 return true;
183             }
184             return false;
185         }
186
187         public override void Flush()
188         {
189         }
190
191         public override Task FlushAsync(CancellationToken cancellationToken)
192         {
193             if (cancellationToken.IsCancellationRequested)
194                 return Task.FromCanceled(cancellationToken);
195
196             try
197             {
198                 Flush();
199                 return Task.CompletedTask;
200             }
201             catch (Exception ex)
202             {
203                 return Task.FromException(ex);
204             }
205         }
206
207
208         public virtual byte[] GetBuffer()
209         {
210             if (!_exposable)
211                 throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer);
212             return _buffer;
213         }
214
215         public virtual bool TryGetBuffer(out ArraySegment<byte> buffer)
216         {
217             if (!_exposable)
218             {
219                 buffer = default(ArraySegment<byte>);
220                 return false;
221             }
222
223             buffer = new ArraySegment<byte>(_buffer, offset: _origin, count: (_length - _origin));
224             return true;
225         }
226
227         // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) ---------------
228
229         // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer())
230         internal byte[] InternalGetBuffer()
231         {
232             return _buffer;
233         }
234
235         // PERF: True cursor position, we don't need _origin for direct access
236         internal int InternalGetPosition()
237         {
238             if (!_isOpen) __Error.StreamIsClosed();
239             return _position;
240         }
241
242         // PERF: Takes out Int32 as fast as possible
243         internal int InternalReadInt32()
244         {
245             if (!_isOpen)
246                 __Error.StreamIsClosed();
247
248             int pos = (_position += 4); // use temp to avoid a race condition
249             if (pos > _length)
250             {
251                 _position = _length;
252                 __Error.EndOfFile();
253             }
254             return (int)(_buffer[pos - 4] | _buffer[pos - 3] << 8 | _buffer[pos - 2] << 16 | _buffer[pos - 1] << 24);
255         }
256
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)
259         {
260             if (!_isOpen) __Error.StreamIsClosed();
261
262             int n = _length - _position;
263             if (n > count) n = count;
264             if (n < 0) n = 0;
265
266             Debug.Assert(_position + n >= 0, "_position + n >= 0");  // len is less than 2^31 -1.
267             _position += n;
268             return n;
269         }
270
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
273         // of the stream.
274         // 
275         public virtual int Capacity
276         {
277             get
278             {
279                 if (!_isOpen) __Error.StreamIsClosed();
280                 return _capacity - _origin;
281             }
282             set
283             {
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);
287
288                 if (!_isOpen) __Error.StreamIsClosed();
289                 if (!_expandable && (value != Capacity)) __Error.MemoryStreamNotExpandable();
290
291                 // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors)
292                 if (_expandable && value != _capacity)
293                 {
294                     if (value > 0)
295                     {
296                         byte[] newBuffer = new byte[value];
297                         if (_length > 0) Buffer.InternalBlockCopy(_buffer, 0, newBuffer, 0, _length);
298                         _buffer = newBuffer;
299                     }
300                     else
301                     {
302                         _buffer = null;
303                     }
304                     _capacity = value;
305                 }
306             }
307         }
308
309         public override long Length
310         {
311             get
312             {
313                 if (!_isOpen) __Error.StreamIsClosed();
314                 return _length - _origin;
315             }
316         }
317
318         public override long Position
319         {
320             get
321             {
322                 if (!_isOpen) __Error.StreamIsClosed();
323                 return _position - _origin;
324             }
325             set
326             {
327                 if (value < 0)
328                     throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
329
330                 if (!_isOpen) __Error.StreamIsClosed();
331
332                 if (value > MemStreamMaxLength)
333                     throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
334                 _position = _origin + (int)value;
335             }
336         }
337
338         public override int Read([In, Out] byte[] buffer, int offset, int count)
339         {
340             if (buffer == null)
341                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
342             if (offset < 0)
343                 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
344             if (count < 0)
345                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
346             if (buffer.Length - offset < count)
347                 throw new ArgumentException(SR.Argument_InvalidOffLen);
348
349             if (!_isOpen) __Error.StreamIsClosed();
350
351             int n = _length - _position;
352             if (n > count) n = count;
353             if (n <= 0)
354                 return 0;
355
356             Debug.Assert(_position + n >= 0, "_position + n >= 0");  // len is less than 2^31 -1.
357
358             if (n <= 8)
359             {
360                 int byteCount = n;
361                 while (--byteCount >= 0)
362                     buffer[offset + byteCount] = _buffer[_position + byteCount];
363             }
364             else
365                 Buffer.InternalBlockCopy(_buffer, _position, buffer, offset, n);
366             _position += n;
367
368             return n;
369         }
370
371         public override int Read(Span<byte> destination)
372         {
373             if (GetType() != typeof(MemoryStream))
374             {
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);
379             }
380
381             if (!_isOpen)
382             {
383                 __Error.StreamIsClosed();
384             }
385
386             int n = Math.Min(_length - _position, destination.Length);
387             if (n <= 0)
388             {
389                 return 0;
390             }
391
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);
397
398             _position += n;
399             return n;
400         }
401
402         public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
403         {
404             if (buffer == null)
405                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
406             if (offset < 0)
407                 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
408             if (count < 0)
409                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
410             if (buffer.Length - offset < count)
411                 throw new ArgumentException(SR.Argument_InvalidOffLen);
412
413             // If cancellation was requested, bail early
414             if (cancellationToken.IsCancellationRequested)
415                 return Task.FromCanceled<int>(cancellationToken);
416
417             try
418             {
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));
424             }
425             catch (OperationCanceledException oce)
426             {
427                 return Task.FromCancellation<int>(oce);
428             }
429             catch (Exception exception)
430             {
431                 return Task.FromException<int>(exception);
432             }
433         }
434
435         public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
436         {
437             if (cancellationToken.IsCancellationRequested)
438             {
439                 return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
440             }
441
442             try
443             {
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));
460             }
461             catch (OperationCanceledException oce)
462             {
463                 return new ValueTask<int>(Task.FromCancellation<int>(oce));
464             }
465             catch (Exception exception)
466             {
467                 return new ValueTask<int>(Task.FromException<int>(exception));
468             }
469         }
470
471
472         public override int ReadByte()
473         {
474             if (!_isOpen) __Error.StreamIsClosed();
475
476             if (_position >= _length) return -1;
477
478             return _buffer[_position++];
479         }
480
481         public override void CopyTo(Stream destination, int bufferSize)
482         {
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);
486
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))
492             {
493                 base.CopyTo(destination, bufferSize);
494                 return;
495             }
496
497             int originalPosition = _position;
498
499             // Seek to the end of the MemoryStream.
500             int remaining = InternalEmulateRead(_length - originalPosition);
501
502             // If we were already at or past the end, there's no copying to do so just quit.
503             if (remaining > 0)
504             {
505                 // Call Write() on the other Stream, using our internal buffer and avoiding any
506                 // intermediary allocations.
507                 destination.Write(_buffer, originalPosition, remaining);
508             }
509         }
510
511         public override Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
512         {
513             // This implementation offers beter performance compared to the base class version.
514
515             StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
516
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);
523
524             // If cancelled - return fast:
525             if (cancellationToken.IsCancellationRequested)
526                 return Task.FromCanceled(cancellationToken);
527
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)
531
532             Int32 pos = _position;
533             Int32 n = InternalEmulateRead(_length - _position);
534
535             // If we were already at or past the end, there's no copying to do so just quit.
536             if (n == 0)
537                 return Task.CompletedTask;
538
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);
543
544             try
545             {
546                 // If destination is a MemoryStream, CopyTo synchronously:
547                 memStrDest.Write(_buffer, pos, n);
548                 return Task.CompletedTask;
549             }
550             catch (Exception ex)
551             {
552                 return Task.FromException(ex);
553             }
554         }
555
556
557         public override long Seek(long offset, SeekOrigin loc)
558         {
559             if (!_isOpen) __Error.StreamIsClosed();
560
561             if (offset > MemStreamMaxLength)
562                 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_StreamLength);
563             switch (loc)
564             {
565                 case SeekOrigin.Begin:
566                     {
567                         int tempPosition = unchecked(_origin + (int)offset);
568                         if (offset < 0 || tempPosition < _origin)
569                             throw new IOException(SR.IO_SeekBeforeBegin);
570                         _position = tempPosition;
571                         break;
572                     }
573                 case SeekOrigin.Current:
574                     {
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;
579                         break;
580                     }
581                 case SeekOrigin.End:
582                     {
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;
587                         break;
588                     }
589                 default:
590                     throw new ArgumentException(SR.Argument_InvalidSeekOrigin);
591             }
592
593             Debug.Assert(_position >= 0, "_position >= 0");
594             return _position;
595         }
596
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).
606         // 
607         public override void SetLength(long value)
608         {
609             if (value < 0 || value > Int32.MaxValue)
610             {
611                 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
612             }
613             EnsureWriteable();
614
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))
618             {
619                 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
620             }
621
622             int newLength = _origin + (int)value;
623             bool allocatedNewArray = EnsureCapacity(newLength);
624             if (!allocatedNewArray && newLength > _length)
625                 Array.Clear(_buffer, _length, newLength - _length);
626             _length = newLength;
627             if (_position > newLength) _position = newLength;
628         }
629
630         public virtual byte[] ToArray()
631         {
632             byte[] copy = new byte[_length - _origin];
633             Buffer.InternalBlockCopy(_buffer, _origin, copy, 0, _length - _origin);
634             return copy;
635         }
636
637         public override void Write(byte[] buffer, int offset, int count)
638         {
639             if (buffer == null)
640                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
641             if (offset < 0)
642                 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
643             if (count < 0)
644                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
645             if (buffer.Length - offset < count)
646                 throw new ArgumentException(SR.Argument_InvalidOffLen);
647
648             if (!_isOpen) __Error.StreamIsClosed();
649             EnsureWriteable();
650
651             int i = _position + count;
652             // Check for overflow
653             if (i < 0)
654                 throw new IOException(SR.IO_StreamTooLong);
655
656             if (i > _length)
657             {
658                 bool mustZero = _position > _length;
659                 if (i > _capacity)
660                 {
661                     bool allocatedNewArray = EnsureCapacity(i);
662                     if (allocatedNewArray)
663                         mustZero = false;
664                 }
665                 if (mustZero)
666                     Array.Clear(_buffer, _length, i - _length);
667                 _length = i;
668             }
669             if ((count <= 8) && (buffer != _buffer))
670             {
671                 int byteCount = count;
672                 while (--byteCount >= 0)
673                     _buffer[_position + byteCount] = buffer[offset + byteCount];
674             }
675             else
676                 Buffer.InternalBlockCopy(buffer, offset, _buffer, _position, count);
677             _position = i;
678         }
679
680         public override void Write(ReadOnlySpan<byte> source)
681         {
682             if (GetType() != typeof(MemoryStream))
683             {
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.
687                 base.Write(source);
688                 return;
689             }
690
691             if (!_isOpen)
692             {
693                 __Error.StreamIsClosed();
694             }
695             EnsureWriteable();
696
697             // Check for overflow
698             int i = _position + source.Length;
699             if (i < 0)
700             {
701                 throw new IOException(SR.IO_StreamTooLong);
702             }
703
704             if (i > _length)
705             {
706                 bool mustZero = _position > _length;
707                 if (i > _capacity)
708                 {
709                     bool allocatedNewArray = EnsureCapacity(i);
710                     if (allocatedNewArray)
711                     {
712                         mustZero = false;
713                     }
714                 }
715                 if (mustZero)
716                 {
717                     Array.Clear(_buffer, _length, i - _length);
718                 }
719                 _length = i;
720             }
721
722             source.CopyTo(new Span<byte>(_buffer, _position, source.Length));
723             _position = i;
724         }
725
726         public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
727         {
728             if (buffer == null)
729                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
730             if (offset < 0)
731                 throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
732             if (count < 0)
733                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
734             if (buffer.Length - offset < count)
735                 throw new ArgumentException(SR.Argument_InvalidOffLen);
736
737             // If cancellation is already requested, bail early
738             if (cancellationToken.IsCancellationRequested)
739                 return Task.FromCanceled(cancellationToken);
740
741             try
742             {
743                 Write(buffer, offset, count);
744                 return Task.CompletedTask;
745             }
746             catch (OperationCanceledException oce)
747             {
748                 return Task.FromCancellation<VoidTaskResult>(oce);
749             }
750             catch (Exception exception)
751             {
752                 return Task.FromException(exception);
753             }
754         }
755
756         public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
757         {
758             if (cancellationToken.IsCancellationRequested)
759             {
760                 return Task.FromCanceled(cancellationToken);
761             }
762
763             try
764             {
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))
768                 {
769                     Write(sourceArray.Array, sourceArray.Offset, sourceArray.Count);
770                 }
771                 else
772                 {
773                     Write(source.Span);
774                 }
775                 return Task.CompletedTask;
776             }
777             catch (OperationCanceledException oce)
778             {
779                 return Task.FromCancellation<VoidTaskResult>(oce);
780             }
781             catch (Exception exception)
782             {
783                 return Task.FromException(exception);
784             }
785         }
786
787         public override void WriteByte(byte value)
788         {
789             if (!_isOpen) __Error.StreamIsClosed();
790             EnsureWriteable();
791
792             if (_position >= _length)
793             {
794                 int newLength = _position + 1;
795                 bool mustZero = _position > _length;
796                 if (newLength >= _capacity)
797                 {
798                     bool allocatedNewArray = EnsureCapacity(newLength);
799                     if (allocatedNewArray)
800                         mustZero = false;
801                 }
802                 if (mustZero)
803                     Array.Clear(_buffer, _length, _position - _length);
804                 _length = newLength;
805             }
806             _buffer[_position++] = value;
807         }
808
809         // Writes this MemoryStream to another stream.
810         public virtual void WriteTo(Stream stream)
811         {
812             if (stream == null)
813                 throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream);
814
815             if (!_isOpen) __Error.StreamIsClosed();
816             stream.Write(_buffer, _origin, _length - _origin);
817         }
818     }
819 }