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

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

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

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

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

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

0
.zed/settings.json Normal file
View File

View File

@@ -244,7 +244,7 @@ public unsafe sealed class JobScheduler : IDisposable
public JobHandle Schedule<T>(ref T job, int threadIndex, JobHandle dependency) public JobHandle Schedule<T>(ref T job, int threadIndex, JobHandle dependency)
where T : unmanaged, IJob where T : unmanaged, IJob
{ {
var jobData = _jobDataAllocator.Allocate(MemoryUtilities.SizeOf<T>(), MemoryUtilities.AlignOf<T>()); var jobData = _jobDataAllocator.Allocate(MemoryUtility.SizeOf<T>(), MemoryUtility.AlignOf<T>());
if (jobData == null) if (jobData == null)
{ {
return JobHandle.Invalid; return JobHandle.Invalid;
@@ -252,7 +252,7 @@ public unsafe sealed class JobScheduler : IDisposable
fixed (T* pJob = &job) fixed (T* pJob = &job)
{ {
MemoryUtilities.MemCpy(pJob, jobData, MemoryUtilities.SizeOf<T>()); MemoryUtility.MemCpy(pJob, jobData, MemoryUtility.SizeOf<T>());
} }
var jobInfo = new JobInfo var jobInfo = new JobInfo
@@ -320,7 +320,7 @@ public unsafe sealed class JobScheduler : IDisposable
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency) public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency)
where T : unmanaged, IJobParallelFor where T : unmanaged, IJobParallelFor
{ {
var jobData = _jobDataAllocator.Allocate(MemoryUtilities.SizeOf<T>(), MemoryUtilities.AlignOf<T>()); var jobData = _jobDataAllocator.Allocate(MemoryUtility.SizeOf<T>(), MemoryUtility.AlignOf<T>());
if (jobData == null) if (jobData == null)
{ {
return JobHandle.Invalid; return JobHandle.Invalid;
@@ -328,7 +328,7 @@ public unsafe sealed class JobScheduler : IDisposable
fixed (T* pJob = &job) fixed (T* pJob = &job)
{ {
MemoryUtilities.MemCpy(pJob, jobData, MemoryUtilities.SizeOf<T>()); MemoryUtility.MemCpy(pJob, jobData, MemoryUtility.SizeOf<T>());
} }
var optimalBatchSize = Math.Max(1, batchSize); var optimalBatchSize = Math.Max(1, batchSize);

View File

@@ -1,4 +1,4 @@
global using static Misaki.HighPerformance.LowLevel.Utilities.MemoryUtilities; global using static Misaki.HighPerformance.LowLevel.Utilities.MemoryUtility;
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*>;

View File

@@ -11,7 +11,7 @@ public unsafe struct Stack : IDisposable
{ {
private const nuint _DEFAULT_SIZE = 1024 * 1024; // 1MB private const nuint _DEFAULT_SIZE = 1024 * 1024; // 1MB
public readonly ref struct Scope public readonly struct Scope : IDisposable
{ {
private readonly Stack* _allocator; private readonly Stack* _allocator;
private readonly nuint _originalOffset; private readonly nuint _originalOffset;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
_index++; _index++;
if (_index < _collection->_count) if (_index < _collection->_count)
{ {
_value = UnsafeUtilities.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index); _value = UnsafeUtility.ReadArrayElement<T>(_collection->_array.GetUnsafePtr(), _index);
return true; return true;
} }
@@ -96,7 +96,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
{ {
var idx = Interlocked.Increment(ref listData->_count) - 1; var idx = Interlocked.Increment(ref listData->_count) - 1;
listData->CheckNoResizeCapacity(idx, 1); listData->CheckNoResizeCapacity(idx, 1);
UnsafeUtilities.WriteArrayElement(listData->_array.GetUnsafePtr(), idx, value); UnsafeUtility.WriteArrayElement(listData->_array.GetUnsafePtr(), idx, value);
} }
/// <summary> /// <summary>
@@ -111,7 +111,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
fixed (T* pCollection = collection) fixed (T* pCollection = collection)
{ {
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(listData->_array.GetUnsafePtr(), index), pCollection, (uint)(count * sizeof(T))); MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(listData->_array.GetUnsafePtr(), index), pCollection, (uint)(count * sizeof(T)));
} }
} }
} }
@@ -136,14 +136,14 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
get => ref _array[index]; get => ref _array[index];
} }
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this)); public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary> /// <summary>
/// Provides a parallel writer for the current list, enabling thread-safe additions to the list. /// Provides a parallel writer for the current list, enabling thread-safe additions to the list.
/// </summary> /// </summary>
/// <returns>A <see cref="ParallelWriter"/> instance that can be used to add items to the list in a thread-safe manner.</returns> /// <returns>A <see cref="ParallelWriter"/> instance that can be used to add items to the list in a thread-safe manner.</returns>
public ParallelWriter AsParallelWriter() => new((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this)); public ParallelWriter AsParallelWriter() => new((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
/// <summary> /// <summary>
/// Converts the current list to an UnsafeArray representation. /// Converts the current list to an UnsafeArray representation.
@@ -221,6 +221,11 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
} }
} }
public readonly ReadOnlyUnsafeCollection<T> AsReadOnly()
{
return new ReadOnlyUnsafeCollection<T>((T*)_array.GetUnsafePtr(), _count);
}
/// <summary> /// <summary>
/// Adds a new element to the end of the list, resizing the internal array if necessary. /// Adds a new element to the end of the list, resizing the internal array if necessary.
/// </summary> /// </summary>
@@ -232,7 +237,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
Resize(Capacity + (int)(Capacity * 0.5f)); Resize(Capacity + (int)(Capacity * 0.5f));
} }
UnsafeUtilities.WriteArrayElement(_array.GetUnsafePtr(), _count, value); UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
_count++; _count++;
} }
@@ -244,7 +249,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
{ {
CheckNoResizeCapacity(1); CheckNoResizeCapacity(1);
UnsafeUtilities.WriteArrayElement(_array.GetUnsafePtr(), _count, value); UnsafeUtility.WriteArrayElement(_array.GetUnsafePtr(), _count, value);
_count++; _count++;
} }
@@ -264,7 +269,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
fixed (T* ptr = values) fixed (T* ptr = values)
{ {
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T))); MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T)));
} }
_count += count; _count += count;
@@ -280,7 +285,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
fixed (T* pCollection = collection) fixed (T* pCollection = collection)
{ {
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), pCollection, (uint)(collection.Length * sizeof(T))); MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), pCollection, (uint)(collection.Length * sizeof(T)));
} }
_count += collection.Length; _count += collection.Length;
@@ -295,7 +300,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
{ {
CheckNoResizeCapacity(count); CheckNoResizeCapacity(count);
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T))); MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), _count), ptr, (uint)(count * sizeof(T)));
_count += count; _count += count;
} }
@@ -314,8 +319,8 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
} }
var copyFrom = Math.Min(start + length, _count); var copyFrom = Math.Min(start + length, _count);
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start), MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start),
UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom), UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom),
(uint)((_count - copyFrom) * sizeof(T)) (uint)((_count - copyFrom) * sizeof(T))
); );
_count -= length; _count -= length;
@@ -345,8 +350,8 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
} }
var copyFrom = Math.Min(_count - length, start + length); var copyFrom = Math.Min(_count - length, start + length);
MemCpy(UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start), MemCpy(UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), start),
UnsafeUtilities.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom), UnsafeUtility.ReadArrayElementUnsafe<T>(_array.GetUnsafePtr(), copyFrom),
(uint)((_count - copyFrom) * sizeof(T)) (uint)((_count - copyFrom) * sizeof(T))
); );
_count -= length; _count -= length;
@@ -390,4 +395,3 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
_count = 0; _count = 0;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,9 +21,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Collections\FixedString.tt"> <None Update="Collections\FixedText.tt">
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>FixedString.cs</LastGenOutput> <LastGenOutput>FixedText.cs</LastGenOutput>
</None> </None>
</ItemGroup> </ItemGroup>
@@ -32,10 +32,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Collections\FixedString.cs"> <Compile Update="Collections\FixedText.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>FixedString.tt</DependentUpon> <DependentUpon>FixedText.tt</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>

