2018-01-06 18:37:05 -05:00
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using Vanara.Extensions;
|
|
|
|
|
|
|
|
namespace Vanara.InteropServices
|
|
|
|
{
|
|
|
|
/// <summary>
|
2018-11-19 23:18:50 -05:00
|
|
|
/// 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.
|
2018-01-06 18:37:05 -05:00
|
|
|
/// </summary>
|
2019-04-24 10:26:50 -04:00
|
|
|
/// <typeparam name="TElem">The type of the array elements.</typeparam>
|
|
|
|
/// <typeparam name="TPrefix">The type of the value used to represent the number of elements in the array.</typeparam>
|
|
|
|
/// <typeparam name="TMem">The memory methods to use for allocation.</typeparam>
|
2022-10-12 11:39:43 -04:00
|
|
|
public class SafeElementArray<TElem, TPrefix, TMem> : SafeMemoryHandle<TMem>, IReadOnlyList<TElem> where TMem : IMemoryMethods, new() where TElem : struct where TPrefix : IConvertible
|
2018-01-06 18:37:05 -05:00
|
|
|
{
|
2022-10-12 09:52:36 -04:00
|
|
|
/// <summary>The size, in bytes, of <c>TElem</c>.</summary>
|
|
|
|
protected static readonly int ElemSize = InteropExtensions.SizeOf(typeof(TElem));
|
|
|
|
/// <summary>The size, in bytes, of <c>TPrefix</c>.</summary>
|
|
|
|
protected static readonly int PrefixSize = InteropExtensions.SizeOf(typeof(TPrefix));
|
2018-01-06 18:37:05 -05:00
|
|
|
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafeElementArray{TElem, TPrefix, TMem}"/> class.</summary>
|
|
|
|
protected SafeElementArray() : this(0) { }
|
|
|
|
|
2018-11-19 23:18:50 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="SafeElementArray{TElem, TPrefix, TMem}"/> class and allocates <paramref
|
|
|
|
/// name="elementCount"/> bytes.
|
|
|
|
/// </summary>
|
2018-04-03 20:35:18 -04:00
|
|
|
/// <param name="elementCount">The TElem count to allocate.</param>
|
2018-01-06 18:37:05 -05:00
|
|
|
protected SafeElementArray(TPrefix elementCount) : this(Convert.ToInt32(elementCount)) { }
|
|
|
|
|
2018-11-19 23:18:50 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="SafeElementArray{TElem, TPrefix, TMem}"/> class from a copy of a managed TElem array.
|
|
|
|
/// </summary>
|
2018-01-06 18:37:05 -05:00
|
|
|
/// <param name="array">The array of bytes to copy.</param>
|
2018-04-03 20:35:18 -04:00
|
|
|
/// <param name="getElemSize">Size of the get elem.</param>
|
2019-04-24 10:26:50 -04:00
|
|
|
protected SafeElementArray(TElem[] array, Func<TElem, int> getElemSize = null) : base(IntPtr.Zero, 0, true)
|
|
|
|
{
|
|
|
|
GetElemSize = getElemSize;
|
|
|
|
Elements = array;
|
|
|
|
}
|
2018-01-06 18:37:05 -05:00
|
|
|
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafeElementArray{TElem, TPrefix, TMem}"/> class.</summary>
|
|
|
|
/// <param name="byteSize">Size of the byte.</param>
|
|
|
|
/// <param name="elementCount">The element count. This value can be 0.</param>
|
|
|
|
protected SafeElementArray(int byteSize, int elementCount) : base(byteSize)
|
|
|
|
{
|
|
|
|
Zero();
|
|
|
|
if (elementCount > 0)
|
|
|
|
IntCount = elementCount;
|
|
|
|
}
|
|
|
|
|
2018-11-19 23:18:50 -05:00
|
|
|
private SafeElementArray(int elementCount) : base(GetRequiredSize(elementCount))
|
|
|
|
{
|
|
|
|
}
|
2018-01-06 18:37:05 -05:00
|
|
|
|
2022-10-12 11:39:43 -04:00
|
|
|
/// <summary>Gets or sets the <typeparamref name="TElem"/> value at the specified index.</summary>
|
|
|
|
/// <value>The <typeparamref name="TElem"/> value.</value>
|
|
|
|
/// <param name="index">The index.</param>
|
|
|
|
/// <returns></returns>
|
|
|
|
/// <exception cref="ArgumentOutOfRangeException">index or index</exception>
|
|
|
|
public virtual TElem this[TPrefix index]
|
|
|
|
{
|
|
|
|
get => handle.ToStructure<TElem>(Size, ElemOffset(index));
|
|
|
|
set => handle.Write(value, ElemOffset(index), Size);
|
|
|
|
}
|
|
|
|
|
2018-01-06 18:37:05 -05:00
|
|
|
/// <summary>Gets the number of elements contained in the <see cref="SafeElementArray{TElem, TPrefix, TMem}"/>.</summary>
|
|
|
|
protected TPrefix Count
|
|
|
|
{
|
2018-11-19 23:18:50 -05:00
|
|
|
get => IsInvalid ? default : handle.ToStructure<TPrefix>();
|
2018-01-06 18:37:05 -05:00
|
|
|
private set { if (!IsInvalid) Marshal.StructureToPtr(value, handle, false); }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>Gets or sets the elements.</summary>
|
|
|
|
/// <value>The elements of the array.</value>
|
|
|
|
protected TElem[] Elements
|
|
|
|
{
|
|
|
|
get => handle.ToArray<TElem>(IntCount, PrefixSize);
|
|
|
|
set
|
|
|
|
{
|
|
|
|
Size = GetElemSize != null ? PrefixSize + value.Sum(GetElemSize) : GetRequiredSize(value.Length);
|
2019-04-24 10:26:50 -04:00
|
|
|
Zero();
|
2019-08-17 23:13:57 -04:00
|
|
|
InteropExtensions.Write(handle, value, PrefixSize);
|
2018-01-06 18:37:05 -05:00
|
|
|
IntCount = value.Length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-03 20:35:18 -04:00
|
|
|
/// <summary>Gets or sets the size of the element.</summary>
|
|
|
|
/// <value>The size of the element.</value>
|
2018-01-06 18:37:05 -05:00
|
|
|
protected Func<TElem, int> GetElemSize { get; set; }
|
|
|
|
|
|
|
|
private int IntCount
|
|
|
|
{
|
|
|
|
get => Convert.ToInt32(Count);
|
|
|
|
set => Count = (TPrefix)Convert.ChangeType(value, typeof(TPrefix));
|
|
|
|
}
|
|
|
|
|
2022-10-12 11:39:43 -04:00
|
|
|
int IReadOnlyCollection<TElem>.Count => Count.ToInt32(null);
|
|
|
|
|
|
|
|
TElem IReadOnlyList<TElem>.this[int index] => this[(TPrefix)Convert.ChangeType(index, typeof(TPrefix))];
|
|
|
|
|
2018-01-06 18:37:05 -05:00
|
|
|
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
|
|
|
/// <returns>A <see cref="IEnumerator{TElem}"/> that can be used to iterate through the collection.</returns>
|
|
|
|
public IEnumerator<TElem> GetEnumerator() => ((IEnumerable<TElem>)Elements).GetEnumerator();
|
|
|
|
|
|
|
|
/// <summary>Returns an enumerator that iterates through a collection.</summary>
|
|
|
|
/// <returns>An <see cref="IEnumerator"/> object that can be used to iterate through the collection.</returns>
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
|
2022-10-12 11:39:43 -04:00
|
|
|
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));
|
|
|
|
|
2018-01-06 18:37:05 -05:00
|
|
|
/// <summary>Converts a byte count to a element count.</summary>
|
|
|
|
/// <param name="byteSize">The byte count.</param>
|
|
|
|
/// <returns>The converted element count.</returns>
|
|
|
|
/// <exception cref="ArgumentException">Byte count does not match this structure.</exception>
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|