feat(memory): transition to AllocationHandle API

Replaced the deprecated Allocator API with the new AllocationHandle API across the codebase. Updated constructors, methods, and tests to use AllocationHandle for memory management. Marked Allocator-based methods as [Obsolete] and provided alternatives.

Added OwnershipTransferAnalyzer to detect ownership transfer issues and introduced OwnershipTransferAttribute for marking parameters. Enhanced DefensiveCopyAnalyzer with additional checks for readonly and ValueType instances.

Refactored internal memory management in AllocationManager and updated benchmarks, utilities, and documentation to reflect the changes.

BREAKING CHANGE: Deprecated Allocator API in favor of AllocationHandle. Updated constructors and methods to use AllocationHandle. Users must migrate to the new API.
This commit is contained in:
2026-04-12 17:50:12 +09:00
parent a0deadc363
commit 9c4faa107a
40 changed files with 260 additions and 85 deletions

View File

@@ -620,7 +620,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return MoveNextSearch(ref bucketIndex, ref nextIndex, out index);
}
internal UnsafeArray<TKey> GetKeyArray(Allocator allocator)
internal UnsafeArray<TKey> GetKeyArray(AllocationHandle allocator)
{
ThrowIfNotCreated();
@@ -640,7 +640,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return result;
}
internal UnsafeArray<TValue> GetValueArray<TValue>(Allocator allocator)
internal UnsafeArray<TValue> GetValueArray<TValue>(AllocationHandle allocator)
where TValue : unmanaged
{
ThrowIfNotCreated();
@@ -661,7 +661,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return result;
}
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays<TValue>(Allocator allocator)
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays<TValue>(AllocationHandle allocator)
where TValue : unmanaged
{
ThrowIfNotCreated();

View File

@@ -1,7 +1,6 @@
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -119,10 +118,10 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
}
/// <summary>
/// Invalid constructor, use <see cref="UnsafeArray(int, Allocator, AllocationOption)"/> or <see cref="UnsafeArray(int, AllocationHandle, AllocationOption)"/> instead.
/// Initializes a new instance of UnsafeArray with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeArray()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -157,6 +156,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
/// <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>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception>
[Obsolete("Use AllocationHandle instead.")]
public UnsafeArray(int count, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(count, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{

View File

@@ -82,11 +82,11 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
public readonly bool IsCreated => _bits.IsCreated;
/// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// Initializes a new instance of UnsafeBitSet with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeBitSet()
{
_bits = new UnsafeArray<uint>(0, Allocator.Invalid, AllocationOption.None);
_bits = new UnsafeArray<uint>(1, AllocationHandle.Persistent, AllocationOption.None);
}
/// <summary>
@@ -106,6 +106,7 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
/// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// </summary>
[Obsolete("Use AllocationHandle instead.")]
public UnsafeBitSet(int minimalLength, Allocator allocator, AllocationOption option = AllocationOption.None)
: this(minimalLength, AllocationManager.GetAllocationHandle(allocator), option)
{
@@ -114,6 +115,7 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
/// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// </summary>
[Obsolete("Use AllocationHandle instead.")]
public UnsafeBitSet(Span<uint> bits, Allocator allocator)
{
_bits = new UnsafeArray<uint>(bits.Length, allocator, AllocationOption.None);

View File

@@ -97,6 +97,7 @@ public unsafe struct UnsafeChunkedQueue<T> : IDisposable
#endif
}
[Obsolete("Use AllocationHandle instead.")]
public UnsafeChunkedQueue(int capacityPerChunk, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacityPerChunk, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{

View File

@@ -75,10 +75,10 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
}
/// <summary>
/// Invalid constructor, use <see cref="UnsafeHashMap(int, Allocator, AllocationOption)"/> or <see cref="UnsafeHashMap(int, AllocationHandle, AllocationOption)"/> instead.
/// Initializes a new instance of UnsafeHashMap with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeHashMap()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -87,6 +87,7 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
_helper = new HashMapHelper<TKey>(capacity, sizeof(TValue), (int)AlignOf<TValue>(), HashMapHelper<TKey>.MINIMAL_CAPACITY, handle, allocationOption);
}
[Obsolete("Use AllocationHandle instead.")]
public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{
@@ -235,32 +236,34 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeHashCollection<KeyValu
/// <summary>
/// Retrieves an array of keys from the hash map.
/// </summary>
/// <param name="allocationHandle">The allocation handle to use to allocate the array.</param>
/// <returns>An array containing the keys stored in the hash map.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<TKey> GetKeyArray(Allocator allocator)
public UnsafeArray<TKey> GetKeyArray(AllocationHandle allocationHandle)
{
return _helper.GetKeyArray(allocator);
return _helper.GetKeyArray(allocationHandle);
}
/// <summary>
/// Retrieves an array of values from the underlying hash map.
/// </summary>
/// <param name="allocationHandle">The allocation handle to use to allocate the array.</param>
/// <returns>An UnsafeArray containing the values stored in the hash map.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<TValue> GetValueArray(Allocator allocator)
public UnsafeArray<TValue> GetValueArray(AllocationHandle allocationHandle)
{
return _helper.GetValueArray<TValue>(allocator);
return _helper.GetValueArray<TValue>(allocationHandle);
}
/// <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.
/// 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>
/// <param name="allocationHandle">The allocation handle to use to allocate the array.</param>
/// <returns>Returns an UnsafeArray containing KeyValuePair objects.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays(Allocator allocator)
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays(AllocationHandle allocationHandle)
{
return _helper.GetKeyValueArrays<TValue>(allocator);
return _helper.GetKeyValueArrays<TValue>(allocationHandle);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -43,10 +43,10 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>
public readonly bool IsCreated => _helper.IsCreated;
/// <summary>
/// Invalid constructor. Use <see cref="UnsafeHashSet(int, Allocator, AllocationOption)"/> or <see cref="UnsafeHashSet(int, AllocationHandle, AllocationOption)"/> instead."/>
/// Initializes a new instance of UnsafeHashSet with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeHashSet()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -55,6 +55,7 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>
_helper = new HashMapHelper<T>(capacity, 0, 0, HashMapHelper<T>.MINIMAL_CAPACITY, handle, allocationOption);
}
[Obsolete("Use AllocationHandle instead.")]
public UnsafeHashSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{
@@ -112,12 +113,12 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>
/// <summary>
/// Returns an array with a copy of this set's values (in no particular order).
/// </summary>
/// <param name="allocator">The allocator to use.</param>
/// <param name="allocationHandle">The allocation handle to use to allocate the array.</param>
/// <returns>An array with a copy of the set's values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<T> ToNativeArray(Allocator allocator)
public UnsafeArray<T> ToUnsafeArray(AllocationHandle allocationHandle)
{
return _helper.GetKeyArray(allocator);
return _helper.GetKeyArray(allocationHandle);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -174,7 +174,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
/// Invalid constructor, use <see cref="UnsafeList(int, Allocator, AllocationOption)"/> or <see cref="UnsafeList(int, AllocationHandle, AllocationOption)"/> instead.
/// </summary>
public UnsafeList()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -196,6 +196,7 @@ 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="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>
[Obsolete("Use AllocationHandle instead.")]
public UnsafeList(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{

View File

@@ -112,8 +112,11 @@ public unsafe struct UnsafeMultiHashMap<TKey, TValue> : IUnsafeHashCollection<Ke
public readonly int Capacity => _helper.Capacity;
public readonly bool IsCreated => _helper.IsCreated;
/// <summary>
/// Initializes a new instance of UnsafeMultiHashMap with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeMultiHashMap()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -122,6 +125,7 @@ public unsafe struct UnsafeMultiHashMap<TKey, TValue> : IUnsafeHashCollection<Ke
_helper = new HashMapHelper<TKey>(capacity, sizeof(TValue), (int)AlignOf<TValue>(), HashMapHelper<TKey>.MINIMAL_CAPACITY, handle, allocationOption);
}
[Obsolete("Use AllocationHandle instead.")]
public UnsafeMultiHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{
@@ -239,21 +243,21 @@ public unsafe struct UnsafeMultiHashMap<TKey, TValue> : IUnsafeHashCollection<Ke
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<TKey> GetKeyArray(Allocator allocator)
public UnsafeArray<TKey> GetKeyArray(AllocationHandle allocationHandle)
{
return _helper.GetKeyArray(allocator);
return _helper.GetKeyArray(allocationHandle);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<TValue> GetValueArray(Allocator allocator)
public UnsafeArray<TValue> GetValueArray(AllocationHandle allocationHandle)
{
return _helper.GetValueArray<TValue>(allocator);
return _helper.GetValueArray<TValue>(allocationHandle);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays(Allocator allocator)
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays(AllocationHandle allocationHandle)
{
return _helper.GetKeyValueArrays<TValue>(allocator);
return _helper.GetKeyValueArrays<TValue>(allocationHandle);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -58,10 +58,10 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
}
/// <summary>
/// Invalid constructor. Use <see cref="UnsafeQueue(int, Allocator, AllocationOption)"/> or <see cref="UnsafeQueue(int, AllocationHandle, AllocationOption)"/> instead."/>
/// Initializes a new instance of UnsafeQueue with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeQueue()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -72,6 +72,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
_offset = 0;
}
[Obsolete("Use AllocationHandle instead.")]
public UnsafeQueue(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationType)
{

View File

@@ -82,10 +82,10 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
public readonly bool IsCreated => _data.IsCreated && _generations.IsCreated && _freeSlots.IsCreated && _validBits.IsCreated;
/// <summary>
/// Invalid constructor. Use <see cref="UnsafeSlotMap(int, Allocator, AllocationOption)"/> or <see cref="UnsafeSlotMap(int, AllocationHandle, AllocationOption)"/> instead."/>
/// Initializes a new instance of UnsafeSlotMap with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeSlotMap()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -126,6 +126,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
/// <param name="capacity">The initial number of slots to allocate for the map. Must be greater than zero.</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>
[Obsolete("Use AllocationHandle instead.")]
public UnsafeSlotMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{

View File

@@ -88,7 +88,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
/// Constructs an UnsafeSparseSet with a default size of 1 and uses the Persistent allocator.
/// </summary>
public UnsafeSparseSet()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -133,6 +133,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
/// <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>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified capacity is less than or equal to zero.</exception>
[Obsolete("Use AllocationHandle instead.")]
public UnsafeSparseSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{

View File

@@ -77,10 +77,10 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
public readonly bool IsCreated => _array.IsCreated;
/// <summary>
/// Invalid constructor, use <see cref="UnsafeStack(int, Allocator, AllocationOption)"/> or <see cref="UnsafeStack(int, AllocationHandle, AllocationOption)"/> instead.
/// Initializes a new instance of UnsafeStack with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeStack()
: this(0, Allocator.Invalid)
: this(1, AllocationHandle.Persistent)
{
}
@@ -102,6 +102,7 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
/// <param name="capacity">The initial number of elements that the stack can hold. Must be greater than zero.</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>
[Obsolete("Use AllocationHandle instead.")]
public UnsafeStack(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
: this(capacity, AllocationManager.GetAllocationHandle(allocator), allocationOption)
{