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;
}
}