using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.PInvoke;
#if ALLOWSPAN
using System.Buffers;
#endif
namespace Vanara.InteropServices
{
/// Base abstract class for a structure handler based on .
/// The type of the structure.
/// The type of the memory.
///
public abstract class SafeMemStruct : SafeMemoryHandle, IEquatable where TMem : IMemoryMethods, new() where TStruct : struct
{
/// The structure size, in bytes, of TStruct.
protected static readonly SizeT BaseStructSize = InteropExtensions.SizeOf();
/// Initializes a new instance of the class.
/// The TStruct value.
/// The capacity of the buffer, in bytes.
protected SafeMemStruct(in TStruct s, SizeT capacity = default) : base(Math.Max(BaseStructSize, (ulong)capacity)) => handle.Write(s);
/// Initializes a new instance of the class.
/// The capacity of the buffer, in bytes.
protected SafeMemStruct(SizeT capacity = default) : base(Math.Max(BaseStructSize, (ulong)capacity)) { }
/// Initializes a new instance of the class.
/// The PTR.
/// true to reliably release the handle during finalization; false to prevent it.
/// The number of bytes allocated to .
[ExcludeFromCodeCoverage]
protected SafeMemStruct(IntPtr ptr, bool ownsHandle = true, SizeT allocatedBytes = default) : base(ptr, allocatedBytes, ownsHandle) { }
/// Gets a value indicating whether the current memory has a valid value of its underlying type.
/// if this instance has a value; otherwise, .
public bool HasValue => !IsClosed && !IsInvalid;
///
/// Gets or sets the value of the current object if it has been assigned a valid
/// underlying value.
///
///
/// The value of the current object if the HasValue property is true. An exception is
/// thrown if the HasValue property is false.
///
/// The HasValue property is false.
public TStruct Value
{
get => HasValue ? handle.ToStructure(Size) : throw new InvalidOperationException("The HasValue property is false.");
set => _ = HasValue ? handle.Write(value, 0, Size) : throw new InvalidOperationException("The HasValue property is false.");
}
/// Returns the TStruct value held by a .
/// The instance.
///
/// A nullable value held by the or null if the handle or value is invalid.
///
public static explicit operator TStruct?(SafeMemStruct s) => s is null || !s.HasValue ? (TStruct?)null : s.Value;
/// Performs an explicit conversion from to .
/// The instance.
/// The result of the conversion.
/// Cannot convert an ANSI string to a Char pointer.
public static unsafe explicit operator void*(SafeMemStruct s) => (void*)s.handle;
/// Returns the value of the field.
/// The instance.
///
/// An representing the value of the handle field. If the handle has been marked invalid with , this method still returns the original handle value, which can be a stale value.
///
public static implicit operator IntPtr(SafeMemStruct s) => s.DangerousGetHandle();
/// Returns the TStruct value held by a .
/// The instance.
///
/// The structure value held by the or an
/// exception if the handle or value is invalid.
///
public static implicit operator TStruct(SafeMemStruct s) => !(s is null) ? s.Value : throw new ArgumentNullException(nameof(s));
/// Appends the specified bytes to the end of the allocated memory for this structure, expanding the allocation to fit the byte array.
/// The bytes.
/// A pointer to the copied bytes in memory.
public virtual IntPtr Append(byte[] bytes)
{
var sz = Size;
Size += bytes.Length;
Marshal.Copy(bytes, 0, handle.Offset(sz), bytes.Length);
return handle.Offset(sz);
}
/// Appends the specified memory to the end of the allocated memory for this structure, expanding the allocation to fit the added memory.
/// The memory to append.
/// A pointer to the copied memory.
public virtual IntPtr Append(SafeAllocatedMemoryHandleBase mem)
{
var sz = Size;
Size += mem.Size;
((IntPtr)mem).CopyTo(handle.Offset(sz), mem.Size);
return handle.Offset(sz);
}
/// Appends the specified object to the end of the allocated memory for this structure, expanding the allocation to fit the added object.
/// The value to append.
/// A pointer to the copied memory.
public virtual IntPtr Append(object value)
{
var sz = Size;
var vSz = InteropExtensions.SizeOf(value);
Size += vSz;
handle.Write(value, sz, Size);
return handle.Offset(sz);
}
/// Determines whether the specified , is equal to this instance.
/// The to compare with this instance.
/// true if the specified is equal to this instance; otherwise, false.
public override bool Equals(object obj) => ReferenceEquals(this, obj) ||
(obj switch
{
null => false,
SafeMemStruct ms => Equals((TStruct?)this, (TStruct?)ms),
TStruct s => Equals(s),
SafeAllocatedMemoryHandle m => m.DangerousGetHandle() == handle,
_ => false,
});
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(TStruct other) => HasValue && EqualityComparer.Default.Equals(handle.ToStructure(Size), other);
/// Gets the memory address of a field within .
/// Name of the field.
/// The pointer to the field in memory.
public virtual IntPtr GetFieldAddress(string fieldName) => handle.Offset(FieldOffset(fieldName));
/// Returns a hash code for this instance.
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
public override int GetHashCode() => handle.ToInt32();
/// Retrieves the value of the current object, or the specified default value.
/// A value to return if the property is .
///
/// The value of the property if the property is ; otherwise, the
/// parameter.
///
public virtual TStruct GetValueOrDefault(in TStruct defaultValue = default) => HasValue ? Value : defaultValue;
/// Initializes the size field by the specified name or the first four bytes of the structure's memory.
/// Name of the field.
public virtual void InitializeSizeField(string fieldName = null) => (fieldName is null ? handle : GetFieldAddress(fieldName)).Write((uint)BaseStructSize);
/// Returns the string value held by this instance.
/// A value held by this instance or null if the handle is invalid.
public override string ToString() => ((TStruct?)this).ToString();
/// Returns the field offset of the named field.
/// The field name.
/// The offset, in bytes, of the field within the structure.
protected static long FieldOffset(string name) => Marshal.OffsetOf(typeof(TStruct), name).ToInt64();
#if ALLOWSPAN
/// Gets a reference to a structure based on this allocated memory.
/// A referenced structure.
public ref TStruct AsRef() => ref MemoryMarshal.GetReference(AsSpan());
/// Creates a new span over this allocated memory.
/// The span representation of the structure.
public Span AsSpan() => base.AsSpan(1);
#endif
}
///
/// A structure handler based on unmanaged memory allocated by AllocCoTaskMem.
///
/// The type of the structure.
///
public class SafeCoTaskMemStruct : SafeMemStruct where TStruct : struct
{
/// Initializes a new instance of the class.
/// The TStruct value.
/// The capacity of the buffer, in bytes.
public SafeCoTaskMemStruct(in TStruct s, SizeT capacity = default) : base(s, capacity) { }
/// Initializes a new instance of the class.
/// The capacity of the buffer, in bytes.
public SafeCoTaskMemStruct(SizeT capacity = default) : base(capacity) { }
/// Initializes a new instance of the class.
/// The PTR.
/// true to reliably release the handle during finalization; false to prevent it.
/// The number of bytes allocated to .
[ExcludeFromCodeCoverage]
public SafeCoTaskMemStruct(IntPtr ptr, bool ownsHandle = true, SizeT allocatedBytes = default) : base(ptr, ownsHandle, allocatedBytes) { }
/// Represents the equivalent of this class instances.
public static readonly SafeCoTaskMemStruct Null = new SafeCoTaskMemStruct(IntPtr.Zero, false);
/// Performs an implicit conversion from to .
/// The value of the instance or .
/// The resulting instance from the conversion.
public static implicit operator SafeCoTaskMemStruct(TStruct? s) => s.HasValue ? new SafeCoTaskMemStruct(s.Value) : new SafeCoTaskMemStruct(IntPtr.Zero);
}
///
/// A structure handler based on unmanaged memory allocated by AllocHGlobal.
///
/// The type of the structure.
///
public class SafeHGlobalStruct : SafeMemStruct where TStruct : struct
{
/// Initializes a new instance of the class.
/// The TStruct value.
/// The capacity of the buffer, in bytes.
public SafeHGlobalStruct(in TStruct s, SizeT capacity = default) : base(s, capacity) { }
/// Initializes a new instance of the class.
/// The capacity of the buffer, in bytes.
public SafeHGlobalStruct(SizeT capacity = default) : base(capacity) { }
/// Initializes a new instance of the class.
/// The PTR.
/// true to reliably release the handle during finalization; false to prevent it.
/// The number of bytes allocated to .
[ExcludeFromCodeCoverage]
public SafeHGlobalStruct(IntPtr ptr, bool ownsHandle = true, SizeT allocatedBytes = default) : base(ptr, ownsHandle, allocatedBytes) { }
/// Represents the equivalent of this class instances.
public static readonly SafeHGlobalStruct Null = new SafeHGlobalStruct(IntPtr.Zero, false);
/// Performs an implicit conversion from to .
/// The value of the instance or .
/// The resulting instance from the conversion.
public static implicit operator SafeHGlobalStruct(TStruct? s) => s.HasValue ? new SafeHGlobalStruct(s.Value) : new SafeHGlobalStruct(IntPtr.Zero);
}
}