using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Dalamud.Bindings.ImGui;
///
/// A structure representing a dynamic array for unmanaged types.
///
public unsafe struct ImVector
{
public readonly int Size;
public readonly int Capacity;
public readonly void* Data;
public ImVector(int size, int capacity, void* data)
{
Size = size;
Capacity = capacity;
Data = data;
}
public readonly ref T Ref(int index) => ref Unsafe.AsRef((byte*)this.Data + (index * Unsafe.SizeOf()));
public readonly nint Address(int index) => (nint)((byte*)this.Data + (index * Unsafe.SizeOf()));
}
///
/// A structure representing a dynamic array for unmanaged types.
///
/// The type of elements in the vector, must be unmanaged.
[StructLayout(LayoutKind.Sequential)]
public unsafe struct ImVector : IEnumerable
where T : unmanaged
{
private int size;
private int capacity;
private T* data;
///
/// Initializes a new instance of the struct with the specified size, capacity, and data pointer.
///
/// The initial size of the vector.
/// The initial capacity of the vector.
/// Pointer to the initial data.
public ImVector(int size, int capacity, T* data)
{
this.size = size;
this.capacity = capacity;
this.data = data;
}
///
/// Gets or sets the element at the specified index.
///
/// The zero-based index of the element to get or set.
/// The element at the specified index.
/// Thrown when the index is out of range.
public T this[int index]
{
readonly get
{
if (index < 0 || index >= this.size)
throw new IndexOutOfRangeException();
return this.data[index];
}
set
{
if (index < 0 || index >= this.size)
throw new IndexOutOfRangeException();
this.data[index] = value;
}
}
///
/// Gets a pointer to the first element of the vector.
///
public readonly T* Data => this.data;
///
/// Gets a pointer to the first element of the vector.
///
public readonly T* Front => this.data;
///
/// Gets a pointer to the last element of the vector.
///
public readonly T* Back => this.size > 0 ? this.data + this.size - 1 : null;
///
/// Gets or sets the capacity of the vector.
///
public int Capacity
{
readonly get => this.capacity;
set
{
ArgumentOutOfRangeException.ThrowIfLessThan(value, this.size, nameof(Capacity));
if (this.capacity == value)
return;
if (this.data == null)
{
this.data = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T)));
}
else
{
var newSize = Math.Min(this.size, value);
var newData = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T)));
Buffer.MemoryCopy(this.data, newData, (nuint)(value * sizeof(T)), (nuint)(newSize * sizeof(T)));
ImGui.MemFree(this.data);
this.data = newData;
this.size = newSize;
}
this.capacity = value;
// Clear the rest of the data
new Span(this.data + this.size, this.capacity - this.size).Clear();
}
}
///
/// Gets the number of elements in the vector.
///
public readonly int Size => this.size;
///
/// Grows the capacity of the vector to at least the specified value.
///
/// The new capacity.
public void Grow(int newCapacity)
{
var newCapacity2 = this.capacity > 0 ? this.capacity + (this.capacity / 2) : 8;
this.Capacity = newCapacity2 > newCapacity ? newCapacity2 : newCapacity;
}
///
/// Ensures that the vector has at least the specified capacity.
///
/// The minimum capacity required.
public void EnsureCapacity(int size)
{
if (size > this.capacity)
Grow(size);
}
///
/// Resizes the vector to the specified size.
///
/// The new size of the vector.
public void Resize(int newSize)
{
EnsureCapacity(newSize);
this.size = newSize;
}
///
/// Clears all elements from the vector.
///
public void Clear() => this.size = 0;
///
/// Adds an element to the end of the vector.
///
/// The value to add.
[OverloadResolutionPriority(1)]
public void PushBack(T value)
{
this.EnsureCapacity(this.size + 1);
this.data[this.size++] = value;
}
///
/// Adds an element to the end of the vector.
///
/// The value to add.
[OverloadResolutionPriority(2)]
public void PushBack(in T value)
{
EnsureCapacity(this.size + 1);
this.data[this.size++] = value;
}
///
/// Adds an element to the front of the vector.
///
/// The value to add.
public void PushFront(in T value)
{
if (this.size == 0)
this.PushBack(value);
else
this.Insert(0, value);
}
///
/// Removes the last element from the vector.
///
public void PopBack()
{
if (this.size > 0)
{
this.size--;
}
}
public ref T Insert(int index, in T v) {
ArgumentOutOfRangeException.ThrowIfNegative(index, nameof(index));
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, this.size, nameof(index));
this.EnsureCapacity(this.size + 1);
if (index < this.size)
{
Buffer.MemoryCopy(
this.data + index,
this.data + index + 1,
(this.size - index) * sizeof(T),
(this.size - index) * sizeof(T));
}
this.data[index] = v;
this.size++;
return ref this.data[index];
}
public Span InsertRange(int index, ReadOnlySpan v)
{
ArgumentOutOfRangeException.ThrowIfNegative(index, nameof(index));
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, this.size, nameof(index));
this.EnsureCapacity(this.size + v.Length);
if (index < this.size)
{
Buffer.MemoryCopy(
this.data + index,
this.data + index + v.Length,
(this.size - index) * sizeof(T),
(this.size - index) * sizeof(T));
}
var dstSpan = new Span(this.data + index, v.Length);
v.CopyTo(new(this.data + index, v.Length));
this.size += v.Length;
return dstSpan;
}
///
/// Frees the memory allocated for the vector.
///
public void Free()
{
if (this.data != null)
{
ImGui.MemFree(this.data);
this.data = null;
this.size = 0;
this.capacity = 0;
}
}
public readonly ref T Ref(int index)
{
return ref Unsafe.AsRef((byte*)Data + (index * Unsafe.SizeOf()));
}
public readonly ref TCast Ref(int index)
{
return ref Unsafe.AsRef((byte*)Data + (index * Unsafe.SizeOf()));
}
public readonly void* Address(int index)
{
return (byte*)Data + (index * Unsafe.SizeOf());
}
public readonly void* Address(int index)
{
return (byte*)Data + (index * Unsafe.SizeOf());
}
public readonly ImVector* ToUntyped()
{
return (ImVector*)Unsafe.AsPointer(ref Unsafe.AsRef(in this));
}
public readonly Span AsSpan() => new(this.data, this.size);
public readonly Enumerator GetEnumerator() => new(this.data, this.data + this.size);
readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
public struct Enumerator(T* begin, T* end) : IEnumerator, IEnumerable
{
private T* current = null;
public readonly ref T Current => ref *this.current;
readonly T IEnumerator.Current => this.Current;
readonly object IEnumerator.Current => this.Current;
public bool MoveNext()
{
var next = this.current == null ? begin : this.current + 1;
if (next == end)
return false;
this.current = next;
return true;
}
public void Reset() => this.current = null;
public readonly Enumerator GetEnumerator() => new(begin, end);
readonly void IDisposable.Dispose()
{
}
readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
}