Added new TempJobAllocator

Added new AllocationHandle property in Stack.Scope.

Changed the ref AllocationHandle constructor parameter to AllocationHandle on of all UnsafeCollection types
Removed Allocator.Stack. Use Stack.Scope.AllocationHandle to allocate on stack instead.
This commit is contained in:
2025-12-06 22:16:39 +09:00
parent d6c472753d
commit f3b0f295a8
24 changed files with 301 additions and 170 deletions

View File

@@ -9,14 +9,17 @@ namespace Misaki.HighPerformance.Jobs;
/// <summary> /// <summary>
/// Provides a mechanism for scheduling and executing jobs across multiple worker threads. /// Provides a mechanism for scheduling and executing jobs across multiple worker threads.
/// </summary> /// </summary>
/// <remarks>The <see cref="JobScheduler"/> class is designed to manage the execution of jobs, including support
/// for dependencies, parallel execution, and thread-specific job assignment. It allows developers to schedule jobs that
/// implement the <see cref="IJob"/> or <see cref="IJobParallelFor"/> interfaces, and it ensures efficient utilization
/// of worker threads through job batching and work-stealing mechanisms. This class is thread-safe and can be used in
/// multi-threaded environments. However, it must be disposed when no longer needed to release resources and terminate
/// worker threads.</remarks>
public sealed unsafe class JobScheduler : IDisposable public sealed unsafe class JobScheduler : IDisposable
{ {
private static readonly TempJobAllocator* pTempAllocator;
public static AllocationHandle TempAllocatorHandle => pTempAllocator->Handle;
static JobScheduler()
{
pTempAllocator = (TempJobAllocator*)MemoryUtility.Malloc((nuint)sizeof(TempJobAllocator));
pTempAllocator->Init();
}
private const int _SLEEP_THRESHOLD = 100; private const int _SLEEP_THRESHOLD = 100;
private FreeList _jobDataAllocator; private FreeList _jobDataAllocator;

View File

@@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AssemblyVersion>1.1.0</AssemblyVersion> <AssemblyVersion>1.2.0</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>

View File

@@ -0,0 +1,101 @@
using System.Runtime.CompilerServices;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Utilities;
namespace Misaki.HighPerformance.Jobs;
public unsafe struct TempJobAllocator : IAllocator, IDisposable
{
private const int _FRAME_LATENCY = 4;
private const uint _ARENA_SIZE = 1024 * 1024; // 1 MB
private DynamicArena* _pArena;
private int _currentFrameIndex;
private fixed int _allocationsPerFrame[_FRAME_LATENCY];
private MemoryHandle _memoryHandle;
private AllocationHandle _handle;
public readonly AllocationHandle Handle => _handle;
internal void Init()
{
var memoryHandle = default(MemoryHandle);
_pArena = (DynamicArena*)AllocationManager.HeapAlloc((nuint)(sizeof(DynamicArena) * _FRAME_LATENCY), MemoryUtility.AlignOf<DynamicArena>(), AllocationOption.Clear, &memoryHandle);
_currentFrameIndex = 0;
_memoryHandle = memoryHandle;
for (int i = 0; i < _FRAME_LATENCY; i++)
{
_pArena[i].Initialize(_ARENA_SIZE);
_allocationsPerFrame[i] = 0;
}
_handle = new(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &Free);
}
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
{
var selfPtr = (TempJobAllocator*)instance;
var pCurrentArena = selfPtr->_pArena + selfPtr->_currentFrameIndex;
var ptr = pCurrentArena->Allocate(size, alignment, allocationOption);
if (ptr == null)
{
*pHandle = MemoryHandle.Invalid;
return null;
}
Interlocked.Increment(ref selfPtr->_allocationsPerFrame[selfPtr->_currentFrameIndex]);
*pHandle = AllocationManager.GetMagicHandle();
return ptr;
}
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
{
if (ptr == null)
{
return Allocate(instance, newSize, alignment, allocationOption, pHandle);
}
var selfPtr = (TempJobAllocator*)instance;
var pCurrentArena = selfPtr->_pArena + selfPtr->_currentFrameIndex;
var newPtr = pCurrentArena->Allocate(newSize, alignment, allocationOption);
if (newPtr == null)
{
return null;
}
MemoryUtility.MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
return newPtr;
}
private static void Free(void* instance, void* ptr, MemoryHandle pHandle)
{
// The arena allocator does not free individual blocks, as it manages memory in chunks.
var selfPtr = (TempJobAllocator*)instance;
Interlocked.Decrement(ref selfPtr->_allocationsPerFrame[selfPtr->_currentFrameIndex]);
}
public int IncrementFrame()
{
var allocations = Interlocked.Exchange(ref _allocationsPerFrame[_currentFrameIndex], 0);
_currentFrameIndex = (_currentFrameIndex + 1) % _FRAME_LATENCY;
var pCurrentArena = _pArena + _currentFrameIndex;
pCurrentArena->Reset();
return allocations;
}
public void Dispose()
{
for (int i = 0; i < _FRAME_LATENCY; i++)
{
_pArena[i].Dispose();
}
AllocationManager.HeapFree(_pArena, _memoryHandle);
}
}

View File

@@ -1,12 +1,12 @@
using Misaki.HighPerformance.Collections; using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel.Contracts;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Misaki.HighPerformance.LowLevel.Buffer; namespace Misaki.HighPerformance.LowLevel.Buffer;
public readonly struct MemoryHandle public readonly struct MemoryHandle : IEquatable<MemoryHandle>
{ {
public readonly int id; public readonly int id;
public readonly int generation; public readonly int generation;
@@ -20,6 +20,36 @@ public readonly struct MemoryHandle
this.id = id; this.id = id;
this.generation = generation; this.generation = generation;
} }
public bool Equals(MemoryHandle other)
{
return id == other.id && generation == other.generation;
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is MemoryHandle other && Equals(other);
}
public override int GetHashCode()
{
return id ^ generation;
}
public override string? ToString()
{
return $"MemoryHandle(Id: {id}, Generation: {generation})";
}
public static bool operator ==(MemoryHandle left, MemoryHandle right)
{
return left.Equals(right);
}
public static bool operator !=(MemoryHandle left, MemoryHandle right)
{
return !(left == right);
}
} }
/// <summary> /// <summary>
@@ -66,7 +96,7 @@ public static unsafe class AllocationManager
private DynamicArena _arena; private DynamicArena _arena;
private AllocationHandle _handle; private AllocationHandle _handle;
public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle); public readonly AllocationHandle Handle => _handle;
public void Init(uint initialSize) public void Init(uint initialSize)
{ {
@@ -84,7 +114,7 @@ public static unsafe class AllocationManager
return null; return null;
} }
*pHandle = AddAllocation((IntPtr)ptr); *pHandle = GetMagicHandle();
return ptr; return ptr;
} }
@@ -103,16 +133,13 @@ public static unsafe class AllocationManager
} }
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize)); MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
RemoveAllocation(*pHandle);
*pHandle = AddAllocation((IntPtr)newPtr);
return newPtr; return newPtr;
} }
private static void Free(void* instance, void* ptr, MemoryHandle pHandle) private static void Free(void* instance, void* ptr, MemoryHandle handle)
{ {
// The arena allocator does not free individual blocks, as it manages memory in chunks. // The arena allocator does not free individual blocks, as it manages memory in chunks.
s_allocations.Remove(pHandle.id, pHandle.generation);
} }
public void Reset() public void Reset()
@@ -130,7 +157,7 @@ public static unsafe class AllocationManager
{ {
private AllocationHandle _handle; private AllocationHandle _handle;
public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle); public readonly AllocationHandle Handle => _handle;
public void Init() public void Init()
{ {
@@ -177,11 +204,11 @@ public static unsafe class AllocationManager
private static Stack s_stack; private static Stack s_stack;
private AllocationHandle _handle; private AllocationHandle _handle;
public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle); public readonly AllocationHandle Handle => _handle;
public void Init() public void Init()
{ {
_handle = new(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &FreeBlock); _handle = new(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &Free);
} }
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle) private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
@@ -193,7 +220,7 @@ public static unsafe class AllocationManager
return null; return null;
} }
*pHandle = AddAllocation((IntPtr)ptr); *pHandle = GetMagicHandle();
return ptr; return ptr;
} }
@@ -211,24 +238,21 @@ public static unsafe class AllocationManager
} }
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize)); MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
RemoveAllocation(*pHandle);
*pHandle = AddAllocation((IntPtr)newPtr);
return newPtr; return newPtr;
} }
private static void FreeBlock(void* instance, void* ptr, MemoryHandle pHandle) private static void Free(void* instance, void* ptr, MemoryHandle handle)
{ {
s_allocations.Remove(pHandle.id, pHandle.generation);
} }
public static Stack.Scope CreateScope() public static Stack.Scope CreateScope(StackAllocator* pSelf)
{ {
return s_stack.CreateScope(); return s_stack.CreateScope(pSelf->_handle);
} }
} }
private const uint _DEFAULT_MEMORY_POOL_SIZE = 512 * 1024; // 512 KB private const uint _DEFAULT_MEMORY_POOL_SIZE = 1024 * 1024; // 1 MB
private static readonly ArenaAllocator* s_pArenaAllocator; private static readonly ArenaAllocator* s_pArenaAllocator;
private static readonly HeapAllocator* s_pHeapAllocator; private static readonly HeapAllocator* s_pHeapAllocator;
@@ -247,6 +271,11 @@ public static unsafe class AllocationManager
/// </summary> /// </summary>
public static int LiveAllocationCount => s_allocations.Count; public static int LiveAllocationCount => s_allocations.Count;
/// <summary>
/// Gets a value indicating whether the debug layer is currently enabled.
/// </summary>
public static bool IsDebugLayerEnabled => s_debugLayer;
static AllocationManager() static AllocationManager()
{ {
s_pArenaAllocator = (ArenaAllocator*)NativeMemory.Alloc((nuint)sizeof(ArenaAllocator)); s_pArenaAllocator = (ArenaAllocator*)NativeMemory.Alloc((nuint)sizeof(ArenaAllocator));
@@ -432,16 +461,14 @@ public static unsafe class AllocationManager
/// <returns>A reference to the allocation pHandle associated with the specified allocator type.</returns> /// <returns>A reference to the allocation pHandle associated with the specified allocator type.</returns>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref AllocationHandle GetAllocationHandle(Allocator allocator) public static AllocationHandle GetAllocationHandle(Allocator allocator)
{ {
switch (allocator) switch (allocator)
{ {
case Allocator.Temp: case Allocator.Temp:
return ref s_pArenaAllocator->Handle; return s_pArenaAllocator->Handle;
case Allocator.Persistent: case Allocator.Persistent:
return ref s_pHeapAllocator->Handle; return s_pHeapAllocator->Handle;
case Allocator.Stack:
return ref s_pStackAllocator->Handle;
default: default:
throw new ArgumentException("Target allocator type does not support custom allocation.", nameof(allocator)); throw new ArgumentException("Target allocator type does not support custom allocation.", nameof(allocator));
} }
@@ -489,6 +516,7 @@ public static unsafe class AllocationManager
/// </summary> /// </summary>
/// <param name="ptr">A pointer to the memory block to be freed. The pointer must have been returned by a compatible heap allocation /// <param name="ptr">A pointer to the memory block to be freed. The pointer must have been returned by a compatible heap allocation
/// method and must not be null.</param> /// method and must not be null.</param>
/// <param name="handle">The handle representing the memory allocation to free. The handle must be valid and previously allocated.</param>
public static void HeapFree(void* ptr, MemoryHandle handle) public static void HeapFree(void* ptr, MemoryHandle handle)
{ {
if (s_debugLayer) if (s_debugLayer)
@@ -503,6 +531,18 @@ public static unsafe class AllocationManager
RemoveAllocation(handle); RemoveAllocation(handle);
} }
/// <summary>
/// Releases a block of unmanaged memory previously allocated by the heap allocator.
/// </summary>
/// <param name="handle">The handle representing the memory allocation to free. The handle must be valid and previously allocated.</param>
public static void HeapFree(MemoryHandle handle)
{
if (TryGetAllocation(handle, out var ptr))
{
HeapFree((void*)ptr, handle);
}
}
/// <summary> /// <summary>
/// Resets the temporary memory allocator, clearing all allocated memory. /// Resets the temporary memory allocator, clearing all allocated memory.
/// </summary> /// </summary>
@@ -519,7 +559,7 @@ public static unsafe class AllocationManager
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Stack.Scope CreateStackScope() public static Stack.Scope CreateStackScope()
{ {
return StackAllocator.CreateScope(); return StackAllocator.CreateScope(s_pStackAllocator);
} }
/// <summary> /// <summary>
@@ -534,6 +574,12 @@ public static unsafe class AllocationManager
return new MemoryHandle(id, generation); return new MemoryHandle(id, generation);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MemoryHandle GetMagicHandle()
{
return new MemoryHandle(int.MinValue, int.MinValue);
}
/// <summary> /// <summary>
/// Removes the memory allocation associated with the specified handle. /// Removes the memory allocation associated with the specified handle.
/// </summary> /// </summary>
@@ -565,6 +611,12 @@ public static unsafe class AllocationManager
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool ContainsAllocation(MemoryHandle handle) public static bool ContainsAllocation(MemoryHandle handle)
{ {
if (handle.id == int.MinValue && handle.generation == int.MinValue)
{
// Magic handle always valid
return true;
}
return s_allocations.Contains(handle.id, handle.generation); return s_allocations.Contains(handle.id, handle.generation);
} }

View File

@@ -25,8 +25,4 @@ public enum Allocator : byte
/// Allocator for persistent allocations. Allocations are not automatically released after use. /// Allocator for persistent allocations. Allocations are not automatically released after use.
/// </summary> /// </summary>
Persistent, Persistent,
/// <summary>
/// Allocator for stack allocations. Must have at least one active stack scope. Allocations are automatically released when the stack scope is exited.
/// </summary>
Stack
} }

View File

@@ -47,6 +47,8 @@ public unsafe struct DynamicArena : IDisposable
_root->arena = new Arena(initialSize); _root->arena = new Arena(initialSize);
_root->next = null; _root->next = null;
_current = _root; _current = _root;
_nodeCreationLock = 0;
} }
private bool TryCreateNewNode(nuint size) private bool TryCreateNewNode(nuint size)

