diff options
Diffstat (limited to 'src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs')
-rw-r--r-- | src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs | 731 |
1 files changed, 0 insertions, 731 deletions
diff --git a/src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs b/src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs deleted file mode 100644 index f21fe47371..0000000000 --- a/src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs +++ /dev/null @@ -1,731 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** -** Purpose: Create a stream over unmanaged memory, mostly -** useful for memory-mapped files. -** -** -===========================================================*/ - -using System; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; -using System.Threading; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Threading.Tasks; - - -namespace System.IO -{ - /* - * This class is used to access a contiguous block of memory, likely outside - * the GC heap (or pinned in place in the GC heap, but a MemoryStream may - * make more sense in those cases). It's great if you have a pointer and - * a length for a section of memory mapped in by someone else and you don't - * want to copy this into the GC heap. UnmanagedMemoryStream assumes these - * two things: - * - * 1) All the memory in the specified block is readable or writable, - * depending on the values you pass to the constructor. - * 2) The lifetime of the block of memory is at least as long as the lifetime - * of the UnmanagedMemoryStream. - * 3) You clean up the memory when appropriate. The UnmanagedMemoryStream - * currently will do NOTHING to free this memory. - * 4) All calls to Write and WriteByte may not be threadsafe currently. - * - * It may become necessary to add in some sort of - * DeallocationMode enum, specifying whether we unmap a section of memory, - * call free, run a user-provided delegate to free the memory, etc. - * We'll suggest user write a subclass of UnmanagedMemoryStream that uses - * a SafeHandle subclass to hold onto the memory. - * Check for problems when using this in the negative parts of a - * process's address space. We may need to use unsigned longs internally - * and change the overflow detection logic. - * - * -----SECURITY MODEL AND SILVERLIGHT----- - * A few key notes about exposing UMS in silverlight: - * 1. No ctors are exposed to transparent code. This version of UMS only - * supports byte* (not SafeBuffer). Therefore, framework code can create - * a UMS and hand it to transparent code. Transparent code can use most - * operations on a UMS, but not operations that directly expose a - * pointer. - * - * 2. Scope of "unsafe" and non-CLS compliant operations reduced: The - * Whidbey version of this class has CLSCompliant(false) at the class - * level and unsafe modifiers at the method level. These were reduced to - * only where the unsafe operation is performed -- i.e. immediately - * around the pointer manipulation. Note that this brings UMS in line - * with recent changes in pu/clr to support SafeBuffer. - * - * 3. Currently, the only caller that creates a UMS is ResourceManager, - * which creates read-only UMSs, and therefore operations that can - * change the length will throw because write isn't supported. A - * conservative option would be to formalize the concept that _only_ - * read-only UMSs can be creates, and enforce this by making WriteX and - * SetLength SecurityCritical. However, this is a violation of - * security inheritance rules, so we must keep these safe. The - * following notes make this acceptable for future use. - * a. a race condition in WriteX that could have allowed a thread to - * read from unzeroed memory was fixed - * b. memory region cannot be expanded beyond _capacity; in other - * words, a UMS creator is saying a writable UMS is safe to - * write to anywhere in the memory range up to _capacity, specified - * in the ctor. Even if the caller doesn't specify a capacity, then - * length is used as the capacity. - */ - public class UnmanagedMemoryStream : Stream - { - private const long UnmanagedMemStreamMaxLength = Int64.MaxValue; - - private SafeBuffer _buffer; - private unsafe byte* _mem; - private long _length; - private long _capacity; - private long _position; - private long _offset; - private FileAccess _access; - internal bool _isOpen; - [NonSerialized] - private Task<Int32> _lastReadTask; // The last successful task returned from ReadAsync - - - // Needed for subclasses that need to map a file, etc. - protected UnmanagedMemoryStream() - { - unsafe - { - _mem = null; - } - _isOpen = false; - } - - public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) - { - Initialize(buffer, offset, length, FileAccess.Read); - } - - public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) - { - Initialize(buffer, offset, length, access); - } - - protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (buffer.ByteLength < (ulong)(offset + length)) - { - throw new ArgumentException(SR.Argument_InvalidSafeBufferOffLen); - } - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - { - throw new ArgumentOutOfRangeException(nameof(access)); - } - Contract.EndContractBlock(); - - if (_isOpen) - { - throw new InvalidOperationException(SR.InvalidOperation_CalledTwice); - } - - // check for wraparound - unsafe - { - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - buffer.AcquirePointer(ref pointer); - if ((pointer + offset + length) < pointer) - { - throw new ArgumentException(SR.ArgumentOutOfRange_UnmanagedMemStreamWrapAround); - } - } - finally - { - if (pointer != null) - { - buffer.ReleasePointer(); - } - } - } - - _offset = offset; - _buffer = buffer; - _length = length; - _capacity = length; - _access = access; - _isOpen = true; - } - - [CLSCompliant(false)] - public unsafe UnmanagedMemoryStream(byte* pointer, long length) - { - Initialize(pointer, length, length, FileAccess.Read); - } - - [CLSCompliant(false)] - public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access) - { - Initialize(pointer, length, capacity, access); - } - - [CLSCompliant(false)] - protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access) - { - if (pointer == null) - throw new ArgumentNullException(nameof(pointer)); - if (length < 0 || capacity < 0) - throw new ArgumentOutOfRangeException((length < 0) ? nameof(length) : nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); - if (length > capacity) - throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_LengthGreaterThanCapacity); - Contract.EndContractBlock(); - // Check for wraparound. - if (((byte*)((long)pointer + capacity)) < pointer) - throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_UnmanagedMemStreamWrapAround); - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); - if (_isOpen) - throw new InvalidOperationException(SR.InvalidOperation_CalledTwice); - - _mem = pointer; - _offset = 0; - _length = length; - _capacity = capacity; - _access = access; - _isOpen = true; - } - - public override bool CanRead - { - [Pure] - get { return _isOpen && (_access & FileAccess.Read) != 0; } - } - - public override bool CanSeek - { - [Pure] - get { return _isOpen; } - } - - public override bool CanWrite - { - [Pure] - get { return _isOpen && (_access & FileAccess.Write) != 0; } - } - - protected override void Dispose(bool disposing) - { - _isOpen = false; - unsafe { _mem = null; } - - // Stream allocates WaitHandles for async calls. So for correctness - // call base.Dispose(disposing) for better perf, avoiding waiting - // for the finalizers to run on those types. - base.Dispose(disposing); - } - - public override void Flush() - { - if (!_isOpen) __Error.StreamIsClosed(); - } - - public override Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - try - { - Flush(); - return Task.CompletedTask; - } - catch (Exception ex) - { - return Task.FromException(ex); - } - } - - - public override long Length - { - get - { - if (!_isOpen) __Error.StreamIsClosed(); - return Interlocked.Read(ref _length); - } - } - - public long Capacity - { - get - { - if (!_isOpen) __Error.StreamIsClosed(); - return _capacity; - } - } - - public override long Position - { - get - { - if (!CanSeek) __Error.StreamIsClosed(); - Contract.EndContractBlock(); - return Interlocked.Read(ref _position); - } - set - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); - if (!CanSeek) __Error.StreamIsClosed(); - -#if !BIT64 - unsafe { - // On 32 bit machines, ensure we don't wrap around. - if (value > (long) Int32.MaxValue || _mem + value < _mem) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); - } -#endif - Interlocked.Exchange(ref _position, value); - } - } - - [CLSCompliant(false)] - public unsafe byte* PositionPointer - { - get - { - if (_buffer != null) - { - throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); - } - - // Use a temp to avoid a race - long pos = Interlocked.Read(ref _position); - if (pos > _capacity) - throw new IndexOutOfRangeException(SR.IndexOutOfRange_UMSPosition); - byte* ptr = _mem + pos; - if (!_isOpen) __Error.StreamIsClosed(); - return ptr; - } - set - { - if (_buffer != null) - throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); - if (!_isOpen) __Error.StreamIsClosed(); - - // Note: subtracting pointers returns an Int64. Working around - // to avoid hitting compiler warning CS0652 on this line. - if (new IntPtr(value - _mem).ToInt64() > UnmanagedMemStreamMaxLength) - throw new ArgumentOutOfRangeException("offset", SR.ArgumentOutOfRange_UnmanagedMemStreamLength); - if (value < _mem) - throw new IOException(SR.IO_SeekBeforeBegin); - - Interlocked.Exchange(ref _position, value - _mem); - } - } - - internal unsafe byte* Pointer - { - get - { - if (_buffer != null) - throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); - - return _mem; - } - } - - public override int Read([In, Out] byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - if (buffer.Length - offset < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync - - if (!_isOpen) __Error.StreamIsClosed(); - if (!CanRead) __Error.ReadNotSupported(); - - // Use a local variable to avoid a race where another thread - // changes our position after we decide we can read some bytes. - long pos = Interlocked.Read(ref _position); - long len = Interlocked.Read(ref _length); - long n = len - pos; - if (n > count) - n = count; - if (n <= 0) - return 0; - - int nInt = (int)n; // Safe because n <= count, which is an Int32 - if (nInt < 0) - return 0; // _position could be beyond EOF - Debug.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1. - - unsafe - { - fixed (byte* pBuffer = buffer) - { - if (_buffer != null) - { - byte* pointer = null; - - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(pBuffer + offset, pointer + pos + _offset, nInt); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - else - { - Buffer.Memcpy(pBuffer + offset, _mem + pos, nInt); - } - } - } - Interlocked.Exchange(ref _position, pos + n); - return nInt; - } - - public override Task<Int32> ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - if (buffer.Length - offset < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // contract validation copied from Read(...) - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled<Int32>(cancellationToken); - - try - { - Int32 n = Read(buffer, offset, count); - Task<Int32> t = _lastReadTask; - return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<Int32>(n)); - } - catch (Exception ex) - { - Debug.Assert(!(ex is OperationCanceledException)); - return Task.FromException<Int32>(ex); - } - } - - public override int ReadByte() - { - if (!_isOpen) __Error.StreamIsClosed(); - if (!CanRead) __Error.ReadNotSupported(); - - long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition - long len = Interlocked.Read(ref _length); - if (pos >= len) - return -1; - Interlocked.Exchange(ref _position, pos + 1); - int result; - if (_buffer != null) - { - unsafe - { - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - _buffer.AcquirePointer(ref pointer); - result = *(pointer + pos + _offset); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - else - { - unsafe - { - result = _mem[pos]; - } - } - return result; - } - - public override long Seek(long offset, SeekOrigin loc) - { - if (!_isOpen) __Error.StreamIsClosed(); - if (offset > UnmanagedMemStreamMaxLength) - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_UnmanagedMemStreamLength); - switch (loc) - { - case SeekOrigin.Begin: - if (offset < 0) - throw new IOException(SR.IO_SeekBeforeBegin); - Interlocked.Exchange(ref _position, offset); - break; - - case SeekOrigin.Current: - long pos = Interlocked.Read(ref _position); - if (offset + pos < 0) - throw new IOException(SR.IO_SeekBeforeBegin); - Interlocked.Exchange(ref _position, offset + pos); - break; - - case SeekOrigin.End: - long len = Interlocked.Read(ref _length); - if (len + offset < 0) - throw new IOException(SR.IO_SeekBeforeBegin); - Interlocked.Exchange(ref _position, len + offset); - break; - - default: - throw new ArgumentException(SR.Argument_InvalidSeekOrigin); - } - - long finalPos = Interlocked.Read(ref _position); - Debug.Assert(finalPos >= 0, "_position >= 0"); - return finalPos; - } - - public override void SetLength(long value) - { - if (value < 0) - throw new ArgumentOutOfRangeException("length", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); - if (_buffer != null) - throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); - if (!_isOpen) __Error.StreamIsClosed(); - if (!CanWrite) __Error.WriteNotSupported(); - - if (value > _capacity) - throw new IOException(SR.IO_FixedCapacity); - - long pos = Interlocked.Read(ref _position); - long len = Interlocked.Read(ref _length); - if (value > len) - { - unsafe - { - Buffer.ZeroMemory(_mem + len, value - len); - } - } - Interlocked.Exchange(ref _length, value); - if (pos > value) - { - Interlocked.Exchange(ref _position, value); - } - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - if (buffer.Length - offset < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..) - - if (!_isOpen) __Error.StreamIsClosed(); - if (!CanWrite) __Error.WriteNotSupported(); - - long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition - long len = Interlocked.Read(ref _length); - long n = pos + count; - // Check for overflow - if (n < 0) - throw new IOException(SR.IO_StreamTooLong); - - if (n > _capacity) - { - throw new NotSupportedException(SR.IO_FixedCapacity); - } - - if (_buffer == null) - { - // Check to see whether we are now expanding the stream and must - // zero any memory in the middle. - if (pos > len) - { - unsafe - { - Buffer.ZeroMemory(_mem + len, pos - len); - } - } - - // set length after zeroing memory to avoid race condition of accessing unzeroed memory - if (n > len) - { - Interlocked.Exchange(ref _length, n); - } - } - - unsafe - { - fixed (byte* pBuffer = buffer) - { - if (_buffer != null) - { - long bytesLeft = _capacity - pos; - if (bytesLeft < count) - { - throw new ArgumentException(SR.Arg_BufferTooSmall); - } - - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(pointer + pos + _offset, pBuffer + offset, count); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - else - { - Buffer.Memcpy(_mem + pos, pBuffer + offset, count); - } - } - } - Interlocked.Exchange(ref _position, n); - return; - } - - public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - if (buffer.Length - offset < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // contract validation copied from Write(..) - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - try - { - Write(buffer, offset, count); - return Task.CompletedTask; - } - catch (Exception ex) - { - Debug.Assert(!(ex is OperationCanceledException)); - return Task.FromException<Int32>(ex); - } - } - - - public override void WriteByte(byte value) - { - if (!_isOpen) __Error.StreamIsClosed(); - if (!CanWrite) __Error.WriteNotSupported(); - - long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition - long len = Interlocked.Read(ref _length); - long n = pos + 1; - if (pos >= len) - { - // Check for overflow - if (n < 0) - throw new IOException(SR.IO_StreamTooLong); - - if (n > _capacity) - throw new NotSupportedException(SR.IO_FixedCapacity); - - // Check to see whether we are now expanding the stream and must - // zero any memory in the middle. - // don't do if created from SafeBuffer - if (_buffer == null) - { - if (pos > len) - { - unsafe - { - Buffer.ZeroMemory(_mem + len, pos - len); - } - } - - // set length after zeroing memory to avoid race condition of accessing unzeroed memory - Interlocked.Exchange(ref _length, n); - } - } - - if (_buffer != null) - { - unsafe - { - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - _buffer.AcquirePointer(ref pointer); - *(pointer + pos + _offset) = value; - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - else - { - unsafe - { - _mem[pos] = value; - } - } - Interlocked.Exchange(ref _position, n); - } - } -} |