Refactor and enhance math and utility libraries
Some checks failed
Publish NuGet Packages / publish (push) Failing after 3m12s

Refactored `sincos` usage across `quaternion` and `random` to use `out` parameters for improved performance. Enhanced `random` struct with updated random direction generation methods.

Added new benchmarks in `MathematicsBenchmark` for vector operations, including SIMD-based `f4` struct. Downgraded target framework to `net9.0` for compatibility.

Introduced `ReadOnlyUnsafeCollection` for low-level memory management. Added utility methods in `CollectionUtility` for span creation and optimized list operations.

Renamed `MemoryUtilities` to `MemoryUtility` and updated all references. Enhanced `ObjectPool` with `Rent` and `TryRent` methods. Enabled `AllowUnsafeBlocks` and AOT compatibility in project configuration.

Performed general code cleanup, including removal of unused methods, improved formatting, and alignment with modern coding practices.
This commit is contained in:
2025-11-04 14:53:01 +09:00
parent 081103372f
commit 49e1171781
38 changed files with 5149 additions and 1259 deletions

View File

@@ -9,10 +9,10 @@ namespace Misaki.HighPerformance.LowLevel.Collections;
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 32 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString32"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 32 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText32"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 32)]
public unsafe struct FixedString32
public unsafe struct FixedText32
{
private ushort _length;
private fixed byte _buffer[30];
@@ -38,7 +38,7 @@ public unsafe struct FixedString32
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 30)
{
throw new ArgumentException("Input string is too long to fit in FixedString32.");
throw new ArgumentException("Input string is too long to fit in FixedText32.");
}
fixed (byte* bufferPtr = _buffer)
@@ -48,12 +48,12 @@ public unsafe struct FixedString32
}
}
public FixedString32(ReadOnlySpan<char> input)
public FixedText32(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 30)
{
throw new ArgumentException("Input string is too long to fit in FixedString32.");
throw new ArgumentException("Input string is too long to fit in FixedText32.");
}
fixed (char* inputPtr = input)
@@ -64,21 +64,21 @@ public unsafe struct FixedString32
}
}
public FixedString32(string input)
public FixedText32(string input)
: this(input.AsSpan())
{
}
public FixedString32(char* input, ushort length)
public FixedText32(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString32(ReadOnlySpan<byte> input)
public FixedText32(ReadOnlySpan<byte> input)
{
if (input.Length > 30)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString32.");
throw new ArgumentException("Input byte array is too long to fit in FixedText32.");
}
_length = (ushort)input.Length;
@@ -90,7 +90,7 @@ public unsafe struct FixedString32
}
}
public FixedString32(byte* input, ushort length)
public FixedText32(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}
@@ -124,10 +124,10 @@ public unsafe struct FixedString32
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 64 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString64"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 64 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText64"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 64)]
public unsafe struct FixedString64
public unsafe struct FixedText64
{
private ushort _length;
private fixed byte _buffer[62];
@@ -153,7 +153,7 @@ public unsafe struct FixedString64
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 62)
{
throw new ArgumentException("Input string is too long to fit in FixedString64.");
throw new ArgumentException("Input string is too long to fit in FixedText64.");
}
fixed (byte* bufferPtr = _buffer)
@@ -163,12 +163,12 @@ public unsafe struct FixedString64
}
}
public FixedString64(ReadOnlySpan<char> input)
public FixedText64(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 62)
{
throw new ArgumentException("Input string is too long to fit in FixedString64.");
throw new ArgumentException("Input string is too long to fit in FixedText64.");
}
fixed (char* inputPtr = input)
@@ -179,21 +179,21 @@ public unsafe struct FixedString64
}
}
public FixedString64(string input)
public FixedText64(string input)
: this(input.AsSpan())
{
}
public FixedString64(char* input, ushort length)
public FixedText64(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString64(ReadOnlySpan<byte> input)
public FixedText64(ReadOnlySpan<byte> input)
{
if (input.Length > 62)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString64.");
throw new ArgumentException("Input byte array is too long to fit in FixedText64.");
}
_length = (ushort)input.Length;
@@ -205,7 +205,7 @@ public unsafe struct FixedString64
}
}
public FixedString64(byte* input, ushort length)
public FixedText64(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}
@@ -239,10 +239,10 @@ public unsafe struct FixedString64
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 128 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString128"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 128 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText128"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 128)]
public unsafe struct FixedString128
public unsafe struct FixedText128
{
private ushort _length;
private fixed byte _buffer[126];
@@ -268,7 +268,7 @@ public unsafe struct FixedString128
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 126)
{
throw new ArgumentException("Input string is too long to fit in FixedString128.");
throw new ArgumentException("Input string is too long to fit in FixedText128.");
}
fixed (byte* bufferPtr = _buffer)
@@ -278,12 +278,12 @@ public unsafe struct FixedString128
}
}
public FixedString128(ReadOnlySpan<char> input)
public FixedText128(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 126)
{
throw new ArgumentException("Input string is too long to fit in FixedString128.");
throw new ArgumentException("Input string is too long to fit in FixedText128.");
}
fixed (char* inputPtr = input)
@@ -294,21 +294,21 @@ public unsafe struct FixedString128
}
}
public FixedString128(string input)
public FixedText128(string input)
: this(input.AsSpan())
{
}
public FixedString128(char* input, ushort length)
public FixedText128(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString128(ReadOnlySpan<byte> input)
public FixedText128(ReadOnlySpan<byte> input)
{
if (input.Length > 126)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString128.");
throw new ArgumentException("Input byte array is too long to fit in FixedText128.");
}
_length = (ushort)input.Length;
@@ -320,7 +320,7 @@ public unsafe struct FixedString128
}
}
public FixedString128(byte* input, ushort length)
public FixedText128(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}
@@ -354,10 +354,10 @@ public unsafe struct FixedString128
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 256 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString256"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 256 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText256"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 256)]
public unsafe struct FixedString256
public unsafe struct FixedText256
{
private ushort _length;
private fixed byte _buffer[254];
@@ -383,7 +383,7 @@ public unsafe struct FixedString256
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 254)
{
throw new ArgumentException("Input string is too long to fit in FixedString256.");
throw new ArgumentException("Input string is too long to fit in FixedText256.");
}
fixed (byte* bufferPtr = _buffer)
@@ -393,12 +393,12 @@ public unsafe struct FixedString256
}
}
public FixedString256(ReadOnlySpan<char> input)
public FixedText256(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 254)
{
throw new ArgumentException("Input string is too long to fit in FixedString256.");
throw new ArgumentException("Input string is too long to fit in FixedText256.");
}
fixed (char* inputPtr = input)
@@ -409,21 +409,21 @@ public unsafe struct FixedString256
}
}
public FixedString256(string input)
public FixedText256(string input)
: this(input.AsSpan())
{
}
public FixedString256(char* input, ushort length)
public FixedText256(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString256(ReadOnlySpan<byte> input)
public FixedText256(ReadOnlySpan<byte> input)
{
if (input.Length > 254)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString256.");
throw new ArgumentException("Input byte array is too long to fit in FixedText256.");
}
_length = (ushort)input.Length;
@@ -435,7 +435,7 @@ public unsafe struct FixedString256
}
}
public FixedString256(byte* input, ushort length)
public FixedText256(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}
@@ -469,10 +469,10 @@ public unsafe struct FixedString256
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 512 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString512"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 512 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText512"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 512)]
public unsafe struct FixedString512
public unsafe struct FixedText512
{
private ushort _length;
private fixed byte _buffer[510];
@@ -498,7 +498,7 @@ public unsafe struct FixedString512
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 510)
{
throw new ArgumentException("Input string is too long to fit in FixedString512.");
throw new ArgumentException("Input string is too long to fit in FixedText512.");
}
fixed (byte* bufferPtr = _buffer)
@@ -508,12 +508,12 @@ public unsafe struct FixedString512
}
}
public FixedString512(ReadOnlySpan<char> input)
public FixedText512(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 510)
{
throw new ArgumentException("Input string is too long to fit in FixedString512.");
throw new ArgumentException("Input string is too long to fit in FixedText512.");
}
fixed (char* inputPtr = input)
@@ -524,21 +524,21 @@ public unsafe struct FixedString512
}
}
public FixedString512(string input)
public FixedText512(string input)
: this(input.AsSpan())
{
}
public FixedString512(char* input, ushort length)
public FixedText512(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString512(ReadOnlySpan<byte> input)
public FixedText512(ReadOnlySpan<byte> input)
{
if (input.Length > 510)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString512.");
throw new ArgumentException("Input byte array is too long to fit in FixedText512.");
}
_length = (ushort)input.Length;
@@ -550,7 +550,7 @@ public unsafe struct FixedString512
}
}
public FixedString512(byte* input, ushort length)
public FixedText512(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}
@@ -584,10 +584,10 @@ public unsafe struct FixedString512
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 1024 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString1024"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 1024 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText1024"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 1024)]
public unsafe struct FixedString1024
public unsafe struct FixedText1024
{
private ushort _length;
private fixed byte _buffer[1022];
@@ -613,7 +613,7 @@ public unsafe struct FixedString1024
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 1022)
{
throw new ArgumentException("Input string is too long to fit in FixedString1024.");
throw new ArgumentException("Input string is too long to fit in FixedText1024.");
}
fixed (byte* bufferPtr = _buffer)
@@ -623,12 +623,12 @@ public unsafe struct FixedString1024
}
}
public FixedString1024(ReadOnlySpan<char> input)
public FixedText1024(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 1022)
{
throw new ArgumentException("Input string is too long to fit in FixedString1024.");
throw new ArgumentException("Input string is too long to fit in FixedText1024.");
}
fixed (char* inputPtr = input)
@@ -639,21 +639,21 @@ public unsafe struct FixedString1024
}
}
public FixedString1024(string input)
public FixedText1024(string input)
: this(input.AsSpan())
{
}
public FixedString1024(char* input, ushort length)
public FixedText1024(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString1024(ReadOnlySpan<byte> input)
public FixedText1024(ReadOnlySpan<byte> input)
{
if (input.Length > 1022)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString1024.");
throw new ArgumentException("Input byte array is too long to fit in FixedText1024.");
}
_length = (ushort)input.Length;
@@ -665,7 +665,7 @@ public unsafe struct FixedString1024
}
}
public FixedString1024(byte* input, ushort length)
public FixedText1024(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}
@@ -699,10 +699,10 @@ public unsafe struct FixedString1024
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 2048 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString2048"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 2048 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText2048"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 2048)]
public unsafe struct FixedString2048
public unsafe struct FixedText2048
{
private ushort _length;
private fixed byte _buffer[2046];
@@ -728,7 +728,7 @@ public unsafe struct FixedString2048
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 2046)
{
throw new ArgumentException("Input string is too long to fit in FixedString2048.");
throw new ArgumentException("Input string is too long to fit in FixedText2048.");
}
fixed (byte* bufferPtr = _buffer)
@@ -738,12 +738,12 @@ public unsafe struct FixedString2048
}
}
public FixedString2048(ReadOnlySpan<char> input)
public FixedText2048(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 2046)
{
throw new ArgumentException("Input string is too long to fit in FixedString2048.");
throw new ArgumentException("Input string is too long to fit in FixedText2048.");
}
fixed (char* inputPtr = input)
@@ -754,21 +754,21 @@ public unsafe struct FixedString2048
}
}
public FixedString2048(string input)
public FixedText2048(string input)
: this(input.AsSpan())
{
}
public FixedString2048(char* input, ushort length)
public FixedText2048(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString2048(ReadOnlySpan<byte> input)
public FixedText2048(ReadOnlySpan<byte> input)
{
if (input.Length > 2046)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString2048.");
throw new ArgumentException("Input byte array is too long to fit in FixedText2048.");
}
_length = (ushort)input.Length;
@@ -780,7 +780,7 @@ public unsafe struct FixedString2048
}
}
public FixedString2048(byte* input, ushort length)
public FixedText2048(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}
@@ -814,10 +814,10 @@ public unsafe struct FixedString2048
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 4096 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString4096"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 4096 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText4096"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = 4096)]
public unsafe struct FixedString4096
public unsafe struct FixedText4096
{
private ushort _length;
private fixed byte _buffer[4094];
@@ -843,7 +843,7 @@ public unsafe struct FixedString4096
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > 4094)
{
throw new ArgumentException("Input string is too long to fit in FixedString4096.");
throw new ArgumentException("Input string is too long to fit in FixedText4096.");
}
fixed (byte* bufferPtr = _buffer)
@@ -853,12 +853,12 @@ public unsafe struct FixedString4096
}
}
public FixedString4096(ReadOnlySpan<char> input)
public FixedText4096(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > 4094)
{
throw new ArgumentException("Input string is too long to fit in FixedString4096.");
throw new ArgumentException("Input string is too long to fit in FixedText4096.");
}
fixed (char* inputPtr = input)
@@ -869,21 +869,21 @@ public unsafe struct FixedString4096
}
}
public FixedString4096(string input)
public FixedText4096(string input)
: this(input.AsSpan())
{
}
public FixedString4096(char* input, ushort length)
public FixedText4096(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString4096(ReadOnlySpan<byte> input)
public FixedText4096(ReadOnlySpan<byte> input)
{
if (input.Length > 4094)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString4096.");
throw new ArgumentException("Input byte array is too long to fit in FixedText4096.");
}
_length = (ushort)input.Length;
@@ -895,7 +895,7 @@ public unsafe struct FixedString4096
}
}
public FixedString4096(byte* input, ushort length)
public FixedText4096(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}

View File

@@ -16,10 +16,10 @@ namespace Misaki.HighPerformance.LowLevel.Collections;
/// </summary>
/// <remarks>
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length <#= i #> bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString<#= i #>"/>.
/// If you need a heap allocated fixed-size UTF-8 encoded string of length <#= i #> bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedText<#= i #>"/>.
/// </remarks>
[StructLayout(LayoutKind.Sequential, Size = <#= i #>)]
public unsafe struct FixedString<#= i #>
public unsafe struct FixedText<#= i #>
{
private ushort _length;
private fixed byte _buffer[<#= i - 2 #>];
@@ -45,7 +45,7 @@ public unsafe struct FixedString<#= i #>
var maxBytes = Encoding.UTF8.GetByteCount(value);
if (maxBytes > <#= i - 2 #>)
{
throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>.");
throw new ArgumentException("Input string is too long to fit in FixedText<#= i #>.");
}
fixed (byte* bufferPtr = _buffer)
@@ -55,12 +55,12 @@ public unsafe struct FixedString<#= i #>
}
}
public FixedString<#= i #>(ReadOnlySpan<char> input)
public FixedText<#= i #>(ReadOnlySpan<char> input)
{
var maxBytes = Encoding.UTF8.GetByteCount(input);
if (maxBytes > <#= i - 2 #>)
{
throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>.");
throw new ArgumentException("Input string is too long to fit in FixedText<#= i #>.");
}
fixed (char* inputPtr = input)
@@ -71,21 +71,21 @@ public unsafe struct FixedString<#= i #>
}
}
public FixedString<#= i #>(string input)
public FixedText<#= i #>(string input)
: this(input.AsSpan())
{
}
public FixedString<#= i #>(char* input, ushort length)
public FixedText<#= i #>(char* input, ushort length)
: this(new Span<char>(input, length))
{
}
public FixedString<#= i #>(ReadOnlySpan<byte> input)
public FixedText<#= i #>(ReadOnlySpan<byte> input)
{
if (input.Length > <#= i - 2 #>)
{
throw new ArgumentException("Input byte array is too long to fit in FixedString<#= i #>.");
throw new ArgumentException("Input byte array is too long to fit in FixedText<#= i #>.");
}
_length = (ushort)input.Length;
@@ -97,7 +97,7 @@ public unsafe struct FixedString<#= i #>
}
}
public FixedString<#= i #>(byte* input, ushort length)
public FixedText<#= i #>(byte* input, ushort length)
: this(new ReadOnlySpan<byte>(input, length))
{
}

View File

@@ -0,0 +1,118 @@
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Collections;
using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.LowLevel.Collections;
/// <summary>
/// Provides a read-only, unsafe view over a contiguous region of unmanaged memory as an array of elements of type T.
/// Enables efficient, low-level access to memory without copying or additional safety checks.
/// </summary>
/// <remarks>
/// This read only collection does not own the memory it points to. The user is responsible for ensuring the memory remains valid for the lifetime of this structure.
/// The goal of this struc is similar to <see cref="ReadOnlySpan{T}"/>, but it can be used in contexts where spans are not allowed, such as fields in structs and shared across threads.
/// </remarks>
/// <typeparam name="T">The type of elements in the collection. Must be an unmanaged type.</typeparam>
public readonly unsafe struct ReadOnlyUnsafeCollection<T> : IEnumerable<T>
where T : unmanaged
{
public struct Enumerator : IEnumerator<T>
{
private readonly ReadOnlyUnsafeCollection<T> _collection;
private int _index;
private T _value;
public readonly T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _value;
}
readonly object IEnumerator.Current => Current;
public Enumerator(ref readonly ReadOnlyUnsafeCollection<T> array)
{
_collection = array;
_index = -1;
_value = default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index++;
if (_index < _collection.Count)
{
_value = UnsafeUtility.ReadArrayElement<T>(_collection._buffer, _index);
return true;
}
_value = default;
return false;
}
public void Reset()
{
_index = -1;
}
public void Dispose()
{
}
}
private readonly T* _buffer;
private readonly int _count;
public readonly int Count => _count;
public readonly T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => UnsafeUtility.ReadArrayElement<T>(_buffer, index);
}
public readonly T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => UnsafeUtility.ReadArrayElement<T>(_buffer, index);
}
public IEnumerator<T> GetEnumerator() => new Enumerator(in this);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public ReadOnlyUnsafeCollection(T* buffer, int count)
{
_buffer = buffer;
_count = count;
}
/// <summary>
/// Returns a read-only span that represents the valid elements in the underlying buffer.
/// </summary>
/// <returns>A <see cref="ReadOnlySpan{T}"/> containing the elements of the buffer up to the current count.</returns>
public ReadOnlySpan<T> AsSpan()
{
return new ReadOnlySpan<T>(_buffer, _count);
}
/// <summary>
/// Reinterprets the underlying collection as a read-only collection of a different unmanaged type without copying the data.
/// </summary>
/// <typeparam name="U">The unmanaged type to reinterpret the collection elements as.</typeparam>
/// <returns>A new ReadOnlyUnsafeCollection<U> that provides a read-only view of the same memory, interpreted as elements of type U.</returns>
/// <exception cref="InvalidOperationException">Thrown if the total size of the underlying collection is not a multiple of the size of type U, making the reinterpretation invalid.</exception>
public ReadOnlyUnsafeCollection<U> Reinterpret<U>()
where U : unmanaged
{
var totalSize = (nuint)(Count * sizeof(T));
if (totalSize % (nuint)sizeof(U) != 0)
{
throw new InvalidOperationException("Cannot reinterpret collection: size mismatch.");
}
var newCount = (int)(totalSize / (nuint)sizeof(U));
return new ReadOnlyUnsafeCollection<U>((U*)_buffer, newCount);
}
}

View File

@@ -75,7 +75,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
public readonly ref T GetElementAt<T>(uint index)
where T : unmanaged
{
return ref UnsafeUtilities.ReadArrayElementRef<T>(_buffer, index);
return ref UnsafeUtility.ReadArrayElementRef<T>(_buffer, index);
}
/// <inheritdoc/>

View File

@@ -18,26 +18,25 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
{
private readonly UnsafeArray<T>* _collection;
private int _index;
private T _value;
public Enumerator(UnsafeArray<T>* collection)
{
_collection = collection;
_index = -1;
_value = default;
Current = default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index++;
if (_index < _collection->_count)
if (_index < _collection->Count)
{
_value = UnsafeUtilities.ReadArrayElement<T>(_collection->_buffer, _index);
Current = UnsafeUtility.ReadArrayElement<T>(_collection->_buffer, _index);
return true;
}
_value = default;
Current = default;
return false;
}
@@ -47,10 +46,10 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
}
// Let NativeArray indexer check for out of range.
public readonly T Current
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _value;
get; private set;
}
readonly object IEnumerator.Current
@@ -66,7 +65,6 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
private T* _buffer;
private int _count;
private AllocationHandle* _handle;
public readonly int Count => _count;
@@ -83,7 +81,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
}
#endif
return ref UnsafeUtilities.ReadArrayElementRef<T>(_buffer, index);
return ref UnsafeUtility.ReadArrayElementRef<T>(_buffer, index);
}
}
@@ -99,7 +97,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
}
#endif
return ref UnsafeUtilities.ReadArrayElementRef<T>(_buffer, index);
return ref UnsafeUtility.ReadArrayElementRef<T>(_buffer, index);
}
}
@@ -109,7 +107,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
get => _buffer != null;
}
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeArray<T>*)UnsafeUtilities.AddressOf(ref this));
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeArray<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
@@ -167,16 +165,21 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
_count = count;
}
public ReadOnlyUnsafeCollection<T> AsReadOnly()
{
return new ReadOnlyUnsafeCollection<T>(_buffer, _count);
}
/// <inheritdoc/>
public void Resize(int newSize, AllocationOption option = AllocationOption.None)
{
if (newSize == _count)
if (newSize == Count)
{
return;
}
var elemSize = SizeOf<T>();
_buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (nuint)_count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option);
_buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option);
_count = newSize;
}
@@ -184,7 +187,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void Clear()
{
MemClear(_buffer, (nuint)(_count * sizeof(T)));
MemClear(_buffer, (nuint)(Count * sizeof(T)));
}
/// <inheritdoc/>
@@ -194,6 +197,25 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
return _buffer;
}
/// <summary>
/// Reinterprets the underlying buffer as an array of a different unmanaged type without copying the data.
/// </summary>
/// <typeparam name="U">The unmanaged type to reinterpret the buffer as.</typeparam>
/// <returns>An UnsafeArray<U> that views the same memory as the original array, but as elements of type U.</returns>
/// <exception cref="InvalidOperationException">Thrown if the total size of the buffer in bytes is not a multiple of the size of type U.</exception>
public readonly UnsafeArray<U> Reinterpret<U>()
where U : unmanaged
{
var totalSize = (nuint)(Count * sizeof(T));
if (totalSize % (nuint)sizeof(U) != 0)
{
throw new InvalidOperationException("Cannot reinterpret array: size mismatch.");
}
var newCount = (int)(totalSize / (nuint)sizeof(U));
return new UnsafeArray<U>((U*)_buffer, newCount);
}
/// <inheritdoc/>
public void Dispose()
{

View File

@@ -53,25 +53,25 @@ public struct UnsafeBitSet : IDisposable
/// </summary>
public UnsafeBitSet()
{
_bits = new UnsafeArray<uint>(s_padding, Allocator.Persistent);
_bits = new UnsafeArray<uint>(s_padding, Allocator.Persistent, AllocationOption.Clear);
}
/// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// </summary>
public UnsafeBitSet(int minimalLength)
public UnsafeBitSet(int minimalLength, Allocator allocator, AllocationOption option = AllocationOption.Clear)
{
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
var length = RoundToPadding(uints);
_bits = new UnsafeArray<uint>(length, Allocator.Persistent);
_bits = new UnsafeArray<uint>(length, allocator, option);
}
/// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// </summary>
public UnsafeBitSet(params Span<uint> bits)
public UnsafeBitSet(Span<uint> bits, Allocator allocator, AllocationOption option = AllocationOption.Clear)
{
_bits = new UnsafeArray<uint>(bits.Length, Allocator.Persistent);
_bits = new UnsafeArray<uint>(bits.Length, allocator, option);
_bits.CopyFrom(bits);
_highestBit = 0;
@@ -109,6 +109,11 @@ public struct UnsafeBitSet : IDisposable
get => _bits.Count;
}
public bool IsCreated
{
get => _bits.IsCreated;
}
/// <summary>
/// Checks whether a bit is set at the index.
/// </summary>
@@ -443,10 +448,88 @@ public struct UnsafeBitSet : IDisposable
return true;
}
public unsafe void AndOperation(UnsafeBitSet other)
{
var min = Math.Min(Length, other.Length);
var temp = stackalloc uint[min];
if (!Vector.IsHardwareAccelerated || min < s_padding)
{
for (var i = 0; i < min; i++)
{
temp[i] = _bits[i] & other._bits[i];
}
}
else
{
for (var i = 0; i < min; i += s_padding)
{
var vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
var resultVector = Vector.BitwiseAnd(vectorLeft, vectorRight);
resultVector.CopyTo(new Span<uint>(temp + i, s_padding));
}
}
_bits.CopyFrom(new Span<uint>(temp, min));
}
public unsafe void OrOperation(UnsafeBitSet other)
{
var min = Math.Min(Length, other.Length);
var temp = stackalloc uint[min];
if (!Vector.IsHardwareAccelerated || min < s_padding)
{
for (var i = 0; i < min; i++)
{
temp[i] = _bits[i] | other._bits[i];
}
}
else
{
for (var i = 0; i < min; i += s_padding)
{
var vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
var resultVector = Vector.BitwiseOr(vectorLeft, vectorRight);
resultVector.CopyTo(new Span<uint>(temp + i, s_padding));
}
}
_bits.CopyFrom(new Span<uint>(temp, min));
}
public unsafe void XorOperation(UnsafeBitSet other)
{
var min = Math.Min(Length, other.Length);
var temp = stackalloc uint[min];
if (!Vector.IsHardwareAccelerated || min < s_padding)
{
for (var i = 0; i < min; i++)
{
temp[i] = _bits[i] ^ other._bits[i];
}
}
else
{
for (var i = 0; i < min; i += s_padding)
{
var vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
var resultVector = Vector.Xor(vectorLeft, vectorRight);
resultVector.CopyTo(new Span<uint>(temp + i, s_padding));
}
}
_bits.CopyFrom(new Span<uint>(temp, min));
}
public static UnsafeBitSet operator &(UnsafeBitSet left, UnsafeBitSet right)
{
var min = Math.Min(left.Length, right.Length);
var result = new UnsafeBitSet(min);
var result = new UnsafeBitSet(min, Allocator.Persistent);
if (!Vector.IsHardwareAccelerated || min < s_padding)
{
for (var i = 0; i < min; i++)
@@ -470,7 +553,7 @@ public struct UnsafeBitSet : IDisposable
public static UnsafeBitSet operator |(UnsafeBitSet left, UnsafeBitSet right)
{
var min = Math.Min(left.Length, right.Length);
var result = new UnsafeBitSet(min);
var result = new UnsafeBitSet(min, Allocator.Persistent);
if (!Vector.IsHardwareAccelerated || min < s_padding)
{
for (var i = 0; i < min; i++)

View File

@@ -84,7 +84,7 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeCollection<KeyValuePai
var idx = _hashMap.Find(key);
if (-1 != idx)
{
UnsafeUtilities.WriteArrayElement(_hashMap.Buffer, idx, value);
UnsafeUtility.WriteArrayElement(_hashMap.Buffer, idx, value);
return;
}
@@ -92,7 +92,7 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeCollection<KeyValuePai
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => new Enumerator((HashMapHelper<TKey>*)UnsafeUtilities.AddressOf(ref _hashMap));
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => new Enumerator((HashMapHelper<TKey>*)UnsafeUtility.AddressOf(ref _hashMap));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
@@ -125,7 +125,7 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeCollection<KeyValuePai
var idx = _hashMap.TryAdd(key);
if (idx != -1)
{
UnsafeUtilities.WriteArrayElement(_hashMap.Buffer, idx, item);
UnsafeUtility.WriteArrayElement(_hashMap.Buffer, idx, item);
return true;
}

View File

@@ -48,7 +48,7 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeCollection<T>, IEnumerable<T>
public readonly int Capacity => _hashMap.Capacity;
public readonly bool IsCreated => _hashMap.IsCreated;
public IEnumerator<T> GetEnumerator() => new Enumerator((HashMapHelper<T>*)UnsafeUtilities.AddressOf(ref _hashMap));
public IEnumerator<T> GetEnumerator() => new Enumerator((HashMapHelper<T>*)UnsafeUtility.AddressOf(ref _hashMap));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>

View File

@@ -33,7 +33,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
_index++;
if (_index < _collection->_count)
{
_value = UnsafeUtilities.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
_value = UnsafeUtility.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
return true;
}
@@ -96,7 +96,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
{
var idx = Interlocked.Increment(ref listData->_count) - 1;
listData->CheckNoResizeCapacity(idx, 1);
UnsafeUtilities.WriteArrayElement(listData->_array.GetUnsafePtr(), idx, value);
UnsafeUtility.WriteArrayElement(listData->_array.GetUnsafePtr(), idx, value);
}
/// <summary>
@@ -111,7 +111,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
fixed (T* pCollection = collection)
{
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(listData->_array.GetUnsafePtr(), index), pCollection, (uint)(count * sizeof(T)));
MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(listData->_array.GetUnsafePtr(), index), pCollection, (uint)(count * sizeof(T)));
}
}
}
@@ -136,14 +136,14 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
get => ref _array[index];
}
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this));
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
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>*)UnsafeUtilities.AddressOf(ref this));
public ParallelWriter AsParallelWriter() => new((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
/// <summary>
/// Converts the current list to an UnsafeArray representation.
@@ -221,6 +221,11 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
}
}
public readonly ReadOnlyUnsafeCollection<T> AsReadOnly()
{
return new ReadOnlyUnsafeCollection<T>((T*)_array.GetUnsafePtr(), _count);
}
/// <summary>
/// Adds a new element to the end of the list, resizing the internal array if necessary.
/// </summary>
@@ -232,7 +237,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
Resize(Capacity + (int)(Capacity * 0.5f));
}
UnsafeUtilities.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
_count++;
}
@@ -244,7 +249,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
{
CheckNoResizeCapacity(1);
UnsafeUtilities.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
_count++;
}
@@ -264,7 +269,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
fixed (T* ptr = values)
{
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T)));
MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T)));
}
_count += count;
@@ -280,7 +285,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
fixed (T* pCollection = collection)
{
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), pCollection, (uint)(collection.Length * sizeof(T)));
MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), pCollection, (uint)(collection.Length * sizeof(T)));
}
_count += collection.Length;
@@ -295,7 +300,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
{
CheckNoResizeCapacity(count);
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T)));
MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T)));
_count += count;
}
@@ -314,8 +319,8 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
}
var copyFrom = Math.Min(start + length, _count);
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start),
UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom),
MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start),
UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom),
(uint)((_count - copyFrom) * sizeof(T))
);
_count -= length;
@@ -345,8 +350,8 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
}
var copyFrom = Math.Min(_count - length, start + length);
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start),
UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom),
MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start),
UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom),
(uint)((_count - copyFrom) * sizeof(T))
);
_count -= length;
@@ -389,5 +394,4 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
_array.Dispose();
_count = 0;
}
}
}

View File

@@ -33,7 +33,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
_index++;
if (_index < _collection->_count)
{
_value = UnsafeUtilities.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
_value = UnsafeUtility.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
return true;
}
@@ -80,7 +80,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
set => _array[index] = value;
}
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeQueue<T>*)UnsafeUtilities.AddressOf(ref this));
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeQueue<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
@@ -115,7 +115,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
throw new InvalidOperationException("Queue is empty.");
}
return ref UnsafeUtilities.ReadArrayElementRef<T>(_array.GetUnsafePtr(), _offset);
return ref UnsafeUtility.ReadArrayElementRef<T>(_array.GetUnsafePtr(), _offset);
}
/// <summary>
@@ -130,7 +130,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
Resize(Capacity + (int)(Capacity * 0.5f));
}
UnsafeUtilities.WriteArrayElement(_array.GetUnsafePtr(), (_offset + _count) % Capacity, value);
UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), (_offset + _count) % Capacity, value);
_count++;
}
@@ -146,7 +146,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
throw new InvalidOperationException("Queue is empty.");
}
var value = UnsafeUtilities.ReadArrayElement<T>(_array.GetUnsafePtr(), _offset);
var value = UnsafeUtility.ReadArrayElement<T>(_array.GetUnsafePtr(), _offset);
_offset = (_offset + 1) % Capacity;
_count--;

View File

@@ -67,7 +67,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
public readonly bool IsCreated => _data.IsCreated && _freeSlots.IsCreated;
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeSlotMap<T>*)UnsafeUtilities.AddressOf(ref this));
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeSlotMap<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

View File

@@ -80,7 +80,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
public readonly int Capacity => _capacity;
public readonly bool IsCreated => _dense.IsCreated && _sparse.IsCreated && _reverse.IsCreated;
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeSparseSet<T>*)UnsafeUtilities.AddressOf(ref this));
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeSparseSet<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>

View File

@@ -72,7 +72,7 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
Resize(_array.Count + (int)(_array.Count * 0.5f));
}
UnsafeUtilities.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
_count++;
}