Refactor job API: add JobExecutionContext, update tests

Major breaking change: job interfaces now use JobExecutionContext
instead of threadIndex, enabling thread-aware and dynamic job
dispatching. Updated all job system, SPMD, and test code to match.
Collections improved with new methods and clearer enumerators.
Renamed IJobScheduler.WaitComplete to Wait. Incremented project
versions. Includes bug fixes, documentation, and style updates.
This commit is contained in:
2026-03-04 11:43:39 +09:00
parent b9ca71834f
commit 37d548085e
31 changed files with 652 additions and 207 deletions

View File

@@ -7,7 +7,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Misaki</Authors>
<AssemblyVersion>1.3.9</AssemblyVersion>
<AssemblyVersion>1.4.0</AssemblyVersion>
<Version>$(AssemblyVersion)</Version>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>

View File

@@ -176,7 +176,7 @@ public static unsafe class AllocationManager
}
}
private struct StackAllocator : IAllocator
private struct StackAllocator : IAllocator, IDisposable
{
private const int _STACK_MAGIC_ID = -6843541;
@@ -257,6 +257,11 @@ public static unsafe class AllocationManager
{
return s_stack.CreateScope(pSelf->_handle);
}
public readonly void Dispose()
{
Stack.DisposeAll();
}
}
private const uint _DEFAULT_MEMORY_POOL_SIZE = 1024 * 1024; // 1 MB
@@ -313,10 +318,16 @@ public static unsafe class AllocationManager
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static GCHandle HeaderGetHandle(AllocationHeader* header) => header->stackHandle;
private static GCHandle HeaderGetHandle(AllocationHeader* header)
{
return header->stackHandle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void HeaderSetHandle(AllocationHeader* header, GCHandle handle) => header->stackHandle = handle;
private static void HeaderSetHandle(AllocationHeader* header, GCHandle handle)
{
header->stackHandle = handle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void HeaderFreeHandle(AllocationHeader* header)
@@ -691,9 +702,10 @@ public static unsafe class AllocationManager
{
throw new MemoryLeakException(CollectionsMarshal.AsSpan(snapshot));
}
}
if (LiveAllocationCount != 0)
Debug.Assert(LiveAllocationCount == 0);
}
else if (LiveAllocationCount != 0)
{
throw new MemoryLeakException($"Found {LiveAllocationCount} memory lakes! Please enable debug layer for more informations.");
}
@@ -702,8 +714,7 @@ public static unsafe class AllocationManager
if (s_pArenaAllocator != null)
{
s_pArenaAllocator->Dispose();
Stack.DisposeAll();
s_pStackAllocator->Dispose();
NativeMemory.Free(s_pArenaAllocator);
}

View File