View File

@@ -1,4 +1,4 @@
namespace Misaki.HighPerformance.LowLevel.Contracts; namespace Misaki.HighPerformance.LowLevel.Buffer;
/// <summary> /// <summary>
/// A structure that encapsulates function pointers for memory allocation operations. /// A structure that encapsulates function pointers for memory allocation operations.
@@ -8,7 +8,7 @@ public readonly unsafe struct AllocationHandle
/// <summary> /// <summary>
/// Gets a pointer to the allocator instance associated with this allocation handle. /// Gets a pointer to the allocator instance associated with this allocation handle.
/// </summary> /// </summary>
public void* Allocator public void* pAllocator
{ {
get; get;
} }
@@ -47,7 +47,7 @@ public readonly unsafe struct AllocationHandle
/// <param name="free">The function used to free allocated memory.</param> /// <param name="free">The function used to free allocated memory.</param>
public AllocationHandle(void* allocator, AllocFunc alloc, ReallocFunc realloc, FreeFunc free) public AllocationHandle(void* allocator, AllocFunc alloc, ReallocFunc realloc, FreeFunc free)
{ {
Allocator = allocator; pAllocator = allocator;
Alloc = alloc; Alloc = alloc;
Realloc = realloc; Realloc = realloc;
Free = free; Free = free;
@@ -59,14 +59,14 @@ public readonly unsafe struct AllocationHandle
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The allocator must be pined to a specific memory region. /// The allocator must be pined to a specific memory region.
/// Otherwise the reference of the <see cref="AllocationHandle.Allocator"/>, may become invalid and lead to undefined behavior. /// Otherwise the reference of the <see cref="AllocationHandle.pAllocator"/>, may become invalid and lead to undefined behavior.
/// </remarks> /// </remarks>
public interface IAllocator public interface IAllocator
{ {
/// <summary> /// <summary>
/// Gets a reference to the allocation handle associated with this allocator. /// Gets a reference to the allocation handle associated with this allocator.
/// </summary> /// </summary>
ref AllocationHandle Handle AllocationHandle Handle
{ {
get; get;
} }

View File

@@ -14,11 +14,15 @@ public unsafe struct Stack : IDisposable
public readonly ref struct Scope : IDisposable public readonly ref struct Scope : IDisposable
{ {
private readonly Stack* _allocator; private readonly Stack* _allocator;
private readonly AllocationHandle _handle;
private readonly nuint _originalOffset; private readonly nuint _originalOffset;
internal Scope(Stack* allocator) public readonly AllocationHandle AllocationHandle => _handle;
internal Scope(Stack* allocator, AllocationHandle handle)
{ {
_allocator = allocator; _allocator = allocator;
_handle = handle;
_originalOffset = allocator->_offset; _originalOffset = allocator->_offset;
_allocator->_activeScopeCount++; _allocator->_activeScopeCount++;
} }
@@ -84,12 +88,15 @@ public unsafe struct Stack : IDisposable
/// <summary> /// <summary>
/// Creates a new scope instance associated with the current stack context. /// Creates a new scope instance associated with the current stack context.
/// </summary> /// </summary>
/// <remarks>
/// The instance of <see cref="Stack"/> must be pinned or allocated on the native heap to ensure that the pointer remains valid for the lifetime of the scope.
/// </remarks>
/// <returns>A <see cref="Scope"/> object that represents a scope tied to this stack.</returns> /// <returns>A <see cref="Scope"/> object that represents a scope tied to this stack.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Scope CreateScope() public Scope CreateScope(AllocationHandle handle)
{ {
EnsureInitialize(); EnsureInitialize();
return new Scope((Stack*)Unsafe.AsPointer(ref this)); return new Scope((Stack*)Unsafe.AsPointer(ref this), handle);
} }
/// <summary> /// <summary>

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -13,12 +12,12 @@ public unsafe struct UnTypedArray : IUnTypedCollection
private nuint _alignment; private nuint _alignment;
private MemoryHandle _memoryHandle; private MemoryHandle _memoryHandle;
private AllocationHandle* _allocationHandle; private AllocationHandle _allocationHandle;
public readonly nuint Size => _size; public readonly nuint Size => _size;
public readonly nuint Alignment => _alignment; public readonly nuint Alignment => _alignment;
public readonly bool IsCreated => _buffer != null && _allocationHandle != null && _memoryHandle.IsValid; public readonly bool IsCreated => _buffer != null && _allocationHandle.pAllocator != null && _memoryHandle.IsValid;
/// <summary> /// <summary>
/// Constructs an UnsafeArray with a default size of 1 and uses the Persistent allocator. /// Constructs an UnsafeArray with a default size of 1 and uses the Persistent allocator.
@@ -28,7 +27,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
{ {
} }
public UnTypedArray(nuint size, nuint alignment, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnTypedArray(nuint size, nuint alignment, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
if (size <= 0) if (size <= 0)
{ {
@@ -36,12 +35,12 @@ public unsafe struct UnTypedArray : IUnTypedCollection
} }
MemoryHandle memHandle; MemoryHandle memHandle;
_buffer = handle.Alloc(_allocationHandle->Allocator, size, alignment, allocationOption, &memHandle); _buffer = handle.Alloc(_allocationHandle.pAllocator, size, alignment, allocationOption, &memHandle);
_size = size; _size = size;
_alignment = alignment; _alignment = alignment;
_memoryHandle = memHandle; _memoryHandle = memHandle;
_allocationHandle = (AllocationHandle*)Unsafe.AsPointer(ref handle); _allocationHandle = handle;
} }
/// <summary> /// <summary>
@@ -52,7 +51,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
/// <param name="allocationOption">Determines how the memory should be allocated.</param> /// <param name="allocationOption">Determines how the memory should be allocated.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception>
public UnTypedArray(nuint size, nuint alignment, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnTypedArray(nuint size, nuint alignment, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(size, alignment, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(size, alignment, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }
@@ -88,7 +87,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
} }
MemoryHandle memHandle = _memoryHandle; MemoryHandle memHandle = _memoryHandle;
_buffer = _allocationHandle->Realloc(_allocationHandle->Allocator, _buffer, _size, newSize, _alignment, option, &memHandle); _buffer = _allocationHandle.Realloc(_allocationHandle.pAllocator, _buffer, _size, newSize, _alignment, option, &memHandle);
_size = newSize; _size = newSize;
_memoryHandle = memHandle; _memoryHandle = memHandle;
} }
@@ -233,12 +232,11 @@ public unsafe struct UnTypedArray : IUnTypedCollection
return; return;
} }
if (_allocationHandle != null) if (_allocationHandle.pAllocator != null)
{ {
_allocationHandle->Free(_allocationHandle->Allocator, _buffer, _memoryHandle); _allocationHandle.Free(_allocationHandle.pAllocator, _buffer, _memoryHandle);
} }
_allocationHandle = null;
_buffer = null; _buffer = null;
_size = 0; _size = 0;
_alignment = 0; _alignment = 0;

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections; using System.Collections;
using System.Diagnostics; using System.Diagnostics;
@@ -50,7 +49,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
private T* _buffer; private T* _buffer;
private int _count; private int _count;
private MemoryHandle _memoryHandle; private MemoryHandle _memoryHandle;
private AllocationHandle* _allocationHandle; private AllocationHandle _allocationHandle;
public readonly int Count => _count; public readonly int Count => _count;
@@ -74,7 +73,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
} }
} }
public readonly bool IsCreated => _buffer != null && _allocationHandle != null && _memoryHandle.IsValid; public readonly bool IsCreated => _buffer != null && _allocationHandle.pAllocator != null && _memoryHandle.IsValid;
public Enumerator GetEnumerator() => new((UnsafeArray<T>*)UnsafeUtility.AddressOf(ref this)); public Enumerator GetEnumerator() => new((UnsafeArray<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator(); IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
@@ -95,7 +94,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
/// <param name="handle">A reference to an AllocationHandle that manages the memory allocation for the array.</param> /// <param name="handle">A reference to an AllocationHandle that manages the memory allocation for the array.</param>
/// <param name="allocationOption">Specifies how the memory should be allocated.</param> /// <param name="allocationOption">Specifies how the memory should be allocated.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception>
public UnsafeArray(int count, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeArray(int count, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
if (count < 0) if (count < 0)
{ {
@@ -103,11 +102,11 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
} }
MemoryHandle memHandle; MemoryHandle memHandle;
var buff = handle.Alloc(handle.Allocator, (nuint)(count * sizeof(T)), AlignOf<T>(), allocationOption, &memHandle); var buff = handle.Alloc(handle.pAllocator, (nuint)(count * sizeof(T)), AlignOf<T>(), allocationOption, &memHandle);
_buffer = (T*)buff; _buffer = (T*)buff;
_memoryHandle = memHandle; _memoryHandle = memHandle;
_allocationHandle = (AllocationHandle*)Unsafe.AsPointer(ref handle); _allocationHandle = handle;
_count = count; _count = count;
} }
@@ -119,7 +118,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
/// <param name="allocationOption">Determines how the memory should be allocated.</param> /// <param name="allocationOption">Determines how the memory should be allocated.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception>
public UnsafeArray(int count, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnsafeArray(int count, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(count, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(count, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }
@@ -181,7 +180,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
MemoryHandle memHandle = _memoryHandle; MemoryHandle memHandle = _memoryHandle;
var elemSize = SizeOf<T>(); var elemSize = SizeOf<T>();
_buffer = (T*)_allocationHandle->Realloc(_allocationHandle->Allocator, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option, &memHandle); _buffer = (T*)_allocationHandle.Realloc(_allocationHandle.pAllocator, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option, &memHandle);
_memoryHandle = memHandle; _memoryHandle = memHandle;
_count = newSize; _count = newSize;
} }
@@ -245,12 +244,11 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
return; return;
} }
if (_allocationHandle != null) if (_allocationHandle.pAllocator != null)
{ {
_allocationHandle->Free(_allocationHandle->Allocator, _buffer, _memoryHandle); _allocationHandle.Free(_allocationHandle.pAllocator, _buffer, _memoryHandle);
} }
_allocationHandle = null;
_buffer = null; _buffer = null;
_count = 0; _count = 0;
} }

