using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using Vanara.Extensions; namespace Vanara.InteropServices { /// /// A safe unmanaged array of structures allocated on the global heap with a prefix type (usually a uint or int) that determines the /// count of elements. /// /// The type of the array elements. /// The type of the value used to represent the number of elements in the array. /// The memory methods to use for allocation. public class SafeElementArray : SafeMemoryHandle, IEnumerable where TMem : IMemoryMethods, new() where TElem : struct where TPrefix : IConvertible { private static readonly int ElemSize = InteropExtensions.SizeOf(typeof(TElem)); private static readonly int PrefixSize = InteropExtensions.SizeOf(typeof(TPrefix)); /// Initializes a new instance of the class. protected SafeElementArray() : this(0) { } /// /// Initializes a new instance of the class and allocates bytes. /// /// The TElem count to allocate. protected SafeElementArray(TPrefix elementCount) : this(Convert.ToInt32(elementCount)) { } /// /// Initializes a new instance of the class from a copy of a managed TElem array. /// /// The array of bytes to copy. /// Size of the get elem. protected SafeElementArray(TElem[] array, Func getElemSize = null) : base(IntPtr.Zero, 0, true) { GetElemSize = getElemSize; Elements = array; } /// Initializes a new instance of the class. /// Size of the byte. /// The element count. This value can be 0. protected SafeElementArray(int byteSize, int elementCount) : base(byteSize) { Zero(); if (elementCount > 0) IntCount = elementCount; } private SafeElementArray(int elementCount) : base(GetRequiredSize(elementCount)) { } /// Gets the number of elements contained in the . protected TPrefix Count { get => IsInvalid ? default : handle.ToStructure(); private set { if (!IsInvalid) Marshal.StructureToPtr(value, handle, false); } } /// Gets or sets the elements. /// The elements of the array. protected TElem[] Elements { get => handle.ToArray(IntCount, PrefixSize); set { Size = GetElemSize != null ? PrefixSize + value.Sum(GetElemSize) : GetRequiredSize(value.Length); Zero(); InteropExtensions.Write(handle, value, PrefixSize); IntCount = value.Length; } } /// Gets or sets the size of the element. /// The size of the element. protected Func GetElemSize { get; set; } private int IntCount { get => Convert.ToInt32(Count); set => Count = (TPrefix)Convert.ChangeType(value, typeof(TPrefix)); } /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() => ((IEnumerable)Elements).GetEnumerator(); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// Converts a byte count to a element count. /// The byte count. /// The converted element count. /// Byte count does not match this structure. protected static uint BytesToCount(uint byteSize) { if ((byteSize - PrefixSize) % ElemSize != 0) throw new ArgumentException("Byte count does not match this structure."); return (uint)((byteSize - PrefixSize) / ElemSize); } private static int GetRequiredSize(int elementCount) => PrefixSize + ElemSize * elementCount; } }