@@ -9,7 +9,7 @@ namespace Misaki.HighPerformance.LowLevel.Collections;
public unsafe struct HashMapHelper<TKey> : IDisposable
where TKey : unmanaged, IEquatable<TKey>
{
internal unsafe struct Enumerator
internal struct Enumerator
{
public HashMapHelper<TKey>* buffer;
public int index;
@@ -192,11 +192,17 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly int GetBucket(int hash)
{
return hash & (_bucketCapacity - 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly int GetBucket(in TKey key)
{
var h = (uint)key.GetHashCode();
return (int)(h & (uint)(_bucketCapacity - 1));
var h = key.GetHashCode();
return GetBucket(h);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -431,6 +437,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
where TValue : unmanaged
{
ThrowIfNotCreated();
var idx = Find(key);
if (idx != -1)
{
@@ -442,6 +449,82 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return ref Unsafe.NullRef<TValue>();
}
public ref TValue GetValueRefOrAddDefault<TValue>(in TKey key, out bool exists)
where TValue : unmanaged
{
ThrowIfNotCreated();
var idx = -1;
var bucket = -1;
var hash = key.GetHashCode();
if (_allocatedIndex > 0)
{
// First find the slot based on the hash
bucket = GetBucket(hash);
var entryIdx = _buckets[bucket];
if ((uint)entryIdx < (uint)_capacity)
{
var nextPtrs = _next;
while (!UnsafeUtility.ReadArrayElement<TKey>(_keys, entryIdx).Equals(key))
{
entryIdx = nextPtrs[entryIdx];
if ((uint)entryIdx >= (uint)_capacity)
{
goto Found;
}
}
idx = entryIdx;
goto Found;
}
}
Found:
if (idx != -1)
{
exists = true;
return ref UnsafeUtility.ReadArrayElementRef<TValue>(_buffer, idx);
}
int* next;
if (_allocatedIndex >= _capacity && _firstFreeIndex < 0)
{
var newCap = CalcCapacityCeilPow2(_capacity + (1 << _log2MinGrowth));
Resize(newCap);
}
idx = _firstFreeIndex;
if (idx >= 0)
{
_firstFreeIndex = _next[idx];
}
else
{
idx = _allocatedIndex++;
}
CheckIndexOutOfBounds(idx);
UnsafeUtility.WriteArrayElement(_keys, idx, key);
bucket = GetBucket(hash);
// Add the index to the hash-map
next = _next;
next[idx] = _buckets[bucket];
_buckets[bucket] = idx;
_count++;
UnsafeUtility.WriteArrayElement(_buffer, idx, default(TValue));
exists = false;
return ref UnsafeUtility.ReadArrayElementRef<TValue>(_buffer, idx);
}
public bool MoveNextSearch(ref int bucketIndex, ref int nextIndex, out int index)
{
ThrowIfNotCreated();

View File

@@ -73,9 +73,20 @@ public readonly unsafe struct ReadOnlyUnsafeCollection<T> : IEnumerable<T>
}
}
public Enumerator GetEnumerator() => new Enumerator(in this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new Enumerator(in this);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public ReadOnlyUnsafeCollection(T* buffer, int count)
{

View File

@@ -122,9 +122,20 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
}
}
public Enumerator GetEnumerator() => new((UnsafeArray<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new((UnsafeArray<T>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Invalid constructor, use <see cref="UnsafeArray(int, Allocator, AllocationOption)"/> or <see cref="UnsafeArray(int, ref AllocationHandle, AllocationOption)"/> instead.

View File

@@ -7,7 +7,8 @@ using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.LowLevel.Collections;
public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValuePair<TKey, TValue>>
where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged
where TKey : unmanaged, IEquatable<TKey>
where TValue : unmanaged
{
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
{
@@ -22,9 +23,15 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => _enumerator.MoveNext();
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset() => _enumerator.Reset();
public void Reset()
{
_enumerator.Reset();
}
public void Dispose()
{
@@ -68,9 +75,20 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
}
}
public Enumerator GetEnumerator() => new((HashMapHelper<TKey>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new((HashMapHelper<TKey>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Invalid constructor, use <see cref="UnsafeHashMap(int, Allocator, AllocationOption)"/> or <see cref="UnsafeHashMap(int, ref AllocationHandle, AllocationOption)"/> instead.
@@ -167,6 +185,11 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
return ref _helper.GetValueRef<TValue>(key, out exists);
}
public ref TValue GetValueRefOrAddDefault(in TKey key, out bool exists)
{
return ref _helper.GetValueRefOrAddDefault<TValue>(key, out exists);
}
/// <summary>
/// Returns true if a given key is present in this hash map.
/// </summary>
@@ -180,7 +203,10 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
/// <summary>
/// Sets the capacity to match what it would be if it had been originally initialized with all its entries.
/// </summary>
public void TrimExcess() => _helper.TrimExcess();
public void TrimExcess()
{
_helper.TrimExcess();
}
public void Resize(int newSize, AllocationOption option = AllocationOption.None)
{
@@ -196,20 +222,29 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
/// Retrieves an array of keys from the hash map.
/// </summary>
/// <returns>An array containing the keys stored in the hash map.</returns>
public UnsafeArray<TKey> GetKeyArray(Allocator allocator) => _helper.GetKeyArray(allocator);
public UnsafeArray<TKey> GetKeyArray(Allocator allocator)
{
return _helper.GetKeyArray(allocator);
}
/// <summary>
/// Retrieves an array of values from the underlying hash map.
/// </summary>
/// <returns>An UnsafeArray containing the values stored in the hash map.</returns>
public UnsafeArray<TValue> GetValueArray(Allocator allocator) => _helper.GetValueArray<TValue>(allocator);
public UnsafeArray<TValue> GetValueArray(Allocator allocator)
{
return _helper.GetValueArray<TValue>(allocator);
}
/// <summary>
/// Retrieves an array of key-value pairs from the hash map. The keys are of type TKey and the values are of type
/// TValue.
/// </summary>
/// <returns>Returns an UnsafeArray containing KeyValuePair objects.</returns>
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays(Allocator allocator) => _helper.GetKeyValueArrays<TValue>(allocator);
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays(Allocator allocator)
{
return _helper.GetKeyValueArrays<TValue>(allocator);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void* GetUnsafePtr()

View File

@@ -27,9 +27,15 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>, IEnumerable<T>
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => _enumerator.MoveNext();
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset() => _enumerator.Reset();
public void Reset()
{
_enumerator.Reset();
}
public readonly void Dispose()
{
@@ -42,9 +48,20 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>, IEnumerable<T>
public readonly int Capacity => _helper.Capacity;
public readonly bool IsCreated => _helper.IsCreated;
public Enumerator GetEnumerator() => new((HashMapHelper<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new((HashMapHelper<T>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Invalid constructor. Use <see cref="UnsafeHashSet(int, Allocator, AllocationOption)"/> or <see cref="UnsafeHashSet(int, ref AllocationHandle, AllocationOption)"/> instead."/>
@@ -97,7 +114,10 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>, IEnumerable<T>
/// <summary>
/// Sets the capacity to match what it would be if it had been originally initialized with all its entries.
/// </summary>
public void TrimExcess() => _helper.TrimExcess();
public void TrimExcess()
{
_helper.TrimExcess();
}
/// <summary>
/// Returns an array with a copy of this set's values (in no particular order).

View File

@@ -22,7 +22,7 @@ internal class UnsafeListDebugView<T>
get
{
var array = new T[_list.Count];
for (int i = 0; i < _list.Count; i++)
for (var i = 0; i < _list.Count; i++)
{
array[i] = _list[i];
}
@@ -71,20 +71,56 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
}
}
/// <summary>
/// A Parallel reader for an UnsafeList.
/// </summary>
/// <remarks>
/// Use <see cref="AsParallelReader"/> to create a parallel reader for a list.
/// The list must live at least as long as the parallel reader, and the parallel reader must not be used after the list is disposed.
/// </remarks>
public readonly unsafe struct ParallelReader
{
public readonly UnsafeList<T>* listData;
public readonly int Count => listData->_count;
public ref readonly T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref listData->_array[index];
}
public ref readonly T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref listData->_array[index];
}
internal ParallelReader(UnsafeList<T>* list)
{
listData = list;
}
public readonly Enumerator GetEnumerator()
{
return new Enumerator(listData);
}
public readonly ReadOnlySpan<T> AsSpan()
{
return new ReadOnlySpan<T>(listData->_array.GetUnsafePtr(), listData->_count);
}
}
/// <summary>
/// A parallel writer for an UnsafeList.
/// </summary>
/// <remarks>
/// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
/// The list must live at least as long as the parallel writer, and the parallel writer must not be used after the list is disposed.
/// </remarks>
public unsafe struct ParallelWriter
public readonly unsafe struct ParallelWriter
{
private volatile int _resizeLock;
/// <summary>
/// The UnsafeList to write to.
/// </summary>
public UnsafeList<T>* listData;
public readonly UnsafeList<T>* listData;
internal ParallelWriter(UnsafeList<T>* list)
{
@@ -139,23 +175,6 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
get => ref _array[index];
}
public Enumerator GetEnumerator() => new ((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Provides a parallel writer for the current list, enabling thread-safe additions to the list.
/// </summary>
/// <returns>A <see cref="ParallelWriter"/> instance that can be used to add items to the list in a thread-safe manner.</returns>
public ParallelWriter AsParallelWriter() => new((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
/// <summary>
/// Converts the current list to an UnsafeArray representation.
/// </summary>
/// <returns>A new <see cref="UnsafeArray{T}"/> instance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly UnsafeArray<T> AsUnsafeArray() => new((T*)_array.GetUnsafePtr(), _count);
/// <summary>
/// Invalid constructor, use <see cref="UnsafeList(int, Allocator, AllocationOption)"/> or <see cref="UnsafeList(int, ref AllocationHandle, AllocationOption)"/> instead.
/// </summary>
@@ -196,9 +215,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
{
if (index + count > Capacity)
{
throw new Exception(
$"AddNoResize assumes that list capacity is sufficient (Capacity {Capacity}, Size {Count}), requested count {count}!"
);
throw new Exception($"AddNoResize assumes that list capacity is sufficient (Capacity {Capacity}, Size {Count}), requested count {count}!");
}
}
@@ -225,6 +242,66 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
}
}
public Enumerator GetEnumerator()
{
return new((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Provides a parallel reader for the current list, enabling thread-safe read operations.
/// </summary>
/// <remarks>
/// The list must live at least as long as the parallel reader, and the parallel reader must not be used after the list is disposed.
/// For example, if you need to access the list in job system and wait that job in another stack frame, please always allocate the list struct itself on heap.
/// Otherwise the parallel reader will be invalid after the stack frame that creates the list is popped, even if the list's internal array is still valid.
/// </remarks>
/// <returns>A <see cref="ParallelReader"/> instance that can be used to read items from the list in a thread-safe manner.</returns>
public ParallelReader AsParallelReader()
{
return new((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
}
/// <summary>
/// Provides a parallel writer for the current list, enabling thread-safe additions to the list.
/// </summary>
/// <remarks>
/// The list must live at least as long as the parallel writer, and the parallel writer must not be used after the list is disposed.
/// For example, if you need to access the list in job system and wait that job in another stack frame, please always allocate the list struct itself on heap.
/// Otherwise the parallel writer will be invalid after the stack frame that creates the list is popped, even if the list's internal array is still valid.
/// </remarks>
/// <returns>A <see cref="ParallelWriter"/> instance that can be used to add items to the list in a thread-safe manner.</returns>
public ParallelWriter AsParallelWriter()
{
return new((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
}
/// <summary>
/// Converts the current list to an UnsafeArray representation.
/// </summary>
/// <remarks>
/// The returned <see cref="UnsafeArray{T}"/> shares the same underlying data as the list and does not own the memory.
/// </remarks>
/// <returns>A new <see cref="UnsafeArray{T}"/> instance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly UnsafeArray<T> AsUnsafeArray()
{
return new UnsafeArray<T>((T*)_array.GetUnsafePtr(), _count);
}
/// <summary>
/// Converts the current list to a read-only collection that provides unsafe access to its elements.
/// </summary>
/// <returns>A new <see cref="ReadOnlyUnsafeCollection{T}"/> instance that allows for read-only access to the list's elements without copying.</returns>
public readonly ReadOnlyUnsafeCollection<T> AsReadOnly()
{
return new ReadOnlyUnsafeCollection<T>((T*)_array.GetUnsafePtr(), _count);

View File

@@ -62,9 +62,20 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
set => _array[index] = value;
}
public Enumerator GetEnumerator() => new((UnsafeQueue<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new((UnsafeQueue<T>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Invalid constructor. Use <see cref="UnsafeQueue(int, Allocator, AllocationOption)"/> or <see cref="UnsafeQueue(int, ref AllocationHandle, AllocationOption)"/> instead."/>

View File

@@ -86,9 +86,20 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
public readonly bool IsCreated => _data.IsCreated && _generations.IsCreated && _freeSlots.IsCreated && _validBits.IsCreated;
public Enumerator GetEnumerator() => new((UnsafeSlotMap<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new((UnsafeSlotMap<T>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Invalid constructor. Use <see cref="UnsafeSlotMap(int, Allocator, AllocationOption)"/> or <see cref="UnsafeSlotMap(int, ref AllocationHandle, AllocationOption)"/> instead."/>

View File

@@ -89,9 +89,20 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
public readonly int Capacity => _capacity;
public readonly bool IsCreated => _dense.IsCreated && _sparse.IsCreated && _reverse.IsCreated;
public Enumerator GetEnumerator() => new((UnsafeSparseSet<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new((UnsafeSparseSet<T>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Constructs an UnsafeSparseSet with a default size of 1 and uses the Persistent allocator.

View File

@@ -81,9 +81,20 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
public readonly int Capacity => _array.Count;
public readonly bool IsCreated => _array.IsCreated;
public Enumerator GetEnumerator() => new((UnsafeStack<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Enumerator GetEnumerator()
{
return new((UnsafeStack<T>*)UnsafeUtility.AddressOf(ref this));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Invalid constructor, use <see cref="UnsafeStack(int, Allocator, AllocationOption)"/> or <see cref="UnsafeStack(int, ref AllocationHandle, AllocationOption)"/> instead.

View File

@@ -24,6 +24,11 @@ public readonly unsafe struct SharedPtr<T> : IEquatable<SharedPtr<T>>
return _value;
}
public ref T GetRef()
{
return ref *_value;
}
public bool Equals(SharedPtr<T> other)
{
return _value == other._value;
@@ -81,11 +86,16 @@ public unsafe struct UniquePtr<T> : IEquatable<UniquePtr<T>>
_value = value;
}
public readonly T* Get()
public T* Get()
{
return _value;
}
public ref T GetRef()
{
return ref *_value;
}
public readonly SharedPtr<T> Share()
{
return new SharedPtr<T>(_value);

View File

@@ -15,23 +15,33 @@ public static unsafe partial class MemoryUtility
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<byte> LoadVector128(ref byte start, nuint offset)
=> Unsafe.ReadUnaligned<Vector128<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
{
return Unsafe.ReadUnaligned<Vector128<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<byte> LoadVector256(ref byte start, nuint offset)
=> Unsafe.ReadUnaligned<Vector256<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
{
return Unsafe.ReadUnaligned<Vector256<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static nuint GetByteVector128SpanLength(nuint offset, int length)
=> (uint)((length - (int)offset) & ~(Vector128<byte>.Count - 1));
{
return (uint)((length - (int)offset) & ~(Vector128<byte>.Count - 1));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static nuint GetByteVector256SpanLength(nuint offset, int length)
=> (uint)((length - (int)offset) & ~(Vector256<byte>.Count - 1));
{
return (uint)((length - (int)offset) & ~(Vector256<byte>.Count - 1));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static nuint GetByteVector512SpanLength(nuint offset, int length)
=> (uint)((length - (int)offset) & ~(Vector512<byte>.Count - 1));
{
return (uint)((length - (int)offset) & ~(Vector512<byte>.Count - 1));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe nuint UnalignedCountVector128(byte* searchSpace)