View File

@@ -1,5 +1,4 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -66,19 +65,19 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class. /// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// </summary> /// </summary>
public UnsafeBitSet(int minimalLength, ref AllocationHandle handle, AllocationOption option = AllocationOption.None) public UnsafeBitSet(int minimalLength, AllocationHandle handle, AllocationOption option = AllocationOption.None)
{ {
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE); var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
var length = RoundToPadding(uints); var length = RoundToPadding(uints);
_bits = new UnsafeArray<uint>(length, ref handle, option); _bits = new UnsafeArray<uint>(length, handle, option);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class. /// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// </summary> /// </summary>
public UnsafeBitSet(int minimalLength, Allocator allocator, AllocationOption option = AllocationOption.None) public UnsafeBitSet(int minimalLength, Allocator allocator, AllocationOption option = AllocationOption.None)
: this(minimalLength, ref AllocationManager.GetAllocationHandle(allocator), option) : this(minimalLength, AllocationManager.GetAllocationHandle(allocator), option)
{ {
} }
@@ -88,7 +87,7 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
public UnsafeBitSet(Span<uint> bits, Allocator allocator) public UnsafeBitSet(Span<uint> bits, Allocator allocator)
{ {
_bits = new UnsafeArray<uint>(bits.Length, allocator, AllocationOption.None); _bits = new UnsafeArray<uint>(bits.Length, allocator, AllocationOption.None);
_bits.CopyFrom(bits); _bits.CopyFrom<UnsafeArray<uint>, uint>(bits);
_highestBit = 0; _highestBit = 0;
_max = _bits.Count * (_BIT_SIZE + 1) - 1; // Calculate the maximum index in use _max = _bits.Count * (_BIT_SIZE + 1) - 1; // Calculate the maximum index in use

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -82,13 +81,13 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
{ {
} }
public UnsafeHashMap(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeHashMap(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
_helper = new HashMapHelper<TKey>(capacity, sizeof(TValue), (int)AlignOf<TValue>(), HashMapHelper<TKey>.MINIMAL_CAPACITY, ref handle, allocationOption); _helper = new HashMapHelper<TKey>(capacity, sizeof(TValue), (int)AlignOf<TValue>(), HashMapHelper<TKey>.MINIMAL_CAPACITY, handle, allocationOption);
} }
public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -55,13 +54,13 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>, IEnumerable<T>
{ {
} }
public UnsafeHashSet(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeHashSet(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
_helper = new HashMapHelper<T>(capacity, 0, 0, HashMapHelper<T>.MINIMAL_CAPACITY, ref handle, allocationOption); _helper = new HashMapHelper<T>(capacity, 0, 0, HashMapHelper<T>.MINIMAL_CAPACITY, handle, allocationOption);
} }
public UnsafeHashSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnsafeHashSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }

View File

@@ -1,8 +1,6 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -144,9 +142,9 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
/// <param name="capacity">Specifies the number of initial capacity to allocate in the list, which must be greater than zero.</param> /// <param name="capacity">Specifies the number of initial capacity to allocate in the list, which must be greater than zero.</param>
/// <param name="handle">A reference to an AllocationHandle that manages the memory allocation for the array.</param> /// <param name="handle">A reference to an AllocationHandle that manages the memory allocation for the array.</param>
/// <param name="allocationOption">Specifies how the memory should be allocated.</param> /// <param name="allocationOption">Specifies how the memory should be allocated.</param>
public UnsafeList(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeList(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
_array = new UnsafeArray<T>(capacity, ref handle, allocationOption); _array = new UnsafeArray<T>(capacity, handle, allocationOption);
_count = 0; _count = 0;
} }
@@ -157,7 +155,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
/// <param name="allocator">Specifies the allocator to use for memory allocation, which determines the memory management strategy.</param> /// <param name="allocator">Specifies the allocator to use for memory allocation, which determines the memory management strategy.</param>
/// <param name="allocationOption">Determines how the memory should be allocated.</param> /// <param name="allocationOption">Determines how the memory should be allocated.</param>
public UnsafeList(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnsafeList(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -75,15 +74,15 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
{ {
} }
public UnsafeQueue(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeQueue(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
_array = new UnsafeArray<T>(capacity, ref handle, allocationOption); _array = new UnsafeArray<T>(capacity, handle, allocationOption);
_count = 0; _count = 0;
_offset = 0; _offset = 0;
} }
public UnsafeQueue(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.None) public UnsafeQueue(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.None)
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationType) : this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationType)
{ {
} }

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -79,17 +78,17 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
/// <param name="handle">A reference to the allocation handle used to manage memory for the slot map.</param> /// <param name="handle">A reference to the allocation handle used to manage memory for the slot map.</param>
/// <param name="allocationOption">The allocation options to use when creating internal data structures. The default is AllocationOption.None.</param> /// <param name="allocationOption">The allocation options to use when creating internal data structures. The default is AllocationOption.None.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when capacity is less than or equal to zero.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when capacity is less than or equal to zero.</exception>
public UnsafeSlotMap(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeSlotMap(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
if (capacity <= 0) if (capacity <= 0)
{ {
throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero."); throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero.");
} }
_data = new UnsafeArray<T>(capacity, ref handle, allocationOption); _data = new UnsafeArray<T>(capacity, handle, allocationOption);
_generations = new UnsafeArray<int>(capacity, ref handle, allocationOption); _generations = new UnsafeArray<int>(capacity, handle, allocationOption);
_freeSlots = new UnsafeQueue<int>(capacity, ref handle, allocationOption); _freeSlots = new UnsafeQueue<int>(capacity, handle, allocationOption);
_validBits = new UnsafeBitSet(GetBitSetCapacity(capacity), ref handle, allocationOption); _validBits = new UnsafeBitSet(GetBitSetCapacity(capacity), handle, allocationOption);
if (!allocationOption.HasFlag(AllocationOption.Clear)) if (!allocationOption.HasFlag(AllocationOption.Clear))
{ {
@@ -109,7 +108,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
/// <param name="allocator">The allocator to use for memory management of the slot map.</param> /// <param name="allocator">The allocator to use for memory management of the slot map.</param>
/// <param name="allocationOption">The allocation option that determines how memory is allocated. The default is AllocationOption.None.</param> /// <param name="allocationOption">The allocation option that determines how memory is allocated. The default is AllocationOption.None.</param>
public UnsafeSlotMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnsafeSlotMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -81,18 +80,18 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
/// <param name="handle">A reference to an AllocationHandle that manages the memory allocation for the sparse set.</param> /// <param name="handle">A reference to an AllocationHandle that manages the memory allocation for the sparse set.</param>
/// <param name="allocationOption">Specifies how the memory should be allocated.</param> /// <param name="allocationOption">Specifies how the memory should be allocated.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified capacity is less than or equal to zero.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when the specified capacity is less than or equal to zero.</exception>
public UnsafeSparseSet(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeSparseSet(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
if (capacity <= 0) if (capacity <= 0)
{ {
throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero."); throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero.");
} }
_dense = new UnsafeArray<T>(capacity, ref handle, allocationOption); _dense = new UnsafeArray<T>(capacity, handle, allocationOption);
_generations = new UnsafeArray<int>(capacity, ref handle, allocationOption); _generations = new UnsafeArray<int>(capacity, handle, allocationOption);
_sparse = new UnsafeArray<int>(capacity, ref handle, allocationOption); _sparse = new UnsafeArray<int>(capacity, handle, allocationOption);
_reverse = new UnsafeArray<int>(capacity, ref handle, allocationOption); _reverse = new UnsafeArray<int>(capacity, handle, allocationOption);
_freeSparse = new UnsafeStack<int>(capacity, ref handle, allocationOption); _freeSparse = new UnsafeStack<int>(capacity, handle, allocationOption);
if (!allocationOption.HasFlag(AllocationOption.Clear)) if (!allocationOption.HasFlag(AllocationOption.Clear))
{ {
@@ -115,7 +114,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
/// <param name="allocationOption">Determines how the memory should be allocated.</param> /// <param name="allocationOption">Determines how the memory should be allocated.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified capacity is less than or equal to zero.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when the specified capacity is less than or equal to zero.</exception>
public UnsafeSparseSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnsafeSparseSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -72,9 +71,9 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
/// <param name="capacity">The number of elements the stack can initially hold. Must be greater than zero.</param> /// <param name="capacity">The number of elements the stack can initially hold. Must be greater than zero.</param>
/// <param name="handle">A reference to an AllocationHandle used to manage the underlying memory allocation for the stack.</param> /// <param name="handle">A reference to an AllocationHandle used to manage the underlying memory allocation for the stack.</param>
/// <param name="allocationOption">Specifies additional options for memory allocation. The default is AllocationOption.None.</param> /// <param name="allocationOption">Specifies additional options for memory allocation. The default is AllocationOption.None.</param>
public UnsafeStack(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) public UnsafeStack(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{ {
_array = new UnsafeArray<T>(capacity, ref handle, allocationOption); _array = new UnsafeArray<T>(capacity, handle, allocationOption);
} }
/// <summary> /// <summary>
@@ -85,7 +84,7 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
/// <param name="allocator">The allocator to use for memory management of the stack's storage.</param> /// <param name="allocator">The allocator to use for memory management of the stack's storage.</param>
/// <param name="allocationOption">The allocation option that determines how memory is allocated for the stack. The default is AllocationOption.None.</param> /// <param name="allocationOption">The allocation option that determines how memory is allocated for the stack. The default is AllocationOption.None.</param>
public UnsafeStack(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) public UnsafeStack(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) : this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{ {
} }

View File

@@ -6,7 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<AssemblyVersion>1.2.8</AssemblyVersion> <AssemblyVersion>1.3.0</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl> <PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>

View File

@@ -1,6 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Contracts;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -78,7 +77,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
private readonly int _log2MinGrowth; private readonly int _log2MinGrowth;
private MemoryHandle _memoryHandle; private MemoryHandle _memoryHandle;
private AllocationHandle* _allocationHandle; private AllocationHandle _allocationHandle;
public const int MINIMAL_CAPACITY = 64; public const int MINIMAL_CAPACITY = 64;
@@ -90,7 +89,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
public readonly bool IsEmpty => !IsCreated || _count == 0; public readonly bool IsEmpty => !IsCreated || _count == 0;
public readonly bool IsCreated => _buffer != null && _allocationHandle != null && _memoryHandle.IsValid; public readonly bool IsCreated => _buffer != null && _allocationHandle.pAllocator != null && _memoryHandle.IsValid;
private static int CalculateDataSize(int capacity, int bucketCapacity, int sizeOfTValue, out int outKeyOffset, out int outNextOffset, out int outBucketOffset) private static int CalculateDataSize(int capacity, int bucketCapacity, int sizeOfTValue, out int outKeyOffset, out int outNextOffset, out int outBucketOffset)
{ {
@@ -110,7 +109,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return totalSize; return totalSize;
} }
public HashMapHelper(int capacity, int sizeOfTValue, int alignOfTValue, uint minGrowth, ref AllocationHandle handle, AllocationOption allocationOption) public HashMapHelper(int capacity, int sizeOfTValue, int alignOfTValue, uint minGrowth, AllocationHandle handle, AllocationOption allocationOption)
{ {
if (capacity <= 0) if (capacity <= 0)
{ {
@@ -133,7 +132,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
_sizeOfTValue = sizeOfTValue; _sizeOfTValue = sizeOfTValue;
_log2MinGrowth = BitOperations.Log2(minGrowth); _log2MinGrowth = BitOperations.Log2(minGrowth);
_allocationHandle = (AllocationHandle*)Unsafe.AsPointer(ref handle); _allocationHandle = handle;
var totalSize = CalculateDataSize(_capacity, _bucketCapacity, sizeOfTValue, var totalSize = CalculateDataSize(_capacity, _bucketCapacity, sizeOfTValue,
out var keyOffset, out var nextOffset, out var bucketOffset); out var keyOffset, out var nextOffset, out var bucketOffset);
@@ -193,7 +192,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
private void AllocateBuffer(int totalSize, int keyOffset, int nextOffset, int bucketOffset, AllocationOption allocationOption) private void AllocateBuffer(int totalSize, int keyOffset, int nextOffset, int bucketOffset, AllocationOption allocationOption)
{ {
MemoryHandle memHandle; MemoryHandle memHandle;
var buf = (byte*)_allocationHandle->Alloc(_allocationHandle->Allocator, (uint)totalSize, (nuint)_alignment, allocationOption, &memHandle); var buf = (byte*)_allocationHandle.Alloc(_allocationHandle.pAllocator, (uint)totalSize, (nuint)_alignment, allocationOption, &memHandle);
_buffer = buf; _buffer = buf;
_keys = (TKey*)(_buffer + keyOffset); _keys = (TKey*)(_buffer + keyOffset);
@@ -229,7 +228,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
} }
} }
_allocationHandle->Free(_allocationHandle->Allocator, oldBuffer, oldMemoryHandle); _allocationHandle.Free(_allocationHandle.pAllocator, oldBuffer, oldMemoryHandle);
} }
public void Resize(int newCapacity) public void Resize(int newCapacity)
@@ -523,9 +522,9 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return; return;
} }
if (_allocationHandle != null) if (_allocationHandle.pAllocator != null)
{ {
_allocationHandle->Free(_allocationHandle->Allocator, _buffer, _memoryHandle); _allocationHandle.Free(_allocationHandle.pAllocator, _buffer, _memoryHandle);
} }
_buffer = null; _buffer = null;

View File

@@ -24,6 +24,17 @@ public static unsafe partial class MemoryUtility
return NativeMemory.Alloc(size); return NativeMemory.Alloc(size);
} }
/// <summary>
/// Allocates a block of memory of the specified size in bytes and initializes it to zero.
/// </summary>
/// <param name="size">Specifies the number of bytes to allocate in memory.</param>
/// <returns>Returns a pointer to the allocated and zero-initialized memory block.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void* Calloc(nuint size)
{
return NativeMemory.AllocZeroed(size);
}
/// <summary> /// <summary>
/// Allocates a block of memory with a specified size and alignment. /// Allocates a block of memory with a specified size and alignment.
/// </summary> /// </summary>
@@ -114,7 +125,7 @@ public static unsafe partial class MemoryUtility
/// <param name="destination">Specifies the memory address where the copied data will be stored.</param> /// <param name="destination">Specifies the memory address where the copied data will be stored.</param>
/// <param name="size">Defines the number of bytes to be copied from the source to the destination.</param> /// <param name="size">Defines the number of bytes to be copied from the source to the destination.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MemCpy(void* source, void* destination, nuint size) public static void MemCpy(void* destination, void* source, nuint size)
{ {
NativeMemory.Copy(source, destination, size); NativeMemory.Copy(source, destination, size);
} }

View File

@@ -1,4 +1,3 @@
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.LowLevel.Utilities; namespace Misaki.HighPerformance.LowLevel.Utilities;

View File

@@ -22,38 +22,17 @@
//BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.CollectionBenchmark>(); //BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.CollectionBenchmark>();
//using Misaki.HighPerformance.LowLevel.Buffer;
//using Misaki.HighPerformance.LowLevel.Collections;
//using Misaki.HighPerformance.LowLevel.Utilities;
//using (AllocationManager.CreateStackScope())
//{
// var array = new UnsafeArray<int>(10, Allocator.Stack);
// for (var i = 0; i < array.Count; i++)
// {
// array[i] = i;
// }
// foreach (var item in array.AsSpan())
// {
// Console.WriteLine(item);
// }
//}
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Collections;
AllocationManager.EnableDebugLayer();
var arr1 = new Misaki.HighPerformance.LowLevel.Collections.UnsafeArray<int>(10, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); using var scope = AllocationManager.CreateStackScope();
var arr2 = arr1; var array = new UnsafeArray<int>(10, scope.AllocationHandle);
arr2.CopyFrom(arr1.AsSpan()); for (var i = 0; i < array.Count; i++)
//arr1.Dispose();
try
{ {
arr2[0] = 42; // This should throw an exception because arr1 has been disposed. array[i] = i;
//arr2.Dispose();
AllocationManager.Dispose();
} }
catch (Exception ex)
foreach (var item in array.AsSpan())
{ {
Console.WriteLine($"Caught expected exception: {ex.Message}"); Console.WriteLine(item);
} }

View File

@@ -1,10 +1,5 @@
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Misaki.HighPerformance.Test.UnitTest.Collections; namespace Misaki.HighPerformance.Test.UnitTest.Collections;