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, IReadOnlyList where TMem : IMemoryMethods, new() where TElem : struct where TPrefix : IConvertible
{
/// The size, in bytes, of TElem.
protected static readonly int ElemSize = InteropExtensions.SizeOf(typeof(TElem));
/// The size, in bytes, of TPrefix.
protected 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 or sets the value at the specified index.
/// The value.
/// The index.
///
/// index or index
public virtual TElem this[TPrefix index]
{
get => handle.ToStructure(Size, ElemOffset(index));
set => handle.Write(value, ElemOffset(index), Size);
}
/// 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));
}
int IReadOnlyCollection.Count => Count.ToInt32(null);
TElem IReadOnlyList.this[int index] => this[(TPrefix)Convert.ChangeType(index, 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();
private int ElemOffset(TPrefix index) => index.ToUInt64(null) >= 0 && index.ToUInt64(null) < Count.ToUInt64(null) ? index.ToInt32(null) * ElemSize + PrefixSize : throw new ArgumentOutOfRangeException(nameof(index));
/// 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;
}
}