using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using Vanara.Extensions;
using Vanara.PInvoke;
namespace Vanara.InteropServices
{
/// Method used to pack a list of strings into memory.
public enum StringListPackMethod
{
/// Each string is separated by a single '\0' character and is terminated by two '\0' characters.
Concatenated,
///
/// A contiguous block of memory containing an array of pointers to strings followed by a NULL pointer and then followed by the
/// actual strings.
///
Packed
}
/// Interface to capture unmanaged memory methods.
public interface IMemoryMethods : ISimpleMemoryMethods
{
/// Gets the Ansi allocation method.
/// The secure string.
/// A memory handle.
IntPtr AllocSecureStringAnsi(SecureString secureString);
/// Gets the Unicode allocation method.
/// The secure string.
/// A memory handle.
IntPtr AllocSecureStringUni(SecureString secureString);
/// Gets the Ansi string allocation method.
/// The value.
/// A memory handle.
IntPtr AllocStringAnsi(string value);
/// Gets the Unicode string allocation method.
/// The value.
/// A memory handle.
IntPtr AllocStringUni(string value);
/// Gets the Ansi free method.
/// A memory handle.
void FreeSecureStringAnsi(IntPtr hMem);
/// Gets the Unicode free method.
/// A memory handle.
void FreeSecureStringUni(IntPtr hMem);
/// Gets the reallocation method.
/// A memory handle.
/// The size, in bytes, of memory to allocate.
/// A memory handle.
IntPtr ReAllocMem(IntPtr hMem, int size);
}
/// Interface for classes that support safe memory pointers.
public interface ISafeMemoryHandle : IDisposable
{
/// Gets a value indicating whether the handle value is invalid.
bool IsInvalid { get; }
/// Gets the size of the allocated memory block.
/// The size of the allocated memory block.
SizeT Size { get; set; }
///
/// Adds reference to other SafeMemoryHandle objects, the pointer to which are referred to by this object. This is to ensure that
/// such objects being referred to wouldn't be unreferenced until this object is active. For e.g. when this object is an array of
/// pointers to other objects
///
/// Collection of SafeMemoryHandle objects referred to by this object.
void AddSubReference(IEnumerable children);
/// Returns the instance as an . This is a dangerous call as the value is mutable.
/// An to the internally held memory.
IntPtr DangerousGetHandle();
/// Gets a copy of bytes from the allocated memory block.
/// The start index.
/// The number of bytes to retrieve.
/// A byte array with the copied bytes.
public byte[] GetBytes(int startIndex, int count);
///
/// Extracts an array of structures of containing items. This
/// call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.
///
/// The type of the structures to retrieve.
/// The number of structures to retrieve.
/// The number of bytes to skip before reading the structures.
/// An array of structures of .
T[] ToArray(int count, int prefixBytes = 0);
///
/// Extracts an enumeration of structures of containing items. This call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.
///
/// The type of the structures to retrieve.
/// The number of structures to retrieve.
/// The number of bytes to skip before reading the structures.
/// An enumeration of structures of .
IEnumerable ToEnumerable(int count, int prefixBytes = 0);
/// Returns a that represents this instance.
/// The length.
/// The character set of the string.
/// A that represents this instance.
string ToString(int len, CharSet charSet = CharSet.Unicode);
/// Returns a that represents this instance.
/// The length.
/// Number of bytes preceding the string pointer.
/// The character set of the string.
/// A that represents this instance.
string ToString(int len, int prefixBytes, CharSet charSet = CharSet.Unicode);
///
/// Gets an enumerated list of strings from a block of unmanaged memory where each string is separated by a single '\0' character
/// and is terminated by two '\0' characters.
///
/// The character set of the strings.
/// Number of bytes preceding the array of string pointers.
/// Enumeration of strings.
IEnumerable ToStringEnum(CharSet charSet = CharSet.Auto, int prefixBytes = 0);
///
/// Returns an enumeration of strings from memory where each string is pointed to by a preceding list of pointers of length
/// .
///
/// The count.
/// The character set of the strings.
/// Number of bytes preceding the array of string pointers.
/// An enumerated list of strings.
IEnumerable ToStringEnum(int count, CharSet charSet = CharSet.Auto, int prefixBytes = 0);
///
/// Marshals data from this block of memory to a newly allocated managed object of the type specified by a generic type parameter.
///
/// The type of the object to which the data is to be copied. This must be a structure.
/// Number of bytes preceding the structure.
/// A managed object that contains the data that this holds.
T ToStructure(int prefixBytes = 0);
}
/// Interface to capture unmanaged simple (alloc/free) memory methods.
public interface ISimpleMemoryMethods
{
/// Gets a value indicating whether this memory supports locking.
/// if lockable; otherwise, .
bool Lockable { get; }
/// Gets a handle to a memory allocation of the specified size.
/// The size, in bytes, of memory to allocate.
/// A memory handle.
IntPtr AllocMem(int size);
/// Frees the memory associated with a handle.
/// A memory handle.
void FreeMem(IntPtr hMem);
/// Locks the memory of a specified handle and gets a pointer to it.
/// A memory handle.
/// A pointer to the locked memory.
IntPtr LockMem(IntPtr hMem);
/// Unlocks the memory of a specified handle.
/// A memory handle.
/// if the memory object is still locked after decrementing the lock count; otherwise .
bool UnlockMem(IntPtr hMem);
}
/// Implementation of using just the methods from .
///
public abstract class MemoryMethodsBase : IMemoryMethods
{
/// Gets a value indicating whether this memory supports locking.
/// if lockable; otherwise, .
public virtual bool Lockable => false;
///
/// Gets a handle to a memory allocation of the specified size.
///
/// The size, in bytes, of memory to allocate.
///
/// A memory handle.
///
///
public abstract IntPtr AllocMem(int size);
/// Gets the Ansi allocation method.
/// The secure string.
/// A memory handle.
public virtual IntPtr AllocSecureStringAnsi(SecureString secureString) => StringHelper.AllocSecureString(secureString, CharSet.Ansi, AllocMem);
/// Gets the Unicode allocation method.
/// The secure string.
/// A memory handle.
public virtual IntPtr AllocSecureStringUni(SecureString secureString) => StringHelper.AllocSecureString(secureString, CharSet.Unicode, AllocMem);
/// Gets the Ansi string allocation method.
/// The value.
/// A memory handle.
public virtual IntPtr AllocStringAnsi(string value) => StringHelper.AllocString(value, CharSet.Ansi, AllocMem);
/// Gets the Unicode string allocation method.
/// The value.
/// A memory handle.
public virtual IntPtr AllocStringUni(string value) => StringHelper.AllocString(value, CharSet.Unicode, AllocMem);
/// Frees the memory associated with a handle.
/// A memory handle.
public abstract void FreeMem(IntPtr hMem);
/// Gets the Ansi free method.
/// A memory handle.
public virtual void FreeSecureStringAnsi(IntPtr hMem) => FreeMem(hMem);
/// Gets the Unicode free method.
/// A memory handle.
public virtual void FreeSecureStringUni(IntPtr hMem) => FreeMem(hMem);
/// Locks the memory of a specified handle and gets a pointer to it.
/// A memory handle.
/// A pointer to the locked memory.
public virtual IntPtr LockMem(IntPtr hMem) => hMem;
/// Gets the reallocation method.
/// A memory handle.
/// The size, in bytes, of memory to allocate.
/// A memory handle.
public virtual IntPtr ReAllocMem(IntPtr hMem, int size)
{
var hNew = AllocMem(size);
hMem.CopyTo(hNew, size);
FreeMem(hMem);
return hNew;
}
/// Unlocks the memory of a specified handle.
/// A memory handle.
public virtual bool UnlockMem(IntPtr hMem) => false;
}
///
/// Abstract base class for all SafeHandle derivatives that encapsulate handling unmanaged memory. This class assumes read-only memory.
///
///
public abstract class SafeAllocatedMemoryHandleBase : SafeHandle
{
/// Initializes a new instance of the class.
/// The handle.
/// if set to true if this class is responsible for freeing the memory on disposal.
protected SafeAllocatedMemoryHandleBase(IntPtr handle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle) => SetHandle(handle);
#if DEBUG
/// Dumps memory to byte string.
[ExcludeFromCodeCoverage]
public string Dump => Size == 0 ? "" : string.Join(" ", GetBytes(0, Size).Select(b => b.ToString("X2")).ToArray());
#endif
/// Gets a value indicating whether this memory supports locking.
/// if lockable; otherwise, .
public virtual bool Lockable => false;
/// Gets or sets the size in bytes of the allocated memory block.
/// The size in bytes of the allocated memory block.
public abstract SizeT Size { get; set; }
/// Performs an explicit conversion from to pointer.
/// The instance.
/// The result of the conversion.
public static unsafe explicit operator byte*(SafeAllocatedMemoryHandleBase hMem) => (byte*)hMem.handle;
/// Performs an explicit conversion from to .
/// The instance.
/// The result of the conversion.
public static explicit operator SafeBuffer(SafeAllocatedMemoryHandleBase hMem) => new SafeBufferImpl(hMem);
/// Performs an implicit conversion from to .
/// The instance.
/// The result of the conversion.
public static implicit operator IntPtr(SafeAllocatedMemoryHandleBase hMem) => hMem.handle;
#if ALLOWSPAN
/// Creates a new span over this allocated memory.
/// The span representation of the structure.
public virtual ReadOnlySpan AsReadOnlySpan(int length) => handle.AsReadOnlySpan(length, 0, Size);
/// Creates a new span over this allocated memory.
/// The span representation of the structure.
public virtual Span AsSpan(int length) => handle.AsSpan(length, 0, Size);
/// Casts this allocated memory to a Span<Byte>.
/// A span of type .
public virtual Span AsBytes() => AsSpan(Size);
#endif
/// Locks this instance.
public virtual void Lock()
{
}
/// Decrements the lock count.
/// if the memory object is still locked after decrementing the lock count; otherwise .
public virtual bool Unlock() => false;
/// Releases the owned handle without releasing the allocated memory and returns a pointer to the current memory.
/// A pointer to the currently allocated memory. The caller now has the responsibility to free this memory.
public virtual IntPtr TakeOwnership()
{
while (Unlock()) ;
var h = handle;
SetHandleAsInvalid();
handle = IntPtr.Zero;
Size = 0;
return h;
}
/// Gets a copy of bytes from the allocated memory block.
/// A byte array with the copied bytes.
public byte[] GetBytes() => GetBytes(0, Size);
/// Gets a copy of bytes from the allocated memory block.
/// The start index.
/// The number of bytes to retrieve.
/// A byte array with the copied bytes.
public byte[] GetBytes(int startIndex, int count)
{
if (startIndex < 0 || startIndex + count > Size) throw new ArgumentOutOfRangeException(nameof(startIndex));
var ret = new byte[count];
CallLocked(p => Marshal.Copy(p.Offset(startIndex), ret, 0, count));
return ret;
}
/// Runs a delegate method while locking the memory.
/// The action to perform while memory is locked.
protected void CallLocked(Action action)
{
if (!Lockable)
action.Invoke(handle);
else
{
try { Lock(); action.Invoke(handle); }
finally { Unlock(); }
}
}
/// Runs a delegate method while locking the memory.
/// The action to perform while memory is locked.
protected TOut CallLocked(Func action)
{
if (!Lockable)
return action.Invoke(handle);
try { Lock(); return action.Invoke(handle); }
finally { Unlock(); }
}
private class SafeBufferImpl : SafeBuffer
{
public SafeBufferImpl(SafeAllocatedMemoryHandleBase hMem) : base(false) => Initialize((ulong)hMem.Size);
protected override bool ReleaseHandle() => true;
}
}
/// Abstract base class for all SafeHandle derivatives that encapsulate handling unmanaged memory.
///
public abstract class SafeAllocatedMemoryHandle : SafeAllocatedMemoryHandleBase
{
/// Initializes a new instance of the class.
/// The handle.
/// if set to true if this class is responsible for freeing the memory on disposal.
protected SafeAllocatedMemoryHandle(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle) { }
/// Fills the allocated memory with a specific byte value.
/// The byte value.
public virtual void Fill(byte value) => Fill(value, Size);
/// Fills the allocated memory with a specific byte value.
/// The byte value.
/// The number of bytes in the block of memory to be filled.
public virtual void Fill(byte value, int length)
{
if (length > Size) throw new ArgumentOutOfRangeException(nameof(length));
CallLocked(p => p.FillMemory(value, length));
}
/// Zero out all allocated memory.
public virtual void Zero() => Fill(0, Size);
}
/// Abstract base class for all SafeAllocatedMemoryHandle derivatives that apply a specific memory handling routine set.
/// The implementation.
public abstract class SafeMemoryHandle : SafeAllocatedMemoryHandle where TMem : IMemoryMethods, new()
{
/// The implementation instance.
protected static readonly TMem mm = new();
/// The number of bytes currently allocated.
protected SizeT sz;
private IntPtr unlockedHandle;
/// Initializes a new instance of the class.
/// The size of memory to allocate, in bytes.
/// size - The value of this argument must be non-negative
protected SafeMemoryHandle(SizeT size = default) : base(IntPtr.Zero, true)
{
if (size == 0) return;
InitFromSize(size);
Zero();
}
/// Initializes a new instance of the class.
/// The handle.
/// The size of memory allocated to the handle, in bytes.
/// if set to true if this class is responsible for freeing the memory on disposal.
protected SafeMemoryHandle(IntPtr handle, SizeT size, bool ownsHandle) : base(handle, ownsHandle) => sz = size;
///
/// Allocates from unmanaged memory to represent an array of pointers and marshals the unmanaged pointers (IntPtr) to the native
/// array equivalent.
///
/// Array of unmanaged pointers
/// SafeHGlobalHandle object to an native (unmanaged) array of pointers
protected SafeMemoryHandle(byte[] bytes) : base(IntPtr.Zero, true)
{
if ((bytes?.Length ?? 0) == 0) return;
InitFromSize(bytes.Length);
CallLocked(p => Marshal.Copy(bytes, 0, p, bytes.Length));
}
///
/// Initializes a new instance of the class from a
/// instance, copying all the memory.
///
/// The source memory block.
protected SafeMemoryHandle(SafeAllocatedMemoryHandle source) : base(IntPtr.Zero, true)
{
if (source is null) return;
InitFromSize(source.Size);
CallLocked(p => ((IntPtr)source).CopyTo(p, source.Size));
}
/// When overridden in a derived class, gets a value indicating whether the handle value is invalid.
public override bool IsInvalid => handle == IntPtr.Zero;
/// Gets a value indicating whether this memory supports locking.
/// if lockable; otherwise, .
public override bool Lockable => mm.Lockable;
/// Gets or sets the size in bytes of the allocated memory block.
/// The size in bytes of the allocated memory block.
public override SizeT Size
{
get => sz;
set
{
if (value == 0)
{
ReleaseHandle();
}
else
{
RuntimeHelpers.PrepareConstrainedRegions();
handle = IsInvalid ? mm.AllocMem(value) : mm.ReAllocMem(handle, value);
if (value > sz)
handle.Offset(sz).FillMemory(0, value - sz);
sz = value;
}
}
}
/// Locks this instance.
public override void Lock()
{
if (!Lockable) return;
if (unlockedHandle == default)
{
var hlocked = mm.LockMem(handle);
if (hlocked != handle)
{
unlockedHandle = handle;
SetHandle(hlocked);
}
}
else
mm.LockMem(unlockedHandle);
}
/// Decrements the lock count.
/// if the memory object is still locked after decrementing the lock count; otherwise .
public override bool Unlock()
{
if (!Lockable || unlockedHandle == default)
return false;
var stillLocked = mm.UnlockMem(unlockedHandle);
if (!stillLocked)
{
SetHandle(unlockedHandle);
unlockedHandle = default;
}
return stillLocked;
}
/// When overridden in a derived class, executes the code required to free the handle.
///
/// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it
/// generates a releaseHandleFailed MDA Managed Debugging Assistant.
///
protected override bool ReleaseHandle()
{
while (Unlock()) ;
mm.FreeMem(handle);
sz = 0;
handle = IntPtr.Zero;
return true;
}
private void InitFromSize(SizeT size)
{
RuntimeHelpers.PrepareConstrainedRegions();
SetHandle(mm.AllocMem(sz = size));
}
}
/// A for memory allocated via COM.
///
public abstract class SafeMemoryHandleExt : SafeMemoryHandle, ISafeMemoryHandle where TMem : IMemoryMethods, new()
{
///
/// Maintains reference to other SafeMemoryHandleExt objects, the pointer to which are referred to by this object. This is to ensure
/// that such objects being referred to wouldn't be unreferenced until this object is active.
///
private List references;
/// Initializes a new instance of the class.
/// The size of memory to allocate, in bytes.
/// size - The value of this argument must be non-negative
protected SafeMemoryHandleExt(SizeT size) : base(size) { }
/// Initializes a new instance of the class.
/// The handle.
/// The size of memory allocated to the handle, in bytes.
/// if set to true if this class is responsible for freeing the memory on disposal.
protected SafeMemoryHandleExt(IntPtr handle, SizeT size, bool ownsHandle) : base(handle, size, ownsHandle) { }
///
/// Allocates from unmanaged memory to represent an array of pointers and marshals the unmanaged pointers (IntPtr) to the native
/// array equivalent.
///
/// Array of unmanaged pointers
/// SafeHGlobalHandle object to an native (unmanaged) array of pointers
protected SafeMemoryHandleExt(byte[] bytes) : base(bytes) { }
///
/// Allocates from unmanaged memory to represent an array of pointers and marshals the unmanaged pointers (IntPtr) to the native
/// array equivalent.
///
/// Array of unmanaged pointers
/// SafeMemoryHandleExt object to an native (unmanaged) array of pointers
protected SafeMemoryHandleExt(IntPtr[] values) : this(IntPtr.Size * values.Length) => CallLocked(p => Marshal.Copy(values, 0, p, values.Length));
/// Allocates from unmanaged memory to represent a Unicode string (WSTR) and marshal this to a native PWSTR.
/// The string value.
/// The character set of the string.
/// SafeMemoryHandleExt object to an native (unmanaged) string
protected SafeMemoryHandleExt(string s, CharSet charSet = CharSet.Unicode) : base(IntPtr.Zero, s.GetByteCount(true, charSet), true)
{
RuntimeHelpers.PrepareConstrainedRegions();
SetHandle(StringHelper.GetCharSize(charSet) == 2 ? mm.AllocStringUni(s) : mm.AllocStringAnsi(s));
}
///
/// Initializes a new instance of the class from a
/// instance, copying all the memory.
///
/// The source memory block.
protected SafeMemoryHandleExt(SafeAllocatedMemoryHandle source) : base(source) { }
///
/// Adds reference to other SafeMemoryHandle objects, the pointer to which are referred to by this object. This is to ensure that
/// such objects being referred to wouldn't be unreferenced until this object is active. For e.g. when this object is an array of
/// pointers to other objects
///
/// Collection of SafeMemoryHandle objects referred to by this object.
public void AddSubReference(IEnumerable children)
{
references ??= new List();
references.AddRange(children);
}
///
/// Extracts an array of structures of containing items. This
/// call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.
///
/// The type of the structures to retrieve.
/// The number of structures to retrieve.
/// The number of bytes to skip before reading the structures.
/// An array of structures of .
public T[] ToArray(int count, int prefixBytes = 0)
{
if (IsInvalid) return null;
//if (Size < Marshal.SizeOf(typeof(T)) * count + prefixBytes)
// throw new InsufficientMemoryException("Requested array is larger than the memory allocated.");
//if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
//Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
return CallLocked(p => p.ToArray(count, prefixBytes, sz));
}
///
/// Extracts an enumeration of structures of containing items. This call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.
///
/// The type of the structures to retrieve.
/// The number of structures to retrieve.
/// The number of bytes to skip before reading the structures.
/// An enumeration of structures of .
public IEnumerable ToEnumerable(int count, int prefixBytes = 0)
{
if (IsInvalid) yield break;
//if (Size < Marshal.SizeOf(typeof(T)) * count + prefixBytes)
// throw new InsufficientMemoryException("Requested array is larger than the memory allocated.");
//if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
//Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
try
{
Lock();
foreach (var i in handle.ToIEnum(count, prefixBytes, sz))
yield return i;
}
finally
{
Unlock();
}
}
/// Returns a that represents this instance.
/// The length.
/// The character set of the string.
/// A that represents this instance.
public string ToString(int len, CharSet charSet = CharSet.Unicode) => ToString(len, 0, charSet);
/// Returns a that represents this instance.
/// The length.
/// Number of bytes preceding the string pointer.
/// The character set of the string.
/// A that represents this instance.
public string ToString(int len, int prefixBytes, CharSet charSet = CharSet.Unicode)
{
var str = CallLocked(p => StringHelper.GetString(p.Offset(prefixBytes), charSet, sz == 0 ? long.MaxValue : sz - prefixBytes));
return len == -1 ? str : str.Substring(0, Math.Min(len, str.Length));
}
///
/// Returns an enumeration of strings from memory where each string is pointed to by a preceding list of pointers of length
/// .
///
/// The count of expected strings.
/// The character set of the strings.
/// Number of bytes preceding the array of string pointers.
/// Enumeration of strings.
public IEnumerable ToStringEnum(int count, CharSet charSet = CharSet.Auto, int prefixBytes = 0) =>
IsInvalid ? new string[0] : CallLocked(p => p.ToStringEnum(count, charSet, prefixBytes, sz));
///
/// Gets an enumerated list of strings from a block of unmanaged memory where each string is separated by a single '\0' character
/// and is terminated by two '\0' characters.
///
/// The character set of the strings.
/// Number of bytes preceding the array of string pointers.
/// An enumerated list of strings.
public IEnumerable ToStringEnum(CharSet charSet = CharSet.Auto, int prefixBytes = 0) =>
IsInvalid ? new string[0] : CallLocked(p => p.ToStringEnum(charSet, prefixBytes, sz));
///
/// Marshals data from this block of memory to a newly allocated managed object of the type specified by a generic type parameter.
///
/// The type of the object to which the data is to be copied. This must be a structure.
/// Number of bytes preceding the structure.
/// A managed object that contains the data that this holds.
public T ToStructure(int prefixBytes = 0)
{
if (IsInvalid) return default;
return CallLocked(p => p.ToStructure(sz, prefixBytes));
}
/// Marshals data from a managed list of specified type to an offset within this allocated memory.
///
/// A type of the enumerated managed object that holds the data to be marshaled. The object must be a structure or an instance of a
/// formatted class.
///
/// The enumerated list of items to marshal.
///
/// if set to true automatically extend the allocated memory to the size required to hold .
///
/// The number of bytes to skip before writing the first element of .
public void Write(IEnumerable items, bool autoExtend = true, int offset = 0)
{
if (IsInvalid) throw new MemberAccessException("Safe memory pointer is not valid.");
if (autoExtend)
{
var count = items?.Count() ?? 0;
if (count == 0) return;
InteropExtensions.TrueType(typeof(T), out var iSz);
var reqSz = iSz * count + offset;
if (sz < reqSz)
Size = reqSz;
}
CallLocked(p => p.Write(items, offset, sz));
}
/// Writes the specified value to an offset within this allocated memory.
/// The type of the value to write.
/// The value to write.
///
/// if set to true automatically extend the allocated memory to the size required to hold .
///
/// The number of bytes to offset from the beginning of this allocated memory before writing.
public void Write(in T value, bool autoExtend = true, int offset = 0) where T : struct
{
if (IsInvalid) throw new MemberAccessException("Safe memory pointer is not valid.");
if (autoExtend)
{
InteropExtensions.TrueType(typeof(T), out var iSz);
var reqSz = iSz + offset;
if (sz < reqSz)
Size = reqSz;
}
try { Lock(); handle.Write(value, offset, sz); }
finally { Unlock(); }
}
/// Writes the specified value to an offset within this allocated memory.
/// The value to write.
///
/// if set to true automatically extend the allocated memory to the size required to hold .
///
/// The number of bytes to offset from the beginning of this allocated memory before writing.
public void Write(object value, bool autoExtend = true, int offset = 0)
{
if (IsInvalid) throw new MemberAccessException("Safe memory pointer is not valid.");
if (value is null) return;
if (autoExtend)
{
InteropExtensions.TrueType(value.GetType(), out var iSz);
var reqSz = iSz + offset;
if (sz < reqSz)
Size = reqSz;
}
CallLocked(p => p.Write(value, offset, sz));
}
/// When overridden in a derived class, executes the code required to free the handle.
///
/// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it
/// generates a releaseHandleFailed MDA Managed Debugging Assistant.
///
protected override bool ReleaseHandle()
{
var released = base.ReleaseHandle();
handle = IntPtr.Zero;
return released;
}
#if ALLOWSPAN
/// Gets a reference to a structure based on this allocated memory.
/// A referenced structure.
public virtual ref T AsRef() => ref MemoryMarshal.GetReference(AsSpan(1));
#endif
}
}