mirror of https://github.com/dahall/Vanara.git
Added backwards compatible classes SafeBuffer, UnmanagedMemoryAccessor and UnmanagedMemoryStream for .NET 2.0 and 3.5.
parent
f64c2b5e6b
commit
40155dc239
|
@ -0,0 +1,314 @@
|
|||
#if NET20 || NET35
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace System.Runtime.InteropServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a controlled memory buffer that can be used for reading and writing. Attempts to access memory outside the controlled buffer
|
||||
/// (under-runs and overruns) raise exceptions.
|
||||
/// </summary>
|
||||
/// <seealso cref="Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid"/>
|
||||
[Security.SecurityCritical]
|
||||
public abstract unsafe class SafeBuffer : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private static readonly UIntPtr Uninitialized = UIntPtr.Size == 4 ? (UIntPtr)uint.MaxValue : (UIntPtr)ulong.MaxValue;
|
||||
|
||||
private UIntPtr numBytes;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:System.Runtime.InteropServices.SafeBuffer" /> class, and specifies whether the buffer handle is to be reliably released.
|
||||
/// </summary>
|
||||
/// <param name="ownsHandle">
|
||||
/// <see langword="true" /> to reliably release the handle during the finalization phase; <see langword="false" /> to prevent reliable
|
||||
/// release (not recommended).
|
||||
/// </param>
|
||||
protected SafeBuffer(bool ownsHandle) : base(ownsHandle) => numBytes = Uninitialized;
|
||||
|
||||
/// <summary>Gets the size of the buffer, in bytes.</summary>
|
||||
public ulong ByteLength
|
||||
{
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
get => numBytes != Uninitialized ? (ulong)numBytes : throw NotInitialized();
|
||||
}
|
||||
|
||||
/// <summary>Obtains a pointer from a <see cref="SafeBuffer"/> object for a block of memory.</summary>
|
||||
/// <param name="pointer">
|
||||
/// A byte pointer, passed by reference, to receive the pointer from within the <see cref="SafeBuffer"/> object. You must set this
|
||||
/// pointer to <see langword="null"/> before you call this method.
|
||||
/// </param>
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public void AcquirePointer(ref byte* pointer)
|
||||
{
|
||||
if (numBytes == Uninitialized)
|
||||
throw NotInitialized();
|
||||
|
||||
pointer = null;
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
var junk = false;
|
||||
DangerousAddRef(ref junk);
|
||||
pointer = (byte*)handle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the allocation size of the memory region in bytes. You must call this method before you use the SafeBuffer instance.
|
||||
/// </summary>
|
||||
/// <param name="numBytes">The number of bytes in the buffer.</param>
|
||||
public void Initialize(ulong numBytes)
|
||||
{
|
||||
if (IntPtr.Size == 4 && numBytes > uint.MaxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(numBytes), ResourceHelper.GetString("ArgumentOutOfRange_AddressSpace"));
|
||||
Contract.EndContractBlock();
|
||||
|
||||
if (numBytes >= (ulong)Uninitialized)
|
||||
throw new ArgumentOutOfRangeException(nameof(numBytes), ResourceHelper.GetString("ArgumentOutOfRange_UIntPtrMax-1"));
|
||||
|
||||
this.numBytes = (UIntPtr)numBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the allocation size of the memory buffer by using the specified number of elements and element size. You must call this
|
||||
/// method before you use the SafeBuffer instance.
|
||||
/// </summary>
|
||||
/// <param name="numElements">The number of elements in the buffer.</param>
|
||||
/// <param name="sizeOfEachElement">The size of each element in the buffer.</param>
|
||||
public void Initialize(uint numElements, uint sizeOfEachElement)
|
||||
{
|
||||
if (IntPtr.Size == 4 && numElements * (ulong)sizeOfEachElement > uint.MaxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(numElements), ResourceHelper.GetString("ArgumentOutOfRange_AddressSpace"));
|
||||
Contract.EndContractBlock();
|
||||
|
||||
if (numElements * (ulong)sizeOfEachElement >= (ulong)Uninitialized)
|
||||
throw new ArgumentOutOfRangeException(nameof(numElements), ResourceHelper.GetString("ArgumentOutOfRange_UIntPtrMax-1"));
|
||||
|
||||
numBytes = checked((UIntPtr)(numElements * sizeOfEachElement));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the allocation size of the memory region by specifying the number of value types. You must call this method before you
|
||||
/// use the SafeBuffer instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The value type to allocate memory for.</typeparam>
|
||||
/// <param name="numElements">The number of elements of the value type to allocate memory for.</param>
|
||||
public void Initialize<T>(uint numElements) where T : struct => Initialize(numElements, (uint)Marshal.SizeOf(typeof(T)));
|
||||
|
||||
/// <summary>Reads a value type from memory at the specified offset.</summary>
|
||||
/// <typeparam name="T">The value type to read.</typeparam>
|
||||
/// <param name="byteOffset">The location from which to read the value type. You may have to consider alignment issues.</param>
|
||||
/// <returns>The value type that was read from memory.</returns>
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public T Read<T>(ulong byteOffset) where T : struct
|
||||
{
|
||||
if (numBytes == Uninitialized)
|
||||
throw NotInitialized();
|
||||
|
||||
var sizeofT = (uint)Marshal.SizeOf(typeof(T));
|
||||
var ptr = (byte*)handle + byteOffset;
|
||||
SpaceCheck(ptr, sizeofT);
|
||||
|
||||
// return *(T*) (_ptr + byteOffset);
|
||||
T value;
|
||||
var mustCallRelease = false;
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try
|
||||
{
|
||||
DangerousAddRef(ref mustCallRelease);
|
||||
GenericPtrToStructure(ptr, out value, sizeofT);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mustCallRelease)
|
||||
DangerousRelease();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of value types from memory starting at the offset, and writes them into an array starting at the index.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The value type to read.</typeparam>
|
||||
/// <param name="byteOffset">The location from which to start reading.</param>
|
||||
/// <param name="array">The output array to write to.</param>
|
||||
/// <param name="index">The location in the output array to begin writing to.</param>
|
||||
/// <param name="count">The number of value types to read from the input array and to write to the output array.</param>
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public void ReadArray<T>(ulong byteOffset, T[] array, int index, int count) where T : struct
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array), ResourceHelper.GetString("ArgumentNull_Buffer"));
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
if (array.Length - index < count)
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
|
||||
Contract.EndContractBlock();
|
||||
|
||||
if (numBytes == Uninitialized)
|
||||
throw NotInitialized();
|
||||
|
||||
var sizeofT = (uint)Marshal.SizeOf(typeof(T));
|
||||
var alignedSizeofT = (uint)Marshal.SizeOf(typeof(T));
|
||||
var ptr = (byte*)handle + byteOffset;
|
||||
SpaceCheck(ptr, checked((ulong)(alignedSizeofT * count)));
|
||||
|
||||
var mustCallRelease = false;
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try
|
||||
{
|
||||
DangerousAddRef(ref mustCallRelease);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
GenericPtrToStructure(ptr + alignedSizeofT * i, out array[i + index], sizeofT);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mustCallRelease)
|
||||
DangerousRelease();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Releases a pointer that was obtained by the <see cref="AcquirePointer(ref byte*)"/> method.</summary>
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
public void ReleasePointer()
|
||||
{
|
||||
if (numBytes == Uninitialized)
|
||||
throw NotInitialized();
|
||||
|
||||
DangerousRelease();
|
||||
}
|
||||
|
||||
/// <summary>Writes a value type to memory at the given location.</summary>
|
||||
/// <typeparam name="T">The value type to write.</typeparam>
|
||||
/// <param name="byteOffset">The location at which to start writing. You may have to consider alignment issues.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public void Write<T>(ulong byteOffset, T value) where T : struct
|
||||
{
|
||||
if (numBytes == Uninitialized)
|
||||
throw NotInitialized();
|
||||
|
||||
var sizeofT = (uint)Marshal.SizeOf(typeof(T));
|
||||
var ptr = (byte*)handle + byteOffset;
|
||||
SpaceCheck(ptr, sizeofT);
|
||||
|
||||
// *((T*) (_ptr + byteOffset)) = value;
|
||||
var mustCallRelease = false;
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try
|
||||
{
|
||||
DangerousAddRef(ref mustCallRelease);
|
||||
GenericStructureToPtr(ref value, ptr, sizeofT);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mustCallRelease)
|
||||
DangerousRelease();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified number of value types to a memory location by reading bytes starting from the specified location in the
|
||||
/// input array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The value type to write.</typeparam>
|
||||
/// <param name="byteOffset">The location in memory to write to.</param>
|
||||
/// <param name="array">The input array.</param>
|
||||
/// <param name="index">The offset in the array to start reading from.</param>
|
||||
/// <param name="count">The number of value types to write.</param>
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public void WriteArray<T>(ulong byteOffset, T[] array, int index, int count) where T : struct
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array), ResourceHelper.GetString("ArgumentNull_Buffer"));
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
if (array.Length - index < count)
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
|
||||
Contract.EndContractBlock();
|
||||
|
||||
if (numBytes == Uninitialized)
|
||||
throw NotInitialized();
|
||||
|
||||
var sizeofT = (uint)Marshal.SizeOf(typeof(T));
|
||||
var alignedSizeofT = (uint)Marshal.SizeOf(typeof(T));
|
||||
var ptr = (byte*)handle + byteOffset;
|
||||
SpaceCheck(ptr, checked((ulong)(alignedSizeofT * count)));
|
||||
|
||||
var mustCallRelease = false;
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try
|
||||
{
|
||||
DangerousAddRef(ref mustCallRelease);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
GenericStructureToPtr(ref array[i + index], ptr + alignedSizeofT * i, sizeofT);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mustCallRelease)
|
||||
DangerousRelease();
|
||||
}
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
internal static void GenericPtrToStructure<T>(byte* ptr, out T structure, uint sizeofT) where T : struct
|
||||
{
|
||||
structure = default;
|
||||
PtrToStructureNative(ptr, __makeref(structure), sizeofT);
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
internal static void GenericStructureToPtr<T>(ref T structure, byte* ptr, uint sizeofT) where T : struct => StructureToPtrNative(__makeref(structure), ptr, sizeofT);
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
private static void NotEnoughRoom() => throw new ArgumentException(ResourceHelper.GetString("Arg_BufferTooSmall"));
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
private static InvalidOperationException NotInitialized()
|
||||
{
|
||||
Contract.Assert(false, "Uninitialized SafeBuffer! Someone needs to call Initialize before using this instance!");
|
||||
return new InvalidOperationException(ResourceHelper.GetString("InvalidOperation_MustCallInitialize"));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
private static extern void PtrToStructureNative(byte* ptr, /*out T*/ TypedReference structure, uint sizeofT);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
private static extern void StructureToPtrNative(/*ref T*/ TypedReference structure, byte* ptr, uint sizeofT);
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
private void SpaceCheck(byte* ptr, ulong sizeInBytes)
|
||||
{
|
||||
if ((ulong)numBytes < sizeInBytes)
|
||||
NotEnoughRoom();
|
||||
if ((ulong)(ptr - (byte*)handle) > (ulong)numBytes - sizeInBytes)
|
||||
NotEnoughRoom();
|
||||
}
|
||||
}
|
||||
|
||||
internal static class ResourceHelper
|
||||
{
|
||||
public static string GetString(string value, params object[] vars) => string.Format(Vanara.Properties.Resources.ResourceManager.GetString(value) ?? throw new InvalidOperationException(), vars);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,794 @@
|
|||
#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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>Provides random access to unmanaged blocks of memory from managed code.</summary>
|
||||
/// <seealso cref="T:System.IDisposable" />
|
||||
public class UnmanagedMemoryAccessor : IDisposable
|
||||
{
|
||||
private FileAccess access;
|
||||
[SecurityCritical]
|
||||
private SafeBuffer buffer;
|
||||
private bool canRead;
|
||||
private bool canWrite;
|
||||
private long offset;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnmanagedMemoryAccessor"/> class with a specified buffer, offset, and capacity.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to contain the accessor.</param>
|
||||
/// <param name="offset">The byte at which to start the accessor.</param>
|
||||
/// <param name="capacity">The size, in bytes, of memory to allocate.</param>
|
||||
[SecuritySafeCritical]
|
||||
public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity) => Initialize(buffer, offset, capacity, FileAccess.Read);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnmanagedMemoryAccessor"/> class with a specified buffer, offset, capacity, and
|
||||
/// access right.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to contain the accessor.</param>
|
||||
/// <param name="offset">The byte at which to start the accessor.</param>
|
||||
/// <param name="capacity">The size, in bytes, of memory to allocate.</param>
|
||||
/// <param name="access">The type of access allowed to the memory. The default is ReadWrite.</param>
|
||||
[SecuritySafeCritical]
|
||||
public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity, FileAccess access) => Initialize(buffer, offset, capacity, access);
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="UnmanagedMemoryAccessor"/> class.</summary>
|
||||
protected UnmanagedMemoryAccessor() => IsOpen = false;
|
||||
|
||||
private unsafe delegate T AlignedPtrReadFunc<out T>(byte* ptr) where T : unmanaged;
|
||||
|
||||
private unsafe delegate void AlignedPtrWriteFunc(byte* ptr);
|
||||
|
||||
/// <summary>Determines whether the accessor is readable.</summary>
|
||||
/// <value><see langword="true"/> if the accessor is readable; otherwise, <see langword="false"/>.</value>
|
||||
public bool CanRead => IsOpen && canRead;
|
||||
|
||||
/// <summary>Determines whether the accessory is writable.</summary>
|
||||
/// <value><see langword="true"/> if the accessor is writable; otherwise, <see langword="false"/>.</value>
|
||||
public bool CanWrite => IsOpen && canWrite;
|
||||
|
||||
/// <summary>Gets the capacity of the accessor.</summary>
|
||||
/// <value>The capacity of the accessor.</value>
|
||||
[field: ContractPublicPropertyName("Capacity")]
|
||||
public long Capacity { get; private set; }
|
||||
|
||||
/// <summary>Determines whether the accessor is currently open by a process.</summary>
|
||||
/// <value><see langword="true"/> if the accessor is open; otherwise, <see langword="false"/>.</value>
|
||||
protected bool IsOpen { get; private set; }
|
||||
|
||||
/// <summary>Releases all resources used by the UnmanagedMemoryAccessor.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>Reads a structure of type <typeparamref name="T"/> from the accessor into a provided reference.</summary>
|
||||
/// <typeparam name="T">The type of structure.</typeparam>
|
||||
/// <param name="position">The position in the accessor at which to begin reading.</param>
|
||||
/// <param name="structure">The structure to contain the read data.</param>
|
||||
[SecurityCritical]
|
||||
public void Read<T>(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<T>((ulong)(offset + position));
|
||||
}
|
||||
|
||||
/// <summary>Reads structures of type <typeparamref name="T"/> from the accessor into an array of type <typeparamref name="T"/>.</summary>
|
||||
/// <typeparam name="T">The type of structure.</typeparam>
|
||||
/// <param name="position">The number of bytes in the accessor at which to begin reading.</param>
|
||||
/// <param name="array">The array to contain the structures read from the accessor.</param>
|
||||
/// <param name="offset">The index in <paramref name="array"/> in which to place the first copied structure.</param>
|
||||
/// <param name="count">The number of structures of type <typeparamref name="T"/> to read from the accessor.</param>
|
||||
/// <returns>
|
||||
/// The number of structures read into <paramref name="array"/>. This value can be less than <paramref name="count"/> if there are
|
||||
/// fewer structures available, or zero if the end of the accessor is reached.
|
||||
/// </returns>
|
||||
[SecurityCritical]
|
||||
public int ReadArray<T>(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;
|
||||
}
|
||||
|
||||
/// <summary>Reads a Boolean value from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns><see langword="true"/> or <see langword="false"/>.</returns>
|
||||
public bool ReadBoolean(long position) => ReadByte(position) != 0;
|
||||
|
||||
/// <summary>Reads a byte value from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
public byte ReadByte(long position)
|
||||
{
|
||||
const int sizeOfType = sizeof(byte);
|
||||
EnsureSafeToRead(position, sizeOfType);
|
||||
|
||||
return InternalReadByte(position);
|
||||
}
|
||||
|
||||
/// <summary>Reads a character from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[SecuritySafeCritical]
|
||||
public char ReadChar(long position) { unsafe { return InternalRead(position, pointer => (char)(*pointer | *(pointer + 1) << 8)); } }
|
||||
|
||||
/// <summary>Reads a decimal value from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>Reads a double-precision floating-point value from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reads a 16-bit integer from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[SecuritySafeCritical]
|
||||
public short ReadInt16(long position) { unsafe { return InternalRead(position, pointer => (short)(*pointer | *(pointer + 1) << 8)); } }
|
||||
|
||||
/// <summary>Reads a 32-bit integer from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[SecuritySafeCritical]
|
||||
public int ReadInt32(long position) { unsafe { return InternalRead(position, pointer => *pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); } }
|
||||
|
||||
/// <summary>Reads a 64-bit integer from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reads an 8-bit integer from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[SecuritySafeCritical]
|
||||
public sbyte ReadSByte(long position) => unchecked((sbyte)ReadByte(position));
|
||||
|
||||
/// <summary>Reads a single-precision floating-point value from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reads an unsigned 16-bit integer from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[SecuritySafeCritical]
|
||||
public ushort ReadUInt16(long position) { unsafe { return InternalRead(position, pointer => (ushort)(*pointer | *(pointer + 1) << 8)); } }
|
||||
|
||||
/// <summary>Reads an unsigned 32-bit integer from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[SecuritySafeCritical]
|
||||
public uint ReadUInt32(long position) { unsafe { return InternalRead(position, pointer => (uint)(*pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24)); } }
|
||||
|
||||
/// <summary>Reads an unsigned 64-bit integer from the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin reading.</param>
|
||||
/// <returns>The value that was read.</returns>
|
||||
[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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a Boolean value into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
public void Write(long position, bool value) => Write(position, (byte)(value ? 1 : 0));
|
||||
|
||||
/// <summary>Writes a byte value into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
public void Write(long position, byte value)
|
||||
{
|
||||
const int sizeOfType = sizeof(byte);
|
||||
EnsureSafeToWrite(position, sizeOfType);
|
||||
|
||||
InternalWriteByte(position, value);
|
||||
}
|
||||
|
||||
/// <summary>Writes a character into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[SecuritySafeCritical]
|
||||
public void Write(long position, char value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
InternalWrite(position, value, pointer =>
|
||||
{
|
||||
*pointer = (byte)value;
|
||||
*(pointer + 1) = (byte)(value >> 8);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a 16-bit integer into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[SecuritySafeCritical]
|
||||
public void Write(long position, short value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
InternalWrite(position, value, pointer =>
|
||||
{
|
||||
*pointer = (byte)value;
|
||||
*(pointer + 1) = (byte)(value >> 8);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a 32-bit integer into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a 64-bit integer into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a decimal value into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>Writes a Single into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a Double into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes an unsigned 8-bit integer into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[SecuritySafeCritical]
|
||||
public void Write(long position, sbyte value) => Write(position, unchecked((byte)value));
|
||||
|
||||
/// <summary>Writes an unsigned 16-bit integer into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[SecuritySafeCritical]
|
||||
public void Write(long position, ushort value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
InternalWrite(position, value, pointer =>
|
||||
{
|
||||
*pointer = (byte)value;
|
||||
*(pointer + 1) = (byte)(value >> 8);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes an unsigned 32-bit integer into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes an unsigned 64-bit integer into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
[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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a structure into the accessor.</summary>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="structure">The structure to write.</param>
|
||||
[SecurityCritical]
|
||||
public void Write<T>(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);
|
||||
}
|
||||
|
||||
/// <summary>Writes structures from an array of type <typeparamref name="T"/> into the accessor.</summary>
|
||||
/// <typeparam name="T">The type of structure.</typeparam>
|
||||
/// <param name="position">The number of bytes into the accessor at which to begin writing.</param>
|
||||
/// <param name="array">The array to write into the accessor.</param>
|
||||
/// <param name="offset">The index in <paramref name="array"/> to start writing from.</param>
|
||||
/// <param name="count">The number of structures in <paramref name="array"/> to write.</param>
|
||||
[SecurityCritical]
|
||||
public void WriteArray<T>(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);
|
||||
}
|
||||
|
||||
/// <summary>Releases the unmanaged resources used by the UnmanagedMemoryAccessor and optionally releases the managed resources.</summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing) => IsOpen = false;
|
||||
|
||||
/// <summary>Sets the initial values for the accessor.</summary>
|
||||
/// <param name="buffer">The buffer to contain the accessor.</param>
|
||||
/// <param name="offset">The byte at which to start the accessor.</param>
|
||||
/// <param name="capacity">The size, in bytes, of memory to allocate.</param>
|
||||
/// <param name="access">The type of access allowed to the memory. The default is ReadWrite.</param>
|
||||
[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<T>(long position, AlignedPtrReadFunc<T> 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<T>(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
|
|
@ -0,0 +1,818 @@
|
|||
#if NET20 || NET35
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.Extensions;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
/// <summary>Provides access to unmanaged blocks of memory from managed code.</summary>
|
||||
/// <seealso cref="System.IO.Stream"/>
|
||||
public class UnmanagedMemoryStream : Stream
|
||||
{
|
||||
internal bool _isOpen;
|
||||
private FileAccess _access;
|
||||
|
||||
[SecurityCritical] // auto-generated
|
||||
private SafeBuffer _buffer;
|
||||
|
||||
private long _capacity;
|
||||
|
||||
[NonSerialized]
|
||||
private Task<int> _lastReadTask;
|
||||
|
||||
private long _length;
|
||||
|
||||
[SecurityCritical]
|
||||
private unsafe byte* _mem;
|
||||
|
||||
private long _offset;
|
||||
private long _position;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class in a safe buffer with a specified offset and length..
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to contain the unmanaged memory stream.</param>
|
||||
/// <param name="offset">The byte position in the buffer at which to start the unmanaged memory stream.</param>
|
||||
/// <param name="length">The length of the unmanaged memory stream.</param>
|
||||
[SecuritySafeCritical]
|
||||
public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) => Initialize(buffer, offset, length, FileAccess.Read, false);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class in a safe buffer with a specified offset, length, and
|
||||
/// file access.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to contain the unmanaged memory stream.</param>
|
||||
/// <param name="offset">The byte position in the buffer at which to start the unmanaged memory stream.</param>
|
||||
/// <param name="length">The length of the unmanaged memory stream.</param>
|
||||
/// <param name="access">The mode of file access to the unmanaged memory stream.</param>
|
||||
[SecuritySafeCritical]
|
||||
public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) => Initialize(buffer, offset, length, access, false);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class using the specified location and memory length.
|
||||
/// </summary>
|
||||
/// <param name="pointer">A pointer to an unmanaged memory location.</param>
|
||||
/// <param name="length">The length of the memory to use.</param>
|
||||
[SecurityCritical]
|
||||
public unsafe UnmanagedMemoryStream(byte* pointer, long length) => Initialize(pointer, length, length, FileAccess.Read, false);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class using the specified location, memory length, total
|
||||
/// amount of memory, and file access values.
|
||||
/// </summary>
|
||||
/// <param name="pointer">A pointer to an unmanaged memory location.</param>
|
||||
/// <param name="length">The length of the memory to use.</param>
|
||||
/// <param name="capacity">The total amount of memory assigned to the stream.</param>
|
||||
/// <param name="access">One of the <see cref="FileAccess"/> values.</param>
|
||||
[SecurityCritical]
|
||||
public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access) => Initialize(pointer, length, capacity, access, false);
|
||||
|
||||
[SecurityCritical]
|
||||
internal UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) => Initialize(buffer, offset, length, access, skipSecurityCheck);
|
||||
|
||||
[SecurityCritical]
|
||||
internal unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) => Initialize(pointer, length, capacity, access, skipSecurityCheck);
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class.</summary>
|
||||
[SecuritySafeCritical]
|
||||
protected UnmanagedMemoryStream()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
_mem = null;
|
||||
}
|
||||
_isOpen = false;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating whether a stream supports reading.</summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
[Pure]
|
||||
get => _isOpen && (_access & FileAccess.Read) != 0;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating whether a stream supports seeking.</summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
[Pure]
|
||||
get => _isOpen;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating whether a stream supports writing.</summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
[Pure]
|
||||
get => _isOpen && (_access & FileAccess.Write) != 0;
|
||||
}
|
||||
|
||||
/// <summary>Gets the stream length (size) or the total amount of memory assigned to a stream (capacity).</summary>
|
||||
/// <value>The size or capacity of the stream.</value>
|
||||
public long Capacity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
return _capacity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the length of the data in a stream.</summary>
|
||||
/// <value>The length of the data in the stream.</value>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
return Interlocked.Read(ref _length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the position within the current stream.</summary>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanSeek) ErrorStreamIsClosed();
|
||||
Contract.EndContractBlock();
|
||||
return Interlocked.Read(ref _position);
|
||||
}
|
||||
[SecuritySafeCritical]
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
Contract.EndContractBlock();
|
||||
if (!CanSeek) ErrorStreamIsClosed();
|
||||
|
||||
unsafe
|
||||
{
|
||||
// On 32 bit machines, ensure we don't wrap around.
|
||||
if (value > int.MaxValue || _mem + value < _mem)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), ResourceHelper.GetString("ArgumentOutOfRange_StreamLength"));
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref _position, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets a byte pointer to a stream based on the current position in the stream.</summary>
|
||||
/// <value>A byte pointer.</value>
|
||||
public unsafe byte* PositionPointer
|
||||
{
|
||||
[SecurityCritical] // auto-generated_required
|
||||
get
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
|
||||
}
|
||||
|
||||
// Use a temp to avoid a race
|
||||
var pos = Interlocked.Read(ref _position);
|
||||
if (pos > _capacity)
|
||||
throw new IndexOutOfRangeException(ResourceHelper.GetString("IndexOutOfRange_UMSPosition"));
|
||||
var ptr = _mem + pos;
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
return ptr;
|
||||
}
|
||||
[SecurityCritical] // auto-generated_required
|
||||
set
|
||||
{
|
||||
if (_buffer != null)
|
||||
throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
|
||||
if (value < _mem)
|
||||
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
|
||||
|
||||
Interlocked.Exchange(ref _position, value - _mem);
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe byte* Pointer
|
||||
{
|
||||
[SecurityCritical]
|
||||
get
|
||||
{
|
||||
if (_buffer != null)
|
||||
throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
|
||||
|
||||
return _mem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clears all buffers for this stream and causes any buffered data to be written to the underlying device.</summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously clears all buffers for this stream, causes any buffered data to be written to the underlying device, and monitors
|
||||
/// cancellation requests.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.</param>
|
||||
/// <returns>A task that represents the asynchronous flush operation.</returns>
|
||||
[HostProtection(ExternalThreading = true)]
|
||||
[ComVisible(false)]
|
||||
public virtual Task FlushAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return Task.FromCanceled(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
Flush();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream
|
||||
/// by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between
|
||||
/// <paramref name="offset"/> and ( <paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the
|
||||
/// current source.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.
|
||||
/// </param>
|
||||
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
||||
/// <returns>
|
||||
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not
|
||||
/// currently available, or zero (0) if the end of the stream has been reached.
|
||||
/// </returns>
|
||||
[SecuritySafeCritical]
|
||||
public override int Read([In, Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer), 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 (buffer.Length - offset < count)
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
|
||||
Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync
|
||||
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
if (!CanRead) ErrorReadNotSupported();
|
||||
|
||||
// Use a local variable to avoid a race where another thread changes our position after we decide we can read some bytes.
|
||||
var pos = Interlocked.Read(ref _position);
|
||||
var len = Interlocked.Read(ref _length);
|
||||
var n = len - pos;
|
||||
if (n > count)
|
||||
n = count;
|
||||
if (n <= 0)
|
||||
return 0;
|
||||
|
||||
var 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);
|
||||
BufferMemcpy(buffer, offset, pointer + pos + _offset, 0, nInt);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pointer != null)
|
||||
{
|
||||
_buffer.ReleasePointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
BufferMemcpy(buffer, offset, _mem + pos, 0, nInt);
|
||||
}
|
||||
}
|
||||
Interlocked.Exchange(ref _position, pos + n);
|
||||
return nInt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously reads a sequence of bytes from the current stream and advances the position within the stream by the number of
|
||||
/// bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to write the data into.</param>
|
||||
/// <param name="offset">The byte offset in buffer at which to begin writing data from the stream.</param>
|
||||
/// <param name="count">The maximum number of bytes to read.</param>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous read operation. The value of the TResult parameter contains the total number of bytes
|
||||
/// read into the buffer. The result value can be less than the number of bytes requested if the number of bytes currently available
|
||||
/// is less than the requested number, or it can be 0 (zero) if the end of the stream has been reached.
|
||||
/// </returns>
|
||||
[HostProtection(ExternalThreading = true)]
|
||||
[ComVisible(false)]
|
||||
public virtual Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer), 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 (buffer.Length - offset < count)
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
|
||||
Contract.EndContractBlock(); // contract validation copied from Read(...)
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return Task.FromCanceled<int>(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var n = Read(buffer, offset, count);
|
||||
var t = _lastReadTask;
|
||||
return t != null && t.Result == n ? t : _lastReadTask = Task.FromResult(n);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Contract.Assert(!(ex is OperationCanceledException));
|
||||
return Task.FromException<int>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
|
||||
/// </summary>
|
||||
/// <returns>The unsigned byte cast to an Int32, or -1 if at the end of the stream.</returns>
|
||||
[SecuritySafeCritical]
|
||||
public override int ReadByte()
|
||||
{
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
if (!CanRead) ErrorReadNotSupported();
|
||||
|
||||
var pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
|
||||
var 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;
|
||||
}
|
||||
|
||||
/// <summary>When overridden in a derived class, sets the position within the current stream.</summary>
|
||||
/// <param name="offset">A byte offset relative to the origin parameter.</param>
|
||||
/// <param name="loc">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
|
||||
/// <returns>The new position within the current stream.</returns>
|
||||
public override long Seek(long offset, SeekOrigin loc)
|
||||
{
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
switch (loc)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
if (offset < 0)
|
||||
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
|
||||
Interlocked.Exchange(ref _position, offset);
|
||||
break;
|
||||
|
||||
case SeekOrigin.Current:
|
||||
var pos = Interlocked.Read(ref _position);
|
||||
if (offset + pos < 0)
|
||||
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
|
||||
Interlocked.Exchange(ref _position, offset + pos);
|
||||
break;
|
||||
|
||||
case SeekOrigin.End:
|
||||
var len = Interlocked.Read(ref _length);
|
||||
if (len + offset < 0)
|
||||
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
|
||||
Interlocked.Exchange(ref _position, len + offset);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidSeekOrigin"));
|
||||
}
|
||||
|
||||
var finalPos = Interlocked.Read(ref _position);
|
||||
Contract.Assert(finalPos >= 0, "_position >= 0");
|
||||
return finalPos;
|
||||
}
|
||||
|
||||
/// <summary>When overridden in a derived class, sets the length of the current stream.</summary>
|
||||
/// <param name="value">The desired length of the current stream in bytes.</param>
|
||||
[SecuritySafeCritical]
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
if (value < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
Contract.EndContractBlock();
|
||||
if (_buffer != null)
|
||||
throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
if (!CanWrite) ErrorWriteNotSupported();
|
||||
|
||||
if (value > _capacity)
|
||||
throw new IOException(ResourceHelper.GetString("IO.IO_FixedCapacity"));
|
||||
|
||||
var pos = Interlocked.Read(ref _position);
|
||||
var len = Interlocked.Read(ref _length);
|
||||
if (value > len)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
BufferZeroMemory(_mem + len, value - len);
|
||||
}
|
||||
}
|
||||
Interlocked.Exchange(ref _length, value);
|
||||
if (pos > value)
|
||||
{
|
||||
Interlocked.Exchange(ref _position, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within
|
||||
/// this stream by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream.
|
||||
/// </param>
|
||||
/// <param name="count">The number of bytes to be written to the current stream.</param>
|
||||
[SecuritySafeCritical]
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer), 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 (buffer.Length - offset < count)
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
|
||||
Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..)
|
||||
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
if (!CanWrite) ErrorWriteNotSupported();
|
||||
|
||||
var pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
|
||||
var len = Interlocked.Read(ref _length);
|
||||
var n = pos + count;
|
||||
// Check for overflow
|
||||
if (n < 0)
|
||||
throw new IOException(ResourceHelper.GetString("IO.IO_StreamTooLong"));
|
||||
|
||||
if (n > _capacity)
|
||||
{
|
||||
throw new NotSupportedException(ResourceHelper.GetString("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
|
||||
{
|
||||
BufferZeroMemory(_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)
|
||||
{
|
||||
var bytesLeft = _capacity - pos;
|
||||
if (bytesLeft < count)
|
||||
{
|
||||
throw new ArgumentException(ResourceHelper.GetString("Arg_BufferTooSmall"));
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
byte* pointer = null;
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try
|
||||
{
|
||||
_buffer.AcquirePointer(ref pointer);
|
||||
BufferMemcpy(pointer + pos + _offset, 0, buffer, offset, count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pointer != null)
|
||||
{
|
||||
_buffer.ReleasePointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
BufferMemcpy(_mem + pos, 0, buffer, offset, count);
|
||||
}
|
||||
}
|
||||
Interlocked.Exchange(ref _position, n);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number
|
||||
/// of bytes written, and monitors cancellation requests.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to write data from.</param>
|
||||
/// <param name="offset">The zero-based byte offset in buffer from which to begin copying bytes to the stream.</param>
|
||||
/// <param name="count">The maximum number of bytes to write.</param>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.</param>
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
[HostProtection(ExternalThreading = true)]
|
||||
[ComVisible(false)]
|
||||
public virtual Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer), 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 (buffer.Length - offset < count)
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
|
||||
Contract.EndContractBlock(); // contract validation copied from Write(..)
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return Task.FromCanceled(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
Write(buffer, offset, count);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Contract.Assert(!(ex is OperationCanceledException));
|
||||
return Task.FromException<int>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Writes a byte to the current position in the stream and advances the position within the stream by one byte.</summary>
|
||||
/// <param name="value">The byte to write to the stream.</param>
|
||||
[SecuritySafeCritical]
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
if (!_isOpen) ErrorStreamIsClosed();
|
||||
if (!CanWrite) ErrorWriteNotSupported();
|
||||
|
||||
var pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
|
||||
var len = Interlocked.Read(ref _length);
|
||||
var n = pos + 1;
|
||||
if (pos >= len)
|
||||
{
|
||||
// Check for overflow
|
||||
if (n < 0)
|
||||
throw new IOException(ResourceHelper.GetString("IO.IO_StreamTooLong"));
|
||||
|
||||
if (n > _capacity)
|
||||
throw new NotSupportedException(ResourceHelper.GetString("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
|
||||
{
|
||||
BufferZeroMemory(_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);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
internal void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
if (length < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(length), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
if (buffer.ByteLength < (ulong)(offset + length))
|
||||
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidSafeBufferOffLen"));
|
||||
if (access < FileAccess.Read || access > FileAccess.ReadWrite)
|
||||
throw new ArgumentOutOfRangeException(nameof(access));
|
||||
Contract.EndContractBlock();
|
||||
|
||||
if (_isOpen)
|
||||
throw new InvalidOperationException(ResourceHelper.GetString("InvalidOperation_CalledTwice"));
|
||||
if (!skipSecurityCheck)
|
||||
#pragma warning disable 618
|
||||
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
|
||||
#pragma warning restore 618
|
||||
|
||||
// check for wraparound
|
||||
unsafe
|
||||
{
|
||||
byte* pointer = null;
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try
|
||||
{
|
||||
buffer.AcquirePointer(ref pointer);
|
||||
if (pointer + offset + length < pointer)
|
||||
{
|
||||
throw new ArgumentException(ResourceHelper.GetString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pointer != null)
|
||||
{
|
||||
buffer.ReleasePointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_offset = offset;
|
||||
_buffer = buffer;
|
||||
_length = length;
|
||||
_capacity = length;
|
||||
_access = access;
|
||||
_isOpen = true;
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
internal unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck)
|
||||
{
|
||||
if (pointer == null)
|
||||
throw new ArgumentNullException(nameof(pointer));
|
||||
if (length < 0 || capacity < 0)
|
||||
throw new ArgumentOutOfRangeException(length < 0 ? "length" : "capacity", ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
|
||||
if (length > capacity)
|
||||
throw new ArgumentOutOfRangeException(nameof(length), ResourceHelper.GetString("ArgumentOutOfRange_LengthGreaterThanCapacity"));
|
||||
Contract.EndContractBlock();
|
||||
// Check for wraparound.
|
||||
if ((byte*)((long)pointer + capacity) < pointer)
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity), ResourceHelper.GetString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
|
||||
if (access < FileAccess.Read || access > FileAccess.ReadWrite)
|
||||
throw new ArgumentOutOfRangeException(nameof(access), ResourceHelper.GetString("ArgumentOutOfRange_Enum"));
|
||||
if (_isOpen)
|
||||
throw new InvalidOperationException(ResourceHelper.GetString("InvalidOperation_CalledTwice"));
|
||||
|
||||
if (!skipSecurityCheck)
|
||||
#pragma warning disable 618
|
||||
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
|
||||
#pragma warning restore 618
|
||||
|
||||
_mem = pointer;
|
||||
_offset = 0;
|
||||
_length = length;
|
||||
_capacity = capacity;
|
||||
_access = access;
|
||||
_isOpen = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the <see cref="T:System.IO.Stream"/> and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
|
||||
[SecuritySafeCritical]
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the UnmanagedMemoryStream class in a safe buffer with a specified offset, length, and file access.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to contain the unmanaged memory stream.</param>
|
||||
/// <param name="offset">The byte position in the buffer at which to start the unmanaged memory stream.</param>
|
||||
/// <param name="length">The length of the unmanaged memory stream.</param>
|
||||
/// <param name="access">The mode of file access to the unmanaged memory stream.</param>
|
||||
[SecuritySafeCritical]
|
||||
protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) => Initialize(buffer, offset, length, access, false);
|
||||
|
||||
/// <summary>Initializes a new instance of the UnmanagedMemoryStream class by using a pointer to an unmanaged memory location.</summary>
|
||||
/// <param name="pointer">A pointer to an unmanaged memory location.</param>
|
||||
/// <param name="length">The length of the memory to use.</param>
|
||||
/// <param name="capacity">The total amount of memory assigned to the stream.</param>
|
||||
/// <param name="access">One of the <see cref="FileAccess"/> values.</param>
|
||||
[SecurityCritical]
|
||||
protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access) => Initialize(pointer, length, capacity, access, false);
|
||||
|
||||
[SecurityCritical]
|
||||
private static unsafe void BufferMemcpy(byte[] dest, int destIndex, byte* src, int srcIndex, int len)
|
||||
{
|
||||
Contract.Assert(srcIndex >= 0 && destIndex >= 0 && len >= 0, "Index and length must be non-negative!");
|
||||
Contract.Assert(dest.Length - destIndex >= len, "not enough bytes in dest");
|
||||
if (len == 0)
|
||||
return;
|
||||
Marshal.Copy((IntPtr)src, dest, srcIndex, len);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
private static unsafe void BufferMemcpy(byte* dest, int destIndex, byte[] src, int srcIndex, int len)
|
||||
{
|
||||
Contract.Assert(srcIndex >= 0 && destIndex >= 0 && len >= 0, "Index and length must be non-negative!");
|
||||
Contract.Assert(src.Length - srcIndex >= len, "not enough bytes in src");
|
||||
if (len == 0)
|
||||
return;
|
||||
Marshal.Copy(src, srcIndex, (IntPtr)dest, len);
|
||||
}
|
||||
|
||||
private static unsafe void BufferZeroMemory(byte* src, long len) => ((IntPtr)src).FillMemory(0, len);
|
||||
|
||||
private static void ErrorReadNotSupported() => throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UnreadableStream"));
|
||||
|
||||
private static void ErrorStreamIsClosed() => throw new ObjectDisposedException(null, ResourceHelper.GetString("ObjectDisposed_StreamClosed"));
|
||||
|
||||
private static void ErrorWriteNotSupported() => throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UnwritableStream"));
|
||||
}
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue