#if NET20 || NET35 using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; namespace System.IO { /// /// Provides random access to unmanaged blocks of memory from managed code. /// public class UnmanagedMemoryAccessor : IDisposable { private FileAccess access; [SecurityCritical] private SafeBuffer buffer; private bool canRead; private bool canWrite; private long offset; /// /// Initializes a new instance of the class with a specified buffer, offset, and capacity. /// /// The buffer to contain the accessor. /// The byte at which to start the accessor. /// The size, in bytes, of memory to allocate. [SecuritySafeCritical] public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity) => Initialize(buffer, offset, capacity, FileAccess.Read); /// /// Initializes a new instance of the class with a specified buffer, offset, capacity, and /// access right. /// /// The buffer to contain the accessor. /// The byte at which to start the accessor. /// The size, in bytes, of memory to allocate. /// The type of access allowed to the memory. The default is ReadWrite. [SecuritySafeCritical] public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity, FileAccess access) => Initialize(buffer, offset, capacity, access); /// Initializes a new instance of the class. protected UnmanagedMemoryAccessor() => IsOpen = false; private unsafe delegate T AlignedPtrReadFunc(byte* ptr) where T : unmanaged; private unsafe delegate void AlignedPtrWriteFunc(byte* ptr); /// Determines whether the accessor is readable. /// if the accessor is readable; otherwise, . public bool CanRead => IsOpen && canRead; /// Determines whether the accessory is writable. /// if the accessor is writable; otherwise, . public bool CanWrite => IsOpen && canWrite; /// Gets the capacity of the accessor. /// The capacity of the accessor. [field: ContractPublicPropertyName("Capacity")] public long Capacity { get; private set; } /// Determines whether the accessor is currently open by a process. /// if the accessor is open; otherwise, . protected bool IsOpen { get; private set; } /// Releases all resources used by the UnmanagedMemoryAccessor. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// Reads a structure of type from the accessor into a provided reference. /// The type of structure. /// The position in the accessor at which to begin reading. /// The structure to contain the read data. [SecurityCritical] public void Read(long position, out T structure) where T : struct { if (position < 0) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); Contract.EndContractBlock(); if (!IsOpen) throw new ObjectDisposedException("UnmanagedMemoryAccessor", ResourceHelper.GetString("ObjectDisposed_ViewAccessorClosed")); if (!CanRead) throw new NotSupportedException(ResourceHelper.GetString("NotSupported_Reading")); var sizeOfT = (uint)Marshal.SizeOf(typeof(T)); if (position > Capacity - sizeOfT) { if (position >= Capacity) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); throw new ArgumentException(ResourceHelper.GetString("Argument_NotEnoughBytesToRead", typeof(T).FullName), nameof(position)); } structure = buffer.Read((ulong)(offset + position)); } /// Reads structures of type from the accessor into an array of type . /// The type of structure. /// The number of bytes in the accessor at which to begin reading. /// The array to contain the structures read from the accessor. /// The index in in which to place the first copied structure. /// The number of structures of type to read from the accessor. /// /// The number of structures read into . This value can be less than if there are /// fewer structures available, or zero if the end of the accessor is reached. /// [SecurityCritical] public int ReadArray(long position, T[] array, int offset, int count) where T : struct { if (array == null) throw new ArgumentNullException(nameof(array), ResourceHelper.GetString("ArgumentNull_Buffer")); if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); if (array.Length - offset < count) throw new ArgumentException(ResourceHelper.GetString("Argument_OffsetAndLengthOutOfBounds")); Contract.EndContractBlock(); if (!CanRead) { if (!IsOpen) throw new ObjectDisposedException("UnmanagedMemoryAccessor", ResourceHelper.GetString("ObjectDisposed_ViewAccessorClosed")); throw new NotSupportedException(ResourceHelper.GetString("NotSupported_Reading")); } if (position < 0) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); var sizeOfT = (uint)Marshal.SizeOf(typeof(T)); // only check position and ask for fewer Ts if count is too big if (position >= Capacity) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); var n = count; var spaceLeft = Capacity - position; if (spaceLeft < 0) { n = 0; } else { var spaceNeeded = (ulong)(sizeOfT * count); if ((ulong)spaceLeft < spaceNeeded) { n = (int)(spaceLeft / sizeOfT); } } buffer.ReadArray((ulong)(this.offset + position), array, offset, n); return n; } /// Reads a Boolean value from the accessor. /// The number of bytes into the accessor at which to begin reading. /// or . public bool ReadBoolean(long position) => ReadByte(position) != 0; /// Reads a byte value from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. public byte ReadByte(long position) { const int sizeOfType = sizeof(byte); EnsureSafeToRead(position, sizeOfType); return InternalReadByte(position); } /// Reads a character from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public char ReadChar(long position) { unsafe { return InternalRead(position, pointer => (char)(*pointer | *(pointer + 1) << 8)); } } /// Reads a decimal value from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public decimal ReadDecimal(long position) { const int sizeOfType = sizeof(decimal); EnsureSafeToRead(position, sizeOfType); var decimalArray = new int[4]; ReadArray(position, decimalArray, 0, decimalArray.Length); return new decimal(decimalArray); } /// Reads a double-precision floating-point value from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public double ReadDouble(long position) { unsafe { return InternalRead(position, pointer => { var lo = (uint) (*pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); var hi = (uint) (*(pointer + 4) | *(pointer + 5) << 8 | *(pointer + 6) << 16 | *(pointer + 7) << 24); var tempResult = (ulong)hi << 32 | lo; return *(double*) &tempResult; }); } } /// Reads a 16-bit integer from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public short ReadInt16(long position) { unsafe { return InternalRead(position, pointer => (short)(*pointer | *(pointer + 1) << 8)); } } /// Reads a 32-bit integer from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public int ReadInt32(long position) { unsafe { return InternalRead(position, pointer => *pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); } } /// Reads a 64-bit integer from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public long ReadInt64(long position) { unsafe { return InternalRead(position, pointer => { var lo = *pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24; var hi = *(pointer + 4) | *(pointer + 5) << 8 | *(pointer + 6) << 16 | *(pointer + 7) << 24; return ((long)hi << 32) | (uint)lo; }); } } /// Reads an 8-bit integer from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public sbyte ReadSByte(long position) => unchecked((sbyte)ReadByte(position)); /// Reads a single-precision floating-point value from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public float ReadSingle(long position) { unsafe { return InternalRead(position, pointer => { var tempResult = (uint)(*pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); return *(float*)&tempResult; }); } } /// Reads an unsigned 16-bit integer from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public ushort ReadUInt16(long position) { unsafe { return InternalRead(position, pointer => (ushort)(*pointer | *(pointer + 1) << 8)); } } /// Reads an unsigned 32-bit integer from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public uint ReadUInt32(long position) { unsafe { return InternalRead(position, pointer => (uint)(*pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24)); } } /// Reads an unsigned 64-bit integer from the accessor. /// The number of bytes into the accessor at which to begin reading. /// The value that was read. [SecuritySafeCritical] public ulong ReadUInt64(long position) { unsafe { return InternalRead(position, pointer => { var lo = (uint)(*pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); var hi = (uint)(*(pointer + 4) | *(pointer + 5) << 8 | *(pointer + 6) << 16 | *(pointer + 7) << 24); return ((ulong)hi << 32) | lo; }); } } /// Writes a Boolean value into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. public void Write(long position, bool value) => Write(position, (byte)(value ? 1 : 0)); /// Writes a byte value into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. public void Write(long position, byte value) { const int sizeOfType = sizeof(byte); EnsureSafeToWrite(position, sizeOfType); InternalWriteByte(position, value); } /// Writes a character into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, char value) { unsafe { InternalWrite(position, value, pointer => { *pointer = (byte)value; *(pointer + 1) = (byte)(value >> 8); }); } } /// Writes a 16-bit integer into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, short value) { unsafe { InternalWrite(position, value, pointer => { *pointer = (byte)value; *(pointer + 1) = (byte)(value >> 8); }); } } /// Writes a 32-bit integer into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, int value) { unsafe { InternalWrite(position, value, pointer => { *pointer = (byte)value; *(pointer + 1) = (byte)(value >> 8); *(pointer + 2) = (byte)(value >> 16); *(pointer + 3) = (byte)(value >> 24); }); } } /// Writes a 64-bit integer into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, long value) { unsafe { InternalWrite(position, value, pointer => { *pointer = (byte)value; *(pointer + 1) = (byte)(value >> 8); *(pointer + 2) = (byte)(value >> 16); *(pointer + 3) = (byte)(value >> 24); *(pointer + 4) = (byte)(value >> 32); *(pointer + 5) = (byte)(value >> 40); *(pointer + 6) = (byte)(value >> 48); *(pointer + 7) = (byte)(value >> 56); }); } } /// Writes a decimal value into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, decimal value) { const int sizeOfType = sizeof(decimal); EnsureSafeToWrite(position, sizeOfType); var decimalArray = new byte[16]; GetDecimalBytes(value, decimalArray); var bits = new int[4]; var flags = decimalArray[12] | (decimalArray[13] << 8) | (decimalArray[14] << 16) | (decimalArray[15] << 24); var lo = decimalArray[0] | (decimalArray[1] << 8) | (decimalArray[2] << 16) | (decimalArray[3] << 24); var mid = decimalArray[4] | (decimalArray[5] << 8) | (decimalArray[6] << 16) | (decimalArray[7] << 24); var hi = decimalArray[8] | (decimalArray[9] << 8) | (decimalArray[10] << 16) | (decimalArray[11] << 24); bits[0] = lo; bits[1] = mid; bits[2] = hi; bits[3] = flags; WriteArray(position, bits, 0, bits.Length); } /// Writes a Single into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, float value) { unsafe { InternalWrite(position, value, pointer => { var lValue = value; var tmpValue = *(uint*)&lValue; *pointer = (byte)tmpValue; *(pointer + 1) = (byte)(tmpValue >> 8); *(pointer + 2) = (byte)(tmpValue >> 16); *(pointer + 3) = (byte)(tmpValue >> 24); }); } } /// Writes a Double into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, double value) { unsafe { InternalWrite(position, value, pointer => { var lValue = value; var tmpValue = *(ulong*)&lValue; *pointer = (byte)tmpValue; *(pointer + 1) = (byte)(tmpValue >> 8); *(pointer + 2) = (byte)(tmpValue >> 16); *(pointer + 3) = (byte)(tmpValue >> 24); *(pointer + 4) = (byte)(tmpValue >> 32); *(pointer + 5) = (byte)(tmpValue >> 40); *(pointer + 6) = (byte)(tmpValue >> 48); *(pointer + 7) = (byte)(tmpValue >> 56); }); } } /// Writes an unsigned 8-bit integer into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, sbyte value) => Write(position, unchecked((byte)value)); /// Writes an unsigned 16-bit integer into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, ushort value) { unsafe { InternalWrite(position, value, pointer => { *pointer = (byte)value; *(pointer + 1) = (byte)(value >> 8); }); } } /// Writes an unsigned 32-bit integer into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, uint value) { unsafe { InternalWrite(position, value, pointer => { *pointer = (byte)value; *(pointer + 1) = (byte)(value >> 8); *(pointer + 2) = (byte)(value >> 16); *(pointer + 3) = (byte)(value >> 24); }); } } /// Writes an unsigned 64-bit integer into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The value to write. [SecuritySafeCritical] public void Write(long position, ulong value) { unsafe { InternalWrite(position, value, pointer => { *pointer = (byte)value; *(pointer + 1) = (byte)(value >> 8); *(pointer + 2) = (byte)(value >> 16); *(pointer + 3) = (byte)(value >> 24); *(pointer + 4) = (byte)(value >> 32); *(pointer + 5) = (byte)(value >> 40); *(pointer + 6) = (byte)(value >> 48); *(pointer + 7) = (byte)(value >> 56); }); } } /// Writes a structure into the accessor. /// The number of bytes into the accessor at which to begin writing. /// The structure to write. [SecurityCritical] public void Write(long position, ref T structure) where T : struct { if (position < 0) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); Contract.EndContractBlock(); if (!IsOpen) throw new ObjectDisposedException("UnmanagedMemoryAccessor", ResourceHelper.GetString("ObjectDisposed_ViewAccessorClosed")); if (!CanWrite) throw new NotSupportedException(ResourceHelper.GetString("NotSupported_Writing")); var sizeOfT = (uint)Marshal.SizeOf(typeof(T)); if (position > Capacity - sizeOfT) { if (position >= Capacity) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); throw new ArgumentException(ResourceHelper.GetString("Argument_NotEnoughBytesToWrite", typeof(T).FullName), nameof(position)); } buffer.Write((ulong)(offset + position), structure); } /// Writes structures from an array of type into the accessor. /// The type of structure. /// The number of bytes into the accessor at which to begin writing. /// The array to write into the accessor. /// The index in to start writing from. /// The number of structures in to write. [SecurityCritical] public void WriteArray(long position, T[] array, int offset, int count) where T : struct { if (array == null) throw new ArgumentNullException(nameof(array), ResourceHelper.GetString("ArgumentNull_Buffer")); if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); if (array.Length - offset < count) throw new ArgumentException(ResourceHelper.GetString("Argument_OffsetAndLengthOutOfBounds")); if (position < 0) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); if (position >= Capacity) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); Contract.EndContractBlock(); if (!IsOpen) throw new ObjectDisposedException("UnmanagedMemoryAccessor", ResourceHelper.GetString("ObjectDisposed_ViewAccessorClosed")); if (!CanWrite) throw new NotSupportedException(ResourceHelper.GetString("NotSupported_Writing")); buffer.WriteArray((ulong)(this.offset + position), array, offset, count); } internal static void GetDecimalBytes(decimal d, byte[] buffer) { Contract.Requires(buffer != null && buffer.Length >= 16, "[GetBytes]buffer != null && buffer.Length >= 16"); Buffer.BlockCopy(decimal.GetBits(d), 0, buffer, 0, buffer.Length); } /// Releases the unmanaged resources used by the UnmanagedMemoryAccessor and optionally releases the managed resources. /// /// to release both managed and unmanaged resources; to release only unmanaged resources. /// protected virtual void Dispose(bool disposing) => IsOpen = false; /// Sets the initial values for the accessor. /// The buffer to contain the accessor. /// The byte at which to start the accessor. /// The size, in bytes, of memory to allocate. /// The type of access allowed to the memory. The default is ReadWrite. [SecuritySafeCritical] [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] protected void Initialize(SafeBuffer buffer, long offset, long capacity, FileAccess access) { if (buffer == null) throw new ArgumentNullException(nameof(buffer)); if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.ByteLength < (ulong)(offset + capacity)) throw new ArgumentException(ResourceHelper.GetString("Argument_OffsetAndCapacityOutOfBounds")); if (access < FileAccess.Read || access > FileAccess.ReadWrite) throw new ArgumentOutOfRangeException(nameof(access)); Contract.EndContractBlock(); if (IsOpen) throw new InvalidOperationException(ResourceHelper.GetString("InvalidOperation_CalledTwice")); unsafe { byte* pointer = null; RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.AcquirePointer(ref pointer); if ((byte*)((long)pointer + offset + capacity) < pointer) { throw new ArgumentException(ResourceHelper.GetString("Argument_UnmanagedMemAccessorWrapAround")); } } finally { if (pointer != null) { buffer.ReleasePointer(); } } } this.offset = offset; this.buffer = buffer; this.Capacity = capacity; this.access = access; IsOpen = true; canRead = (this.access & FileAccess.Read) != 0; canWrite = (this.access & FileAccess.Write) != 0; } private void EnsureSafeToRead(long position, int sizeOfType) { if (!IsOpen) throw new ObjectDisposedException("UnmanagedMemoryAccessor", ResourceHelper.GetString("ObjectDisposed_ViewAccessorClosed")); if (!CanRead) throw new NotSupportedException(ResourceHelper.GetString("NotSupported_Reading")); if (position < 0) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); Contract.EndContractBlock(); if (position <= Capacity - sizeOfType) return; if (position >= Capacity) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); throw new ArgumentException(ResourceHelper.GetString("Argument_NotEnoughBytesToRead"), nameof(position)); } private void EnsureSafeToWrite(long position, int sizeOfType) { if (!IsOpen) throw new ObjectDisposedException("UnmanagedMemoryAccessor", ResourceHelper.GetString("ObjectDisposed_ViewAccessorClosed")); if (!CanWrite) throw new NotSupportedException(ResourceHelper.GetString("NotSupported_Writing")); if (position < 0) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum")); Contract.EndContractBlock(); if (position > Capacity - sizeOfType) { if (position >= Capacity) throw new ArgumentOutOfRangeException(nameof(position), ResourceHelper.GetString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); throw new ArgumentException(ResourceHelper.GetString("Argument_NotEnoughBytesToWrite", "Byte"), nameof(position)); } } private unsafe T InternalRead(long position, AlignedPtrReadFunc func) where T : unmanaged, IConvertible { var sizeOfType = sizeof(T); EnsureSafeToRead(position, sizeOfType); T result; byte* pointer = null; RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.AcquirePointer(ref pointer); pointer += offset + position; #if ALIGN_ACCESS // check if pointer is aligned if (((int)pointer & (sizeOfType - 1)) == 0) { #endif result = *(T*)pointer; #if ALIGN_ACCESS } else { result = func(pointer); } #endif } finally { if (pointer != null) { buffer.ReleasePointer(); } } return result; } [SecuritySafeCritical] private byte InternalReadByte(long position) { Contract.Assert(CanRead, "UMA not readable"); Contract.Assert(position >= 0, "position less than 0"); Contract.Assert(position <= Capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)"); byte result; unsafe { byte* pointer = null; RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.AcquirePointer(ref pointer); result = *(pointer + offset + position); } finally { if (pointer != null) { buffer.ReleasePointer(); } } } return result; } [SecuritySafeCritical] private unsafe void InternalWrite(long position, T value, AlignedPtrWriteFunc func) where T : unmanaged, IConvertible { var sizeOfType = sizeof(T); EnsureSafeToWrite(position, sizeOfType); byte* pointer = null; RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.AcquirePointer(ref pointer); pointer += offset + position; #if ALIGN_ACCESS // check if pointer is aligned if (((int)pointer & (sizeOfType - 1)) == 0) { #endif *(T*)pointer = value; #if ALIGN_ACCESS } else { func(pointer); } #endif } finally { if (pointer != null) { buffer.ReleasePointer(); } } } [SecuritySafeCritical] private void InternalWriteByte(long position, byte value) { Contract.Assert(CanWrite, "UMA not writable"); Contract.Assert(position >= 0, "position less than 0"); Contract.Assert(position <= Capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)"); unsafe { byte* pointer = null; RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.AcquirePointer(ref pointer); *(pointer + offset + position) = value; } finally { if (pointer != null) { buffer.ReleasePointer(); } } } } } } #endif