Refactor and enhance math, memory, and utilities
Refactored `sincos` usage in `quaternion` to use tuple-based returns for improved readability. Introduced a `random` struct with methods for generating random values of various types and dimensions, including ranges and directions. Added a `DynamicArray` class for dynamic resizing and manipulation of collections. Enhanced `SlotMap` with new methods for safe access and updates. Updated `uint` vector types with `NumericConvertable` attributes for better type interoperability. Removed the `MathUtilities` class and refactored `adj` and `adjInverse` methods for encapsulation. Improved memory management with `StackAllocator` and `UnsafeArray` enhancements. Added geometry utilities like `AABB`, `OBB`, `Plane`, and `SphereBounds` for 3D operations. Updated project configuration for versioning and NuGet packaging. Performed general code cleanup, improved validation, and aligned with modern C# practices.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
using Misaki.HighPerformance.Collections;
|
using Misaki.HighPerformance.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
global using static Misaki.HighPerformance.LowLevel.Helpers.MemoryUtilities;
|
global using static Misaki.HighPerformance.LowLevel.Utilities.MemoryUtilities;
|
||||||
|
|
||||||
|
|
||||||
global using unsafe AllocFunc = delegate*<void*, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption, void*>;
|
global using unsafe AllocFunc = delegate*<void*, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption, void*>;
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ public static unsafe class AllocationManager
|
|||||||
var selfPtr = (ArenaAllocator*)instance;
|
var selfPtr = (ArenaAllocator*)instance;
|
||||||
var newPtr = selfPtr->_arena.Allocate(size, alignment, AllocationOption.None);
|
var newPtr = selfPtr->_arena.Allocate(size, alignment, AllocationOption.None);
|
||||||
MemCpy(newPtr, ptr, size);
|
MemCpy(newPtr, ptr, size);
|
||||||
|
|
||||||
// We do not free the old pointer here, as it is managed by the arena.
|
// We do not free the old pointer here, as it is managed by the arena.
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
@@ -129,6 +130,7 @@ public static unsafe class AllocationManager
|
|||||||
{
|
{
|
||||||
var newPtr = AlignedRealloc(ptr, size, alignment);
|
var newPtr = AlignedRealloc(ptr, size, alignment);
|
||||||
UpdateAllocation(ptr, newPtr, size, instance, &FreeBlock);
|
UpdateAllocation(ptr, newPtr, size, instance, &FreeBlock);
|
||||||
|
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,10 +141,50 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const uint _DEFAULT_ARENA_SIZE = 512 * 1024;
|
private unsafe struct StackAllocator : IAllocator
|
||||||
|
{
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Stack s_stack;
|
||||||
|
private AllocationHandle _handle;
|
||||||
|
|
||||||
|
public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle);
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
_handle = new(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &FreeBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption)
|
||||||
|
{
|
||||||
|
var ptr = s_stack.Allocate(size, alignment, allocationOption);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void* Reallocate(void* instance, void* ptr, nuint size, nuint alignment)
|
||||||
|
{
|
||||||
|
var newPtr = s_stack.Allocate(size, alignment, AllocationOption.None);
|
||||||
|
MemCpy(newPtr, ptr, size);
|
||||||
|
// We do not free the old pointer here, as it is managed by the stack.
|
||||||
|
return newPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FreeBlock(void* instance, void* ptr)
|
||||||
|
{
|
||||||
|
// The stack allocator does not free individual blocks, as it manages memory in a stack-like manner.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stack.Scope CreateScope()
|
||||||
|
{
|
||||||
|
return s_stack.CreateScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const uint _DEFAULT_MEMORY_POOL_SIZE = 512 * 1024;
|
||||||
|
|
||||||
private static readonly ArenaAllocator* s_arenaAllocator;
|
private static readonly ArenaAllocator* s_arenaAllocator;
|
||||||
private static readonly HeapAllocator* s_persistentAllocator;
|
private static readonly HeapAllocator* s_persistentAllocator;
|
||||||
|
private static readonly StackAllocator* s_stackAllocator;
|
||||||
|
|
||||||
private static bool s_debugLayer;
|
private static bool s_debugLayer;
|
||||||
private static ConcurrentDictionary<nint, AllocationInfo>? s_allocated;
|
private static ConcurrentDictionary<nint, AllocationInfo>? s_allocated;
|
||||||
@@ -151,9 +193,11 @@ public static unsafe class AllocationManager
|
|||||||
{
|
{
|
||||||
s_arenaAllocator = (ArenaAllocator*)NativeMemory.Alloc((nuint)sizeof(ArenaAllocator));
|
s_arenaAllocator = (ArenaAllocator*)NativeMemory.Alloc((nuint)sizeof(ArenaAllocator));
|
||||||
s_persistentAllocator = (HeapAllocator*)NativeMemory.Alloc((nuint)sizeof(HeapAllocator));
|
s_persistentAllocator = (HeapAllocator*)NativeMemory.Alloc((nuint)sizeof(HeapAllocator));
|
||||||
|
s_stackAllocator = (StackAllocator*)NativeMemory.Alloc((nuint)sizeof(StackAllocator));
|
||||||
|
|
||||||
s_arenaAllocator->Init(_DEFAULT_ARENA_SIZE);
|
s_arenaAllocator->Init(_DEFAULT_MEMORY_POOL_SIZE);
|
||||||
s_persistentAllocator->Init();
|
s_persistentAllocator->Init();
|
||||||
|
s_stackAllocator->Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -181,6 +225,8 @@ public static unsafe class AllocationManager
|
|||||||
return ref s_arenaAllocator->Handle;
|
return ref s_arenaAllocator->Handle;
|
||||||
case Allocator.Persistent:
|
case Allocator.Persistent:
|
||||||
return ref s_persistentAllocator->Handle;
|
return ref s_persistentAllocator->Handle;
|
||||||
|
case Allocator.Stack:
|
||||||
|
return ref s_stackAllocator->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));
|
||||||
}
|
}
|
||||||
@@ -263,6 +309,16 @@ public static unsafe class AllocationManager
|
|||||||
s_arenaAllocator->Reset();
|
s_arenaAllocator->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new thread local stack scope for managing temporary allocations within the current context.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="Stack.Scope"/> instance representing the newly created stack scope. The scope must be disposed when no longer needed to release allocated resources.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Stack.Scope CreateStackScope()
|
||||||
|
{
|
||||||
|
return StackAllocator.CreateScope();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes of the AllocationManager, freeing all allocated memory and resources.
|
/// Disposes of the AllocationManager, freeing all allocated memory and resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -291,6 +347,11 @@ public static unsafe class AllocationManager
|
|||||||
NativeMemory.Free(s_arenaAllocator);
|
NativeMemory.Free(s_arenaAllocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s_stackAllocator != null)
|
||||||
|
{
|
||||||
|
NativeMemory.Free(s_stackAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
if (s_persistentAllocator != null)
|
if (s_persistentAllocator != null)
|
||||||
{
|
{
|
||||||
NativeMemory.Free(s_persistentAllocator);
|
NativeMemory.Free(s_persistentAllocator);
|
||||||
|
|||||||
@@ -23,11 +23,15 @@ public enum Allocator : byte
|
|||||||
// Make the first allocator as invalid because we don't want to user create a default collection without passing any parameters
|
// Make the first allocator as invalid because we don't want to user create a default collection without passing any parameters
|
||||||
Invalid,
|
Invalid,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allocator for temporary allocations. Allocations are cleared after use.
|
/// Allocator for temporary allocations. Allocations are released after use automatically.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Temp,
|
Temp,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allocator for persistent allocations. Allocations are not cleared after use.
|
/// Allocator for persistent allocations. Allocations are not released after use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Persistent
|
Persistent,
|
||||||
|
/// <summary>
|
||||||
|
/// Allocator for stack allocations. Must have at least one active stack scope. Allocations are released when the stack scope is exited.
|
||||||
|
/// </summary>
|
||||||
|
Stack
|
||||||
}
|
}
|
||||||
@@ -16,11 +16,6 @@ public unsafe struct Arena : IDisposable
|
|||||||
private nuint _offset;
|
private nuint _offset;
|
||||||
|
|
||||||
public Arena(nuint size)
|
public Arena(nuint size)
|
||||||
{
|
|
||||||
Initialize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize(nuint size)
|
|
||||||
{
|
{
|
||||||
if (_buffer != null)
|
if (_buffer != null)
|
||||||
{
|
{
|
||||||
@@ -46,7 +41,17 @@ public unsafe struct Arena : IDisposable
|
|||||||
{
|
{
|
||||||
if (_buffer == null)
|
if (_buffer == null)
|
||||||
{
|
{
|
||||||
throw new ObjectDisposedException(nameof(DynamicArena));
|
throw new ObjectDisposedException(nameof(Arena));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((alignment & (alignment - 1)) != 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Alignment must be a power of two.", nameof(alignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
nuint currentOffset, newOffset, alignedOffset;
|
nuint currentOffset, newOffset, alignedOffset;
|
||||||
@@ -1,894 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 32 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
|
||||||
public unsafe struct FixedString32 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 30)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString32.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 30));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString32(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 30)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString32.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(30);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 30);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString32(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString32(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString32(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 30)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString32.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(30);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString32(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 64 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 64)]
|
|
||||||
public unsafe struct FixedString64 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 62)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString64.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 62));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString64(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 62)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString64.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(62);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 62);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString64(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString64(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString64(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 62)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString64.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(62);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString64(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 128 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 128)]
|
|
||||||
public unsafe struct FixedString128 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 126)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString128.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 126));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString128(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 126)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString128.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(126);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 126);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString128(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString128(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString128(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 126)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString128.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(126);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString128(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 256 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 256)]
|
|
||||||
public unsafe struct FixedString256 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 254)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString256.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 254));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString256(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 254)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString256.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(254);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 254);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString256(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString256(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString256(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 254)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString256.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(254);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString256(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 512 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 512)]
|
|
||||||
public unsafe struct FixedString512 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 510)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString512.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 510));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString512(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 510)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString512.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(510);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 510);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString512(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString512(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString512(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 510)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString512.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(510);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString512(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 1024 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 1024)]
|
|
||||||
public unsafe struct FixedString1024 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 1022)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString1024.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 1022));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString1024(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 1022)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString1024.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(1022);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 1022);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString1024(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString1024(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString1024(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 1022)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString1024.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(1022);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString1024(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 2048 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 2048)]
|
|
||||||
public unsafe struct FixedString2048 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 2046)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString2048.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 2046));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString2048(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 2046)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString2048.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(2046);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 2046);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString2048(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString2048(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString2048(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 2046)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString2048.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(2046);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString2048(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 4096 bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 4096)]
|
|
||||||
public unsafe struct FixedString4096 : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > 4094)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString4096.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 4094));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString4096(ReadOnlySpan<char> input)
|
|
||||||
{
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
|
||||||
if (maxBytes > 4094)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString4096.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(4094);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 4094);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString4096(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString4096(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString4096(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > 4094)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString4096.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(4094);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString4096(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
|
||||||
<#@ assembly name="System.Core" #>
|
|
||||||
<#@ import namespace="System.Linq" #>
|
|
||||||
<#@ import namespace="System.Text" #>
|
|
||||||
<#@ import namespace="System.Collections.Generic" #>
|
|
||||||
<#@ output extension=".cs" #>
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
|
||||||
|
|
||||||
<# for (int i = 32; i <= 4096; i *= 2) { #>
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length <#= i #> bytes.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = <#= i #>)]
|
|
||||||
public unsafe struct FixedString<#= i #> : IDisposable
|
|
||||||
{
|
|
||||||
private ushort _length;
|
|
||||||
private byte* _buffer;
|
|
||||||
|
|
||||||
public ushort Length => _length;
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
readonly get
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(_buffer, _length);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
|
||||||
if (maxBytes > <#= i - 2 #>)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, <#= i - 2 #>));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString<#= 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 #>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(<#= i - 2 #>);
|
|
||||||
|
|
||||||
fixed (char* inputPtr = input)
|
|
||||||
{
|
|
||||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, <#= i - 2 #>);
|
|
||||||
_length = (ushort)actualByteCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString<#= i #>(string input)
|
|
||||||
: this(input.AsSpan())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString<#= i #>(char* input, ushort length)
|
|
||||||
: this(new Span<char>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString<#= i #>(ReadOnlySpan<byte> input)
|
|
||||||
{
|
|
||||||
if (input.Length > <#= i - 2 #>)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input byte array is too long to fit in FixedString<#= i #>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = (byte*)NativeMemory.Alloc(<#= i - 2 #>);
|
|
||||||
_length = (ushort)input.Length;
|
|
||||||
|
|
||||||
fixed (byte* inputPtr = input)
|
|
||||||
{
|
|
||||||
Unsafe.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FixedString<#= i #>(byte* input, ushort length)
|
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<byte> AsSpan()
|
|
||||||
{
|
|
||||||
return new(_buffer, _length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly byte* GetUnsafePointer()
|
|
||||||
{
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
NativeMemory.Free(_buffer);
|
|
||||||
|
|
||||||
_length = 0;
|
|
||||||
_buffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<# } #>
|
|
||||||
@@ -186,7 +186,12 @@ public unsafe struct FreeList : IDisposable
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption = AllocationOption.None)
|
public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption = AllocationOption.None)
|
||||||
{
|
{
|
||||||
if (_disposed != 0 || size == 0)
|
if (_disposed != 0)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(FreeList));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -196,6 +201,11 @@ public unsafe struct FreeList : IDisposable
|
|||||||
alignment = _alignment;
|
alignment = _alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((alignment & (alignment - 1)) != 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Alignment must be a power of two.", nameof(alignment));
|
||||||
|
}
|
||||||
|
|
||||||
// Align size to alignment boundary
|
// Align size to alignment boundary
|
||||||
var alignedSize = (size + alignment - 1) & ~(alignment - 1);
|
var alignedSize = (size + alignment - 1) & ~(alignment - 1);
|
||||||
alignedSize = Math.Max(alignedSize, _MIN_BLOCK_SIZE);
|
alignedSize = Math.Max(alignedSize, _MIN_BLOCK_SIZE);
|
||||||
|
|||||||
157
Misaki.HighPerformance.LowLevel/Buffer/Stack.cs
Normal file
157
Misaki.HighPerformance.LowLevel/Buffer/Stack.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a stack-based memory allocator for unmanaged memory, enabling fast allocation and deallocation of memory
|
||||||
|
/// blocks within a preallocated buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This is not a thread-safe implementation.</remarks>
|
||||||
|
public unsafe struct Stack : IDisposable
|
||||||
|
{
|
||||||
|
private const nuint _DEFAULT_SIZE = 1024 * 1024; // 1MB
|
||||||
|
|
||||||
|
public readonly ref struct Scope
|
||||||
|
{
|
||||||
|
private readonly Stack* _allocator;
|
||||||
|
private readonly nuint _originalOffset;
|
||||||
|
|
||||||
|
internal Scope(Stack* allocator)
|
||||||
|
{
|
||||||
|
_allocator = allocator;
|
||||||
|
_originalOffset = allocator->_offset;
|
||||||
|
_allocator->_activeScopeCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_allocator != null)
|
||||||
|
{
|
||||||
|
_allocator->_offset = _allocator->_offset > _originalOffset ? _originalOffset : _allocator->_offset;
|
||||||
|
_allocator->_activeScopeCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte* _buffer;
|
||||||
|
private nuint _size;
|
||||||
|
private nuint _offset;
|
||||||
|
private uint _activeScopeCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the StackAllocator class with a buffer of the specified size.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">The size, in bytes, of the memory buffer to allocate for stack-based allocations. Must be greater than zero.</param>
|
||||||
|
public Stack(nuint size)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Size must be greater than zero.", nameof(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Init(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Init(nuint size)
|
||||||
|
{
|
||||||
|
if (_buffer != null)
|
||||||
|
{
|
||||||
|
Free(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer = (byte*)Malloc(size);
|
||||||
|
_size = size;
|
||||||
|
_offset = 0;
|
||||||
|
_activeScopeCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly void ThrowIfNoScope()
|
||||||
|
{
|
||||||
|
if (_activeScopeCount == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Allocations can only be made within an active memory scope.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureInitialize()
|
||||||
|
{
|
||||||
|
if (_buffer == null || _size == 0)
|
||||||
|
{
|
||||||
|
Init(_DEFAULT_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new scope instance associated with the current stack context.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="Scope"/> object that represents a scope tied to this stack.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Scope CreateScope()
|
||||||
|
{
|
||||||
|
EnsureInitialize();
|
||||||
|
return new Scope((Stack*)Unsafe.AsPointer(ref this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allocates a block of memory of the specified size and alignment from the buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">The number of bytes to allocate. Must be greater than zero and less than or equal to the remaining buffer size.</param>
|
||||||
|
/// <param name="alignment">The alignment, in bytes, for the allocated memory block. Must be a power of two and greater than zero.</param>
|
||||||
|
/// <param name="allocationOption">An option specifying additional allocation behavior, such as whether the allocated memory should be cleared. The
|
||||||
|
/// default is <see cref="AllocationOption.None"/>.</param>
|
||||||
|
/// <returns>A pointer to the beginning of the allocated memory block if successful; otherwise, <see langword="null"/> if
|
||||||
|
/// there is insufficient space in the buffer.</returns>
|
||||||
|
public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption = AllocationOption.None)
|
||||||
|
{
|
||||||
|
ThrowIfNoScope();
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((alignment & (alignment - 1)) != 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Alignment must be a power of two.", nameof(alignment));
|
||||||
|
}
|
||||||
|
|
||||||
|
var alignedOffset = (_offset + alignment - 1) & ~(alignment - 1);
|
||||||
|
|
||||||
|
var newOffset = alignedOffset + size;
|
||||||
|
|
||||||
|
if (newOffset > _size)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Insufficient memory in stack allocator.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = _buffer + alignedOffset;
|
||||||
|
|
||||||
|
_offset = newOffset;
|
||||||
|
|
||||||
|
if (allocationOption.HasFlag(AllocationOption.Clear))
|
||||||
|
{
|
||||||
|
MemClear(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the internal offset to its initial position.
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Free(_buffer);
|
||||||
|
|
||||||
|
_buffer = null;
|
||||||
|
_size = 0;
|
||||||
|
_offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ public unsafe interface IUnsafeCollection<T> : IUnsafeCollection, IEnumerable<T>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the size of a collection to the specified value.
|
/// Changes the size of a collection to the specified value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>This is to adjust the element count of the collection, not the size of the underlying buffer in memory.</remarks>
|
||||||
/// <param name="newSize">Specifies the new size to which the collection should be adjusted.</param>
|
/// <param name="newSize">Specifies the new size to which the collection should be adjusted.</param>
|
||||||
public void Resize(int newSize);
|
public void Resize(int newSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 32 bytes.
|
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 32 bytes.
|
||||||
@@ -12,12 +12,12 @@ namespace Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
/// 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.FixedString32"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
||||||
public unsafe struct FixedStackString32
|
public unsafe struct FixedString32
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[30];
|
private fixed byte _buffer[30];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -38,7 +38,7 @@ public unsafe struct FixedStackString32
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 30)
|
if (maxBytes > 30)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString32.");
|
throw new ArgumentException("Input string is too long to fit in FixedString32.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -48,7 +48,7 @@ public unsafe struct FixedStackString32
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString32(ReadOnlySpan<char> input)
|
public FixedString32(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 30)
|
if (maxBytes > 30)
|
||||||
@@ -64,17 +64,17 @@ public unsafe struct FixedStackString32
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString32(string input)
|
public FixedString32(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString32(char* input, ushort length)
|
public FixedString32(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString32(ReadOnlySpan<byte> input)
|
public FixedString32(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 30)
|
if (input.Length > 30)
|
||||||
{
|
{
|
||||||
@@ -90,7 +90,7 @@ public unsafe struct FixedStackString32
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString32(byte* input, ushort length)
|
public FixedString32(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -127,12 +127,12 @@ public unsafe struct FixedStackString32
|
|||||||
/// 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.FixedString64"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 64)]
|
[StructLayout(LayoutKind.Sequential, Size = 64)]
|
||||||
public unsafe struct FixedStackString64
|
public unsafe struct FixedString64
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[62];
|
private fixed byte _buffer[62];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -153,7 +153,7 @@ public unsafe struct FixedStackString64
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 62)
|
if (maxBytes > 62)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString64.");
|
throw new ArgumentException("Input string is too long to fit in FixedString64.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -163,7 +163,7 @@ public unsafe struct FixedStackString64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString64(ReadOnlySpan<char> input)
|
public FixedString64(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 62)
|
if (maxBytes > 62)
|
||||||
@@ -179,17 +179,17 @@ public unsafe struct FixedStackString64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString64(string input)
|
public FixedString64(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString64(char* input, ushort length)
|
public FixedString64(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString64(ReadOnlySpan<byte> input)
|
public FixedString64(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 62)
|
if (input.Length > 62)
|
||||||
{
|
{
|
||||||
@@ -205,7 +205,7 @@ public unsafe struct FixedStackString64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString64(byte* input, ushort length)
|
public FixedString64(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -242,12 +242,12 @@ public unsafe struct FixedStackString64
|
|||||||
/// 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.FixedString128"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 128)]
|
[StructLayout(LayoutKind.Sequential, Size = 128)]
|
||||||
public unsafe struct FixedStackString128
|
public unsafe struct FixedString128
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[126];
|
private fixed byte _buffer[126];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -268,7 +268,7 @@ public unsafe struct FixedStackString128
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 126)
|
if (maxBytes > 126)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString128.");
|
throw new ArgumentException("Input string is too long to fit in FixedString128.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -278,7 +278,7 @@ public unsafe struct FixedStackString128
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString128(ReadOnlySpan<char> input)
|
public FixedString128(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 126)
|
if (maxBytes > 126)
|
||||||
@@ -294,17 +294,17 @@ public unsafe struct FixedStackString128
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString128(string input)
|
public FixedString128(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString128(char* input, ushort length)
|
public FixedString128(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString128(ReadOnlySpan<byte> input)
|
public FixedString128(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 126)
|
if (input.Length > 126)
|
||||||
{
|
{
|
||||||
@@ -320,7 +320,7 @@ public unsafe struct FixedStackString128
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString128(byte* input, ushort length)
|
public FixedString128(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -357,12 +357,12 @@ public unsafe struct FixedStackString128
|
|||||||
/// 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.FixedString256"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 256)]
|
[StructLayout(LayoutKind.Sequential, Size = 256)]
|
||||||
public unsafe struct FixedStackString256
|
public unsafe struct FixedString256
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[254];
|
private fixed byte _buffer[254];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -383,7 +383,7 @@ public unsafe struct FixedStackString256
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 254)
|
if (maxBytes > 254)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString256.");
|
throw new ArgumentException("Input string is too long to fit in FixedString256.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -393,7 +393,7 @@ public unsafe struct FixedStackString256
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString256(ReadOnlySpan<char> input)
|
public FixedString256(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 254)
|
if (maxBytes > 254)
|
||||||
@@ -409,17 +409,17 @@ public unsafe struct FixedStackString256
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString256(string input)
|
public FixedString256(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString256(char* input, ushort length)
|
public FixedString256(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString256(ReadOnlySpan<byte> input)
|
public FixedString256(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 254)
|
if (input.Length > 254)
|
||||||
{
|
{
|
||||||
@@ -435,7 +435,7 @@ public unsafe struct FixedStackString256
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString256(byte* input, ushort length)
|
public FixedString256(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -472,12 +472,12 @@ public unsafe struct FixedStackString256
|
|||||||
/// 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.FixedString512"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 512)]
|
[StructLayout(LayoutKind.Sequential, Size = 512)]
|
||||||
public unsafe struct FixedStackString512
|
public unsafe struct FixedString512
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[510];
|
private fixed byte _buffer[510];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -498,7 +498,7 @@ public unsafe struct FixedStackString512
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 510)
|
if (maxBytes > 510)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString512.");
|
throw new ArgumentException("Input string is too long to fit in FixedString512.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -508,7 +508,7 @@ public unsafe struct FixedStackString512
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString512(ReadOnlySpan<char> input)
|
public FixedString512(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 510)
|
if (maxBytes > 510)
|
||||||
@@ -524,17 +524,17 @@ public unsafe struct FixedStackString512
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString512(string input)
|
public FixedString512(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString512(char* input, ushort length)
|
public FixedString512(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString512(ReadOnlySpan<byte> input)
|
public FixedString512(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 510)
|
if (input.Length > 510)
|
||||||
{
|
{
|
||||||
@@ -550,7 +550,7 @@ public unsafe struct FixedStackString512
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString512(byte* input, ushort length)
|
public FixedString512(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -587,12 +587,12 @@ public unsafe struct FixedStackString512
|
|||||||
/// 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.FixedString1024"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 1024)]
|
[StructLayout(LayoutKind.Sequential, Size = 1024)]
|
||||||
public unsafe struct FixedStackString1024
|
public unsafe struct FixedString1024
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[1022];
|
private fixed byte _buffer[1022];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -613,7 +613,7 @@ public unsafe struct FixedStackString1024
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 1022)
|
if (maxBytes > 1022)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString1024.");
|
throw new ArgumentException("Input string is too long to fit in FixedString1024.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -623,7 +623,7 @@ public unsafe struct FixedStackString1024
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString1024(ReadOnlySpan<char> input)
|
public FixedString1024(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 1022)
|
if (maxBytes > 1022)
|
||||||
@@ -639,17 +639,17 @@ public unsafe struct FixedStackString1024
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString1024(string input)
|
public FixedString1024(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString1024(char* input, ushort length)
|
public FixedString1024(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString1024(ReadOnlySpan<byte> input)
|
public FixedString1024(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 1022)
|
if (input.Length > 1022)
|
||||||
{
|
{
|
||||||
@@ -665,7 +665,7 @@ public unsafe struct FixedStackString1024
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString1024(byte* input, ushort length)
|
public FixedString1024(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -702,12 +702,12 @@ public unsafe struct FixedStackString1024
|
|||||||
/// 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.FixedString2048"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 2048)]
|
[StructLayout(LayoutKind.Sequential, Size = 2048)]
|
||||||
public unsafe struct FixedStackString2048
|
public unsafe struct FixedString2048
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[2046];
|
private fixed byte _buffer[2046];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -728,7 +728,7 @@ public unsafe struct FixedStackString2048
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 2046)
|
if (maxBytes > 2046)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString2048.");
|
throw new ArgumentException("Input string is too long to fit in FixedString2048.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -738,7 +738,7 @@ public unsafe struct FixedStackString2048
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString2048(ReadOnlySpan<char> input)
|
public FixedString2048(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 2046)
|
if (maxBytes > 2046)
|
||||||
@@ -754,17 +754,17 @@ public unsafe struct FixedStackString2048
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString2048(string input)
|
public FixedString2048(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString2048(char* input, ushort length)
|
public FixedString2048(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString2048(ReadOnlySpan<byte> input)
|
public FixedString2048(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 2046)
|
if (input.Length > 2046)
|
||||||
{
|
{
|
||||||
@@ -780,7 +780,7 @@ public unsafe struct FixedStackString2048
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString2048(byte* input, ushort length)
|
public FixedString2048(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -817,12 +817,12 @@ public unsafe struct FixedStackString2048
|
|||||||
/// 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.FixedString4096"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 4096)]
|
[StructLayout(LayoutKind.Sequential, Size = 4096)]
|
||||||
public unsafe struct FixedStackString4096
|
public unsafe struct FixedString4096
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[4094];
|
private fixed byte _buffer[4094];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -843,7 +843,7 @@ public unsafe struct FixedStackString4096
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > 4094)
|
if (maxBytes > 4094)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString4096.");
|
throw new ArgumentException("Input string is too long to fit in FixedString4096.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -853,7 +853,7 @@ public unsafe struct FixedStackString4096
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString4096(ReadOnlySpan<char> input)
|
public FixedString4096(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > 4094)
|
if (maxBytes > 4094)
|
||||||
@@ -869,17 +869,17 @@ public unsafe struct FixedStackString4096
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString4096(string input)
|
public FixedString4096(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString4096(char* input, ushort length)
|
public FixedString4096(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString4096(ReadOnlySpan<byte> input)
|
public FixedString4096(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > 4094)
|
if (input.Length > 4094)
|
||||||
{
|
{
|
||||||
@@ -895,7 +895,7 @@ public unsafe struct FixedStackString4096
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString4096(byte* input, ushort length)
|
public FixedString4096(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
<# for (int i = 32; i <= 4096; i *= 2) { #>
|
<# for (int i = 32; i <= 4096; i *= 2) { #>
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -19,12 +19,12 @@ namespace Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
/// 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.FixedString<#= i #>"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = <#= i #>)]
|
[StructLayout(LayoutKind.Sequential, Size = <#= i #>)]
|
||||||
public unsafe struct FixedStackString<#= i #>
|
public unsafe struct FixedString<#= i #>
|
||||||
{
|
{
|
||||||
private ushort _length;
|
private ushort _length;
|
||||||
private fixed byte _buffer[<#= i - 2 #>];
|
private fixed byte _buffer[<#= i - 2 #>];
|
||||||
|
|
||||||
public ushort Length => _length;
|
public readonly ushort Length => _length;
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -45,7 +45,7 @@ public unsafe struct FixedStackString<#= i #>
|
|||||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
if (maxBytes > <#= i - 2 #>)
|
if (maxBytes > <#= i - 2 #>)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input string is too long to fit in FixedStackString<#= i #>.");
|
throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* bufferPtr = _buffer)
|
fixed (byte* bufferPtr = _buffer)
|
||||||
@@ -55,7 +55,7 @@ public unsafe struct FixedStackString<#= i #>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString<#= i #>(ReadOnlySpan<char> input)
|
public FixedString<#= i #>(ReadOnlySpan<char> input)
|
||||||
{
|
{
|
||||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||||
if (maxBytes > <#= i - 2 #>)
|
if (maxBytes > <#= i - 2 #>)
|
||||||
@@ -71,17 +71,17 @@ public unsafe struct FixedStackString<#= i #>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString<#= i #>(string input)
|
public FixedString<#= i #>(string input)
|
||||||
: this(input.AsSpan())
|
: this(input.AsSpan())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString<#= i #>(char* input, ushort length)
|
public FixedString<#= i #>(char* input, ushort length)
|
||||||
: this(new Span<char>(input, length))
|
: this(new Span<char>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString<#= i #>(ReadOnlySpan<byte> input)
|
public FixedString<#= i #>(ReadOnlySpan<byte> input)
|
||||||
{
|
{
|
||||||
if (input.Length > <#= i - 2 #>)
|
if (input.Length > <#= i - 2 #>)
|
||||||
{
|
{
|
||||||
@@ -97,7 +97,7 @@ public unsafe struct FixedStackString<#= i #>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedStackString<#= i #>(byte* input, ushort length)
|
public FixedString<#= i #>(byte* input, ushort length)
|
||||||
: this(new ReadOnlySpan<byte>(input, length))
|
: this(new ReadOnlySpan<byte>(input, length))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
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.Contracts;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
@@ -26,7 +26,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
|||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UnTypedArray()
|
public UnTypedArray()
|
||||||
: this(1, 1, Allocator.Persistent)
|
: this(0, 8, Allocator.Invalid)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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.Contracts;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
{
|
{
|
||||||
public struct Enumerator : IEnumerator<T>
|
public struct Enumerator : IEnumerator<T>
|
||||||
{
|
{
|
||||||
private UnsafeArray<T>* _collection;
|
private readonly UnsafeArray<T>* _collection;
|
||||||
private int _index;
|
private int _index;
|
||||||
private T _value;
|
private T _value;
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
#if DISABLE_COLLECTION_CHECKS
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
if (index < 0 || index >= _count)
|
if (index < 0 || index >= _count)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||||||
@@ -92,7 +92,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
#if DISABLE_COLLECTION_CHECKS
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
if (index >= _count)
|
if (index >= _count)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||||||
@@ -113,7 +113,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs an UnsafeArray with a default size of 1 and uses the Persistent allocator.
|
/// Invalid constructor, use <see cref="UnsafeArray(int, Allocator, AllocationOption)"/> or <see cref="UnsafeArray(int, ref AllocationHandle, AllocationOption)"/> instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UnsafeArray()
|
public UnsafeArray()
|
||||||
: this(0, Allocator.Invalid)
|
: this(0, Allocator.Invalid)
|
||||||
@@ -175,7 +175,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (uint)newSize, (uint)AlignOf<T>());
|
_buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (nuint)newSize * SizeOf<T>(), AlignOf<T>());
|
||||||
_count = newSize;
|
_count = newSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using System.Numerics;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
public sealed class BitSet
|
public struct UnsafeBitSet : IDisposable
|
||||||
{
|
{
|
||||||
private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31
|
private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31
|
||||||
private const int _INDEX_SIZE = 5; // log_2(BitSize + 1)
|
private const int _INDEX_SIZE = 5; // log_2(BitSize + 1)
|
||||||
@@ -12,7 +14,7 @@ public sealed class BitSet
|
|||||||
private static readonly int s_padding = Vector<uint>.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically
|
private static readonly int s_padding = Vector<uint>.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines the required length of an <see cref="BitSet"/> to hold the passed id or bit.
|
/// Determines the required length of an <see cref="UnsafeBitSet"/> to hold the passed id or bit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The id or bit.</param>
|
/// <param name="id">The id or bit.</param>
|
||||||
/// <returns>A size of required <see cref="uint"/>s for the bitset.</returns>
|
/// <returns>A size of required <see cref="uint"/>s for the bitset.</returns>
|
||||||
@@ -34,7 +36,7 @@ public sealed class BitSet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bits from the bitset.
|
/// The bits from the bitset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private uint[] _bits;
|
private UnsafeArray<uint> _bits;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The highest bit set.
|
/// The highest bit set.
|
||||||
@@ -47,32 +49,34 @@ public sealed class BitSet
|
|||||||
private int _max;
|
private int _max;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BitSet()
|
public UnsafeBitSet()
|
||||||
{
|
{
|
||||||
_bits = new uint[s_padding];
|
_bits = new UnsafeArray<uint>(s_padding, Allocator.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BitSet(int minimalLength)
|
public UnsafeBitSet(int minimalLength)
|
||||||
{
|
{
|
||||||
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 uint[length];
|
_bits = new UnsafeArray<uint>(length, Allocator.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BitSet(params Span<uint> bits)
|
public UnsafeBitSet(params Span<uint> bits)
|
||||||
{
|
{
|
||||||
_bits = bits.ToArray();
|
_bits = new UnsafeArray<uint>(bits.Length, Allocator.Persistent);
|
||||||
|
_bits.CopyFrom(bits);
|
||||||
|
|
||||||
_highestBit = 0;
|
_highestBit = 0;
|
||||||
_max = _bits.Length * (_BIT_SIZE + 1) - 1; // Calculate the maximum index in use
|
_max = _bits.Count * (_BIT_SIZE + 1) - 1; // Calculate the maximum index in use
|
||||||
for (var i = 0; i < _bits.Length; i++)
|
for (var i = 0; i < _bits.Count; i++)
|
||||||
{
|
{
|
||||||
if (_bits[i] != 0)
|
if (_bits[i] != 0)
|
||||||
{
|
{
|
||||||
@@ -102,7 +106,7 @@ public sealed class BitSet
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Length
|
public int Length
|
||||||
{
|
{
|
||||||
get => _bits.Length;
|
get => _bits.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -113,7 +117,7 @@ public sealed class BitSet
|
|||||||
public bool IsSet(int index)
|
public bool IsSet(int index)
|
||||||
{
|
{
|
||||||
var b = index >> _INDEX_SIZE;
|
var b = index >> _INDEX_SIZE;
|
||||||
if (b >= _bits.Length)
|
if (b >= _bits.Count)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -129,9 +133,9 @@ public sealed class BitSet
|
|||||||
public void SetBit(int index)
|
public void SetBit(int index)
|
||||||
{
|
{
|
||||||
var b = index >> _INDEX_SIZE;
|
var b = index >> _INDEX_SIZE;
|
||||||
if (b >= _bits.Length)
|
if (b >= _bits.Count)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _bits, RoundToPadding(b));
|
_bits.Resize(RoundToPadding(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track highest set bit
|
// Track highest set bit
|
||||||
@@ -147,7 +151,7 @@ public sealed class BitSet
|
|||||||
public void ClearBit(int index)
|
public void ClearBit(int index)
|
||||||
{
|
{
|
||||||
var b = index >> _INDEX_SIZE;
|
var b = index >> _INDEX_SIZE;
|
||||||
if (b >= _bits.Length)
|
if (b >= _bits.Count)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -160,13 +164,13 @@ public sealed class BitSet
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetAll()
|
public void SetAll()
|
||||||
{
|
{
|
||||||
var count = _bits.Length;
|
var count = _bits.Count;
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
_bits[i] = 0xffffffff;
|
_bits[i] = 0xffffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
_highestBit = _bits.Length * (_BIT_SIZE + 1) - 1;
|
_highestBit = _bits.Count * (_BIT_SIZE + 1) - 1;
|
||||||
_max = _highestBit / (_BIT_SIZE + 1) + 1;
|
_max = _highestBit / (_BIT_SIZE + 1) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +179,7 @@ public sealed class BitSet
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ClearAll()
|
public void ClearAll()
|
||||||
{
|
{
|
||||||
Array.Clear(_bits);
|
_bits.Clear();
|
||||||
_highestBit = 0;
|
_highestBit = 0;
|
||||||
_max = 0;
|
_max = 0;
|
||||||
}
|
}
|
||||||
@@ -186,7 +190,7 @@ public sealed class BitSet
|
|||||||
public int NextSetBit(int startIndex)
|
public int NextSetBit(int startIndex)
|
||||||
{
|
{
|
||||||
var wordIndex = startIndex >> _BIT_SIZE;
|
var wordIndex = startIndex >> _BIT_SIZE;
|
||||||
if (wordIndex >= _bits.Length)
|
if (wordIndex >= _bits.Count)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -203,7 +207,7 @@ public sealed class BitSet
|
|||||||
}
|
}
|
||||||
|
|
||||||
wordIndex++;
|
wordIndex++;
|
||||||
if (wordIndex >= _bits.Length)
|
if (wordIndex >= _bits.Count)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -215,10 +219,10 @@ public sealed class BitSet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if all bits from this instance match those of the other instance.
|
/// Checks if all bits from this instance match those of the other instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if they match, false if not.</returns>
|
/// <returns>True if they match, false if not.</returns>
|
||||||
[SkipLocalsInit]
|
[SkipLocalsInit]
|
||||||
public bool All(BitSet other)
|
public bool All(UnsafeBitSet other)
|
||||||
{
|
{
|
||||||
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
@@ -277,9 +281,9 @@ public sealed class BitSet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if any bits from this instance match those of the other instance.
|
/// Checks if any bits from this instance match those of the other instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if they match, false if not.</returns>
|
/// <returns>True if they match, false if not.</returns>
|
||||||
public bool Any(BitSet other)
|
public bool Any(UnsafeBitSet other)
|
||||||
{
|
{
|
||||||
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
@@ -338,9 +342,9 @@ public sealed class BitSet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if none bits from this instance match those of the other instance.
|
/// Checks if none bits from this instance match those of the other instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if none match, false if not.</returns>
|
/// <returns>True if none match, false if not.</returns>
|
||||||
public bool None(BitSet other)
|
public bool None(UnsafeBitSet other)
|
||||||
{
|
{
|
||||||
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
@@ -380,9 +384,9 @@ public sealed class BitSet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if exactly all bits from this instance match those of the other instance.
|
/// Checks if exactly all bits from this instance match those of the other instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if they match, false if not.</returns>
|
/// <returns>True if they match, false if not.</returns>
|
||||||
public bool Exclusive(BitSet other)
|
public bool Exclusive(UnsafeBitSet other)
|
||||||
{
|
{
|
||||||
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
var min = Math.Min(Math.Min(Length, other.Length), _max);
|
||||||
|
|
||||||
@@ -439,10 +443,10 @@ public sealed class BitSet
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BitSet operator &(BitSet left, BitSet right)
|
public static UnsafeBitSet operator &(UnsafeBitSet left, UnsafeBitSet right)
|
||||||
{
|
{
|
||||||
var min = Math.Min(left.Length, right.Length);
|
var min = Math.Min(left.Length, right.Length);
|
||||||
var result = new BitSet(min);
|
var result = new UnsafeBitSet(min);
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < min; i++)
|
for (var i = 0; i < min; i++)
|
||||||
@@ -463,10 +467,10 @@ public sealed class BitSet
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BitSet operator |(BitSet left, BitSet right)
|
public static UnsafeBitSet operator |(UnsafeBitSet left, UnsafeBitSet right)
|
||||||
{
|
{
|
||||||
var min = Math.Min(left.Length, right.Length);
|
var min = Math.Min(left.Length, right.Length);
|
||||||
var result = new BitSet(min);
|
var result = new UnsafeBitSet(min);
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < min; i++)
|
for (var i = 0; i < min; i++)
|
||||||
@@ -487,7 +491,7 @@ public sealed class BitSet
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BitSet operator ~(BitSet bitSet)
|
public static UnsafeBitSet operator ~(UnsafeBitSet bitSet)
|
||||||
{
|
{
|
||||||
if (!Vector.IsHardwareAccelerated || bitSet.Length < s_padding)
|
if (!Vector.IsHardwareAccelerated || bitSet.Length < s_padding)
|
||||||
{
|
{
|
||||||
@@ -513,7 +517,7 @@ public sealed class BitSet
|
|||||||
/// Creates a <see cref="Span{T}"/> to access the <see cref="_bits"/>.
|
/// Creates a <see cref="Span{T}"/> to access the <see cref="_bits"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The hash.</returns>
|
/// <returns>The hash.</returns>
|
||||||
public Span<uint> AsSpan()
|
public readonly Span<uint> AsSpan()
|
||||||
{
|
{
|
||||||
var max = _highestBit / (_BIT_SIZE + 1) + 1;
|
var max = _highestBit / (_BIT_SIZE + 1) + 1;
|
||||||
return _bits.AsSpan()[..max];
|
return _bits.AsSpan()[..max];
|
||||||
@@ -543,10 +547,6 @@ public sealed class BitSet
|
|||||||
return span[..Length];
|
return span[..Length];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prints the content of this instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The string.</returns>
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
// Convert uint to binary form for pretty printing
|
// Convert uint to binary form for pretty printing
|
||||||
@@ -559,12 +559,19 @@ public sealed class BitSet
|
|||||||
|
|
||||||
return $"{nameof(_bits)}: {binaryBuilder}, {nameof(Length)}: {Length}";
|
return $"{nameof(_bits)}: {binaryBuilder}, {nameof(Length)}: {Length}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_bits.Dispose();
|
||||||
|
_highestBit = 0;
|
||||||
|
_max = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="SpanBitSet"/> struct
|
/// The <see cref="SpanBitSet"/> struct
|
||||||
/// represents a non resizable collection of bits.
|
/// represents a non resizable collection of bits.
|
||||||
/// Used to set, check and clear bits on a allocated <see cref="BitSet"/> or on the stack.
|
/// Used to set, check and clear bits on a allocated <see cref="UnsafeBitSet"/> or on the stack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly ref struct SpanBitSet
|
public readonly ref struct SpanBitSet
|
||||||
{
|
{
|
||||||
@@ -578,7 +585,7 @@ public readonly ref struct SpanBitSet
|
|||||||
private readonly Span<uint> _bits;
|
private readonly Span<uint> _bits;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SpanBitSet(Span<uint> bits)
|
public SpanBitSet(Span<uint> bits)
|
||||||
{
|
{
|
||||||
@@ -636,7 +643,7 @@ public readonly ref struct SpanBitSet
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Sets all bits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public void SetAll()
|
public void SetAll()
|
||||||
@@ -691,11 +698,6 @@ public readonly ref struct SpanBitSet
|
|||||||
return span[.._bits.Length];
|
return span[.._bits.Length];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prints the content of this instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The string.</returns>
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
// Convert uint to binary form for pretty printing
|
// Convert uint to binary form for pretty printing
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
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.Contracts;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -95,6 +95,14 @@ 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>*)UnsafeUtilities.AddressOf(ref _hashMap));
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid constructor, use <see cref="UnsafeHashMap(int, Allocator, AllocationOption)"/> or <see cref="UnsafeHashMap(int, ref AllocationHandle, AllocationOption)"/> instead.
|
||||||
|
/// </summary>
|
||||||
|
public UnsafeHashMap()
|
||||||
|
: this(0, Allocator.Invalid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public UnsafeHashMap(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
public UnsafeHashMap(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
||||||
{
|
{
|
||||||
_hashMap = new HashMapHelper<TKey>(capacity, sizeof(TValue), HashMapHelper<TKey>.MINIMAL_CAPACITY, ref handle, allocationOption);
|
_hashMap = new HashMapHelper<TKey>(capacity, sizeof(TValue), HashMapHelper<TKey>.MINIMAL_CAPACITY, ref handle, allocationOption);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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.Contracts;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -51,6 +51,14 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeCollection<T>, IEnumerable<T>
|
|||||||
public IEnumerator<T> GetEnumerator() => new Enumerator((HashMapHelper<T>*)UnsafeUtilities.AddressOf(ref _hashMap));
|
public IEnumerator<T> GetEnumerator() => new Enumerator((HashMapHelper<T>*)UnsafeUtilities.AddressOf(ref _hashMap));
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid constructor. Use <see cref="UnsafeHashSet(int, Allocator, AllocationOption)"/> or <see cref="UnsafeHashSet(int, ref AllocationHandle, AllocationOption)"/> instead."/>
|
||||||
|
/// </summary>
|
||||||
|
public UnsafeHashSet()
|
||||||
|
: this(0, Allocator.Invalid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public UnsafeHashSet(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
public UnsafeHashSet(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
||||||
{
|
{
|
||||||
_hashMap = new HashMapHelper<T>(capacity, 0, HashMapHelper<T>.MINIMAL_CAPACITY, ref handle, allocationOption);
|
_hashMap = new HashMapHelper<T>(capacity, 0, HashMapHelper<T>.MINIMAL_CAPACITY, ref handle, allocationOption);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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.Contracts;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
|
|||||||
{
|
{
|
||||||
public struct Enumerator : IEnumerator<T>
|
public struct Enumerator : IEnumerator<T>
|
||||||
{
|
{
|
||||||
private UnsafeList<T>* _collection;
|
private readonly UnsafeList<T>* _collection;
|
||||||
private int _index;
|
private int _index;
|
||||||
private T _value;
|
private T _value;
|
||||||
|
|
||||||
@@ -152,25 +152,35 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly UnsafeArray<T> AsUnsafeArray() => new((T*)_array.GetUnsafePtr(), _count);
|
public readonly UnsafeArray<T> AsUnsafeArray() => new((T*)_array.GetUnsafePtr(), _count);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid constructor, use <see cref="UnsafeList(int, Allocator, AllocationOption)"/> or <see cref="UnsafeList(int, ref AllocationHandle, AllocationOption)"/> instead.
|
||||||
|
/// </summary>
|
||||||
public UnsafeList()
|
public UnsafeList()
|
||||||
: this(0, Allocator.Invalid)
|
: this(0, Allocator.Invalid)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnsafeList(int capacity, ref AllocationHandle handle, AllocationOption allocationType = AllocationOption.None)
|
/// <summary>
|
||||||
|
/// Initializes a new instance of UnsafeList with a specified number of initial capacity and an allocation handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <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="allocationOption">Specifies how the memory should be allocated.</param>
|
||||||
|
public UnsafeList(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
||||||
{
|
{
|
||||||
if (capacity <= 0)
|
_array = new UnsafeArray<T>(capacity, ref handle, allocationOption);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero.");
|
|
||||||
}
|
|
||||||
_array = new UnsafeArray<T>(capacity, ref handle, allocationType);
|
|
||||||
_count = 0;
|
_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnsafeList(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.None)
|
/// <summary>
|
||||||
|
/// Initializes a new instance of UnsafeList with a specified number of initial capacity and an allocation type.
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
public UnsafeList(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
|
||||||
|
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption)
|
||||||
{
|
{
|
||||||
_array = new UnsafeArray<T>(capacity, allocator, allocationType);
|
|
||||||
_count = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly void CheckNoResizeCapacity(int count)
|
private readonly void CheckNoResizeCapacity(int count)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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.Helpers;
|
using Misaki.HighPerformance.LowLevel.Contracts;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
|
|||||||
{
|
{
|
||||||
public struct Enumerator : IEnumerator<T>
|
public struct Enumerator : IEnumerator<T>
|
||||||
{
|
{
|
||||||
private UnsafeQueue<T>* _collection;
|
private readonly UnsafeQueue<T>* _collection;
|
||||||
private int _index;
|
private int _index;
|
||||||
private T _value;
|
private T _value;
|
||||||
|
|
||||||
@@ -82,13 +83,24 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
|
|||||||
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeQueue<T>*)UnsafeUtilities.AddressOf(ref this));
|
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeQueue<T>*)UnsafeUtilities.AddressOf(ref this));
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
public UnsafeQueue() : this(1, Allocator.Persistent)
|
/// <summary>
|
||||||
|
/// Invalid constructor. Use <see cref="UnsafeQueue(int, Allocator, AllocationOption)"/> or <see cref="UnsafeQueue(int, ref AllocationHandle, AllocationOption)"/> instead."/>
|
||||||
|
/// </summary>
|
||||||
|
public UnsafeQueue()
|
||||||
|
: this(0, Allocator.Invalid)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnsafeQueue(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.None)
|
public UnsafeQueue(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
||||||
|
{
|
||||||
|
_array = new UnsafeArray<T>(capacity, ref handle, allocationOption);
|
||||||
|
_count = 0;
|
||||||
|
_offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnsafeQueue(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.None)
|
||||||
|
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationType)
|
||||||
{
|
{
|
||||||
_array = new UnsafeArray<T>(capacity, allocator, allocationType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
308
Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs
Normal file
308
Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Contracts;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an unsafe, high-performance slot map for storing and managing unmanaged values, supporting fast insertion,
|
||||||
|
/// removal, and lookup by slot index and generation.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of value to store in the slot map. Must be unmanaged.</typeparam>
|
||||||
|
public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
public struct Enumerator : IEnumerator<T>
|
||||||
|
{
|
||||||
|
private readonly UnsafeSlotMap<T>* _collection;
|
||||||
|
private int _currentIndex;
|
||||||
|
|
||||||
|
public Enumerator(UnsafeSlotMap<T>* collection)
|
||||||
|
{
|
||||||
|
_collection = collection;
|
||||||
|
_currentIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly T Current => _collection->_data[_currentIndex].value;
|
||||||
|
readonly object? IEnumerator.Current => Current;
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
while (++_currentIndex < _collection->_capacity)
|
||||||
|
{
|
||||||
|
if (_collection->_data[_currentIndex].isValid)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset() => _currentIndex = -1;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct SlotData
|
||||||
|
{
|
||||||
|
public T value;
|
||||||
|
public int generation;
|
||||||
|
public bool isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UnsafeArray<SlotData> _data;
|
||||||
|
private UnsafeQueue<int> _freeSlots;
|
||||||
|
|
||||||
|
private int _count;
|
||||||
|
private int _capacity;
|
||||||
|
|
||||||
|
public readonly int Count => _count;
|
||||||
|
public readonly int Capacity => _capacity;
|
||||||
|
|
||||||
|
public readonly bool IsCreated => _data.IsCreated && _freeSlots.IsCreated;
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeSlotMap<T>*)UnsafeUtilities.AddressOf(ref this));
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid constructor. Use <see cref="UnsafeSlotMap(int, Allocator, AllocationOption)"/> or <see cref="UnsafeSlotMap(int, ref AllocationHandle, AllocationOption)"/> instead."/>
|
||||||
|
/// </summary>
|
||||||
|
public UnsafeSlotMap()
|
||||||
|
: this(0, Allocator.Invalid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the UnsafeSlotMap class with the specified capacity, allocation handle, and
|
||||||
|
/// allocation options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="capacity">The number of slots to allocate for the map. Must be greater than zero.</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>
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (capacity <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_data = new UnsafeArray<SlotData>(capacity, ref handle, allocationOption);
|
||||||
|
_freeSlots = new UnsafeQueue<int>(capacity, ref handle, allocationOption);
|
||||||
|
_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the UnsafeSlotMap class with the specified capacity, allocator, and allocation
|
||||||
|
/// options.
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
public UnsafeSlotMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
|
||||||
|
: this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified item to the collection and returns the index of the slot where it was stored.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to add to the collection.</param>
|
||||||
|
/// <param name="generation">When this method returns, contains the generation number associated with the slot where the item was stored.</param>
|
||||||
|
/// <returns>The index of the slot in which the item was stored.</returns>
|
||||||
|
public int Add(T item, out int generation)
|
||||||
|
{
|
||||||
|
if (_count >= _capacity)
|
||||||
|
{
|
||||||
|
Resize(_capacity * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int slotIndex;
|
||||||
|
if (_freeSlots.Count == 0)
|
||||||
|
{
|
||||||
|
slotIndex = _count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slotIndex = _freeSlots.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
slot.value = item;
|
||||||
|
slot.isValid = true;
|
||||||
|
generation = slot.generation;
|
||||||
|
|
||||||
|
_count++;
|
||||||
|
|
||||||
|
return slotIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to remove the item at the specified slot index and generation from the collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slotIndex">The zero-based index of the slot to remove. Must be within the valid range of slot indices.</param>
|
||||||
|
/// <param name="generation">The generation value associated with the slot. Removal succeeds only if this matches the current generation of
|
||||||
|
/// the slot.</param>
|
||||||
|
/// <returns>true if the item was successfully removed; otherwise, false.</returns>
|
||||||
|
public bool Remove(int slotIndex, int generation)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= _capacity)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
if (slot.generation != generation)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot.generation++;
|
||||||
|
slot.isValid = false;
|
||||||
|
|
||||||
|
_freeSlots.Enqueue(slotIndex);
|
||||||
|
_count--;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified slot index contains a valid entry with the given generation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slotIndex">The zero-based index of the slot to check. Must be greater than or equal to 0 and less than the current capacity.</param>
|
||||||
|
/// <param name="generation">The generation value to compare against the slot's generation.</param>
|
||||||
|
/// <returns>true if the slot at the specified index is valid and its generation matches the specified value; otherwise, false.</returns>
|
||||||
|
public bool Contain(int slotIndex, int generation)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= Volatile.Read(ref _capacity))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
|
||||||
|
if (slot.isValid && slot.generation == generation)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to retrieve the element at the specified slot index and generation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slotIndex">The zero-based index of the slot to retrieve. Must be within the valid range of slots.</param>
|
||||||
|
/// <param name="generation">The generation identifier associated with the slot. Used to verify that the slot has not been replaced or
|
||||||
|
/// invalidated.</param>
|
||||||
|
/// <param name="value">When this method returns, contains the element at the specified slot and generation if found; otherwise, the
|
||||||
|
/// default value for type <typeparamref name="T"/>.</param>
|
||||||
|
/// <returns>true if the element at the specified slot index and generation is found; otherwise, false.</returns>
|
||||||
|
public bool TryGetElementAt(int slotIndex, int generation, out T value)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= _capacity)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
if (slot.generation != generation)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = slot.value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the element stored at the specified slot index and generation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slotIndex">The zero-based index of the slot from which to retrieve the element. Must be within the valid range of allocated slots.</param>
|
||||||
|
/// <param name="generation">The generation identifier associated with the slot. Used to ensure the element has not been replaced or removed since allocation.</param>
|
||||||
|
/// <returns>The element of type <see cref="T"/> stored at the specified slot and generation.</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="slotIndex"/> is less than zero or greater than or equal to the capacity.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown when the specified slot is not occupied or the generation does not match.</exception>
|
||||||
|
public T GetElementAt(int slotIndex, int generation)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= _capacity)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(slotIndex), "Slot index is out of range.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
if (!slot.isValid || slot.generation != generation)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Slot {slotIndex} is not occupied.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return slot.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a reference to the element at the specified slot index and generation, if it exists; otherwise, returns
|
||||||
|
/// a null reference.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slotIndex">The zero-based index of the slot to retrieve. Must be within the valid range of allocated slots.</param>
|
||||||
|
/// <param name="generation">The expected generation value for the slot. Used to verify that the slot has not been recycled or replaced.</param>
|
||||||
|
/// <param name="exist">When this method returns, contains <see langword="true"/> if a valid element exists at the specified slot and generation; otherwise, <see langword="false"/>.</param>
|
||||||
|
/// <returns>A reference to the element of type <typeparamref name="T"/> at the specified slot and generation if it exists; otherwise, a null reference.</returns>
|
||||||
|
public ref T GetElementReferenceAt(int slotIndex, int generation, out bool exist)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= _capacity)
|
||||||
|
{
|
||||||
|
exist = false;
|
||||||
|
return ref Unsafe.NullRef<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
|
||||||
|
if (!slot.isValid|| slot.generation != generation)
|
||||||
|
{
|
||||||
|
exist = false;
|
||||||
|
return ref Unsafe.NullRef<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
exist = true;
|
||||||
|
return ref slot.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Resize(int newSize)
|
||||||
|
{
|
||||||
|
_data.Resize(newSize);
|
||||||
|
_freeSlots.Resize(newSize);
|
||||||
|
|
||||||
|
_capacity = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_data.Clear();
|
||||||
|
_freeSlots.Clear();
|
||||||
|
|
||||||
|
_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe readonly void* GetUnsafePtr()
|
||||||
|
{
|
||||||
|
return _data.GetUnsafePtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_data.Dispose();
|
||||||
|
_freeSlots.Dispose();
|
||||||
|
|
||||||
|
_count = 0;
|
||||||
|
_capacity = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
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.Contracts;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
|
|
||||||
public struct Enumerator : IEnumerator<T>
|
public struct Enumerator : IEnumerator<T>
|
||||||
{
|
{
|
||||||
private UnsafeSparseSet<T>* _collection;
|
private readonly UnsafeSparseSet<T>* _collection;
|
||||||
private int _index;
|
private int _index;
|
||||||
private T _value;
|
private T _value;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
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.Helpers;
|
using Misaki.HighPerformance.LowLevel.Contracts;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
|
/// <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>
|
||||||
public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
@@ -24,15 +30,41 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
|||||||
return GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnsafeStack() : this(1, Allocator.Persistent)
|
/// <summary>
|
||||||
|
/// Invalid constructor, use <see cref="UnsafeStack(int, Allocator, AllocationOption)"/> or <see cref="UnsafeStack(int, ref AllocationHandle, AllocationOption)"/> instead.
|
||||||
|
/// </summary>
|
||||||
|
public UnsafeStack()
|
||||||
|
: this(0, Allocator.Invalid)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnsafeStack(int initialSize, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the UnsafeStack class with the specified initial capacity and allocation options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initialCapacity">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 initialCapacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
||||||
{
|
{
|
||||||
_array = new UnsafeArray<T>(initialSize, allocator, allocationOption);
|
_array = new UnsafeArray<T>(initialCapacity, ref handle, allocationOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the UnsafeStack class with the specified initial capacity, allocator, and
|
||||||
|
/// allocation options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initialCapacity">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>
|
||||||
|
public UnsafeStack(int initialCapacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
|
||||||
|
: this(initialCapacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
public void Push(T value)
|
||||||
{
|
{
|
||||||
if (_count >= _array.Count)
|
if (_count >= _array.Count)
|
||||||
@@ -44,6 +76,11 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
|||||||
_count++;
|
_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()
|
public T Pop()
|
||||||
{
|
{
|
||||||
if (_count == 0)
|
if (_count == 0)
|
||||||
@@ -55,6 +92,12 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
|||||||
return _array[_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)
|
public bool TryPop(out T value)
|
||||||
{
|
{
|
||||||
if (_count == 0)
|
if (_count == 0)
|
||||||
@@ -68,6 +111,11 @@ public unsafe struct UnsafeStack<T> : IUnsafeCollection<T>
|
|||||||
return true;
|
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()
|
public readonly T Peek()
|
||||||
{
|
{
|
||||||
if (_count == 0)
|
if (_count == 0)
|
||||||
|
|||||||
@@ -21,13 +21,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Buffer\FixedString.tt">
|
<None Update="Collections\FixedString.tt">
|
||||||
|
<Generator>TextTemplatingFileGenerator</Generator>
|
||||||
<LastGenOutput>FixedString.cs</LastGenOutput>
|
<LastGenOutput>FixedString.cs</LastGenOutput>
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
|
||||||
</None>
|
|
||||||
<None Update="Buffer\FixedStackString.tt">
|
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
|
||||||
<LastGenOutput>FixedStackString.cs</LastGenOutput>
|
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -36,15 +32,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Buffer\FixedString.cs">
|
<Compile Update="Collections\FixedString.cs">
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>FixedString.tt</DependentUpon>
|
<DependentUpon>FixedString.tt</DependentUpon>
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Buffer\FixedStackString.cs">
|
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
<DependentUpon>FixedStackString.tt</DependentUpon>
|
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Misaki.HighPerformance.LowLevel.Contracts;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Helpers;
|
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|
||||||
public unsafe struct HashMapHelper<TKey> : IDisposable
|
public unsafe struct HashMapHelper<TKey> : IDisposable
|
||||||
where TKey : unmanaged, IEquatable<TKey>
|
where TKey : unmanaged, IEquatable<TKey>
|
||||||
@@ -135,12 +135,24 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
Clear();
|
Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int CeilPow2(int x)
|
||||||
|
{
|
||||||
|
x -= 1;
|
||||||
|
x |= x >> 1;
|
||||||
|
x |= x >> 2;
|
||||||
|
x |= x >> 4;
|
||||||
|
x |= x >> 8;
|
||||||
|
x |= x >> 16;
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private readonly int CalcCapacityCeilPow2(int capacity)
|
private readonly int CalcCapacityCeilPow2(int capacity)
|
||||||
{
|
{
|
||||||
capacity = Math.Max(Math.Max(1, _count), capacity);
|
capacity = Math.Max(Math.Max(1, _count), capacity);
|
||||||
var newCapacity = Math.Max(capacity, 1 << _log2MinGrowth);
|
var newCapacity = Math.Max(capacity, 1 << _log2MinGrowth);
|
||||||
var result = MathUtilities.CeilPow2(newCapacity);
|
var result = CeilPow2(newCapacity);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -204,7 +216,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
internal void Resize(int newCapacity)
|
internal void Resize(int newCapacity)
|
||||||
{
|
{
|
||||||
newCapacity = Math.Max(newCapacity, _count);
|
newCapacity = Math.Max(newCapacity, _count);
|
||||||
var newBucketCapacity = MathUtilities.CeilPow2(newCapacity * 2);
|
var newBucketCapacity = CeilPow2(newCapacity * 2);
|
||||||
|
|
||||||
if (_capacity == newCapacity && _bucketCapacity == newBucketCapacity)
|
if (_capacity == newCapacity && _bucketCapacity == newBucketCapacity)
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@ using System.Numerics;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Helpers;
|
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|
||||||
public static unsafe partial class MemoryUtilities
|
public static unsafe partial class MemoryUtilities
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Helpers;
|
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|
||||||
public static unsafe partial class MemoryUtilities
|
public static unsafe partial class MemoryUtilities
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
|||||||
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Helpers;
|
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides extension methods for copying elements between unsafe collections and spans, converting collections to
|
/// Provides extension methods for copying elements between unsafe collections and spans, converting collections to
|
||||||
@@ -208,6 +208,22 @@ public unsafe static class UnsafeCollectionExtensions
|
|||||||
return new(source.GetUnsafePtr(), source.Count);
|
return new(source.GetUnsafePtr(), source.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a span over a contiguous region of elements in the specified unsafe collection, starting at the given
|
||||||
|
/// index and covering the specified number of elements.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of elements in the collection. Must be an unmanaged type.</typeparam>
|
||||||
|
/// <param name="source">The unsafe collection from which to create the span. Must not be null.</param>
|
||||||
|
/// <param name="start">The zero-based index of the first element in the collection to include in the span. Must be greater than or equal to zero and less than the number of elements in the collection.</param>
|
||||||
|
/// <param name="length">The number of elements to include in the span. Must be greater than or equal to zero and the range defined by
|
||||||
|
/// <paramref name="start"/> and <paramref name="length"/> must not exceed the bounds of the collection.</param>
|
||||||
|
/// <returns>A <see cref="Span{T}"/> representing the specified region of the collection.</returns>
|
||||||
|
public static Span<T> AsSpan<T>(this IUnsafeCollection<T> source, int start, int length)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
return new((T*)source.GetUnsafePtr() + start, length);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an UnTypedCollection into a Span for efficient memory access.
|
/// Converts an UnTypedCollection into a Span for efficient memory access.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -218,6 +234,20 @@ public unsafe static class UnsafeCollectionExtensions
|
|||||||
return new(source.GetUnsafePtr(), (int)source.Size);
|
return new(source.GetUnsafePtr(), (int)source.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a span over a contiguous region of elements in the specified unsafe collection, starting at the given
|
||||||
|
/// index and covering the specified number of elements.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The unsafe collection from which to create the span. Must not be null.</param>
|
||||||
|
/// <param name="start">The zero-based index of the first element in the collection to include in the span. Must be greater than or equal to zero and less than the number of elements in the collection.</param>
|
||||||
|
/// <param name="length">The number of elements to include in the span. Must be greater than or equal to zero and the range defined by
|
||||||
|
/// <paramref name="start"/> and <paramref name="length"/> must not exceed the bounds of the collection.</param>
|
||||||
|
/// <returns>A <see cref="Span{byte}"/> representing the specified region of the collection.</returns>
|
||||||
|
public static Span<byte> AsSpan(this IUnTypedCollection source, int start, int length)
|
||||||
|
{
|
||||||
|
return new((byte*)source.GetUnsafePtr() + start, length);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a managed array to an UnsafeArray by copying its elements to unmanaged memory.
|
/// Converts a managed array to an UnsafeArray by copying its elements to unmanaged memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Helpers;
|
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|
||||||
public static unsafe class UnsafeUtilities
|
public static unsafe class UnsafeUtilities
|
||||||
{
|
{
|
||||||
@@ -8,6 +8,9 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
{
|
{
|
||||||
protected const string INLINE_METHOD_ATTRIBUTE = "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]";
|
protected const string INLINE_METHOD_ATTRIBUTE = "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]";
|
||||||
|
|
||||||
|
protected static readonly string[] s_matrixComponents = new[] { "c0", "c1", "c2", "c3" };
|
||||||
|
protected static readonly string[] s_vectorComponents = new[] { "x", "y", "z", "w" };
|
||||||
|
|
||||||
protected readonly StringBuilder sourceBuilder = new();
|
protected readonly StringBuilder sourceBuilder = new();
|
||||||
protected NumericTypeInfo typeInfo = null!;
|
protected NumericTypeInfo typeInfo = null!;
|
||||||
|
|
||||||
@@ -30,13 +33,31 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
return sourceBuilder.ToString();
|
return sourceBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void StartRegion(string name)
|
||||||
|
{
|
||||||
|
sourceBuilder.AppendLine($@"
|
||||||
|
#region {name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void EndRegion()
|
||||||
|
{
|
||||||
|
sourceBuilder.AppendLine($@"
|
||||||
|
#endregion");
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Initialize()
|
protected virtual void Initialize()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void GenerateHeader()
|
protected virtual void GenerateHeader()
|
||||||
{
|
{
|
||||||
sourceBuilder.AppendLine("// <auto-generated/>");
|
sourceBuilder.AppendLine(@"// <auto-generated>
|
||||||
|
// This code is generated by tool. Do not edit manually.
|
||||||
|
// Update this file may introduce incorrect behavior and will be lost if the code is regenerated.
|
||||||
|
// To update this file, edit the generator in the Misaki.HighPerformance.Mathematics.CodeGen and recompile it.
|
||||||
|
// </auto-generated>
|
||||||
|
");
|
||||||
|
sourceBuilder.AppendLine("#nullable enable");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void GenerateNamespaceStart()
|
protected virtual void GenerateNamespaceStart()
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
||||||
{
|
{
|
||||||
internal class MatrixGenerator : GeneratorBase
|
internal class MatrixGenerator : GeneratorBase
|
||||||
{
|
{
|
||||||
private readonly string[] _matrixComponents = new[] { "c0", "c1", "c2", "c3" };
|
private readonly List<(string signature, string assignment)> _constructorSignatures = new();
|
||||||
private readonly string[] _vectorComponents = new[] { "x", "y", "z", "w" };
|
|
||||||
|
private string GetConversionFromTemplate(string template, int componentIndex)
|
||||||
|
{
|
||||||
|
return template.Replace("{v}", "v")
|
||||||
|
.Replace("{c}", s_matrixComponents[componentIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void GenerateBody()
|
protected override void GenerateBody()
|
||||||
{
|
{
|
||||||
GenerateField();
|
GenerateField();
|
||||||
|
|
||||||
if (typeInfo.Arithmetic)
|
if (typeInfo.Arithmetic)
|
||||||
{
|
{
|
||||||
GenerateUnitMatrix();
|
GenerateUnitMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
GenerateConstructors();
|
GenerateConstructors();
|
||||||
GenerateUnsafeMethod();
|
GenerateUnsafeMethod();
|
||||||
GenerateOverrideMethod();
|
GenerateOverrideMethod();
|
||||||
|
|
||||||
if (typeInfo.Arithmetic)
|
if (typeInfo.Arithmetic)
|
||||||
{
|
{
|
||||||
GenerateArithmeticOperators();
|
GenerateArithmeticOperators();
|
||||||
@@ -38,14 +47,24 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
for (var i = 0; i < typeInfo.Column; i++)
|
for (var i = 0; i < typeInfo.Column; i++)
|
||||||
{
|
{
|
||||||
sourceBuilder.Append($@"
|
sourceBuilder.Append($@"
|
||||||
public {typeInfo.ComponentTypeFullName} {_matrixComponents[i]};");
|
public {typeInfo.ComponentTypeFullName} {s_matrixComponents[i]};");
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceBuilder.AppendLine();
|
sourceBuilder.AppendLine();
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public unsafe ref {typeInfo.ComponentTypeFullName} this[int index] {{
|
public unsafe ref {typeInfo.ComponentTypeFullName} this[int index]
|
||||||
|
{{
|
||||||
{INLINE_METHOD_ATTRIBUTE}
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
get => ref (({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this))[index];
|
get
|
||||||
|
{{
|
||||||
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
|
if (index < 0 || index >= {typeInfo.Column})
|
||||||
|
{{
|
||||||
|
throw new global::System.ArgumentOutOfRangeException(nameof(index), $""Index {{index}} is out of range of '{typeInfo.TypeName}'"");
|
||||||
|
}}
|
||||||
|
#endif
|
||||||
|
return ref (({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this))[index];
|
||||||
|
}}
|
||||||
}}");
|
}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,28 +124,6 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
}}
|
}}
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
sourceBuilder.Append($@"
|
|
||||||
public {typeInfo.TypeName}({typeInfo.ComponentTypeFullName} value)
|
|
||||||
{{");
|
|
||||||
for (var i = 0; i < typeInfo.Column; i++)
|
|
||||||
{
|
|
||||||
sourceBuilder.Append($@"
|
|
||||||
this.{_matrixComponents[i]} = value;");
|
|
||||||
}
|
|
||||||
sourceBuilder.AppendLine(@"
|
|
||||||
}");
|
|
||||||
|
|
||||||
sourceBuilder.Append($@"
|
|
||||||
public {typeInfo.TypeName}({string.Join(", ", Enumerable.Range(0, typeInfo.Column).Select(i => $"{typeInfo.ComponentTypeFullName} c{i}"))})
|
|
||||||
{{");
|
|
||||||
for (var i = 0; i < typeInfo.Column; i++)
|
|
||||||
{
|
|
||||||
sourceBuilder.Append($@"
|
|
||||||
this.{_matrixComponents[i]} = c{i};");
|
|
||||||
}
|
|
||||||
sourceBuilder.Append(@"
|
|
||||||
}");
|
|
||||||
|
|
||||||
if (typeInfo.ElementTypeSymbol != null)
|
if (typeInfo.ElementTypeSymbol != null)
|
||||||
{
|
{
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
@@ -144,16 +141,9 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
}}
|
}}
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
sourceBuilder.Append($@"
|
_constructorSignatures.Add((
|
||||||
public {typeInfo.TypeName}({typeInfo.ElementTypeFullName} value)
|
signature: $"{typeInfo.ElementTypeFullName} value",
|
||||||
{{");
|
assignment: string.Join(", ", Enumerable.Range(0, typeInfo.Column).Select(_ => $"new {typeInfo.ComponentTypeFullName}(value)"))));
|
||||||
for (var c = 0; c < typeInfo.Column; c++)
|
|
||||||
{
|
|
||||||
sourceBuilder.Append($@"
|
|
||||||
this.{_matrixComponents[c]} = value;");
|
|
||||||
}
|
|
||||||
sourceBuilder.AppendLine($@"
|
|
||||||
}}");
|
|
||||||
|
|
||||||
var tempSB = new StringBuilder();
|
var tempSB = new StringBuilder();
|
||||||
for (var r = 0; r < typeInfo.Row; r++)
|
for (var r = 0; r < typeInfo.Row; r++)
|
||||||
@@ -168,17 +158,73 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceBuilder.Append($@"
|
_constructorSignatures.Add((
|
||||||
public {typeInfo.TypeName}({tempSB})
|
signature: tempSB.ToString(),
|
||||||
{{");
|
assignment: string.Join(", ", Enumerable.Range(0, typeInfo.Column).Select(c => $"new {typeInfo.ComponentTypeFullName}({string.Join(", ", Enumerable.Range(0, typeInfo.Row).Select(r => $"m{r}{c}"))})"))));
|
||||||
for (var c = 0; c < typeInfo.Column; c++)
|
|
||||||
{
|
|
||||||
sourceBuilder.Append($@"
|
|
||||||
this.{_matrixComponents[c]} = new({string.Join(", ", Enumerable.Range(0, typeInfo.Row).Select(r => $"m{r}{c}"))});");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_constructorSignatures.Add((
|
||||||
|
signature: $"{typeInfo.ComponentTypeFullName} value",
|
||||||
|
assignment: string.Join(", ", Enumerable.Range(0, typeInfo.Column).Select(i => "value"))));
|
||||||
|
|
||||||
|
_constructorSignatures.Add((
|
||||||
|
signature: string.Join(", ", Enumerable.Range(0, typeInfo.Column).Select(i => $"{typeInfo.ComponentTypeFullName} c{i}")),
|
||||||
|
assignment: string.Join(", ", Enumerable.Range(0, typeInfo.Column).Select(i => $"c{i}"))));
|
||||||
|
|
||||||
|
if (typeInfo.ConvertableTypes != null)
|
||||||
|
{
|
||||||
|
foreach (var kv in typeInfo.ConvertableTypes)
|
||||||
|
{
|
||||||
|
var targetTemplate = kv.Key;
|
||||||
|
var targetTypes = kv.Value;
|
||||||
|
|
||||||
|
var tempSB = new StringBuilder();
|
||||||
|
foreach (var type in targetTypes)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < typeInfo.Column; i++)
|
||||||
|
{
|
||||||
|
tempSB.Append(GetConversionFromTemplate(targetTemplate, i));
|
||||||
|
if (i < typeInfo.Column - 1)
|
||||||
|
{
|
||||||
|
tempSB.Append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_constructorSignatures.Add((
|
||||||
|
signature: $"{type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} v",
|
||||||
|
assignment: tempSB.ToString()));
|
||||||
|
|
||||||
|
tempSB.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (signature, assignment) in _constructorSignatures)
|
||||||
|
{
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
|
public {typeInfo.TypeName}({signature})
|
||||||
|
{{
|
||||||
|
this = Create({assignment});
|
||||||
}}");
|
}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceBuilder.Append($@"
|
||||||
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
|
public static {typeInfo.TypeName} Create({string.Join(", ", Enumerable.Range(0, typeInfo.Column).Select((_, i) => $"{typeInfo.ComponentTypeFullName} {s_matrixComponents[i]}"))})
|
||||||
|
{{
|
||||||
|
global::System.Runtime.CompilerServices.Unsafe.SkipInit(out {typeInfo.TypeFullName} result);
|
||||||
|
");
|
||||||
|
|
||||||
|
for (var i = 0; i < typeInfo.Column; i++)
|
||||||
|
{
|
||||||
|
sourceBuilder.Append($@"
|
||||||
|
result.{s_matrixComponents[i]} = {s_matrixComponents[i]};");
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceBuilder.AppendLine($@"
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateUnsafeMethod()
|
private void GenerateUnsafeMethod()
|
||||||
@@ -199,7 +245,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
|
|
||||||
private void GenerateOverrideMethod()
|
private void GenerateOverrideMethod()
|
||||||
{
|
{
|
||||||
var components = _matrixComponents.Take(typeInfo.Column).ToArray();
|
var components = s_matrixComponents.Take(typeInfo.Column).ToArray();
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public override readonly string ToString()
|
public override readonly string ToString()
|
||||||
{{
|
{{
|
||||||
@@ -248,85 +294,85 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} operator +({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator +({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} + rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} + rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator +({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator +({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} + rhs"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} + rhs"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator +({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator +({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs + rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs + rhs.{c}"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Subtract
|
// Subtract
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} operator -({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator -({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} - rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} - rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator -({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator -({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} - rhs"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} - rhs"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator -({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator -({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs - rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs - rhs.{c}"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Multiply
|
// Multiply
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} operator *({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator *({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} * rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} * rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator *({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator *({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} * rhs"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} * rhs"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator *({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator *({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs * rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs * rhs.{c}"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Divide
|
// Divide
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} operator /({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator /({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs / rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs / rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator /({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator /({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} / rhs"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} / rhs"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator /({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator /({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} / rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} / rhs.{c}"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Modulus
|
// Modulus
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} operator %({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator %({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} % rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} % rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator %({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator %({typeInfo.TypeFullName} lhs, {typeInfo.ComponentTypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} % rhs"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} % rhs"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator %({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator %({typeInfo.ComponentTypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs % rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs % rhs.{c}"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Unary operators
|
// Unary operators
|
||||||
@@ -338,71 +384,71 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator -({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypeFullName} operator -({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"-value.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"-value.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator ++({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypeFullName} operator ++({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"value.{c} + 1"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"value.{c} + 1"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator --({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypeFullName} operator --({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"value.{c} - 1"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"value.{c} - 1"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Comparison operators
|
// Comparison operators
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator <({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator <({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} < rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} < rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator <=({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator <=({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} <= rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} <= rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator >({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator >({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} > rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} > rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator >=({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row}x{typeInfo.Column} operator >=({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} >= rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} >= rhs.{c}"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Bitwise operators
|
// Bitwise operators
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} operator <<({typeInfo.TypeFullName} lhs, int shift)
|
public static {typeInfo.TypeFullName} operator <<({typeInfo.TypeFullName} lhs, int shift)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} << shift"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} << shift"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator >>({typeInfo.TypeFullName} lhs, int shift)
|
public static {typeInfo.TypeFullName} operator >>({typeInfo.TypeFullName} lhs, int shift)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} >> shift"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} >> shift"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator &({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator &({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} & rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} & rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator |({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator |({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} | rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} | rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator ^({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
public static {typeInfo.TypeFullName} operator ^({typeInfo.TypeFullName} lhs, {typeInfo.TypeFullName} rhs)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} ^ rhs.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"lhs.{c} ^ rhs.{c}"))});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public static {typeInfo.TypeFullName} operator ~({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypeFullName} operator ~({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
return new({string.Join(", ", _matrixComponents.Take(typeInfo.Column).Select(c => $"~value.{c}"))});
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"~value.{c}"))});
|
||||||
}}");
|
}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,14 +461,25 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
|
|
||||||
sourceBuilder.Append($@"
|
sourceBuilder.Append($@"
|
||||||
public static partial class math
|
public static partial class math
|
||||||
|
{{");
|
||||||
|
foreach (var (signature, assignment) in _constructorSignatures)
|
||||||
|
{
|
||||||
|
sourceBuilder.AppendLine($@"
|
||||||
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
|
public static {typeInfo.TypeFullName} {typeInfo.TypeName}({signature})
|
||||||
{{
|
{{
|
||||||
|
return {typeInfo.TypeFullName}.Create({assignment});
|
||||||
|
}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceBuilder.Append($@"
|
||||||
public static {typeInfo.TypePrefix}{typeInfo.Column}x{typeInfo.Row} transpose({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypePrefix}{typeInfo.Column}x{typeInfo.Row} transpose({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
return new {typeInfo.TypePrefix}{typeInfo.Column}x{typeInfo.Row}(");
|
return new {typeInfo.TypePrefix}{typeInfo.Column}x{typeInfo.Row}(");
|
||||||
for (var i = 0; i < typeInfo.Column; i++)
|
for (var i = 0; i < typeInfo.Column; i++)
|
||||||
{
|
{
|
||||||
sourceBuilder.Append($@"
|
sourceBuilder.Append($@"
|
||||||
{string.Join(", ", _matrixComponents.Take(typeInfo.Row).Select((c, j) => $"value.{_matrixComponents[i]}.{_vectorComponents[j]}"))}");
|
{string.Join(", ", s_matrixComponents.Take(typeInfo.Row).Select((c, j) => $"value.{s_matrixComponents[i]}.{s_vectorComponents[j]}"))}");
|
||||||
if (i < typeInfo.Column - 1)
|
if (i < typeInfo.Column - 1)
|
||||||
{
|
{
|
||||||
sourceBuilder.Append(",");
|
sourceBuilder.Append(",");
|
||||||
@@ -459,6 +516,13 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
}
|
}
|
||||||
|
|
||||||
GenerateMulMethod();
|
GenerateMulMethod();
|
||||||
|
|
||||||
|
sourceBuilder.AppendLine($@"
|
||||||
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
|
public static {typeInfo.TypeFullName} abs({typeInfo.TypeFullName} value)
|
||||||
|
{{
|
||||||
|
return new({string.Join(", ", s_matrixComponents.Take(typeInfo.Column).Select(c => $"abs(value.{c})"))});
|
||||||
|
}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceBuilder.Append($@"
|
sourceBuilder.Append($@"
|
||||||
@@ -470,8 +534,8 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} inverse({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypeFullName} inverse({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
var c0 = value.{_matrixComponents[0]};
|
var c0 = value.{s_matrixComponents[0]};
|
||||||
var c1 = value.{_matrixComponents[1]};
|
var c1 = value.{s_matrixComponents[1]};
|
||||||
|
|
||||||
// elements
|
// elements
|
||||||
var m00 = c0.x;
|
var m00 = c0.x;
|
||||||
@@ -497,9 +561,9 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} inverse({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypeFullName} inverse({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
var c0 = value.{_matrixComponents[0]};
|
var c0 = value.{s_matrixComponents[0]};
|
||||||
var c1 = value.{_matrixComponents[1]};
|
var c1 = value.{s_matrixComponents[1]};
|
||||||
var c2 = value.{_matrixComponents[2]};
|
var c2 = value.{s_matrixComponents[2]};
|
||||||
|
|
||||||
var a00 = c0.x;
|
var a00 = c0.x;
|
||||||
var a01 = c1.x;
|
var a01 = c1.x;
|
||||||
@@ -542,10 +606,10 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
public static {typeInfo.TypeFullName} inverse({typeInfo.TypeFullName} value)
|
public static {typeInfo.TypeFullName} inverse({typeInfo.TypeFullName} value)
|
||||||
{{
|
{{
|
||||||
var c0 = value.{_matrixComponents[0]};
|
var c0 = value.{s_matrixComponents[0]};
|
||||||
var c1 = value.{_matrixComponents[1]};
|
var c1 = value.{s_matrixComponents[1]};
|
||||||
var c2 = value.{_matrixComponents[2]};
|
var c2 = value.{s_matrixComponents[2]};
|
||||||
var c3 = value.{_matrixComponents[3]};
|
var c3 = value.{s_matrixComponents[3]};
|
||||||
|
|
||||||
// movelh
|
// movelh
|
||||||
var r0y_r1y_r0x_r1x = shuffle(c1, c0, ShuffleComponent.LeftX, ShuffleComponent.LeftY, ShuffleComponent.RightX, ShuffleComponent.RightY);
|
var r0y_r1y_r0x_r1x = shuffle(c1, c0, ShuffleComponent.LeftX, ShuffleComponent.LeftY, ShuffleComponent.RightX, ShuffleComponent.RightY);
|
||||||
@@ -588,19 +652,19 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
|
|
||||||
var rcp_denom_ppnn = 1 / denom;
|
var rcp_denom_ppnn = 1 / denom;
|
||||||
{typeInfo.TypeFullName} res;
|
{typeInfo.TypeFullName} res;
|
||||||
res.{_matrixComponents[0]} = minors0 * rcp_denom_ppnn;
|
res.{s_matrixComponents[0]} = minors0 * rcp_denom_ppnn;
|
||||||
|
|
||||||
var inner30 = shuffle(inner30_01, inner30_01, ShuffleComponent.LeftX, ShuffleComponent.LeftZ, ShuffleComponent.RightZ, ShuffleComponent.RightX);
|
var inner30 = shuffle(inner30_01, inner30_01, ShuffleComponent.LeftX, ShuffleComponent.LeftZ, ShuffleComponent.RightZ, ShuffleComponent.RightX);
|
||||||
var inner01 = shuffle(inner30_01, inner30_01, ShuffleComponent.LeftY, ShuffleComponent.LeftW, ShuffleComponent.RightW, ShuffleComponent.RightY);
|
var inner01 = shuffle(inner30_01, inner30_01, ShuffleComponent.LeftY, ShuffleComponent.LeftW, ShuffleComponent.RightW, ShuffleComponent.RightY);
|
||||||
|
|
||||||
var minors1 = r2_wzyx * inner30 - r0_wzyx * inner23 - r3_wzyx * inner02;
|
var minors1 = r2_wzyx * inner30 - r0_wzyx * inner23 - r3_wzyx * inner02;
|
||||||
res.{_matrixComponents[1]} = minors1 * rcp_denom_ppnn;
|
res.{s_matrixComponents[1]} = minors1 * rcp_denom_ppnn;
|
||||||
|
|
||||||
var minors2 = r0_wzyx * inner13 - r1_wzyx * inner30 - r3_wzyx * inner01;
|
var minors2 = r0_wzyx * inner13 - r1_wzyx * inner30 - r3_wzyx * inner01;
|
||||||
res.{_matrixComponents[2]} = minors2 * rcp_denom_ppnn;
|
res.{s_matrixComponents[2]} = minors2 * rcp_denom_ppnn;
|
||||||
|
|
||||||
var minors3 = r1_wzyx * inner02 - r0_wzyx * inner12 + r2_wzyx * inner01;
|
var minors3 = r1_wzyx * inner02 - r0_wzyx * inner12 + r2_wzyx * inner01;
|
||||||
res.{_matrixComponents[3]} = minors3 * rcp_denom_ppnn;
|
res.{s_matrixComponents[3]} = minors3 * rcp_denom_ppnn;
|
||||||
return res;
|
return res;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@@ -640,12 +704,12 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
for (var i = 0; i < typeInfo.Column; i++)
|
for (var i = 0; i < typeInfo.Column; i++)
|
||||||
{
|
{
|
||||||
sourceBuilder.Append($@"
|
sourceBuilder.Append($@"
|
||||||
var {_matrixComponents[i]} = value.{_matrixComponents[i]};");
|
var {s_matrixComponents[i]} = value.{s_matrixComponents[i]};");
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceBuilder.AppendLine();
|
sourceBuilder.AppendLine();
|
||||||
|
|
||||||
string Elem(int r, int c) => $"{_matrixComponents[c]}.{_vectorComponents[r]}";
|
string Elem(int r, int c) => $"{s_matrixComponents[c]}.{s_vectorComponents[r]}";
|
||||||
|
|
||||||
// recursive function that returns a string for determinant of submatrix defined by rowIndices and colIndices
|
// recursive function that returns a string for determinant of submatrix defined by rowIndices and colIndices
|
||||||
string DetExpr(List<int> rows, List<int> cols)
|
string DetExpr(List<int> rows, List<int> cols)
|
||||||
@@ -709,11 +773,12 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
// Matrix-Vector Multiplication: RxC * C-element vector = R-element vector
|
// Matrix-Vector Multiplication: RxC * C-element vector = R-element vector
|
||||||
var rhsVectorType = $"{typePrefix}{lhsCols}";
|
var rhsVectorType = $"{typePrefix}{lhsCols}";
|
||||||
var resultVectorType = $"{typePrefix}{lhsRows}";
|
var resultVectorType = $"{typePrefix}{lhsRows}";
|
||||||
|
|
||||||
sourceBuilder.AppendLine($@"
|
sourceBuilder.AppendLine($@"
|
||||||
{INLINE_METHOD_ATTRIBUTE}
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
public static {resultVectorType} mul({lhsType} m, {rhsVectorType} v)
|
public static {resultVectorType} mul({lhsType} m, {rhsVectorType} v)
|
||||||
{{
|
{{
|
||||||
return {string.Join(" + ", Enumerable.Range(0, lhsCols).Select(c => $"m.{_matrixComponents[c]} * v.{_vectorComponents[c]}"))};
|
return {string.Join(" + ", Enumerable.Range(0, lhsCols).Select(c => $"m.{s_matrixComponents[c]} * v.{s_vectorComponents[c]}"))};
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Vector-Matrix Multiplication: R-element vector * RxC = C-element vector
|
// Vector-Matrix Multiplication: R-element vector * RxC = C-element vector
|
||||||
@@ -723,7 +788,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
{INLINE_METHOD_ATTRIBUTE}
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
public static {resultVectorTypeT} mul({lhsVectorType} v, {lhsType} m)
|
public static {resultVectorTypeT} mul({lhsVectorType} v, {lhsType} m)
|
||||||
{{
|
{{
|
||||||
return new {resultVectorTypeT}({string.Join(", ", Enumerable.Range(0, lhsCols).Select(c => $"dot(v, m.{_matrixComponents[c]})"))});
|
return new {resultVectorTypeT}({string.Join(", ", Enumerable.Range(0, lhsCols).Select(c => $"dot(v, m.{s_matrixComponents[c]})"))});
|
||||||
}}");
|
}}");
|
||||||
|
|
||||||
// Matrix-Matrix Multiplication
|
// Matrix-Matrix Multiplication
|
||||||
@@ -743,7 +808,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
|
|||||||
{INLINE_METHOD_ATTRIBUTE}
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
public static {resultType} mul({lhsType} lhs, {rhsType} rhs)
|
public static {resultType} mul({lhsType} lhs, {rhsType} rhs)
|
||||||
{{
|
{{
|
||||||
return new {resultType}({string.Join(", ", Enumerable.Range(0, rhsCols).Select(c => $"mul(lhs, rhs.{_matrixComponents[c]})"))});
|
return new {resultType}({string.Join(", ", Enumerable.Range(0, rhsCols).Select(c => $"mul(lhs, rhs.{s_matrixComponents[c]})"))});
|
||||||
}}");
|
}}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Mathematics.CodeGen.Models
|
namespace Misaki.HighPerformance.Mathematics.CodeGen.Models
|
||||||
{
|
{
|
||||||
@@ -54,6 +55,12 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Models
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, INamedTypeSymbol[]>? ConvertableTypes
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
public string TypeName => TypeSymbol.Name;
|
public string TypeName => TypeSymbol.Name;
|
||||||
public string ComponentTypeName => ComponentTypeSymbol.Name;
|
public string ComponentTypeName => ComponentTypeSymbol.Name;
|
||||||
public string TypeFullName => TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
public string TypeFullName => TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen
|
|||||||
internal class NumericTypeGenerator : IIncrementalGenerator
|
internal class NumericTypeGenerator : IIncrementalGenerator
|
||||||
{
|
{
|
||||||
private const string _TARGET_ATTRIBUTE_NAME = "Misaki.HighPerformance.Mathematics.Attributes.NumericTypeAttribute";
|
private const string _TARGET_ATTRIBUTE_NAME = "Misaki.HighPerformance.Mathematics.Attributes.NumericTypeAttribute";
|
||||||
|
private const string _CONVERTABLE_ATTRIBUTE_NAME = "Misaki.HighPerformance.Mathematics.Attributes.NumericConvertableAttribute";
|
||||||
|
|
||||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||||
{
|
{
|
||||||
@@ -56,6 +57,9 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen
|
|||||||
var attribute = typeSymbol.GetAttributes()
|
var attribute = typeSymbol.GetAttributes()
|
||||||
.FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == _TARGET_ATTRIBUTE_NAME);
|
.FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == _TARGET_ATTRIBUTE_NAME);
|
||||||
|
|
||||||
|
var convertableAttributes = typeSymbol.GetAttributes()
|
||||||
|
.Where(a => a.AttributeClass?.ToDisplayString() == _CONVERTABLE_ATTRIBUTE_NAME);
|
||||||
|
|
||||||
if (attribute == null)
|
if (attribute == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -73,7 +77,23 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen
|
|||||||
var elementType = (INamedTypeSymbol?)attribute.ConstructorArguments[index++].Value;
|
var elementType = (INamedTypeSymbol?)attribute.ConstructorArguments[index++].Value;
|
||||||
var vectorType = (INamedTypeSymbol?)attribute.ConstructorArguments.ElementAtOrDefault(index++).Value;
|
var vectorType = (INamedTypeSymbol?)attribute.ConstructorArguments.ElementAtOrDefault(index++).Value;
|
||||||
|
|
||||||
return new NumericTypeInfo(typeSymbol, componentType, componentSize, row, column, typePrefix, arithmetic, canInverse, elementType, vectorType);
|
var info = new NumericTypeInfo(typeSymbol, componentType, componentSize, row, column, typePrefix, arithmetic, canInverse, elementType, vectorType);
|
||||||
|
|
||||||
|
if (convertableAttributes != null)
|
||||||
|
{
|
||||||
|
foreach (var convertableAttribute in convertableAttributes)
|
||||||
|
{
|
||||||
|
var template = (string)convertableAttribute.ConstructorArguments[0].Value!;
|
||||||
|
var types = convertableAttribute.ConstructorArguments[1].Values
|
||||||
|
.Select(v => (INamedTypeSymbol)v.Value!)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
info.ConvertableTypes??= new();
|
||||||
|
info.ConvertableTypes[template] = types;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,3 +7,11 @@ internal class NumericTypeAttribute : Attribute
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
|
||||||
|
internal class NumericConvertableAttribute : Attribute
|
||||||
|
{
|
||||||
|
public NumericConvertableAttribute(string template, params Type[] types)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
245
Misaki.HighPerformance.Mathematics/Geometry/AABB.cs
Normal file
245
Misaki.HighPerformance.Mathematics/Geometry/AABB.cs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.Mathematics.Geometry;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an axis-aligned bounding box (AABB) defined by minimum and maximum points in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
public struct AABB : IEquatable<AABB>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum point contained by the AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If any component of <see cref="Min"/> is greater than <see cref="Max"/> then this AABB is invalid.
|
||||||
|
/// </remarks>
|
||||||
|
/// <seealso cref="IsValid"/>
|
||||||
|
public float3 Min
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum point contained by the AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If any component of <see cref="Max"/> is less than <see cref="Min"/> then this AABB is invalid.
|
||||||
|
/// </remarks>
|
||||||
|
/// <seealso cref="IsValid"/>
|
||||||
|
public float3 Max
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs the AABB with the given minimum and maximum.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If you have a center and extents, you can call <see cref="CreateFromCenterAndExtents"/> or <see cref="CreateFromCenterAndHalfExtents"/>
|
||||||
|
/// to create the AABB.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="min">Minimum point inside AABB.</param>
|
||||||
|
/// <param name="max">Maximum point inside AABB.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public AABB(float3 min, float3 max)
|
||||||
|
{
|
||||||
|
Min = min;
|
||||||
|
Max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the AABB from a center and extents.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This function takes full extents. It is the distance between <see cref="Min"/> and <see cref="Max"/>.
|
||||||
|
/// If you have half extents, you can call <see cref="CreateFromCenterAndHalfExtents"/>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="center">Center of AABB.</param>
|
||||||
|
/// <param name="extents">Full extents of AABB.</param>
|
||||||
|
/// <returns>AABB created from inputs.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static AABB CreateFromCenterAndExtents(float3 center, float3 extents)
|
||||||
|
{
|
||||||
|
return CreateFromCenterAndHalfExtents(center, extents * 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the AABB from a center and half extents.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This function takes half extents. It is half the distance between <see cref="Min"/> and <see cref="Max"/>.
|
||||||
|
/// If you have full extents, you can call <see cref="CreateFromCenterAndExtents"/>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="center">Center of AABB.</param>
|
||||||
|
/// <param name="halfExtents">Half extents of AABB.</param>
|
||||||
|
/// <returns>AABB created from inputs.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static AABB CreateFromCenterAndHalfExtents(float3 center, float3 halfExtents)
|
||||||
|
{
|
||||||
|
return new AABB(center - halfExtents, center + halfExtents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new AABB with zero extents, centered at the origin.
|
||||||
|
/// </summary>
|
||||||
|
public static AABB Zero
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new AABB(float3.zero, float3.zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the extents of the AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Extents is the componentwise distance between min and max.
|
||||||
|
/// </remarks>
|
||||||
|
public readonly float3 Extents => Max - Min;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the half extents of the AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// HalfExtents is half of the componentwise distance between min and max. Subtracting HalfExtents from Center
|
||||||
|
/// gives Min and adding HalfExtents to Center gives Max.
|
||||||
|
/// </remarks>
|
||||||
|
public readonly float3 HalfExtents => (Max - Min) * 0.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the center of the AABB.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float3 Center => (Max + Min) * 0.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the AABB is valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// An AABB is considered valid if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns>True if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.</returns>
|
||||||
|
public readonly bool IsValid => math.dot(Min, Min) <= math.dot(Max, Max);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the surface area for this axis aligned bounding box.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float SurfaceArea
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var diff = Max - Min;
|
||||||
|
return 2 * math.dot(diff, diff.yzx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests if the input point is contained by the AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">Point to test.</param>
|
||||||
|
/// <returns>True if AABB contains the input point.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly bool Contains(float3 point) => math.dot(point, point) >= math.dot(Min, Min) && math.dot(point, point) <= math.dot(Max, Max);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests if the input AABB is contained entirely by this AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aabb">AABB to test.</param>
|
||||||
|
/// <returns>True if input AABB is contained entirely by this AABB.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly bool Contains(AABB aabb) => math.dot(Min, Min) <= math.dot(aabb.Min, aabb.Min) && math.dot(Max, Max) >= math.dot(aabb.Max, aabb.Max);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests if the input AABB overlaps this AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aabb">AABB to test.</param>
|
||||||
|
/// <returns>True if input AABB overlaps with this AABB.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly bool Overlaps(AABB aabb)
|
||||||
|
{
|
||||||
|
return math.dot(Max, Max) >= math.dot(aabb.Min, aabb.Min) && math.dot(Min, Min) <= math.dot(aabb.Max, aabb.Max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Expands the AABB by the given signed distance.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Positive distance expands the AABB while negative distance shrinks the AABB.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="signedDistance">Signed distance to expand the AABB with.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Expand(float signedDistance)
|
||||||
|
{
|
||||||
|
Min -= new float3(signedDistance);
|
||||||
|
Max += new float3(signedDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encapsulates the given AABB.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Modifies this AABB so that it contains the given AABB. If the given AABB is already contained by this AABB,
|
||||||
|
/// then this AABB doesn't change.
|
||||||
|
/// </remarks>
|
||||||
|
/// <seealso cref="Contains(Unity.Mathematics.Geometry.MinMaxAABB)"/>
|
||||||
|
/// <param name="aabb">AABB to encapsulate.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Encapsulate(AABB aabb)
|
||||||
|
{
|
||||||
|
Min = math.min(Min, aabb.Min);
|
||||||
|
Max = math.max(Max, aabb.Max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encapsulate the given point.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Modifies this AABB so that it contains the given point. If the given point is already contained by this AABB,
|
||||||
|
/// then this AABB doesn't change.
|
||||||
|
/// </remarks>
|
||||||
|
/// <seealso cref="Contains(Unity.Mathematics.float3)"/>
|
||||||
|
/// <param name="point">Point to encapsulate.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Encapsulate(float3 point)
|
||||||
|
{
|
||||||
|
Min = math.min(Min, point);
|
||||||
|
Max = math.max(Max, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly bool Equals(AABB other)
|
||||||
|
{
|
||||||
|
return Min.Equals(other.Min) && Max.Equals(other.Max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is AABB AABB)
|
||||||
|
{
|
||||||
|
return Equals(AABB);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool operator ==(AABB left, AABB right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(AABB left, AABB right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Min, Max);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("AABB({0}, {1})", Min, Max);
|
||||||
|
}
|
||||||
|
}
|
||||||
126
Misaki.HighPerformance.Mathematics/Geometry/OBB.cs
Normal file
126
Misaki.HighPerformance.Mathematics/Geometry/OBB.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
namespace Misaki.HighPerformance.Mathematics.Geometry;
|
||||||
|
|
||||||
|
public struct OBB : IEquatable<OBB>
|
||||||
|
{
|
||||||
|
public quaternion Rotation
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float3 Center
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float3 Extents
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OBB(quaternion rotation, float3 extents)
|
||||||
|
{
|
||||||
|
Rotation = rotation;
|
||||||
|
Extents = extents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Contains(float3 point)
|
||||||
|
{
|
||||||
|
var localPoint = math.mul(math.conjugate(Rotation), point - Center);
|
||||||
|
return math.all(math.abs(localPoint) <= Extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly void ProjectOntoAxis(float3 axis, out float min, out float max)
|
||||||
|
{
|
||||||
|
var localAxis = math.mul(math.conjugate(Rotation), axis);
|
||||||
|
var r = math.abs(localAxis.x) * Extents.x + math.abs(localAxis.y) * Extents.y + math.abs(localAxis.z) * Extents.z;
|
||||||
|
var centerProjection = math.dot(Center, axis);
|
||||||
|
min = centerProjection - r;
|
||||||
|
max = centerProjection + r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe readonly bool Overlaps(OBB other)
|
||||||
|
{
|
||||||
|
// Using the Separating Axis Theorem (SAT) for OBB-OBB intersection test
|
||||||
|
var axes = stackalloc float3[15];
|
||||||
|
var thisRotation = new float3x3(Rotation);
|
||||||
|
var otherRotation = new float3x3(other.Rotation);
|
||||||
|
|
||||||
|
// 3 axes from this OBB
|
||||||
|
axes[0] = thisRotation.c0;
|
||||||
|
axes[1] = thisRotation.c1;
|
||||||
|
axes[2] = thisRotation.c2;
|
||||||
|
|
||||||
|
// 3 axes from other OBB
|
||||||
|
axes[3] = otherRotation.c0;
|
||||||
|
axes[4] = otherRotation.c1;
|
||||||
|
axes[5] = otherRotation.c2;
|
||||||
|
|
||||||
|
// 9 cross products of edges
|
||||||
|
var index = 6;
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < 3; j++)
|
||||||
|
{
|
||||||
|
axes[index++] = math.cross(thisRotation[i], otherRotation[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var toOther = other.Center - Center;
|
||||||
|
for (var i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
var axis = axes[i];
|
||||||
|
if (math.lengthsq(axis) < 1e-6f)
|
||||||
|
{
|
||||||
|
continue; // Skip near-zero axes
|
||||||
|
}
|
||||||
|
|
||||||
|
axis = math.normalize(axis);
|
||||||
|
|
||||||
|
// Project both OBBs onto the axis
|
||||||
|
ProjectOntoAxis(axis, out var thisMin, out var thisMax);
|
||||||
|
other.ProjectOntoAxis(axis, out var otherMin, out var otherMax);
|
||||||
|
|
||||||
|
// Project the vector between centers onto the axis
|
||||||
|
var distance = math.dot(toOther, axis);
|
||||||
|
|
||||||
|
// Check for overlap
|
||||||
|
if (thisMax + distance < otherMin || otherMax + distance < thisMin)
|
||||||
|
{
|
||||||
|
return false; // Found a separating axis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true; // No separating axis found, OBBs overlap
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly AABB ToAABB()
|
||||||
|
{
|
||||||
|
var absRotation = math.abs(new float3x3(Rotation));
|
||||||
|
var worldExtents = absRotation.c0 * Extents.x + absRotation.c1 * Extents.y + absRotation.c2 * Extents.z;
|
||||||
|
return new AABB(Center - worldExtents, Center + worldExtents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(OBB other)
|
||||||
|
{
|
||||||
|
return Rotation.Equals(other.Rotation) && Center.Equals(other.Center) && Extents.Equals(other.Extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is OBB obb && Equals(obb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Rotation, Center, Extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(OBB left, OBB right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(OBB left, OBB right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
}
|
||||||
228
Misaki.HighPerformance.Mathematics/Geometry/Plane.cs
Normal file
228
Misaki.HighPerformance.Mathematics/Geometry/Plane.cs
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.Mathematics.Geometry;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A plane represented by a normal vector and a distance along the normal from the origin.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A plane splits the 3D space in half. The normal vector points to the positive half and the other half is
|
||||||
|
/// considered negative.
|
||||||
|
/// </remarks>
|
||||||
|
public struct Plane
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A plane in the form Ax + By + Cz + Dw = 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Stores the plane coefficients A, B, C, D where (A, B, C) is a unit normal vector and D is the distance
|
||||||
|
/// from the origin. A plane stored with a unit normal vector is called a normalized plane.
|
||||||
|
/// </remarks>
|
||||||
|
public float4 normalAndDistance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a Plane from arbitrary coefficients A, B, C, D of the plane equation Ax + By + Cz + Dw = 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The constructed plane will be the normalized form of the plane specified by the given coefficients.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="coefficientA">Coefficient A from plane equation.</param>
|
||||||
|
/// <param name="coefficientB">Coefficient B from plane equation.</param>
|
||||||
|
/// <param name="coefficientC">Coefficient C from plane equation.</param>
|
||||||
|
/// <param name="coefficientD">Coefficient D from plane equation.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Plane(float coefficientA, float coefficientB, float coefficientC, float coefficientD)
|
||||||
|
{
|
||||||
|
normalAndDistance = Normalize(new float4(coefficientA, coefficientB, coefficientC, coefficientD));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a plane with a normal vector and distance from the origin.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The constructed plane will be the normalized form of the plane specified by the inputs.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="normal">A non-zero vector that is perpendicular to the plane. It may be any length.</param>
|
||||||
|
/// <param name="distance">Distance from the origin along the normal. A negative value moves the plane in the
|
||||||
|
/// same direction as the normal while a positive value moves it in the opposite direction.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Plane(float3 normal, float distance)
|
||||||
|
{
|
||||||
|
normalAndDistance = Normalize(new float4(normal, distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a plane with a normal vector and a point that lies in the plane.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The constructed plane will be the normalized form of the plane specified by the inputs.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="normal">A non-zero vector that is perpendicular to the plane. It may be any length.</param>
|
||||||
|
/// <param name="pointInPlane">A point that lies in the plane.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Plane(float3 normal, float3 pointInPlane)
|
||||||
|
: this(normal, -math.dot(normal, pointInPlane))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a plane with two vectors and a point that all lie in the plane.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The constructed plane will be the normalized form of the plane specified by the inputs.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="vector1InPlane">A non-zero vector that lies in the plane. It may be any length.</param>
|
||||||
|
/// <param name="vector2InPlane">A non-zero vector that lies in the plane. It may be any length and must not be a scalar multiple of <paramref name="vector1InPlane"/>.</param>
|
||||||
|
/// <param name="pointInPlane">A point that lies in the plane.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Plane(float3 vector1InPlane, float3 vector2InPlane, float3 pointInPlane)
|
||||||
|
: this(math.cross(vector1InPlane, vector2InPlane), pointInPlane)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a normalized Plane directly without normalization cost.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors
|
||||||
|
/// by calling this function.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="unitNormal">A non-zero vector that is perpendicular to the plane. It must be unit length.</param>
|
||||||
|
/// <param name="distance">Distance from the origin along the normal. A negative value moves the plane in the
|
||||||
|
/// same direction as the normal while a positive value moves it in the opposite direction.</param>
|
||||||
|
/// <returns>Normalized Plane constructed from given inputs.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Plane CreateFromUnitNormalAndDistance(float3 unitNormal, float distance)
|
||||||
|
{
|
||||||
|
return new Plane { normalAndDistance = new float4(unitNormal, distance) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a normalized Plane without normalization cost.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors
|
||||||
|
/// by calling this function.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="unitNormal">A non-zero vector that is perpendicular to the plane. It must be unit length.</param>
|
||||||
|
/// <param name="pointInPlane">A point that lies in the plane.</param>
|
||||||
|
/// <returns>Normalized Plane constructed from given inputs.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Plane CreateFromUnitNormalAndPointInPlane(float3 unitNormal, float3 pointInPlane)
|
||||||
|
{
|
||||||
|
return new Plane { normalAndDistance = new float4(unitNormal, -math.dot(unitNormal, pointInPlane)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get/set the normal vector of the plane.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but
|
||||||
|
/// (A, B, C) is not unit length, then you must normalize the plane by calling <see cref="Normalize(Plane)"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public float3 Normal
|
||||||
|
{
|
||||||
|
get => normalAndDistance.xyz;
|
||||||
|
set => normalAndDistance.xyz = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get/set the distance of the plane from the origin. May be a negative value.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but
|
||||||
|
/// (A, B, C) is not unit length, then you must normalize the plane by calling <see cref="Normalize(Plane)"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public float Distance
|
||||||
|
{
|
||||||
|
get => normalAndDistance.w;
|
||||||
|
set => normalAndDistance.w = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalizes the given Plane.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plane">Plane to normalize.</param>
|
||||||
|
/// <returns>Normalized Plane.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Plane Normalize(Plane plane)
|
||||||
|
{
|
||||||
|
return new Plane { normalAndDistance = Normalize(plane.normalAndDistance) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalizes the plane represented by the given plane coefficients.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The plane coefficients are A, B, C, D and stored in that order in the <see cref="float4"/>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="planeCoefficients">Plane coefficients A, B, C, D stored in x, y, z, w (respectively).</param>
|
||||||
|
/// <returns>Normalized plane coefficients.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static float4 Normalize(float4 planeCoefficients)
|
||||||
|
{
|
||||||
|
float recipLength = math.rsqrt(math.lengthsq(planeCoefficients.xyz));
|
||||||
|
return new Plane { normalAndDistance = planeCoefficients * recipLength };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the signed distance from the point to the plane.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Plane must be normalized prior to calling this function. Distance is positive if point is on side of the
|
||||||
|
/// plane the normal points to, negative if on the opposite side and zero if the point lies in the plane.
|
||||||
|
/// Avoid comparing equality with 0.0f when testing if a point lies exactly in the plane and use an approximate
|
||||||
|
/// comparison instead.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="point">Point to find the signed distance with.</param>
|
||||||
|
/// <returns>Signed distance of the point from the plane.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float SignedDistanceToPoint(float3 point)
|
||||||
|
{
|
||||||
|
CheckPlaneIsNormalized();
|
||||||
|
return math.dot(normalAndDistance, new float4(point, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Projects the given point onto the plane.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Plane must be normalized prior to calling this function. The result is the position closest to the point
|
||||||
|
/// that still lies in the plane.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="point">Point to project onto the plane.</param>
|
||||||
|
/// <returns>Projected point that's inside the plane.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float3 Projection(float3 point)
|
||||||
|
{
|
||||||
|
CheckPlaneIsNormalized();
|
||||||
|
return point - Normal * SignedDistanceToPoint(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flips the plane so the normal points in the opposite direction.
|
||||||
|
/// </summary>
|
||||||
|
public Plane Flipped => new Plane { normalAndDistance = -normalAndDistance };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly converts a <see cref="Plane"/> to <see cref="float4"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plane">Plane to convert.</param>
|
||||||
|
/// <returns>A <see cref="float4"/> representing the plane.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator float4(Plane plane) => plane.normalAndDistance;
|
||||||
|
|
||||||
|
[Conditional("ENABLE_COLLECTIONS_CHECKS")]
|
||||||
|
void CheckPlaneIsNormalized()
|
||||||
|
{
|
||||||
|
float ll = math.lengthsq(Normal.xyz);
|
||||||
|
const float lowerBound = 0.999f * 0.999f;
|
||||||
|
const float upperBound = 1.001f * 1.001f;
|
||||||
|
|
||||||
|
if (ll < lowerBound || ll > upperBound)
|
||||||
|
{
|
||||||
|
throw new System.ArgumentException("Plane must be normalized. Call Plane.Normalize() to normalize plane.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
130
Misaki.HighPerformance.Mathematics/Geometry/SphereBounds.cs
Normal file
130
Misaki.HighPerformance.Mathematics/Geometry/SphereBounds.cs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.Mathematics.Geometry;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a three-dimensional sphere defined by a center point and a radius, typically used for spatial bounding or
|
||||||
|
/// intersection tests.
|
||||||
|
/// </summary>
|
||||||
|
public struct SphereBounds : IEquatable<SphereBounds>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the coordinates of the center point in three-dimensional space.
|
||||||
|
/// </summary>
|
||||||
|
public float3 Center
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the radius of the shape.
|
||||||
|
/// </summary>
|
||||||
|
public float Radius
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SphereBounds(float3 center, float radius)
|
||||||
|
{
|
||||||
|
Center = center;
|
||||||
|
Radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified point is contained within the sphere.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point to test for inclusion within the sphere.</param>
|
||||||
|
/// <returns>true if the point lies inside or on the boundary of the sphere; otherwise, false.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly bool Contains(float3 point)
|
||||||
|
{
|
||||||
|
var toPoint = point - Center;
|
||||||
|
return math.lengthsq(toPoint) <= Radius * Radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether this sphere intersects with the specified sphere.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The sphere to test for intersection with this sphere.</param>
|
||||||
|
/// <returns>true if the spheres intersect or touch; otherwise, false.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly bool Overlaps(SphereBounds other)
|
||||||
|
{
|
||||||
|
var toOther = other.Center - Center;
|
||||||
|
var radiusSum = Radius + other.Radius;
|
||||||
|
return math.lengthsq(toOther) <= radiusSum * radiusSum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly float3 ClosestPoint(float3 point)
|
||||||
|
{
|
||||||
|
var toPoint = point - Center;
|
||||||
|
var toPointLength = math.length(toPoint);
|
||||||
|
if (toPointLength <= Radius || toPointLength == 0.0f)
|
||||||
|
{
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Center + toPoint / toPointLength * Radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Encapsulate(SphereBounds other)
|
||||||
|
{
|
||||||
|
var toOther = other.Center - Center;
|
||||||
|
var toOtherLength = math.length(toOther);
|
||||||
|
|
||||||
|
if (toOtherLength + other.Radius > Radius)
|
||||||
|
{
|
||||||
|
var newRadius = (Radius + toOtherLength + other.Radius) * 0.5f;
|
||||||
|
var k = (newRadius - Radius) / toOtherLength;
|
||||||
|
Center += toOther * k;
|
||||||
|
Radius = newRadius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Encapsulate(float3 point)
|
||||||
|
{
|
||||||
|
var toPoint = point - Center;
|
||||||
|
var toPointLength = math.length(toPoint);
|
||||||
|
|
||||||
|
if (toPointLength > Radius)
|
||||||
|
{
|
||||||
|
var newRadius = (Radius + toPointLength) * 0.5f;
|
||||||
|
var k = (newRadius - Radius) / toPointLength;
|
||||||
|
Center += toPoint * k;
|
||||||
|
Radius = newRadius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override string ToString()
|
||||||
|
{
|
||||||
|
return $"Center: {Center}, Radius: {Radius}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Center, Radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is SphereBounds bound && Equals(bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(SphereBounds other)
|
||||||
|
{
|
||||||
|
return Center.Equals(other.Center) && Radius.Equals(other.Radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(SphereBounds left, SphereBounds right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(SphereBounds left, SphereBounds right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
<Title>$(AssemblyName)</Title>
|
<Title>$(AssemblyName)</Title>
|
||||||
<Authors>Misaki</Authors>
|
<Authors>Misaki</Authors>
|
||||||
<IsPackable>True</IsPackable>
|
<IsPackable>True</IsPackable>
|
||||||
|
<Version>1.1.0</Version>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Misaki.HighPerformance.Mathematics;
|
namespace Misaki.HighPerformance.Mathematics;
|
||||||
|
|
||||||
[NumericType(typeof(bool), sizeof(bool), 2, 1, "global::Misaki.HighPerformance.Mathematics.bool", false, vectorType: typeof(byte))]
|
[NumericType(typeof(bool), sizeof(bool), 2, 1, "global::Misaki.HighPerformance.Mathematics.bool", false, vectorType: typeof(byte))]
|
||||||
|
[NumericConvertable("{v}.{c} != 0", typeof(int2), typeof(uint2), typeof(float2), typeof(double2))]
|
||||||
public partial struct bool2
|
public partial struct bool2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace Misaki.HighPerformance.Mathematics;
|
namespace Misaki.HighPerformance.Mathematics;
|
||||||
|
|
||||||
[NumericType(typeof(double), sizeof(double), 2, 1, "global::Misaki.HighPerformance.Mathematics.double")]
|
[NumericType(typeof(double), sizeof(double), 2, 1, "global::Misaki.HighPerformance.Mathematics.double")]
|
||||||
|
[NumericConvertable("(double){v}.{c}", typeof(int2), typeof(uint2), typeof(float2))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1.0 : 0.0", typeof(bool2))]
|
||||||
public partial struct double2
|
public partial struct double2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -23,6 +25,8 @@ public partial struct double2x4
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(double), sizeof(double), 3, 1, "global::Misaki.HighPerformance.Mathematics.double")]
|
[NumericType(typeof(double), sizeof(double), 3, 1, "global::Misaki.HighPerformance.Mathematics.double")]
|
||||||
|
[NumericConvertable("(double){v}.{c}", typeof(int3), typeof(uint3), typeof(float3))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1.0 : 0.0", typeof(bool3))]
|
||||||
public partial struct double3
|
public partial struct double3
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -43,6 +47,8 @@ public partial struct double3x4
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(double), sizeof(double), 4, 1, "global::Misaki.HighPerformance.Mathematics.double")]
|
[NumericType(typeof(double), sizeof(double), 4, 1, "global::Misaki.HighPerformance.Mathematics.double")]
|
||||||
|
[NumericConvertable("(double){v}.{c}", typeof(int4), typeof(uint4), typeof(float4))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1.0 : 0.0", typeof(bool4))]
|
||||||
public partial struct double4
|
public partial struct double4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,36 +3,45 @@
|
|||||||
namespace Misaki.HighPerformance.Mathematics;
|
namespace Misaki.HighPerformance.Mathematics;
|
||||||
|
|
||||||
[NumericType(typeof(float), sizeof(float), 2, 1, "global::Misaki.HighPerformance.Mathematics.float")]
|
[NumericType(typeof(float), sizeof(float), 2, 1, "global::Misaki.HighPerformance.Mathematics.float")]
|
||||||
|
[NumericConvertable("(float){v}.{c}", typeof(int2), typeof(uint2), typeof(double2))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1.0f : 0.0f", typeof(bool2))]
|
||||||
public partial struct float2
|
public partial struct float2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float2), sizeof(float), 2, 2, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float2), sizeof(float), 2, 2, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int2x2), typeof(uint2x2), typeof(double2x2))]
|
||||||
public partial struct float2x2
|
public partial struct float2x2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float2), sizeof(float), 2, 3, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float2), sizeof(float), 2, 3, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int2x3), typeof(uint2x3), typeof(double2x3))]
|
||||||
public partial struct float2x3
|
public partial struct float2x3
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float2), sizeof(float), 2, 4, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float2), sizeof(float), 2, 4, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int2x4), typeof(uint2x4), typeof(double2x4))]
|
||||||
public partial struct float2x4
|
public partial struct float2x4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float), sizeof(float), 3, 1, "global::Misaki.HighPerformance.Mathematics.float")]
|
[NumericType(typeof(float), sizeof(float), 3, 1, "global::Misaki.HighPerformance.Mathematics.float")]
|
||||||
|
[NumericConvertable("(float){v}.{c}", typeof(int3), typeof(uint3), typeof(double3))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1.0f : 0.0f", typeof(bool3))]
|
||||||
public partial struct float3
|
public partial struct float3
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float3), sizeof(float), 3, 2, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float3), sizeof(float), 3, 2, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int3x2), typeof(uint3x2), typeof(double3x2))]
|
||||||
public partial struct float3x2
|
public partial struct float3x2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float3), sizeof(float), 3, 3, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float3), sizeof(float), 3, 3, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int3x3), typeof(uint3x3), typeof(double3x3))]
|
||||||
public partial struct float3x3
|
public partial struct float3x3
|
||||||
{
|
{
|
||||||
public float3x3(quaternion q)
|
public float3x3(quaternion q)
|
||||||
@@ -50,26 +59,32 @@ public partial struct float3x3
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float3), sizeof(float), 3, 4, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float3), sizeof(float), 3, 4, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int3x4), typeof(uint3x4), typeof(double3x4))]
|
||||||
public partial struct float3x4
|
public partial struct float3x4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float), sizeof(float), 4, 1, "global::Misaki.HighPerformance.Mathematics.float")]
|
[NumericType(typeof(float), sizeof(float), 4, 1, "global::Misaki.HighPerformance.Mathematics.float")]
|
||||||
|
[NumericConvertable("(float){v}.{c}", typeof(int4), typeof(uint4), typeof(double4))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1.0f : 0.0f", typeof(bool4))]
|
||||||
public partial struct float4
|
public partial struct float4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float4), sizeof(float), 4, 2, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float4), sizeof(float), 4, 2, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int4x2), typeof(uint4x2), typeof(double4x2))]
|
||||||
public partial struct float4x2
|
public partial struct float4x2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float4), sizeof(float), 4, 3, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float4), sizeof(float), 4, 3, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int4x3), typeof(uint4x3), typeof(double4x3))]
|
||||||
public partial struct float4x3
|
public partial struct float4x3
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(float4), sizeof(float), 4, 4, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
[NumericType(typeof(float4), sizeof(float), 4, 4, "global::Misaki.HighPerformance.Mathematics.float", elementType: typeof(float))]
|
||||||
|
[NumericConvertable("{v}.{c}", typeof(int4x4), typeof(uint4x4), typeof(double4x4))]
|
||||||
public partial struct float4x4
|
public partial struct float4x4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace Misaki.HighPerformance.Mathematics;
|
namespace Misaki.HighPerformance.Mathematics;
|
||||||
|
|
||||||
[NumericType(typeof(int), sizeof(int), 2, 1, "global::Misaki.HighPerformance.Mathematics.int")]
|
[NumericType(typeof(int), sizeof(int), 2, 1, "global::Misaki.HighPerformance.Mathematics.int")]
|
||||||
|
[NumericConvertable("(int){v}.{c}", typeof(uint2), typeof(float2), typeof(double2))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1 : 0", typeof(bool2))]
|
||||||
public partial struct int2
|
public partial struct int2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -23,6 +25,8 @@ public partial struct int2x4
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(int), sizeof(int), 3, 1, "global::Misaki.HighPerformance.Mathematics.int")]
|
[NumericType(typeof(int), sizeof(int), 3, 1, "global::Misaki.HighPerformance.Mathematics.int")]
|
||||||
|
[NumericConvertable("(int){v}.{c}", typeof(uint3), typeof(float3), typeof(double3))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1 : 0", typeof(bool3))]
|
||||||
public partial struct int3
|
public partial struct int3
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -43,6 +47,8 @@ public partial struct int3x4
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(int), sizeof(int), 4, 1, "global::Misaki.HighPerformance.Mathematics.int")]
|
[NumericType(typeof(int), sizeof(int), 4, 1, "global::Misaki.HighPerformance.Mathematics.int")]
|
||||||
|
[NumericConvertable("(int){v}.{c}", typeof(uint4), typeof(float4), typeof(double4))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1 : 0", typeof(bool4))]
|
||||||
public partial struct int4
|
public partial struct int4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -106,7 +106,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static quaternion AxisAngle(float3 axis, float angle)
|
public static quaternion AxisAngle(float3 axis, float angle)
|
||||||
{
|
{
|
||||||
sincos(0.5f * angle, out var sina, out var cosa);
|
var (sina, cosa) = sincos(0.5f * angle);
|
||||||
return new quaternion(new float4(axis * sina, cosa));
|
return new quaternion(new float4(axis * sina, cosa));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,8 +120,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
public static quaternion EulerXYZ(float3 xyz)
|
public static quaternion EulerXYZ(float3 xyz)
|
||||||
{
|
{
|
||||||
// return mul(rotateZ(xyz.z), mul(rotateY(xyz.y), rotateX(xyz.x)));
|
// return mul(rotateZ(xyz.z), mul(rotateY(xyz.y), rotateX(xyz.x)));
|
||||||
float3 s, c;
|
var (s, c) = sincos(0.5f * xyz);
|
||||||
sincos(0.5f * xyz, out s, out c);
|
|
||||||
return new quaternion(
|
return new quaternion(
|
||||||
// s.x * c.y * c.z - s.y * s.z * c.x,
|
// s.x * c.y * c.z - s.y * s.z * c.x,
|
||||||
// s.y * c.x * c.z + s.x * s.z * c.y,
|
// s.y * c.x * c.z + s.x * s.z * c.y,
|
||||||
@@ -140,8 +139,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
public static quaternion EulerXZY(float3 xyz)
|
public static quaternion EulerXZY(float3 xyz)
|
||||||
{
|
{
|
||||||
// return mul(rotateY(xyz.y), mul(rotateZ(xyz.z), rotateX(xyz.x)));
|
// return mul(rotateY(xyz.y), mul(rotateZ(xyz.z), rotateX(xyz.x)));
|
||||||
float3 s, c;
|
var (s, c) = sincos(0.5f * xyz);
|
||||||
sincos(0.5f * xyz, out s, out c);
|
|
||||||
return new quaternion(
|
return new quaternion(
|
||||||
// s.x * c.y * c.z + s.y * s.z * c.x,
|
// s.x * c.y * c.z + s.y * s.z * c.x,
|
||||||
// s.y * c.x * c.z + s.x * s.z * c.y,
|
// s.y * c.x * c.z + s.x * s.z * c.y,
|
||||||
@@ -160,8 +158,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
public static quaternion EulerYXZ(float3 xyz)
|
public static quaternion EulerYXZ(float3 xyz)
|
||||||
{
|
{
|
||||||
// return mul(rotateZ(xyz.z), mul(rotateX(xyz.x), rotateY(xyz.y)));
|
// return mul(rotateZ(xyz.z), mul(rotateX(xyz.x), rotateY(xyz.y)));
|
||||||
float3 s, c;
|
var (s, c) = sincos(0.5f * xyz);
|
||||||
sincos(0.5f * xyz, out s, out c);
|
|
||||||
return new quaternion(
|
return new quaternion(
|
||||||
// s.x * c.y * c.z - s.y * s.z * c.x,
|
// s.x * c.y * c.z - s.y * s.z * c.x,
|
||||||
// s.y * c.x * c.z + s.x * s.z * c.y,
|
// s.y * c.x * c.z + s.x * s.z * c.y,
|
||||||
@@ -180,8 +177,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
public static quaternion EulerYZX(float3 xyz)
|
public static quaternion EulerYZX(float3 xyz)
|
||||||
{
|
{
|
||||||
// return mul(rotateX(xyz.x), mul(rotateZ(xyz.z), rotateY(xyz.y)));
|
// return mul(rotateX(xyz.x), mul(rotateZ(xyz.z), rotateY(xyz.y)));
|
||||||
float3 s, c;
|
var (s, c) = sincos(0.5f * xyz);
|
||||||
sincos(0.5f * xyz, out s, out c);
|
|
||||||
return new quaternion(
|
return new quaternion(
|
||||||
// s.x * c.y * c.z - s.y * s.z * c.x,
|
// s.x * c.y * c.z - s.y * s.z * c.x,
|
||||||
// s.y * c.x * c.z - s.x * s.z * c.y,
|
// s.y * c.x * c.z - s.x * s.z * c.y,
|
||||||
@@ -201,8 +197,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
public static quaternion EulerZXY(float3 xyz)
|
public static quaternion EulerZXY(float3 xyz)
|
||||||
{
|
{
|
||||||
// return mul(rotateY(xyz.y), mul(rotateX(xyz.x), rotateZ(xyz.z)));
|
// return mul(rotateY(xyz.y), mul(rotateX(xyz.x), rotateZ(xyz.z)));
|
||||||
float3 s, c;
|
var (s, c) = sincos(0.5f * xyz);
|
||||||
sincos(0.5f * xyz, out s, out c);
|
|
||||||
return new quaternion(
|
return new quaternion(
|
||||||
// s.x * c.y * c.z + s.y * s.z * c.x,
|
// s.x * c.y * c.z + s.y * s.z * c.x,
|
||||||
// s.y * c.x * c.z - s.x * s.z * c.y,
|
// s.y * c.x * c.z - s.x * s.z * c.y,
|
||||||
@@ -221,8 +216,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
public static quaternion EulerZYX(float3 xyz)
|
public static quaternion EulerZYX(float3 xyz)
|
||||||
{
|
{
|
||||||
// return mul(rotateX(xyz.x), mul(rotateY(xyz.y), rotateZ(xyz.z)));
|
// return mul(rotateX(xyz.x), mul(rotateY(xyz.y), rotateZ(xyz.z)));
|
||||||
float3 s, c;
|
var (s, c) = sincos(0.5f * xyz);
|
||||||
sincos(0.5f * xyz, out s, out c);
|
|
||||||
return new quaternion(
|
return new quaternion(
|
||||||
// s.x * c.y * c.z + s.y * s.z * c.x,
|
// s.x * c.y * c.z + s.y * s.z * c.x,
|
||||||
// s.y * c.x * c.z - s.x * s.z * c.y,
|
// s.y * c.x * c.z - s.x * s.z * c.y,
|
||||||
@@ -370,8 +364,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static quaternion RotateX(float angle)
|
public static quaternion RotateX(float angle)
|
||||||
{
|
{
|
||||||
float sina, cosa;
|
var (sina, cosa) = sincos(0.5f * angle);
|
||||||
sincos(0.5f * angle, out sina, out cosa);
|
|
||||||
return new quaternion(sina, 0.0f, 0.0f, cosa);
|
return new quaternion(sina, 0.0f, 0.0f, cosa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,8 +374,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static quaternion RotateY(float angle)
|
public static quaternion RotateY(float angle)
|
||||||
{
|
{
|
||||||
float sina, cosa;
|
var (sina, cosa) = sincos(0.5f * angle);
|
||||||
sincos(0.5f * angle, out sina, out cosa);
|
|
||||||
return new quaternion(0.0f, sina, 0.0f, cosa);
|
return new quaternion(0.0f, sina, 0.0f, cosa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,8 +384,7 @@ public partial struct quaternion : IEquatable<quaternion>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static quaternion RotateZ(float angle)
|
public static quaternion RotateZ(float angle)
|
||||||
{
|
{
|
||||||
float sina, cosa;
|
var (sina, cosa) = sincos(0.5f * angle);
|
||||||
sincos(0.5f * angle, out sina, out cosa);
|
|
||||||
return new quaternion(0.0f, 0.0f, sina, cosa);
|
return new quaternion(0.0f, 0.0f, sina, cosa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,8 +585,7 @@ public static partial class math
|
|||||||
{
|
{
|
||||||
var v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
|
var v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
|
||||||
var v_len = rcp(v_rcp_len);
|
var v_len = rcp(v_rcp_len);
|
||||||
float sin_v_len, cos_v_len;
|
var (sin_v_len, cos_v_len) = sincos(v_len);
|
||||||
sincos(v_len, out sin_v_len, out cos_v_len);
|
|
||||||
return quaternion(new float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len));
|
return quaternion(new float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,8 +597,7 @@ public static partial class math
|
|||||||
{
|
{
|
||||||
var v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
|
var v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
|
||||||
var v_len = rcp(v_rcp_len);
|
var v_len = rcp(v_rcp_len);
|
||||||
float sin_v_len, cos_v_len;
|
var (sin_v_len, cos_v_len) = sincos(v_len);
|
||||||
sincos(v_len, out sin_v_len, out cos_v_len);
|
|
||||||
return quaternion(new float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len) * exp(q.value.w));
|
return quaternion(new float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len) * exp(q.value.w));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +741,7 @@ public static partial class math
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
static float3x3 adj(float3x3 m, out float det)
|
private static float3x3 adj(float3x3 m, out float det)
|
||||||
{
|
{
|
||||||
float3x3 adjT;
|
float3x3 adjT;
|
||||||
adjT.c0 = cross(m.c1, m.c2);
|
adjT.c0 = cross(m.c1, m.c2);
|
||||||
@@ -764,7 +753,7 @@ public static partial class math
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
static bool adjInverse(float3x3 m, out float3x3 i, float epsilon = svd.EPSILON_NORMAL)
|
private static bool adjInverse(float3x3 m, out float3x3 i, float epsilon = svd.EPSILON_NORMAL)
|
||||||
{
|
{
|
||||||
i = adj(m, out var det);
|
i = adj(m, out var det);
|
||||||
var c = abs(det) > epsilon;
|
var c = abs(det) > epsilon;
|
||||||
|
|||||||
766
Misaki.HighPerformance.Mathematics/random.cs
Normal file
766
Misaki.HighPerformance.Mathematics/random.cs
Normal file
@@ -0,0 +1,766 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using static Misaki.HighPerformance.Mathematics.math;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.Mathematics;
|
||||||
|
|
||||||
|
#pragma warning disable CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language.
|
||||||
|
public struct random
|
||||||
|
#pragma warning restore CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language.
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The random number generator state. It should not be zero.
|
||||||
|
/// </summary>
|
||||||
|
public uint state;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a Random instance with a given seed value. The seed must be non-zero.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">The seed to initialize with.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public random(uint seed)
|
||||||
|
{
|
||||||
|
state = seed;
|
||||||
|
CheckInitState();
|
||||||
|
NextState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <see cref="random"/> instance with an index that gets hashed. The index must not be uint.MaxValue.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Use this function when you expect to create several Random instances in a loop.
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// for (uint i = 0; i < 4096; ++i)
|
||||||
|
/// {
|
||||||
|
/// Random rand = Random.CreateFromIndex(i);
|
||||||
|
///
|
||||||
|
/// // Random numbers drawn from loop iteration j will be very different
|
||||||
|
/// // from every other loop iteration k.
|
||||||
|
/// rand.NextUInt();
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// <param name="index">An index that will be hashed for Random creation. Must not be uint.MaxValue.</param>
|
||||||
|
/// <returns><see cref="random"/> created from an index.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static random CreateFromIndex(uint index)
|
||||||
|
{
|
||||||
|
CheckIndexForHash(index);
|
||||||
|
|
||||||
|
// Wang hash will hash 61 to zero but we want uint.MaxValue to hash to zero. To make this happen
|
||||||
|
// we must offset by 62.
|
||||||
|
return new random(WangHash(index + 62u));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static uint WangHash(uint n)
|
||||||
|
{
|
||||||
|
// https://gist.github.com/badboy/6267743#hash-function-construction-principles
|
||||||
|
// Wang hash: this has the property that none of the outputs will
|
||||||
|
// collide with each other, which is important for the purposes of
|
||||||
|
// seeding a random number generator. This was verified empirically
|
||||||
|
// by checking all 2^32 uints.
|
||||||
|
n = (n ^ 61u) ^ (n >> 16);
|
||||||
|
n *= 9u;
|
||||||
|
n = n ^ (n >> 4);
|
||||||
|
n *= 0x27d4eb2du;
|
||||||
|
n = n ^ (n >> 15);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialized the state of the Random instance with a given seed value. The seed must be non-zero.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">The seed to initialize with.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void InitState(uint seed = 0x6E624EB7u)
|
||||||
|
{
|
||||||
|
state = seed;
|
||||||
|
NextState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random bool value.</summary>
|
||||||
|
/// <returns>A uniformly random boolean value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool NextBool()
|
||||||
|
{
|
||||||
|
return (NextState() & 1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random bool2 value.</summary>
|
||||||
|
/// <returns>A uniformly random bool2 value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool2 NextBool2()
|
||||||
|
{
|
||||||
|
var v = NextState();
|
||||||
|
return (uint2(v) & uint2(1, 2)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random bool3 value.</summary>
|
||||||
|
/// <returns>A uniformly random bool3 value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool3 NextBool3()
|
||||||
|
{
|
||||||
|
var v = NextState();
|
||||||
|
return (uint3(v) & uint3(1, 2, 4)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random bool4 value.</summary>
|
||||||
|
/// <returns>A uniformly random bool4 value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool4 NextBool4()
|
||||||
|
{
|
||||||
|
var v = NextState();
|
||||||
|
return (uint4(v) & uint4(1, 2, 4, 8)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int value in the interval [-2147483647, 2147483647].</summary>
|
||||||
|
/// <returns>A uniformly random integer value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int NextInt()
|
||||||
|
{
|
||||||
|
return (int)NextState() ^ -2147483648;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int2 value with all components in the interval [-2147483647, 2147483647].</summary>
|
||||||
|
/// <returns>A uniformly random int2 value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int2 NextInt2()
|
||||||
|
{
|
||||||
|
return int2((int)NextState(), (int)NextState()) ^ -2147483648;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int3 value with all components in the interval [-2147483647, 2147483647].</summary>
|
||||||
|
/// <returns>A uniformly random int3 value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int3 NextInt3()
|
||||||
|
{
|
||||||
|
return int3((int)NextState(), (int)NextState(), (int)NextState()) ^ -2147483648;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int4 value with all components in the interval [-2147483647, 2147483647].</summary>
|
||||||
|
/// <returns>A uniformly random int4 value.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int4 NextInt4()
|
||||||
|
{
|
||||||
|
return int4((int)NextState(), (int)NextState(), (int)NextState(), (int)NextState()) ^ -2147483648;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int value in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random int value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int NextInt(int max)
|
||||||
|
{
|
||||||
|
CheckNextIntMax(max);
|
||||||
|
return (int)((NextState() * (ulong)max) >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int2 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random int2 value with all components in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int2 NextInt2(int2 max)
|
||||||
|
{
|
||||||
|
CheckNextIntMax(max.x);
|
||||||
|
CheckNextIntMax(max.y);
|
||||||
|
return int2((int)(NextState() * (ulong)max.x >> 32),
|
||||||
|
(int)(NextState() * (ulong)max.y >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int3 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random int3 value with all components in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int3 NextInt3(int3 max)
|
||||||
|
{
|
||||||
|
CheckNextIntMax(max.x);
|
||||||
|
CheckNextIntMax(max.y);
|
||||||
|
CheckNextIntMax(max.z);
|
||||||
|
return int3((int)(NextState() * (ulong)max.x >> 32),
|
||||||
|
(int)(NextState() * (ulong)max.y >> 32),
|
||||||
|
(int)(NextState() * (ulong)max.z >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int4 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random int4 value with all components in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int4 NextInt4(int4 max)
|
||||||
|
{
|
||||||
|
CheckNextIntMax(max.x);
|
||||||
|
CheckNextIntMax(max.y);
|
||||||
|
CheckNextIntMax(max.z);
|
||||||
|
CheckNextIntMax(max.w);
|
||||||
|
return int4((int)(NextState() * (ulong)max.x >> 32),
|
||||||
|
(int)(NextState() * (ulong)max.y >> 32),
|
||||||
|
(int)(NextState() * (ulong)max.z >> 32),
|
||||||
|
(int)(NextState() * (ulong)max.w >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int value in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random integer between [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int NextInt(int min, int max)
|
||||||
|
{
|
||||||
|
CheckNextIntMinMax(min, max);
|
||||||
|
var range = (uint)(max - min);
|
||||||
|
return (int)(NextState() * (ulong)range >> 32) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int2 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random int2 between [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int2 NextInt2(int2 min, int2 max)
|
||||||
|
{
|
||||||
|
CheckNextIntMinMax(min.x, max.x);
|
||||||
|
CheckNextIntMinMax(min.y, max.y);
|
||||||
|
var range = (uint2)(max - min);
|
||||||
|
return int2((int)(NextState() * (ulong)range.x >> 32),
|
||||||
|
(int)(NextState() * (ulong)range.y >> 32)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int3 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random int3 between [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int3 NextInt3(int3 min, int3 max)
|
||||||
|
{
|
||||||
|
CheckNextIntMinMax(min.x, max.x);
|
||||||
|
CheckNextIntMinMax(min.y, max.y);
|
||||||
|
CheckNextIntMinMax(min.z, max.z);
|
||||||
|
var range = (uint3)(max - min);
|
||||||
|
return int3((int)(NextState() * (ulong)range.x >> 32),
|
||||||
|
(int)(NextState() * (ulong)range.y >> 32),
|
||||||
|
(int)(NextState() * (ulong)range.z >> 32)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random int4 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random int4 between [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int4 NextInt4(int4 min, int4 max)
|
||||||
|
{
|
||||||
|
CheckNextIntMinMax(min.x, max.x);
|
||||||
|
CheckNextIntMinMax(min.y, max.y);
|
||||||
|
CheckNextIntMinMax(min.z, max.z);
|
||||||
|
CheckNextIntMinMax(min.w, max.w);
|
||||||
|
var range = (uint4)(max - min);
|
||||||
|
return int4((int)(NextState() * (ulong)range.x >> 32),
|
||||||
|
(int)(NextState() * (ulong)range.y >> 32),
|
||||||
|
(int)(NextState() * (ulong)range.z >> 32),
|
||||||
|
(int)(NextState() * (ulong)range.w >> 32)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint value in the interval [0, 4294967294].</summary>
|
||||||
|
/// <returns>A uniformly random unsigned integer.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint NextUInt()
|
||||||
|
{
|
||||||
|
return NextState() - 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint2 value with all components in the interval [0, 4294967294].</summary>
|
||||||
|
/// <returns>A uniformly random uint2.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint2 NextUInt2()
|
||||||
|
{
|
||||||
|
return uint2(NextState(), NextState()) - 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint3 value with all components in the interval [0, 4294967294].</summary>
|
||||||
|
/// <returns>A uniformly random uint3.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint3 NextUInt3()
|
||||||
|
{
|
||||||
|
return uint3(NextState(), NextState(), NextState()) - 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint4 value with all components in the interval [0, 4294967294].</summary>
|
||||||
|
/// <returns>A uniformly random uint4.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint4 NextUInt4()
|
||||||
|
{
|
||||||
|
return uint4(NextState(), NextState(), NextState(), NextState()) - 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint value in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random unsigned integer in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint NextUInt(uint max)
|
||||||
|
{
|
||||||
|
return (uint)((NextState() * (ulong)max) >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint2 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random uint2 in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint2 NextUInt2(uint2 max)
|
||||||
|
{
|
||||||
|
return uint2((uint)(NextState() * (ulong)max.x >> 32),
|
||||||
|
(uint)(NextState() * (ulong)max.y >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint3 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random uint3 in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint3 NextUInt3(uint3 max)
|
||||||
|
{
|
||||||
|
return uint3((uint)(NextState() * (ulong)max.x >> 32),
|
||||||
|
(uint)(NextState() * (ulong)max.y >> 32),
|
||||||
|
(uint)(NextState() * (ulong)max.z >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint4 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random uint4 in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint4 NextUInt4(uint4 max)
|
||||||
|
{
|
||||||
|
return uint4((uint)(NextState() * (ulong)max.x >> 32),
|
||||||
|
(uint)(NextState() * (ulong)max.y >> 32),
|
||||||
|
(uint)(NextState() * (ulong)max.z >> 32),
|
||||||
|
(uint)(NextState() * (ulong)max.w >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint value in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random unsigned integer in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint NextUInt(uint min, uint max)
|
||||||
|
{
|
||||||
|
CheckNextUIntMinMax(min, max);
|
||||||
|
var range = max - min;
|
||||||
|
return (uint)(NextState() * (ulong)range >> 32) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint2 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random uint2 in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint2 NextUInt2(uint2 min, uint2 max)
|
||||||
|
{
|
||||||
|
CheckNextUIntMinMax(min.x, max.x);
|
||||||
|
CheckNextUIntMinMax(min.y, max.y);
|
||||||
|
var range = max - min;
|
||||||
|
return uint2((uint)(NextState() * (ulong)range.x >> 32),
|
||||||
|
(uint)(NextState() * (ulong)range.y >> 32)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint3 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random uint3 in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint3 NextUInt3(uint3 min, uint3 max)
|
||||||
|
{
|
||||||
|
CheckNextUIntMinMax(min.x, max.x);
|
||||||
|
CheckNextUIntMinMax(min.y, max.y);
|
||||||
|
CheckNextUIntMinMax(min.z, max.z);
|
||||||
|
var range = max - min;
|
||||||
|
return uint3((uint)(NextState() * (ulong)range.x >> 32),
|
||||||
|
(uint)(NextState() * (ulong)range.y >> 32),
|
||||||
|
(uint)(NextState() * (ulong)range.z >> 32)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random uint4 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random uint4 in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public uint4 NextUInt4(uint4 min, uint4 max)
|
||||||
|
{
|
||||||
|
CheckNextUIntMinMax(min.x, max.x);
|
||||||
|
CheckNextUIntMinMax(min.y, max.y);
|
||||||
|
CheckNextUIntMinMax(min.z, max.z);
|
||||||
|
CheckNextUIntMinMax(min.w, max.w);
|
||||||
|
var range = (uint4)(max - min);
|
||||||
|
return uint4((uint)(NextState() * (ulong)range.x >> 32),
|
||||||
|
(uint)(NextState() * (ulong)range.y >> 32),
|
||||||
|
(uint)(NextState() * (ulong)range.z >> 32),
|
||||||
|
(uint)(NextState() * (ulong)range.w >> 32)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float value in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random float value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float NextFloat()
|
||||||
|
{
|
||||||
|
return asfloat(0x3f800000 | (NextState() >> 9)) - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float2 value with all components in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random float2 value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float2 NextFloat2()
|
||||||
|
{
|
||||||
|
return asfloat(0x3f800000 | (uint2(NextState(), NextState()) >> 9)) - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float3 value with all components in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random float3 value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float3 NextFloat3()
|
||||||
|
{
|
||||||
|
return asfloat(0x3f800000 | (uint3(NextState(), NextState(), NextState()) >> 9)) - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float4 value with all components in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random float4 value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float4 NextFloat4()
|
||||||
|
{
|
||||||
|
return asfloat(0x3f800000 | (uint4(NextState(), NextState(), NextState(), NextState()) >> 9)) - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float value in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float NextFloat(float max)
|
||||||
|
{
|
||||||
|
return NextFloat() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float2 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float2 value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float2 NextFloat2(float2 max)
|
||||||
|
{
|
||||||
|
return NextFloat2() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float3 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float3 value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float3 NextFloat3(float3 max)
|
||||||
|
{
|
||||||
|
return NextFloat3() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float4 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float4 value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float4 NextFloat4(float4 max)
|
||||||
|
{
|
||||||
|
return NextFloat4() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float value in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float NextFloat(float min, float max)
|
||||||
|
{
|
||||||
|
return NextFloat() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float2 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float2 value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float2 NextFloat2(float2 min, float2 max)
|
||||||
|
{
|
||||||
|
return NextFloat2() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float3 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float3 value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float3 NextFloat3(float3 min, float3 max)
|
||||||
|
{
|
||||||
|
return NextFloat3() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random float4 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random float4 value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float4 NextFloat4(float4 min, float4 max)
|
||||||
|
{
|
||||||
|
return NextFloat4() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double value in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random double value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double NextDouble()
|
||||||
|
{
|
||||||
|
var sx = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
return asdouble(0x3ff0000000000000 | sx) - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double2 value with all components in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random double2 value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double2 NextDouble2()
|
||||||
|
{
|
||||||
|
var sx = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
var sy = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
return double2(asdouble(0x3ff0000000000000 | sx),
|
||||||
|
asdouble(0x3ff0000000000000 | sy)) - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double3 value with all components in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random double3 value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double3 NextDouble3()
|
||||||
|
{
|
||||||
|
var sx = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
var sy = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
var sz = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
return double3(asdouble(0x3ff0000000000000 | sx),
|
||||||
|
asdouble(0x3ff0000000000000 | sy),
|
||||||
|
asdouble(0x3ff0000000000000 | sz)) - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double4 value with all components in the interval [0, 1).</summary>
|
||||||
|
/// <returns>A uniformly random double4 value in the range [0, 1).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double4 NextDouble4()
|
||||||
|
{
|
||||||
|
var sx = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
var sy = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
var sz = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
var sw = ((ulong)NextState() << 20) ^ NextState();
|
||||||
|
return double4(asdouble(0x3ff0000000000000 | sx),
|
||||||
|
asdouble(0x3ff0000000000000 | sy),
|
||||||
|
asdouble(0x3ff0000000000000 | sz),
|
||||||
|
asdouble(0x3ff0000000000000 | sw)) - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double value in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double NextDouble(double max)
|
||||||
|
{
|
||||||
|
return NextDouble() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double2 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double2 value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double2 NextDouble2(double2 max)
|
||||||
|
{
|
||||||
|
return NextDouble2() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double3 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double3 value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double3 NextDouble3(double3 max)
|
||||||
|
{
|
||||||
|
return NextDouble3() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double4 value with all components in the interval [0, max).</summary>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double4 value in the range [0, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double4 NextDouble4(double4 max)
|
||||||
|
{
|
||||||
|
return NextDouble4() * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double value in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double NextDouble(double min, double max)
|
||||||
|
{
|
||||||
|
return NextDouble() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double2 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double2 value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double2 NextDouble2(double2 min, double2 max)
|
||||||
|
{
|
||||||
|
return NextDouble2() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double3 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double3 value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double3 NextDouble3(double3 min, double3 max)
|
||||||
|
{
|
||||||
|
return NextDouble3() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a uniformly random double4 value with all components in the interval [min, max).</summary>
|
||||||
|
/// <param name="min">The componentwise minimum value to generate, inclusive.</param>
|
||||||
|
/// <param name="max">The componentwise maximum value to generate, exclusive.</param>
|
||||||
|
/// <returns>A uniformly random double4 value in the range [min, max).</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double4 NextDouble4(double4 min, double4 max)
|
||||||
|
{
|
||||||
|
return NextDouble4() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a unit length float2 vector representing a uniformly random 2D direction.</summary>
|
||||||
|
/// <returns>A uniformly random unit length float2 vector.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float2 NextFloat2Direction()
|
||||||
|
{
|
||||||
|
var angle = NextFloat() * PI * 2.0f;
|
||||||
|
var (s, c) = sincos(angle);
|
||||||
|
return float2(c, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a unit length double2 vector representing a uniformly random 2D direction.</summary>
|
||||||
|
/// <returns>A uniformly random unit length double2 vector.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double2 NextDouble2Direction()
|
||||||
|
{
|
||||||
|
var angle = NextDouble() * PI_DBL * 2.0;
|
||||||
|
var (s, c) = sincos(angle);
|
||||||
|
return double2(c, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a unit length float3 vector representing a uniformly random 3D direction.</summary>
|
||||||
|
/// <returns>A uniformly random unit length float3 vector.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public float3 NextFloat3Direction()
|
||||||
|
{
|
||||||
|
var rnd = NextFloat2();
|
||||||
|
var z = rnd.x * 2.0f - 1.0f;
|
||||||
|
var r = sqrt(max(1.0f - z * z, 0.0f));
|
||||||
|
var angle = rnd.y * PI * 2.0f;
|
||||||
|
var (s, c) = sincos(angle);
|
||||||
|
return float3(c * r, s * r, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a unit length double3 vector representing a uniformly random 3D direction.</summary>
|
||||||
|
/// <returns>A uniformly random unit length double3 vector.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public double3 NextDouble3Direction()
|
||||||
|
{
|
||||||
|
var rnd = NextDouble2();
|
||||||
|
var z = rnd.x * 2.0 - 1.0;
|
||||||
|
var r = sqrt(max(1.0 - z * z, 0.0));
|
||||||
|
var angle = rnd.y * PI_DBL * 2.0;
|
||||||
|
var (s, c) = sincos(angle);
|
||||||
|
return double3(c * r, s * r, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a unit length quaternion representing a uniformly 3D rotation.</summary>
|
||||||
|
/// <returns>A uniformly random unit length quaternion.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public quaternion NextQuaternionRotation()
|
||||||
|
{
|
||||||
|
var rnd = NextFloat3(float3(2.0f * PI, 2.0f * PI, 1.0f));
|
||||||
|
var u1 = rnd.z;
|
||||||
|
var theta_rho = rnd.xy;
|
||||||
|
|
||||||
|
var i = sqrt(1.0f - u1);
|
||||||
|
var j = sqrt(u1);
|
||||||
|
|
||||||
|
var (sin_theta_rho, cos_theta_rho) = sincos(theta_rho);
|
||||||
|
|
||||||
|
var q = quaternion(i * sin_theta_rho.x, i * cos_theta_rho.x, j * sin_theta_rho.y, j * cos_theta_rho.y);
|
||||||
|
return quaternion(select(q.value, -q.value, q.value.w < 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private uint NextState()
|
||||||
|
{
|
||||||
|
CheckState();
|
||||||
|
var t = state;
|
||||||
|
state ^= state << 13;
|
||||||
|
state ^= state >> 17;
|
||||||
|
state ^= state << 5;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("ENABLE_COLLECTION_CHECKS")]
|
||||||
|
private void CheckInitState()
|
||||||
|
{
|
||||||
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
|
if (state == 0)
|
||||||
|
throw new System.ArgumentException("Seed must be non-zero");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("ENABLE_COLLECTION_CHECKS")]
|
||||||
|
private static void CheckIndexForHash(uint index)
|
||||||
|
{
|
||||||
|
if (index == uint.MaxValue)
|
||||||
|
throw new System.ArgumentException("Index must not be uint.MaxValue");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("ENABLE_COLLECTION_CHECKS")]
|
||||||
|
private void CheckState()
|
||||||
|
{
|
||||||
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
|
if(state == 0)
|
||||||
|
throw new System.ArgumentException("Invalid state 0. Random object has not been properly initialized");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("ENABLE_COLLECTION_CHECKS")]
|
||||||
|
private void CheckNextIntMax(int max)
|
||||||
|
{
|
||||||
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
|
if (max < 0)
|
||||||
|
throw new System.ArgumentException("max must be positive");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("ENABLE_COLLECTION_CHECKS")]
|
||||||
|
private void CheckNextIntMinMax(int min, int max)
|
||||||
|
{
|
||||||
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
|
if (min > max)
|
||||||
|
throw new System.ArgumentException("min must be less than or equal to max");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("ENABLE_COLLECTION_CHECKS")]
|
||||||
|
private void CheckNextUIntMinMax(uint min, uint max)
|
||||||
|
{
|
||||||
|
#if ENABLE_COLLECTION_CHECKS
|
||||||
|
if (min > max)
|
||||||
|
throw new System.ArgumentException("min must be less than or equal to max");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace Misaki.HighPerformance.Mathematics;
|
namespace Misaki.HighPerformance.Mathematics;
|
||||||
|
|
||||||
[NumericType(typeof(uint), sizeof(uint), 2, 1, "global::Misaki.HighPerformance.Mathematics.uint")]
|
[NumericType(typeof(uint), sizeof(uint), 2, 1, "global::Misaki.HighPerformance.Mathematics.uint")]
|
||||||
|
[NumericConvertable("(uint){v}.{c}", typeof(int2), typeof(float2), typeof(double2))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1u : 0u", typeof(bool2))]
|
||||||
public partial struct uint2
|
public partial struct uint2
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -23,6 +25,8 @@ public partial struct uint2x4
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(uint), sizeof(uint), 3, 1, "global::Misaki.HighPerformance.Mathematics.uint")]
|
[NumericType(typeof(uint), sizeof(uint), 3, 1, "global::Misaki.HighPerformance.Mathematics.uint")]
|
||||||
|
[NumericConvertable("(uint){v}.{c}", typeof(int3), typeof(float3), typeof(double3))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1u : 0u", typeof(bool3))]
|
||||||
public partial struct uint3
|
public partial struct uint3
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -43,6 +47,8 @@ public partial struct uint3x4
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NumericType(typeof(uint), sizeof(uint), 4, 1, "global::Misaki.HighPerformance.Mathematics.uint")]
|
[NumericType(typeof(uint), sizeof(uint), 4, 1, "global::Misaki.HighPerformance.Mathematics.uint")]
|
||||||
|
[NumericConvertable("(uint){v}.{c}", typeof(int4), typeof(float4), typeof(double4))]
|
||||||
|
[NumericConvertable("{v}.{c} ? 1u : 0u", typeof(bool4))]
|
||||||
public partial struct uint4
|
public partial struct uint4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,23 @@
|
|||||||
|
|
||||||
//Console.WriteLine($"Count should be {threadCount * 990}, actual: {map.Count}");
|
//Console.WriteLine($"Count should be {threadCount * 990}, actual: {map.Count}");
|
||||||
|
|
||||||
using Misaki.HighPerformance.Test.Benchmark;
|
//using Misaki.HighPerformance.Test.Benchmark;
|
||||||
|
|
||||||
BenchmarkDotNet.Running.BenchmarkRunner.Run<MathematicsBenchmark>();
|
//BenchmarkDotNet.Running.BenchmarkRunner.Run<MathematicsBenchmark>();
|
||||||
|
|
||||||
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
|
var scope = 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)
|
||||||
|
{
|
||||||
|
Console.WriteLine(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.Dispose();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Misaki.HighPerformance.Jobs;
|
using Misaki.HighPerformance.Jobs;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Test.UnitTest.Jobs;
|
namespace Misaki.HighPerformance.Test.UnitTest.Jobs;
|
||||||
|
|
||||||
|
|||||||
154
Misaki.HighPerformance/Collections/DynamicArray.cs
Normal file
154
Misaki.HighPerformance/Collections/DynamicArray.cs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.Collections;
|
||||||
|
|
||||||
|
public class DynamicArray<T> : IEnumerable<T>, IList<T>
|
||||||
|
{
|
||||||
|
private T[] _array;
|
||||||
|
private int _count;
|
||||||
|
|
||||||
|
public ref T this[int index] => ref _array[index];
|
||||||
|
|
||||||
|
public int Count => _count;
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
public int Capacity
|
||||||
|
{
|
||||||
|
get => _array.Length;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < _count)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), "Capacity cannot be set to a value less than Count.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != _array.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _array, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T IList<T>.this[int index]
|
||||||
|
{
|
||||||
|
get => _array[index];
|
||||||
|
set => _array[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicArray(int initialCapacity = 4)
|
||||||
|
{
|
||||||
|
if (initialCapacity < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(initialCapacity), "Initial capacity must be non-negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_array = new T[initialCapacity];
|
||||||
|
_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _count; i++)
|
||||||
|
{
|
||||||
|
yield return _array[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureCapacity(int min)
|
||||||
|
{
|
||||||
|
if (_array.Length < min)
|
||||||
|
{
|
||||||
|
int newCapacity = _array.Length == 0 ? 4 : _array.Length * 2;
|
||||||
|
if (newCapacity < min) newCapacity = min;
|
||||||
|
Array.Resize(ref _array, newCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
EnsureCapacity(_count + 1);
|
||||||
|
_array[_count++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, T item)
|
||||||
|
{
|
||||||
|
if (index < 0 || index > _count)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCapacity(_count + 1);
|
||||||
|
Array.Copy(_array, index, _array, index + 1, _count - index);
|
||||||
|
|
||||||
|
_array[index] = item;
|
||||||
|
_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Array.Clear(_array, 0, _count);
|
||||||
|
_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(T item)
|
||||||
|
{
|
||||||
|
return IndexOf(item) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(T item)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _count; i++)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(_array[i], item))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
Array.Copy(_array, 0, array, arrayIndex, _count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(T item)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _count; i++)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(_array[i], item))
|
||||||
|
{
|
||||||
|
_count--;
|
||||||
|
Array.Copy(_array, i + 1, _array, i, _count - i);
|
||||||
|
_array[_count] = default!;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= _count)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Copy(_array, index + 1, _array, index, _count - index);
|
||||||
|
|
||||||
|
_count--;
|
||||||
|
_array[_count] = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<T> AsSpan()
|
||||||
|
{
|
||||||
|
return _array.AsSpan(0, _count);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
using System.Collections;
|
||||||
using System.Collections;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
namespace Misaki.HighPerformance.Collections;
|
||||||
|
|
||||||
public class SlotMap<T> : IEnumerable<T>
|
public class SlotMap<T> : IEnumerable<T>
|
||||||
{
|
{
|
||||||
@@ -143,7 +144,26 @@ public class SlotMap<T> : IEnumerable<T>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref T GetElementAt(int slotIndex, int generation)
|
public bool TryGetElement(int slotIndex, int generation, [MaybeNullWhen(false)] out T value)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= _capacity)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
if (slot.generation != generation)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = slot.value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetElementAt(int slotIndex, int generation)
|
||||||
{
|
{
|
||||||
if (slotIndex < 0 || slotIndex >= _capacity)
|
if (slotIndex < 0 || slotIndex >= _capacity)
|
||||||
{
|
{
|
||||||
@@ -151,12 +171,48 @@ public class SlotMap<T> : IEnumerable<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
ref var slot = ref _data[slotIndex];
|
ref var slot = ref _data[slotIndex];
|
||||||
if (slot.generation != generation)
|
if (!slot.isValid || slot.generation != generation)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Slot {slotIndex} is not occupied.");
|
throw new InvalidOperationException($"Slot {slotIndex} is not occupied.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref slot.value;
|
return slot.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref T GetElementReferenceAt(int slotIndex, int generation, out bool exist)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= Volatile.Read(ref _capacity))
|
||||||
|
{
|
||||||
|
exist = false;
|
||||||
|
return ref Unsafe.NullRef<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
|
||||||
|
if (!slot.isValid || slot.generation != generation)
|
||||||
|
{
|
||||||
|
exist = false;
|
||||||
|
return ref Unsafe.NullRef<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
exist = true;
|
||||||
|
return ref slot.value!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateElement(int slotIndex, int generation, T newValue)
|
||||||
|
{
|
||||||
|
if (slotIndex < 0 || slotIndex >= Volatile.Read(ref _capacity))
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(slotIndex), "Slot index is out of range.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var slot = ref _data[slotIndex];
|
||||||
|
if (!slot.isValid || slot.generation != generation)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Slot {slotIndex} is not occupied or generation mismatch.");
|
||||||
|
}
|
||||||
|
|
||||||
|
slot.value = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance;
|
|
||||||
|
|
||||||
public static class MathUtilities
|
|
||||||
{
|
|
||||||
/// <summary>Returns the smallest power of two that is greater than or equal to the specified number.</summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static int CeilPow2(int x)
|
|
||||||
{
|
|
||||||
x -= 1;
|
|
||||||
x |= x >> 1;
|
|
||||||
x |= x >> 2;
|
|
||||||
x |= x >> 4;
|
|
||||||
x |= x >> 8;
|
|
||||||
x |= x >> 16;
|
|
||||||
return x + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user