UnmanagedMemoryStream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / IO / UnmanagedMemoryStream.cs / 1305376 / UnmanagedMemoryStream.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
/*============================================================
** 
** Class:  UnmanagedMemoryStream 
**
** [....] 
**
** Purpose: Create a stream over unmanaged memory, mostly
**          useful for memory-mapped files.
** 
** Date:  October 20, 2000 (made public August 4, 2003)
** 
===========================================================*/ 
using System;
using System.Runtime; 
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Threading; 
using System.Diagnostics.Contracts;
 
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 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 writeable 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; 
 
        [System.Security.SecurityCritical /*auto-generated*/]
        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; 

        // Needed for subclasses that need to map a file, etc. 
        [System.Security.SecuritySafeCritical]  // auto-generated
        protected UnmanagedMemoryStream()
        {
            unsafe { 
                _mem = null;
            } 
            _isOpen = false; 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) {
            Initialize(buffer, offset, length, FileAccess.Read, false);
        } 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) { 
            Initialize(buffer, offset, length, access, false);
        } 

        // We must create one of these without doing a security check.  This
        // class is created while security is trying to start up.  Plus, doing
        // a Demand from Assembly.GetManifestResourceStream isn't useful. 
        [System.Security.SecurityCritical]  // auto-generated
        internal UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) { 
            Initialize(buffer, offset, length, access, skipSecurityCheck); 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) {
            Initialize(buffer, offset, length, access, false);
        } 

        [System.Security.SecurityCritical]  // auto-generated 
        internal void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) { 
            if (buffer == null) {
                throw new ArgumentNullException("buffer"); 
            }
            if (offset < 0) {
                throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            } 
            if (length < 0) {
                throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 
            } 
            if (buffer.ByteLength < (ulong)(offset + length)) {
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSafeBufferOffLen")); 
            }
            if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
                throw new ArgumentOutOfRangeException("access");
            } 
            Contract.EndContractBlock();
 
            if (_isOpen) { 
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice"));
            } 
            if (!skipSecurityCheck) {
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
            }
 
            // check for wraparound
            unsafe { 
                byte* pointer = null; 
                RuntimeHelpers.PrepareConstrainedRegions();
                try { 
                    buffer.AcquirePointer(ref pointer);
                    if ( (pointer + offset + length) < pointer) {
                        throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
                    } 
                }
                finally { 
                    if (pointer != null) { 
                        buffer.ReleasePointer();
                    } 
                }
            }

            _offset = offset; 
            _buffer = buffer;
            _length = length; 
            _capacity = length; 
            _access = access;
            _isOpen = true; 
        }

        [System.Security.SecurityCritical]  // auto-generated
        [CLSCompliant(false)] 
        public unsafe UnmanagedMemoryStream(byte* pointer, long length)
        { 
            Initialize(pointer, length, length, FileAccess.Read, false); 
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [CLSCompliant(false)]
        public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access)
        { 
            Initialize(pointer, length, capacity, access, false);
        } 
 
        // We must create one of these without doing a security check.  This
        // class is created while security is trying to start up.  Plus, doing 
        // a Demand from Assembly.GetManifestResourceStream isn't useful.
        [System.Security.SecurityCritical]  // auto-generated
        internal unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck)
        { 
            Initialize(pointer, length, capacity, access, skipSecurityCheck);
        } 
 
        [System.Security.SecurityCritical]  // auto-generated
        [CLSCompliant(false)] 
        protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access)
        {
            Initialize(pointer, length, capacity, access, false);
        } 

        [System.Security.SecurityCritical]  // auto-generated 
        internal unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) 
        {
            if (pointer == null) 
                throw new ArgumentNullException("pointer");
            if (length < 0 || capacity < 0)
                throw new ArgumentOutOfRangeException((length < 0) ? "length" : "capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (length > capacity) 
                throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_LengthGreaterThanCapacity"));
            Contract.EndContractBlock(); 
            // Check for wraparound. 
            if (((byte*) ((long)pointer + capacity)) < pointer)
                throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround")); 
            if (access < FileAccess.Read || access > FileAccess.ReadWrite)
                throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
            if (_isOpen)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice")); 

            if (!skipSecurityCheck) 
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); 

            _mem = pointer; 
            _offset = 0;
            _length = length;
            _capacity = capacity;
            _access = access; 
            _isOpen = true;
        } 
 
        public override bool CanRead {
            [Pure] 
#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
            get { return _isOpen && (_access & FileAccess.Read) != 0; } 
        }
 
        public override bool CanSeek { 
            [Pure]
#if !FEATURE_CORECLR 
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
            get { return _isOpen; }
        } 

        public override bool CanWrite { 
            [Pure] 
#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] 
#endif
            get { return _isOpen && (_access & FileAccess.Write) != 0; }
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        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 long Length {
#if !FEATURE_CORECLR 
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif 
            get { 
                if (!_isOpen) __Error.StreamIsClosed();
                return Interlocked.Read(ref _length); 
            }
        }

        public long Capacity { 
            get {
                if (!_isOpen) __Error.StreamIsClosed(); 
                return _capacity; 
            }
        } 

        public override long Position {
#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] 
#endif
            get { 
                if (!CanSeek) __Error.StreamIsClosed(); 
                Contract.EndContractBlock();
                return Interlocked.Read(ref _position); 
            }
            [System.Security.SecuritySafeCritical]  // auto-generated
            set {
                if (value < 0) 
                    throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
                Contract.EndContractBlock(); 
                if (!CanSeek) __Error.StreamIsClosed(); 

#if WIN32 
                unsafe {
                    // On 32 bit machines, ensure we don't wrap around.
                    if (value > (long) Int32.MaxValue || _mem + value < _mem)
                        throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); 
                }
#endif 
                Interlocked.Exchange(ref _position, value); 
            }
        } 

        [CLSCompliant(false)]
        public unsafe byte* PositionPointer {
            [System.Security.SecurityCritical]  // auto-generated_required 
            get {
                if (_buffer != null) { 
                    throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer")); 
                }
 
                // Use a temp to avoid a race
                long pos = Interlocked.Read(ref _position);
                if (pos > _capacity)
                    throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_UMSPosition")); 
                byte * ptr = _mem + pos;
                if (!_isOpen) __Error.StreamIsClosed(); 
                return ptr; 
            }
            [System.Security.SecurityCritical]  // auto-generated_required 
#if FEATURE_CORECLR  //Need the getter to be public and the setter to be internal: TrimSrc doesn't parse into property declarations
            internal
#endif //FEATURE_CORECLR
            set { 
                if (_buffer != null)
                    throw new NotSupportedException(Environment.GetResourceString("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", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength"));
                if (value < _mem) 
                    throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
 
                Interlocked.Exchange(ref _position, value - _mem); 
            }
        } 

        internal unsafe byte* Pointer {
            [System.Security.SecurityCritical]  // auto-generated
            get { 
                if (_buffer != null)
                    throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer")); 
 
                return _mem;
            } 
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        public override int Read([In, Out] byte[] buffer, int offset, int count) { 
            if (buffer==null)
                throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); 
            if (offset < 0) 
                throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (buffer.Length - offset < count)
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
            Contract.EndContractBlock(); 

            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)
                nInt = 0;  // _position could be beyond EOF
            Contract.Assert(pos + nInt >= 0, "_position + n >= 0");  // len is less than 2^63 -1. 

            if (_buffer != null) { 
                unsafe { 
                    byte* pointer = null;
                    RuntimeHelpers.PrepareConstrainedRegions(); 
                    try {
                        _buffer.AcquirePointer(ref pointer);
                        Buffer.memcpy(pointer + pos + _offset, 0, buffer, offset, nInt);
                    } 
                    finally {
                        if (pointer != null) { 
                            _buffer.ReleasePointer(); 
                        }
                    } 
                }
            }
            else {
                unsafe { 
                    Buffer.memcpy(_mem + pos, 0, buffer, offset, nInt);
                } 
            } 
            Interlocked.Exchange(ref _position, pos + n);
            return nInt; 
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        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("offset", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength")); 
            switch(loc) {
            case SeekOrigin.Begin: 
                if (offset < 0) 
                    throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
                Interlocked.Exchange(ref _position, offset); 
                break;

            case SeekOrigin.Current:
                long pos = Interlocked.Read(ref _position); 
                if (offset + pos < 0)
                    throw new IOException(Environment.GetResourceString("IO.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(Environment.GetResourceString("IO.IO_SeekBeforeBegin")); 
                Interlocked.Exchange(ref _position, len + offset);
                break; 
 
            default:
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin")); 
            }

            long finalPos = Interlocked.Read(ref _position);
            Contract.Assert(finalPos >= 0, "_position >= 0"); 
            return finalPos;
        } 
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public override void SetLength(long value) { 
            if (value < 0)
                throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            Contract.EndContractBlock();
            if (_buffer != null) 
                throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer"));
            if (!_isOpen) __Error.StreamIsClosed(); 
            if (!CanWrite) __Error.WriteNotSupported(); 

            if (value > _capacity) 
                throw new IOException(Environment.GetResourceString("IO.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);
            } 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated 
        public override void Write(byte[] buffer, int offset, int count) {
            if (buffer==null) 
                throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (buffer.Length - offset < count) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); 
            Contract.EndContractBlock();
 
            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(Environment.GetResourceString("IO.IO_StreamTooLong")); 

            if (n > _capacity) {
                throw new NotSupportedException(Environment.GetResourceString("IO.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);
                } 
            }

            if (_buffer != null) {
 
                long bytesLeft = _capacity - pos;
                if (bytesLeft < count) { 
                    throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall")); 
                }
 
                unsafe {
                    byte* pointer = null;
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try { 
                        _buffer.AcquirePointer(ref pointer);
                        Buffer.memcpy(buffer, offset, pointer + pos + _offset, 0, count); 
                    } 
                    finally {
                        if (pointer != null) { 
                            _buffer.ReleasePointer();
                        }
                    }
                } 
            }
            else { 
                unsafe { 
                    Buffer.memcpy(buffer, offset, _mem + pos, 0, count);
                } 
            }
            Interlocked.Exchange(ref _position, n);
            return;
        } 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        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(Environment.GetResourceString("IO.IO_StreamTooLong"));
 
                if (n > _capacity)
                    throw new NotSupportedException(Environment.GetResourceString("IO.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);
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
/*============================================================
** 
** Class:  UnmanagedMemoryStream 
**
** [....] 
**
** Purpose: Create a stream over unmanaged memory, mostly
**          useful for memory-mapped files.
** 
** Date:  October 20, 2000 (made public August 4, 2003)
** 
===========================================================*/ 
using System;
using System.Runtime; 
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Threading; 
using System.Diagnostics.Contracts;
 
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 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 writeable 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; 
 
        [System.Security.SecurityCritical /*auto-generated*/]
        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; 

        // Needed for subclasses that need to map a file, etc. 
        [System.Security.SecuritySafeCritical]  // auto-generated
        protected UnmanagedMemoryStream()
        {
            unsafe { 
                _mem = null;
            } 
            _isOpen = false; 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) {
            Initialize(buffer, offset, length, FileAccess.Read, false);
        } 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) { 
            Initialize(buffer, offset, length, access, false);
        } 

        // We must create one of these without doing a security check.  This
        // class is created while security is trying to start up.  Plus, doing
        // a Demand from Assembly.GetManifestResourceStream isn't useful. 
        [System.Security.SecurityCritical]  // auto-generated
        internal UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) { 
            Initialize(buffer, offset, length, access, skipSecurityCheck); 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) {
            Initialize(buffer, offset, length, access, false);
        } 

        [System.Security.SecurityCritical]  // auto-generated 
        internal void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) { 
            if (buffer == null) {
                throw new ArgumentNullException("buffer"); 
            }
            if (offset < 0) {
                throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            } 
            if (length < 0) {
                throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 
            } 
            if (buffer.ByteLength < (ulong)(offset + length)) {
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSafeBufferOffLen")); 
            }
            if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
                throw new ArgumentOutOfRangeException("access");
            } 
            Contract.EndContractBlock();
 
            if (_isOpen) { 
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice"));
            } 
            if (!skipSecurityCheck) {
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
            }
 
            // check for wraparound
            unsafe { 
                byte* pointer = null; 
                RuntimeHelpers.PrepareConstrainedRegions();
                try { 
                    buffer.AcquirePointer(ref pointer);
                    if ( (pointer + offset + length) < pointer) {
                        throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
                    } 
                }
                finally { 
                    if (pointer != null) { 
                        buffer.ReleasePointer();
                    } 
                }
            }

            _offset = offset; 
            _buffer = buffer;
            _length = length; 
            _capacity = length; 
            _access = access;
            _isOpen = true; 
        }

        [System.Security.SecurityCritical]  // auto-generated
        [CLSCompliant(false)] 
        public unsafe UnmanagedMemoryStream(byte* pointer, long length)
        { 
            Initialize(pointer, length, length, FileAccess.Read, false); 
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [CLSCompliant(false)]
        public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access)
        { 
            Initialize(pointer, length, capacity, access, false);
        } 
 
        // We must create one of these without doing a security check.  This
        // class is created while security is trying to start up.  Plus, doing 
        // a Demand from Assembly.GetManifestResourceStream isn't useful.
        [System.Security.SecurityCritical]  // auto-generated
        internal unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck)
        { 
            Initialize(pointer, length, capacity, access, skipSecurityCheck);
        } 
 
        [System.Security.SecurityCritical]  // auto-generated
        [CLSCompliant(false)] 
        protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access)
        {
            Initialize(pointer, length, capacity, access, false);
        } 

        [System.Security.SecurityCritical]  // auto-generated 
        internal unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) 
        {
            if (pointer == null) 
                throw new ArgumentNullException("pointer");
            if (length < 0 || capacity < 0)
                throw new ArgumentOutOfRangeException((length < 0) ? "length" : "capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (length > capacity) 
                throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_LengthGreaterThanCapacity"));
            Contract.EndContractBlock(); 
            // Check for wraparound. 
            if (((byte*) ((long)pointer + capacity)) < pointer)
                throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround")); 
            if (access < FileAccess.Read || access > FileAccess.ReadWrite)
                throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
            if (_isOpen)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice")); 

            if (!skipSecurityCheck) 
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); 

            _mem = pointer; 
            _offset = 0;
            _length = length;
            _capacity = capacity;
            _access = access; 
            _isOpen = true;
        } 
 
        public override bool CanRead {
            [Pure] 
#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
            get { return _isOpen && (_access & FileAccess.Read) != 0; } 
        }
 
        public override bool CanSeek { 
            [Pure]
#if !FEATURE_CORECLR 
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
            get { return _isOpen; }
        } 

        public override bool CanWrite { 
            [Pure] 
#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] 
#endif
            get { return _isOpen && (_access & FileAccess.Write) != 0; }
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        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 long Length {
#if !FEATURE_CORECLR 
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif 
            get { 
                if (!_isOpen) __Error.StreamIsClosed();
                return Interlocked.Read(ref _length); 
            }
        }

        public long Capacity { 
            get {
                if (!_isOpen) __Error.StreamIsClosed(); 
                return _capacity; 
            }
        } 

        public override long Position {
#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] 
#endif
            get { 
                if (!CanSeek) __Error.StreamIsClosed(); 
                Contract.EndContractBlock();
                return Interlocked.Read(ref _position); 
            }
            [System.Security.SecuritySafeCritical]  // auto-generated
            set {
                if (value < 0) 
                    throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
                Contract.EndContractBlock(); 
                if (!CanSeek) __Error.StreamIsClosed(); 

#if WIN32 
                unsafe {
                    // On 32 bit machines, ensure we don't wrap around.
                    if (value > (long) Int32.MaxValue || _mem + value < _mem)
                        throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); 
                }
#endif 
                Interlocked.Exchange(ref _position, value); 
            }
        } 

        [CLSCompliant(false)]
        public unsafe byte* PositionPointer {
            [System.Security.SecurityCritical]  // auto-generated_required 
            get {
                if (_buffer != null) { 
                    throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer")); 
                }
 
                // Use a temp to avoid a race
                long pos = Interlocked.Read(ref _position);
                if (pos > _capacity)
                    throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_UMSPosition")); 
                byte * ptr = _mem + pos;
                if (!_isOpen) __Error.StreamIsClosed(); 
                return ptr; 
            }
            [System.Security.SecurityCritical]  // auto-generated_required 
#if FEATURE_CORECLR  //Need the getter to be public and the setter to be internal: TrimSrc doesn't parse into property declarations
            internal
#endif //FEATURE_CORECLR
            set { 
                if (_buffer != null)
                    throw new NotSupportedException(Environment.GetResourceString("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", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength"));
                if (value < _mem) 
                    throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
 
                Interlocked.Exchange(ref _position, value - _mem); 
            }
        } 

        internal unsafe byte* Pointer {
            [System.Security.SecurityCritical]  // auto-generated
            get { 
                if (_buffer != null)
                    throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer")); 
 
                return _mem;
            } 
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        public override int Read([In, Out] byte[] buffer, int offset, int count) { 
            if (buffer==null)
                throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); 
            if (offset < 0) 
                throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (buffer.Length - offset < count)
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
            Contract.EndContractBlock(); 

            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)
                nInt = 0;  // _position could be beyond EOF
            Contract.Assert(pos + nInt >= 0, "_position + n >= 0");  // len is less than 2^63 -1. 

            if (_buffer != null) { 
                unsafe { 
                    byte* pointer = null;
                    RuntimeHelpers.PrepareConstrainedRegions(); 
                    try {
                        _buffer.AcquirePointer(ref pointer);
                        Buffer.memcpy(pointer + pos + _offset, 0, buffer, offset, nInt);
                    } 
                    finally {
                        if (pointer != null) { 
                            _buffer.ReleasePointer(); 
                        }
                    } 
                }
            }
            else {
                unsafe { 
                    Buffer.memcpy(_mem + pos, 0, buffer, offset, nInt);
                } 
            } 
            Interlocked.Exchange(ref _position, pos + n);
            return nInt; 
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        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("offset", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength")); 
            switch(loc) {
            case SeekOrigin.Begin: 
                if (offset < 0) 
                    throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
                Interlocked.Exchange(ref _position, offset); 
                break;

            case SeekOrigin.Current:
                long pos = Interlocked.Read(ref _position); 
                if (offset + pos < 0)
                    throw new IOException(Environment.GetResourceString("IO.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(Environment.GetResourceString("IO.IO_SeekBeforeBegin")); 
                Interlocked.Exchange(ref _position, len + offset);
                break; 
 
            default:
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin")); 
            }

            long finalPos = Interlocked.Read(ref _position);
            Contract.Assert(finalPos >= 0, "_position >= 0"); 
            return finalPos;
        } 
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public override void SetLength(long value) { 
            if (value < 0)
                throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            Contract.EndContractBlock();
            if (_buffer != null) 
                throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer"));
            if (!_isOpen) __Error.StreamIsClosed(); 
            if (!CanWrite) __Error.WriteNotSupported(); 

            if (value > _capacity) 
                throw new IOException(Environment.GetResourceString("IO.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);
            } 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated 
        public override void Write(byte[] buffer, int offset, int count) {
            if (buffer==null) 
                throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (buffer.Length - offset < count) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); 
            Contract.EndContractBlock();
 
            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(Environment.GetResourceString("IO.IO_StreamTooLong")); 

            if (n > _capacity) {
                throw new NotSupportedException(Environment.GetResourceString("IO.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);
                } 
            }

            if (_buffer != null) {
 
                long bytesLeft = _capacity - pos;
                if (bytesLeft < count) { 
                    throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall")); 
                }
 
                unsafe {
                    byte* pointer = null;
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try { 
                        _buffer.AcquirePointer(ref pointer);
                        Buffer.memcpy(buffer, offset, pointer + pos + _offset, 0, count); 
                    } 
                    finally {
                        if (pointer != null) { 
                            _buffer.ReleasePointer();
                        }
                    }
                } 
            }
            else { 
                unsafe { 
                    Buffer.memcpy(buffer, offset, _mem + pos, 0, count);
                } 
            }
            Interlocked.Exchange(ref _position, n);
            return;
        } 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        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(Environment.GetResourceString("IO.IO_StreamTooLong"));
 
                if (n > _capacity)
                    throw new NotSupportedException(Environment.GetResourceString("IO.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);
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK