Files
Misaki.HighPerformance/Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs
Misaki 9c4faa107a 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.
2026-04-12 17:50:12 +09:00

227 lines
7.1 KiB
C#

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;
namespace Misaki.HighPerformance.LowLevel.Collections;
internal class UnsafeStackDebugView<T>
where T : unmanaged
{
private readonly UnsafeStack<T> _stack;
public UnsafeStackDebugView(UnsafeStack<T> stack)
{
_stack = stack;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public unsafe T[] Items
{
get
{
var items = new T[_stack.Count];
var pItems = (T*)_stack.GetUnsafePtr();
for (var i = 0; i < _stack.Count; i++)
{
items[i] = pItems[i];
}
return items;
}
}
}
/// <summary>
/// Provides a high-performance, unsafe stack data structure for unmanaged types, supporting manual memory management
/// and allocation control.
/// </summary>
/// <typeparam name="T">The type of elements stored in the stack. Must be an unmanaged type.</typeparam>
[DebuggerTypeProxy(typeof(UnsafeStackDebugView<>))]
public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
where T : unmanaged
{
public ref struct Enumerator
{
private readonly ref UnsafeStack<T> _collection;
private int _index;
public readonly ref T Current => ref _collection._array[_index];
public Enumerator(ref UnsafeStack<T> collection)
{
_collection = ref collection;
_index = collection.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index--;
return _index >= 0;
}
public void Reset()
{
_index = _collection.Count;
}
}
private UnsafeArray<T> _array;
private int _count;
public readonly int Count => _count;
public readonly int Capacity => _array.Count;
public readonly bool IsCreated => _array.IsCreated;
/// <summary>
/// Initializes a new instance of UnsafeStack with a default size of 1 and a persistent allocation handle.
/// </summary>
public UnsafeStack()
: this(1, AllocationHandle.Persistent)
{
}
/// <summary>
/// Initializes a new instance of the UnsafeStack class with the specified initial capacity and allocation options.
/// </summary>
/// <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="allocationOption">Specifies additional options for memory allocation. The default is AllocationOption.None.</param>
public UnsafeStack(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
{
_array = new UnsafeArray<T>(capacity, handle, allocationOption);
}
/// <summary>
/// Initializes a new instance of the UnsafeStack class with the specified initial capacity, allocator, and
/// allocation options.
/// </summary>
/// <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)
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[UnscopedRef]
public Enumerator GetEnumerator()
{
return new(ref this);
}
/// <summary>
/// Adds an element to the top of the stack.
/// </summary>
/// <param name="value">The element to add to the stack.</param>
public void Push(T value)
{
if (_count >= Capacity)
{
Resize(Math.Max(1, Capacity * 2));
}
UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
_count++;
}
/// <summary>
/// Removes and returns the object at the top of the stack.
/// </summary>
/// <returns>The object removed from the top of the stack.</returns>
/// <exception cref="InvalidOperationException">Thrown when the stack is empty.</exception>
public T Pop()
{
if (_count == 0)
{
throw new InvalidOperationException("Stack is empty.");
}
_count--;
return _array[_count];
}
/// <summary>
/// Attempts to remove and return the object at the top of the stack.
/// </summary>
/// <param name="value">When this method returns, contains the object removed from the top of the stack, if the operation succeeded;
/// otherwise, the default value of <typeparamref name="T"/>.</param>
/// <returns>true if an object was successfully removed and returned from the stack; otherwise, false.</returns>
public bool TryPop(out T value)
{
if (_count == 0)
{
value = default;
return false;
}
_count--;
value = _array[_count];
return true;
}
/// <summary>
/// Returns the item at the top of the stack without removing it.
/// </summary>
/// <returns>The item of type <typeparamref name="T"/> at the top of the stack.</returns>
/// <exception cref="InvalidOperationException">Thrown when the stack is empty.</exception>
public readonly T Peek()
{
if (_count == 0)
{
throw new InvalidOperationException("Stack is empty.");
}
return _array[_count - 1];
}
/// <summary>
/// Attempts to return the item at the top of the stack without removing it.
/// </summary>
/// <param name="value">When this method returns, contains the item at the top of the stack if the stack is not empty; otherwise, the default value of <typeparamref name="T"/>.</param>
/// <returns><see langword="true"/> if an item was successfully returned; otherwise, <see langword="false"/>.</returns>
public readonly bool TryPeek(out T value)
{
if (_count == 0)
{
value = default;
return false;
}
value = _array[_count - 1];
return true;
}
public void Resize(int newSize, AllocationOption option = AllocationOption.None)
{
_array.Resize(newSize, option);
if (_count > newSize)
{
_count = newSize;
}
}
public void Clear()
{
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void* GetUnsafePtr()
{
return _array.GetUnsafePtr();
}
public void Dispose()
{
_array.Dispose();
_count = 0;
}
}