#if NET20 || NET35
using Microsoft.Win32.SafeHandles;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using Vanara.Extensions;
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;
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"));
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) => Initialize((ulong)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, AlignedSizeOf());
/// 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 => handle.ToStructure(numBytes.ToUInt64(), (int)byteOffset);
///
/// 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"));
if (numBytes == Uninitialized)
throw NotInitialized();
Array.Copy(handle.ToArray(count, (int)byteOffset, numBytes.ToUInt64()), 0, array, index, count);
}
/// 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();
handle.Write(value, (int)byteOffset, numBytes.ToUInt64());
}
///
/// 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"));
if (numBytes == Uninitialized)
throw NotInitialized();
handle.Write(array, (int)byteOffset, numBytes.ToUInt64());
}
[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() => new InvalidOperationException(ResourceHelper.GetString("InvalidOperation_MustCallInitialize"));
internal static uint AlignedSizeOf()
{
var size = SizeOf();
return (size == 1 || size == 2) ? size : (uint)((size + 3) & (~3));
}
internal static uint SizeOf() => (uint)Marshal.SizeOf(typeof(T));
}
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