SafePointer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / clr / src / BCL / System / Runtime / InteropServices / SafePointer.cs / 1 / SafePointer.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
/*============================================================
** 
** Purpose: Unsafe code that uses pointers should use 
** SafePointer to fix subtle lifetime problems with the
** underlying resource. 
**
===========================================================*/

// Design points: 
// *) Avoid handle-recycling problems (including ones triggered via
// resurrection attacks) for all accesses via pointers.  This requires tying 
// together the lifetime of the unmanaged resource with the code that reads 
// from that resource, in a package that uses synchronization to enforce
// the correct semantics during finalization.  We're using SafeHandle's 
// ref count as a gate on whether the pointer can be dereferenced because that
// controls the lifetime of the resource.
//
// *) Keep the penalties for using this class small, both in terms of space 
// and time.  Having multiple threads reading from a memory mapped file
// will already require 2 additional interlocked operations.  If we add in 
// a "current position" concept, that requires additional space in memory and 
// synchronization.  Since the position in memory is often (but not always)
// something that can be stored on the stack, we can save some memory by 
// excluding it from this object.  However, avoiding the need for
// synchronization is a more significant win.  This design allows multiple
// threads to read and write memory simultaneously without locks (as long as
// you don't write to a region of memory that overlaps with what another 
// thread is accessing).
// 
// *) Space-wise, we use the following memory, including SafeHandle's fields: 
// Object Header  MT*  handle  int bool bool <2 pad bytes> length
// On 32 bit platforms: 24 bytes.  On 64 bit platforms: 40 bytes. 
// (We can safe 4 bytes on x86 only by shrinking SafeHandle)
//
// *) Wrapping a SafeHandle would have been a nice solution, but without an
// ordering between critical finalizable objects, it would have required 
// changes to each SafeHandle subclass to opt in to being usable from a
// SafePointer (or some clever exposure of SafeHandle's state fields and a 
// way of forcing ReleaseHandle to run even after the SafeHandle has been 
// finalized with a ref count > 1).  We can use less memory and create fewer
// objects by simply inserting a SafePointer into the class hierarchy. 
//
// *) In an ideal world, we could get marshaling support for SafePointer that
// would allow us to annotate a P/Invoke declaration, saying this parameter
// specifies the length of the buffer, and the units of that length are X. 
// P/Invoke would then pass that size parameter to SafePointer.
//     [DllImport(...)] 
//     static extern SafeMemoryHandle AllocCharBuffer(int numChars); 
// If we could put an attribute on the SafeMemoryHandle saying numChars is
// the element length, and it must be multiplied by 2 to get to the byte 
// length, we can simplify the usage model for SafePointer.
//
// *) This class could benefit from a constraint saying T is a value type
// containing no GC references. 

// Implementation notes: 
// *) The Initialize method must be called before you use any instance of 
// a SafePointer.  To avoid ----s when storing SafePointers in statics,
// you either need to take a lock when publishing the SafePointer, or you 
// need to create a local, initialize the SafePointer, then assign to the
// static variable (perhaps using Interlocked.CompareExchange).  Of course,
// assignments in a static class constructor are under a lock implicitly.
 

using System; 
using System.Security.Permissions; 
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices; 
using System.Runtime.ConstrainedExecution;
using System.Runtime.Versioning;
using Microsoft.Win32.SafeHandles;
 

namespace System.Runtime.InteropServices 
{ 
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
    internal abstract unsafe class SafePointer : SafeHandleZeroOrMinusOneIsInvalid 
    {
        // Steal UIntPtr.MaxValue as our uninitialized value.
        private static readonly UIntPtr Uninitialized = (UIntPtr.Size == 4) ?
            ((UIntPtr) UInt32.MaxValue) : ((UIntPtr) UInt64.MaxValue); 

        private UIntPtr _numBytes; 
 
        protected SafePointer(bool ownsHandle) : base(ownsHandle)
        { 
            _numBytes = Uninitialized;
        }

        ///  
        /// Specifies the size of the region of memory, in bytes.  Must be
        /// called before using the SafePointer. 
        ///  
        /// Number of valid bytes in memory.
        //[CLSCompliant(false)] 
        public void Initialize(ulong numBytes)
        {
            if (numBytes < 0)
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 
            if (IntPtr.Size == 4 && numBytes > UInt32.MaxValue)
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_AddressSpace")); 
            if (numBytes >= (ulong) Uninitialized) 
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_UIntPtrMax-1"));
 
            _numBytes = (UIntPtr) numBytes;
        }

