Refactor unsafe collections and improve memory handling
Some checks failed
Publish NuGet Packages / publish (pull_request) Has been cancelled
Some checks failed
Publish NuGet Packages / publish (pull_request) Has been cancelled
Refactored enumerators across multiple unsafe collections to use `ref` returns for `Current`, improving performance and reducing memory usage. Enhanced memory management with `AllocationOption` support and optimized resizing logic for collections like `UnsafeBitSet`, `UnsafeSlotMap`, and `UnsafeSparseSet`. Updated `publish-nuget.yaml` to support manual workflow dispatch and trigger on `push`/`pull_request` events. Incremented project version to `1.1.2` and ensured NuGet package generation on build.
This commit is contained in:
@@ -40,6 +40,7 @@ public unsafe interface IUnsafeCollection<T> : IUnsafeCollection, IEnumerable<T>
|
||||
/// </summary>
|
||||
/// <remarks>This is to adjust the element count of the collection, not the size of the underlying buffer in memory.</remarks>
|
||||
/// <param name="newSize">Specifies the new size to which the collection should be adjusted.</param>
|
||||
/// <param name="option">Specifies allocation options that may affect how memory is managed during the resize operation.</param>
|
||||
void Resize(int newSize, AllocationOption option);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,25 +19,21 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
private readonly UnsafeArray<T>* _collection;
|
||||
private int _index;
|
||||
|
||||
public readonly ref T Current => ref _collection->_buffer[_index];
|
||||
readonly T IEnumerator<T>.Current => Current;
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(UnsafeArray<T>* collection)
|
||||
{
|
||||
_collection = collection;
|
||||
_index = -1;
|
||||
Current = default;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
if (_index < _collection->Count)
|
||||
{
|
||||
Current = UnsafeUtility.ReadArrayElement<T>(_collection->_buffer, _index);
|
||||
return true;
|
||||
}
|
||||
|
||||
Current = default;
|
||||
return false;
|
||||
return _index < _collection->_count;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -45,19 +41,6 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
// Let NativeArray indexer check for out of range.
|
||||
public T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get; private set;
|
||||
}
|
||||
|
||||
readonly object IEnumerator.Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Current;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Contracts;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -6,7 +7,7 @@ using System.Text;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
public struct UnsafeBitSet : IDisposable
|
||||
public unsafe struct UnsafeBitSet : IDisposable
|
||||
{
|
||||
private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31
|
||||
private const int _INDEX_SIZE = 5; // log_2(BitSize + 1)
|
||||
@@ -53,23 +54,31 @@ public struct UnsafeBitSet : IDisposable
|
||||
/// </summary>
|
||||
public UnsafeBitSet()
|
||||
{
|
||||
_bits = new UnsafeArray<uint>(s_padding, Allocator.Persistent, AllocationOption.Clear);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||
/// </summary>
|
||||
public UnsafeBitSet(int minimalLength, Allocator allocator, AllocationOption option = AllocationOption.Clear)
|
||||
{
|
||||
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
|
||||
var length = RoundToPadding(uints);
|
||||
_bits = new UnsafeArray<uint>(length, allocator, option);
|
||||
_bits = new UnsafeArray<uint>(s_padding, Allocator.Persistent, AllocationOption.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||
/// </summary>
|
||||
public UnsafeBitSet(Span<uint> bits, Allocator allocator, AllocationOption option = AllocationOption.Clear)
|
||||
public UnsafeBitSet(int minimalLength, ref AllocationHandle handle, AllocationOption option = AllocationOption.None)
|
||||
{
|
||||
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
|
||||
var length = RoundToPadding(uints);
|
||||
_bits = new UnsafeArray<uint>(length, ref handle, option);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||
/// </summary>
|
||||
public UnsafeBitSet(int minimalLength, Allocator allocator, AllocationOption option = AllocationOption.None)
|
||||
: this(minimalLength, ref AllocationManager.GetAllocationHandle(allocator), option)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||
/// </summary>
|
||||
public UnsafeBitSet(Span<uint> bits, Allocator allocator, AllocationOption option = AllocationOption.None)
|
||||
{
|
||||
_bits = new UnsafeArray<uint>(bits.Length, allocator, option);
|
||||
_bits.CopyFrom(bits);
|
||||
@@ -169,11 +178,7 @@ public struct UnsafeBitSet : IDisposable
|
||||
/// </summary>
|
||||
public void SetAll()
|
||||
{
|
||||
var count = _bits.Count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
_bits[i] = 0xffffffff;
|
||||
}
|
||||
_bits.AsSpan().Fill(0xffffffff);
|
||||
|
||||
_highestBit = _bits.Count * (_BIT_SIZE + 1) - 1;
|
||||
_max = _highestBit / (_BIT_SIZE + 1) + 1;
|
||||
@@ -192,7 +197,7 @@ public struct UnsafeBitSet : IDisposable
|
||||
/// <summary>
|
||||
/// Finds the next set bit at or after `startIndex`, or -1 if none.
|
||||
/// </summary>
|
||||
public int NextSetBit(int startIndex)
|
||||
public readonly int NextSetBit(int startIndex)
|
||||
{
|
||||
var wordIndex = startIndex >> _BIT_SIZE;
|
||||
if (wordIndex >= _bits.Count)
|
||||
@@ -221,6 +226,13 @@ public struct UnsafeBitSet : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public void Resize(int minimalLength, AllocationOption option = AllocationOption.None)
|
||||
{
|
||||
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
|
||||
var length = RoundToPadding(uints);
|
||||
_bits.Resize(length, option);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if all bits from this instance match those of the other instance.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,41 +14,19 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeCollection<KeyValuePai
|
||||
{
|
||||
internal HashMapHelper<TKey>.Enumerator _enumerator;
|
||||
|
||||
public KeyValuePair<TKey, TValue> Current => _enumerator.GetCurrent<TValue>();
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(HashMapHelper<TKey>* data)
|
||||
{
|
||||
_enumerator = new HashMapHelper<TKey>.Enumerator(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current key-value pair.
|
||||
/// </summary>
|
||||
/// <value>The current key-value pair.</value>
|
||||
public KeyValuePair<TKey, TValue> Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _enumerator.GetCurrent<TValue>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the current position of the enumerator in the container.
|
||||
/// </summary>
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
/// <summary>
|
||||
/// Advances the enumerator to the next key-value pair.
|
||||
/// </summary>
|
||||
/// <returns>True if <see cref="Current"/> is valid to read after the call.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => _enumerator.MoveNext();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the enumerator to its initial state.
|
||||
/// </summary>
|
||||
public void Reset() => _enumerator.Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Does nothing.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -19,19 +19,14 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeCollection<T>, IEnumerable<T>
|
||||
{
|
||||
internal HashMapHelper<T>.Enumerator _enumerator;
|
||||
|
||||
public readonly T Current => _enumerator.buffer->_keys[_enumerator.index];
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(HashMapHelper<T>* hashMap)
|
||||
{
|
||||
_enumerator = new HashMapHelper<T>.Enumerator(hashMap);
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _enumerator.buffer->_keys[_enumerator.index];
|
||||
}
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => _enumerator.MoveNext();
|
||||
|
||||
|
||||
@@ -18,27 +18,22 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
|
||||
{
|
||||
private readonly UnsafeList<T>* _collection;
|
||||
private int _index;
|
||||
private T _value;
|
||||
|
||||
public readonly ref T Current => ref _collection->_array[_index];
|
||||
readonly T IEnumerator<T>.Current => Current;
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(UnsafeList<T>* collection)
|
||||
{
|
||||
_collection = collection;
|
||||
_index = -1;
|
||||
_value = default;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
if (_index < _collection->_count)
|
||||
{
|
||||
_value = UnsafeUtility.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
|
||||
return true;
|
||||
}
|
||||
|
||||
_value = default;
|
||||
return false;
|
||||
return _index < _collection->_count;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -46,25 +41,6 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
// Let NativeArray indexer check for out of range.
|
||||
public readonly T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
readonly object IEnumerator.Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -17,46 +17,29 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
private readonly UnsafeQueue<T>* _collection;
|
||||
private int _index;
|
||||
private T _value;
|
||||
private int _currentIndex;
|
||||
|
||||
// We assume _currentIndex will always be in range when accessed.
|
||||
public readonly ref T Current => ref _collection->_array[(_collection->_offset + _currentIndex) % _collection->Capacity];
|
||||
readonly T IEnumerator<T>.Current => Current;
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(UnsafeQueue<T>* collection)
|
||||
{
|
||||
_collection = collection;
|
||||
_index = -1;
|
||||
_value = default;
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
if (_index < _collection->_count)
|
||||
{
|
||||
_value = UnsafeUtility.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
|
||||
return true;
|
||||
}
|
||||
|
||||
_value = default;
|
||||
return false;
|
||||
_currentIndex++;
|
||||
return _currentIndex < _collection->_count;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
// Let NativeArray indexer check for out of range.
|
||||
public readonly T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _value;
|
||||
}
|
||||
|
||||
readonly object IEnumerator.Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Current;
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
@@ -128,7 +111,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
|
||||
{
|
||||
if (_count >= Capacity)
|
||||
{
|
||||
Resize(Capacity + (int)(Capacity * 0.5f));
|
||||
Resize((int)(Capacity * 1.5f));
|
||||
}
|
||||
|
||||
UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), (_offset + _count) % Capacity, value);
|
||||
|
||||
@@ -20,44 +20,36 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
private readonly UnsafeSlotMap<T>* _collection;
|
||||
private int _currentIndex;
|
||||
|
||||
public readonly ref T Current => ref _collection->_data[_currentIndex];
|
||||
readonly T IEnumerator<T>.Current => Current;
|
||||
readonly object? IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(UnsafeSlotMap<T>* collection)
|
||||
{
|
||||
_collection = collection;
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
public readonly T Current => _collection->_data[_currentIndex].value;
|
||||
readonly object? IEnumerator.Current => Current;
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (++_currentIndex < _collection->_capacity)
|
||||
{
|
||||
if (_collection->_data[_currentIndex].isValid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
_currentIndex = _collection->_validBits.NextSetBit(_currentIndex + 1);
|
||||
return _currentIndex != -1;
|
||||
}
|
||||
|
||||
public void Reset() => _currentIndex = -1;
|
||||
public void Reset()
|
||||
{
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private struct SlotData
|
||||
{
|
||||
public T value;
|
||||
public int generation;
|
||||
public bool isValid;
|
||||
}
|
||||
|
||||
private UnsafeArray<SlotData> _data;
|
||||
private UnsafeArray<T> _data;
|
||||
private UnsafeArray<int> _generations;
|
||||
private UnsafeQueue<int> _freeSlots;
|
||||
private UnsafeBitSet _validBits;
|
||||
|
||||
private int _count;
|
||||
private int _capacity;
|
||||
@@ -94,8 +86,12 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero.");
|
||||
}
|
||||
|
||||
_data = new UnsafeArray<SlotData>(capacity, ref handle, allocationOption);
|
||||
_data = new UnsafeArray<T>(capacity, ref handle, allocationOption);
|
||||
_generations = new UnsafeArray<int>(capacity, ref handle, allocationOption);
|
||||
_freeSlots = new UnsafeQueue<int>(capacity, ref handle, allocationOption);
|
||||
_validBits = new UnsafeBitSet(GetBitSetCapacity(capacity), ref handle, allocationOption);
|
||||
|
||||
_validBits.ClearAll();
|
||||
|
||||
_count = 0;
|
||||
_capacity = capacity;
|
||||
@@ -113,6 +109,12 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
{
|
||||
}
|
||||
|
||||
private static int GetBitSetCapacity(int capacity)
|
||||
{
|
||||
// Each uint32 can hold 32 bits.
|
||||
return (capacity + 31) / 32;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified item to the collection and returns the index of the slot where it was stored.
|
||||
/// </summary>
|
||||
@@ -123,27 +125,26 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
{
|
||||
if (_count >= _capacity)
|
||||
{
|
||||
Resize(_capacity * 2);
|
||||
Resize((int)(_capacity * 1.5f));
|
||||
}
|
||||
|
||||
int slotIndex;
|
||||
int index;
|
||||
if (_freeSlots.Count == 0)
|
||||
{
|
||||
slotIndex = _count;
|
||||
index = _count;
|
||||
}
|
||||
else
|
||||
{
|
||||
slotIndex = _freeSlots.Dequeue();
|
||||
index = _freeSlots.Dequeue();
|
||||
}
|
||||
|
||||
ref var slot = ref _data[slotIndex];
|
||||
slot.value = item;
|
||||
slot.isValid = true;
|
||||
generation = slot.generation;
|
||||
_data[index] = item;
|
||||
_validBits.SetBit(index);
|
||||
|
||||
_count++;
|
||||
|
||||
return slotIndex;
|
||||
generation = _generations[index];
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -160,16 +161,16 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
return false;
|
||||
}
|
||||
|
||||
ref var slot = ref _data[slotIndex];
|
||||
if (slot.generation != generation)
|
||||
ref var gen = ref _generations[slotIndex];
|
||||
if (gen != generation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
slot.generation++;
|
||||
slot.isValid = false;
|
||||
|
||||
gen++;
|
||||
_validBits.ClearBit(slotIndex);
|
||||
_freeSlots.Enqueue(slotIndex);
|
||||
|
||||
_count--;
|
||||
|
||||
return true;
|
||||
@@ -188,9 +189,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
return false;
|
||||
}
|
||||
|
||||
ref var slot = ref _data[slotIndex];
|
||||
|
||||
if (slot.isValid && slot.generation == generation)
|
||||
if (_validBits.IsSet(slotIndex) && _generations[slotIndex] == generation)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -215,14 +214,13 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
return false;
|
||||
}
|
||||
|
||||
ref var slot = ref _data[slotIndex];
|
||||
if (slot.generation != generation)
|
||||
if (_generations[slotIndex] != generation)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = slot.value;
|
||||
value = _data[slotIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -241,13 +239,12 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
throw new ArgumentOutOfRangeException(nameof(slotIndex), "Slot index is out of range.");
|
||||
}
|
||||
|
||||
ref var slot = ref _data[slotIndex];
|
||||
if (!slot.isValid || slot.generation != generation)
|
||||
if (!_validBits.IsSet(slotIndex)|| _generations[slotIndex] != generation)
|
||||
{
|
||||
throw new InvalidOperationException($"Slot {slotIndex} is not occupied.");
|
||||
}
|
||||
|
||||
return slot.value;
|
||||
return _data[slotIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -268,28 +265,31 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||
|
||||
ref var slot = ref _data[slotIndex];
|
||||
|
||||
if (!slot.isValid || slot.generation != generation)
|
||||
if (!_validBits.IsSet(slotIndex) || _generations[slotIndex] != generation)
|
||||
{
|
||||
exist = false;
|
||||
return ref Unsafe.NullRef<T>();
|
||||
}
|
||||
|
||||
exist = true;
|
||||
return ref slot.value;
|
||||
return ref _data[slotIndex];
|
||||
}
|
||||
|
||||
public void Resize(int newSize, AllocationOption option = AllocationOption.None)
|
||||
{
|
||||
_data.Resize(newSize, option);
|
||||
_generations.Resize(newSize, option);
|
||||
_freeSlots.Resize(newSize, option);
|
||||
_validBits.Resize(GetBitSetCapacity(newSize), option);
|
||||
|
||||
_capacity = newSize;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_data.Clear();
|
||||
_generations.Clear();
|
||||
_freeSlots.Clear();
|
||||
_validBits.ClearAll();
|
||||
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
@@ -20,45 +20,27 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
private readonly UnsafeSparseSet<T>* _collection;
|
||||
private int _index;
|
||||
private T _value;
|
||||
private int _currentIndex;
|
||||
|
||||
public readonly ref T Current => ref _collection->_dense[_currentIndex];
|
||||
readonly T IEnumerator<T>.Current => Current;
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(UnsafeSparseSet<T>* collection)
|
||||
{
|
||||
_collection = collection;
|
||||
_index = -1;
|
||||
_value = default;
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
if (_index < _collection->_count)
|
||||
{
|
||||
_value = _collection->_dense[_index];
|
||||
return true;
|
||||
}
|
||||
|
||||
_value = default;
|
||||
return false;
|
||||
_currentIndex++;
|
||||
return _currentIndex < _collection->_count;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
public readonly T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _value;
|
||||
}
|
||||
|
||||
readonly object IEnumerator.Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Current;
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
public readonly unsafe void Dispose()
|
||||
@@ -158,10 +140,9 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
}
|
||||
|
||||
// Resize dense arrays if necessary
|
||||
if (_count >= _dense.Count)
|
||||
if (_count >= _capacity)
|
||||
{
|
||||
var newCapacity = _dense.Count + (int)Math.Max(1, _dense.Count * 0.5f);
|
||||
Resize(newCapacity);
|
||||
Resize((int)(_capacity * 1.5f));
|
||||
}
|
||||
|
||||
// Add the value to the dense array and update mappings
|
||||
@@ -337,8 +318,6 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
throw new ArgumentOutOfRangeException(nameof(newSize), "New size must be greater than zero.");
|
||||
}
|
||||
|
||||
var oldSize = _capacity;
|
||||
|
||||
_dense.Resize(newSize, option);
|
||||
_generations.Resize(newSize, option | AllocationOption.Clear);
|
||||
_reverse.Resize(newSize, option);
|
||||
|
||||
@@ -19,27 +19,22 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
||||
{
|
||||
private readonly UnsafeStack<T>* _collection;
|
||||
private int _index;
|
||||
private T _value;
|
||||
|
||||
public readonly ref T Current => ref _collection->_array[_index];
|
||||
readonly T IEnumerator<T>.Current => Current;
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(UnsafeStack<T>* collection)
|
||||
{
|
||||
_collection = collection;
|
||||
_index = collection->Count;
|
||||
_value = default;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index--;
|
||||
if (_index >= 0)
|
||||
{
|
||||
_value = UnsafeUtility.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
|
||||
return true;
|
||||
}
|
||||
|
||||
_value = default;
|
||||
return false;
|
||||
return _index >= 0;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -47,18 +42,6 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
||||
_index = _collection->Count;
|
||||
}
|
||||
|
||||
public readonly T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _value;
|
||||
}
|
||||
|
||||
readonly object IEnumerator.Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Current;
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
}
|
||||
@@ -68,6 +51,7 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
||||
private int _count;
|
||||
|
||||
public readonly int Count => _count;
|
||||
public readonly int Capacity => _array.Count;
|
||||
public readonly bool IsCreated => _array.IsCreated;
|
||||
|
||||
public Enumerator GetEnumerator() => new((UnsafeStack<T>*)UnsafeUtility.AddressOf(ref this));
|
||||
@@ -111,9 +95,9 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
||||
/// <param name="value">The element to add to the stack.</param>
|
||||
public void Push(T value)
|
||||
{
|
||||
if (_count >= _array.Count)
|
||||
if (_count >= Capacity)
|
||||
{
|
||||
Resize(_array.Count + (int)(_array.Count * 0.5f));
|
||||
Resize((int)(Capacity * 1.5f));
|
||||
}
|
||||
|
||||
UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<Authors>Misaki</Authors>
|
||||
<AssemblyVersion>1.1.1</AssemblyVersion>
|
||||
<AssemblyVersion>1.1.2</AssemblyVersion>
|
||||
<Version>$(AssemblyVersion)</Version>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||
|
||||
Reference in New Issue
Block a user