diff --git a/Core/BkwdComp/SafeBuffer.PreNet40.cs b/Core/BkwdComp/SafeBuffer.PreNet40.cs
new file mode 100644
index 00000000..f10bb219
--- /dev/null
+++ b/Core/BkwdComp/SafeBuffer.PreNet40.cs
@@ -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
+{
+ ///
+ /// 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.
+ ///
+ ///
+ [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;
+
+ ///
+ ///
+ /// Creates a new instance of the class, and specifies whether the buffer handle is to be reliably released.
+ ///
+ ///
+ /// to reliably release the handle during the finalization phase; to prevent reliable
+ /// release (not recommended).
+ ///
+ protected SafeBuffer(bool ownsHandle) : base(ownsHandle) => numBytes = Uninitialized;
+
+ /// Gets the size of the buffer, in bytes.
+ public ulong ByteLength
+ {
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ get => numBytes != Uninitialized ? (ulong)numBytes : throw NotInitialized();
+ }
+
+ /// Obtains a pointer from a object for a block of memory.
+ ///
+ /// A byte pointer, passed by reference, to receive the pointer from within the object. You must set this
+ /// pointer to before you call this method.
+ ///
+ [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;
+ }
+ }
+
+ ///
+ /// Defines the allocation size of the memory region in bytes. You must call this method before you use the SafeBuffer instance.
+ ///
+ /// The number of bytes in the buffer.
+ 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;
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The number of elements in the buffer.
+ /// The size of each element in the buffer.
+ 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));
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The value type to allocate memory for.
+ /// The number of elements of the value type to allocate memory for.
+ public void Initialize(uint numElements) where T : struct => Initialize(numElements, (uint)Marshal.SizeOf(typeof(T)));
+
+ /// Reads a value type from memory at the specified offset.
+ /// The value type to read.
+ /// The location from which to read the value type. You may have to consider alignment issues.
+ /// The value type that was read from memory.
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public T Read(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;
+ }
+
+ ///
+ /// Reads the specified number of value types from memory starting at the offset, and writes them into an array starting at the index.
+ ///
+ /// The value type to read.
+ /// The location from which to start reading.
+ /// The output array to write to.
+ /// The location in the output array to begin writing to.
+ /// The number of value types to read from the input array and to write to the output array.
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public void ReadArray(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();
+ }
+ }
+
+ /// Releases a pointer that was obtained by the method.
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public void ReleasePointer()
+ {
+ if (numBytes == Uninitialized)
+ throw NotInitialized();
+
+ DangerousRelease();
+ }
+
+ /// Writes a value type to memory at the given location.
+ /// The value type to write.
+ /// The location at which to start writing. You may have to consider alignment issues.
+ /// The value to write.
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public void Write(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();
+ }
+ }
+
+ ///
+ /// Writes the specified number of value types to a memory location by reading bytes starting from the specified location in the
+ /// input array.
+ ///
+ /// The value type to write.
+ /// The location in memory to write to.
+ /// The input array.
+ /// The offset in the array to start reading from.
+ /// The number of value types to write.
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public void WriteArray(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(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(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
\ No newline at end of file
diff --git a/Core/BkwdComp/UnmanagedMemoryAccessor.PreNet40.cs b/Core/BkwdComp/UnmanagedMemoryAccessor.PreNet40.cs
new file mode 100644
index 00000000..2f3e41b6
--- /dev/null
+++ b/Core/BkwdComp/UnmanagedMemoryAccessor.PreNet40.cs
@@ -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
+{
+ ///
+ /// 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
\ No newline at end of file
diff --git a/Core/BkwdComp/UnmanagedMemoryStream.PreNet40.cs b/Core/BkwdComp/UnmanagedMemoryStream.PreNet40.cs
new file mode 100644
index 00000000..0062ddcf
--- /dev/null
+++ b/Core/BkwdComp/UnmanagedMemoryStream.PreNet40.cs
@@ -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
+{
+ /// Provides access to unmanaged blocks of memory from managed code.
+ ///
+ public class UnmanagedMemoryStream : Stream
+ {
+ internal bool _isOpen;
+ private FileAccess _access;
+
+ [SecurityCritical] // auto-generated
+ private SafeBuffer _buffer;
+
+ private long _capacity;
+
+ [NonSerialized]
+ private Task _lastReadTask;
+
+ private long _length;
+
+ [SecurityCritical]
+ private unsafe byte* _mem;
+
+ private long _offset;
+ private long _position;
+
+ ///
+ /// Initializes a new instance of the class in a safe buffer with a specified offset and length..
+ ///
+ /// The buffer to contain the unmanaged memory stream.
+ /// The byte position in the buffer at which to start the unmanaged memory stream.
+ /// The length of the unmanaged memory stream.
+ [SecuritySafeCritical]
+ public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) => Initialize(buffer, offset, length, FileAccess.Read, false);
+
+ ///
+ /// Initializes a new instance of the class in a safe buffer with a specified offset, length, and
+ /// file access.
+ ///
+ /// The buffer to contain the unmanaged memory stream.
+ /// The byte position in the buffer at which to start the unmanaged memory stream.
+ /// The length of the unmanaged memory stream.
+ /// The mode of file access to the unmanaged memory stream.
+ [SecuritySafeCritical]
+ public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) => Initialize(buffer, offset, length, access, false);
+
+ ///
+ /// Initializes a new instance of the class using the specified location and memory length.
+ ///
+ /// A pointer to an unmanaged memory location.
+ /// The length of the memory to use.
+ [SecurityCritical]
+ public unsafe UnmanagedMemoryStream(byte* pointer, long length) => Initialize(pointer, length, length, FileAccess.Read, false);
+
+ ///
+ /// Initializes a new instance of the class using the specified location, memory length, total
+ /// amount of memory, and file access values.
+ ///
+ /// A pointer to an unmanaged memory location.
+ /// The length of the memory to use.
+ /// The total amount of memory assigned to the stream.
+ /// One of the values.
+ [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);
+
+ /// Initializes a new instance of the class.
+ [SecuritySafeCritical]
+ protected UnmanagedMemoryStream()
+ {
+ unsafe
+ {
+ _mem = null;
+ }
+ _isOpen = false;
+ }
+
+ /// Gets a value indicating whether a stream supports reading.
+ public override bool CanRead
+ {
+ [Pure]
+ get => _isOpen && (_access & FileAccess.Read) != 0;
+ }
+
+ /// Gets a value indicating whether a stream supports seeking.
+ public override bool CanSeek
+ {
+ [Pure]
+ get => _isOpen;
+ }
+
+ /// Gets a value indicating whether a stream supports writing.
+ public override bool CanWrite
+ {
+ [Pure]
+ get => _isOpen && (_access & FileAccess.Write) != 0;
+ }
+
+ /// Gets the stream length (size) or the total amount of memory assigned to a stream (capacity).
+ /// The size or capacity of the stream.
+ public long Capacity
+ {
+ get
+ {
+ if (!_isOpen) ErrorStreamIsClosed();
+ return _capacity;
+ }
+ }
+
+ /// Gets the length of the data in a stream.
+ /// The length of the data in the stream.
+ public override long Length
+ {
+ get
+ {
+ if (!_isOpen) ErrorStreamIsClosed();
+ return Interlocked.Read(ref _length);
+ }
+ }
+
+ /// Gets or sets the position within the current stream.
+ 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);
+ }
+ }
+
+ /// Gets or sets a byte pointer to a stream based on the current position in the stream.
+ /// A byte pointer.
+ 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;
+ }
+ }
+
+ /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
+ public override void Flush()
+ {
+ if (!_isOpen) ErrorStreamIsClosed();
+ }
+
+ ///
+ /// Asynchronously clears all buffers for this stream, causes any buffered data to be written to the underlying device, and monitors
+ /// cancellation requests.
+ ///
+ /// The token to monitor for cancellation requests. The default value is None.
+ /// A task that represents the asynchronous flush operation.
+ [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);
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between
+ /// and ( + - 1) replaced by the bytes read from the
+ /// current source.
+ ///
+ ///
+ /// The zero-based byte offset in at which to begin storing the data read from the current stream.
+ ///
+ /// The maximum number of bytes to be read from the current stream.
+ ///
+ /// 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.
+ ///
+ [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;
+ }
+
+ ///
+ /// Asynchronously reads a sequence of bytes from the current stream and advances the position within the stream by the number of
+ /// bytes read.
+ ///
+ /// The buffer to write the data into.
+ /// The byte offset in buffer at which to begin writing data from the stream.
+ /// The maximum number of bytes to read.
+ /// The token to monitor for cancellation requests. The default value is None.
+ ///
+ /// 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.
+ ///
+ [HostProtection(ExternalThreading = true)]
+ [ComVisible(false)]
+ public virtual Task 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(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(ex);
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The unsigned byte cast to an Int32, or -1 if at the end of the stream.
+ [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;
+ }
+
+ /// When overridden in a derived class, sets the position within the current stream.
+ /// A byte offset relative to the origin parameter.
+ /// A value of type SeekOrigin indicating the reference point used to obtain the new position.
+ /// The new position within the current stream.
+ 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;
+ }
+
+ /// When overridden in a derived class, sets the length of the current stream.
+ /// The desired length of the current stream in bytes.
+ [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);
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// An array of bytes. This method copies bytes from to the current stream.
+ ///
+ ///
+ /// The zero-based byte offset in at which to begin copying bytes to the current stream.
+ ///
+ /// The number of bytes to be written to the current stream.
+ [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);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The buffer to write data from.
+ /// The zero-based byte offset in buffer from which to begin copying bytes to the stream.
+ /// The maximum number of bytes to write.
+ /// The token to monitor for cancellation requests. The default value is None.
+ /// A task that represents the asynchronous write operation.
+ [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(ex);
+ }
+ }
+
+ /// Writes a byte to the current position in the stream and advances the position within the stream by one byte.
+ /// The byte to write to the stream.
+ [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;
+ }
+
+ ///
+ /// Releases the unmanaged resources used by the and optionally releases the managed resources.
+ ///
+ /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ [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);
+ }
+
+ ///
+ /// Initializes a new instance of the UnmanagedMemoryStream class in a safe buffer with a specified offset, length, and file access.
+ ///
+ /// The buffer to contain the unmanaged memory stream.
+ /// The byte position in the buffer at which to start the unmanaged memory stream.
+ /// The length of the unmanaged memory stream.
+ /// The mode of file access to the unmanaged memory stream.
+ [SecuritySafeCritical]
+ protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) => Initialize(buffer, offset, length, access, false);
+
+ /// Initializes a new instance of the UnmanagedMemoryStream class by using a pointer to an unmanaged memory location.
+ /// A pointer to an unmanaged memory location.
+ /// The length of the memory to use.
+ /// The total amount of memory assigned to the stream.
+ /// One of the values.
+ [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
\ No newline at end of file