View File

@@ -41,7 +41,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
public KeyValuePair<TKey, TValue> GetCurrent<TValue>() public KeyValuePair<TKey, TValue> GetCurrent<TValue>()
where TValue : unmanaged where TValue : unmanaged
{ {
return new KeyValuePair<TKey, TValue>(buffer->_keys[index], UnsafeUtilities.ReadArrayElementRef<TValue>(buffer->_buffer, index)); return new KeyValuePair<TKey, TValue>(buffer->_keys[index], UnsafeUtility.ReadArrayElementRef<TValue>(buffer->_buffer, index));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -246,7 +246,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
if ((uint)entryIdx < (uint)_capacity) if ((uint)entryIdx < (uint)_capacity)
{ {
var nextPtrs = _next; var nextPtrs = _next;
while (!UnsafeUtilities.ReadArrayElement<TKey>(_keys, entryIdx).Equals(key)) while (!UnsafeUtility.ReadArrayElement<TKey>(_keys, entryIdx).Equals(key))
{ {
entryIdx = nextPtrs[entryIdx]; entryIdx = nextPtrs[entryIdx];
if ((uint)entryIdx >= (uint)_capacity) if ((uint)entryIdx >= (uint)_capacity)
@@ -292,7 +292,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
CheckIndexOutOfBounds(idx); CheckIndexOutOfBounds(idx);
UnsafeUtilities.WriteArrayElement(_keys, idx, key); UnsafeUtility.WriteArrayElement(_keys, idx, key);
var bucket = GetBucket(key); var bucket = GetBucket(key);
// Add the index to the hash-map // Add the index to the hash-map
@@ -321,7 +321,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
while (entryIdx >= 0 && entryIdx < _capacity) while (entryIdx >= 0 && entryIdx < _capacity)
{ {
if (UnsafeUtilities.ReadArrayElement<TKey>(_keys, entryIdx).Equals(key)) if (UnsafeUtility.ReadArrayElement<TKey>(_keys, entryIdx).Equals(key))
{ {
removed++; removed++;
@@ -361,7 +361,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
if (idx != -1) if (idx != -1)
{ {
item = UnsafeUtilities.ReadArrayElement<TValue>(_buffer, idx); item = UnsafeUtility.ReadArrayElement<TValue>(_buffer, idx);
return true; return true;
} }
@@ -414,7 +414,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
while (bucket != -1) while (bucket != -1)
{ {
result[count++] = UnsafeUtilities.ReadArrayElement<TKey>(_keys, bucket); result[count++] = UnsafeUtility.ReadArrayElement<TKey>(_keys, bucket);
bucket = _next[bucket]; bucket = _next[bucket];
} }
} }
@@ -433,7 +433,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
while (bucket != -1) while (bucket != -1)
{ {
result[count++] = UnsafeUtilities.ReadArrayElement<TValue>(_buffer, bucket); result[count++] = UnsafeUtility.ReadArrayElement<TValue>(_buffer, bucket);
bucket = _next[bucket]; bucket = _next[bucket];
} }
} }
@@ -452,8 +452,8 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
while (bucket != -1) while (bucket != -1)
{ {
result[count] = new(UnsafeUtilities.ReadArrayElement<TKey>(_keys, bucket), result[count] = new(UnsafeUtility.ReadArrayElement<TKey>(_keys, bucket),
UnsafeUtilities.ReadArrayElement<TValue>(_buffer, bucket)); UnsafeUtility.ReadArrayElement<TValue>(_buffer, bucket));
count++; count++;
bucket = _next[bucket]; bucket = _next[bucket];

View File

@@ -5,7 +5,7 @@ using System.Runtime.Intrinsics;
namespace Misaki.HighPerformance.LowLevel.Utilities; namespace Misaki.HighPerformance.LowLevel.Utilities;
public static unsafe partial class MemoryUtilities public static unsafe partial class MemoryUtility
{ {
[DoesNotReturn] [DoesNotReturn]
private static void ThrowMustBeNullTerminatedString() private static void ThrowMustBeNullTerminatedString()

View File

@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Misaki.HighPerformance.LowLevel.Utilities; namespace Misaki.HighPerformance.LowLevel.Utilities;
public static unsafe partial class MemoryUtilities public static unsafe partial class MemoryUtility
{ {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
private struct AlignOfHelper<T> private struct AlignOfHelper<T>

View File

@@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.LowLevel.Utilities; namespace Misaki.HighPerformance.LowLevel.Utilities;
public static unsafe class UnsafeUtilities public static unsafe class UnsafeUtility
{ {
/// <summary> /// <summary>
/// Converts a pointer to a reference of a specified type. /// Converts a pointer to a reference of a specified type.

View File

@@ -6,7 +6,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{ {
internal abstract class GeneratorBase internal abstract class GeneratorBase
{ {
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 | global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)]";
protected static readonly string[] s_matrixComponents = new[] { "c0", "c1", "c2", "c3" }; protected static readonly string[] s_matrixComponents = new[] { "c0", "c1", "c2", "c3" };
protected static readonly string[] s_vectorComponents = new[] { "x", "y", "z", "w" }; protected static readonly string[] s_vectorComponents = new[] { "x", "y", "z", "w" };
@@ -70,7 +70,7 @@ namespace {typeInfo.TypeSymbol.ContainingNamespace.ToDisplayString()}
protected virtual void GenerateTypeStart() protected virtual void GenerateTypeStart()
{ {
sourceBuilder.Append($@" sourceBuilder.Append($@"
[global::System.Runtime.CompilerServices.SkipLocalsInit] //[global::System.Runtime.CompilerServices.SkipLocalsInit]
public partial struct {typeInfo.TypeSymbol.Name} : global::System.IEquatable<{typeInfo.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}> public partial struct {typeInfo.TypeSymbol.Name} : global::System.IEquatable<{typeInfo.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>
{{"); {{");
} }

View File

@@ -515,13 +515,6 @@ 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($@"

View File

@@ -12,7 +12,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
private int _missingComponents; private int _missingComponents;
private string _componentTypePrefix = null!; private string _componentTypePrefix = null!;
private readonly List<(string signature, string assignment)> _constructorSignatures = new(); private readonly List<(string signature, List<string> assignment)> _constructorSignatures = new();
private string GetConversionFromTemplate(string template, int componentIndex) private string GetConversionFromTemplate(string template, int componentIndex)
{ {
@@ -209,7 +209,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
_constructorSignatures.Add(( _constructorSignatures.Add((
signature: $"{componentType} value", signature: $"{componentType} value",
assignment: string.Join(", ", Enumerable.Range(0, typeInfo.Row).Select(_ => "value")))); assignment: Enumerable.Range(0, typeInfo.Row).Select(_ => "value").ToList()));
if (string.IsNullOrEmpty(typeInfo.TypePrefix)) if (string.IsNullOrEmpty(typeInfo.TypePrefix))
{ {
@@ -253,8 +253,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
continue; continue;
} }
var assignment = string.Join(", ", assignments); _constructorSignatures.Add((signature, assignments));
_constructorSignatures.Add((signature, assignment));
} }
} }
@@ -265,58 +264,34 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
var targetTemplate = kv.Key; var targetTemplate = kv.Key;
var targetTypes = kv.Value; var targetTypes = kv.Value;
var tempSB = new StringBuilder();
foreach (var type in targetTypes) foreach (var type in targetTypes)
{ {
var assignments = new List<string>();
for (var i = 0; i < typeInfo.Row; i++) for (var i = 0; i < typeInfo.Row; i++)
{ {
tempSB.Append(GetConversionFromTemplate(targetTemplate, i)); assignments.Add(GetConversionFromTemplate(targetTemplate, i));
if (i < typeInfo.Row - 1)
{
tempSB.Append(", ");
}
} }
_constructorSignatures.Add(( _constructorSignatures.Add((
signature: $"{type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} v", signature: $"{type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} v",
assignment: tempSB.ToString())); assignment: assignments));
tempSB.Clear();
} }
} }
} }
foreach (var (signature, assignment) in _constructorSignatures) foreach (var (signature, assignment) in _constructorSignatures)
{ {
sourceBuilder.AppendLine($@" sourceBuilder.Append($@"
public {typeName}({signature}) public {typeName}({signature})
{{
this = Create({assignment});
}}");
}
sourceBuilder.Append($@"
{INLINE_METHOD_ATTRIBUTE}
public static {typeName} Create({string.Join(", ", Enumerable.Range(0, typeInfo.Row).Select((_, i) => $"{typeInfo.ComponentTypeFullName} {s_vectorComponents[i]}"))})
{{"); {{");
for (var i = 0; i < typeInfo.Row; i++)
if (typeInfo.VectorType != null)
{ {
sourceBuilder.Append($@" sourceBuilder.Append($@"
global::System.Span<{typeInfo.ComponentTypeFullName}> span = [{string.Join(", ", Enumerable.Range(0, typeInfo.Row + _missingComponents).Select(i => i < typeInfo.Row ? $"{s_vectorComponents[i]}" : "default"))}]; this.{s_vectorComponents[i]} = {assignment[i]};");
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Create(global::System.Runtime.CompilerServices.Unsafe.As<global::System.Span<{typeInfo.ComponentTypeFullName}>, global::System.ReadOnlySpan<{typeInfo.VectorTypeFullName}>>(ref span));");
} }
else
{
sourceBuilder.Append($@"
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Create({string.Join(", ", Enumerable.Range(0, typeInfo.Row + _missingComponents).Select(i => i < typeInfo.Row ? $"{s_vectorComponents[i]}" : "default"))});");
}
sourceBuilder.AppendLine($@" sourceBuilder.AppendLine($@"
ref var address = ref global::System.Runtime.CompilerServices.Unsafe.As<global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}<{typeInfo.VectorTypeFullName}>, byte>(ref vector);
return global::System.Runtime.CompilerServices.Unsafe.ReadUnaligned<{typeInfo.TypeFullName}>(ref address);
}}"); }}");
}
EndRegion(); EndRegion();
} }
@@ -434,23 +409,22 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator +({typeName} lhs, {typeName} rhs) public static {typeName} operator +({typeName} lhs, {typeName} rhs)
{{ {{
return (lhs.AsVector{_vectorBitsSize}() + rhs.AsVector{_vectorBitsSize}()).{asResult}; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} + rhs.{c}"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator +({typeName} lhs, {componentType} rhs) public static {typeName} operator +({typeName} lhs, {componentType} rhs)
{{ {{
return lhs + new {typeName}(rhs); return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} + rhs"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator +({componentType} lhs, {typeName} rhs) public static {typeName} operator +({componentType} lhs, {typeName} rhs)
{{ {{
return new {typeName}(lhs) + rhs; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs + rhs.{c}"))});
}} }}
#if NET10_0_OR_GREATER #if false //NET10_0_OR_GREATER
// Use scaler here to let JIT handle the simd optimization since we can not do a in-place vectorlization manually.
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public void operator +=({typeName} other) public void operator +=({typeName} other)
{{"); {{");
@@ -469,23 +443,22 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator -({typeName} lhs, {typeName} rhs) public static {typeName} operator -({typeName} lhs, {typeName} rhs)
{{ {{
return (lhs.AsVector{_vectorBitsSize}() - rhs.AsVector{_vectorBitsSize}()).{asResult}; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} - rhs.{c}"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator -({typeName} lhs, {componentType} rhs) public static {typeName} operator -({typeName} lhs, {componentType} rhs)
{{ {{
return lhs - new {typeName}(rhs); return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} - rhs"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator -({componentType} lhs, {typeName} rhs) public static {typeName} operator -({componentType} lhs, {typeName} rhs)
{{ {{
return new {typeName}(lhs) - rhs; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs - rhs.{c}"))});
}} }}
#if NET10_0_OR_GREATER #if NET10_0_OR_GREATER
// Use scaler here to let JIT handle the simd optimization since we can not do a in-place vectorlization manually.
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public void operator -=({typeName} other) public void operator -=({typeName} other)
{{"); {{");
@@ -504,19 +477,19 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator *({typeName} lhs, {typeName} rhs) public static {typeName} operator *({typeName} lhs, {typeName} rhs)
{{ {{
return (lhs.AsVector{_vectorBitsSize}() * rhs.AsVector{_vectorBitsSize}()).{asResult}; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} * rhs.{c}"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator *({typeName} lhs, {componentType} rhs) public static {typeName} operator *({typeName} lhs, {componentType} rhs)
{{ {{
return (lhs.AsVector{_vectorBitsSize}() * rhs).{asResult}; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} * rhs"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator *({componentType} lhs, {typeName} rhs) public static {typeName} operator *({componentType} lhs, {typeName} rhs)
{{ {{
return new {typeName}(lhs) * rhs; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs * rhs.{c}"))});
}} }}
#if NET10_0_OR_GREATER #if NET10_0_OR_GREATER
@@ -539,19 +512,19 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator /({typeName} lhs, {typeName} rhs) public static {typeName} operator /({typeName} lhs, {typeName} rhs)
{{ {{
return (lhs.AsVector{_vectorBitsSize}() / rhs.AsVector{_vectorBitsSize}()).{asResult}; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} / rhs.{c}"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator /({typeName} lhs, {componentType} rhs) public static {typeName} operator /({typeName} lhs, {componentType} rhs)
{{ {{
return (lhs.AsVector{_vectorBitsSize}() / rhs).{asResult}; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} / rhs"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator /({componentType} lhs, {typeName} rhs) public static {typeName} operator /({componentType} lhs, {typeName} rhs)
{{ {{
return new {typeName}(lhs) / rhs; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs / rhs.{c}"))});
}} }}
#if NET10_0_OR_GREATER #if NET10_0_OR_GREATER
@@ -580,13 +553,13 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator %({typeName} lhs, {componentType} rhs) public static {typeName} operator %({typeName} lhs, {componentType} rhs)
{{ {{
return lhs % new {typeName}(rhs); return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs.{c} % rhs"))});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator %({componentType} lhs, {typeName} rhs) public static {typeName} operator %({componentType} lhs, {typeName} rhs)
{{ {{
return new {typeName}(lhs) % rhs; return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs % rhs.{c}"))});
}} }}
#if NET10_0_OR_GREATER #if NET10_0_OR_GREATER
@@ -615,19 +588,19 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator -({typeName} value) public static {typeName} operator -({typeName} value)
{{ {{
return (-value.AsVector{_vectorBitsSize}()).{asResult}; return new {typeName}(0{_componentTypePrefix}) - value;
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator ++({typeName} value) public static {typeName} operator ++({typeName} value)
{{ {{
return (value.AsVector{_vectorBitsSize}() + global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.CreateScalar(1{_componentTypePrefix})).{asResult}; return value + new {typeName}(1{_componentTypePrefix});
}} }}
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeName} operator --({typeName} value) public static {typeName} operator --({typeName} value)
{{ {{
return (value.AsVector{_vectorBitsSize}() - global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.CreateScalar(1{_componentTypePrefix})).{asResult}; return value - new {typeName}(1{_componentTypePrefix});
}}"); }}");
// Comparison operators // Comparison operators
@@ -788,7 +761,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
else else
{ {
sourceBuilder.Append($@" sourceBuilder.Append($@"
return global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Create({string.Join(", ", Enumerable.Range(0, typeInfo.Row + _missingComponents).Select(i => i < typeInfo.Row ? $"value.{s_vectorComponents[i]}" : "0"))});"); return global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Create({string.Join(", ", Enumerable.Range(0, typeInfo.Row + _missingComponents).Select(i => i < typeInfo.Row ? $"value.{s_vectorComponents[i]}" : "default"))});");
} }
sourceBuilder.Append($@" sourceBuilder.Append($@"
}} }}
@@ -803,11 +776,6 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
}}"); }}");
} }
private string ComponentwiseAssignments(Func<string, string> func, string separator = ", ")
{
return string.Join(separator, s_vectorComponents.Take(typeInfo.Row).Select(func));
}
private void GenerateMathMethod() private void GenerateMathMethod()
{ {
var typeName = typeInfo.TypeName; var typeName = typeInfo.TypeName;
@@ -824,7 +792,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} {typeName}({signature}) public static {typeFullName} {typeName}({signature})
{{ {{
return {typeFullName}.Create({assignment}); return new {typeFullName}({string.Join(", ", assignment)});
}}"); }}");
} }
@@ -881,724 +849,6 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
throw new System.ArgumentException(""Invalid shuffle component: "" + component); throw new System.ArgumentException(""Invalid shuffle component: "" + component);
}} }}
}}"); }}");
if (typeInfo.ComponentTypeSymbol.SpecialType == SpecialType.System_Boolean)
{
sourceBuilder.AppendLine($@"
/// <summary>Returns true if any component of the input {typeName} vector is true, false otherwise.</summary>
/// <param name=""v"">Vector of values to compare.</param>
/// <returns>True if any the components of v are true, false otherwise.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static bool any({typeFullName} v)
{{
return {ComponentwiseAssignments(c => $"v.{c}", " ||")};
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns true if all component of the input {typeName} vector is true, false otherwise.</summary>
/// <param name=""v"">Vector of values to compare.</param>
/// <returns>True if all the components of v are true, false otherwise.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static bool all({typeFullName} v)
{{
return {ComponentwiseAssignments(c => $"v.{c}", " && ")};
}}");
}
if (typeInfo.Arithmetic)
{
StartRegion("Math Methods");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise minimum of two {typeName} vectors.</summary>
/// <param name=""x"">The first vector to compare.</param>
/// <param name=""y"">The second vector to compare.</param>
/// <returns>The componentwise minimum of the two vectors.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} min({typeFullName} x, {typeFullName} y)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Min(x.AsVector{_vectorBitsSize}(), y.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise maximum of two {typeName} vectors.</summary>
/// <param name=""x"">The first vector to compare.</param>
/// <param name=""y"">The second vector to compare.</param>
/// <returns>The componentwise maximum of the two vectors.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} max({typeFullName} x, {typeFullName} y)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Max(x.AsVector{_vectorBitsSize}(), y.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise absolute value of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise absolute value of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} abs({typeFullName} v)
{{
return max(v, -v);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the result of a componentwise clamping of the value v into the interval (inclusive) [min, max], where v, min and max are {typeName} vectors.</summary>
/// <param name=""v"">Input value to be clamped.</param>
/// <param name=""min"">Lower bound of the interval.</param>
/// <param name=""max"">Upper bound of the interval.</param>
/// <returns>The componentwise clamping of the input valueToClamp into the interval (inclusive) [min, max].</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} clamp({typeFullName} v, {typeFullName} min, {typeFullName} max)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Clamp(v.AsVector{_vectorBitsSize}(), min.AsVector{_vectorBitsSize}(), max.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the dot product of two {typeName} vectors.</summary>
/// <param name=""x"">The first vector to dot.</param>
/// <param name=""y"">The second vector to dot.</param>
/// <returns>The dot product of the two vectors.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {componentTypeFullName} dot({typeFullName} x, {typeFullName} y)
{{
return global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Dot(x.AsVector{_vectorBitsSize}(), y.AsVector{_vectorBitsSize}());
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns trueValue if test is true, falseValue otherwise.</summary>
/// <param name=""falseValue"">Value to use if test is false.</param>
/// <param name=""trueValue"">Value to use if test is true.</param>
/// <param name=""test"">Bool value to choose between falseValue and trueValue.</param>
/// <returns>The selection between falseValue and trueValue according to bool test.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} select({typeFullName} falseValue, {typeFullName} trueValue, bool test)
{{
return test ? trueValue : falseValue;
}}");
sourceBuilder.AppendLine($@"
/// <summary>
/// Returns a componentwise selection between two {typeName} vectors falseValue and trueValue based on a bool{typeInfo.Row} selection mask test.
/// Per component, the component from trueValue is selected when test is true, otherwise the component from falseValue is selected.
/// </summary>
/// <param name=""falseValue"">Values to use if test is false.</param>
/// <param name=""trueValue"">Values to use if test is true.</param>
/// <param name=""test"">Selection mask to choose between falseValue and trueValue.</param>
/// <returns>The componentwise selection between falseValue and trueValue according to selection mask test.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} select({typeFullName} falseValue, {typeFullName} trueValue, global::Misaki.HighPerformance.Mathematics.bool{typeInfo.Row} test)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"test.{c} ? trueValue.{c} : falseValue.{c}")});
}}");
if (typeInfo.ComponentTypeSymbol.SpecialType != SpecialType.System_UInt16
&& typeInfo.ComponentTypeSymbol.SpecialType != SpecialType.System_UInt32
&& typeInfo.ComponentTypeSymbol.SpecialType != SpecialType.System_UInt64)
{
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise sign of a {typeName} vector. 1 for positive components, 0 for zero components and -1 for negative components.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise sign of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} sign({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"sign(v.{c})")});
}}");
}
sourceBuilder.AppendLine($@"
/// <summary>Returns true if any component of the input {typeName} vector is true, false otherwise.</summary>
/// <param name=""v"">Vector of values to compare.</param>
/// <returns>True if any the components of v are true, false otherwise.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static bool any({typeFullName} v)
{{
return {ComponentwiseAssignments(c => $"v.{c} != 0", " || ")};
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns true if all component of the input {typeName} vector is true, false otherwise.</summary>
/// <param name=""v"">Vector of values to compare.</param>
/// <returns>True if all the components of v are true, false otherwise.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static bool all({typeFullName} v)
{{
return {ComponentwiseAssignments(c => $"v.{c} != 0", " && ")};
}}");
// Only floating point types support these methods
if (typeInfo.ComponentTypeSymbol.SpecialType == SpecialType.System_Single
|| typeInfo.ComponentTypeSymbol.SpecialType == SpecialType.System_Double)
{
sourceBuilder.AppendLine($@"
/// <summary>Returns the result of a componentwise clamping of the {typeName} vector v into the interval [0, 1].</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise clamping of the input into the interval [0, 1].</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} saturate({typeFullName} v)
{{
return clamp(v, new {typeFullName}(0{_componentTypePrefix}), new {typeFullName}(1{_componentTypePrefix}));
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the linear interpolation between two {typeName} vectors x and y by the interpolant t.</summary>
/// <param name=""x"">The vector when t is 0.</param>
/// <param name=""y"">The vector when t is 1.</param>
/// <param name=""t"">The interpolation factor.</param>
/// <returns>The linearly interpolated vector.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} lerp({typeFullName} x, {typeFullName} y, {componentTypeFullName} t)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Lerp(x.AsVector{_vectorBitsSize}(), y.AsVector{_vectorBitsSize}(), global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Create(t));
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the result of a componentwise linear interpolation between two {typeName} vectors x and y using a {typeName} vector t.</summary>
/// <param name=""x"">The value to interpolate from.</param>
/// <param name=""y"">The value to interpolate to.</param>
/// <param name=""t"">The interpolation factor.</param>
/// <returns>The linearly interpolated vector.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} lerp({typeFullName} x, {typeFullName} y, {typeFullName} t)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Lerp(x.AsVector{_vectorBitsSize}(), y.AsVector{_vectorBitsSize}(), t.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise result of normalizing a value x to a range [a, b]. The opposite of lerp. Equivalent to (v - a) / (b - a).</summary>
/// <param name=""start"">The start point of the range.</param>
/// <param name=""end"">The end point of the range.</param>
/// <param name=""v""> The value to normalize to the range.</param>
/// <returns>The componentwise interpolation parameter of x with respect to the input range [a, b].</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} unlerp({typeFullName} start, {typeFullName} end, {typeFullName} v)
{{
return (v - start) / (end - start);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise result of a non-clamping linear remapping of a value x from source range [srcStart, srcEnd] to the destination range [dstStart, dstEnd].</summary>
/// <param name=""srcStart"">The start point of the source range [srcStart, srcEnd].</param>
/// <param name=""srcEnd"">The end point of the source range [srcStart, srcEnd].</param>
/// <param name=""dstStart"">The start point of the destination range [dstStart, dstEnd].</param>
/// <param name=""dstEnd"">The end point of the destination range [dstStart, dstEnd].</param>
/// <param name=""v"">The value to remap from the source to destination range.</param>
/// <returns>The componentwise remap of input x from the source range to the destination range.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} remap({typeFullName} srcStart, {typeFullName} srcEnd, {typeFullName} dstStart, {typeFullName} dstEnd, {typeFullName} v)
{{
return lerp(dstStart, dstEnd, unlerp(srcStart, srcEnd, v));
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the result of a componentwise multiply-add operation (a * b + c) on 3 {typeName} vectors.</summary>
/// <remarks>
/// When fast math enabled on some architectures, this could be converted to a fused multiply add (FMA).
/// FMA is more accurate due to rounding once at the end of the computation rather than twice that is required when
/// this computation is not fused.
/// </remarks>
/// <param name=""mulA"">First value to multiply.</param>
/// <param name=""mulB"">Second value to multiply.</param>
/// <param name=""addC"">Third value to add to the product of a and b.</param>
/// <returns>The componentwise multiply-add of the inputs.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} mad({typeFullName} mulA, {typeFullName} mulB, {typeFullName} addC)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.FusedMultiplyAdd(mulA.AsVector{_vectorBitsSize}(), mulB.AsVector{_vectorBitsSize}(), addC.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise tangent of a {typeFullName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise tangent of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} tan({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"tan(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise hyperbolic tangent of a {typeFullName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise hyperbolic tangent of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} tanh({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"tanh(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise arctangent of a {typeFullName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise arctangent of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} atan({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"atan(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise 2-argument arctangent of a {typeFullName} vector.</summary>
/// <param name=""x"">Input value x.</param>
/// <param name=""y"">Input value y.</param>
/// <returns>The componentwise 2-argument arctangent of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} atan2({typeFullName} x, {typeFullName} y)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"atan2(x.{c}, y.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise cosine of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise cosine of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} cos({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Cos(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise hyperbolic cosine of a {typeFullName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise hyperbolic cosine of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} cosh({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"cosh(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise arccosine of a {typeFullName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise arccosine of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} acos({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"acos(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise sine of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise sine of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} sin({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Sin(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise hyperbolic sine of a {typeFullName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise hyperbolic sine of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} sinh({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"sinh(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise arcsine of a {typeFullName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise arcsine of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} asin({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"asin(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise sine and cosine of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise sine and cosine of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static ({typeFullName} sin, {typeFullName} cos) sincos({typeFullName} v)
{{
var vectors = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.SinCos(v.AsVector{_vectorBitsSize}());
return (vectors.Sin.As{typeName}(), vectors.Cos.As{typeName}());
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise floor of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise floor of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} floor({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Floor(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise ceiling of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise ceiling of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} ceil({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Ceiling(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the result of rounding each component of a {typeName} vector value to the nearest integral value.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise round to nearest integral value of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} round({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"round(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise base-e exponential of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise base-e exponential of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} exp({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Exp(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise base-2 exponential of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise base-2 exponential of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} exp2({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Exp(v.AsVector{_vectorBitsSize}() * 0.693147180559945309{_componentTypePrefix});
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise base-10 exponential of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise base-10 exponential of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} exp10({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Exp(v.AsVector{_vectorBitsSize}() * 2.302585092994045684{_componentTypePrefix});
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise natural logarithm (base-e) of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise natural logarithm of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} log({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Log(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise base-2 logarithm of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise base-2 logarithm of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} log2({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Log2(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise base-10 logarithm of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise base-10 logarithm of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} log10({typeFullName} v)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"log10(v.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise quadratic root of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise quadratic root of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} sqrt({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Sqrt(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the reciprocal square root of a {typeName} vector.</summary>
/// <param name=""v"">Value to use when computing reciprocal square root.</param>
/// <returns>The reciprocal square root.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} rsqrt({typeFullName} v)
{{
return 1{_componentTypePrefix} / sqrt(v);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the length of a {typeName} vector.</summary>
/// <param name=""v"">Vector to use when computing length.</param>
/// <returns>Length of vector v.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {componentTypeFullName} length({typeFullName} v)
{{
return sqrt(dot(v, v));
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the squared length of a {typeName} vector.</summary>
/// <param name=""v"">Vector to use when computing squared length.</param>
/// <returns>Squared length of vector v.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {componentTypeFullName} lengthsq({typeFullName} v)
{{
return dot(v, v);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the distance between two {typeName} vectors.</summary>
/// <param name=""x"">First vector to use in distance computation.</param>
/// <param name=""y"">Second vector to use in distance computation.</param>
/// <returns>The distance between x and y.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {componentTypeFullName} distance({typeFullName} x, {typeFullName} y)
{{
return length(y - x);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the squared distance between two {typeName} vectors.</summary>
/// <param name=""x"">First vector to use in distance computation.</param>
/// <param name=""y"">Second vector to use in distance computation.</param>
/// <returns>The squared distance between x and y.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {componentTypeFullName} distancesq({typeFullName} x, {typeFullName} y)
{{
return lengthsq(y - x);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise truncate of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise truncate of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} trunc({typeFullName} v)
{{
var vector = global::System.Runtime.Intrinsics.Vector{_vectorBitsSize}.Truncate(v.AsVector{_vectorBitsSize}());
return vector.As{typeName}();
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise fractional parts of a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The componentwise fractional part of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} frac({typeFullName} v)
{{
return v - floor(v);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the reciprocal a {typeName} vector.</summary>
/// <param name=""v"">Input value.</param>
/// <returns>The reciprocal of the input.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} rcp({typeFullName} v)
{{
return 1{_componentTypePrefix} / v;
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise result of raising v to the power p.</summary>
/// <param name=""x"">The exponent base.</param>
/// <param name=""y"">The exponent power.</param>
/// <returns>The componentwise result of raising x to the power y.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} pow({typeFullName} x, {typeFullName} y)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"pow(x.{c}, y.{c})")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the componentwise floating point remainder of x/y.</summary>
/// <param name=""x"">The dividend in x/y.</param>
/// <param name=""y"">The divisor in x/y.</param>
/// <returns>The componentwise remainder of x/y.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} fmod({typeFullName} x, {typeFullName} y)
{{
return new {typeFullName}({ComponentwiseAssignments(c => $"x.{c} % y.{c}")});
}}");
sourceBuilder.AppendLine($@"
/// <summary>
/// Performs a componentwise split of a {typeName} vector into an integral part i and a fractional part that gets returned.
/// Both parts take the sign of the corresponding input component.
/// </summary>
/// <param name=""x"">Value to split into integral and fractional part.</param>
/// <param name=""i"">Output value containing integral part of x.</param>
/// <returns>The componentwise fractional part of x.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} modf({typeFullName} x, out {typeFullName} i)
{{
i = trunc(x);
return x - i;
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns a normalized version of the {typeName} vector v by scaling it by 1 / length(v).</summary>
/// <param name=""v"">Vector to normalize.</param>
/// <returns>The normalized vector.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} normalize({typeFullName} v)
{{
return rsqrt(dot(v, v)) * v;
}}");
sourceBuilder.AppendLine($@"
/// <summary>
/// Returns a safe normalized version of the {typeName} vector v by scaling it by 1 / length(v).
/// Returns the given default value when 1 / length(v) does not produce a finite number.
/// </summary>
/// <param name=""v"">Vector to normalize.</param>
/// <param name=""defaultvalue"">Vector to return if normalized vector is not finite.</param>
/// <returns>The normalized vector or the default value if the normalized vector is not finite.</returns>
{INLINE_METHOD_ATTRIBUTE}
static public {typeFullName} normalizesafe({typeFullName} v, {typeFullName} defaultvalue = default)
{{
var len = dot(v, v);
return select(defaultvalue, v * rsqrt(len), len > FLT_MIN_NORMAL);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns a componentwise smooth Hermite interpolation between 0.0f and 1.0f when v is in the interval (inclusive) [vMin, vMax].</summary>
/// <param name=""vMin"">The minimum range of the v parameter.</param>
/// <param name=""vMax"">The maximum range of the v parameter.</param>
/// <param name=""v"">The value to be interpolated.</param>
/// <returns>Returns component values camped to the range [0, 1].</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} smoothstep({typeFullName} vMin, {typeFullName} vMax, {typeFullName} v)
{{
var t = saturate((v - vMin) / (vMax - vMin));
return t * t * (3{_componentTypePrefix} - (2{_componentTypePrefix} * t));
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the result of a componentwise step function where each component is 1.0f when v = threshold and 0.0f otherwise.</summary>
/// <param name=""threshold"">Vector of values to be used as a threshold for returning 1.</param>
/// <param name=""v"">Vector of values to compare against threshold.</param>
/// <returns>1 if the componentwise comparison v = threshold is true, otherwise 0.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} step({typeFullName} threshold, {typeFullName} v)
{{
return select(new {typeFullName}(0{_componentTypePrefix}), new {typeFullName}(1{_componentTypePrefix}), v == threshold);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Given an incident vector i and a normal vector n, returns the reflection vector r = i - 2.0 * dot(i, n) * n.</summary>
/// <param name=""i"">Incident vector.</param>
/// <param name=""n"">Normal vector.</param>
/// <returns>Reflection vector.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} reflect({typeFullName} i, {typeFullName} n)
{{
return i - 2{_componentTypePrefix} * n * dot(i, n);
}}");
sourceBuilder.AppendLine($@"
/// <summary>Returns the refraction vector given the incident vector i, the normal vector n and the refraction index.</summary>
/// <param name=""i"">Incident vector.</param>
/// <param name=""n"">Normal vector.</param>
/// <param name=""indexOfRefraction"">Index of refraction.</param>
/// <returns>Refraction vector.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} refract({typeFullName} i, {typeFullName} n, {componentTypeFullName} indexOfRefraction)
{{
var ni = dot(n, i);
var k = 1.0f - indexOfRefraction * indexOfRefraction * (1{_componentTypePrefix} - ni * ni);
return select(new(0{_componentTypePrefix}), indexOfRefraction * i - (indexOfRefraction * ni + sqrt(k)) * n, k >= 0);
}}");
sourceBuilder.AppendLine($@"
/// <summary>
/// Compute vector projection of a onto b.
/// </summary>
/// <remarks>
/// Some finite vectors a and b could generate a non-finite result. This is most likely when a's components
/// are very large (close to Single.MaxValue) or when b's components are very small (close to FLT_MIN_NORMAL).
/// In these cases, you can call <see cref=""projectsafe({typeFullName},{typeFullName},{typeFullName})""/>
/// which will use a given default value if the result is not finite.
/// </remarks>
/// <param name=""a"">Vector to project.</param>
/// <param name=""ontoB"">Non-zero vector to project onto.</param>
/// <returns>Vector projection of a onto b.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} project({typeFullName} a, {typeFullName} ontoB)
{{
return (dot(a, ontoB) / dot(ontoB, ontoB)) * ontoB;
}}");
sourceBuilder.AppendLine($@"
/// <summary>
/// Compute vector projection of a onto b. If result is not finite, then return the default value instead.
/// </summary>
/// <remarks>
/// This function performs extra checks to see if the result of projecting a onto b is finite. If you know that
/// your inputs will generate a finite result or you don't care if the result is finite, then you can call
/// <see cref=""project({typeFullName},{typeFullName})""/> instead which is faster than this function.
/// </remarks>
/// <param name=""a"">Vector to project.</param>
/// <param name=""ontoB"">Non-zero vector to project onto.</param>
/// <param name=""defaultValue"">Default value to return if projection is not finite.</param>
/// <returns>Vector projection of a onto b or the default value.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} projectsafe({typeFullName} a, {typeFullName} ontoB, {typeFullName} defaultValue = default)
{{
var proj = project(a, ontoB);
return select(defaultValue, proj, all(isfinite(proj)));
}}");
sourceBuilder.AppendLine($@"
/// <summary>Conditionally flips a vector n if two vectors i and ng are pointing in the same direction. Returns n if dot(i, ng) &lt; 0, -n otherwise.</summary>
/// <param name=""n"">Vector to conditionally flip.</param>
/// <param name=""i"">First vector in direction comparison.</param>
/// <param name=""ng"">Second vector in direction comparison.</param>
/// <returns>-n if i and ng point in the same direction; otherwise return n unchanged.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} faceforward({typeFullName} n, {typeFullName} i, {typeFullName} ng)
{{
return select(n, -n, dot(ng, i) >= 0.0f);
}}");
}
else
{
sourceBuilder.AppendLine($@"
/// <summary>Returns the result of a componentwise multiply-add operation (a * b + c) on 3 {typeName} vectors.</summary>
/// <param name=""mulA"">First value to multiply.</param>
/// <param name=""mulB"">Second value to multiply.</param>
/// <param name=""addC"">Third value to add to the product of a and b.</param>
/// <returns>The componentwise multiply-add of the inputs.</returns>
{INLINE_METHOD_ATTRIBUTE}
public static {typeFullName} mad({typeFullName} mulA, {typeFullName} mulB, {typeFullName} addC)
{{
return mulA * mulB + addC;
}}");
}
EndRegion();
}
sourceBuilder.Append($@" sourceBuilder.Append($@"
}}"); }}");
} }

View File

@@ -9,9 +9,6 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen
[Generator] [Generator]
internal class NumericTypeGenerator : IIncrementalGenerator internal class NumericTypeGenerator : IIncrementalGenerator
{ {
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)
{ {
// Create a provider that finds all types with NumericTypeAttribute // Create a provider that finds all types with NumericTypeAttribute

View File

@@ -94,7 +94,12 @@ public struct OBB : IEquatable<OBB>
public readonly AABB ToAABB() public readonly AABB ToAABB()
{ {
var absRotation = math.abs(new float3x3(Rotation)); var absRotation = new float3x3
{
c0 = math.abs(Rotation.value.x),
c1 = math.abs(Rotation.value.y),
c2 = math.abs(Rotation.value.z)
};
var worldExtents = absRotation.c0 * Extents.x + absRotation.c1 * Extents.y + absRotation.c2 * Extents.z; var worldExtents = absRotation.c0 * Extents.x + absRotation.c1 * Extents.y + absRotation.c2 * Extents.z;
return new AABB(Center - worldExtents, Center + worldExtents); return new AABB(Center - worldExtents, Center + worldExtents);
} }

View File

@@ -1,7 +1,5 @@
using Misaki.HighPerformance.Mathematics.CodeGen; using Misaki.HighPerformance.Mathematics.CodeGen;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace Misaki.HighPerformance.Mathematics; namespace Misaki.HighPerformance.Mathematics;

File diff suppressed because it is too large Load Diff

View File

@@ -106,8 +106,8 @@ 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)
{ {
var (sina, cosa) = sincos(0.5f * angle); sincos(0.5f * angle, out var s, out var c);
return new quaternion(new float4(axis * sina, cosa)); return new quaternion(new float4(axis * s, c));
} }
/// <summary> /// <summary>
@@ -120,7 +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)));
var (s, c) = sincos(0.5f * xyz); sincos(0.5f * xyz, out var s, out var 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,
@@ -139,7 +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)));
var (s, c) = sincos(0.5f * xyz); sincos(0.5f * xyz, out var s, out var 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,
@@ -158,7 +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)));
var (s, c) = sincos(0.5f * xyz); sincos(0.5f * xyz, out var s, out var 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,
@@ -177,7 +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)));
var (s, c) = sincos(0.5f * xyz); sincos(0.5f * xyz, out var s, out var 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,
@@ -197,7 +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)));
var (s, c) = sincos(0.5f * xyz); sincos(0.5f * xyz, out var s, out var 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,
@@ -216,7 +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)));
var (s, c) = sincos(0.5f * xyz); sincos(0.5f * xyz, out var s, out var 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,
@@ -364,8 +364,8 @@ public partial struct quaternion : IEquatable<quaternion>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static quaternion RotateX(float angle) public static quaternion RotateX(float angle)
{ {
var (sina, cosa) = sincos(0.5f * angle); sincos(0.5f * angle, out var s, out var c);
return new quaternion(sina, 0.0f, 0.0f, cosa); return new quaternion(s, 0.0f, 0.0f, c);
} }
/// <summary>Returns a quaternion that rotates around the y-axis by a given number of radians.</summary> /// <summary>Returns a quaternion that rotates around the y-axis by a given number of radians.</summary>
@@ -374,8 +374,8 @@ public partial struct quaternion : IEquatable<quaternion>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static quaternion RotateY(float angle) public static quaternion RotateY(float angle)
{ {
var (sina, cosa) = sincos(0.5f * angle); sincos(0.5f * angle, out var s, out var c);
return new quaternion(0.0f, sina, 0.0f, cosa); return new quaternion(0.0f, s, 0.0f, c);
} }
/// <summary>Returns a quaternion that rotates around the z-axis by a given number of radians.</summary> /// <summary>Returns a quaternion that rotates around the z-axis by a given number of radians.</summary>
@@ -384,8 +384,8 @@ public partial struct quaternion : IEquatable<quaternion>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static quaternion RotateZ(float angle) public static quaternion RotateZ(float angle)
{ {
var (sina, cosa) = sincos(0.5f * angle); sincos(0.5f * angle, out var s, out var c);
return new quaternion(0.0f, 0.0f, sina, cosa); return new quaternion(0.0f, 0.0f, s, c);
} }
/// <summary> /// <summary>
@@ -585,8 +585,8 @@ 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);
var (sin_v_len, cos_v_len) = sincos(v_len); sincos(v_len, out var s, out var c);
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 * s, c));
} }
/// <summary>Returns the natural exponent of a quaternion.</summary> /// <summary>Returns the natural exponent of a quaternion.</summary>
@@ -597,8 +597,8 @@ 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);
var (sin_v_len, cos_v_len) = sincos(v_len); sincos(v_len, out var s, out var c);
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 * s, c) * exp(q.value.w));
} }
/// <summary>Returns the natural logarithm of a unit length quaternion.</summary> /// <summary>Returns the natural logarithm of a unit length quaternion.</summary>

View File

@@ -642,7 +642,7 @@ public struct random
public float2 NextFloat2Direction() public float2 NextFloat2Direction()
{ {
var angle = NextFloat() * PI * 2.0f; var angle = NextFloat() * PI * 2.0f;
var (s, c) = sincos(angle); sincos(angle, out var s, out var c);
return float2(c, s); return float2(c, s);
} }
@@ -652,7 +652,7 @@ public struct random
public double2 NextDouble2Direction() public double2 NextDouble2Direction()
{ {
var angle = NextDouble() * PI_DBL * 2.0; var angle = NextDouble() * PI_DBL * 2.0;
var (s, c) = sincos(angle); sincos(angle, out var s, out var c);
return double2(c, s); return double2(c, s);
} }
@@ -665,7 +665,7 @@ public struct random
var z = rnd.x * 2.0f - 1.0f; var z = rnd.x * 2.0f - 1.0f;
var r = sqrt(max(1.0f - z * z, 0.0f)); var r = sqrt(max(1.0f - z * z, 0.0f));
var angle = rnd.y * PI * 2.0f; var angle = rnd.y * PI * 2.0f;
var (s, c) = sincos(angle); sincos(angle, out var s, out var c);
return float3(c * r, s * r, z); return float3(c * r, s * r, z);
} }
@@ -678,7 +678,7 @@ public struct random
var z = rnd.x * 2.0 - 1.0; var z = rnd.x * 2.0 - 1.0;
var r = sqrt(max(1.0 - z * z, 0.0)); var r = sqrt(max(1.0 - z * z, 0.0));
var angle = rnd.y * PI_DBL * 2.0; var angle = rnd.y * PI_DBL * 2.0;
var (s, c) = sincos(angle); sincos(angle, out var s, out var c);
return double3(c * r, s * r, z); return double3(c * r, s * r, z);
} }
@@ -694,7 +694,7 @@ public struct random
var i = sqrt(1.0f - u1); var i = sqrt(1.0f - u1);
var j = sqrt(u1); var j = sqrt(u1);
var (sin_theta_rho, cos_theta_rho) = sincos(theta_rho); sincos(theta_rho, out var sin_theta_rho, out var cos_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); 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)); return quaternion(select(q.value, -q.value, q.value.w < 0.0f));

View File

@@ -3,52 +3,67 @@ using Misaki.HighPerformance.Mathematics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Misaki.HighPerformance.Test.Benchmark; namespace Misaki.HighPerformance.Test.Benchmark;
public class MathematicsBenchmark public unsafe class MathematicsBenchmark
{ {
[Params(10)] public struct f4
public int count = 10; {
private Vector128<float> _vec;
private unsafe static Vector128<float> CreateVector128(float2 value) public f4(float x, float y, float z, float w)
{ {
return Vector128.AsSingle(Sse2.LoadScalarVector128((double*)&value)); _vec = Vector128.Create(x, y, z, w);
} }
public f4(Vector128<float> vec)
{
_vec = vec;
}
public static f4 operator +(f4 a, f4 b)
{
var result = a._vec + b._vec;
return Unsafe.As<Vector128<float>, f4>(ref result);
}
}
[Params(100)]
public int count;
[Benchmark] [Benchmark]
public void Vector2Add() public Vector2 Vector2Add()
{ {
var a = new Vector2(1, 2); var a = new Vector2(1, 2);
var b = new Vector2(5, 6); var b = new Vector2(3, 4);
var result = new Vector2(); var c = new Vector2(5, 6);
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
result += a + b; c += a + b;
} }
return c;
} }
[Benchmark] [Benchmark]
public void Float2Add() public float2 Float2Add()
{ {
var a = new float2(1, 2); var a = new float2(1, 2);
var b = new float2(5, 6); var b = new float2(3, 4);
var result = new float2(); var c = new float2(5, 6);
//var vr = CreateVector128(result);
//var va = CreateVector128(a);
//var vb = CreateVector128(b);
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
result += a + b; c += a + b;
//vr = Sse.Add(va, vb);
}
} }
//[Benchmark] return c;
public void Vector4Add() }
[Benchmark]
public Vector4 Vector4Add()
{ {
var a = new Vector4(1, 2, 3, 4); var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(5, 6, 7, 8); var b = new Vector4(5, 6, 7, 8);
@@ -58,10 +73,12 @@ public class MathematicsBenchmark
{ {
result += a + b; result += a + b;
} }
return result;
} }
//[Benchmark] [Benchmark]
public void Float4Add() public float4 Float4Add()
{ {
var a = new float4(1, 2, 3, 4); var a = new float4(1, 2, 3, 4);
var b = new float4(5, 6, 7, 8); var b = new float4(5, 6, 7, 8);
@@ -71,5 +88,37 @@ public class MathematicsBenchmark
{ {
result += a + b; result += a + b;
} }
return result;
}
[Benchmark]
public f4 f4Add()
{
var a = new f4(1, 2, 3, 4);
var b = new f4(5, 6, 7, 8);
var result = new f4(0, 0, 0, 0);
for (var i = 0; i < count; i++)
{
result += a + b;
}
return result;
}
[Benchmark]
public unsafe Vector128<float> v128Add()
{
var a = Vector128.Create(1f, 2f, 3f, 4f);
var b = Vector128.Create(5f, 6f, 7f, 8f);
var result = Vector128<float>.Zero;
for (var i = 0; i < count; i++)
{
result += a + b;
}
return result;
} }
} }

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PublishAot>True</PublishAot> <PublishAot>True</PublishAot>

View File

@@ -18,9 +18,9 @@
//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.Buffer;
//using Misaki.HighPerformance.LowLevel.Collections; //using Misaki.HighPerformance.LowLevel.Collections;
@@ -39,3 +39,8 @@ BenchmarkDotNet.Running.BenchmarkRunner.Run<MathematicsBenchmark>();
// Console.WriteLine(item); // Console.WriteLine(item);
// } // }
//} //}
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
var array = new UnsafeArray<int>(10, Allocator.Persistent);

View File

@@ -130,7 +130,7 @@ public unsafe class TestJobSystem
{ {
const int size = 1000; const int size = 1000;
var result = stackalloc float[size]; var result = stackalloc float[size];
MemoryUtilities.MemSet(result, 0, sizeof(float) * size); MemoryUtility.MemSet(result, 0, sizeof(float) * size);
var job = new ParallelAddJob var job = new ParallelAddJob
{ {
value = 1.0f, value = 1.0f,

View File

@@ -42,6 +42,17 @@ namespace Misaki.HighPerformance.Buffer
Dispose(); Dispose();
} }
public T Rent()
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (_objects.TryDequeue(out var obj))
{
return obj;
}
return _factory();
}
public bool TryRent([MaybeNullWhen(false)] out T obj) public bool TryRent([MaybeNullWhen(false)] out T obj)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);

View File

@@ -5,6 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>True</IsPackable> <IsPackable>True</IsPackable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

View File

@@ -0,0 +1,46 @@
using System.Runtime.InteropServices;
namespace Misaki.HighPerformance.Utilities;
public static class CollectionUtility
{
/// <summary>
/// Creates a span over the elements of the specified list.
/// </summary>
/// <remarks>
/// The span will become invalid if the list is modified (e.g., elements are added or removed).
/// </remarks>
/// <typeparam name="T">The type of elements in the list.</typeparam>
/// <param name="list">The list whose elements the span will cover. Can be null.</param>
/// <returns>A span over the elements of the list, or an empty span if the list is null or empty.</returns>
public static Span<T> AsSpan<T>(this List<T>? list)
{
return CollectionsMarshal.AsSpan(list);
}
/// <summary>
/// Removes the element at the specified index from the list by replacing it with the last element, then removing
/// the last element. This operation does not preserve the order of elements.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
/// <param name="list">The list from which to remove the element. Cannot be null.</param>
/// <param name="index">The zero-based index of the element to remove. Must be within the bounds of the list.</param>
/// <returns>The modified list after the element has been removed.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown if index is less than 0 or greater than or equal to the number of elements in the list.</exception>
public static List<T> RemoveAndSwapBack<T>(this List<T> list, int index)
{
var lastIndex = list.Count - 1;
if (index < 0 || index > lastIndex)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
if (index != lastIndex)
{
list[index] = list[lastIndex];
}
list.RemoveAt(lastIndex);
return list;
}
}