        ///  
        /// Specifies the the size of the region in memory, as the number of
        /// elements in an array.  Must be called before using the SafePointer. 
        ///  
        //[CLSCompliant(false)]
        public void Initialize(uint numElements, uint sizeOfEachElement) 
        {
            if (numElements < 0)
                throw new ArgumentOutOfRangeException("numElements", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (sizeOfEachElement < 0) 
                throw new ArgumentOutOfRangeException("sizeOfEachElement", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
 
            if (IntPtr.Size == 4 && numElements * sizeOfEachElement > UInt32.MaxValue) 
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_AddressSpace"));
            if (numElements * sizeOfEachElement >= (ulong)Uninitialized) 
                throw new ArgumentOutOfRangeException("numElements", Environment.GetResourceString("ArgumentOutOfRange_UIntPtrMax-1"));

            _numBytes = checked((UIntPtr) (numElements * sizeOfEachElement));
        } 

        ///  
        /// Specifies the the size of the region in memory, as the number of 
        /// elements in an array.  Must be called before using the SafePointer.
        ///  
        //[CLSCompliant(false)]
        public void Initialize(uint numElements) where T : struct
        {
            Initialize(numElements, SizeOf()); 
        }
 
        ///  
        /// Returns the size of an instance of a value type.
        ///  
        /// Provide a value type to figure out its size
        /// The size of T in bytes.
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        public static uint SizeOf() where T : struct { 
            return SizeOfType(typeof(T));
        } 
 
        // Callers should ensure that they check whether the pointer ref param
        // is null when AcquirePointer returns.  If it is not null, they must 
        // call ReleasePointer in a CER.  This method calls DangerousAddRef
        // & exposes the pointer. Unlike Read, it does not alter the "current
        // position" of the pointer.  Here's how to use it:
        // 
        // byte* pointer = null;
        // RuntimeHelpers.PrepareConstrainedRegions(); 
        // try { 
        //     safePointer.AcquirePointer(ref pointer);
        //     // Use pointer here, with your own bounds checking 
        // }
        // finally {
        //     if (pointer != null)
        //         safePointer.ReleasePointer(); 
        // }
        // 
        // Note: If you cast this byte* to a T*, you have to worry about 
        // whether your pointer is aligned.  Additionally, you must take
        // responsibility for all bounds checking with this pointer. 
        /// 
        /// Obtain the pointer from a SafePointer for a block of code,
        /// with the express responsibility for bounds checking and calling
        /// ReleasePointer later within a CER to ensure the pointer can be 
        /// freed later.  This method either completes successfully or
        /// throws an exception and returns with pointer set to null. 
        ///  
        /// A byte*, passed by reference, to receive
        /// the pointer from within the SafePointer.  You must set 
        /// pointer to null before calling this method.
        //[CLSCompliant(false)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void AcquirePointer(ref byte* pointer) 
        {
            if (_numBytes == Uninitialized) 
                throw NotInitialized(); 

            pointer = null; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
            } 
            finally
            { 
                bool junk = false; 
                DangerousAddRef(ref junk);
                pointer = (byte*)handle; 
            }
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        public void ReleasePointer()
        { 
            if (_numBytes == Uninitialized) 
                throw NotInitialized();
 
            DangerousRelease();
        }

        ///  
        /// Read a value type from memory at the given offset.  This is
        /// equivalent to:  return *(T*)(bytePtr + byteOffset); 
        ///  
        /// The value type to read
        /// Where to start reading from memory.  You 
        /// may have to consider alignment.
        /// An instance of T read from memory.
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public T Read(uint byteOffset) where T : struct { 
            if (_numBytes == Uninitialized)
                throw NotInitialized(); 
 
            uint sizeofT = SizeOf();
            byte* ptr = (byte*)handle + byteOffset; 
            SpaceCheck(ptr, sizeofT);

            // return *(T*) (_ptr + byteOffset);
            T value; 
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try 
            {
                DangerousAddRef(ref mustCallRelease); 

                GenericPtrToStructure(ptr, out value, sizeofT);
            }
            finally 
            {
                if (mustCallRelease) 
                    DangerousRelease(); 
            }
            return value; 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void ReadArray(uint byteOffset, T[] array, int index, int count) 
            where T : struct
        { 
            if (array == null) 
                throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
            if (index < 0) 
                throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (array.Length - index < count) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
 
            if (_numBytes == Uninitialized) 
                throw NotInitialized();
 
            uint sizeofT = SizeOf();
            byte* ptr = (byte*)handle + byteOffset;
            SpaceCheck(ptr, checked((ulong) (sizeofT * count)));
 
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try 
            {
                DangerousAddRef(ref mustCallRelease); 

                for (int i = 0; i < count; i++)
                    unsafe { GenericPtrToStructure(ptr + sizeofT * i, out array[i + count], sizeofT); }
            } 
            finally
            { 
                if (mustCallRelease) 
                    DangerousRelease();
            } 
        }

        /// 
        /// Write a value type to memory at the given offset.  This is 
        /// equivalent to:  *(T*)(bytePtr + byteOffset) = value;
        ///  
        /// The type of the value type to write to memory. 
        /// The location in memory to write to.  You
        /// may have to consider alignment. 
        /// The value type to write to memory.
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void Write(uint byteOffset, T value) where T : struct {
            if (_numBytes == Uninitialized) 
                throw NotInitialized();
 
            uint sizeofT = SizeOf(); 
            byte* ptr = (byte*)handle + byteOffset;
            SpaceCheck(ptr, sizeofT); 

            // *((T*) (_ptr + byteOffset)) = value;
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try
            { 
                DangerousAddRef(ref mustCallRelease); 
                GenericStructureToPtr(ref value, ptr, sizeofT);
            } 
            finally
            {
                if (mustCallRelease)
                    DangerousRelease(); 
            }
        } 
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void WriteArray(uint byteOffset, T[] array, int index, int count) 
            where T : struct
        {
            if (array == null)
                throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); 
            if (index < 0)
                throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (array.Length - index < count) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));

            if (_numBytes == Uninitialized)
                throw NotInitialized(); 

            uint sizeofT = SizeOf(); 
            byte* ptr = (byte*)handle + byteOffset; 
            SpaceCheck(ptr, checked((ulong)(sizeofT * count)));
 
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            { 
                DangerousAddRef(ref mustCallRelease);
                for (int i = 0; i < count; i++) 
                    unsafe { GenericStructureToPtr(ref array[i + count], ptr + sizeofT * i, sizeofT); } 
            }
            finally 
            {
                if (mustCallRelease)
                    DangerousRelease();
            } 
        }
 
