[bcl] Rename FEATURE_MONO_CAS to MONO_FEATURE_CAS
[mono.git] / mcs / class / referencesource / mscorlib / system / io / unmanagedmemorystream.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  UnmanagedMemoryStream
9 **
10 ** <OWNER>[....]</OWNER>
11 **
12 ** Purpose: Create a stream over unmanaged memory, mostly
13 **          useful for memory-mapped files.
14 **
15 ** Date:  October 20, 2000 (made public August 4, 2003)
16 **
17 ===========================================================*/
18 using System;
19 using System.Runtime;
20 using System.Runtime.CompilerServices;
21 using System.Runtime.InteropServices;
22 using System.Security;
23 using System.Security.Permissions;
24 using System.Threading;
25 using System.Diagnostics.Contracts;
26 #if !FEATURE_PAL && FEATURE_ASYNC_IO || MONO
27 using System.Threading.Tasks; 
28 #endif  // !FEATURE_PAL && FEATURE_ASYNC_IO 
29
30
31 namespace System.IO {
32
33     /*
34      * This class is used to access a contiguous block of memory, likely outside 
35      * the GC heap (or pinned in place in the GC heap, but a MemoryStream may 
36      * make more sense in those cases).  It's great if you have a pointer and
37      * a length for a section of memory mapped in by someone else and you don't
38      * want to copy this into the GC heap.  UnmanagedMemoryStream assumes these 
39      * two things:
40      *
41      * 1) All the memory in the specified block is readable or writable,
42      *    depending on the values you pass to the constructor.
43      * 2) The lifetime of the block of memory is at least as long as the lifetime
44      *    of the UnmanagedMemoryStream.
45      * 3) You clean up the memory when appropriate.  The UnmanagedMemoryStream 
46      *    currently will do NOTHING to free this memory.
47      * 4) All calls to Write and WriteByte may not be threadsafe currently.
48      *
49      * It may become necessary to add in some sort of 
50      * DeallocationMode enum, specifying whether we unmap a section of memory, 
51      * call free, run a user-provided delegate to free the memory, etc etc.  
52      * We'll suggest user write a subclass of UnmanagedMemoryStream that uses
53      * a SafeHandle subclass to hold onto the memory.
54      * Check for problems when using this in the negative parts of a 
55      * process's address space.  We may need to use unsigned longs internally
56      * and change the overflow detection logic.
57      * 
58      * -----SECURITY MODEL AND SILVERLIGHT-----
59      * A few key notes about exposing UMS in silverlight:
60      * 1. No ctors are exposed to transparent code. This version of UMS only
61      * supports byte* (not SafeBuffer). Therefore, framework code can create
62      * a UMS and hand it to transparent code. Transparent code can use most
63      * operations on a UMS, but not operations that directly expose a 
64      * pointer.
65      * 
66      * 2. Scope of "unsafe" and non-CLS compliant operations reduced: The
67      * Whidbey version of this class has CLSCompliant(false) at the class 
68      * level and unsafe modifiers at the method level. These were reduced to 
69      * only where the unsafe operation is performed -- i.e. immediately 
70      * around the pointer manipulation. Note that this brings UMS in line 
71      * with recent changes in pu/clr to support SafeBuffer.
72      * 
73      * 3. Currently, the only caller that creates a UMS is ResourceManager, 
74      * which creates read-only UMSs, and therefore operations that can 
75      * change the length will throw because write isn't supported. A 
76      * conservative option would be to formalize the concept that _only_
77      * read-only UMSs can be creates, and enforce this by making WriteX and
78      * SetLength SecurityCritical. However, this is a violation of 
79      * security inheritance rules, so we must keep these safe. The 
80      * following notes make this acceptable for future use.
81      *    a. a race condition in WriteX that could have allowed a thread to 
82      *    read from unzeroed memory was fixed
83      *    b. memory region cannot be expanded beyond _capacity; in other 
84      *    words, a UMS creator is saying a writeable UMS is safe to 
85      *    write to anywhere in the memory range up to _capacity, specified
86      *    in the ctor. Even if the caller doesn't specify a capacity, then
87      *    length is used as the capacity.
88      */
89     public class UnmanagedMemoryStream : Stream
90     {
91         // 
92
93         private const long UnmanagedMemStreamMaxLength = Int64.MaxValue;
94
95         [System.Security.SecurityCritical] // auto-generated
96         private SafeBuffer _buffer;
97         [SecurityCritical]
98         private unsafe byte* _mem;
99         private long _length;
100         private long _capacity;
101         private long _position;
102         private long _offset;
103         private FileAccess _access;
104         internal bool _isOpen;        
105 #if !FEATURE_PAL && FEATURE_ASYNC_IO || MONO
106         [NonSerialized] 
107         private Task<Int32> _lastReadTask; // The last successful task returned from ReadAsync 
108 #endif  // FEATURE_PAL && FEATURE_ASYNC_IO 
109
110
111         // Needed for subclasses that need to map a file, etc.
112         [System.Security.SecuritySafeCritical]  // auto-generated
113         protected UnmanagedMemoryStream()
114         {
115             unsafe {
116                 _mem = null;
117             }
118             _isOpen = false;
119         }
120
121         [System.Security.SecuritySafeCritical]  // auto-generated
122         public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) {
123             Initialize(buffer, offset, length, FileAccess.Read, false);
124         }
125
126         [System.Security.SecuritySafeCritical]  // auto-generated
127         public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) {
128             Initialize(buffer, offset, length, access, false);
129         }
130
131         // We must create one of these without doing a security check.  This
132         // class is created while security is trying to start up.  Plus, doing
133         // a Demand from Assembly.GetManifestResourceStream isn't useful.
134         [System.Security.SecurityCritical]  // auto-generated
135         internal UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) {
136             Initialize(buffer, offset, length, access, skipSecurityCheck);
137         }
138
139         [System.Security.SecuritySafeCritical]  // auto-generated
140         protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) {
141             Initialize(buffer, offset, length, access, false);
142         }
143
144         [System.Security.SecurityCritical]  // auto-generated
145         internal void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) {
146             if (buffer == null) {
147                 throw new ArgumentNullException("buffer");
148             }
149             if (offset < 0) {
150                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
151             }
152             if (length < 0) {
153                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
154             }
155             if (buffer.ByteLength < (ulong)(offset + length)) {
156                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSafeBufferOffLen"));
157             }
158             if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
159                 throw new ArgumentOutOfRangeException("access");
160             }
161             Contract.EndContractBlock();
162
163             if (_isOpen) {
164                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice"));
165             }
166 #if MONO_FEATURE_CAS
167             if (!skipSecurityCheck) {
168 #pragma warning disable 618
169                 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
170 #pragma warning restore 618
171             }
172 #endif
173
174             // check for wraparound
175             unsafe {
176                 byte* pointer = null;
177                 RuntimeHelpers.PrepareConstrainedRegions();
178                 try {
179                     buffer.AcquirePointer(ref pointer);
180                     if ( (pointer + offset + length) < pointer) {
181                         throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
182                     }
183                 }
184                 finally {
185                     if (pointer != null) {
186                         buffer.ReleasePointer();
187                     }
188                 }
189             }
190
191             _offset = offset;
192             _buffer = buffer;
193             _length = length;
194             _capacity = length;
195             _access = access;
196             _isOpen = true;
197         }
198
199         [System.Security.SecurityCritical]  // auto-generated
200         [CLSCompliant(false)]
201         public unsafe UnmanagedMemoryStream(byte* pointer, long length)
202         {
203             Initialize(pointer, length, length, FileAccess.Read, false);
204         }
205
206         [System.Security.SecurityCritical]  // auto-generated
207         [CLSCompliant(false)]
208         public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access) 
209         {
210             Initialize(pointer, length, capacity, access, false);
211         }
212
213         // We must create one of these without doing a security check.  This
214         // class is created while security is trying to start up.  Plus, doing
215         // a Demand from Assembly.GetManifestResourceStream isn't useful.
216         [System.Security.SecurityCritical]  // auto-generated
217         internal unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) 
218         {
219             Initialize(pointer, length, capacity, access, skipSecurityCheck);
220         }
221
222         [System.Security.SecurityCritical]  // auto-generated
223         [CLSCompliant(false)]
224         protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access) 
225         {
226             Initialize(pointer, length, capacity, access, false);
227         }
228
229         [System.Security.SecurityCritical]  // auto-generated
230         internal unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) 
231         {
232             if (pointer == null)
233                 throw new ArgumentNullException("pointer");
234             if (length < 0 || capacity < 0)
235                 throw new ArgumentOutOfRangeException((length < 0) ? "length" : "capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
236             if (length > capacity)
237                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_LengthGreaterThanCapacity"));
238             Contract.EndContractBlock();
239             // Check for wraparound.
240             if (((byte*) ((long)pointer + capacity)) < pointer)
241                 throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
242             if (access < FileAccess.Read || access > FileAccess.ReadWrite)
243                 throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
244             if (_isOpen)
245                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice"));
246
247 #if MONO_FEATURE_CAS
248             if (!skipSecurityCheck)
249 #pragma warning disable 618
250                 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
251 #pragma warning restore 618
252 #endif
253             _mem = pointer;
254             _offset = 0;
255             _length = length;
256             _capacity = capacity;
257             _access = access;
258             _isOpen = true;
259         }
260
261         public override bool CanRead {
262             [Pure]
263             get { return _isOpen && (_access & FileAccess.Read) != 0; }
264         }
265
266         public override bool CanSeek {
267             [Pure]
268             get { return _isOpen; }
269         }
270
271         public override bool CanWrite {
272             [Pure]
273             get { return _isOpen && (_access & FileAccess.Write) != 0; }
274         }
275
276         [System.Security.SecuritySafeCritical]  // auto-generated
277         protected override void Dispose(bool disposing)
278         {
279             _isOpen = false;
280             unsafe { _mem = null; }
281
282             // Stream allocates WaitHandles for async calls. So for correctness 
283             // call base.Dispose(disposing) for better perf, avoiding waiting
284             // for the finalizers to run on those types.
285             base.Dispose(disposing);
286         }
287
288         public override void Flush() {
289             if (!_isOpen) __Error.StreamIsClosed();
290         }
291         
292 #if !FEATURE_PAL && FEATURE_ASYNC_IO || MONO
293         [HostProtection(ExternalThreading=true)] 
294         [ComVisible(false)] 
295         public override Task FlushAsync(CancellationToken cancellationToken) { 
296         
297             if (cancellationToken.IsCancellationRequested) 
298                 return Task.FromCancellation(cancellationToken); 
299
300             try { 
301             
302                 Flush(); 
303                 return Task.CompletedTask; 
304                 
305             } catch(Exception ex) { 
306             
307                 return Task.FromException(ex); 
308             } 
309       } 
310 #endif  // !FEATURE_PAL && FEATURE_ASYNC_IO 
311
312
313         public override long Length {
314             get {
315                 if (!_isOpen) __Error.StreamIsClosed();
316                 return Interlocked.Read(ref _length);
317             }
318         }
319
320         public long Capacity {
321             get {
322                 if (!_isOpen) __Error.StreamIsClosed();
323                 return _capacity;
324             }
325         }
326
327         public override long Position {
328             get { 
329                 if (!CanSeek) __Error.StreamIsClosed();
330                 Contract.EndContractBlock();
331                 return Interlocked.Read(ref _position);
332             }
333             [System.Security.SecuritySafeCritical]  // auto-generated
334             set {
335                 if (value < 0)
336                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
337                 Contract.EndContractBlock();
338                 if (!CanSeek) __Error.StreamIsClosed();
339                 
340 #if WIN32
341                 unsafe {
342                     // On 32 bit machines, ensure we don't wrap around.
343                     if (value > (long) Int32.MaxValue || _mem + value < _mem)
344                         throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
345                 }
346 #endif
347                 Interlocked.Exchange(ref _position, value);
348             }
349         }
350
351         [CLSCompliant(false)]
352         public unsafe byte* PositionPointer {
353             [System.Security.SecurityCritical]  // auto-generated_required
354             get {
355                 if (_buffer != null) {
356                     throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer"));
357                 }
358
359                 // Use a temp to avoid a race
360                 long pos = Interlocked.Read(ref _position);
361                 if (pos > _capacity)
362                     throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_UMSPosition"));
363                 byte * ptr = _mem + pos;
364                 if (!_isOpen) __Error.StreamIsClosed();
365                 return ptr;
366             }
367             [System.Security.SecurityCritical]  // auto-generated_required
368             set {
369                 if (_buffer != null)
370                     throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer"));
371                 if (!_isOpen) __Error.StreamIsClosed();
372
373                 // Note: subtracting pointers returns an Int64.  Working around
374                 // to avoid hitting compiler warning CS0652 on this line. 
375                 if (new IntPtr(value - _mem).ToInt64() > UnmanagedMemStreamMaxLength)
376                     throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength"));
377                 if (value < _mem)
378                     throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
379
380                 Interlocked.Exchange(ref _position, value - _mem);
381             }
382         }
383
384         internal unsafe byte* Pointer {
385             [System.Security.SecurityCritical]  // auto-generated
386             get {
387                 if (_buffer != null)
388                     throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer"));
389
390                 return _mem;
391             }
392         }
393         
394         [System.Security.SecuritySafeCritical]  // auto-generated
395         public override int Read([In, Out] byte[] buffer, int offset, int count) {
396             if (buffer==null)
397                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
398             if (offset < 0)
399                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
400             if (count < 0)
401                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
402             if (buffer.Length - offset < count)
403                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
404             Contract.EndContractBlock();  // Keep this in [....] with contract validation in ReadAsync
405
406             if (!_isOpen) __Error.StreamIsClosed();
407             if (!CanRead) __Error.ReadNotSupported();
408
409             // Use a local variable to avoid a race where another thread 
410             // changes our position after we decide we can read some bytes.
411             long pos = Interlocked.Read(ref _position);
412             long len = Interlocked.Read(ref _length);
413             long n = len - pos;
414             if (n > count)
415                 n = count;
416             if (n <= 0)
417                 return 0;
418
419             int nInt = (int) n; // Safe because n <= count, which is an Int32
420             if (nInt < 0)
421                 nInt = 0;  // _position could be beyond EOF
422             Contract.Assert(pos + nInt >= 0, "_position + n >= 0");  // len is less than 2^63 -1.
423
424             if (_buffer != null) {
425                 unsafe {
426                     byte* pointer = null;
427                     RuntimeHelpers.PrepareConstrainedRegions();
428                     try {
429                         _buffer.AcquirePointer(ref pointer);
430                         Buffer.Memcpy(buffer, offset, pointer + pos + _offset, 0, nInt);
431                     }
432                     finally {
433                         if (pointer != null) {
434                             _buffer.ReleasePointer();
435                         }
436                     }
437                 }
438             }
439             else {
440                 unsafe {
441                     Buffer.Memcpy(buffer, offset, _mem + pos, 0, nInt);
442                 }
443             }
444             Interlocked.Exchange(ref _position, pos + n);
445             return nInt;
446         }
447         
448 #if !FEATURE_PAL && FEATURE_ASYNC_IO || MONO
449         [HostProtection(ExternalThreading = true)]
450         [ComVisible(false)]
451         public override Task<Int32> ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) {        
452             if (buffer==null)
453                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
454             if (offset < 0)
455                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
456             if (count < 0)
457                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
458             if (buffer.Length - offset < count)
459                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
460             Contract.EndContractBlock();  // contract validation copied from Read(...) 
461       
462             if (cancellationToken.IsCancellationRequested)  
463                 return Task.FromCancellation<Int32>(cancellationToken); 
464         
465             try { 
466             
467                 Int32 n = Read(buffer, offset, count); 
468                 Task<Int32> t = _lastReadTask;
469                 return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<Int32>(n)); 
470                 
471             } catch (Exception ex) { 
472             
473                 Contract.Assert(! (ex is OperationCanceledException));
474                 return Task.FromException<Int32>(ex); 
475             } 
476         } 
477 #endif  // !FEATURE_PAL && FEATURE_ASYNC_IO 
478
479         [System.Security.SecuritySafeCritical]  // auto-generated
480         public override int ReadByte() {
481             if (!_isOpen) __Error.StreamIsClosed();
482             if (!CanRead) __Error.ReadNotSupported();
483
484             long pos = Interlocked.Read(ref _position);  // Use a local to avoid a race condition
485             long len = Interlocked.Read(ref _length);
486             if (pos >= len)
487                 return -1;
488             Interlocked.Exchange(ref _position, pos + 1);
489             int result;
490             if (_buffer != null) {
491                 unsafe {
492                     byte* pointer = null;
493                     RuntimeHelpers.PrepareConstrainedRegions();
494                     try {
495                         _buffer.AcquirePointer(ref pointer);
496                         result = *(pointer + pos + _offset);
497                     }
498                     finally {
499                         if (pointer != null) {
500                             _buffer.ReleasePointer();
501                         }
502                     }
503                 }
504             }
505             else {
506                 unsafe {
507                     result = _mem[pos];
508                 }
509             }
510             return result;
511         }
512
513         public override long Seek(long offset, SeekOrigin loc) {
514             if (!_isOpen) __Error.StreamIsClosed();
515             if (offset > UnmanagedMemStreamMaxLength)
516                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength"));
517             switch(loc) {
518             case SeekOrigin.Begin:
519                 if (offset < 0)
520                     throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
521                 Interlocked.Exchange(ref _position, offset);
522                 break;
523                 
524             case SeekOrigin.Current:
525                 long pos = Interlocked.Read(ref _position);
526                 if (offset + pos < 0)
527                     throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
528                 Interlocked.Exchange(ref _position, offset + pos);
529                 break;
530                 
531             case SeekOrigin.End:
532                 long len = Interlocked.Read(ref _length);
533                 if (len + offset < 0)
534                     throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
535                 Interlocked.Exchange(ref _position, len + offset);
536                 break;
537                 
538             default:
539                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin"));
540             }
541
542             long finalPos = Interlocked.Read(ref _position);
543             Contract.Assert(finalPos >= 0, "_position >= 0");
544             return finalPos;
545         }
546
547         [System.Security.SecuritySafeCritical]  // auto-generated
548         public override void SetLength(long value) {
549             if (value < 0)
550                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
551             Contract.EndContractBlock();
552             if (_buffer != null)
553                 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer"));
554             if (!_isOpen) __Error.StreamIsClosed();
555             if (!CanWrite) __Error.WriteNotSupported();
556
557             if (value > _capacity)
558                 throw new IOException(Environment.GetResourceString("IO.IO_FixedCapacity"));
559
560             long pos = Interlocked.Read(ref _position);
561             long len = Interlocked.Read(ref _length);
562             if (value > len) {
563                 unsafe {
564                     Buffer.ZeroMemory(_mem+len, value-len);
565                 }
566             }
567             Interlocked.Exchange(ref _length, value);
568             if (pos > value) {
569                 Interlocked.Exchange(ref _position, value);
570             } 
571         }
572
573         [System.Security.SecuritySafeCritical]  // auto-generated
574         public override void Write(byte[] buffer, int offset, int count) {
575             if (buffer==null)
576                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
577             if (offset < 0)
578                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
579             if (count < 0)
580                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
581             if (buffer.Length - offset < count)
582                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
583             Contract.EndContractBlock();  // Keep contract validation in [....] with WriteAsync(..)
584
585             if (!_isOpen) __Error.StreamIsClosed();
586             if (!CanWrite) __Error.WriteNotSupported();
587
588             long pos = Interlocked.Read(ref _position);  // Use a local to avoid a race condition
589             long len = Interlocked.Read(ref _length);
590             long n = pos + count;
591             // Check for overflow
592             if (n < 0)
593                 throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
594
595             if (n > _capacity) {
596                 throw new NotSupportedException(Environment.GetResourceString("IO.IO_FixedCapacity"));
597             }
598
599             if (_buffer == null) {
600                 // Check to see whether we are now expanding the stream and must 
601                 // zero any memory in the middle.
602                 if (pos > len) {
603                     unsafe {
604                         Buffer.ZeroMemory(_mem+len, pos-len);
605                     }
606                 }
607
608                 // set length after zeroing memory to avoid race condition of accessing unzeroed memory
609                 if (n > len) {
610                     Interlocked.Exchange(ref _length, n);
611                 }
612             }
613
614             if (_buffer != null) {
615
616                 long bytesLeft = _capacity - pos;
617                 if (bytesLeft < count) {
618                     throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall"));
619                 }
620
621                 unsafe {
622                     byte* pointer = null;
623                     RuntimeHelpers.PrepareConstrainedRegions();
624                     try {
625                         _buffer.AcquirePointer(ref pointer);
626                         Buffer.Memcpy(pointer + pos + _offset, 0, buffer, offset, count);
627                     }
628                     finally {
629                         if (pointer != null) {
630                             _buffer.ReleasePointer();
631                         }
632                     }
633                 }
634             }
635             else {
636                 unsafe {
637                     Buffer.Memcpy(_mem + pos, 0, buffer, offset, count);
638                 }
639             }
640             Interlocked.Exchange(ref _position, n);
641             return;
642         }
643         
644 #if !FEATURE_PAL && FEATURE_ASYNC_IO || MONO 
645         [HostProtection(ExternalThreading = true)] 
646         [ComVisible(false)] 
647         public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) { 
648         
649             if (buffer==null)
650                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
651             if (offset < 0)
652                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
653             if (count < 0)
654                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
655             if (buffer.Length - offset < count)
656                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
657             Contract.EndContractBlock();  // contract validation copied from Write(..) 
658                             
659             if (cancellationToken.IsCancellationRequested)  
660                 return Task.FromCancellation(cancellationToken); 
661          
662             try { 
663                        
664                 Write(buffer, offset, count); 
665                 return Task.CompletedTask; 
666                 
667             } catch (Exception ex) { 
668             
669                 Contract.Assert(! (ex is OperationCanceledException));
670                 return Task.FromException<Int32>(ex); 
671             } 
672         } 
673 #endif  // !FEATURE_PAL && FEATURE_ASYNC_IO 
674
675
676         [System.Security.SecuritySafeCritical]  // auto-generated
677         public override void WriteByte(byte value) {
678             if (!_isOpen) __Error.StreamIsClosed();
679             if (!CanWrite) __Error.WriteNotSupported();
680
681             long pos = Interlocked.Read(ref _position);  // Use a local to avoid a race condition
682             long len = Interlocked.Read(ref _length);
683             long n = pos + 1;
684             if (pos >= len) {
685                 // Check for overflow
686                 if (n < 0)
687                     throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
688                 
689                 if (n > _capacity)
690                     throw new NotSupportedException(Environment.GetResourceString("IO.IO_FixedCapacity"));
691
692                 // Check to see whether we are now expanding the stream and must 
693                 // zero any memory in the middle.
694                 // don't do if created from SafeBuffer
695                 if (_buffer == null) {                             
696                     if (pos > len) {
697                         unsafe {
698                             Buffer.ZeroMemory(_mem+len, pos-len);
699                         }
700                     }
701
702                     // set length after zeroing memory to avoid race condition of accessing unzeroed memory
703                     Interlocked.Exchange(ref _length, n);
704                 }
705             }
706
707             if (_buffer != null) {
708                 unsafe {
709                     byte* pointer = null;
710                     RuntimeHelpers.PrepareConstrainedRegions();
711                     try {
712                         _buffer.AcquirePointer(ref pointer);
713                         *(pointer + pos + _offset) = value;
714                     }
715                     finally {
716                         if (pointer != null) {
717                             _buffer.ReleasePointer();
718                         }
719                     }
720                 }
721             }
722             else {
723                 unsafe {
724             _mem[pos] = value;
725                 }
726             }
727             Interlocked.Exchange(ref _position, n);
728         }
729     }
730 }