        ///  
        /// Returns the number of bytes in the memory region.
        ///  
        public ulong ByteLength {
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            get {
                if (_numBytes == Uninitialized) 
                    throw NotInitialized();
 
                return (ulong) _numBytes; 
            }
        } 

        /* No indexer.  The perf would be misleadingly bad.  People should use
         * AcquirePointer and ReleasePointer instead.  */
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        private void SpaceCheck(byte* ptr, ulong sizeInBytes) 
        { 
            if ((ulong)(ptr - (byte*) handle) > ((ulong)_numBytes) - sizeInBytes)
                NotEnoughRoom(); 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        private static void NotEnoughRoom() 
        {
            throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall")); 
        } 

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        private static InvalidOperationException NotInitialized()
        {
            BCLDebug.Assert(false, "Unintialized SafePointer!  Someone needs to call Initialize before using this instance!");
            return new InvalidOperationException(Environment.GetResourceString("InvalidOperation_MustCallInitialize")); 
        }
 
        // FCALL limitations mean we can't have generic FCALL methods.  However, we can pass 
        // TypedReferences to FCALL methods.
        private static void GenericPtrToStructure(byte* ptr, out T structure, uint sizeofT) where T : struct 
        {
            structure = default(T);  // Dummy assignment to silence the compiler
            PtrToStructureNative(ptr, __makeref(structure), sizeofT);
        } 

        [MethodImpl(MethodImplOptions.InternalCall)] 
        [ResourceExposure(ResourceScope.None)] 
        private static extern void PtrToStructureNative(byte* ptr, /*out T*/ TypedReference structure, uint sizeofT);
 
        private static void GenericStructureToPtr(ref T structure, byte* ptr, uint sizeofT) where T : struct
        {
            StructureToPtrNative(__makeref(structure), ptr, sizeofT);
        } 

        [MethodImpl(MethodImplOptions.InternalCall)] 
        [ResourceExposure(ResourceScope.None)] 
        private static extern void StructureToPtrNative(/*ref T*/ TypedReference structure, byte* ptr, uint sizeofT);
 
        // Type must be a value type with no object reference fields.  We only
        // assert this, due to the lack of a suitable generic constraint.
        [MethodImpl(MethodImplOptions.InternalCall)]
        [ResourceExposure(ResourceScope.None)] 
        private static extern uint SizeOfType(Type type);
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
/*============================================================
** 
** Purpose: Unsafe code that uses pointers should use 
** SafePointer to fix subtle lifetime problems with the
** underlying resource. 
**
===========================================================*/

// Design points: 
// *) Avoid handle-recycling problems (including ones triggered via
// resurrection attacks) for all accesses via pointers.  This requires tying 
// together the lifetime of the unmanaged resource with the code that reads 
// from that resource, in a package that uses synchronization to enforce
// the correct semantics during finalization.  We're using SafeHandle's 
// ref count as a gate on whether the pointer can be dereferenced because that
// controls the lifetime of the resource.
//
// *) Keep the penalties for using this class small, both in terms of space 
// and time.  Having multiple threads reading from a memory mapped file
// will already require 2 additional interlocked operations.  If we add in 
// a "current position" concept, that requires additional space in memory and 
// synchronization.  Since the position in memory is often (but not always)
// something that can be stored on the stack, we can save some memory by 
// excluding it from this object.  However, avoiding the need for
// synchronization is a more significant win.  This design allows multiple
// threads to read and write memory simultaneously without locks (as long as
// you don't write to a region of memory that overlaps with what another 
// thread is accessing).
// 
// *) Space-wise, we use the following memory, including SafeHandle's fields: 
// Object Header  MT*  handle  int bool bool <2 pad bytes> length
// On 32 bit platforms: 24 bytes.  On 64 bit platforms: 40 bytes. 
// (We can safe 4 bytes on x86 only by shrinking SafeHandle)
//
// *) Wrapping a SafeHandle would have been a nice solution, but without an
// ordering between critical finalizable objects, it would have required 
// changes to each SafeHandle subclass to opt in to being usable from a
// SafePointer (or some clever exposure of SafeHandle's state fields and a 
// way of forcing ReleaseHandle to run even after the SafeHandle has been 
// finalized with a ref count > 1).  We can use less memory and create fewer
// objects by simply inserting a SafePointer into the class hierarchy. 
//
// *) In an ideal world, we could get marshaling support for SafePointer that
// would allow us to annotate a P/Invoke declaration, saying this parameter
// specifies the length of the buffer, and the units of that length are X. 
// P/Invoke would then pass that size parameter to SafePointer.
//     [DllImport(...)] 
//     static extern SafeMemoryHandle AllocCharBuffer(int numChars); 
// If we could put an attribute on the SafeMemoryHandle saying numChars is
// the element length, and it must be multiplied by 2 to get to the byte 
// length, we can simplify the usage model for SafePointer.
//
// *) This class could benefit from a constraint saying T is a value type
// containing no GC references. 

// Implementation notes: 
// *) The Initialize method must be called before you use any instance of 
// a SafePointer.  To avoid ----s when storing SafePointers in statics,
// you either need to take a lock when publishing the SafePointer, or you 
// need to create a local, initialize the SafePointer, then assign to the
// static variable (perhaps using Interlocked.CompareExchange).  Of course,
// assignments in a static class constructor are under a lock implicitly.
 

using System; 
using System.Security.Permissions; 
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices; 
using System.Runtime.ConstrainedExecution;
using System.Runtime.Versioning;
using Microsoft.Win32.SafeHandles;
 

namespace System.Runtime.InteropServices 
{ 
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
    internal abstract unsafe class SafePointer : SafeHandleZeroOrMinusOneIsInvalid 
    {
        // Steal UIntPtr.MaxValue as our uninitialized value.
        private static readonly UIntPtr Uninitialized = (UIntPtr.Size == 4) ?
            ((UIntPtr) UInt32.MaxValue) : ((UIntPtr) UInt64.MaxValue); 

        private UIntPtr _numBytes; 
 
        protected SafePointer(bool ownsHandle) : base(ownsHandle)
        { 
            _numBytes = Uninitialized;
        }

        ///  
        /// Specifies the size of the region of memory, in bytes.  Must be
        /// called before using the SafePointer. 
        ///  
        /// Number of valid bytes in memory.
        //[CLSCompliant(false)] 
        public void Initialize(ulong numBytes)
        {
            if (numBytes < 0)
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 
            if (IntPtr.Size == 4 && numBytes > UInt32.MaxValue)
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_AddressSpace")); 
            if (numBytes >= (ulong) Uninitialized) 
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_UIntPtrMax-1"));
 
            _numBytes = (UIntPtr) numBytes;
        }

        ///  
        /// Specifies the the size of the region in memory, as the number of
        /// elements in an array.  Must be called before using the SafePointer. 
        ///  
        //[CLSCompliant(false)]
        public void Initialize(uint numElements, uint sizeOfEachElement) 
        {
            if (numElements < 0)
                throw new ArgumentOutOfRangeException("numElements", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (sizeOfEachElement < 0) 
                throw new ArgumentOutOfRangeException("sizeOfEachElement", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
 
            if (IntPtr.Size == 4 && numElements * sizeOfEachElement > UInt32.MaxValue) 
                throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_AddressSpace"));
            if (numElements * sizeOfEachElement >= (ulong)Uninitialized) 
                throw new ArgumentOutOfRangeException("numElements", Environment.GetResourceString("ArgumentOutOfRange_UIntPtrMax-1"));

            _numBytes = checked((UIntPtr) (numElements * sizeOfEachElement));
        } 

        ///  
        /// Specifies the the size of the region in memory, as the number of 
        /// elements in an array.  Must be called before using the SafePointer.
        ///  
        //[CLSCompliant(false)]
        public void Initialize(uint numElements) where T : struct
        {
            Initialize(numElements, SizeOf()); 
        }
 
        ///  
        /// Returns the size of an instance of a value type.
        ///  
        /// Provide a value type to figure out its size
        /// The size of T in bytes.
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        public static uint SizeOf() where T : struct { 
            return SizeOfType(typeof(T));
        } 
 
        // Callers should ensure that they check whether the pointer ref param
        // is null when AcquirePointer returns.  If it is not null, they must 
        // call ReleasePointer in a CER.  This method calls DangerousAddRef
        // & exposes the pointer. Unlike Read, it does not alter the "current
        // position" of the pointer.  Here's how to use it:
        // 
        // byte* pointer = null;
        // RuntimeHelpers.PrepareConstrainedRegions(); 
        // try { 
        //     safePointer.AcquirePointer(ref pointer);
        //     // Use pointer here, with your own bounds checking 
        // }
        // finally {
        //     if (pointer != null)
        //         safePointer.ReleasePointer(); 
        // }
        // 
        // Note: If you cast this byte* to a T*, you have to worry about 
        // whether your pointer is aligned.  Additionally, you must take
        // responsibility for all bounds checking with this pointer. 
        /// 
        /// Obtain the pointer from a SafePointer for a block of code,
        /// with the express responsibility for bounds checking and calling
        /// ReleasePointer later within a CER to ensure the pointer can be 
        /// freed later.  This method either completes successfully or
        /// throws an exception and returns with pointer set to null. 
        ///  
        /// A byte*, passed by reference, to receive
        /// the pointer from within the SafePointer.  You must set 
        /// pointer to null before calling this method.
        //[CLSCompliant(false)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void AcquirePointer(ref byte* pointer) 
        {
            if (_numBytes == Uninitialized) 
                throw NotInitialized(); 

            pointer = null; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
            } 
            finally
            { 
                bool junk = false; 
                DangerousAddRef(ref junk);
                pointer = (byte*)handle; 
            }
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        public void ReleasePointer()
        { 
            if (_numBytes == Uninitialized) 
                throw NotInitialized();
 
            DangerousRelease();
        }

        ///  
        /// Read a value type from memory at the given offset.  This is
        /// equivalent to:  return *(T*)(bytePtr + byteOffset); 
        ///  
        /// The value type to read
        /// Where to start reading from memory.  You 
        /// may have to consider alignment.
        /// An instance of T read from memory.
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public T Read(uint byteOffset) where T : struct { 
            if (_numBytes == Uninitialized)
                throw NotInitialized(); 
 
            uint sizeofT = SizeOf();
            byte* ptr = (byte*)handle + byteOffset; 
            SpaceCheck(ptr, sizeofT);

            // return *(T*) (_ptr + byteOffset);
            T value; 
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try 
            {
                DangerousAddRef(ref mustCallRelease); 

                GenericPtrToStructure(ptr, out value, sizeofT);
            }
            finally 
            {
                if (mustCallRelease) 
                    DangerousRelease(); 
            }
            return value; 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void ReadArray(uint byteOffset, T[] array, int index, int count) 
            where T : struct
        { 
            if (array == null) 
                throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
            if (index < 0) 
                throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (array.Length - index < count) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
 
            if (_numBytes == Uninitialized) 
                throw NotInitialized();
 
            uint sizeofT = SizeOf();
            byte* ptr = (byte*)handle + byteOffset;
            SpaceCheck(ptr, checked((ulong) (sizeofT * count)));
 
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try 
            {
                DangerousAddRef(ref mustCallRelease); 

                for (int i = 0; i < count; i++)
                    unsafe { GenericPtrToStructure(ptr + sizeofT * i, out array[i + count], sizeofT); }
            } 
            finally
            { 
                if (mustCallRelease) 
                    DangerousRelease();
            } 
        }

        /// 
        /// Write a value type to memory at the given offset.  This is 
        /// equivalent to:  *(T*)(bytePtr + byteOffset) = value;
        ///  
        /// The type of the value type to write to memory. 
        /// The location in memory to write to.  You
        /// may have to consider alignment. 
        /// The value type to write to memory.
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void Write(uint byteOffset, T value) where T : struct {
            if (_numBytes == Uninitialized) 
                throw NotInitialized();
 
            uint sizeofT = SizeOf(); 
            byte* ptr = (byte*)handle + byteOffset;
            SpaceCheck(ptr, sizeofT); 

            // *((T*) (_ptr + byteOffset)) = value;
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try
            { 
                DangerousAddRef(ref mustCallRelease); 
                GenericStructureToPtr(ref value, ptr, sizeofT);
            } 
            finally
            {
                if (mustCallRelease)
                    DangerousRelease(); 
            }
        } 
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void WriteArray(uint byteOffset, T[] array, int index, int count) 
            where T : struct
        {
            if (array == null)
                throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); 
            if (index < 0)
                throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            if (array.Length - index < count) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));

            if (_numBytes == Uninitialized)
                throw NotInitialized(); 

            uint sizeofT = SizeOf(); 
            byte* ptr = (byte*)handle + byteOffset; 
            SpaceCheck(ptr, checked((ulong)(sizeofT * count)));
 
            bool mustCallRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            { 
                DangerousAddRef(ref mustCallRelease);
                for (int i = 0; i < count; i++) 
                    unsafe { GenericStructureToPtr(ref array[i + count], ptr + sizeofT * i, sizeofT); } 
            }
            finally 
            {
                if (mustCallRelease)
                    DangerousRelease();
            } 
        }
 
        ///  
        /// Returns the number of bytes in the memory region.
        ///  
        public ulong ByteLength {
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            get {
                if (_numBytes == Uninitialized) 
                    throw NotInitialized();
 
                return (ulong) _numBytes; 
            }
        } 

        /* No indexer.  The perf would be misleadingly bad.  People should use
         * AcquirePointer and ReleasePointer instead.  */
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        private void SpaceCheck(byte* ptr, ulong sizeInBytes) 
        { 
            if ((ulong)(ptr - (byte*) handle) > ((ulong)_numBytes) - sizeInBytes)
                NotEnoughRoom(); 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        private static void NotEnoughRoom() 
        {
            throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall")); 
        } 

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        private static InvalidOperationException NotInitialized()
        {
            BCLDebug.Assert(false, "Unintialized SafePointer!  Someone needs to call Initialize before using this instance!");
            return new InvalidOperationException(Environment.GetResourceString("InvalidOperation_MustCallInitialize")); 
        }
 
        // FCALL limitations mean we can't have generic FCALL methods.  However, we can pass 
        // TypedReferences to FCALL methods.
        private static void GenericPtrToStructure(byte* ptr, out T structure, uint sizeofT) where T : struct 
        {
            structure = default(T);  // Dummy assignment to silence the compiler
            PtrToStructureNative(ptr, __makeref(structure), sizeofT);
        } 

        [MethodImpl(MethodImplOptions.InternalCall)] 
        [ResourceExposure(ResourceScope.None)] 
        private static extern void PtrToStructureNative(byte* ptr, /*out T*/ TypedReference structure, uint sizeofT);
 
        private static void GenericStructureToPtr(ref T structure, byte* ptr, uint sizeofT) where T : struct
        {
            StructureToPtrNative(__makeref(structure), ptr, sizeofT);
        } 

        [MethodImpl(MethodImplOptions.InternalCall)] 
        [ResourceExposure(ResourceScope.None)] 
        private static extern void StructureToPtrNative(/*ref T*/ TypedReference structure, byte* ptr, uint sizeofT);
 
        // Type must be a value type with no object reference fields.  We only
        // assert this, due to the lack of a suitable generic constraint.
        [MethodImpl(MethodImplOptions.InternalCall)]
        [ResourceExposure(ResourceScope.None)] 
        private static extern uint SizeOfType(Type type);
    } 
} 

// 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