From eeff3313b5c7bb4645d8a8dcc293a91b7c1c3b83 Mon Sep 17 00:00:00 2001 From: Misaki Date: Sat, 12 Jul 2025 19:48:42 +0900 Subject: [PATCH] Add image processing and memory management features Added new namespace `Misaki.HighPerformance.Image` for image processing, including classes for animated GIF handling and memory management. Added `AnimatedFrameResult` class for individual frames in animated images. Added `AnimatedGifEnumerator` class for enumerating frames in animated GIFs. Added `ColorComponents` enum for different color formats. Added `ImageInfo` struct for image dimensions and color components. Added `CRuntime` class for low-level memory management functions. Added `MemoryStats` class to track memory allocation statistics. Added utility functions for creating multi-dimensional arrays. Added new structures for fixed-size UTF-8 encoded strings. Added benchmarking classes to test new memory management features. Changed `StbImage.cs` to include new namespaces and functionality for image data manipulation. Changed project files to target .NET 9.0 and enable new features. Changed `Arena.cs` and `DynamicArena.cs` to use `nuint` for size parameters. Changed `BitSet.cs` to enhance bit manipulation methods. Changed `Program.cs` to run `FunctionPtrBenchmark` for performance testing. Removed memory tracking code from `AllocationManager.cs`, including the `_allocated` dictionary and related logic. Removed `Free` method from `IAllocator.cs` interface. Removed `UNSAFE_COLLECTION_CHECK` preprocessor directive from the codebase. Refactored various files to improve organization, moving from `Unsafe` to `LowLevel` namespace. Refactored `MemoryUtilities` class to include new memory operation methods. Refactored `UnsafeUtilities.cs` to support new collection structures. --- .../AnimatedFrameResult.cs | 12 + .../AnimatedGifEnumerator.cs | 149 ++ .../ColorComponents.cs | 16 + Misaki.HighPerformance.Image/ImageInfo.cs | 40 + Misaki.HighPerformance.Image/ImageResult.cs | 98 + .../ImageResultFloat.cs | 89 + .../Misaki.HighPerformance.Image.csproj | 27 + .../Runtime/CRuntime.cs | 186 ++ .../Runtime/MemoryStats.cs | 27 + .../Runtime/Utility.cs | 16 + .../StbImage.Generated.Bmp.cs | 494 ++++ .../StbImage.Generated.Common.cs | 770 ++++++ .../StbImage.Generated.Gif.cs | 545 +++++ .../StbImage.Generated.Hdr.cs | 388 +++ .../StbImage.Generated.Jpg.cs | 1741 ++++++++++++++ .../StbImage.Generated.Png.cs | 818 +++++++ .../StbImage.Generated.Psd.cs | 312 +++ .../StbImage.Generated.Tga.cs | 374 +++ .../StbImage.Generated.Zlib.cs | 595 +++++ Misaki.HighPerformance.Image/StbImage.cs | 95 + .../Misaki.HighPerformance.Jobs.csproj | 9 + .../WorkerThreadPool.cs | 6 + .../AssemblyInfo.cs | 2 + .../Buffer/AllocationManager.cs | 263 ++ .../Buffer/Arena .cs | 18 +- .../Buffer/DynamicArena.cs | 12 +- .../Buffer/FixedStackString.cs | 926 ++++++++ .../Buffer/FixedStackString.tt | 129 + .../Buffer/FixedString.cs | 894 +++++++ .../Buffer/FixedString.tt | 125 + .../Buffer/UnsafeArrayPool.cs | 4 +- .../Collections/AllocationOption.cs | 2 +- .../Collections/BitSet.cs | 711 ++++++ .../Contracts/IUnsafeCollection.cs | 2 +- .../Collections/Contracts/IUnsafeSet.cs | 5 + .../Collections/UnsafeArray.cs | 55 +- .../Collections/UnsafeHashMap.cs | 20 +- .../Collections/UnsafeHashSet.cs | 17 +- .../Collections/UnsafeList.cs | 12 +- .../Collections/UnsafeQueue.cs | 6 +- .../Collections/UnsafeStack.cs | 6 +- .../Contracts/IAllocator.cs | 52 + .../Exceptions/MemoryLeakException.cs | 28 +- .../FunctionPointer.cs | 34 + .../Helpers/HashMapHelper.cs | 46 +- .../Helpers/MemoryUtilities.Byte.cs | 352 +++ .../Helpers/MemoryUtilities.cs | 19 +- .../Helpers/UnsafeCollectionExtensions.cs | 88 +- .../Helpers/UnsafeUtilities.cs | 6 +- .../Misaki.HighPerformance.LowLevel.csproj | 50 + .../Misaki.HighPerformance.Mathematics.csproj | 51 + .../Utilities.ttinclude | 29 + .../Vectorlize.cs | 49 + .../float2.gen.cs | 272 +++ Misaki.HighPerformance.Mathematics/float2.tt | 161 ++ .../float3.gen.cs | 791 ++++++ Misaki.HighPerformance.Mathematics/float3.tt | 140 ++ .../float4.gen.cs | 2111 +++++++++++++++++ Misaki.HighPerformance.Mathematics/float4.tt | 140 ++ .../CollectionBenchmark.cs | 8 +- .../FunctionPtrBenchmark.cs | 47 + .../HashMapBenchmark.cs | 114 + Misaki.HighPerformance.Test/Jobs/NoiseJob.cs | 2 +- .../Misaki.HighPerformance.Test.csproj | 5 +- .../ParallelNoiseBenchmark.cs | 2 +- Misaki.HighPerformance.Test/Program.cs | 2 +- Misaki.HighPerformance.Unsafe/AssemblyInfo.cs | 2 - .../Buffer/AllocationHandler.cs | 32 - .../Buffer/AllocationManager.cs | 207 -- .../Collections/Contracts/IAllocator.cs | 13 - .../Misaki.HighPerformance.Unsafe.csproj | 26 - Misaki.HighPerformance.sln | 20 +- 72 files changed, 14444 insertions(+), 471 deletions(-) create mode 100644 Misaki.HighPerformance.Image/AnimatedFrameResult.cs create mode 100644 Misaki.HighPerformance.Image/AnimatedGifEnumerator.cs create mode 100644 Misaki.HighPerformance.Image/ColorComponents.cs create mode 100644 Misaki.HighPerformance.Image/ImageInfo.cs create mode 100644 Misaki.HighPerformance.Image/ImageResult.cs create mode 100644 Misaki.HighPerformance.Image/ImageResultFloat.cs create mode 100644 Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj create mode 100644 Misaki.HighPerformance.Image/Runtime/CRuntime.cs create mode 100644 Misaki.HighPerformance.Image/Runtime/MemoryStats.cs create mode 100644 Misaki.HighPerformance.Image/Runtime/Utility.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Bmp.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Common.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Gif.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Hdr.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Jpg.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Png.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Psd.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Tga.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.Generated.Zlib.cs create mode 100644 Misaki.HighPerformance.Image/StbImage.cs create mode 100644 Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj create mode 100644 Misaki.HighPerformance.Jobs/WorkerThreadPool.cs create mode 100644 Misaki.HighPerformance.LowLevel/AssemblyInfo.cs create mode 100644 Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Buffer/Arena .cs (83%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Buffer/DynamicArena.cs (90%) create mode 100644 Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.cs create mode 100644 Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.tt create mode 100644 Misaki.HighPerformance.LowLevel/Buffer/FixedString.cs create mode 100644 Misaki.HighPerformance.LowLevel/Buffer/FixedString.tt rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Buffer/UnsafeArrayPool.cs (78%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/AllocationOption.cs (95%) create mode 100644 Misaki.HighPerformance.LowLevel/Collections/BitSet.cs rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/Contracts/IUnsafeCollection.cs (94%) create mode 100644 Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeSet.cs rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/UnsafeArray.cs (78%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/UnsafeHashMap.cs (91%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/UnsafeHashSet.cs (86%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/UnsafeList.cs (96%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/UnsafeQueue.cs (96%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Collections/UnsafeStack.cs (92%) create mode 100644 Misaki.HighPerformance.LowLevel/Contracts/IAllocator.cs rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Exceptions/MemoryLeakException.cs (65%) create mode 100644 Misaki.HighPerformance.LowLevel/FunctionPointer.cs rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Helpers/HashMapHelper.cs (91%) create mode 100644 Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.Byte.cs rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Helpers/MemoryUtilities.cs (94%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Helpers/UnsafeCollectionExtensions.cs (64%) rename {Misaki.HighPerformance.Unsafe => Misaki.HighPerformance.LowLevel}/Helpers/UnsafeUtilities.cs (96%) create mode 100644 Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj create mode 100644 Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj create mode 100644 Misaki.HighPerformance.Mathematics/Utilities.ttinclude create mode 100644 Misaki.HighPerformance.Mathematics/Vectorlize.cs create mode 100644 Misaki.HighPerformance.Mathematics/float2.gen.cs create mode 100644 Misaki.HighPerformance.Mathematics/float2.tt create mode 100644 Misaki.HighPerformance.Mathematics/float3.gen.cs create mode 100644 Misaki.HighPerformance.Mathematics/float3.tt create mode 100644 Misaki.HighPerformance.Mathematics/float4.gen.cs create mode 100644 Misaki.HighPerformance.Mathematics/float4.tt create mode 100644 Misaki.HighPerformance.Test/FunctionPtrBenchmark.cs create mode 100644 Misaki.HighPerformance.Test/HashMapBenchmark.cs delete mode 100644 Misaki.HighPerformance.Unsafe/AssemblyInfo.cs delete mode 100644 Misaki.HighPerformance.Unsafe/Buffer/AllocationHandler.cs delete mode 100644 Misaki.HighPerformance.Unsafe/Buffer/AllocationManager.cs delete mode 100644 Misaki.HighPerformance.Unsafe/Collections/Contracts/IAllocator.cs delete mode 100644 Misaki.HighPerformance.Unsafe/Misaki.HighPerformance.Unsafe.csproj diff --git a/Misaki.HighPerformance.Image/AnimatedFrameResult.cs b/Misaki.HighPerformance.Image/AnimatedFrameResult.cs new file mode 100644 index 0000000..0055cd7 --- /dev/null +++ b/Misaki.HighPerformance.Image/AnimatedFrameResult.cs @@ -0,0 +1,12 @@ +namespace Misaki.HighPerformance.Image +{ +#if !STBSHARP_INTERNAL + public +#else + internal +#endif + class AnimatedFrameResult : ImageResult + { + public int DelayInMs { get; set; } + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/AnimatedGifEnumerator.cs b/Misaki.HighPerformance.Image/AnimatedGifEnumerator.cs new file mode 100644 index 0000000..a309cce --- /dev/null +++ b/Misaki.HighPerformance.Image/AnimatedGifEnumerator.cs @@ -0,0 +1,149 @@ +using Misaki.HighPerformance.Image.Runtime; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace Misaki.HighPerformance.Image +{ + internal class AnimatedGifEnumerator : IEnumerator + { + private readonly StbImage.stbi__context _context; + private StbImage.stbi__gif _gif; + private readonly ColorComponents _colorComponents; + + public AnimatedGifEnumerator(Stream input, ColorComponents colorComponents) + { + if (input == null) + throw new ArgumentNullException("input"); + + _context = new StbImage.stbi__context(input); + + if (StbImage.stbi__gif_test(_context) == 0) + throw new Exception("Input stream is not GIF file."); + + _gif = new StbImage.stbi__gif(); + _colorComponents = colorComponents; + } + + public ColorComponents ColorComponents + { + get + { + return _colorComponents; + } + } + + public AnimatedFrameResult Current + { + get; private set; + } + + object IEnumerator.Current + { + get + { + return Current; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public unsafe bool MoveNext() + { + // Read next frame + int ccomp; + byte two_back; + var result = StbImage.stbi__gif_load_next(_context, _gif, &ccomp, (int)ColorComponents, &two_back); + if (result == null) + return false; + + if (Current == null) + { + Current = new AnimatedFrameResult + { + Width = (uint)_gif.w, + Height = (uint)_gif.h, + SourceComponent = (ColorComponents)ccomp, + Component = ColorComponents == ColorComponents.Default ? (ColorComponents)ccomp : ColorComponents + }; + + Current.SetData(result); + } + + Current.DelayInMs = _gif.delay; + + return true; + } + + public void Reset() + { + throw new NotImplementedException(); + } + + ~AnimatedGifEnumerator() + { + Dispose(false); + } + + protected unsafe virtual void Dispose(bool disposing) + { + if (_gif != null) + { + if (_gif._out_ != null) + { + CRuntime.free(_gif._out_); + _gif._out_ = null; + } + + if (_gif.history != null) + { + CRuntime.free(_gif.history); + _gif.history = null; + } + + if (_gif.background != null) + { + CRuntime.free(_gif.background); + _gif.background = null; + } + + _gif = null; + } + } + } + + internal class AnimatedGifEnumerable : IEnumerable + { + private readonly Stream _input; + private readonly ColorComponents _colorComponents; + + public AnimatedGifEnumerable(Stream input, ColorComponents colorComponents) + { + _input = input; + _colorComponents = colorComponents; + } + + public ColorComponents ColorComponents + { + get + { + return _colorComponents; + } + } + + public IEnumerator GetEnumerator() + { + return new AnimatedGifEnumerator(_input, ColorComponents); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/ColorComponents.cs b/Misaki.HighPerformance.Image/ColorComponents.cs new file mode 100644 index 0000000..70401d9 --- /dev/null +++ b/Misaki.HighPerformance.Image/ColorComponents.cs @@ -0,0 +1,16 @@ +namespace Misaki.HighPerformance.Image +{ +#if !STBSHARP_INTERNAL + public +#else + internal +#endif + enum ColorComponents + { + Default, + R, + RA, + RGB, + RGBA + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/ImageInfo.cs b/Misaki.HighPerformance.Image/ImageInfo.cs new file mode 100644 index 0000000..14b852c --- /dev/null +++ b/Misaki.HighPerformance.Image/ImageInfo.cs @@ -0,0 +1,40 @@ +using System.IO; + +namespace Misaki.HighPerformance.Image +{ +#if !STBSHARP_INTERNAL + public +#else + internal +#endif + struct ImageInfo + { + public int Width; + public int Height; + public ColorComponents ColorComponents; + public int BitsPerChannel; + + public static unsafe ImageInfo? FromStream(Stream stream) + { + int width, height, comp; + var context = new StbImage.stbi__context(stream); + + var is16Bit = StbImage.stbi__is_16_main(context) == 1; + StbImage.stbi__rewind(context); + + var infoResult = StbImage.stbi__info_main(context, &width, &height, &comp); + StbImage.stbi__rewind(context); + + if (infoResult == 0) + return null; + + return new ImageInfo + { + Width = width, + Height = height, + ColorComponents = (ColorComponents)comp, + BitsPerChannel = is16Bit ? 16 : 8 + }; + } + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/ImageResult.cs b/Misaki.HighPerformance.Image/ImageResult.cs new file mode 100644 index 0000000..bddf05e --- /dev/null +++ b/Misaki.HighPerformance.Image/ImageResult.cs @@ -0,0 +1,98 @@ +using Misaki.HighPerformance.Image.Runtime; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Image; + +public unsafe class ImageResult : IDisposable +{ + private byte* _buffer; + + public uint Width + { + get; init; + } + + public uint Height + { + get; init; + } + + public ColorComponents SourceComponent + { + get; init; + } + + public ColorComponents Component + { + get; init; + } + + public Span Data => new(_buffer, (int)(Width * Height * (uint)Component)); + + internal void SetData(byte* data) + { + CRuntime.free(_buffer); + _buffer = data; + } + + internal static unsafe ImageResult FromResult(byte* result, uint width, uint height, ColorComponents comp, + ColorComponents req_comp) + { + if (result == null) + throw new InvalidOperationException(StbImage.stbi__g_failure_reason); + + var image = new ImageResult + { + Width = width, + Height = height, + SourceComponent = comp, + Component = req_comp == ColorComponents.Default ? comp : req_comp + }; + + image._buffer = result; + + return image; + } + + public static unsafe ImageResult FromStream(Stream stream, + ColorComponents requiredComponents = ColorComponents.Default) + { + int x, y, comp; + + var context = new StbImage.stbi__context(stream); + var result = StbImage.stbi__load_and_postprocess_8bit(context, &x, &y, &comp, (int)requiredComponents); + + return FromResult(result, (uint)x, (uint)y, (ColorComponents)comp, requiredComponents); + } + + public static ImageResult FromMemory(byte[] data, ColorComponents requiredComponents = ColorComponents.Default) + { + using var stream = new MemoryStream(data); + return FromStream(stream, requiredComponents); + } + + public static IEnumerable AnimatedGifFramesFromStream(Stream stream, ColorComponents requiredComponents = ColorComponents.Default) + { + return new AnimatedGifEnumerable(stream, requiredComponents); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePtr() + { + return _buffer; + } + + public void Dispose() + { + if (_buffer == null) + { + return; + } + + CRuntime.free(_buffer); + _buffer = null; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/ImageResultFloat.cs b/Misaki.HighPerformance.Image/ImageResultFloat.cs new file mode 100644 index 0000000..e0c7212 --- /dev/null +++ b/Misaki.HighPerformance.Image/ImageResultFloat.cs @@ -0,0 +1,89 @@ +using Misaki.HighPerformance.Image.Runtime; +using System; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Image; + +public unsafe class ImageResultFloat : IDisposable +{ + private float* _buffer; + + public int Width + { + get; init; + } + + public int Height + { + get; init; + } + + public ColorComponents SourceComponent + { + get; init; + } + + public ColorComponents Component + { + get; init; + } + + public Span Data => new(_buffer, (int)(Width * Height * (uint)Component)); + + internal static unsafe ImageResultFloat FromResult(float* result, int width, int height, ColorComponents comp, + ColorComponents req_comp) + { + if (result == null) + throw new InvalidOperationException(StbImage.stbi__g_failure_reason); + + var image = new ImageResultFloat + { + Width = width, + Height = height, + SourceComponent = comp, + Component = req_comp == ColorComponents.Default ? comp : req_comp + }; + + image._buffer = result; + + return image; + } + + public static unsafe ImageResultFloat FromStream(Stream stream, + ColorComponents requiredComponents = ColorComponents.Default) + { + int x, y, comp; + + var context = new StbImage.stbi__context(stream); + var result = StbImage.stbi__loadf_main(context, &x, &y, &comp, (int)requiredComponents); + + return FromResult(result, x, y, (ColorComponents)comp, requiredComponents); + } + + public static ImageResultFloat FromMemory(byte[] data, + ColorComponents requiredComponents = ColorComponents.Default) + { + using (var stream = new MemoryStream(data)) + { + return FromStream(stream, requiredComponents); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float* GetUnsafePtr() + { + return _buffer; + } + + public void Dispose() + { + if (_buffer == null) + { + return; + } + + CRuntime.free(_buffer); + _buffer = null; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj b/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj new file mode 100644 index 0000000..faf6f91 --- /dev/null +++ b/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj @@ -0,0 +1,27 @@ + + + StbImageSharpTeam + StbImageSharp + StbImageSharp + + C# port of the stb_image.h + Public Domain + 1.0.0 + true + net9.0 + + + + true + + + + True + 8 + + + + True + 8 + + \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/Runtime/CRuntime.cs b/Misaki.HighPerformance.Image/Runtime/CRuntime.cs new file mode 100644 index 0000000..8442403 --- /dev/null +++ b/Misaki.HighPerformance.Image/Runtime/CRuntime.cs @@ -0,0 +1,186 @@ +using System; +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.Image.Runtime +{ + internal static unsafe class CRuntime + { + private static readonly string numbers = "0123456789"; + + public static void* malloc(ulong size) + { + return malloc((long)size); + } + + public static void* malloc(long size) + { + var ptr = NativeMemory.Alloc((nuint)size); + + MemoryStats.Allocated(); + + return ptr; + } + + public static void free(void* ptr) + { + if (ptr == null) + return; + + NativeMemory.Free(ptr); + MemoryStats.Freed(); + } + + public static void memcpy(void* a, void* b, long size) + { + NativeMemory.Copy(b, a, (nuint)size); + } + + public static void memcpy(void* a, void* b, ulong size) + { + memcpy(a, b, (long)size); + } + + public static void memmove(void* a, void* b, long size) + { + void* temp = null; + + try + { + temp = malloc(size); + memcpy(temp, b, size); + memcpy(a, temp, size); + } + + finally + { + if (temp != null) + free(temp); + } + } + + public static int memcmp(void* a, void* b, long size) + { + var result = 0; + var ap = (byte*)a; + var bp = (byte*)b; + for (long i = 0; i < size; ++i) + { + if (*ap != *bp) + result += 1; + + ap++; + bp++; + } + + return result; + } + + public static void memset(void* ptr, int value, long size) + { + NativeMemory.Fill(ptr, (nuint)size, (byte)value); + } + + public static void memset(void* ptr, int value, ulong size) + { + memset(ptr, value, (long)size); + } + + public static uint _lrotl(uint x, int y) + { + return x << y | x >> 32 - y; + } + + public static void* realloc(void* ptr, long newSize) + { + if (ptr == null) + return malloc(newSize); + + var result = NativeMemory.Realloc(ptr, (nuint)newSize); + + return result; + } + + public static void* realloc(void* a, ulong newSize) + { + return realloc(a, (long)newSize); + } + + public static int abs(int v) + { + return Math.Abs(v); + } + + public static double pow(double a, double b) + { + return Math.Pow(a, b); + } + + public static double ldexp(double number, int exponent) + { + return number * Math.Pow(2, exponent); + } + + public static int strcmp(sbyte* src, string token) + { + var result = 0; + + for (var i = 0; i < token.Length; ++i) + { + if (src[i] != token[i]) + { + ++result; + } + } + + return result; + } + + public static int strncmp(sbyte* src, string token, ulong size) + { + var result = 0; + + for (var i = 0; i < Math.Min(token.Length, (int)size); ++i) + { + if (src[i] != token[i]) + { + ++result; + } + } + + return result; + } + + public static long strtol(sbyte* start, sbyte** end, int radix) + { + // First step - determine length + var length = 0; + var ptr = start; + while (numbers.IndexOf((char)*ptr) != -1) + { + ++ptr; + ++length; + } + + long result = 0; + + // Now build up the number + ptr = start; + while (length > 0) + { + long num = numbers.IndexOf((char)*ptr); + var pow = (long)Math.Pow(10, length - 1); + result += num * pow; + + ++ptr; + --length; + } + + if (end != null) + { + *end = ptr; + } + + return result; + } + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/Runtime/MemoryStats.cs b/Misaki.HighPerformance.Image/Runtime/MemoryStats.cs new file mode 100644 index 0000000..dc1c72e --- /dev/null +++ b/Misaki.HighPerformance.Image/Runtime/MemoryStats.cs @@ -0,0 +1,27 @@ +using System.Threading; + +namespace Misaki.HighPerformance.Image.Runtime +{ + internal unsafe static class MemoryStats + { + private static int _allocations; + + public static int Allocations + { + get + { + return _allocations; + } + } + + internal static void Allocated() + { + Interlocked.Increment(ref _allocations); + } + + internal static void Freed() + { + Interlocked.Decrement(ref _allocations); + } + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Image/Runtime/Utility.cs b/Misaki.HighPerformance.Image/Runtime/Utility.cs new file mode 100644 index 0000000..175cf2a --- /dev/null +++ b/Misaki.HighPerformance.Image/Runtime/Utility.cs @@ -0,0 +1,16 @@ +namespace Misaki.HighPerformance.Image.Runtime +{ + internal class Utility + { + public static T[][] CreateArray(int d1, int d2) + { + var result = new T[d1][]; + for (var i = 0; i < d1; i++) + { + result[i] = new T[d2]; + } + + return result; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Bmp.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Bmp.cs new file mode 100644 index 0000000..68fc6d6 --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Bmp.cs @@ -0,0 +1,494 @@ +// Generated by Sichem at 9/16/2024 9:09:30 AM + +using System.Runtime.InteropServices; +using Misaki.HighPerformance.Image.Runtime; +using Misaki.HighPerformance.Image; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public static int stbi__bmp_test(stbi__context s) + { + var r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; + } + + public static void* stbi__bmp_load(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri) + { + byte* _out_; + uint mr = 0; + uint mg = 0; + uint mb = 0; + uint ma = 0; + uint all_a = 0; + var pal = Utility.CreateArray(256, 4); + var psize = 0; + var i = 0; + var j = 0; + var width = 0; + var flip_vertically = 0; + var pad = 0; + var target = 0; + var info = new stbi__bmp_data(); + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == null) + return null; + flip_vertically = (int)s.img_y > 0 ? 1 : 0; + s.img_y = (uint)CRuntime.abs((int)s.img_y); + if (s.img_y > 1 << 24) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + if (s.img_x > 1 << 24) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + if (info.hsz == 12) + { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } + else + { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + + if (psize == 0) + { + var bytes_read_so_far = (int)s.Stream.Position; + var header_limit = 1024; + var extra_data_limit = 256 * 4; + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) + return (byte*)(ulong)(stbi__err("bad header") != 0 ? 0 : 0); + + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) + return (byte*)(ulong)(stbi__err("bad offset") != 0 ? 0 : 0); + stbi__skip(s, info.offset - bytes_read_so_far); + } + + if (info.bpp == 24 && ma == 0xff000000) + s.img_n = 3; + else + s.img_n = ma != 0 ? 4 : 3; + if (req_comp != 0 && req_comp >= 3) + target = req_comp; + else + target = s.img_n; + if (stbi__mad3sizes_valid(target, (int)s.img_x, (int)s.img_y, 0) == 0) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + _out_ = (byte*)stbi__malloc_mad3(target, (int)s.img_x, (int)s.img_y, 0); + if (_out_ == null) + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + if (info.bpp < 16) + { + var z = 0; + if (psize == 0 || psize > 256) + { + CRuntime.free(_out_); + return (byte*)(ulong)(stbi__err("invalid") != 0 ? 0 : 0); + } + + for (i = 0; i < psize; ++i) + { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) + stbi__get8(s); + pal[i][3] = 255; + } + + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) + { + width = (int)((s.img_x + 7) >> 3); + } + else if (info.bpp == 4) + { + width = (int)((s.img_x + 1) >> 1); + } + else if (info.bpp == 8) + { + width = (int)s.img_x; + } + else + { + CRuntime.free(_out_); + return (byte*)(ulong)(stbi__err("bad bpp") != 0 ? 0 : 0); + } + + pad = -width & 3; + if (info.bpp == 1) + for (j = 0; j < (int)s.img_y; ++j) + { + var bit_offset = 7; + int v = stbi__get8(s); + for (i = 0; i < (int)s.img_x; ++i) + { + var color = (v >> bit_offset) & 0x1; + _out_[z++] = pal[color][0]; + _out_[z++] = pal[color][1]; + _out_[z++] = pal[color][2]; + if (target == 4) + _out_[z++] = 255; + if (i + 1 == (int)s.img_x) + break; + if (--bit_offset < 0) + { + bit_offset = 7; + v = stbi__get8(s); + } + } + + stbi__skip(s, pad); + } + else + for (j = 0; j < (int)s.img_y; ++j) + { + for (i = 0; i < (int)s.img_x; i += 2) + { + int v = stbi__get8(s); + var v2 = 0; + if (info.bpp == 4) + { + v2 = v & 15; + v >>= 4; + } + + _out_[z++] = pal[v][0]; + _out_[z++] = pal[v][1]; + _out_[z++] = pal[v][2]; + if (target == 4) + _out_[z++] = 255; + if (i + 1 == (int)s.img_x) + break; + v = info.bpp == 8 ? stbi__get8(s) : v2; + _out_[z++] = pal[v][0]; + _out_[z++] = pal[v][1]; + _out_[z++] = pal[v][2]; + if (target == 4) + _out_[z++] = 255; + } + + stbi__skip(s, pad); + } + } + else + { + var rshift = 0; + var gshift = 0; + var bshift = 0; + var ashift = 0; + var rcount = 0; + var gcount = 0; + var bcount = 0; + var acount = 0; + var z = 0; + var easy = 0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) + width = (int)(3 * s.img_x); + else if (info.bpp == 16) + width = (int)(2 * s.img_x); + else + width = 0; + pad = -width & 3; + if (info.bpp == 24) + easy = 1; + else if (info.bpp == 32) + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + + if (easy == 0) + { + if (mr == 0 || mg == 0 || mb == 0) + { + CRuntime.free(_out_); + return (byte*)(ulong)(stbi__err("bad masks") != 0 ? 0 : 0); + } + + rshift = stbi__high_bit(mr) - 7; + rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg) - 7; + gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb) - 7; + bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma) - 7; + acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) + { + CRuntime.free(_out_); + return (byte*)(ulong)(stbi__err("bad masks") != 0 ? 0 : 0); + } + } + + for (j = 0; j < (int)s.img_y; ++j) + { + if (easy != 0) + { + for (i = 0; i < (int)s.img_x; ++i) + { + byte a = 0; + _out_[z + 2] = stbi__get8(s); + _out_[z + 1] = stbi__get8(s); + _out_[z + 0] = stbi__get8(s); + z += 3; + a = (byte)(easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) + _out_[z++] = a; + } + } + else + { + var bpp = info.bpp; + for (i = 0; i < (int)s.img_x; ++i) + { + var v = bpp == 16 ? (uint)stbi__get16le(s) : stbi__get32le(s); + uint a = 0; + _out_[z++] = (byte)(stbi__shiftsigned(v & mr, rshift, rcount) & 255); + _out_[z++] = (byte)(stbi__shiftsigned(v & mg, gshift, gcount) & 255); + _out_[z++] = (byte)(stbi__shiftsigned(v & mb, bshift, bcount) & 255); + a = (uint)(ma != 0 ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) + _out_[z++] = (byte)(a & 255); + } + } + + stbi__skip(s, pad); + } + } + + if (target == 4 && all_a == 0) + for (i = (int)(4 * s.img_x * s.img_y - 1); i >= 0; i -= 4) + _out_[i] = 255; + + if (flip_vertically != 0) + { + byte t = 0; + for (j = 0; j < (int)s.img_y >> 1; ++j) + { + var p1 = _out_ + j * s.img_x * target; + var p2 = _out_ + (s.img_y - 1 - j) * s.img_x * target; + for (i = 0; i < (int)s.img_x * target; ++i) + { + t = p1[i]; + p1[i] = p2[i]; + p2[i] = t; + } + } + } + + if (req_comp != 0 && req_comp != target) + { + _out_ = stbi__convert_format(_out_, target, req_comp, s.img_x, s.img_y); + if (_out_ == null) + return _out_; + } + + *x = (int)s.img_x; + *y = (int)s.img_y; + if (comp != null) + *comp = s.img_n; + return _out_; + } + + public static int stbi__bmp_info(stbi__context s, int* x, int* y, int* comp) + { + void* p; + var info = new stbi__bmp_data(); + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == null) + { + stbi__rewind(s); + return 0; + } + + if (x != null) + *x = (int)s.img_x; + if (y != null) + *y = (int)s.img_y; + if (comp != null) + { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma != 0 ? 4 : 3; + } + + return 1; + } + + public static int stbi__bmp_test_raw(stbi__context s) + { + var r = 0; + var sz = 0; + if (stbi__get8(s) != 66) + return 0; + if (stbi__get8(s) != 77) + return 0; + stbi__get32le(s); + stbi__get16le(s); + stbi__get16le(s); + stbi__get32le(s); + sz = (int)stbi__get32le(s); + r = sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124 ? 1 : 0; + return r; + } + + public static int stbi__bmp_set_mask_defaults(stbi__bmp_data* info, int compress) + { + if (compress == 3) + return 1; + if (compress == 0) + { + if (info->bpp == 16) + { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + else if (info->bpp == 32) + { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; + } + else + { + info->mr = info->mg = info->mb = info->ma = 0; + } + + return 1; + } + + return 0; + } + + public static void* stbi__bmp_parse_header(stbi__context s, stbi__bmp_data* info) + { + var hsz = 0; + if (stbi__get8(s) != 66 || stbi__get8(s) != 77) + return (byte*)(ulong)(stbi__err("not BMP") != 0 ? 0 : 0); + stbi__get32le(s); + stbi__get16le(s); + stbi__get16le(s); + info->offset = (int)stbi__get32le(s); + info->hsz = hsz = (int)stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + if (info->offset < 0) + return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) + return (byte*)(ulong)(stbi__err("unknown BMP") != 0 ? 0 : 0); + if (hsz == 12) + { + s.img_x = (uint)stbi__get16le(s); + s.img_y = (uint)stbi__get16le(s); + } + else + { + s.img_x = stbi__get32le(s); + s.img_y = stbi__get32le(s); + } + + if (stbi__get16le(s) != 1) + return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); + info->bpp = stbi__get16le(s); + if (hsz != 12) + { + var compress = (int)stbi__get32le(s); + if (compress == 1 || compress == 2) + return (byte*)(ulong)(stbi__err("BMP RLE") != 0 ? 0 : 0); + if (compress >= 4) + return (byte*)(ulong)(stbi__err("BMP JPEG/PNG") != 0 ? 0 : 0); + if (compress == 3 && info->bpp != 16 && info->bpp != 32) + return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + if (hsz == 40 || hsz == 56) + { + if (hsz == 56) + { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + + if (info->bpp == 16 || info->bpp == 32) + { + if (compress == 0) + { + stbi__bmp_set_mask_defaults(info, compress); + } + else if (compress == 3) + { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + if (info->mr == info->mg && info->mg == info->mb) + return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); + } + else + { + return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); + } + } + } + else + { + var i = 0; + if (hsz != 108 && hsz != 124) + return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); + for (i = 0; i < 12; ++i) + stbi__get32le(s); + + if (hsz == 124) + { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + } + } + + return (void*)1; + } + + [StructLayout(LayoutKind.Sequential)] + public struct stbi__bmp_data + { + public int bpp; + public int offset; + public int hsz; + public uint mr; + public uint mg; + public uint mb; + public uint ma; + public uint all_a; + public int extra_read; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Common.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Common.cs new file mode 100644 index 0000000..a9ba723 --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Common.cs @@ -0,0 +1,770 @@ +// Generated by Sichem at 9/16/2024 9:09:30 AM + +using Misaki.HighPerformance.Image.Runtime; +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public const int STBI_default = 0; + public const int STBI_grey = 1; + public const int STBI_grey_alpha = 2; + public const int STBI_rgb = 3; + public const int STBI_rgb_alpha = 4; + public const int STBI_ORDER_RGB = 0; + public const int STBI_ORDER_BGR = 1; + public const int STBI__SCAN_load = 0; + public const int STBI__SCAN_type = 1; + public const int STBI__SCAN_header = 2; + public static int stbi__vertically_flip_on_load_global; + public static int stbi__vertically_flip_on_load_local; + public static int stbi__vertically_flip_on_load_set; + public static float stbi__l2h_gamma = 2.2f; + public static float stbi__l2h_scale = 1.0f; + public static float stbi__h2l_gamma_i = 1.0f / 2.2f; + public static float stbi__h2l_scale_i = 1.0f; + public static int stbi__unpremultiply_on_load_global; + public static int stbi__de_iphone_flag_global; + public static int stbi__unpremultiply_on_load_local; + public static int stbi__unpremultiply_on_load_set; + public static int stbi__de_iphone_flag_local; + public static int stbi__de_iphone_flag_set; + public static byte[] stbi__process_marker_tag = { 65, 100, 111, 98, 101, 0 }; + public static byte[] stbi__process_frame_header_rgb = { 82, 71, 66 }; + + public static byte[] stbi__compute_huffman_codes_length_dezigzag = + { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + public static int[] stbi__shiftsigned_mul_table = { 0, 0xff, 0x55, 0x49, 0x11, 0x21, 0x41, 0x81, 0x01 }; + public static int[] stbi__shiftsigned_shift_table = { 0, 0, 0, 1, 0, 2, 4, 6, 0 }; + + public static void stbi_hdr_to_ldr_gamma(float gamma) + { + stbi__h2l_gamma_i = 1 / gamma; + } + + public static void stbi_hdr_to_ldr_scale(float scale) + { + stbi__h2l_scale_i = 1 / scale; + } + + public static void stbi_ldr_to_hdr_gamma(float gamma) + { + stbi__l2h_gamma = gamma; + } + + public static void stbi_ldr_to_hdr_scale(float scale) + { + stbi__l2h_scale = scale; + } + + public static void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) + { + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; + } + + public static void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) + { + stbi__de_iphone_flag_global = flag_true_if_should_convert; + } + + public static void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) + { + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; + } + + public static void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) + { + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; + } + + public static void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) + { + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; + } + + public static void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) + { + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; + } + + public static void* stbi__malloc(ulong size) + { + return CRuntime.malloc(size); + } + + public static int stbi__addsizes_valid(int a, int b) + { + if (b < 0) + return 0; + return a <= 2147483647 - b ? 1 : 0; + } + + public static int stbi__mul2sizes_valid(int a, int b) + { + if (a < 0 || b < 0) + return 0; + if (b == 0) + return 1; + return a <= 2147483647 / b ? 1 : 0; + } + + public static int stbi__mad2sizes_valid(int a, int b, int add) + { + return stbi__mul2sizes_valid(a, b) != 0 && stbi__addsizes_valid(a * b, add) != 0 ? 1 : 0; + } + + public static int stbi__mad3sizes_valid(int a, int b, int c, int add) + { + return stbi__mul2sizes_valid(a, b) != 0 && stbi__mul2sizes_valid(a * b, c) != 0 && + stbi__addsizes_valid(a * b * c, add) != 0 + ? 1 + : 0; + } + + public static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) + { + return stbi__mul2sizes_valid(a, b) != 0 && stbi__mul2sizes_valid(a * b, c) != 0 && + stbi__mul2sizes_valid(a * b * c, d) != 0 && stbi__addsizes_valid(a * b * c * d, add) != 0 + ? 1 + : 0; + } + + public static void* stbi__malloc_mad2(int a, int b, int add) + { + if (stbi__mad2sizes_valid(a, b, add) == 0) + return null; + return stbi__malloc((ulong)(a * b + add)); + } + + public static void* stbi__malloc_mad3(int a, int b, int c, int add) + { + if (stbi__mad3sizes_valid(a, b, c, add) == 0) + return null; + return stbi__malloc((ulong)(a * b * c + add)); + } + + public static void* stbi__malloc_mad4(int a, int b, int c, int d, int add) + { + if (stbi__mad4sizes_valid(a, b, c, d, add) == 0) + return null; + return stbi__malloc((ulong)(a * b * c * d + add)); + } + + public static int stbi__addints_valid(int a, int b) + { + if (a >= 0 != b >= 0) + return 1; + if (a < 0 && b < 0) + return a >= -2147483647 - 1 - b ? 1 : 0; + return a <= 2147483647 - b ? 1 : 0; + } + + public static int stbi__mul2shorts_valid(int a, int b) + { + if (b == 0 || b == -1) + return 1; + if (a >= 0 == b >= 0) + return a <= 32767 / b ? 1 : 0; + if (b < 0) + return a <= -32768 / b ? 1 : 0; + return a >= -32768 / b ? 1 : 0; + } + + public static float* stbi__ldr_to_hdr(byte* data, int x, int y, int comp) + { + var i = 0; + var k = 0; + var n = 0; + float* output; + if (data == null) + return null; + output = (float*)stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == null) + { + CRuntime.free(data); + return (float*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + if ((comp & 1) != 0) + n = comp; + else + n = comp - 1; + for (i = 0; i < x * y; ++i) + for (k = 0; k < n; ++k) + output[i * comp + k] = + (float)(CRuntime.pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + + if (n < comp) + for (i = 0; i < x * y; ++i) + output[i * comp + n] = data[i * comp + n] / 255.0f; + + CRuntime.free(data); + return output; + } + + public static void* stbi__load_main(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri, int bpc) + { + CRuntime.memset(ri, 0, (ulong)sizeof(stbi__result_info)); + ri->bits_per_channel = 8; + ri->channel_order = STBI_ORDER_RGB; + ri->num_channels = 0; + if (stbi__png_test(s) != 0) + return stbi__png_load(s, x, y, comp, req_comp, ri); + if (stbi__bmp_test(s) != 0) + return stbi__bmp_load(s, x, y, comp, req_comp, ri); + if (stbi__gif_test(s) != 0) + return stbi__gif_load(s, x, y, comp, req_comp, ri); + if (stbi__psd_test(s) != 0) + return stbi__psd_load(s, x, y, comp, req_comp, ri, bpc); + if (stbi__jpeg_test(s) != 0) + return stbi__jpeg_load(s, x, y, comp, req_comp, ri); + if (stbi__hdr_test(s) != 0) + { + var hdr = stbi__hdr_load(s, x, y, comp, req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp != 0 ? req_comp : *comp); + } + + if (stbi__tga_test(s) != 0) + return stbi__tga_load(s, x, y, comp, req_comp, ri); + return (byte*)(ulong)(stbi__err("unknown image type") != 0 ? 0 : 0); + } + + public static byte* stbi__convert_16_to_8(ushort* orig, int w, int h, int channels) + { + var i = 0; + var img_len = w * h * channels; + byte* reduced; + reduced = (byte*)stbi__malloc((ulong)img_len); + if (reduced == null) + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + for (i = 0; i < img_len; ++i) + reduced[i] = (byte)((orig[i] >> 8) & 0xFF); + + CRuntime.free(orig); + return reduced; + } + + public static ushort* stbi__convert_8_to_16(byte* orig, int w, int h, int channels) + { + var i = 0; + var img_len = w * h * channels; + ushort* enlarged; + enlarged = (ushort*)stbi__malloc((ulong)(img_len * 2)); + if (enlarged == null) + return (ushort*)(byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + for (i = 0; i < img_len; ++i) + enlarged[i] = (ushort)((orig[i] << 8) + orig[i]); + + CRuntime.free(orig); + return enlarged; + } + + public static void stbi__vertical_flip(void* image, int w, int h, int bytes_per_pixel) + { + var row = 0; + var bytes_per_row = w * bytes_per_pixel; + var temp = stackalloc byte[2048]; + var bytes = (byte*)image; + for (row = 0; row < h >> 1; row++) + { + var row0 = bytes + row * bytes_per_row; + var row1 = bytes + (h - row - 1) * bytes_per_row; + var bytes_left = (ulong)bytes_per_row; + while (bytes_left != 0) + { + var bytes_copy = bytes_left < 2048 * sizeof(byte) ? bytes_left : 2048 * sizeof(byte); + CRuntime.memcpy(temp, row0, bytes_copy); + CRuntime.memcpy(row0, row1, bytes_copy); + CRuntime.memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } + } + + public static void stbi__vertical_flip_slices(void* image, int w, int h, int z, int bytes_per_pixel) + { + var slice = 0; + var slice_size = w * h * bytes_per_pixel; + var bytes = (byte*)image; + for (slice = 0; slice < z; ++slice) + { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } + } + + public static byte* stbi__load_and_postprocess_8bit(stbi__context s, int* x, int* y, int* comp, int req_comp) + { + var ri = new stbi__result_info(); + var result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + if (result == null) + return null; + if (ri.bits_per_channel != 8) + { + result = stbi__convert_16_to_8((ushort*)result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + if ((stbi__vertically_flip_on_load_set != 0 + ? stbi__vertically_flip_on_load_local + : stbi__vertically_flip_on_load_global) != 0) + { + var channels = req_comp != 0 ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(byte)); + } + + return (byte*)result; + } + + public static ushort* stbi__load_and_postprocess_16bit(stbi__context s, int* x, int* y, int* comp, int req_comp) + { + var ri = new stbi__result_info(); + var result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + if (result == null) + return null; + if (ri.bits_per_channel != 16) + { + result = stbi__convert_8_to_16((byte*)result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + if ((stbi__vertically_flip_on_load_set != 0 + ? stbi__vertically_flip_on_load_local + : stbi__vertically_flip_on_load_global) != 0) + { + var channels = req_comp != 0 ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(ushort)); + } + + return (ushort*)result; + } + + public static void stbi__float_postprocess(float* result, int* x, int* y, int* comp, int req_comp) + { + if ((stbi__vertically_flip_on_load_set != 0 + ? stbi__vertically_flip_on_load_local + : stbi__vertically_flip_on_load_global) != 0 && result != null) + { + var channels = req_comp != 0 ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } + } + + public static float* stbi__loadf_main(stbi__context s, int* x, int* y, int* comp, int req_comp) + { + byte* data; + if (stbi__hdr_test(s) != 0) + { + var ri = new stbi__result_info(); + var hdr_data = stbi__hdr_load(s, x, y, comp, req_comp, &ri); + if (hdr_data != null) + stbi__float_postprocess(hdr_data, x, y, comp, req_comp); + return hdr_data; + } + + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data != null) + return stbi__ldr_to_hdr(data, *x, *y, req_comp != 0 ? req_comp : *comp); + return (float*)(ulong)(stbi__err("unknown image type") != 0 ? 0 : 0); + } + + public static int stbi__get16be(stbi__context s) + { + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); + } + + public static uint stbi__get32be(stbi__context s) + { + var z = (uint)stbi__get16be(s); + return (uint)((z << 16) + stbi__get16be(s)); + } + + public static int stbi__get16le(stbi__context s) + { + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); + } + + public static uint stbi__get32le(stbi__context s) + { + var z = (uint)stbi__get16le(s); + z += (uint)stbi__get16le(s) << 16; + return z; + } + + public static byte stbi__compute_y(int r, int g, int b) + { + return (byte)((r * 77 + g * 150 + 29 * b) >> 8); + } + + public static byte* stbi__convert_format(byte* data, int img_n, int req_comp, uint x, uint y) + { + var i = 0; + var j = 0; + byte* good; + if (req_comp == img_n) + return data; + good = (byte*)stbi__malloc_mad3(req_comp, (int)x, (int)y, 0); + if (good == null) + { + CRuntime.free(data); + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + for (j = 0; j < (int)y; ++j) + { + var src = data + j * x * img_n; + var dest = good + j * x * req_comp; + switch (img_n * 8 + req_comp) + { + case 1 * 8 + 2: + for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 2) + { + dest[0] = src[0]; + dest[1] = 255; + } + + break; + case 1 * 8 + 3: + for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 3) + dest[0] = dest[1] = dest[2] = src[0]; + + break; + case 1 * 8 + 4: + for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 4) + { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = 255; + } + + break; + case 2 * 8 + 1: + for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 1) + dest[0] = src[0]; + + break; + case 2 * 8 + 3: + for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 3) + dest[0] = dest[1] = dest[2] = src[0]; + + break; + case 2 * 8 + 4: + for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 4) + { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = src[1]; + } + + break; + case 3 * 8 + 4: + for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 4) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 255; + } + + break; + case 3 * 8 + 1: + for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 1) + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + + break; + case 3 * 8 + 2: + for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 2) + { + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + dest[1] = 255; + } + + break; + case 4 * 8 + 1: + for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 1) + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + + break; + case 4 * 8 + 2: + for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 2) + { + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + dest[1] = src[3]; + } + + break; + case 4 * 8 + 3: + for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 3) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + } + + break; + default: + ; + CRuntime.free(data); + CRuntime.free(good); + return (byte*)(ulong)(stbi__err("unsupported") != 0 ? 0 : 0); + } + } + + CRuntime.free(data); + return good; + } + + public static ushort stbi__compute_y_16(int r, int g, int b) + { + return (ushort)((r * 77 + g * 150 + 29 * b) >> 8); + } + + public static ushort* stbi__convert_format16(ushort* data, int img_n, int req_comp, uint x, uint y) + { + var i = 0; + var j = 0; + ushort* good; + if (req_comp == img_n) + return data; + good = (ushort*)stbi__malloc((ulong)(req_comp * x * y * 2)); + if (good == null) + { + CRuntime.free(data); + return (ushort*)(byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + for (j = 0; j < (int)y; ++j) + { + var src = data + j * x * img_n; + var dest = good + j * x * req_comp; + switch (img_n * 8 + req_comp) + { + case 1 * 8 + 2: + for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 2) + { + dest[0] = src[0]; + dest[1] = 0xffff; + } + + break; + case 1 * 8 + 3: + for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 3) + dest[0] = dest[1] = dest[2] = src[0]; + + break; + case 1 * 8 + 4: + for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 4) + { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = 0xffff; + } + + break; + case 2 * 8 + 1: + for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 1) + dest[0] = src[0]; + + break; + case 2 * 8 + 3: + for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 3) + dest[0] = dest[1] = dest[2] = src[0]; + + break; + case 2 * 8 + 4: + for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 4) + { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = src[1]; + } + + break; + case 3 * 8 + 4: + for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 4) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 0xffff; + } + + break; + case 3 * 8 + 1: + for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 1) + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + + break; + case 3 * 8 + 2: + for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 2) + { + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + dest[1] = 0xffff; + } + + break; + case 4 * 8 + 1: + for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 1) + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + + break; + case 4 * 8 + 2: + for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 2) + { + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + dest[1] = src[3]; + } + + break; + case 4 * 8 + 3: + for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 3) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + } + + break; + default: + ; + CRuntime.free(data); + CRuntime.free(good); + return (ushort*)(byte*)(ulong)(stbi__err("unsupported") != 0 ? 0 : 0); + } + } + + CRuntime.free(data); + return good; + } + + public static byte stbi__clamp(int x) + { + if ((uint)x > 255) + { + if (x < 0) + return 0; + if (x > 255) + return 255; + } + + return (byte)x; + } + + public static byte stbi__blinn_8x8(byte x, byte y) + { + var t = (uint)(x * y + 128); + return (byte)((t + (t >> 8)) >> 8); + } + + public static int stbi__bitreverse16(int n) + { + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; + } + + public static int stbi__bit_reverse(int v, int bits) + { + return stbi__bitreverse16(v) >> (16 - bits); + } + + public static int stbi__high_bit(uint z) + { + var n = 0; + if (z == 0) + return -1; + if (z >= 0x10000) + { + n += 16; + z >>= 16; + } + + if (z >= 0x00100) + { + n += 8; + z >>= 8; + } + + if (z >= 0x00010) + { + n += 4; + z >>= 4; + } + + if (z >= 0x00004) + { + n += 2; + z >>= 2; + } + + if (z >= 0x00002) + n += 1; + + return n; + } + + public static int stbi__bitcount(uint a) + { + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); + a = (a + (a >> 4)) & 0x0f0f0f0f; + a = a + (a >> 8); + a = a + (a >> 16); + return (int)(a & 0xff); + } + + public static int stbi__shiftsigned(uint v, int shift, int bits) + { + if (shift < 0) + v <<= -shift; + else + v >>= shift; + v >>= 8 - bits; + return (int)(v * stbi__shiftsigned_mul_table[bits]) >> stbi__shiftsigned_shift_table[bits]; + } + + public static int stbi__info_main(stbi__context s, int* x, int* y, int* comp) + { + if (stbi__jpeg_info(s, x, y, comp) != 0) + return 1; + if (stbi__png_info(s, x, y, comp) != 0) + return 1; + if (stbi__gif_info(s, x, y, comp) != 0) + return 1; + if (stbi__bmp_info(s, x, y, comp) != 0) + return 1; + if (stbi__psd_info(s, x, y, comp) != 0) + return 1; + if (stbi__hdr_info(s, x, y, comp) != 0) + return 1; + if (stbi__tga_info(s, x, y, comp) != 0) + return 1; + return stbi__err("unknown image type"); + } + + public static int stbi__is_16_main(stbi__context s) + { + if (stbi__png_is16(s) != 0) + return 1; + if (stbi__psd_is16(s) != 0) + return 1; + return 0; + } + + [StructLayout(LayoutKind.Sequential)] + public struct stbi__result_info + { + public int bits_per_channel; + public int num_channels; + public int channel_order; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Gif.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Gif.cs new file mode 100644 index 0000000..29be92a --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Gif.cs @@ -0,0 +1,545 @@ +// Generated by Sichem at 12/24/2021 8:28:15 PM + +using System.Runtime.InteropServices; +using Misaki.HighPerformance.Image.Runtime; +using Misaki.HighPerformance.Image; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public static int stbi__gif_test(stbi__context s) + { + var r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; + } + + public static void* stbi__gif_load(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri) + { + byte* u = null; + var g = new stbi__gif(); + u = stbi__gif_load_next(s, g, comp, req_comp, null); + if (u != null) + { + *x = g.w; + *y = g.h; + if (req_comp != 0 && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, (uint)g.w, (uint)g.h); + } + else if (g._out_ != null) + { + CRuntime.free(g._out_); + } + + CRuntime.free(g.history); + CRuntime.free(g.background); + return u; + } + + public static void* stbi__load_gif_main(stbi__context s, int** delays, int* x, int* y, int* z, int* comp, + int req_comp) + { + if (stbi__gif_test(s) != 0) + { + var layers = 0; + byte* u = null; + byte* _out_ = null; + byte* two_back = null; + var g = new stbi__gif(); + var stride = 0; + var out_size = 0; + var delays_size = 0; + if (delays != null) + *delays = null; + do + { + u = stbi__gif_load_next(s, g, comp, req_comp, two_back); + if (u != null) + { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + if (_out_ != null) + { + void* tmp = (byte*)CRuntime.realloc(_out_, (ulong)(layers * stride)); + if (tmp == null) + return stbi__load_gif_main_outofmem(g, _out_, delays); + _out_ = (byte*)tmp; + out_size = layers * stride; + if (delays != null) + { + var new_delays = (int*)CRuntime.realloc(*delays, (ulong)(sizeof(int) * layers)); + if (new_delays == null) + return stbi__load_gif_main_outofmem(g, _out_, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } + else + { + _out_ = (byte*)stbi__malloc((ulong)(layers * stride)); + if (_out_ == null) + return stbi__load_gif_main_outofmem(g, _out_, delays); + out_size = layers * stride; + if (delays != null) + { + *delays = (int*)stbi__malloc((ulong)(layers * sizeof(int))); + if (*delays == null) + return stbi__load_gif_main_outofmem(g, _out_, delays); + delays_size = layers * sizeof(int); + } + } + + CRuntime.memcpy(_out_ + (layers - 1) * stride, u, (ulong)stride); + if (layers >= 2) + two_back = _out_ - 2 * stride; + if (delays != null) + (*delays)[layers - 1U] = g.delay; + } + } while (u != null); + + CRuntime.free(g._out_); + CRuntime.free(g.history); + CRuntime.free(g.background); + if (req_comp != 0 && req_comp != 4) + _out_ = stbi__convert_format(_out_, 4, req_comp, (uint)(layers * g.w), (uint)g.h); + *z = layers; + return _out_; + } + + return (byte*)(ulong)(stbi__err("not GIF") != 0 ? 0 : 0); + } + + public static int stbi__gif_info(stbi__context s, int* x, int* y, int* comp) + { + return stbi__gif_info_raw(s, x, y, comp); + } + + public static int stbi__gif_test_raw(stbi__context s) + { + var sz = 0; + if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56) + return 0; + sz = stbi__get8(s); + if (sz != 57 && sz != 55) + return 0; + if (stbi__get8(s) != 97) + return 0; + return 1; + } + + public static void stbi__gif_parse_colortable(stbi__context s, byte[][] pal, int num_entries, int transp) + { + var i = 0; + for (i = 0; i < num_entries; ++i) + { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = (byte)(transp == i ? 0 : 255); + } + } + + public static int stbi__gif_header(stbi__context s, stbi__gif g, int* comp, int is_info) + { + byte version = 0; + if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56) + return stbi__err("not GIF"); + version = stbi__get8(s); + if (version != 55 && version != 57) + return stbi__err("not GIF"); + if (stbi__get8(s) != 97) + return stbi__err("not GIF"); + stbi__g_failure_reason = ""; + g.w = stbi__get16le(s); + g.h = stbi__get16le(s); + g.flags = stbi__get8(s); + g.bgindex = stbi__get8(s); + g.ratio = stbi__get8(s); + g.transparent = -1; + if (g.w > 1 << 24) + return stbi__err("too large"); + if (g.h > 1 << 24) + return stbi__err("too large"); + if (comp != null) + *comp = 4; + if (is_info != 0) + return 1; + if ((g.flags & 0x80) != 0) + stbi__gif_parse_colortable(s, g.pal, 2 << (g.flags & 7), -1); + return 1; + } + + public static int stbi__gif_info_raw(stbi__context s, int* x, int* y, int* comp) + { + var g = new stbi__gif(); + if (g == null) + return stbi__err("outofmem"); + if (stbi__gif_header(s, g, comp, 1) == 0) + { + stbi__rewind(s); + return 0; + } + + if (x != null) + *x = g.w; + if (y != null) + *y = g.h; + return 1; + } + + public static void stbi__out_gif_code(stbi__gif g, ushort code) + { + byte* p; + var idx = 0; + if (g.codes[code].prefix >= 0) + stbi__out_gif_code(g, (ushort)g.codes[code].prefix); + if (g.cur_y >= g.max_y) + return; + idx = g.cur_x + g.cur_y; + p = &g._out_[idx]; + g.history[idx / 4] = 1; + var c = g.color_table[g.codes[code].suffix]; + if (c[3] > 128) + { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + + g.cur_x += 4; + if (g.cur_x >= g.max_x) + { + g.cur_x = g.start_x; + g.cur_y += g.step; + while (g.cur_y >= g.max_y && g.parse > 0) + { + g.step = (1 << g.parse) * g.line_size; + g.cur_y = g.start_y + (g.step >> 1); + --g.parse; + } + } + } + + public static byte* stbi__process_gif_raster(stbi__context s, stbi__gif g) + { + byte lzw_cs = 0; + var len = 0; + var init_code = 0; + uint first = 0; + var codesize = 0; + var codemask = 0; + var avail = 0; + var oldcode = 0; + var bits = 0; + var valid_bits = 0; + var clear = 0; + stbi__gif_lzw* p; + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) + return null; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) + { + g.codes[init_code].prefix = -1; + g.codes[init_code].first = (byte)init_code; + g.codes[init_code].suffix = (byte)init_code; + } + + avail = clear + 2; + oldcode = -1; + len = 0; + for (; ; ) + if (valid_bits < codesize) + { + if (len == 0) + { + len = stbi__get8(s); + if (len == 0) + return g._out_; + } + + --len; + bits |= stbi__get8(s) << valid_bits; + valid_bits += 8; + } + else + { + var code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + if (code == clear) + { + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } + else if (code == clear + 1) + { + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s, len); + return g._out_; + } + else if (code <= avail) + { + if (first != 0) + return (byte*)(ulong)(stbi__err("no clear code") != 0 ? 0 : 0); + if (oldcode >= 0) + { + fixed (stbi__gif_lzw *p2 = &g.codes[avail++]) + { + p = p2; + if (avail > 8192) + return (byte*)(ulong)(stbi__err("too many codes") != 0 ? 0 : 0); + p->prefix = (short)oldcode; + p->first = g.codes[oldcode].first; + p->suffix = code == avail ? p->first : g.codes[code].first; + } + } + else if (code == avail) + { + return (byte*)(ulong)(stbi__err("illegal code in raster") != 0 ? 0 : 0); + } + + stbi__out_gif_code(g, (ushort)code); + if ((avail & codemask) == 0 && avail <= 0x0FFF) + { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } + else + { + return (byte*)(ulong)(stbi__err("illegal code in raster") != 0 ? 0 : 0); + } + } + } + + public static byte* stbi__gif_load_next(stbi__context s, stbi__gif g, int* comp, int req_comp, byte* two_back) + { + var dispose = 0; + var first_frame = 0; + var pi = 0; + var pcount = 0; + first_frame = 0; + if (g._out_ == null) + { + if (stbi__gif_header(s, g, comp, 0) == 0) + return null; + if (stbi__mad3sizes_valid(4, g.w, g.h, 0) == 0) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + pcount = g.w * g.h; + g._out_ = (byte*)stbi__malloc((ulong)(4 * pcount)); + g.background = (byte*)stbi__malloc((ulong)(4 * pcount)); + g.history = (byte*)stbi__malloc((ulong)pcount); + if (g._out_ == null || g.background == null || g.history == null) + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + CRuntime.memset(g._out_, 0x00, (ulong)(4 * pcount)); + CRuntime.memset(g.background, 0x00, (ulong)(4 * pcount)); + CRuntime.memset(g.history, 0x00, (ulong)pcount); + first_frame = 1; + } + else + { + dispose = (g.eflags & 0x1C) >> 2; + pcount = g.w * g.h; + if (dispose == 3 && two_back == null) + dispose = 2; + if (dispose == 3) + { + for (pi = 0; pi < pcount; ++pi) + if (g.history[pi] != 0) + CRuntime.memcpy(&g._out_[pi * 4], &two_back[pi * 4], (ulong)4); + } + else if (dispose == 2) + { + for (pi = 0; pi < pcount; ++pi) + if (g.history[pi] != 0) + CRuntime.memcpy(&g._out_[pi * 4], &g.background[pi * 4], (ulong)4); + } + + CRuntime.memcpy(g.background, g._out_, (ulong)(4 * g.w * g.h)); + } + + CRuntime.memset(g.history, 0x00, (ulong)(g.w * g.h)); + for (; ; ) + { + int tag = stbi__get8(s); + switch (tag) + { + case 0x2C: + { + var x = 0; + var y = 0; + var w = 0; + var h = 0; + byte* o; + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (x + w > g.w || y + h > g.h) + return (byte*)(ulong)(stbi__err("bad Image Descriptor") != 0 ? 0 : 0); + g.line_size = g.w * 4; + g.start_x = x * 4; + g.start_y = y * g.line_size; + g.max_x = g.start_x + w * 4; + g.max_y = g.start_y + h * g.line_size; + g.cur_x = g.start_x; + g.cur_y = g.start_y; + if (w == 0) + g.cur_y = g.max_y; + g.lflags = stbi__get8(s); + if ((g.lflags & 0x40) != 0) + { + g.step = 8 * g.line_size; + g.parse = 3; + } + else + { + g.step = g.line_size; + g.parse = 0; + } + + if ((g.lflags & 0x80) != 0) + { + stbi__gif_parse_colortable(s, g.lpal, 2 << (g.lflags & 7), + (g.eflags & 0x01) != 0 ? g.transparent : -1); + g.color_table = g.lpal; + } + else if ((g.flags & 0x80) != 0) + { + g.color_table = g.pal; + } + else + { + return (byte*)(ulong)(stbi__err("missing color table") != 0 ? 0 : 0); + } + + o = stbi__process_gif_raster(s, g); + if (o == null) + return null; + pcount = g.w * g.h; + if (first_frame != 0 && g.bgindex > 0) + for (pi = 0; pi < pcount; ++pi) + if (g.history[pi] == 0) + { + g.pal[g.bgindex][3] = 255; + fixed (byte* ptr = &g.pal[g.bgindex][0]) + { + CRuntime.memcpy(&g._out_[pi * 4], ptr, (ulong)4); + } + } + + return o; + } + + case 0x21: + { + var len = 0; + int ext = stbi__get8(s); + if (ext == 0xF9) + { + len = stbi__get8(s); + if (len == 4) + { + g.eflags = stbi__get8(s); + g.delay = 10 * stbi__get16le(s); + if (g.transparent >= 0) + g.pal[g.transparent][3] = 255; + if ((g.eflags & 0x01) != 0) + { + g.transparent = stbi__get8(s); + if (g.transparent >= 0) + g.pal[g.transparent][3] = 0; + } + else + { + stbi__skip(s, 1); + g.transparent = -1; + } + } + else + { + stbi__skip(s, len); + break; + } + } + + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: + return null; + default: + return (byte*)(ulong)(stbi__err("unknown code") != 0 ? 0 : 0); + } + } + } + + public static void* stbi__load_gif_main_outofmem(stbi__gif g, byte* _out_, int** delays) + { + CRuntime.free(g._out_); + CRuntime.free(g.history); + CRuntime.free(g.background); + if (_out_ != null) + CRuntime.free(_out_); + if (delays != null && *delays != null) + CRuntime.free(*delays); + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + public class stbi__gif + { + public byte* _out_; + public byte* background; + public int bgindex; + public stbi__gif_lzw[] codes = new stbi__gif_lzw[8192]; + public byte[][] color_table; + public int cur_x; + public int cur_y; + public int delay; + public int eflags; + public int flags; + public int h; + public byte* history; + public int lflags; + public int line_size; + public byte[][] lpal = Utility.CreateArray(256, 4); + public int max_x; + public int max_y; + public byte[][] pal = Utility.CreateArray(256, 4); + public int parse; + public int ratio; + public int start_x; + public int start_y; + public int step; + public int transparent; + public int w; + } + + [StructLayout(LayoutKind.Sequential)] + public struct stbi__gif_lzw + { + public short prefix; + public byte first; + public byte suffix; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Hdr.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Hdr.cs new file mode 100644 index 0000000..3dd3df5 --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Hdr.cs @@ -0,0 +1,388 @@ +// Generated by Sichem at 12/24/2021 8:28:15 PM + +using Misaki.HighPerformance.Image.Runtime; +// Generated by Sichem at 12/24/2021 8:28:15 PM + +using Misaki.HighPerformance.Image; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public static int stbi__hdr_test(stbi__context s) + { + var r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if (r == 0) + { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + + return r; + } + + public static float* stbi__hdr_load(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri) + { + var rgbe = stackalloc byte[4]; + var buffer = stackalloc sbyte[1024]; + sbyte* token; + var valid = 0; + var width = 0; + var height = 0; + byte* scanline; + float* hdr_data; + var len = 0; + byte count = 0; + byte value = 0; + var i = 0; + var j = 0; + var k = 0; + var c1 = 0; + var c2 = 0; + var z = 0; + sbyte* headerToken; + headerToken = stbi__hdr_gettoken(s, buffer); + if (CRuntime.strcmp(headerToken, "#?RADIANCE") != 0 && CRuntime.strcmp(headerToken, "#?RGBE") != 0) + return (float*)(ulong)(stbi__err("not HDR") != 0 ? 0 : 0); + for (; ; ) + { + token = stbi__hdr_gettoken(s, buffer); + if (token[0] == 0) + break; + if (CRuntime.strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) + valid = 1; + } + + if (valid == 0) + return (float*)(ulong)(stbi__err("unsupported format") != 0 ? 0 : 0); + token = stbi__hdr_gettoken(s, buffer); + if (CRuntime.strncmp(token, "-Y ", 3) != 0) + return (float*)(ulong)(stbi__err("unsupported data layout") != 0 ? 0 : 0); + token += 3; + height = (int)CRuntime.strtol(token, &token, 10); + while (*token == 32) + ++token; + if (CRuntime.strncmp(token, "+X ", 3) != 0) + return (float*)(ulong)(stbi__err("unsupported data layout") != 0 ? 0 : 0); + token += 3; + width = (int)CRuntime.strtol(token, null, 10); + if (height > 1 << 24) + return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + if (width > 1 << 24) + return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + *x = width; + *y = height; + if (comp != null) + *comp = 3; + if (req_comp == 0) + req_comp = 3; + if (stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0) == 0) + return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + hdr_data = (float*)stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (hdr_data == null) + return (float*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + + if (width < 8 || width >= 32768) + { + for (; j < height; ++j) + { + for (; i < width; ++i) + { + //var rgbe = stackalloc byte[4]; + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } + else + { + scanline = null; + for (j = 0; j < height; ++j) + { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80) != 0) + { + //var rgbe = stackalloc byte[4]; + rgbe[0] = (byte)c1; + rgbe[1] = (byte)c2; + rgbe[2] = (byte)len; + rgbe[3] = stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + CRuntime.free(scanline); + + // goto main_decode_loop + + // Do first row + j = 0; + for (i = 1; i < width; ++i) + { + //var rgbe = stackalloc byte[4]; + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + + // Do the rest of the rows. + for (j = 1; j < height; ++j) + { + for (i = 0; i < width; ++i) + { + //var rgbe = stackalloc byte[4]; + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + + goto finish; + } + + len <<= 8; + len |= stbi__get8(s); + if (len != width) + { + CRuntime.free(hdr_data); + CRuntime.free(scanline); + return (float*)(ulong)(stbi__err("invalid decoded scanline length") != 0 ? 0 : 0); + } + + if (scanline == null) + { + scanline = (byte*)stbi__malloc_mad2(width, 4, 0); + if (scanline == null) + { + CRuntime.free(hdr_data); + return (float*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + } + + for (k = 0; k < 4; ++k) + { + var nleft = 0; + i = 0; + while ((nleft = width - i) > 0) + { + count = stbi__get8(s); + if (count > 128) + { + value = stbi__get8(s); + count -= 128; + if (count == 0 || count > nleft) + { + CRuntime.free(hdr_data); + CRuntime.free(scanline); + return (float*)(ulong)(stbi__err("corrupt") != 0 ? 0 : 0); + } + + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } + else + { + if (count == 0 || count > nleft) + { + CRuntime.free(hdr_data); + CRuntime.free(scanline); + return (float*)(ulong)(stbi__err("corrupt") != 0 ? 0 : 0); + } + + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + + for (i = 0; i < width; ++i) + stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp); + } + + if (scanline != null) + CRuntime.free(scanline); + } + + finish: + return hdr_data; + } + + public static int stbi__hdr_info(stbi__context s, int* x, int* y, int* comp) + { + var buffer = stackalloc sbyte[1024]; + sbyte* token; + var valid = 0; + var dummy = 0; + if (x == null) + x = &dummy; + if (y == null) + y = &dummy; + if (comp == null) + comp = &dummy; + if (stbi__hdr_test(s) == 0) + { + stbi__rewind(s); + return 0; + } + + for (; ; ) + { + token = stbi__hdr_gettoken(s, buffer); + if (token[0] == 0) + break; + if (CRuntime.strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) + valid = 1; + } + + if (valid == 0) + { + stbi__rewind(s); + return 0; + } + + token = stbi__hdr_gettoken(s, buffer); + if (CRuntime.strncmp(token, "-Y ", 3) != 0) + { + stbi__rewind(s); + return 0; + } + + token += 3; + *y = (int)CRuntime.strtol(token, &token, 10); + while (*token == 32) + ++token; + if (CRuntime.strncmp(token, "+X ", 3) != 0) + { + stbi__rewind(s); + return 0; + } + + token += 3; + *x = (int)CRuntime.strtol(token, null, 10); + *comp = 3; + return 1; + } + + public static byte* stbi__hdr_to_ldr(float* data, int x, int y, int comp) + { + var i = 0; + var k = 0; + var n = 0; + byte* output; + if (data == null) + return null; + output = (byte*)stbi__malloc_mad3(x, y, comp, 0); + if (output == null) + { + CRuntime.free(data); + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + if ((comp & 1) != 0) + n = comp; + else + n = comp - 1; + for (i = 0; i < x * y; ++i) + { + for (k = 0; k < n; ++k) + { + var z = (float)CRuntime.pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + + 0.5f; + if (z < 0) + z = 0; + if (z > 255) + z = 255; + output[i * comp + k] = (byte)(int)z; + } + + if (k < comp) + { + var z = data[i * comp + k] * 255 + 0.5f; + if (z < 0) + z = 0; + if (z > 255) + z = 255; + output[i * comp + k] = (byte)(int)z; + } + } + + CRuntime.free(data); + return output; + } + + public static int stbi__hdr_test_core(stbi__context s, string signature) + { + var i = 0; + for (i = 0; i < signature.Length; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; + } + + public static sbyte* stbi__hdr_gettoken(stbi__context z, sbyte* buffer) + { + var len = 0; + sbyte c = 0; + c = (sbyte)stbi__get8(z); + while (stbi__at_eof(z) == 0 && c != 10) + { + buffer[len++] = c; + if (len == 1024 - 1) + { + while (stbi__at_eof(z) == 0 && stbi__get8(z) != 10) + { + } + + break; + } + + c = (sbyte)stbi__get8(z); + } + + buffer[len] = 0; + return buffer; + } + + public static void stbi__hdr_convert(float* output, byte* input, int req_comp) + { + if (input[3] != 0) + { + float f1 = 0; + f1 = (float)CRuntime.ldexp(1.0f, input[3] - (128 + 8)); + if (req_comp <= 2) + { + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + } + else + { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + + if (req_comp == 2) + output[1] = 1; + if (req_comp == 4) + output[3] = 1; + } + else + { + switch (req_comp) + { + case 4: + case 3: + if (req_comp == 4) + output[3] = 1; + output[0] = output[1] = output[2] = 0; + break; + case 2: + case 1: + if (req_comp == 2) + output[1] = 1; + output[0] = 0; + break; + } + } + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Jpg.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Jpg.cs new file mode 100644 index 0000000..743987a --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Jpg.cs @@ -0,0 +1,1741 @@ +// Generated by Sichem at 9/16/2024 9:09:30 AM + +using Misaki.HighPerformance.Image.Runtime; +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public delegate void delegate0(byte* arg0, int arg1, short* arg2); + + public delegate void delegate1(byte* arg0, byte* arg1, byte* arg2, byte* arg3, int arg4, int arg5); + + public delegate byte* delegate2(byte* arg0, byte* arg1, byte* arg2, int arg3, int arg4); + + public static uint[] stbi__bmask = + { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 }; + + public static int[] stbi__jbias = + { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; + + public static byte[] stbi__jpeg_dezigzag = + { + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, + 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 + }; + + public static int stbi__jpeg_test(stbi__context s) + { + var r = 0; + var j = new stbi__jpeg(); + if (j == null) + return stbi__err("outofmem"); + j.s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + return r; + } + + public static void* stbi__jpeg_load(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri) + { + byte* result; + var j = new stbi__jpeg(); + if (j == null) + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + j.s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x, y, comp, req_comp); + return result; + } + + public static int stbi__jpeg_info(stbi__context s, int* x, int* y, int* comp) + { + var result = 0; + var j = new stbi__jpeg(); + if (j == null) + return stbi__err("outofmem"); + j.s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + return result; + } + + public static int stbi__build_huffman(stbi__huffman* h, int* count) + { + var i = 0; + var j = 0; + var k = 0; + uint code = 0; + for (i = 0; i < 16; ++i) + { + for (j = 0; j < count[i]; ++j) + { + h->size[k++] = (byte)(i + 1); + if (k >= 257) + return stbi__err("bad size list"); + } + } + + h->size[k] = 0; + code = 0; + k = 0; + for (j = 1; j <= 16; ++j) + { + h->delta[j] = (int)(k - code); + if (h->size[k] == j) + { + while (h->size[k] == j) + h->code[k++] = (ushort)code++; + + if (code - 1 >= 1u << j) + return stbi__err("bad code lengths"); + } + + h->maxcode[j] = code << (16 - j); + code <<= 1; + } + + h->maxcode[j] = 0xffffffff; + CRuntime.memset(h->fast, 255, (ulong)(1 << 9)); + for (i = 0; i < k; ++i) + { + int s = h->size[i]; + if (s <= 9) + { + var c = h->code[i] << (9 - s); + var m = 1 << (9 - s); + for (j = 0; j < m; ++j) + h->fast[c + j] = (byte)i; + } + } + + return 1; + } + + public static void stbi__build_fast_ac(short[] fast_ac, stbi__huffman* h) + { + var i = 0; + for (i = 0; i < 1 << 9; ++i) + { + var fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) + { + int rs = h->values[fast]; + var run = (rs >> 4) & 15; + var magbits = rs & 15; + int len = h->size[fast]; + if (magbits != 0 && len + magbits <= 9) + { + var k = ((i << len) & ((1 << 9) - 1)) >> (9 - magbits); + var m = 1 << (magbits - 1); + if (k < m) + k += (int)((~0U << magbits) + 1); + if (k >= -128 && k <= 127) + fast_ac[i] = (short)(k * 256 + run * 16 + len + magbits); + } + } + } + } + + public static void stbi__grow_buffer_unsafe(stbi__jpeg j) + { + do + { + var b = (uint)(j.nomore != 0 ? 0 : stbi__get8(j.s)); + if (b == 0xff) + { + int c = stbi__get8(j.s); + while (c == 0xff) + c = stbi__get8(j.s); + + if (c != 0) + { + j.marker = (byte)c; + j.nomore = 1; + return; + } + } + + j.code_buffer |= b << (24 - j.code_bits); + j.code_bits += 8; + } while (j.code_bits <= 24); + } + + public static int stbi__jpeg_huff_decode(stbi__jpeg j, stbi__huffman* h) + { + uint temp = 0; + var c = 0; + var k = 0; + if (j.code_bits < 16) + stbi__grow_buffer_unsafe(j); + c = (int)((j.code_buffer >> (32 - 9)) & ((1 << 9) - 1)); + k = h->fast[c]; + if (k < 255) + { + int s = h->size[k]; + if (s > j.code_bits) + return -1; + j.code_buffer <<= s; + j.code_bits -= s; + return h->values[k]; + } + + temp = j.code_buffer >> 16; + for (k = 9 + 1; ; ++k) + if (temp < h->maxcode[k]) + break; + + if (k == 17) + { + j.code_bits -= 16; + return -1; + } + + if (k > j.code_bits) + return -1; + c = (int)(((j.code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]); + if (c < 0 || c >= 256) + return -1; + j.code_bits -= k; + j.code_buffer <<= k; + return h->values[c]; + } + + public static int stbi__extend_receive(stbi__jpeg j, int n) + { + uint k = 0; + var sgn = 0; + if (j.code_bits < n) + stbi__grow_buffer_unsafe(j); + if (j.code_bits < n) + return 0; + sgn = (int)(j.code_buffer >> 31); + k = CRuntime._lrotl(j.code_buffer, n); + j.code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j.code_bits -= n; + return (int)(k + (stbi__jbias[n] & (sgn - 1))); + } + + public static int stbi__jpeg_get_bits(stbi__jpeg j, int n) + { + uint k = 0; + if (j.code_bits < n) + stbi__grow_buffer_unsafe(j); + if (j.code_bits < n) + return 0; + k = CRuntime._lrotl(j.code_buffer, n); + j.code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j.code_bits -= n; + return (int)k; + } + + public static int stbi__jpeg_get_bit(stbi__jpeg j) + { + uint k = 0; + if (j.code_bits < 1) + stbi__grow_buffer_unsafe(j); + if (j.code_bits < 1) + return 0; + k = j.code_buffer; + j.code_buffer <<= 1; + --j.code_bits; + return (int)(k & 0x80000000); + } + + public static int stbi__jpeg_decode_block(stbi__jpeg j, short* data, stbi__huffman* hdc, stbi__huffman* hac, + short[] fac, int b, ushort[] dequant) + { + var diff = 0; + var dc = 0; + var k = 0; + var t = 0; + if (j.code_bits < 16) + stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) + return stbi__err("bad huffman code"); + CRuntime.memset(data, 0, (ulong)(64 * sizeof(short))); + diff = t != 0 ? stbi__extend_receive(j, t) : 0; + if (stbi__addints_valid(j.img_comp[b].dc_pred, diff) == 0) + return stbi__err("bad delta"); + dc = j.img_comp[b].dc_pred + diff; + j.img_comp[b].dc_pred = dc; + if (stbi__mul2shorts_valid(dc, dequant[0]) == 0) + return stbi__err("can't merge dc and ac"); + data[0] = (short)(dc * dequant[0]); + k = 1; + do + { + uint zig = 0; + var c = 0; + var r = 0; + var s = 0; + if (j.code_bits < 16) + stbi__grow_buffer_unsafe(j); + c = (int)((j.code_buffer >> (32 - 9)) & ((1 << 9) - 1)); + r = fac[c]; + if (r != 0) + { + k += (r >> 4) & 15; + s = r & 15; + if (s > j.code_bits) + return stbi__err("bad huffman code"); + j.code_buffer <<= s; + j.code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short)((r >> 8) * dequant[zig]); + } + else + { + var rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) + return stbi__err("bad huffman code"); + s = rs & 15; + r = rs >> 4; + if (s == 0) + { + if (rs != 0xf0) + break; + k += 16; + } + else + { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short)(stbi__extend_receive(j, s) * dequant[zig]); + } + } + } while (k < 64); + + return 1; + } + + public static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg j, short* data, stbi__huffman* hdc, int b) + { + var diff = 0; + var dc = 0; + var t = 0; + if (j.spec_end != 0) + return stbi__err("can't merge dc and ac"); + if (j.code_bits < 16) + stbi__grow_buffer_unsafe(j); + if (j.succ_high == 0) + { + CRuntime.memset(data, 0, (ulong)(64 * sizeof(short))); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) + return stbi__err("can't merge dc and ac"); + diff = t != 0 ? stbi__extend_receive(j, t) : 0; + if (stbi__addints_valid(j.img_comp[b].dc_pred, diff) == 0) + return stbi__err("bad delta"); + dc = j.img_comp[b].dc_pred + diff; + j.img_comp[b].dc_pred = dc; + if (stbi__mul2shorts_valid(dc, 1 << j.succ_low) == 0) + return stbi__err("can't merge dc and ac"); + data[0] = (short)(dc * (1 << j.succ_low)); + } + else + { + if (stbi__jpeg_get_bit(j) != 0) + data[0] += (short)(1 << j.succ_low); + } + + return 1; + } + + public static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg j, short* data, stbi__huffman* hac, short[] fac) + { + var k = 0; + if (j.spec_start == 0) + return stbi__err("can't merge dc and ac"); + if (j.succ_high == 0) + { + var shift = j.succ_low; + if (j.eob_run != 0) + { + --j.eob_run; + return 1; + } + + k = j.spec_start; + do + { + uint zig = 0; + var c = 0; + var r = 0; + var s = 0; + if (j.code_bits < 16) + stbi__grow_buffer_unsafe(j); + c = (int)((j.code_buffer >> (32 - 9)) & ((1 << 9) - 1)); + r = fac[c]; + if (r != 0) + { + k += (r >> 4) & 15; + s = r & 15; + if (s > j.code_bits) + return stbi__err("bad huffman code"); + j.code_buffer <<= s; + j.code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short)((r >> 8) * (1 << shift)); + } + else + { + var rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) + return stbi__err("bad huffman code"); + s = rs & 15; + r = rs >> 4; + if (s == 0) + { + if (r < 15) + { + j.eob_run = 1 << r; + if (r != 0) + j.eob_run += stbi__jpeg_get_bits(j, r); + --j.eob_run; + break; + } + + k += 16; + } + else + { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short)(stbi__extend_receive(j, s) * (1 << shift)); + } + } + } while (k <= j.spec_end); + } + else + { + var bit = (short)(1 << j.succ_low); + if (j.eob_run != 0) + { + --j.eob_run; + for (k = j.spec_start; k <= j.spec_end; ++k) + { + var p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j) != 0) + if ((*p & bit) == 0) + { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } + else + { + k = j.spec_start; + do + { + var r = 0; + var s = 0; + var rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) + return stbi__err("bad huffman code"); + s = rs & 15; + r = rs >> 4; + if (s == 0) + { + if (r < 15) + { + j.eob_run = (1 << r) - 1; + if (r != 0) + j.eob_run += stbi__jpeg_get_bits(j, r); + r = 64; + } + } + else + { + if (s != 1) + return stbi__err("bad huffman code"); + if (stbi__jpeg_get_bit(j) != 0) + s = bit; + else + s = -bit; + } + + while (k <= j.spec_end) + { + var p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) + { + if (stbi__jpeg_get_bit(j) != 0) + if ((*p & bit) == 0) + { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + else + { + if (r == 0) + { + *p = (short)s; + break; + } + + --r; + } + } + } while (k <= j.spec_end); + } + } + + return 1; + } + + public static void stbi__idct_block(byte* _out_, int out_stride, short* data) + { + var i = 0; + var val = stackalloc int[64]; + var v = val; + byte* o; + var d = data; + for (i = 0; i < 8; ++i, ++d, ++v) + if (d[8] == 0 && d[16] == 0 && d[24] == 0 && d[32] == 0 && d[40] == 0 && d[48] == 0 && d[56] == 0) + { + var dcterm = d[0] * 4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } + else + { + var t0 = 0; + var t1 = 0; + var t2 = 0; + var t3 = 0; + var p1 = 0; + var p2 = 0; + var p3 = 0; + var p4 = 0; + var p5 = 0; + var x0 = 0; + var x1 = 0; + var x2 = 0; + var x3 = 0; + p2 = d[16]; + p3 = d[48]; + p1 = (p2 + p3) * (int)(0.5411961f * 4096 + 0.5); + t2 = p1 + p3 * (int)(-1.847759065f * 4096 + 0.5); + t3 = p1 + p2 * (int)(0.765366865f * 4096 + 0.5); + p2 = d[0]; + p3 = d[32]; + t0 = (p2 + p3) * 4096; + t1 = (p2 - p3) * 4096; + x0 = t0 + t3; + x3 = t0 - t3; + x1 = t1 + t2; + x2 = t1 - t2; + t0 = d[56]; + t1 = d[40]; + t2 = d[24]; + t3 = d[8]; + p3 = t0 + t2; + p4 = t1 + t3; + p1 = t0 + t3; + p2 = t1 + t2; + p5 = (p3 + p4) * (int)(1.175875602f * 4096 + 0.5); + t0 = t0 * (int)(0.298631336f * 4096 + 0.5); + t1 = t1 * (int)(2.053119869f * 4096 + 0.5); + t2 = t2 * (int)(3.072711026f * 4096 + 0.5); + t3 = t3 * (int)(1.501321110f * 4096 + 0.5); + p1 = p5 + p1 * (int)(-0.899976223f * 4096 + 0.5); + p2 = p5 + p2 * (int)(-2.562915447f * 4096 + 0.5); + p3 = p3 * (int)(-1.961570560f * 4096 + 0.5); + p4 = p4 * (int)(-0.390180644f * 4096 + 0.5); + t3 += p1 + p4; + t2 += p2 + p3; + t1 += p2 + p4; + t0 += p1 + p3; + x0 += 512; + x1 += 512; + x2 += 512; + x3 += 512; + v[0] = (x0 + t3) >> 10; + v[56] = (x0 - t3) >> 10; + v[8] = (x1 + t2) >> 10; + v[48] = (x1 - t2) >> 10; + v[16] = (x2 + t1) >> 10; + v[40] = (x2 - t1) >> 10; + v[24] = (x3 + t0) >> 10; + v[32] = (x3 - t0) >> 10; + } + + for (i = 0, v = val, o = _out_; i < 8; ++i, v += 8, o += out_stride) + { + var t0 = 0; + var t1 = 0; + var t2 = 0; + var t3 = 0; + var p1 = 0; + var p2 = 0; + var p3 = 0; + var p4 = 0; + var p5 = 0; + var x0 = 0; + var x1 = 0; + var x2 = 0; + var x3 = 0; + p2 = v[2]; + p3 = v[6]; + p1 = (p2 + p3) * (int)(0.5411961f * 4096 + 0.5); + t2 = p1 + p3 * (int)(-1.847759065f * 4096 + 0.5); + t3 = p1 + p2 * (int)(0.765366865f * 4096 + 0.5); + p2 = v[0]; + p3 = v[4]; + t0 = (p2 + p3) * 4096; + t1 = (p2 - p3) * 4096; + x0 = t0 + t3; + x3 = t0 - t3; + x1 = t1 + t2; + x2 = t1 - t2; + t0 = v[7]; + t1 = v[5]; + t2 = v[3]; + t3 = v[1]; + p3 = t0 + t2; + p4 = t1 + t3; + p1 = t0 + t3; + p2 = t1 + t2; + p5 = (p3 + p4) * (int)(1.175875602f * 4096 + 0.5); + t0 = t0 * (int)(0.298631336f * 4096 + 0.5); + t1 = t1 * (int)(2.053119869f * 4096 + 0.5); + t2 = t2 * (int)(3.072711026f * 4096 + 0.5); + t3 = t3 * (int)(1.501321110f * 4096 + 0.5); + p1 = p5 + p1 * (int)(-0.899976223f * 4096 + 0.5); + p2 = p5 + p2 * (int)(-2.562915447f * 4096 + 0.5); + p3 = p3 * (int)(-1.961570560f * 4096 + 0.5); + p4 = p4 * (int)(-0.390180644f * 4096 + 0.5); + t3 += p1 + p4; + t2 += p2 + p3; + t1 += p2 + p4; + t0 += p1 + p3; + x0 += 65536 + (128 << 17); + x1 += 65536 + (128 << 17); + x2 += 65536 + (128 << 17); + x3 += 65536 + (128 << 17); + o[0] = stbi__clamp((x0 + t3) >> 17); + o[7] = stbi__clamp((x0 - t3) >> 17); + o[1] = stbi__clamp((x1 + t2) >> 17); + o[6] = stbi__clamp((x1 - t2) >> 17); + o[2] = stbi__clamp((x2 + t1) >> 17); + o[5] = stbi__clamp((x2 - t1) >> 17); + o[3] = stbi__clamp((x3 + t0) >> 17); + o[4] = stbi__clamp((x3 - t0) >> 17); + } + } + + public static byte stbi__get_marker(stbi__jpeg j) + { + byte x = 0; + if (j.marker != 0xff) + { + x = j.marker; + j.marker = 0xff; + return x; + } + + x = stbi__get8(j.s); + if (x != 0xff) + return 0xff; + while (x == 0xff) + x = stbi__get8(j.s); + + return x; + } + + public static void stbi__jpeg_reset(stbi__jpeg j) + { + j.code_bits = 0; + j.code_buffer = 0; + j.nomore = 0; + j.img_comp[0].dc_pred = j.img_comp[1].dc_pred = j.img_comp[2].dc_pred = j.img_comp[3].dc_pred = 0; + j.marker = 0xff; + j.todo = j.restart_interval != 0 ? j.restart_interval : 0x7fffffff; + j.eob_run = 0; + } + + public static int stbi__parse_entropy_coded_data(stbi__jpeg z) + { + stbi__jpeg_reset(z); + if (z.progressive == 0) + { + if (z.scan_n == 1) + { + var i = 0; + var j = 0; + var data = stackalloc short[64]; + var n = z.order[0]; + var w = (z.img_comp[n].x + 7) >> 3; + var h = (z.img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) + for (i = 0; i < w; ++i) + { + var ha = z.img_comp[n].ha; + fixed (stbi__huffman* dptr = &z.huff_dc[z.img_comp[n].hd]) + fixed (stbi__huffman* aptr = &z.huff_ac[ha]) + { + if (stbi__jpeg_decode_block(z, data, dptr, aptr, + z.fast_ac[ha], n, z.dequant[z.img_comp[n].tq]) == 0) + return 0; + } + z.idct_block_kernel(z.img_comp[n].data + z.img_comp[n].w2 * j * 8 + i * 8, z.img_comp[n].w2, + data); + if (--z.todo <= 0) + { + if (z.code_bits < 24) + stbi__grow_buffer_unsafe(z); + if (!(z.marker >= 0xd0 && z.marker <= 0xd7)) + return 1; + stbi__jpeg_reset(z); + } + } + + return 1; + } + else + { + var i = 0; + var j = 0; + var k = 0; + var x = 0; + var y = 0; + var data = stackalloc short[64]; + for (j = 0; j < z.img_mcu_y; ++j) + for (i = 0; i < z.img_mcu_x; ++i) + { + for (k = 0; k < z.scan_n; ++k) + { + var n = z.order[k]; + for (y = 0; y < z.img_comp[n].v; ++y) + for (x = 0; x < z.img_comp[n].h; ++x) + { + var x2 = (i * z.img_comp[n].h + x) * 8; + var y2 = (j * z.img_comp[n].v + y) * 8; + var ha = z.img_comp[n].ha; + + fixed (stbi__huffman* dptr = &z.huff_dc[z.img_comp[n].hd]) + fixed (stbi__huffman* aptr = &z.huff_ac[ha]) + { + if (stbi__jpeg_decode_block(z, data, dptr, aptr, + z.fast_ac[ha], n, z.dequant[z.img_comp[n].tq]) == 0) + return 0; + } + z.idct_block_kernel(z.img_comp[n].data + z.img_comp[n].w2 * y2 + x2, z.img_comp[n].w2, + data); + } + } + + if (--z.todo <= 0) + { + if (z.code_bits < 24) + stbi__grow_buffer_unsafe(z); + if (!(z.marker >= 0xd0 && z.marker <= 0xd7)) + return 1; + stbi__jpeg_reset(z); + } + } + + return 1; + } + } + + if (z.scan_n == 1) + { + var i = 0; + var j = 0; + var n = z.order[0]; + var w = (z.img_comp[n].x + 7) >> 3; + var h = (z.img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) + for (i = 0; i < w; ++i) + { + var data = z.img_comp[n].coeff + 64 * (i + j * z.img_comp[n].coeff_w); + if (z.spec_start == 0) + { + fixed (stbi__huffman* dptr = &z.huff_dc[z.img_comp[n].hd]) + { + if (stbi__jpeg_decode_block_prog_dc(z, data, dptr, n) == 0) + return 0; + } + } + else + { + var ha = z.img_comp[n].ha; + fixed (stbi__huffman* aptr = &z.huff_ac[ha]) + { + if (stbi__jpeg_decode_block_prog_ac(z, data, aptr, z.fast_ac[ha]) == 0) + return 0; + } + } + + if (--z.todo <= 0) + { + if (z.code_bits < 24) + stbi__grow_buffer_unsafe(z); + if (!(z.marker >= 0xd0 && z.marker <= 0xd7)) + return 1; + stbi__jpeg_reset(z); + } + } + + return 1; + } + else + { + var i = 0; + var j = 0; + var k = 0; + var x = 0; + var y = 0; + for (j = 0; j < z.img_mcu_y; ++j) + for (i = 0; i < z.img_mcu_x; ++i) + { + for (k = 0; k < z.scan_n; ++k) + { + var n = z.order[k]; + for (y = 0; y < z.img_comp[n].v; ++y) + for (x = 0; x < z.img_comp[n].h; ++x) + { + var x2 = i * z.img_comp[n].h + x; + var y2 = j * z.img_comp[n].v + y; + var data = z.img_comp[n].coeff + 64 * (x2 + y2 * z.img_comp[n].coeff_w); + + + fixed (stbi__huffman* dptr = &z.huff_dc[z.img_comp[n].hd]) + { + if (stbi__jpeg_decode_block_prog_dc(z, data, dptr, n) == 0) + return 0; + } + } + } + + if (--z.todo <= 0) + { + if (z.code_bits < 24) + stbi__grow_buffer_unsafe(z); + if (!(z.marker >= 0xd0 && z.marker <= 0xd7)) + return 1; + stbi__jpeg_reset(z); + } + } + + return 1; + } + } + + public static void stbi__jpeg_dequantize(short* data, ushort[] dequant) + { + var i = 0; + for (i = 0; i < 64; ++i) + data[i] *= (short)dequant[i]; + } + + public static void stbi__jpeg_finish(stbi__jpeg z) + { + if (z.progressive != 0) + { + var i = 0; + var j = 0; + var n = 0; + for (n = 0; n < z.s.img_n; ++n) + { + var w = (z.img_comp[n].x + 7) >> 3; + var h = (z.img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) + for (i = 0; i < w; ++i) + { + var data = z.img_comp[n].coeff + 64 * (i + j * z.img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z.dequant[z.img_comp[n].tq]); + z.idct_block_kernel(z.img_comp[n].data + z.img_comp[n].w2 * j * 8 + i * 8, z.img_comp[n].w2, + data); + } + } + } + } + + public static int stbi__process_marker(stbi__jpeg z, int m) + { + var L = 0; + switch (m) + { + case 0xff: + return stbi__err("expected marker"); + case 0xDD: + if (stbi__get16be(z.s) != 4) + return stbi__err("bad DRI len"); + z.restart_interval = stbi__get16be(z.s); + return 1; + case 0xDB: + L = stbi__get16be(z.s) - 2; + while (L > 0) + { + int q = stbi__get8(z.s); + var p = q >> 4; + var sixteen = p != 0 ? 1 : 0; + var t = q & 15; + var i = 0; + if (p != 0 && p != 1) + return stbi__err("bad DQT type"); + if (t > 3) + return stbi__err("bad DQT table"); + for (i = 0; i < 64; ++i) + z.dequant[t][stbi__jpeg_dezigzag[i]] = + (ushort)(sixteen != 0 ? stbi__get16be(z.s) : stbi__get8(z.s)); + L -= sixteen != 0 ? 129 : 65; + } + + return L == 0 ? 1 : 0; + case 0xC4: + L = stbi__get16be(z.s) - 2; + var sizes = stackalloc int[16]; + while (L > 0) + { + var i = 0; + var n = 0; + int q = stbi__get8(z.s); + var tc = q >> 4; + var th = q & 15; + if (tc > 1 || th > 3) + return stbi__err("bad DHT header"); + for (i = 0; i < 16; ++i) + { + sizes[i] = stbi__get8(z.s); + n += sizes[i]; + } + if (n > 256) + return stbi__err("bad DHT header"); + + L -= 17; + if (tc == 0) + { + fixed (stbi__huffman* hptr = &z.huff_dc[th]) + { + if (stbi__build_huffman(hptr, sizes) == 0) + return 0; + + var v = hptr->values; + for (i = 0; i < n; ++i) + v[i] = stbi__get8(z.s); + } + } + else + { + fixed (stbi__huffman* aptr = &z.huff_ac[th]) + { + if (stbi__build_huffman(aptr, sizes) == 0) + return 0; + + var v = aptr->values; + for (i = 0; i < n; ++i) + v[i] = stbi__get8(z.s); + } + } + + if (tc != 0) + fixed (stbi__huffman* aptr = &z.huff_ac[th]) + { + stbi__build_fast_ac(z.fast_ac[th], aptr); + } + L -= n; + } + + return L == 0 ? 1 : 0; + } + + if (m >= 0xE0 && m <= 0xEF || m == 0xFE) + { + L = stbi__get16be(z.s); + if (L < 2) + { + if (m == 0xFE) + return stbi__err("bad COM len"); + return stbi__err("bad APP len"); + } + + L -= 2; + if (m == 0xE0 && L >= 5) + { + var ok = 1; + var i = 0; + for (i = 0; i < 5; ++i) + if (stbi__get8(z.s) != stbi__process_marker_tag[i]) + ok = 0; + L -= 5; + if (ok != 0) + z.jfif = 1; + } + else if (m == 0xEE && L >= 12) + { + var ok = 1; + var i = 0; + for (i = 0; i < 6; ++i) + if (stbi__get8(z.s) != stbi__process_marker_tag[i]) + ok = 0; + L -= 6; + if (ok != 0) + { + stbi__get8(z.s); + stbi__get16be(z.s); + stbi__get16be(z.s); + z.app14_color_transform = stbi__get8(z.s); + L -= 6; + } + } + + stbi__skip(z.s, L); + return 1; + } + + return stbi__err("unknown marker"); + } + + public static int stbi__process_scan_header(stbi__jpeg z) + { + var i = 0; + var Ls = stbi__get16be(z.s); + z.scan_n = stbi__get8(z.s); + if (z.scan_n < 1 || z.scan_n > 4 || z.scan_n > z.s.img_n) + return stbi__err("bad SOS component count"); + if (Ls != 6 + 2 * z.scan_n) + return stbi__err("bad SOS len"); + for (i = 0; i < z.scan_n; ++i) + { + int id = stbi__get8(z.s); + var which = 0; + int q = stbi__get8(z.s); + for (which = 0; which < z.s.img_n; ++which) + if (z.img_comp[which].id == id) + break; + + if (which == z.s.img_n) + return 0; + z.img_comp[which].hd = q >> 4; + if (z.img_comp[which].hd > 3) + return stbi__err("bad DC huff"); + z.img_comp[which].ha = q & 15; + if (z.img_comp[which].ha > 3) + return stbi__err("bad AC huff"); + z.order[i] = which; + } + + { + var aa = 0; + z.spec_start = stbi__get8(z.s); + z.spec_end = stbi__get8(z.s); + aa = stbi__get8(z.s); + z.succ_high = aa >> 4; + z.succ_low = aa & 15; + if (z.progressive != 0) + { + if (z.spec_start > 63 || z.spec_end > 63 || z.spec_start > z.spec_end || z.succ_high > 13 || + z.succ_low > 13) + return stbi__err("bad SOS"); + } + else + { + if (z.spec_start != 0) + return stbi__err("bad SOS"); + if (z.succ_high != 0 || z.succ_low != 0) + return stbi__err("bad SOS"); + z.spec_end = 63; + } + } + + return 1; + } + + public static int stbi__free_jpeg_components(stbi__jpeg z, int ncomp, int why) + { + var i = 0; + for (i = 0; i < ncomp; ++i) + { + if (z.img_comp[i].raw_data != null) + { + CRuntime.free(z.img_comp[i].raw_data); + z.img_comp[i].raw_data = null; + z.img_comp[i].data = null; + } + + if (z.img_comp[i].raw_coeff != null) + { + CRuntime.free(z.img_comp[i].raw_coeff); + z.img_comp[i].raw_coeff = null; + z.img_comp[i].coeff = null; + } + + if (z.img_comp[i].linebuf != null) + { + CRuntime.free(z.img_comp[i].linebuf); + z.img_comp[i].linebuf = null; + } + } + + return why; + } + + public static int stbi__process_frame_header(stbi__jpeg z, int scan) + { + var s = z.s; + var Lf = 0; + var p = 0; + var i = 0; + var q = 0; + var h_max = 1; + var v_max = 1; + var c = 0; + Lf = stbi__get16be(s); + if (Lf < 11) + return stbi__err("bad SOF len"); + p = stbi__get8(s); + if (p != 8) + return stbi__err("only 8-bit"); + s.img_y = (uint)stbi__get16be(s); + if (s.img_y == 0) + return stbi__err("no header height"); + s.img_x = (uint)stbi__get16be(s); + if (s.img_x == 0) + return stbi__err("0 width"); + if (s.img_y > 1 << 24) + return stbi__err("too large"); + if (s.img_x > 1 << 24) + return stbi__err("too large"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) + return stbi__err("bad component count"); + s.img_n = c; + for (i = 0; i < c; ++i) + { + z.img_comp[i].data = null; + z.img_comp[i].linebuf = null; + } + + if (Lf != 8 + 3 * s.img_n) + return stbi__err("bad SOF len"); + z.rgb = 0; + for (i = 0; i < s.img_n; ++i) + { + z.img_comp[i].id = stbi__get8(s); + if (s.img_n == 3 && z.img_comp[i].id == stbi__process_frame_header_rgb[i]) + ++z.rgb; + q = stbi__get8(s); + z.img_comp[i].h = q >> 4; + if (z.img_comp[i].h == 0 || z.img_comp[i].h > 4) + return stbi__err("bad H"); + z.img_comp[i].v = q & 15; + if (z.img_comp[i].v == 0 || z.img_comp[i].v > 4) + return stbi__err("bad V"); + z.img_comp[i].tq = stbi__get8(s); + if (z.img_comp[i].tq > 3) + return stbi__err("bad TQ"); + } + + if (scan != STBI__SCAN_load) + return 1; + if (stbi__mad3sizes_valid((int)s.img_x, (int)s.img_y, s.img_n, 0) == 0) + return stbi__err("too large"); + for (i = 0; i < s.img_n; ++i) + { + if (z.img_comp[i].h > h_max) + h_max = z.img_comp[i].h; + if (z.img_comp[i].v > v_max) + v_max = z.img_comp[i].v; + } + + for (i = 0; i < s.img_n; ++i) + { + if (h_max % z.img_comp[i].h != 0) + return stbi__err("bad H"); + if (v_max % z.img_comp[i].v != 0) + return stbi__err("bad V"); + } + + z.img_h_max = h_max; + z.img_v_max = v_max; + z.img_mcu_w = h_max * 8; + z.img_mcu_h = v_max * 8; + z.img_mcu_x = (int)((s.img_x + z.img_mcu_w - 1) / z.img_mcu_w); + z.img_mcu_y = (int)((s.img_y + z.img_mcu_h - 1) / z.img_mcu_h); + for (i = 0; i < s.img_n; ++i) + { + z.img_comp[i].x = (int)((s.img_x * z.img_comp[i].h + h_max - 1) / h_max); + z.img_comp[i].y = (int)((s.img_y * z.img_comp[i].v + v_max - 1) / v_max); + z.img_comp[i].w2 = z.img_mcu_x * z.img_comp[i].h * 8; + z.img_comp[i].h2 = z.img_mcu_y * z.img_comp[i].v * 8; + z.img_comp[i].coeff = null; + z.img_comp[i].raw_coeff = null; + z.img_comp[i].linebuf = null; + z.img_comp[i].raw_data = stbi__malloc_mad2(z.img_comp[i].w2, z.img_comp[i].h2, 15); + if (z.img_comp[i].raw_data == null) + return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem")); + z.img_comp[i].data = (byte*)(((long)z.img_comp[i].raw_data + 15) & ~15); + if (z.progressive != 0) + { + z.img_comp[i].coeff_w = z.img_comp[i].w2 / 8; + z.img_comp[i].coeff_h = z.img_comp[i].h2 / 8; + z.img_comp[i].raw_coeff = stbi__malloc_mad3(z.img_comp[i].w2, z.img_comp[i].h2, sizeof(short), 15); + if (z.img_comp[i].raw_coeff == null) + return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem")); + z.img_comp[i].coeff = (short*)(((long)z.img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; + } + + public static int stbi__decode_jpeg_header(stbi__jpeg z, int scan) + { + var m = 0; + z.jfif = 0; + z.app14_color_transform = -1; + z.marker = 0xff; + m = stbi__get_marker(z); + if (!(m == 0xd8)) + return stbi__err("no SOI"); + if (scan == STBI__SCAN_type) + return 1; + m = stbi__get_marker(z); + while (!(m == 0xc0 || m == 0xc1 || m == 0xc2)) + { + if (stbi__process_marker(z, m) == 0) + return 0; + m = stbi__get_marker(z); + while (m == 0xff) + { + if (stbi__at_eof(z.s) != 0) + return stbi__err("no SOF"); + m = stbi__get_marker(z); + } + } + + z.progressive = m == 0xc2 ? 1 : 0; + if (stbi__process_frame_header(z, scan) == 0) + return 0; + return 1; + } + + public static byte stbi__skip_jpeg_junk_at_end(stbi__jpeg j) + { + while (stbi__at_eof(j.s) == 0) + { + var x = stbi__get8(j.s); + while (x == 0xff) + { + if (stbi__at_eof(j.s) != 0) + return 0xff; + x = stbi__get8(j.s); + if (x != 0x00 && x != 0xff) + return x; + } + } + + return 0xff; + } + + public static int stbi__decode_jpeg_image(stbi__jpeg j) + { + var m = 0; + for (m = 0; m < 4; m++) + { + j.img_comp[m].raw_data = null; + j.img_comp[m].raw_coeff = null; + } + + j.restart_interval = 0; + if (stbi__decode_jpeg_header(j, STBI__SCAN_load) == 0) + return 0; + m = stbi__get_marker(j); + while (!(m == 0xd9)) + if (m == 0xda) + { + if (stbi__process_scan_header(j) == 0) + return 0; + if (stbi__parse_entropy_coded_data(j) == 0) + return 0; + if (j.marker == 0xff) + j.marker = stbi__skip_jpeg_junk_at_end(j); + + m = stbi__get_marker(j); + if (m >= 0xd0 && m <= 0xd7) + m = stbi__get_marker(j); + } + else if (m == 0xdc) + { + var Ld = stbi__get16be(j.s); + var NL = (uint)stbi__get16be(j.s); + if (Ld != 4) + return stbi__err("bad DNL len"); + if (NL != j.s.img_y) + return stbi__err("bad DNL height"); + m = stbi__get_marker(j); + } + else + { + if (stbi__process_marker(j, m) == 0) + return 1; + m = stbi__get_marker(j); + } + + if (j.progressive != 0) + stbi__jpeg_finish(j); + return 1; + } + + public static byte* resample_row_1(byte* _out_, byte* in_near, byte* in_far, int w, int hs) + { + return in_near; + } + + public static byte* stbi__resample_row_v_2(byte* _out_, byte* in_near, byte* in_far, int w, int hs) + { + var i = 0; + for (i = 0; i < w; ++i) + _out_[i] = (byte)((3 * in_near[i] + in_far[i] + 2) >> 2); + + return _out_; + } + + public static byte* stbi__resample_row_h_2(byte* _out_, byte* in_near, byte* in_far, int w, int hs) + { + var i = 0; + var input = in_near; + if (w == 1) + { + _out_[0] = _out_[1] = input[0]; + return _out_; + } + + _out_[0] = input[0]; + _out_[1] = (byte)((input[0] * 3 + input[1] + 2) >> 2); + for (i = 1; i < w - 1; ++i) + { + var n = 3 * input[i] + 2; + _out_[i * 2 + 0] = (byte)((n + input[i - 1]) >> 2); + _out_[i * 2 + 1] = (byte)((n + input[i + 1]) >> 2); + } + + _out_[i * 2 + 0] = (byte)((input[w - 2] * 3 + input[w - 1] + 2) >> 2); + _out_[i * 2 + 1] = input[w - 1]; + return _out_; + } + + public static byte* stbi__resample_row_hv_2(byte* _out_, byte* in_near, byte* in_far, int w, int hs) + { + var i = 0; + var t0 = 0; + var t1 = 0; + if (w == 1) + { + _out_[0] = _out_[1] = (byte)((3 * in_near[0] + in_far[0] + 2) >> 2); + return _out_; + } + + t1 = 3 * in_near[0] + in_far[0]; + _out_[0] = (byte)((t1 + 2) >> 2); + for (i = 1; i < w; ++i) + { + t0 = t1; + t1 = 3 * in_near[i] + in_far[i]; + _out_[i * 2 - 1] = (byte)((3 * t0 + t1 + 8) >> 4); + _out_[i * 2] = (byte)((3 * t1 + t0 + 8) >> 4); + } + + _out_[w * 2 - 1] = (byte)((t1 + 2) >> 2); + return _out_; + } + + public static byte* stbi__resample_row_generic(byte* _out_, byte* in_near, byte* in_far, int w, int hs) + { + var i = 0; + var j = 0; + for (i = 0; i < w; ++i) + for (j = 0; j < hs; ++j) + _out_[i * hs + j] = in_near[i]; + + return _out_; + } + + public static void stbi__YCbCr_to_RGB_row(byte* _out_, byte* y, byte* pcb, byte* pcr, int count, int step) + { + var i = 0; + for (i = 0; i < count; ++i) + { + var y_fixed = (y[i] << 20) + (1 << 19); + var r = 0; + var g = 0; + var b = 0; + var cr = pcr[i] - 128; + var cb = pcb[i] - 128; + r = y_fixed + cr * ((int)(1.40200f * 4096.0f + 0.5f) << 8); + g = (int)(y_fixed + cr * -((int)(0.71414f * 4096.0f + 0.5f) << 8) + + ((cb * -((int)(0.34414f * 4096.0f + 0.5f) << 8)) & 0xffff0000)); + b = y_fixed + cb * ((int)(1.77200f * 4096.0f + 0.5f) << 8); + r >>= 20; + g >>= 20; + b >>= 20; + if ((uint)r > 255) + { + if (r < 0) + r = 0; + else + r = 255; + } + + if ((uint)g > 255) + { + if (g < 0) + g = 0; + else + g = 255; + } + + if ((uint)b > 255) + { + if (b < 0) + b = 0; + else + b = 255; + } + + _out_[0] = (byte)r; + _out_[1] = (byte)g; + _out_[2] = (byte)b; + _out_[3] = 255; + _out_ += step; + } + } + + public static void stbi__setup_jpeg(stbi__jpeg j) + { + j.idct_block_kernel = stbi__idct_block; + j.YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j.resample_row_hv_2_kernel = stbi__resample_row_hv_2; + } + + public static void stbi__cleanup_jpeg(stbi__jpeg j) + { + stbi__free_jpeg_components(j, j.s.img_n, 0); + } + + public static byte* load_jpeg_image(stbi__jpeg z, int* out_x, int* out_y, int* comp, int req_comp) + { + var n = 0; + var decode_n = 0; + var is_rgb = 0; + z.s.img_n = 0; + if (req_comp < 0 || req_comp > 4) + return (byte*)(ulong)(stbi__err("bad req_comp") != 0 ? 0 : 0); + if (stbi__decode_jpeg_image(z) == 0) + { + stbi__cleanup_jpeg(z); + return null; + } + + n = req_comp != 0 ? req_comp : z.s.img_n >= 3 ? 3 : 1; + is_rgb = z.s.img_n == 3 && (z.rgb == 3 || (z.app14_color_transform == 0 && z.jfif == 0)) ? 1 : 0; + if (z.s.img_n == 3 && n < 3 && is_rgb == 0) + decode_n = 1; + else + decode_n = z.s.img_n; + if (decode_n <= 0) + { + stbi__cleanup_jpeg(z); + return null; + } + + { + var k = 0; + uint i = 0; + uint j = 0; + byte* output; + var coutput = stackalloc byte*[] { null, null, null, null }; + var res_comp = new stbi__resample[4]; + res_comp[0] = new stbi__resample(); + res_comp[1] = new stbi__resample(); + res_comp[2] = new stbi__resample(); + res_comp[3] = new stbi__resample(); + for (k = 0; k < decode_n; ++k) + { + var r = res_comp[k]; + z.img_comp[k].linebuf = (byte*)stbi__malloc(z.s.img_x + 3); + if (z.img_comp[k].linebuf == null) + { + stbi__cleanup_jpeg(z); + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + r.hs = z.img_h_max / z.img_comp[k].h; + r.vs = z.img_v_max / z.img_comp[k].v; + r.ystep = r.vs >> 1; + r.w_lores = (int)((z.s.img_x + r.hs - 1) / r.hs); + r.ypos = 0; + r.line0 = r.line1 = z.img_comp[k].data; + if (r.hs == 1 && r.vs == 1) + r.resample = resample_row_1; + else if (r.hs == 1 && r.vs == 2) + r.resample = stbi__resample_row_v_2; + else if (r.hs == 2 && r.vs == 1) + r.resample = stbi__resample_row_h_2; + else if (r.hs == 2 && r.vs == 2) + r.resample = z.resample_row_hv_2_kernel; + else + r.resample = stbi__resample_row_generic; + } + + output = (byte*)stbi__malloc_mad3(n, (int)z.s.img_x, (int)z.s.img_y, 1); + if (output == null) + { + stbi__cleanup_jpeg(z); + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + for (j = 0; j < z.s.img_y; ++j) + { + var _out_ = output + n * z.s.img_x * j; + for (k = 0; k < decode_n; ++k) + { + var r = res_comp[k]; + var y_bot = r.ystep >= r.vs >> 1 ? 1 : 0; + coutput[k] = r.resample(z.img_comp[k].linebuf, y_bot != 0 ? r.line1 : r.line0, + y_bot != 0 ? r.line0 : r.line1, r.w_lores, r.hs); + if (++r.ystep >= r.vs) + { + r.ystep = 0; + r.line0 = r.line1; + if (++r.ypos < z.img_comp[k].y) + r.line1 += z.img_comp[k].w2; + } + } + + if (n >= 3) + { + var y = coutput[0]; + if (z.s.img_n == 3) + { + if (is_rgb != 0) + for (i = 0; i < z.s.img_x; ++i) + { + _out_[0] = y[i]; + _out_[1] = coutput[1][i]; + _out_[2] = coutput[2][i]; + _out_[3] = 255; + _out_ += n; + } + else + z.YCbCr_to_RGB_kernel(_out_, y, coutput[1], coutput[2], (int)z.s.img_x, n); + } + else if (z.s.img_n == 4) + { + if (z.app14_color_transform == 0) + { + for (i = 0; i < z.s.img_x; ++i) + { + var m = coutput[3][i]; + _out_[0] = stbi__blinn_8x8(coutput[0][i], m); + _out_[1] = stbi__blinn_8x8(coutput[1][i], m); + _out_[2] = stbi__blinn_8x8(coutput[2][i], m); + _out_[3] = 255; + _out_ += n; + } + } + else if (z.app14_color_transform == 2) + { + z.YCbCr_to_RGB_kernel(_out_, y, coutput[1], coutput[2], (int)z.s.img_x, n); + for (i = 0; i < z.s.img_x; ++i) + { + var m = coutput[3][i]; + _out_[0] = stbi__blinn_8x8((byte)(255 - _out_[0]), m); + _out_[1] = stbi__blinn_8x8((byte)(255 - _out_[1]), m); + _out_[2] = stbi__blinn_8x8((byte)(255 - _out_[2]), m); + _out_ += n; + } + } + else + { + z.YCbCr_to_RGB_kernel(_out_, y, coutput[1], coutput[2], (int)z.s.img_x, n); + } + } + else + { + for (i = 0; i < z.s.img_x; ++i) + { + _out_[0] = _out_[1] = _out_[2] = y[i]; + _out_[3] = 255; + _out_ += n; + } + } + } + else + { + if (is_rgb != 0) + { + if (n == 1) + for (i = 0; i < z.s.img_x; ++i) + *_out_++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else + for (i = 0; i < z.s.img_x; ++i, _out_ += 2) + { + _out_[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + _out_[1] = 255; + } + } + else if (z.s.img_n == 4 && z.app14_color_transform == 0) + { + for (i = 0; i < z.s.img_x; ++i) + { + var m = coutput[3][i]; + var r = stbi__blinn_8x8(coutput[0][i], m); + var g = stbi__blinn_8x8(coutput[1][i], m); + var b = stbi__blinn_8x8(coutput[2][i], m); + _out_[0] = stbi__compute_y(r, g, b); + _out_[1] = 255; + _out_ += n; + } + } + else if (z.s.img_n == 4 && z.app14_color_transform == 2) + { + for (i = 0; i < z.s.img_x; ++i) + { + _out_[0] = stbi__blinn_8x8((byte)(255 - coutput[0][i]), coutput[3][i]); + _out_[1] = 255; + _out_ += n; + } + } + else + { + var y = coutput[0]; + if (n == 1) + for (i = 0; i < z.s.img_x; ++i) + _out_[i] = y[i]; + else + for (i = 0; i < z.s.img_x; ++i) + { + *_out_++ = y[i]; + *_out_++ = 255; + } + } + } + } + + stbi__cleanup_jpeg(z); + *out_x = (int)z.s.img_x; + *out_y = (int)z.s.img_y; + if (comp != null) + *comp = z.s.img_n >= 3 ? 3 : 1; + return output; + } + } + + public static int stbi__jpeg_info_raw(stbi__jpeg j, int* x, int* y, int* comp) + { + if (stbi__decode_jpeg_header(j, STBI__SCAN_header) == 0) + { + stbi__rewind(j.s); + return 0; + } + + if (x != null) + *x = (int)j.s.img_x; + if (y != null) + *y = (int)j.s.img_y; + if (comp != null) + *comp = j.s.img_n >= 3 ? 3 : 1; + return 1; + } + + [StructLayout(LayoutKind.Sequential)] + public struct stbi__huffman + { + public fixed byte fast[512]; + public fixed ushort code[256]; + public fixed byte values[256]; + public fixed byte size[257]; + public fixed uint maxcode[18]; + public fixed int delta[17]; + } + + public class stbi__jpeg + { + public int app14_color_transform; + public int code_bits; + public uint code_buffer; + public ushort[][] dequant = Utility.CreateArray(4, 64); + public int eob_run; + public short[][] fast_ac = Utility.CreateArray(4, 512); + public stbi__huffman[] huff_ac = new stbi__huffman[4]; + public stbi__huffman[] huff_dc = new stbi__huffman[4]; + public delegate0 idct_block_kernel; + public unnamed1[] img_comp = new unnamed1[4]; + public int img_h_max; + public int img_mcu_h; + public int img_mcu_w; + public int img_mcu_x; + public int img_mcu_y; + public int img_v_max; + public int jfif; + public byte marker; + public int nomore; + public int[] order = new int[4]; + public int progressive; + public delegate2 resample_row_hv_2_kernel; + public int restart_interval; + public int rgb; + public stbi__context s; + public int scan_n; + public int spec_end; + public int spec_start; + public int succ_high; + public int succ_low; + public int todo; + public delegate1 YCbCr_to_RGB_kernel; + + [StructLayout(LayoutKind.Sequential)] + public struct unnamed1 + { + public int id; + public int h; + public int v; + public int tq; + public int hd; + public int ha; + public int dc_pred; + public int x; + public int y; + public int w2; + public int h2; + public byte* data; + public void* raw_data; + public void* raw_coeff; + public byte* linebuf; + public short* coeff; + public int coeff_w; + public int coeff_h; + } + } + + public class stbi__resample + { + public int hs; + public byte* line0; + public byte* line1; + public delegate2 resample; + public int vs; + public int w_lores; + public int ypos; + public int ystep; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Png.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Png.cs new file mode 100644 index 0000000..02d12bf --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Png.cs @@ -0,0 +1,818 @@ +// Generated by Sichem at 9/16/2024 9:09:30 AM + +using Misaki.HighPerformance.Image.Runtime; +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public const int STBI__F_none = 0; + public const int STBI__F_sub = 1; + public const int STBI__F_up = 2; + public const int STBI__F_avg = 3; + public const int STBI__F_paeth = 4; + public const int STBI__F_avg_first = 5; + + public static byte[] first_row_filter = + { STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_sub }; + + public static byte[] stbi__check_png_header_png_sig = { 137, 80, 78, 71, 13, 10, 26, 10 }; + public static byte[] stbi__depth_scale_table = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; + + public static int stbi__png_test(stbi__context s) + { + var r = 0; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; + } + + public static void* stbi__png_load(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri) + { + var p = new stbi__png(); + p.s = s; + return stbi__do_png(p, x, y, comp, req_comp, ri); + } + + public static int stbi__png_info(stbi__context s, int* x, int* y, int* comp) + { + var p = new stbi__png(); + p.s = s; + return stbi__png_info_raw(p, x, y, comp); + } + + public static int stbi__png_is16(stbi__context s) + { + var p = new stbi__png(); + p.s = s; + if (stbi__png_info_raw(p, null, null, null) == 0) + return 0; + if (p.depth != 16) + { + stbi__rewind(p.s); + return 0; + } + + return 1; + } + + public static stbi__pngchunk stbi__get_chunk_header(stbi__context s) + { + var c = new stbi__pngchunk(); + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; + } + + public static int stbi__check_png_header(stbi__context s) + { + var i = 0; + for (i = 0; i < 8; ++i) + if (stbi__get8(s) != stbi__check_png_header_png_sig[i]) + return stbi__err("bad png sig"); + + return 1; + } + + public static int stbi__paeth(int a, int b, int c) + { + var thresh = c * 3 - (a + b); + var lo = a < b ? a : b; + var hi = a < b ? b : a; + var t0 = hi <= thresh ? lo : c; + var t1 = thresh <= lo ? hi : t0; + return t1; + } + + public static void stbi__create_png_alpha_expand8(byte* dest, byte* src, uint x, int img_n) + { + var i = 0; + if (img_n == 1) + for (i = (int)(x - 1); i >= 0; --i) + { + dest[i * 2 + 1] = 255; + dest[i * 2 + 0] = src[i]; + } + else + for (i = (int)(x - 1); i >= 0; --i) + { + dest[i * 4 + 3] = 255; + dest[i * 4 + 2] = src[i * 3 + 2]; + dest[i * 4 + 1] = src[i * 3 + 1]; + dest[i * 4 + 0] = src[i * 3 + 0]; + } + } + + + public static int stbi__create_png_image_raw(stbi__png a, byte* raw, uint raw_len, int out_n, uint x, uint y, + int depth, int color) + { + var bytes = depth == 16 ? 2 : 1; + var s = a.s; + uint i = 0; + uint j = 0; + var stride = (uint)(x * out_n * bytes); + uint img_len = 0; + uint img_width_bytes = 0; + byte* filter_buf; + var all_ok = 1; + var k = 0; + var img_n = s.img_n; + var output_bytes = out_n * bytes; + var filter_bytes = img_n * bytes; + var width = (int)x; + a._out_ = (byte*)stbi__malloc_mad3((int)x, (int)y, output_bytes, 0); + if (a._out_ == null) + return stbi__err("outofmem"); + if (stbi__mad3sizes_valid(img_n, (int)x, depth, 7) == 0) + return stbi__err("too large"); + img_width_bytes = (uint)((img_n * x * depth + 7) >> 3); + if (stbi__mad2sizes_valid((int)img_width_bytes, (int)y, (int)img_width_bytes) == 0) + return stbi__err("too large"); + img_len = (img_width_bytes + 1) * y; + if (raw_len < img_len) + return stbi__err("not enough pixels"); + filter_buf = (byte*)stbi__malloc_mad2((int)img_width_bytes, 2, 0); + if (filter_buf == null) + return stbi__err("outofmem"); + if (depth < 8) + { + filter_bytes = 1; + width = (int)img_width_bytes; + } + + for (j = 0; j < y; ++j) + { + var cur = filter_buf + (j & 1) * img_width_bytes; + var prior = filter_buf + (~j & 1) * img_width_bytes; + var dest = a._out_ + stride * j; + var nk = width * filter_bytes; + int filter = *raw++; + if (filter > 4) + { + all_ok = stbi__err("invalid filter"); + break; + } + + if (j == 0) + filter = first_row_filter[filter]; + switch (filter) + { + case STBI__F_none: + CRuntime.memcpy(cur, raw, (ulong)nk); + break; + case STBI__F_sub: + CRuntime.memcpy(cur, raw, (ulong)filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = (byte)((raw[k] + cur[k - filter_bytes]) & 255); + + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = (byte)((raw[k] + prior[k]) & 255); + + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = (byte)((raw[k] + (prior[k] >> 1)) & 255); + + for (k = filter_bytes; k < nk; ++k) + cur[k] = (byte)((raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)) & 255); + + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = (byte)((raw[k] + prior[k]) & 255); + + for (k = filter_bytes; k < nk; ++k) + cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], + prior[k - filter_bytes])) & 255); + + break; + case STBI__F_avg_first: + CRuntime.memcpy(cur, raw, (ulong)filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = (byte)((raw[k] + (cur[k - filter_bytes] >> 1)) & 255); + + break; + } + + raw += nk; + if (depth < 8) + { + var scale = (byte)(color == 0 ? stbi__depth_scale_table[depth] : 1); + var _in_ = cur; + var _out_ = dest; + byte inb = 0; + var nsmp = (uint)(x * img_n); + if (depth == 4) + for (i = 0; i < nsmp; ++i) + { + if ((i & 1) == 0) + inb = *_in_++; + *_out_++ = (byte)(scale * (inb >> 4)); + inb <<= 4; + } + else if (depth == 2) + for (i = 0; i < nsmp; ++i) + { + if ((i & 3) == 0) + inb = *_in_++; + *_out_++ = (byte)(scale * (inb >> 6)); + inb <<= 2; + } + else + for (i = 0; i < nsmp; ++i) + { + if ((i & 7) == 0) + inb = *_in_++; + *_out_++ = (byte)(scale * (inb >> 7)); + inb <<= 1; + } + + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } + else if (depth == 8) + { + if (img_n == out_n) + CRuntime.memcpy(dest, cur, (ulong)(x * img_n)); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } + else if (depth == 16) + { + var dest16 = (ushort*)dest; + var nsmp = (uint)(x * img_n); + if (img_n == out_n) + { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (ushort)((cur[0] << 8) | cur[1]); + } + else + { + if (img_n == 1) + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) + { + dest16[0] = (ushort)((cur[0] << 8) | cur[1]); + dest16[1] = 0xffff; + } + else + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) + { + dest16[0] = (ushort)((cur[0] << 8) | cur[1]); + dest16[1] = (ushort)((cur[2] << 8) | cur[3]); + dest16[2] = (ushort)((cur[4] << 8) | cur[5]); + dest16[3] = 0xffff; + } + } + } + } + + CRuntime.free(filter_buf); + if (all_ok == 0) + return 0; + return 1; + } + + public static int stbi__create_png_image(stbi__png a, byte* image_data, uint image_data_len, int out_n, + int depth, int color, int interlaced) + { + var bytes = depth == 16 ? 2 : 1; + var out_bytes = out_n * bytes; + byte* final; + var p = 0; + if (interlaced == 0) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a.s.img_x, a.s.img_y, depth, + color); + final = (byte*)stbi__malloc_mad3((int)a.s.img_x, (int)a.s.img_y, out_bytes, 0); + if (final == null) + { + return stbi__err("outofmem"); + } + + var xorig = stackalloc int[] { 0, 4, 0, 2, 0, 1, 0 }; + var yorig = stackalloc int[] { 0, 0, 4, 0, 2, 0, 1 }; + var xspc = stackalloc int[] { 8, 8, 4, 4, 2, 2, 1 }; + var yspc = stackalloc int[] { 8, 8, 8, 4, 4, 2, 2 }; + for (p = 0; p < 7; ++p) + { + var i = 0; + var j = 0; + var x = 0; + var y = 0; + x = (int)((a.s.img_x - xorig[p] + xspc[p] - 1) / xspc[p]); + y = (int)((a.s.img_y - yorig[p] + yspc[p] - 1) / yspc[p]); + if (x != 0 && y != 0) + { + var img_len = (uint)((((a.s.img_n * x * depth + 7) >> 3) + 1) * y); + if (stbi__create_png_image_raw(a, image_data, image_data_len, out_n, (uint)x, (uint)y, depth, + color) == 0) + { + CRuntime.free(final); + return 0; + } + + for (j = 0; j < y; ++j) + for (i = 0; i < x; ++i) + { + var out_y = j * yspc[p] + yorig[p]; + var out_x = i * xspc[p] + xorig[p]; + CRuntime.memcpy(final + out_y * a.s.img_x * out_bytes + out_x * out_bytes, + a._out_ + (j * x + i) * out_bytes, (ulong)out_bytes); + } + + CRuntime.free(a._out_); + image_data += img_len; + image_data_len -= img_len; + } + } + + a._out_ = final; + return 1; + } + + public static int stbi__compute_transparency(stbi__png z, byte* tc, int out_n) + { + var s = z.s; + uint i = 0; + var pixel_count = s.img_x * s.img_y; + var p = z._out_; + if (out_n == 2) + for (i = 0; i < pixel_count; ++i) + { + p[1] = (byte)(p[0] == tc[0] ? 0 : 255); + p += 2; + } + else + for (i = 0; i < pixel_count; ++i) + { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + + return 1; + } + + public static int stbi__compute_transparency16(stbi__png z, ushort* tc, int out_n) + { + var s = z.s; + uint i = 0; + var pixel_count = s.img_x * s.img_y; + var p = (ushort*)z._out_; + if (out_n == 2) + for (i = 0; i < pixel_count; ++i) + { + p[1] = (ushort)(p[0] == tc[0] ? 0 : 65535); + p += 2; + } + else + for (i = 0; i < pixel_count; ++i) + { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + + return 1; + } + + public static int stbi__expand_png_palette(stbi__png a, byte* palette, int len, int pal_img_n) + { + uint i = 0; + var pixel_count = a.s.img_x * a.s.img_y; + byte* p; + byte* temp_out; + var orig = a._out_; + p = (byte*)stbi__malloc_mad2((int)pixel_count, pal_img_n, 0); + if (p == null) + return stbi__err("outofmem"); + temp_out = p; + if (pal_img_n == 3) + for (i = 0; i < pixel_count; ++i) + { + var n = orig[i] * 4; + p[0] = palette[n]; + p[1] = palette[n + 1]; + p[2] = palette[n + 2]; + p += 3; + } + else + for (i = 0; i < pixel_count; ++i) + { + var n = orig[i] * 4; + p[0] = palette[n]; + p[1] = palette[n + 1]; + p[2] = palette[n + 2]; + p[3] = palette[n + 3]; + p += 4; + } + + CRuntime.free(a._out_); + a._out_ = temp_out; + return 1; + } + + public static void stbi__de_iphone(stbi__png z) + { + var s = z.s; + uint i = 0; + var pixel_count = s.img_x * s.img_y; + var p = z._out_; + if (s.img_out_n == 3) + { + for (i = 0; i < pixel_count; ++i) + { + var t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } + else + { + if ((stbi__unpremultiply_on_load_set != 0 + ? stbi__unpremultiply_on_load_local + : stbi__unpremultiply_on_load_global) != 0) + for (i = 0; i < pixel_count; ++i) + { + var a = p[3]; + var t = p[0]; + if (a != 0) + { + var half = (byte)(a / 2); + p[0] = (byte)((p[2] * 255 + half) / a); + p[1] = (byte)((p[1] * 255 + half) / a); + p[2] = (byte)((t * 255 + half) / a); + } + else + { + p[0] = p[2]; + p[2] = t; + } + + p += 4; + } + else + for (i = 0; i < pixel_count; ++i) + { + var t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } + + public static int stbi__parse_png_file(stbi__png z, int scan, int req_comp) + { + var palette = stackalloc byte[1024]; + byte pal_img_n = 0; + byte has_trans = 0; + var tc = stackalloc byte[] { 0, 0, 0 }; + var tc16 = stackalloc ushort[3]; + uint ioff = 0; + uint idata_limit = 0; + uint i = 0; + uint pal_len = 0; + var first = 1; + var k = 0; + var interlace = 0; + var color = 0; + var is_iphone = 0; + var s = z.s; + z.expanded = null; + z.idata = null; + z._out_ = null; + if (stbi__check_png_header(s) == 0) + return 0; + if (scan == STBI__SCAN_type) + return 1; + for (; ; ) + { + var c = stbi__get_chunk_header(s); + switch (c.type) + { + case ((uint)67 << 24) + ((uint)103 << 16) + ((uint)66 << 8) + 73: + is_iphone = 1; + stbi__skip(s, (int)c.length); + break; + case ((uint)73 << 24) + ((uint)72 << 16) + ((uint)68 << 8) + 82: + { + var comp = 0; + var filter = 0; + if (first == 0) + return stbi__err("multiple IHDR"); + first = 0; + if (c.length != 13) + return stbi__err("bad IHDR len"); + s.img_x = stbi__get32be(s); + s.img_y = stbi__get32be(s); + if (s.img_y > 1 << 24) + return stbi__err("too large"); + if (s.img_x > 1 << 24) + return stbi__err("too large"); + z.depth = stbi__get8(s); + if (z.depth != 1 && z.depth != 2 && z.depth != 4 && z.depth != 8 && z.depth != 16) + return stbi__err("1/2/4/8/16-bit only"); + color = stbi__get8(s); + if (color > 6) + return stbi__err("bad ctype"); + if (color == 3 && z.depth == 16) + return stbi__err("bad ctype"); + if (color == 3) + pal_img_n = 3; + else if ((color & 1) != 0) + return stbi__err("bad ctype"); + comp = stbi__get8(s); + if (comp != 0) + return stbi__err("bad comp method"); + filter = stbi__get8(s); + if (filter != 0) + return stbi__err("bad filter method"); + interlace = stbi__get8(s); + if (interlace > 1) + return stbi__err("bad interlace method"); + if (s.img_x == 0 || s.img_y == 0) + return stbi__err("0-pixel image"); + if (pal_img_n == 0) + { + s.img_n = ((color & 2) != 0 ? 3 : 1) + ((color & 4) != 0 ? 1 : 0); + if ((1 << 30) / s.img_x / s.img_n < s.img_y) + return stbi__err("too large"); + } + else + { + s.img_n = 1; + if ((1 << 30) / s.img_x / 4 < s.img_y) + return stbi__err("too large"); + } + + break; + } + + case ((uint)80 << 24) + ((uint)76 << 16) + ((uint)84 << 8) + 69: + { + if (first != 0) + return stbi__err("first not IHDR"); + if (c.length > 256 * 3) + return stbi__err("invalid PLTE"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) + return stbi__err("invalid PLTE"); + for (i = 0; i < pal_len; ++i) + { + palette[i * 4 + 0] = stbi__get8(s); + palette[i * 4 + 1] = stbi__get8(s); + palette[i * 4 + 2] = stbi__get8(s); + palette[i * 4 + 3] = 255; + } + + break; + } + + case ((uint)116 << 24) + ((uint)82 << 16) + ((uint)78 << 8) + 83: + { + if (first != 0) + return stbi__err("first not IHDR"); + if (z.idata != null) + return stbi__err("tRNS after IDAT"); + if (pal_img_n != 0) + { + if (scan == STBI__SCAN_header) + { + s.img_n = 4; + return 1; + } + + if (pal_len == 0) + return stbi__err("tRNS before PLTE"); + if (c.length > pal_len) + return stbi__err("bad tRNS len"); + pal_img_n = 4; + for (i = 0; i < c.length; ++i) + palette[i * 4 + 3] = stbi__get8(s); + } + else + { + if ((s.img_n & 1) == 0) + return stbi__err("tRNS with alpha"); + if (c.length != (uint)s.img_n * 2) + return stbi__err("bad tRNS len"); + has_trans = 1; + if (scan == STBI__SCAN_header) + { + ++s.img_n; + return 1; + } + + if (z.depth == 16) + for (k = 0; k < s.img_n && k < 3; ++k) + tc16[k] = (ushort)stbi__get16be(s); + else + for (k = 0; k < s.img_n && k < 3; ++k) + tc[k] = (byte)((byte)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z.depth]); + } + + break; + } + + case ((uint)73 << 24) + ((uint)68 << 16) + ((uint)65 << 8) + 84: + { + if (first != 0) + return stbi__err("first not IHDR"); + if (pal_img_n != 0 && pal_len == 0) + return stbi__err("no PLTE"); + if (scan == STBI__SCAN_header) + { + if (pal_img_n != 0) + s.img_n = pal_img_n; + return 1; + } + + if (c.length > 1u << 30) + return stbi__err("IDAT size limit"); + if ((int)(ioff + c.length) < (int)ioff) + return 0; + if (ioff + c.length > idata_limit) + { + var idata_limit_old = idata_limit; + byte* p; + if (idata_limit == 0) + idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + + p = (byte*)CRuntime.realloc(z.idata, (ulong)idata_limit); + if (p == null) + return stbi__err("outofmem"); + z.idata = p; + } + + if (stbi__getn(s, z.idata + ioff, (int)c.length) == 0) + return stbi__err("outofdata"); + ioff += c.length; + break; + } + + case ((uint)73 << 24) + ((uint)69 << 16) + ((uint)78 << 8) + 68: + { + uint raw_len = 0; + uint bpl = 0; + if (first != 0) + return stbi__err("first not IHDR"); + if (scan != STBI__SCAN_load) + return 1; + if (z.idata == null) + return stbi__err("no IDAT"); + bpl = (uint)((s.img_x * z.depth + 7) / 8); + raw_len = (uint)(bpl * s.img_y * s.img_n + s.img_y); + z.expanded = (byte*)stbi_zlib_decode_malloc_guesssize_headerflag((sbyte*)z.idata, (int)ioff, + (int)raw_len, (int*)&raw_len, is_iphone == 0 ? 1 : 0); + if (z.expanded == null) + return 0; + CRuntime.free(z.idata); + z.idata = null; + if ((req_comp == s.img_n + 1 && req_comp != 3 && pal_img_n == 0) || has_trans != 0) + s.img_out_n = s.img_n + 1; + else + s.img_out_n = s.img_n; + if (stbi__create_png_image(z, z.expanded, raw_len, s.img_out_n, z.depth, color, interlace) == 0) + return 0; + if (has_trans != 0) + { + if (z.depth == 16) + { + if (stbi__compute_transparency16(z, tc16, s.img_out_n) == 0) + return 0; + } + else + { + if (stbi__compute_transparency(z, tc, s.img_out_n) == 0) + return 0; + } + } + + if (is_iphone != 0 && + (stbi__de_iphone_flag_set != 0 + ? stbi__de_iphone_flag_local + : stbi__de_iphone_flag_global) != 0 && s.img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n != 0) + { + s.img_n = pal_img_n; + s.img_out_n = pal_img_n; + if (req_comp >= 3) + s.img_out_n = req_comp; + if (stbi__expand_png_palette(z, palette, (int)pal_len, s.img_out_n) == 0) + return 0; + } + else if (has_trans != 0) + { + ++s.img_n; + } + + CRuntime.free(z.expanded); + z.expanded = null; + stbi__get32be(s); + return 1; + } + + default: + if (first != 0) + return stbi__err("first not IHDR"); + if ((c.type & (1 << 29)) == 0) + { + stbi__parse_png_file_invalid_chunk[0] = (char)((c.type >> 24) & 255); + stbi__parse_png_file_invalid_chunk[1] = (char)((c.type >> 16) & 255); + stbi__parse_png_file_invalid_chunk[2] = (char)((c.type >> 8) & 255); + stbi__parse_png_file_invalid_chunk[3] = (char)((c.type >> 0) & 255); + return stbi__err(new string(stbi__parse_png_file_invalid_chunk)); + } + + stbi__skip(s, (int)c.length); + break; + } + + stbi__get32be(s); + } + } + + public static void* stbi__do_png(stbi__png p, int* x, int* y, int* n, int req_comp, stbi__result_info* ri) + { + void* result = null; + if (req_comp < 0 || req_comp > 4) + return (byte*)(ulong)(stbi__err("bad req_comp") != 0 ? 0 : 0); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp) != 0) + { + if (p.depth <= 8) + ri->bits_per_channel = 8; + else if (p.depth == 16) + ri->bits_per_channel = 16; + else + return (byte*)(ulong)(stbi__err("bad bits_per_channel") != 0 ? 0 : 0); + result = p._out_; + p._out_ = null; + if (req_comp != 0 && req_comp != p.s.img_out_n) + { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((byte*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y); + else + result = stbi__convert_format16((ushort*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y); + p.s.img_out_n = req_comp; + if (result == null) + return result; + } + + *x = (int)p.s.img_x; + *y = (int)p.s.img_y; + if (n != null) + *n = p.s.img_n; + } + + CRuntime.free(p._out_); + p._out_ = null; + CRuntime.free(p.expanded); + p.expanded = null; + CRuntime.free(p.idata); + p.idata = null; + return result; + } + + public static int stbi__png_info_raw(stbi__png p, int* x, int* y, int* comp) + { + if (stbi__parse_png_file(p, STBI__SCAN_header, 0) == 0) + { + stbi__rewind(p.s); + return 0; + } + + if (x != null) + *x = (int)p.s.img_x; + if (y != null) + *y = (int)p.s.img_y; + if (comp != null) + *comp = p.s.img_n; + return 1; + } + + public class stbi__png + { + public byte* _out_; + public int depth; + public byte* expanded; + public byte* idata; + public stbi__context s; + } + + [StructLayout(LayoutKind.Sequential)] + public struct stbi__pngchunk + { + public uint length; + public uint type; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Psd.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Psd.cs new file mode 100644 index 0000000..78633ad --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Psd.cs @@ -0,0 +1,312 @@ +// Generated by Sichem at 12/24/2021 8:28:15 PM + +using Misaki.HighPerformance.Image.Runtime; +// Generated by Sichem at 12/24/2021 8:28:15 PM + +using Misaki.HighPerformance.Image; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public static int stbi__psd_test(stbi__context s) + { + var r = stbi__get32be(s) == 0x38425053 ? 1 : 0; + stbi__rewind(s); + return r; + } + + public static void* stbi__psd_load(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri, int bpc) + { + var pixelCount = 0; + var channelCount = 0; + var compression = 0; + var channel = 0; + var i = 0; + var bitdepth = 0; + var w = 0; + var h = 0; + byte* _out_; + if (stbi__get32be(s) != 0x38425053) + return (byte*)(ulong)(stbi__err("not PSD") != 0 ? 0 : 0); + if (stbi__get16be(s) != 1) + return (byte*)(ulong)(stbi__err("wrong version") != 0 ? 0 : 0); + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return (byte*)(ulong)(stbi__err("wrong channel count") != 0 ? 0 : 0); + h = (int)stbi__get32be(s); + w = (int)stbi__get32be(s); + if (h > 1 << 24) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + if (w > 1 << 24) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return (byte*)(ulong)(stbi__err("unsupported bit depth") != 0 ? 0 : 0); + if (stbi__get16be(s) != 3) + return (byte*)(ulong)(stbi__err("wrong color format") != 0 ? 0 : 0); + stbi__skip(s, (int)stbi__get32be(s)); + stbi__skip(s, (int)stbi__get32be(s)); + stbi__skip(s, (int)stbi__get32be(s)); + compression = stbi__get16be(s); + if (compression > 1) + return (byte*)(ulong)(stbi__err("bad compression") != 0 ? 0 : 0); + if (stbi__mad3sizes_valid(4, w, h, 0) == 0) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + if (compression == 0 && bitdepth == 16 && bpc == 16) + { + _out_ = (byte*)stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } + else + { + _out_ = (byte*)stbi__malloc((ulong)(4 * w * h)); + } + + if (_out_ == null) + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + pixelCount = w * h; + if (compression != 0) + { + stbi__skip(s, h * channelCount * 2); + for (channel = 0; channel < 4; channel++) + { + byte* p; + p = _out_ + channel; + if (channel >= channelCount) + { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (byte)(channel == 3 ? 255 : 0); + } + else + { + if (stbi__psd_decode_rle(s, p, pixelCount) == 0) + { + CRuntime.free(_out_); + return (byte*)(ulong)(stbi__err("corrupt") != 0 ? 0 : 0); + } + } + } + } + else + { + for (channel = 0; channel < 4; channel++) + if (channel >= channelCount) + { + if (bitdepth == 16 && bpc == 16) + { + var q = (ushort*)_out_ + channel; + var val = (ushort)(channel == 3 ? 65535 : 0); + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } + else + { + var p = _out_ + channel; + var val = (byte)(channel == 3 ? 255 : 0); + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } + else + { + if (ri->bits_per_channel == 16) + { + var q = (ushort*)_out_ + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (ushort)stbi__get16be(s); + } + else + { + var p = _out_ + channel; + if (bitdepth == 16) + for (i = 0; i < pixelCount; i++, p += 4) + *p = (byte)(stbi__get16be(s) >> 8); + else + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + + if (channelCount >= 4) + { + if (ri->bits_per_channel == 16) + for (i = 0; i < w * h; ++i) + { + var pixel = (ushort*)_out_ + 4 * i; + if (pixel[3] != 0 && pixel[3] != 65535) + { + var a = pixel[3] / 65535.0f; + var ra = 1.0f / a; + var inv_a = 65535.0f * (1 - ra); + pixel[0] = (ushort)(pixel[0] * ra + inv_a); + pixel[1] = (ushort)(pixel[1] * ra + inv_a); + pixel[2] = (ushort)(pixel[2] * ra + inv_a); + } + } + else + for (i = 0; i < w * h; ++i) + { + var pixel = _out_ + 4 * i; + if (pixel[3] != 0 && pixel[3] != 255) + { + var a = pixel[3] / 255.0f; + var ra = 1.0f / a; + var inv_a = 255.0f * (1 - ra); + pixel[0] = (byte)(pixel[0] * ra + inv_a); + pixel[1] = (byte)(pixel[1] * ra + inv_a); + pixel[2] = (byte)(pixel[2] * ra + inv_a); + } + } + } + + if (req_comp != 0 && req_comp != 4) + { + if (ri->bits_per_channel == 16) + _out_ = (byte*)stbi__convert_format16((ushort*)_out_, 4, req_comp, (uint)w, (uint)h); + else + _out_ = stbi__convert_format(_out_, 4, req_comp, (uint)w, (uint)h); + if (_out_ == null) + return _out_; + } + + if (comp != null) + *comp = 4; + *y = h; + *x = w; + return _out_; + } + + public static int stbi__psd_info(stbi__context s, int* x, int* y, int* comp) + { + var channelCount = 0; + var dummy = 0; + var depth = 0; + if (x == null) + x = &dummy; + if (y == null) + y = &dummy; + if (comp == null) + comp = &dummy; + if (stbi__get32be(s) != 0x38425053) + { + stbi__rewind(s); + return 0; + } + + if (stbi__get16be(s) != 1) + { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + { + stbi__rewind(s); + return 0; + } + + *y = (int)stbi__get32be(s); + *x = (int)stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) + { + stbi__rewind(s); + return 0; + } + + if (stbi__get16be(s) != 3) + { + stbi__rewind(s); + return 0; + } + + *comp = 4; + return 1; + } + + public static int stbi__psd_is16(stbi__context s) + { + var channelCount = 0; + var depth = 0; + if (stbi__get32be(s) != 0x38425053) + { + stbi__rewind(s); + return 0; + } + + if (stbi__get16be(s) != 1) + { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + { + stbi__rewind(s); + return 0; + } + + depth = stbi__get16be(s); + if (depth != 16) + { + stbi__rewind(s); + return 0; + } + + return 1; + } + + public static int stbi__psd_decode_rle(stbi__context s, byte* p, int pixelCount) + { + var count = 0; + var nleft = 0; + var len = 0; + count = 0; + while ((nleft = pixelCount - count) > 0) + { + len = stbi__get8(s); + if (len == 128) + { + } + else if (len < 128) + { + len++; + if (len > nleft) + return 0; + count += len; + while (len != 0) + { + *p = stbi__get8(s); + p += 4; + len--; + } + } + else if (len > 128) + { + byte val = 0; + len = 257 - len; + if (len > nleft) + return 0; + val = stbi__get8(s); + count += len; + while (len != 0) + { + *p = val; + p += 4; + len--; + } + } + } + + return 1; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Tga.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Tga.cs new file mode 100644 index 0000000..ccdec8b --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Tga.cs @@ -0,0 +1,374 @@ +// Generated by Sichem at 12/24/2021 8:28:15 PM + +using Misaki.HighPerformance.Image.Runtime; +// Generated by Sichem at 12/24/2021 8:28:15 PM + +using Misaki.HighPerformance.Image; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public static int stbi__tga_test(stbi__context s) + { + var res = 0; + var sz = 0; + var tga_color_type = 0; + stbi__get8(s); + tga_color_type = stbi__get8(s); + if (tga_color_type > 1) + goto errorEnd; + sz = stbi__get8(s); + if (tga_color_type == 1) + { + if (sz != 1 && sz != 9) + goto errorEnd; + stbi__skip(s, 4); + sz = stbi__get8(s); + if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) + goto errorEnd; + stbi__skip(s, 4); + } + else + { + if (sz != 2 && sz != 3 && sz != 10 && sz != 11) + goto errorEnd; + stbi__skip(s, 9); + } + + if (stbi__get16le(s) < 1) + goto errorEnd; + if (stbi__get16le(s) < 1) + goto errorEnd; + sz = stbi__get8(s); + if (tga_color_type == 1 && sz != 8 && sz != 16) + goto errorEnd; + if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) + goto errorEnd; + res = 1; + errorEnd:; + stbi__rewind(s); + return res; + } + + public static void* stbi__tga_load(stbi__context s, int* x, int* y, int* comp, int req_comp, + stbi__result_info* ri) + { + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + var tga_is_RLE = 0; + var tga_palette_start = stbi__get16le(s); + var tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + var tga_x_origin = stbi__get16le(s); + var tga_y_origin = stbi__get16le(s); + var tga_width = stbi__get16le(s); + var tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + var tga_comp = 0; + var tga_rgb16 = 0; + int tga_inverted = stbi__get8(s); + byte* tga_data; + byte* tga_palette = null; + var i = 0; + var j = 0; + var raw_data = stackalloc byte[4]; + raw_data[0] = 0; + raw_data[1] = 0; + raw_data[2] = 0; + raw_data[3] = 0; + var RLE_count = 0; + var RLE_repeating = 0; + var read_next_pixel = 1; + if (tga_height > 1 << 24) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + if (tga_width > 1 << 24) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + if (tga_image_type >= 8) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + if (tga_indexed != 0) + tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, tga_image_type == 3 ? 1 : 0, &tga_rgb16); + if (tga_comp == 0) + return (byte*)(ulong)(stbi__err("bad format") != 0 ? 0 : 0); + *x = tga_width; + *y = tga_height; + if (comp != null) + *comp = tga_comp; + if (stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0) == 0) + return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); + tga_data = (byte*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (tga_data == null) + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + stbi__skip(s, tga_offset); + if (tga_indexed == 0 && tga_is_RLE == 0 && tga_rgb16 == 0) + { + for (i = 0; i < tga_height; ++i) + { + var row = tga_inverted != 0 ? tga_height - i - 1 : i; + var tga_row = tga_data + row * tga_width * tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } + else + { + if (tga_indexed != 0) + { + if (tga_palette_len == 0) + { + CRuntime.free(tga_data); + return (byte*)(ulong)(stbi__err("bad palette") != 0 ? 0 : 0); + } + + stbi__skip(s, tga_palette_start); + tga_palette = (byte*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (tga_palette == null) + { + CRuntime.free(tga_data); + return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); + } + + if (tga_rgb16 != 0) + { + var pal_entry = tga_palette; + for (i = 0; i < tga_palette_len; ++i) + { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } + else if (stbi__getn(s, tga_palette, tga_palette_len * tga_comp) == 0) + { + CRuntime.free(tga_data); + CRuntime.free(tga_palette); + return (byte*)(ulong)(stbi__err("bad palette") != 0 ? 0 : 0); + } + } + + for (i = 0; i < tga_width * tga_height; ++i) + { + if (tga_is_RLE != 0) + { + if (RLE_count == 0) + { + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } + else if (RLE_repeating == 0) + { + read_next_pixel = 1; + } + } + else + { + read_next_pixel = 1; + } + + if (read_next_pixel != 0) + { + if (tga_indexed != 0) + { + var pal_idx = tga_bits_per_pixel == 8 ? stbi__get8(s) : stbi__get16le(s); + if (pal_idx >= tga_palette_len) + pal_idx = 0; + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) + raw_data[j] = tga_palette[pal_idx + j]; + } + else if (tga_rgb16 != 0) + { + stbi__tga_read_rgb16(s, raw_data); + } + else + { + for (j = 0; j < tga_comp; ++j) + raw_data[j] = stbi__get8(s); + } + + read_next_pixel = 0; + } + + for (j = 0; j < tga_comp; ++j) + tga_data[i * tga_comp + j] = raw_data[j]; + --RLE_count; + } + + if (tga_inverted != 0) + for (j = 0; j * 2 < tga_height; ++j) + { + var index1 = j * tga_width * tga_comp; + var index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + var temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + + if (tga_palette != null) + CRuntime.free(tga_palette); + } + + if (tga_comp >= 3 && tga_rgb16 == 0) + { + var tga_pixel = tga_data; + for (i = 0; i < tga_width * tga_height; ++i) + { + var temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + if (req_comp != 0 && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, (uint)tga_width, (uint)tga_height); + tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + return tga_data; + } + + public static int stbi__tga_info(stbi__context s, int* x, int* y, int* comp) + { + var tga_w = 0; + var tga_h = 0; + var tga_comp = 0; + var tga_image_type = 0; + var tga_bits_per_pixel = 0; + var tga_colormap_bpp = 0; + var sz = 0; + var tga_colormap_type = 0; + stbi__get8(s); + tga_colormap_type = stbi__get8(s); + if (tga_colormap_type > 1) + { + stbi__rewind(s); + return 0; + } + + tga_image_type = stbi__get8(s); + if (tga_colormap_type == 1) + { + if (tga_image_type != 1 && tga_image_type != 9) + { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 4); + sz = stbi__get8(s); + if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) + { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 4); + tga_colormap_bpp = sz; + } + else + { + if (tga_image_type != 2 && tga_image_type != 3 && tga_image_type != 10 && tga_image_type != 11) + { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 9); + tga_colormap_bpp = 0; + } + + tga_w = stbi__get16le(s); + if (tga_w < 1) + { + stbi__rewind(s); + return 0; + } + + tga_h = stbi__get16le(s); + if (tga_h < 1) + { + stbi__rewind(s); + return 0; + } + + tga_bits_per_pixel = stbi__get8(s); + stbi__get8(s); + if (tga_colormap_bpp != 0) + { + if (tga_bits_per_pixel != 8 && tga_bits_per_pixel != 16) + { + stbi__rewind(s); + return 0; + } + + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, null); + } + else + { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, tga_image_type == 3 || tga_image_type == 11 ? 1 : 0, + null); + } + + if (tga_comp == 0) + { + stbi__rewind(s); + return 0; + } + + if (x != null) + *x = tga_w; + if (y != null) + *y = tga_h; + if (comp != null) + *comp = tga_comp; + return 1; + } + + public static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) + { + if (is_rgb16 != null) + *is_rgb16 = 0; + switch (bits_per_pixel) + { + case 8: + return STBI_grey; + case 16: + case 15: + if (bits_per_pixel == 16 && is_grey != 0) + return STBI_grey_alpha; + if (is_rgb16 != null) + *is_rgb16 = 1; + return STBI_rgb; + case 24: + case 32: + return bits_per_pixel / 8; + default: + return 0; + } + } + + public static void stbi__tga_read_rgb16(stbi__context s, byte* _out_) + { + var px = (ushort)stbi__get16le(s); + ushort fiveBitMask = 31; + var r = (px >> 10) & fiveBitMask; + var g = (px >> 5) & fiveBitMask; + var b = px & fiveBitMask; + _out_[0] = (byte)(r * 255 / 31); + _out_[1] = (byte)(g * 255 / 31); + _out_[2] = (byte)(b * 255 / 31); + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.Generated.Zlib.cs b/Misaki.HighPerformance.Image/StbImage.Generated.Zlib.cs new file mode 100644 index 0000000..ce81bf9 --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.Generated.Zlib.cs @@ -0,0 +1,595 @@ +// Generated by Sichem at 9/16/2024 9:09:30 AM + +using Misaki.HighPerformance.Image.Runtime; +using System.Runtime.InteropServices; +using Misaki.HighPerformance.Image; + +namespace Misaki.HighPerformance.Image +{ + unsafe partial class StbImage + { + public static byte[] stbi__zdefault_distance = + { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; + + public static byte[] stbi__zdefault_length = + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8 + }; + + public static int[] stbi__zdist_base = + { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, + 6145, 8193, 12289, 16385, 24577, 0, 0 + }; + + public static int[] stbi__zdist_extra = + { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 }; + + public static int[] stbi__zlength_base = + { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, + 227, 258, 0, 0 + }; + + public static int[] stbi__zlength_extra = + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + + public static sbyte* stbi_zlib_decode_malloc_guesssize(sbyte* buffer, int len, int initial_size, int* outlen) + { + var a = new stbi__zbuf(); + var p = (sbyte*)stbi__malloc((ulong)initial_size); + if (p == null) + return null; + a.zbuffer = (byte*)buffer; + a.zbuffer_end = (byte*)buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1) != 0) + { + if (outlen != null) + *outlen = (int)(a.zout - a.zout_start); + return a.zout_start; + } + + CRuntime.free(a.zout_start); + return null; + } + + public static sbyte* stbi_zlib_decode_malloc_guesssize_headerflag(sbyte* buffer, int len, int initial_size, + int* outlen, int parse_header) + { + var a = new stbi__zbuf(); + var p = (sbyte*)stbi__malloc((ulong)initial_size); + if (p == null) + return null; + a.zbuffer = (byte*)buffer; + a.zbuffer_end = (byte*)buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header) != 0) + { + if (outlen != null) + *outlen = (int)(a.zout - a.zout_start); + return a.zout_start; + } + + CRuntime.free(a.zout_start); + return null; + } + + public static sbyte* stbi_zlib_decode_malloc(sbyte* buffer, int len, int* outlen) + { + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); + } + + public static int stbi_zlib_decode_buffer(sbyte* obuffer, int olen, sbyte* ibuffer, int ilen) + { + var a = new stbi__zbuf(); + a.zbuffer = (byte*)ibuffer; + a.zbuffer_end = (byte*)ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1) != 0) + return (int)(a.zout - a.zout_start); + return -1; + } + + public static sbyte* stbi_zlib_decode_noheader_malloc(sbyte* buffer, int len, int* outlen) + { + var a = new stbi__zbuf(); + var p = (sbyte*)stbi__malloc(16384); + if (p == null) + return null; + a.zbuffer = (byte*)buffer; + a.zbuffer_end = (byte*)buffer + len; + if (stbi__do_zlib(&a, p, 16384, 1, 0) != 0) + { + if (outlen != null) + *outlen = (int)(a.zout - a.zout_start); + return a.zout_start; + } + + CRuntime.free(a.zout_start); + return null; + } + + public static int stbi_zlib_decode_noheader_buffer(sbyte* obuffer, int olen, sbyte* ibuffer, int ilen) + { + var a = new stbi__zbuf(); + a.zbuffer = (byte*)ibuffer; + a.zbuffer_end = (byte*)ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0) != 0) + return (int)(a.zout - a.zout_start); + return -1; + } + + public static int stbi__zbuild_huffman(stbi__zhuffman* z, byte* sizelist, int num) + { + var i = 0; + var k = 0; + var code = 0; + var next_code = stackalloc int[16]; + var sizes = stackalloc int[17]; + CRuntime.memset(sizes, 0, (ulong)(17 * sizeof(int))); + CRuntime.memset(z->fast, 0, (ulong)(512 * sizeof(ushort))); + for (i = 0; i < num; ++i) + ++sizes[sizelist[i]]; + + sizes[0] = 0; + for (i = 1; i < 16; ++i) + if (sizes[i] > 1 << i) + return stbi__err("bad sizes"); + + code = 0; + for (i = 1; i < 16; ++i) + { + next_code[i] = code; + z->firstcode[i] = (ushort)code; + z->firstsymbol[i] = (ushort)k; + code = code + sizes[i]; + if (sizes[i] != 0) + if (code - 1 >= 1 << i) + return stbi__err("bad codelengths"); + z->maxcode[i] = code << (16 - i); + code <<= 1; + k += sizes[i]; + } + + z->maxcode[16] = 0x10000; + for (i = 0; i < num; ++i) + { + int s = sizelist[i]; + if (s != 0) + { + var c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + var fastv = (ushort)((s << 9) | i); + z->size[c] = (byte)s; + z->value[c] = (ushort)i; + if (s <= 9) + { + var j = stbi__bit_reverse(next_code[s], s); + while (j < 1 << 9) + { + z->fast[j] = fastv; + j += 1 << s; + } + } + + ++next_code[s]; + } + } + + return 1; + } + + public static int stbi__zeof(stbi__zbuf* z) + { + return z->zbuffer >= z->zbuffer_end ? 1 : 0; + } + + public static byte stbi__zget8(stbi__zbuf* z) + { + return (byte)(stbi__zeof(z) != 0 ? 0 : *z->zbuffer++); + } + + public static void stbi__fill_bits(stbi__zbuf* z) + { + do + { + if (z->code_buffer >= 1U << z->num_bits) + { + z->zbuffer = z->zbuffer_end; + return; + } + + z->code_buffer |= (uint)stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); + } + + public static uint stbi__zreceive(stbi__zbuf* z, int n) + { + uint k = 0; + if (z->num_bits < n) + stbi__fill_bits(z); + k = (uint)(z->code_buffer & ((1 << n) - 1)); + z->code_buffer >>= n; + z->num_bits -= n; + return k; + } + + public static int stbi__zhuffman_decode_slowpath(stbi__zbuf* a, stbi__zhuffman* z) + { + var b = 0; + var s = 0; + var k = 0; + k = stbi__bit_reverse((int)a->code_buffer, 16); + for (s = 9 + 1; ; ++s) + if (k < z->maxcode[s]) + break; + + if (s >= 16) + return -1; + b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= 288) + return -1; + if (z->size[b] != s) + return -1; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + public static int stbi__zhuffman_decode(stbi__zbuf* a, stbi__zhuffman* z) + { + var b = 0; + var s = 0; + if (a->num_bits < 16) + { + if (stbi__zeof(a) != 0) + { + if (a->hit_zeof_once == 0) + { + a->hit_zeof_once = 1; + a->num_bits += 16; + } + else + { + return -1; + } + } + else + { + stbi__fill_bits(a); + } + } + + b = z->fast[a->code_buffer & ((1 << 9) - 1)]; + if (b != 0) + { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + + return stbi__zhuffman_decode_slowpath(a, z); + } + + public static int stbi__zexpand(stbi__zbuf* z, sbyte* zout, int n) + { + sbyte* q; + uint cur = 0; + uint limit = 0; + uint old_limit = 0; + z->zout = zout; + if (z->z_expandable == 0) + return stbi__err("output buffer limit"); + cur = (uint)(z->zout - z->zout_start); + limit = old_limit = (uint)(z->zout_end - z->zout_start); + if (0xffffffff - cur < (uint)n) + return stbi__err("outofmem"); + while (cur + n > limit) + { + if (limit > 0xffffffff / 2) + return stbi__err("outofmem"); + limit *= 2; + } + + q = (sbyte*)CRuntime.realloc(z->zout_start, (ulong)limit); + if (q == null) + return stbi__err("outofmem"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; + } + + public static int stbi__parse_huffman_block(stbi__zbuf* a) + { + var zout = a->zout; + for (; ; ) + { + var z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) + { + if (z < 0) + return stbi__err("bad huffman code"); + if (zout >= a->zout_end) + { + if (stbi__zexpand(a, zout, 1) == 0) + return 0; + zout = a->zout; + } + + *zout++ = (sbyte)z; + } + else + { + byte* p; + var len = 0; + var dist = 0; + if (z == 256) + { + a->zout = zout; + if (a->hit_zeof_once != 0 && a->num_bits < 16) + return stbi__err("unexpected end"); + + return 1; + } + + if (z >= 286) + return stbi__err("bad huffman code"); + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z] != 0) + len += (int)stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) + return stbi__err("bad huffman code"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z] != 0) + dist += (int)stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) + return stbi__err("bad dist"); + if (len > a->zout_end - zout) + { + if (stbi__zexpand(a, zout, len) == 0) + return 0; + zout = a->zout; + } + + p = (byte*)(zout - dist); + if (dist == 1) + { + var v = *p; + if (len != 0) + do + { + *zout++ = (sbyte)v; + } while (--len != 0); + } + else + { + if (len != 0) + do + { + *zout++ = (sbyte)*p++; + } while (--len != 0); + } + } + } + } + + public static int stbi__compute_huffman_codes(stbi__zbuf* a) + { + var z_codelength = new stbi__zhuffman(); + var lencodes = stackalloc byte[455]; + var codelength_sizes = stackalloc byte[19]; + var i = 0; + var n = 0; + var hlit = (int)(stbi__zreceive(a, 5) + 257); + var hdist = (int)(stbi__zreceive(a, 5) + 1); + var hclen = (int)(stbi__zreceive(a, 4) + 4); + var ntot = hlit + hdist; + CRuntime.memset(codelength_sizes, 0, (ulong)(19 * sizeof(byte))); + for (i = 0; i < hclen; ++i) + { + var s = (int)stbi__zreceive(a, 3); + codelength_sizes[stbi__compute_huffman_codes_length_dezigzag[i]] = (byte)s; + } + + if (stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19) == 0) + return 0; + n = 0; + while (n < ntot) + { + var c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) + return stbi__err("bad codelengths"); + if (c < 16) + { + lencodes[n++] = (byte)c; + } + else + { + byte fill = 0; + if (c == 16) + { + c = (int)(stbi__zreceive(a, 2) + 3); + if (n == 0) + return stbi__err("bad codelengths"); + fill = lencodes[n - 1]; + } + else if (c == 17) + { + c = (int)(stbi__zreceive(a, 3) + 3); + } + else if (c == 18) + { + c = (int)(stbi__zreceive(a, 7) + 11); + } + else + { + return stbi__err("bad codelengths"); + } + + if (ntot - n < c) + return stbi__err("bad codelengths"); + CRuntime.memset(lencodes + n, fill, (ulong)c); + n += c; + } + } + + if (n != ntot) + return stbi__err("bad codelengths"); + if (stbi__zbuild_huffman(&a->z_length, lencodes, hlit) == 0) + return 0; + if (stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist) == 0) + return 0; + return 1; + } + + public static int stbi__parse_uncompressed_block(stbi__zbuf* a) + { + var header = stackalloc byte[4]; + var len = 0; + var nlen = 0; + var k = 0; + if ((a->num_bits & 7) != 0) + stbi__zreceive(a, a->num_bits & 7); + k = 0; + while (a->num_bits > 0) + { + header[k++] = (byte)(a->code_buffer & 255); + a->code_buffer >>= 8; + a->num_bits -= 8; + } + + if (a->num_bits < 0) + return stbi__err("zlib corrupt"); + while (k < 4) + header[k++] = stbi__zget8(a); + + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) + return stbi__err("zlib corrupt"); + if (a->zbuffer + len > a->zbuffer_end) + return stbi__err("read past buffer"); + if (a->zout + len > a->zout_end) + if (stbi__zexpand(a, a->zout, len) == 0) + return 0; + CRuntime.memcpy(a->zout, a->zbuffer, (ulong)len); + a->zbuffer += len; + a->zout += len; + return 1; + } + + public static int stbi__parse_zlib_header(stbi__zbuf* a) + { + int cmf = stbi__zget8(a); + var cm = cmf & 15; + int flg = stbi__zget8(a); + if (stbi__zeof(a) != 0) + return stbi__err("bad zlib header"); + if ((cmf * 256 + flg) % 31 != 0) + return stbi__err("bad zlib header"); + if ((flg & 32) != 0) + return stbi__err("no preset dict"); + if (cm != 8) + return stbi__err("bad compression"); + return 1; + } + + public static int stbi__parse_zlib(stbi__zbuf* a, int parse_header) + { + var final = 0; + var type = 0; + if (parse_header != 0) + if (stbi__parse_zlib_header(a) == 0) + return 0; + a->num_bits = 0; + a->code_buffer = 0; + a->hit_zeof_once = 0; + do + { + final = (int)stbi__zreceive(a, 1); + type = (int)stbi__zreceive(a, 2); + if (type == 0) + { + if (stbi__parse_uncompressed_block(a) == 0) + return 0; + } + else if (type == 3) + { + return 0; + } + else + { + if (type == 1) + { + fixed (byte* b = stbi__zdefault_length) + { + if (stbi__zbuild_huffman(&a->z_length, b, 288) == 0) + return 0; + } + + fixed (byte* b = stbi__zdefault_distance) + { + if (stbi__zbuild_huffman(&a->z_distance, b, 32) == 0) + return 0; + } + } + else + { + if (stbi__compute_huffman_codes(a) == 0) + return 0; + } + + if (stbi__parse_huffman_block(a) == 0) + return 0; + } + } while (final == 0); + + return 1; + } + + public static int stbi__do_zlib(stbi__zbuf* a, sbyte* obuf, int olen, int exp, int parse_header) + { + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + return stbi__parse_zlib(a, parse_header); + } + + [StructLayout(LayoutKind.Sequential)] + public struct stbi__zbuf + { + public byte* zbuffer; + public byte* zbuffer_end; + public int num_bits; + public int hit_zeof_once; + public uint code_buffer; + public sbyte* zout; + public sbyte* zout_start; + public sbyte* zout_end; + public int z_expandable; + public stbi__zhuffman z_length; + public stbi__zhuffman z_distance; + } + + [StructLayout(LayoutKind.Sequential)] + public struct stbi__zhuffman + { + public fixed ushort fast[512]; + public fixed ushort firstcode[16]; + public fixed int maxcode[17]; + public fixed ushort firstsymbol[16]; + public fixed byte size[288]; + public fixed ushort value[288]; + } + } +} diff --git a/Misaki.HighPerformance.Image/StbImage.cs b/Misaki.HighPerformance.Image/StbImage.cs new file mode 100644 index 0000000..bcece13 --- /dev/null +++ b/Misaki.HighPerformance.Image/StbImage.cs @@ -0,0 +1,95 @@ +using Misaki.HighPerformance.Image.Runtime; +using System; +using System.IO; +using System.Runtime.InteropServices; +using Misaki.HighPerformance.Image; + +namespace Misaki.HighPerformance.Image +{ +#if !STBSHARP_INTERNAL + public +#else + internal +#endif + static unsafe partial class StbImage + { + public static string stbi__g_failure_reason; + public static readonly char[] stbi__parse_png_file_invalid_chunk = new char[25]; + + public static int NativeAllocations + { + get + { + return MemoryStats.Allocations; + } + } + + public class stbi__context + { + private readonly Stream _stream; + + public byte[] _tempBuffer; + public int img_n = 0; + public int img_out_n = 0; + public uint img_x = 0; + public uint img_y = 0; + + public stbi__context(Stream stream) + { + if (stream == null) + throw new ArgumentNullException("stream"); + + _stream = stream; + } + + public Stream Stream + { + get + { + return _stream; + } + } + } + + private static int stbi__err(string str) + { + stbi__g_failure_reason = str; + return 0; + } + + public static byte stbi__get8(stbi__context s) + { + var b = s.Stream.ReadByte(); + if (b == -1) return 0; + + return (byte)b; + } + + public static void stbi__skip(stbi__context s, int skip) + { + s.Stream.Seek(skip, SeekOrigin.Current); + } + + public static void stbi__rewind(stbi__context s) + { + s.Stream.Seek(0, SeekOrigin.Begin); + } + + public static int stbi__at_eof(stbi__context s) + { + return s.Stream.Position == s.Stream.Length ? 1 : 0; + } + + public static int stbi__getn(stbi__context s, byte* buf, int size) + { + if (s._tempBuffer == null || + s._tempBuffer.Length < size) + s._tempBuffer = new byte[size * 2]; + + var result = s.Stream.Read(s._tempBuffer, 0, size); + Marshal.Copy(s._tempBuffer, 0, new IntPtr(buf), result); + + return result; + } + } +} diff --git a/Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj b/Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/Misaki.HighPerformance.Jobs/WorkerThreadPool.cs b/Misaki.HighPerformance.Jobs/WorkerThreadPool.cs new file mode 100644 index 0000000..6c46cc7 --- /dev/null +++ b/Misaki.HighPerformance.Jobs/WorkerThreadPool.cs @@ -0,0 +1,6 @@ +namespace Misaki.HighPerformance.Jobs; + +internal static class WorkerThreadPool +{ + private static readonly int _workerThreadCount = Environment.ProcessorCount; +} \ No newline at end of file diff --git a/Misaki.HighPerformance.LowLevel/AssemblyInfo.cs b/Misaki.HighPerformance.LowLevel/AssemblyInfo.cs new file mode 100644 index 0000000..07bc85f --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/AssemblyInfo.cs @@ -0,0 +1,2 @@ +global using static Misaki.HighPerformance.LowLevel.Helpers.MemoryUtilities; +global using SystemUnsfae = System.Runtime.CompilerServices.Unsafe; diff --git a/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs b/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs new file mode 100644 index 0000000..fda88c6 --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs @@ -0,0 +1,263 @@ +using Misaki.HighPerformance.LowLevel.Collections; +using Misaki.HighPerformance.LowLevel.Contracts; +using Misaki.HighPerformance.LowLevel.Exceptions; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.LowLevel.Buffer; + +using unsafe FreeFunc = delegate* unmanaged; + +public unsafe struct ArenaAllocator : IAllocator, IDisposable +{ + private DynamicArena _arena; + private AllocationHandle _handle; + + public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle); + + public ArenaAllocator(uint initialSize) + { + _arena = new DynamicArena(initialSize); + _handle = new AllocationHandle(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &FreeBlock); + } + + [UnmanagedCallersOnly] + private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption) + { + var selfPtr = (ArenaAllocator*)instance; + var ptr = selfPtr->_arena.Allocate(size, alignment, allocationOption); + + return ptr; + } + + [UnmanagedCallersOnly] + private static void* Reallocate(void* instance, void* ptr, nuint size, nuint alignment) + { + var selfPtr = (ArenaAllocator*)instance; + var newPtr = selfPtr->_arena.Allocate(size, alignment, AllocationOption.None); + MemCpy(newPtr, ptr, size); + // NOTE: We do not free the old pointer here, as it is managed by the arena. + return newPtr; + } + + [UnmanagedCallersOnly] + private static void FreeBlock(void* instance, void* ptr) + { + } + + public void Reset() + { + _arena.Reset(); + } + + public void Dispose() + { + _arena.Dispose(); + } +} + +public unsafe struct DefaultAllocator : IAllocator +{ + private AllocationHandle _handle; + + public ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle); + + public DefaultAllocator() + { + _handle = new AllocationHandle(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &FreeBlock); + } + + [UnmanagedCallersOnly] + private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption) + { + var ptr = AlignedAlloc(size, alignment); + AllocationManager.TrackAllocation(ptr, size, instance, &FreeBlock); + if (allocationOption.HasFlag(AllocationOption.Clear)) + { + MemClear(ptr, size); + } + + return ptr; + } + + [UnmanagedCallersOnly] + private static void* Reallocate(void* instance, void* ptr, nuint size, nuint alignment) + { + var newPtr = AlignedRealloc(ptr, size, alignment); + AllocationManager.UpdateAllocation(ptr, newPtr, size, instance, &FreeBlock); + return newPtr; + } + + [UnmanagedCallersOnly] + private static void FreeBlock(void* instance, void* ptr) + { + AlignedFree(ptr); + AllocationManager.RemoveAllocation(ptr); + } +} + +public unsafe struct EmptyAllocator : IAllocator +{ + private AllocationHandle _handle; + + public ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle); + + public EmptyAllocator() + { + _handle = new AllocationHandle(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &FreeBlock); + } + + [UnmanagedCallersOnly] + private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption) + { + return null; + } + + [UnmanagedCallersOnly] + private static void* Reallocate(void* instance, void* ptr, nuint size, nuint alignment) + { + return ptr; + } + + [UnmanagedCallersOnly] + private static void FreeBlock(void* instance, void* ptr) + { + } +} + +public static unsafe class AllocationManager +{ + public readonly struct AllocationInfo + { + public nuint Size + { + get; init; + } + + public void* Allocator + { + get; init; + } + + public FreeFunc FreeHandler + { + get; init; + } + + public StackTrace StackTrace + { + get; init; + } + } + + private const uint _DEFAULT_ARENA_SIZE = 512 * 1024; + + private static ArenaAllocator s_arenaAllocator = new(_DEFAULT_ARENA_SIZE); + private static DefaultAllocator s_persistentAllocator = new(); + private static EmptyAllocator s_emptyAllocator = new(); + + private static bool s_debugLayer; + private static Dictionary? s_allocated; + + public static ArenaAllocator TempAllocator => s_arenaAllocator; + public static DefaultAllocator PersistentAllocator => s_persistentAllocator; + public static EmptyAllocator EmptyAllocator => s_emptyAllocator; + + public static void EnableDebugLayer() + { + s_debugLayer = true; + s_allocated ??= new Dictionary(64); + } + + public static ref AllocationHandle GetAllocationHandle(Allocator allocator) + { + switch (allocator) + { + case Allocator.Temp: + return ref s_arenaAllocator.Handle; + case Allocator.Persistent: + return ref s_persistentAllocator.Handle; + case Allocator.External: + return ref s_emptyAllocator.Handle; + default: + throw new ArgumentException("Invalid allocator type.", nameof(allocator)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void TrackAllocation(void* ptr, nuint allocationSize, void* allocator, FreeFunc freeFunc) + { + if (!s_debugLayer || s_allocated == null || ptr == null) + { + return; + } + + s_allocated[(nint)ptr] = new AllocationInfo + { + Size = allocationSize, + Allocator = allocator, + FreeHandler = freeFunc, + StackTrace = new StackTrace(true) + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UpdateAllocation(void* oldPtr, void* newPtr, nuint allocationSize, void* allocator, FreeFunc freeFunc) + { + if (!s_debugLayer || s_allocated == null || oldPtr == null || newPtr == null) + { + return; + } + + if (s_allocated.Remove((nint)oldPtr, out var info)) + { + s_allocated[(nint)newPtr] = new AllocationInfo + { + Size = allocationSize, + Allocator = allocator, + FreeHandler = freeFunc, + StackTrace = info.StackTrace + }; + } + else + { + TrackAllocation(newPtr, allocationSize, allocator, freeFunc); + } + } + + public static void RemoveAllocation(void* ptr) + { + if (s_allocated == null) + { + return; + } + + s_allocated.Remove((nint)ptr); + } + + /// + /// Disposes of the AllocationManager, freeing all allocated memory and resources. + /// + public static void Dispose() + { + s_arenaAllocator.Dispose(); + + if (s_allocated != null) + { + nuint unfreeBytes = 0u; + foreach (var pair in s_allocated) + { + unfreeBytes += pair.Value.Size; + pair.Value.FreeHandler(pair.Value.Allocator, (void*)pair.Key); + } + + if (unfreeBytes > 0u) + { + throw new MemoryLeakException([.. s_allocated.Values]); + } + + s_allocated.Clear(); + } + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Buffer/Arena .cs b/Misaki.HighPerformance.LowLevel/Buffer/Arena .cs similarity index 83% rename from Misaki.HighPerformance.Unsafe/Buffer/Arena .cs rename to Misaki.HighPerformance.LowLevel/Buffer/Arena .cs index 52936d2..98ed6e7 100644 --- a/Misaki.HighPerformance.Unsafe/Buffer/Arena .cs +++ b/Misaki.HighPerformance.LowLevel/Buffer/Arena .cs @@ -1,6 +1,6 @@ -using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.LowLevel.Collections; -namespace Misaki.HighPerformance.Unsafe.Buffer; +namespace Misaki.HighPerformance.LowLevel.Buffer; /// /// A memory management structure that allocates and resets memory blocks with specified alignment. @@ -8,17 +8,17 @@ namespace Misaki.HighPerformance.Unsafe.Buffer; public unsafe struct Arena : IDisposable { private byte* _buffer; - private uint _size; - private uint _offset; + private nuint _size; + private nuint _offset; private bool _disposed; - public Arena(uint size) + public Arena(nuint size) { Initialize(size); } - public void Initialize(uint size) + public void Initialize(nuint size) { if (_buffer != null) { @@ -36,18 +36,18 @@ public unsafe struct Arena : IDisposable /// You don't need to free the memory manually, it will be freed when the arena is disposed. /// /// Specifies the amount of memory to allocate in bytes. - /// Defines the alignment requirement for the allocated memory. + /// Defines the alignment requirement for the allocated memory. /// The option when allocating memory. /// A pointer to the allocated memory block or null if the allocation cannot be fulfilled. /// Thrown if the arena has been disposed. - public void* Allocate(uint size, uint alignSize, AllocationOption allocationOption) + public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption) { if (_disposed) { throw new ObjectDisposedException(nameof(DynamicArena)); } - var offset = (_offset + alignSize - 1) & ~(alignSize - 1); + var offset = _offset + alignment - 1 & ~(alignment - 1); if (offset + size > _size) { return null; diff --git a/Misaki.HighPerformance.Unsafe/Buffer/DynamicArena.cs b/Misaki.HighPerformance.LowLevel/Buffer/DynamicArena.cs similarity index 90% rename from Misaki.HighPerformance.Unsafe/Buffer/DynamicArena.cs rename to Misaki.HighPerformance.LowLevel/Buffer/DynamicArena.cs index e1b79bf..800ed09 100644 --- a/Misaki.HighPerformance.Unsafe/Buffer/DynamicArena.cs +++ b/Misaki.HighPerformance.LowLevel/Buffer/DynamicArena.cs @@ -1,6 +1,6 @@ -using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.LowLevel.Collections; -namespace Misaki.HighPerformance.Unsafe.Buffer; +namespace Misaki.HighPerformance.LowLevel.Buffer; /// /// A dynamic memory management structure that automatically grows by creating linked arenas @@ -45,7 +45,7 @@ public unsafe struct DynamicArena : IDisposable _current = _root; } - private bool CreateNewNode(uint size) + private bool CreateNewNode(nuint size) { var newNode = (ArenaNode*)Malloc(SizeOf()); try @@ -68,10 +68,10 @@ public unsafe struct DynamicArena : IDisposable /// Allocates a block of memory with specified size and alignment. Creates a new arena if current one is full. /// /// Size of the memory block to allocate in bytes. - /// Alignment requirement for the memory block. + /// Alignment requirement for the memory block. /// Pointer to the allocated memory block. /// Thrown if the arena has been disposed. - public void* Allocate(uint size, uint alignSize, AllocationOption allocationType) + public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption) { if (_root == null) { @@ -83,7 +83,7 @@ public unsafe struct DynamicArena : IDisposable while (current != null) { - result = current->arena.Allocate(size, alignSize, allocationType); + result = current->arena.Allocate(size, alignment, allocationOption); if (result != null) { return result; diff --git a/Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.cs b/Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.cs new file mode 100644 index 0000000..851cd3d --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.cs @@ -0,0 +1,926 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Misaki.HighPerformance.LowLevel.Buffer; + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 32 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 32)] +public unsafe struct FixedStackString32 +{ + private ushort _length; + private fixed byte _buffer[30]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 30) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString32."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 30)); + } + } + } + + public FixedStackString32(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 30) + { + throw new ArgumentException("Input string is too long to fit in FixedString32."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 30); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString32(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString32(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString32(ReadOnlySpan input) + { + if (input.Length > 30) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString32."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString32(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 64 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 64)] +public unsafe struct FixedStackString64 +{ + private ushort _length; + private fixed byte _buffer[62]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 62) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString64."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 62)); + } + } + } + + public FixedStackString64(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 62) + { + throw new ArgumentException("Input string is too long to fit in FixedString64."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 62); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString64(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString64(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString64(ReadOnlySpan input) + { + if (input.Length > 62) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString64."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString64(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 128 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 128)] +public unsafe struct FixedStackString128 +{ + private ushort _length; + private fixed byte _buffer[126]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 126) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString128."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 126)); + } + } + } + + public FixedStackString128(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 126) + { + throw new ArgumentException("Input string is too long to fit in FixedString128."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 126); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString128(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString128(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString128(ReadOnlySpan input) + { + if (input.Length > 126) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString128."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString128(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 256 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 256)] +public unsafe struct FixedStackString256 +{ + private ushort _length; + private fixed byte _buffer[254]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 254) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString256."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 254)); + } + } + } + + public FixedStackString256(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 254) + { + throw new ArgumentException("Input string is too long to fit in FixedString256."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 254); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString256(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString256(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString256(ReadOnlySpan input) + { + if (input.Length > 254) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString256."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString256(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 512 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 512)] +public unsafe struct FixedStackString512 +{ + private ushort _length; + private fixed byte _buffer[510]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 510) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString512."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 510)); + } + } + } + + public FixedStackString512(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 510) + { + throw new ArgumentException("Input string is too long to fit in FixedString512."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 510); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString512(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString512(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString512(ReadOnlySpan input) + { + if (input.Length > 510) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString512."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString512(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 1024 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 1024)] +public unsafe struct FixedStackString1024 +{ + private ushort _length; + private fixed byte _buffer[1022]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 1022) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString1024."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 1022)); + } + } + } + + public FixedStackString1024(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 1022) + { + throw new ArgumentException("Input string is too long to fit in FixedString1024."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 1022); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString1024(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString1024(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString1024(ReadOnlySpan input) + { + if (input.Length > 1022) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString1024."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString1024(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 2048 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 2048)] +public unsafe struct FixedStackString2048 +{ + private ushort _length; + private fixed byte _buffer[2046]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 2046) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString2048."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 2046)); + } + } + } + + public FixedStackString2048(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 2046) + { + throw new ArgumentException("Input string is too long to fit in FixedString2048."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 2046); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString2048(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString2048(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString2048(ReadOnlySpan input) + { + if (input.Length > 2046) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString2048."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString2048(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length 4096 bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = 4096)] +public unsafe struct FixedStackString4096 +{ + private ushort _length; + private fixed byte _buffer[4094]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 4094) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString4096."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, 4094)); + } + } + } + + public FixedStackString4096(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 4094) + { + throw new ArgumentException("Input string is too long to fit in FixedString4096."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, 4094); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString4096(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString4096(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString4096(ReadOnlySpan input) + { + if (input.Length > 4094) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString4096."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString4096(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + diff --git a/Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.tt b/Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.tt new file mode 100644 index 0000000..bf393af --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.tt @@ -0,0 +1,129 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Misaki.HighPerformance.LowLevel.Buffer; + +<# for (int i = 32; i <= 4096; i *= 2) { #> +/// +/// Represents a stack allocated fixed-size UTF-8 encoded string of length <#= i #> bytes. +/// +/// +/// 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 . +/// +[StructLayout(LayoutKind.Sequential, Size = <#= i #>)] +public unsafe struct FixedStackString<#= i #> +{ + private ushort _length; + private fixed byte _buffer[<#= i - 2 #>]; + + public ushort Length => _length; + public string Value + { + get + { + fixed (byte* bufferPtr = _buffer) + { + return Encoding.UTF8.GetString(bufferPtr, _length); + } + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > <#= i - 2 #>) + { + throw new ArgumentException("Input string is too long to fit in FixedStackString<#= i #>."); + } + + fixed (byte* bufferPtr = _buffer) + { + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(bufferPtr, <#= i - 2 #>)); + } + } + } + + public FixedStackString<#= i #>(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > <#= i - 2 #>) + { + throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>."); + } + + fixed (char* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, bufferPtr, <#= i - 2 #>); + _length = (ushort)actualByteCount; + } + } + + public FixedStackString<#= i #>(string input) + : this(input.AsSpan()) + { + } + + public FixedStackString<#= i #>(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedStackString<#= i #>(ReadOnlySpan input) + { + if (input.Length > <#= i - 2 #>) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString<#= i #>."); + } + + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + fixed (byte* bufferPtr = _buffer) + { + SystemUnsfae.CopyBlockUnaligned(bufferPtr, inputPtr, _length); + } + } + + public FixedStackString<#= i #>(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() + { + fixed (byte* ptr = _buffer) + { + return new(ptr, _length); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* GetUnsafePointer() + { + fixed (byte* ptr = _buffer) + { + return ptr; + } + } + + public override string ToString() + { + return Value; + } +} + +<# } #> \ No newline at end of file diff --git a/Misaki.HighPerformance.LowLevel/Buffer/FixedString.cs b/Misaki.HighPerformance.LowLevel/Buffer/FixedString.cs new file mode 100644 index 0000000..149fc8a --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Buffer/FixedString.cs @@ -0,0 +1,894 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Misaki.HighPerformance.LowLevel.Buffer; + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 32 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 32)] +public unsafe struct FixedString32 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 30) + { + throw new ArgumentException("Input string is too long to fit in FixedString32."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 30)); + } + } + + public FixedString32(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 30) + { + throw new ArgumentException("Input string is too long to fit in FixedString32."); + } + + _buffer = (byte*)NativeMemory.Alloc(30); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 30); + _length = (ushort)actualByteCount; + } + } + + public FixedString32(string input) + : this(input.AsSpan()) + { + } + + public FixedString32(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString32(ReadOnlySpan input) + { + if (input.Length > 30) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString32."); + } + + _buffer = (byte*)NativeMemory.Alloc(30); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString32(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 64 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 64)] +public unsafe struct FixedString64 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 62) + { + throw new ArgumentException("Input string is too long to fit in FixedString64."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 62)); + } + } + + public FixedString64(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 62) + { + throw new ArgumentException("Input string is too long to fit in FixedString64."); + } + + _buffer = (byte*)NativeMemory.Alloc(62); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 62); + _length = (ushort)actualByteCount; + } + } + + public FixedString64(string input) + : this(input.AsSpan()) + { + } + + public FixedString64(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString64(ReadOnlySpan input) + { + if (input.Length > 62) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString64."); + } + + _buffer = (byte*)NativeMemory.Alloc(62); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString64(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 128 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 128)] +public unsafe struct FixedString128 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 126) + { + throw new ArgumentException("Input string is too long to fit in FixedString128."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 126)); + } + } + + public FixedString128(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 126) + { + throw new ArgumentException("Input string is too long to fit in FixedString128."); + } + + _buffer = (byte*)NativeMemory.Alloc(126); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 126); + _length = (ushort)actualByteCount; + } + } + + public FixedString128(string input) + : this(input.AsSpan()) + { + } + + public FixedString128(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString128(ReadOnlySpan input) + { + if (input.Length > 126) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString128."); + } + + _buffer = (byte*)NativeMemory.Alloc(126); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString128(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 256 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 256)] +public unsafe struct FixedString256 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 254) + { + throw new ArgumentException("Input string is too long to fit in FixedString256."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 254)); + } + } + + public FixedString256(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 254) + { + throw new ArgumentException("Input string is too long to fit in FixedString256."); + } + + _buffer = (byte*)NativeMemory.Alloc(254); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 254); + _length = (ushort)actualByteCount; + } + } + + public FixedString256(string input) + : this(input.AsSpan()) + { + } + + public FixedString256(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString256(ReadOnlySpan input) + { + if (input.Length > 254) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString256."); + } + + _buffer = (byte*)NativeMemory.Alloc(254); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString256(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 512 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 512)] +public unsafe struct FixedString512 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 510) + { + throw new ArgumentException("Input string is too long to fit in FixedString512."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 510)); + } + } + + public FixedString512(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 510) + { + throw new ArgumentException("Input string is too long to fit in FixedString512."); + } + + _buffer = (byte*)NativeMemory.Alloc(510); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 510); + _length = (ushort)actualByteCount; + } + } + + public FixedString512(string input) + : this(input.AsSpan()) + { + } + + public FixedString512(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString512(ReadOnlySpan input) + { + if (input.Length > 510) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString512."); + } + + _buffer = (byte*)NativeMemory.Alloc(510); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString512(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 1024 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 1024)] +public unsafe struct FixedString1024 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 1022) + { + throw new ArgumentException("Input string is too long to fit in FixedString1024."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 1022)); + } + } + + public FixedString1024(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 1022) + { + throw new ArgumentException("Input string is too long to fit in FixedString1024."); + } + + _buffer = (byte*)NativeMemory.Alloc(1022); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 1022); + _length = (ushort)actualByteCount; + } + } + + public FixedString1024(string input) + : this(input.AsSpan()) + { + } + + public FixedString1024(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString1024(ReadOnlySpan input) + { + if (input.Length > 1022) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString1024."); + } + + _buffer = (byte*)NativeMemory.Alloc(1022); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString1024(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 2048 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 2048)] +public unsafe struct FixedString2048 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 2046) + { + throw new ArgumentException("Input string is too long to fit in FixedString2048."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 2046)); + } + } + + public FixedString2048(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 2046) + { + throw new ArgumentException("Input string is too long to fit in FixedString2048."); + } + + _buffer = (byte*)NativeMemory.Alloc(2046); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 2046); + _length = (ushort)actualByteCount; + } + } + + public FixedString2048(string input) + : this(input.AsSpan()) + { + } + + public FixedString2048(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString2048(ReadOnlySpan input) + { + if (input.Length > 2046) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString2048."); + } + + _buffer = (byte*)NativeMemory.Alloc(2046); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString2048(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length 4096 bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = 4096)] +public unsafe struct FixedString4096 : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > 4094) + { + throw new ArgumentException("Input string is too long to fit in FixedString4096."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, 4094)); + } + } + + public FixedString4096(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > 4094) + { + throw new ArgumentException("Input string is too long to fit in FixedString4096."); + } + + _buffer = (byte*)NativeMemory.Alloc(4094); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 4094); + _length = (ushort)actualByteCount; + } + } + + public FixedString4096(string input) + : this(input.AsSpan()) + { + } + + public FixedString4096(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString4096(ReadOnlySpan input) + { + if (input.Length > 4094) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString4096."); + } + + _buffer = (byte*)NativeMemory.Alloc(4094); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString4096(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + diff --git a/Misaki.HighPerformance.LowLevel/Buffer/FixedString.tt b/Misaki.HighPerformance.LowLevel/Buffer/FixedString.tt new file mode 100644 index 0000000..3f921fe --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Buffer/FixedString.tt @@ -0,0 +1,125 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Misaki.HighPerformance.LowLevel.Buffer; + +<# for (int i = 32; i <= 4096; i *= 2) { #> +/// +/// Represents a heap allocated fixed-size UTF-8 encoded string of length <#= i #> bytes. +/// +[StructLayout(LayoutKind.Sequential, Size = <#= i #>)] +public unsafe struct FixedString<#= i #> : IDisposable +{ + private ushort _length; + private byte* _buffer; + + public ushort Length => _length; + public string Value + { + readonly get + { + return Encoding.UTF8.GetString(_buffer, _length); + } + set + { + if (string.IsNullOrEmpty(value)) + { + _length = 0; + return; + } + + var maxBytes = Encoding.UTF8.GetByteCount(value); + if (maxBytes > <#= i - 2 #>) + { + throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>."); + } + + _length = (ushort)Encoding.UTF8.GetBytes(value, new Span(_buffer, <#= i - 2 #>)); + } + } + + public FixedString<#= i #>(ReadOnlySpan input) + { + var maxBytes = Encoding.UTF8.GetByteCount(input); + if (maxBytes > <#= i - 2 #>) + { + throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>."); + } + + _buffer = (byte*)NativeMemory.Alloc(<#= i - 2 #>); + + fixed (char* inputPtr = input) + { + var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, <#= i - 2 #>); + _length = (ushort)actualByteCount; + } + } + + public FixedString<#= i #>(string input) + : this(input.AsSpan()) + { + } + + public FixedString<#= i #>(char* input, ushort length) + : this(new Span(input, length)) + { + } + + public FixedString<#= i #>(ReadOnlySpan input) + { + if (input.Length > <#= i - 2 #>) + { + throw new ArgumentException("Input byte array is too long to fit in FixedString<#= i #>."); + } + + _buffer = (byte*)NativeMemory.Alloc(<#= i - 2 #>); + _length = (ushort)input.Length; + + fixed (byte* inputPtr = input) + { + SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length); + } + } + + public FixedString<#= i #>(byte* input, ushort length) + : this(new ReadOnlySpan(input, length)) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return new(_buffer, _length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly byte* GetUnsafePointer() + { + return _buffer; + } + + public override string ToString() + { + return Value; + } + + public void Dispose() + { + if (_buffer != null) + { + NativeMemory.Free(_buffer); + + _length = 0; + _buffer = null; + } + } +} + +<# } #> \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Buffer/UnsafeArrayPool.cs b/Misaki.HighPerformance.LowLevel/Buffer/UnsafeArrayPool.cs similarity index 78% rename from Misaki.HighPerformance.Unsafe/Buffer/UnsafeArrayPool.cs rename to Misaki.HighPerformance.LowLevel/Buffer/UnsafeArrayPool.cs index e0b003c..9cc8c1d 100644 --- a/Misaki.HighPerformance.Unsafe/Buffer/UnsafeArrayPool.cs +++ b/Misaki.HighPerformance.LowLevel/Buffer/UnsafeArrayPool.cs @@ -1,6 +1,6 @@ -using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.LowLevel.Collections; -namespace Misaki.HighPerformance.Unsafe.Buffer; +namespace Misaki.HighPerformance.LowLevel.Buffer; // TODO: Implement a pool for UnsafeArray. public unsafe static class UnsafeArrayPool diff --git a/Misaki.HighPerformance.Unsafe/Collections/AllocationOption.cs b/Misaki.HighPerformance.LowLevel/Collections/AllocationOption.cs similarity index 95% rename from Misaki.HighPerformance.Unsafe/Collections/AllocationOption.cs rename to Misaki.HighPerformance.LowLevel/Collections/AllocationOption.cs index dff0977..9e77ef1 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/AllocationOption.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/AllocationOption.cs @@ -1,4 +1,4 @@ -namespace Misaki.HighPerformance.Unsafe.Collections; +namespace Misaki.HighPerformance.LowLevel.Collections; [Flags] public enum AllocationOption : byte diff --git a/Misaki.HighPerformance.LowLevel/Collections/BitSet.cs b/Misaki.HighPerformance.LowLevel/Collections/BitSet.cs new file mode 100644 index 0000000..f2405a8 --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Collections/BitSet.cs @@ -0,0 +1,711 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Misaki.HighPerformance.LowLevel.Collections; + +public sealed class BitSet +{ + private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31 + private const int _INDEX_SIZE = 5; // log_2(BitSize + 1) + private const int _MASK = (1 << 5) - 1; // 0x1F, the mask to get the bit index inside a uint + private static readonly int s_padding = Vector.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically + + /// + /// Determines the required length of an to hold the passed id or bit. + /// + /// The id or bit. + /// A size of required s for the bitset. + public static int RequiredLength(int id) + { + return (id >> 5) + int.Sign(id & _BIT_SIZE); + } + + /// + /// Rounds the given length to the next padding size. + /// + /// The length to round. + /// The rounded length. + public static int RoundToPadding(int length) + { + return (length + s_padding - 1) / s_padding * s_padding; + } + + /// + /// The bits from the bitset. + /// + private uint[] _bits; + + /// + /// The highest bit set. + /// + private int _highestBit; + + /// + /// The maximum -index current in use. + /// + private int _max; + + /// + /// Initializes a new instance of the class. + /// + public BitSet() + { + _bits = new uint[s_padding]; + } + + /// + /// Initializes a new instance of the class. + /// + public BitSet(int minimalLength) + { + var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE); + var length = RoundToPadding(uints); + _bits = new uint[length]; + } + + /// + /// Initializes a new instance of the class. + /// + public BitSet(params Span bits) + { + _bits = bits.ToArray(); + _highestBit = 0; + _max = _bits.Length * (_BIT_SIZE + 1) - 1; // Calculate the maximum index in use + for (var i = 0; i < _bits.Length; i++) + { + if (_bits[i] != 0) + { + _highestBit = Math.Max(_highestBit, i * (_BIT_SIZE + 1) + BitOperations.Log2(_bits[i]) + 1); + } + } + } + + /// + /// The highest uint index in use inside the -array. + /// + public int HighestIndex + { + get => _max; + } + + /// + /// The highest bit set. + /// + public int HighestBit + { + get => _highestBit; + } + + /// + /// Returns the length of the bitset, how many ints it consists of. + /// + public int Length + { + get => _bits.Length; + } + + /// + /// Checks whether a bit is set at the index. + /// + /// The index. + /// True if it is, otherwise false + public bool IsSet(int index) + { + var b = index >> _INDEX_SIZE; + if (b >= _bits.Length) + { + return false; + } + + return (_bits[b] & 1 << (index & _BIT_SIZE)) != 0; + } + + /// + /// Sets a bit at the given index. + /// Resizes its internal array if necessary. + /// + /// The index. + public void SetBit(int index) + { + var b = index >> _INDEX_SIZE; + if (b >= _bits.Length) + { + Array.Resize(ref _bits, RoundToPadding(b)); + } + + // Track highest set bit + _highestBit = Math.Max(_highestBit, index); + _max = _highestBit / (_BIT_SIZE + 1) + 1; + _bits[b] |= 1u << (index & _BIT_SIZE); + } + + /// + /// Clears the bit at the given index. + /// + /// The index. + public void ClearBit(int index) + { + var b = index >> _INDEX_SIZE; + if (b >= _bits.Length) + { + return; + } + + _bits[b] &= ~(1u << (index & _BIT_SIZE)); + } + + /// + /// Sets all bits. + /// + public void SetAll() + { + var count = _bits.Length; + for (var i = 0; i < count; i++) + { + _bits[i] = 0xffffffff; + } + + _highestBit = _bits.Length * (_BIT_SIZE + 1) - 1; + _max = _highestBit / (_BIT_SIZE + 1) + 1; + } + + /// + /// Clears all set bits. + /// + public void ClearAll() + { + Array.Clear(_bits); + _highestBit = 0; + _max = 0; + } + + /// + /// Finds the next set bit at or after `startIndex`, or -1 if none. + /// + public int NextSetBit(int startIndex) + { + var wordIndex = startIndex >> _BIT_SIZE; + if (wordIndex >= _bits.Length) + { + return -1; + } + + // Mask off bits below startIndex in the first word: + var word = _bits[wordIndex] & ~0u << (startIndex & _MASK); + while (true) + { + if (word != 0) + { + // get the least-significant set bit + var bit = BitOperations.TrailingZeroCount(word); + return (wordIndex << _BIT_SIZE) + bit; + } + + wordIndex++; + if (wordIndex >= _bits.Length) + { + return -1; + } + + word = _bits[wordIndex]; + } + } + + /// + /// Checks if all bits from this instance match those of the other instance. + /// + /// The other . + /// True if they match, false if not. + [SkipLocalsInit] + public bool All(BitSet other) + { + var min = Math.Min(Math.Min(Length, other.Length), _max); + if (!Vector.IsHardwareAccelerated || min < s_padding) + { + var bits = _bits.AsSpan(); + var otherBits = other._bits.AsSpan(); + + // Bitwise and + for (var i = 0; i < min; i++) + { + var bit = bits[i]; + if ((bit & otherBits[i]) != bit) + { + return false; + } + } + + // Handle extra bits on our side that might just be all zero. + for (var i = min; i < _max; i++) + { + if (bits[i] != 0) + { + return false; + } + } + } + else + { + // Vectorized bitwise and + for (var i = 0; i < min; i += s_padding) + { + var vector = new Vector(_bits.AsSpan()[i..]); + var otherVector = new Vector(other._bits.AsSpan()[i..]); + + var resultVector = Vector.BitwiseAnd(vector, otherVector); + if (!Vector.EqualsAll(resultVector, vector)) + { + return false; + } + } + + // Handle extra bits on our side that might just be all zero. + for (var i = min; i < _max; i += s_padding) + { + var vector = new Vector(_bits.AsSpan()[i..]); + if (!Vector.EqualsAll(vector, Vector.Zero)) // Vectors are not zero bits[0] != 0 basically + { + return false; + } + } + } + + return true; + } + + /// + /// Checks if any bits from this instance match those of the other instance. + /// + /// The other . + /// True if they match, false if not. + public bool Any(BitSet other) + { + var min = Math.Min(Math.Min(Length, other.Length), _max); + if (!Vector.IsHardwareAccelerated || min < s_padding) + { + var bits = _bits.AsSpan(); + var otherBits = other._bits.AsSpan(); + + // Bitwise and, return true since any is met + for (var i = 0; i < min; i++) + { + var bit = bits[i]; + if ((bit & otherBits[i]) > 0) + { + return true; + } + } + + // Handle extra bits on our side that might just be all zero. + for (var i = min; i < _max; i++) + { + if (bits[i] > 0) + { + return false; + } + } + } + else + { + // Vectorized bitwise and, return true since any is met + for (var i = 0; i < min; i += s_padding) + { + var vector = new Vector(_bits.AsSpan()[i..]); + var otherVector = new Vector(other._bits.AsSpan()[i..]); + + var resultVector = Vector.BitwiseAnd(vector, otherVector); + if (!Vector.EqualsAll(resultVector, Vector.Zero)) + { + return true; + } + } + + // Handle extra bits on our side that might just be all zero. + for (var i = min; i < _max; i += s_padding) + { + var vector = new Vector(_bits.AsSpan()[i..]); + if (!Vector.EqualsAll(vector, Vector.Zero)) // Vectors are not zero bits[0] != 0 basically + { + return false; + } + } + } + + return _highestBit <= 0; + } + + /// + /// Checks if none bits from this instance match those of the other instance. + /// + /// The other . + /// True if none match, false if not. + public bool None(BitSet other) + { + var min = Math.Min(Math.Min(Length, other.Length), _max); + if (!Vector.IsHardwareAccelerated || min < s_padding) + { + var bits = _bits.AsSpan(); + var otherBits = other._bits.AsSpan(); + + // Bitwise and, return true since any is met + for (var i = 0; i < min; i++) + { + var bit = bits[i]; + if ((bit & otherBits[i]) != 0) + { + return false; + } + } + } + else + { + // Vectorized bitwise and, return true since any is met + for (var i = 0; i < min; i += s_padding) + { + var vector = new Vector(_bits.AsSpan()[i..]); + var otherVector = new Vector(other._bits.AsSpan()[i..]); + + var resultVector = Vector.BitwiseAnd(vector, otherVector); + if (!Vector.EqualsAll(resultVector, Vector.Zero)) + { + return false; + } + } + } + + return true; + } + + /// + /// Checks if exactly all bits from this instance match those of the other instance. + /// + /// The other . + /// True if they match, false if not. + public bool Exclusive(BitSet other) + { + var min = Math.Min(Math.Min(Length, other.Length), _max); + + if (!Vector.IsHardwareAccelerated || min < s_padding) + { + var bits = _bits.AsSpan(); + var otherBits = other._bits.AsSpan(); + + // Bitwise xor, if both are not totally equal, return false + for (var i = 0; i < min; i++) + { + var bit = bits[i]; + if ((bit ^ otherBits[i]) != 0) + { + return false; + } + } + + // handle extra bits on our side that might just be all zero + for (var i = min; i < _max; i++) + { + if (bits[i] != 0) + { + return false; + } + } + } + else + { + // Vectorized bitwise xor, return true since any is met + for (var i = 0; i < min; i += s_padding) + { + var vector = new Vector(_bits.AsSpan()[i..]); + var otherVector = new Vector(other._bits.AsSpan()[i..]); + + var resultVector = Vector.Xor(vector, otherVector); + if (!Vector.EqualsAll(resultVector, Vector.Zero)) + { + return false; + } + } + + // Handle extra bits on our side that might just be all zero. + for (var i = min; i < _max; i += s_padding) + { + var vector = new Vector(_bits.AsSpan()[i..]); + if (!Vector.EqualsAll(vector, Vector.Zero)) // Vectors are not zero bits[0] != 0 basically + { + return false; + } + } + } + + return true; + } + + public static BitSet operator &(BitSet left, BitSet right) + { + var min = Math.Min(left.Length, right.Length); + var result = new BitSet(min); + if (!Vector.IsHardwareAccelerated || min < s_padding) + { + for (var i = 0; i < min; i++) + { + result._bits[i] = left._bits[i] & right._bits[i]; + } + } + else + { + for (var i = 0; i < min; i += s_padding) + { + var vectorLeft = new Vector(left._bits.AsSpan()[i..]); + var vectorRight = new Vector(right._bits.AsSpan()[i..]); + var resultVector = Vector.BitwiseAnd(vectorLeft, vectorRight); + resultVector.CopyTo(result._bits.AsSpan(i, s_padding)); + } + } + return result; + } + + public static BitSet operator |(BitSet left, BitSet right) + { + var min = Math.Min(left.Length, right.Length); + var result = new BitSet(min); + if (!Vector.IsHardwareAccelerated || min < s_padding) + { + for (var i = 0; i < min; i++) + { + result._bits[i] = left._bits[i] | right._bits[i]; + } + } + else + { + for (var i = 0; i < min; i += s_padding) + { + var vectorLeft = new Vector(left._bits.AsSpan()[i..]); + var vectorRight = new Vector(right._bits.AsSpan()[i..]); + var resultVector = Vector.BitwiseOr(vectorLeft, vectorRight); + resultVector.CopyTo(result._bits.AsSpan(i, s_padding)); + } + } + return result; + } + + public static BitSet operator ~(BitSet bitSet) + { + if (!Vector.IsHardwareAccelerated || bitSet.Length < s_padding) + { + for (var i = 0; i < bitSet.Length; i++) + { + bitSet._bits[i] = ~bitSet._bits[i]; + } + } + else + { + for (var i = 0; i < bitSet.Length; i += s_padding) + { + var vector = new Vector(bitSet._bits.AsSpan()[i..]); + var resultVector = ~vector; + resultVector.CopyTo(bitSet._bits.AsSpan(i, s_padding)); + } + } + + return bitSet; + } + + /// + /// Creates a to access the . + /// + /// The hash. + public Span AsSpan() + { + var max = _highestBit / (_BIT_SIZE + 1) + 1; + return _bits.AsSpan()[..max]; + } + + /// + /// Copies the bits into a and returns a slice containing the copied . + /// + /// The to copy into. + /// If true, it will zero the unused space from the . + /// The . + public Span AsSpan(Span span, bool zero = true) + { + // Copy everything thats possible from one to another + var length = Math.Min(Length, span.Length); + for (var index = 0; index < length; index++) + { + span[index] = _bits[index]; + } + + // Zero the rest space which was not overriden due to the copy. + for (var index = length; zero && index < span.Length; index++) + { + span[index] = 0; + } + + return span[..Length]; + } + + /// + /// Prints the content of this instance. + /// + /// The string. + public override string ToString() + { + // Convert uint to binary form for pretty printing + var binaryBuilder = new StringBuilder(); + foreach (var bit in _bits) + { + binaryBuilder.Append(Convert.ToString(bit, 2).PadLeft(32, '0')).Append(','); + } + binaryBuilder.Length--; + + return $"{nameof(_bits)}: {binaryBuilder}, {nameof(Length)}: {Length}"; + } +} + +/// +/// The struct +/// represents a non resizable collection of bits. +/// Used to set, check and clear bits on a allocated or on the stack. +/// +public readonly ref struct SpanBitSet +{ + private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31 + // NOTE: Is a byte not 8 bits? + private const int _BYTE_SIZE = 5; // log_2(BitSize + 1) + + /// + /// The bits from the bitset. + /// + private readonly Span _bits; + + /// + /// Initializes a new instance of the class. + /// + public SpanBitSet(Span bits) + { + _bits = bits; + } + + /// + /// Checks whether a bit is set at the index. + /// + /// The index. + /// True if it is, otherwise false + + public bool IsSet(int index) + { + var b = index >> _BYTE_SIZE; + if (b >= _bits.Length) + { + return false; + } + + return (_bits[b] & 1 << (index & _BIT_SIZE)) != 0; + } + + /// + /// Sets a bit at the given index. + /// Resizes its internal array if necessary. + /// + /// The index. + + public void SetBit(int index) + { + var b = index >> _BYTE_SIZE; + if (b >= _bits.Length) + { + return; + } + + _bits[b] |= 1u << (index & _BIT_SIZE); + } + + /// + /// Clears the bit at the given index. + /// + /// The index. + + public void ClearBit(int index) + { + var b = index >> _BYTE_SIZE; + if (b >= _bits.Length) + { + return; + } + + _bits[b] &= ~(1u << (index & _BIT_SIZE)); + } + + /// + /// + /// + + public void SetAll() + { + var count = _bits.Length; + for (var i = 0; i < count; i++) + { + _bits[i] = 0xffffffff; + } + } + + /// + /// Clears all set bits. + /// + + public void ClearAll() + { + _bits.Clear(); + } + + /// + /// Creates a to access the . + /// + /// The hash. + + public Span AsSpan() + { + return _bits; + } + + /// + /// Copies the bits into a and returns a slice containing the copied . + /// + /// + /// The hash. + + public Span AsSpan(Span span, bool zero = true) + { + // Prevent exception because target array is to small for copy operation + var length = Math.Min(_bits.Length, span.Length); + for (var index = 0; index < length; index++) + { + span[index] = _bits[index]; + } + + // Zero the rest space which was not overriden due to the copy. + for (var index = length; zero && index < span.Length; index++) + { + span[index] = 0; + } + + return span[.._bits.Length]; + } + + /// + /// Prints the content of this instance. + /// + /// The string. + + public override string ToString() + { + // Convert uint to binary form for pretty printing + var binaryBuilder = new StringBuilder(); + foreach (var bit in _bits) + { + binaryBuilder.Append(Convert.ToString(bit, 2).PadLeft(32, '0')).Append(','); + } + binaryBuilder.Length--; + + return $"{nameof(_bits)}: {string.Join(",", binaryBuilder)}"; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Collections/Contracts/IUnsafeCollection.cs b/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeCollection.cs similarity index 94% rename from Misaki.HighPerformance.Unsafe/Collections/Contracts/IUnsafeCollection.cs rename to Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeCollection.cs index aebaadc..785b7a1 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/Contracts/IUnsafeCollection.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeCollection.cs @@ -1,4 +1,4 @@ -namespace Misaki.HighPerformance.Unsafe.Collections.Contracts; +namespace Misaki.HighPerformance.LowLevel.Collections.Contracts; public unsafe interface IUnsafeCollection : IEnumerable, IDisposable where T : unmanaged diff --git a/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeSet.cs b/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeSet.cs new file mode 100644 index 0000000..4b6916b --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeSet.cs @@ -0,0 +1,5 @@ +namespace Misaki.HighPerformance.LowLevel.Collections.Contracts; +internal class IUnsafeSet + where T : unmanaged, IEquatable +{ +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeArray.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeArray.cs similarity index 78% rename from Misaki.HighPerformance.Unsafe/Collections/UnsafeArray.cs rename to Misaki.HighPerformance.LowLevel/Collections/UnsafeArray.cs index 2ea3bb5..d88e2c6 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeArray.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeArray.cs @@ -1,10 +1,11 @@ -using Misaki.HighPerformance.Unsafe.Buffer; -using Misaki.HighPerformance.Unsafe.Collections.Contracts; -using Misaki.HighPerformance.Unsafe.Helpers; +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections.Contracts; +using Misaki.HighPerformance.LowLevel.Contracts; +using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Collections; +namespace Misaki.HighPerformance.LowLevel.Collections; /// /// A structure for managing an array of unmanaged types with unsafe memory operations. @@ -66,14 +67,22 @@ public unsafe struct UnsafeArray : IUnsafeCollection private T* _buffer; private int _count; - private readonly Allocator _allocator; + private AllocationHandle* _handle; public readonly int Count => _count; public readonly ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _buffer[index]; + get + { + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range."); + } + + return ref _buffer[index]; + } } public readonly bool IsCreated @@ -88,10 +97,23 @@ public unsafe struct UnsafeArray : IUnsafeCollection /// /// Constructs an UnsafeArray with a default size of 1 and uses the Persistent allocator. /// - public UnsafeArray() : this(1, Allocator.Persistent) + public UnsafeArray() + : this(1, Allocator.Persistent) { } + public UnsafeArray(int count, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) + { + if (count <= 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "Count must be greater than zero."); + } + + _handle = (AllocationHandle*)Unsafe.AsPointer(ref handle); + _buffer = (T*)handle.Alloc(_handle->Allocator, (uint)count * (uint)sizeof(T), (uint)AlignOf(), allocationOption); + _count = count; + } + /// /// Initializes a new instance of UnsafeArray with a specified number of elements and an allocation type. /// @@ -100,15 +122,8 @@ public unsafe struct UnsafeArray : IUnsafeCollection /// Determines how the memory should be allocated. /// Thrown when the specified number of elements is less than or equal to zero. public UnsafeArray(int count, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) + : this(count, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) { - if (count <= 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Count must be greater than zero."); - } - - _buffer = AllocationManager.Allocate((uint)count, (uint)AlignOf(), allocator, allocationOption); - _count = count; - _allocator = allocator; } /// @@ -125,7 +140,7 @@ public unsafe struct UnsafeArray : IUnsafeCollection { _buffer = (T*)buffer; _count = count; - _allocator = Allocator.External; + _handle = (AllocationHandle*)Unsafe.AsPointer(ref AllocationManager.EmptyAllocator.Handle); } /// @@ -136,7 +151,7 @@ public unsafe struct UnsafeArray : IUnsafeCollection return; } - _buffer = AllocationManager.Realloc(_buffer, (uint)newSize, (uint)AlignOf(), _allocator); + _buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (uint)newSize, (uint)AlignOf()); _count = newSize; } @@ -162,10 +177,10 @@ public unsafe struct UnsafeArray : IUnsafeCollection return; } - AllocationManager.Free(_buffer, _allocator); + _handle->Free(_handle->Allocator, _buffer); + _handle = null; _buffer = null; _count = 0; } -} - +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashMap.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashMap.cs similarity index 91% rename from Misaki.HighPerformance.Unsafe/Collections/UnsafeHashMap.cs rename to Misaki.HighPerformance.LowLevel/Collections/UnsafeHashMap.cs index 181b53b..166e80d 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashMap.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashMap.cs @@ -1,11 +1,14 @@ -using Misaki.HighPerformance.Unsafe.Collections.Contracts; -using Misaki.HighPerformance.Unsafe.Helpers; +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections.Contracts; +using Misaki.HighPerformance.LowLevel.Contracts; +using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Collections; +namespace Misaki.HighPerformance.LowLevel.Collections; -public unsafe struct UnsafeHashMap : IUnsafeCollection> where TKey : unmanaged, IEquatable where TValue : unmanaged +public unsafe struct UnsafeHashMap : IUnsafeCollection> + where TKey : unmanaged, IEquatable where TValue : unmanaged { public struct Enumerator : IEnumerator> { @@ -92,9 +95,14 @@ public unsafe struct UnsafeHashMap : IUnsafeCollection> GetEnumerator() => new Enumerator((HashMapHelper*)UnsafeUtilities.AddressOf(ref _hashMap)); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) + public UnsafeHashMap(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) + { + _hashMap = new HashMapHelper(capacity, sizeof(TValue), HashMapHelper.MINIMAL_CAPACITY, ref handle, allocationOption); + } + + public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) + : this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) { - _hashMap = new HashMapHelper(capacity, sizeof(TValue), HashMapHelper.MINIMAL_CAPACITY, allocator, allocationOption); } /// diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashSet.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashSet.cs similarity index 86% rename from Misaki.HighPerformance.Unsafe/Collections/UnsafeHashSet.cs rename to Misaki.HighPerformance.LowLevel/Collections/UnsafeHashSet.cs index ff4801d..f3371fd 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashSet.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeHashSet.cs @@ -1,9 +1,11 @@ -using Misaki.HighPerformance.Unsafe.Collections.Contracts; -using Misaki.HighPerformance.Unsafe.Helpers; +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections.Contracts; +using Misaki.HighPerformance.LowLevel.Contracts; +using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Collections; +namespace Misaki.HighPerformance.LowLevel.Collections; /// /// A collection that provides fast, unsafe operations for managing a set of unmanaged types. It supports adding, @@ -49,9 +51,14 @@ public unsafe struct UnsafeHashSet : IUnsafeCollection, IEnumerable public IEnumerator GetEnumerator() => new Enumerator((HashMapHelper*)UnsafeUtilities.AddressOf(ref _hashMap)); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public UnsafeHashSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) + public UnsafeHashSet(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) + { + _hashMap = new HashMapHelper(capacity, 0, HashMapHelper.MINIMAL_CAPACITY, ref handle, allocationOption); + } + + public UnsafeHashSet(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) + : this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) { - _hashMap = new HashMapHelper(capacity, 0, HashMapHelper.MINIMAL_CAPACITY, allocator, allocationOption); } /// diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeList.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs similarity index 96% rename from Misaki.HighPerformance.Unsafe/Collections/UnsafeList.cs rename to Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs index 487f8f1..7785fe5 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeList.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs @@ -1,9 +1,9 @@ -using Misaki.HighPerformance.Unsafe.Collections.Contracts; -using Misaki.HighPerformance.Unsafe.Helpers; +using Misaki.HighPerformance.LowLevel.Collections.Contracts; +using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Collections; +namespace Misaki.HighPerformance.LowLevel.Collections; /// /// A collection that allows for unsafe operations on a list of unmanaged types. @@ -118,12 +118,10 @@ public unsafe struct UnsafeList : IUnsafeCollection public readonly int Capacity => _array.Count; public readonly bool IsCreated => _array.IsCreated; - public readonly T this[int index] + public readonly ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _array[index]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => _array[index] = value; + get => ref _array[index]; } public IEnumerator GetEnumerator() => new Enumerator((UnsafeList*)UnsafeUtilities.AddressOf(ref this)); diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeQueue.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeQueue.cs similarity index 96% rename from Misaki.HighPerformance.Unsafe/Collections/UnsafeQueue.cs rename to Misaki.HighPerformance.LowLevel/Collections/UnsafeQueue.cs index d7d94bf..79c6004 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeQueue.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeQueue.cs @@ -1,9 +1,9 @@ -using Misaki.HighPerformance.Unsafe.Collections.Contracts; -using Misaki.HighPerformance.Unsafe.Helpers; +using Misaki.HighPerformance.LowLevel.Collections.Contracts; +using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Collections; +namespace Misaki.HighPerformance.LowLevel.Collections; /// /// A structure that implements a queue using unmanaged types for efficient memory management. diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeStack.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs similarity index 92% rename from Misaki.HighPerformance.Unsafe/Collections/UnsafeStack.cs rename to Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs index 0d71a80..f067ace 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeStack.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs @@ -1,9 +1,9 @@ -using Misaki.HighPerformance.Unsafe.Collections.Contracts; -using Misaki.HighPerformance.Unsafe.Helpers; +using Misaki.HighPerformance.LowLevel.Collections.Contracts; +using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Collections; +namespace Misaki.HighPerformance.LowLevel.Collections; public unsafe struct UnsafeStack : IUnsafeCollection where T : unmanaged diff --git a/Misaki.HighPerformance.LowLevel/Contracts/IAllocator.cs b/Misaki.HighPerformance.LowLevel/Contracts/IAllocator.cs new file mode 100644 index 0000000..eeb4d72 --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Contracts/IAllocator.cs @@ -0,0 +1,52 @@ +using Misaki.HighPerformance.LowLevel.Collections; + +namespace Misaki.HighPerformance.LowLevel.Contracts; + +using unsafe AllocFunc = delegate* unmanaged; +using unsafe FreeFunc = delegate* unmanaged; +using unsafe ReallocFunc = delegate* unmanaged; + +public unsafe readonly struct AllocationHandle +{ + public void* Allocator + { + get; + } + + public AllocFunc Alloc + { + get; + } + + public ReallocFunc Realloc + { + get; + } + + public FreeFunc Free + { + get; + } + + public AllocationHandle(void* allocator, AllocFunc alloc, ReallocFunc realloc, FreeFunc free) + { + Allocator = allocator; + Alloc = alloc; + Realloc = realloc; + Free = free; + } +} + +/// +/// Represents an allocator interface for managing memory allocations. +/// +/// +/// The allocator must be static or pined to a specific memory region. +/// +public unsafe interface IAllocator +{ + public ref AllocationHandle Handle + { + get; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Exceptions/MemoryLeakException.cs b/Misaki.HighPerformance.LowLevel/Exceptions/MemoryLeakException.cs similarity index 65% rename from Misaki.HighPerformance.Unsafe/Exceptions/MemoryLeakException.cs rename to Misaki.HighPerformance.LowLevel/Exceptions/MemoryLeakException.cs index 0976c33..17be21a 100644 --- a/Misaki.HighPerformance.Unsafe/Exceptions/MemoryLeakException.cs +++ b/Misaki.HighPerformance.LowLevel/Exceptions/MemoryLeakException.cs @@ -1,27 +1,12 @@ -#if DEBUG +using Misaki.HighPerformance.LowLevel.Buffer; using System.Diagnostics; using System.Text; -#endif -namespace Misaki.HighPerformance.Unsafe; +namespace Misaki.HighPerformance.LowLevel.Exceptions; -public readonly struct MemoryLeakExceptionInfo + +public class MemoryLeakException(params AllocationManager.AllocationInfo[] Infos) : Exception { - public nuint Size - { - get; init; - } -#if DEBUG - public StackTrace StackTrace - { - get; init; - } -#endif -} - -public class MemoryLeakException(params MemoryLeakExceptionInfo[] Infos) : Exception -{ -#if DEBUG private static string GetMessage(StackTrace? stackTrace) { if (stackTrace == null) @@ -43,13 +28,11 @@ public class MemoryLeakException(params MemoryLeakExceptionInfo[] Infos) : Excep return stringBuilder.ToString(); } -#endif public override string Message { get { -#if DEBUG var stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Found {Infos.Length} memory lakes!"); foreach (var info in Infos) @@ -58,9 +41,6 @@ public class MemoryLeakException(params MemoryLeakExceptionInfo[] Infos) : Excep } return stringBuilder.ToString(); -#else - return $"There are still {Infos.Length} buffers that hold {Infos.Sum(i => (uint)i.Size)} bytes in total are not freed yet. Please free them before disposing. Switch to debug mode for more information."; -#endif } } } \ No newline at end of file diff --git a/Misaki.HighPerformance.LowLevel/FunctionPointer.cs b/Misaki.HighPerformance.LowLevel/FunctionPointer.cs new file mode 100644 index 0000000..c53ae69 --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/FunctionPointer.cs @@ -0,0 +1,34 @@ +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.LowLevel; + +public readonly struct FunctionPointer + where T : Delegate +{ + private readonly nint _ptr; + private readonly T _delegate; + + /// + /// Gets the native function pointer associated with this function pointer instance. + /// + public readonly nint Pointer => _ptr; + + /// + /// Gets the delegate instance associated with the specified function pointer. + /// + /// This property uses to convert the function + /// pointer to a delegate. Ensure that the function pointer is valid and compatible with the delegate type + /// . + public T Invoke => _delegate; + + /// + /// Creates a new instance of this function pointer with the following native pointer. + /// + /// + public FunctionPointer(nint ptr) + { + _ptr = ptr; + _delegate = Marshal.GetDelegateForFunctionPointer(ptr); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Helpers/HashMapHelper.cs b/Misaki.HighPerformance.LowLevel/Helpers/HashMapHelper.cs similarity index 91% rename from Misaki.HighPerformance.Unsafe/Helpers/HashMapHelper.cs rename to Misaki.HighPerformance.LowLevel/Helpers/HashMapHelper.cs index 3e08045..ca108d3 100644 --- a/Misaki.HighPerformance.Unsafe/Helpers/HashMapHelper.cs +++ b/Misaki.HighPerformance.LowLevel/Helpers/HashMapHelper.cs @@ -1,9 +1,9 @@ -using Misaki.HighPerformance.Unsafe.Buffer; -using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.LowLevel.Collections; +using Misaki.HighPerformance.LowLevel.Contracts; using System.Numerics; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Helpers; +namespace Misaki.HighPerformance.LowLevel.Helpers; public unsafe struct HashMapHelper : IDisposable where TKey : unmanaged, IEquatable @@ -75,8 +75,7 @@ public unsafe struct HashMapHelper : IDisposable private readonly int _sizeOfTValue; private readonly int _log2MinGrowth; - private readonly Allocator _allocator; - private readonly AllocationOption _allocationOption; + private AllocationHandle* _handle; public const int MINIMAL_CAPACITY = 64; @@ -128,7 +127,7 @@ public unsafe struct HashMapHelper : IDisposable return totalSize; } - public HashMapHelper(int capacity, int sizeOfTValue, uint minGrowth, Allocator allocator, AllocationOption allocationOption) + public HashMapHelper(int capacity, int sizeOfTValue, uint minGrowth, ref AllocationHandle handle, AllocationOption allocationOption) { if (capacity <= 0) { @@ -146,13 +145,12 @@ public unsafe struct HashMapHelper : IDisposable _sizeOfTValue = sizeOfTValue; _log2MinGrowth = BitOperations.Log2(minGrowth); - _allocator = allocator; - _allocationOption = allocationOption; + _handle = (AllocationHandle*)Unsafe.AsPointer(ref handle); var totalSize = CalculateDataSize(_capacity, _bucketCapacity, sizeOfTValue, out var keyOffset, out var nextOffset, out var bucketOffset); - AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset); + AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset, allocationOption); Clear(); } @@ -169,7 +167,8 @@ public unsafe struct HashMapHelper : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] private readonly int GetBucket(in TKey key) { - return (int)((uint)key.GetHashCode() & _bucketCapacity - 1); + var h = (uint)key.GetHashCode(); + return (int)(h & (uint)(_bucketCapacity - 1)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -177,16 +176,16 @@ public unsafe struct HashMapHelper : IDisposable { if ((uint)idx >= (uint)_capacity) { - throw new InvalidOperationException($"Internal HashMap error. idx {idx}"); + throw new InvalidOperationException($"Index {idx} is out of bounds for the hash map with capacity {_capacity}."); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void AllocateBuffer(int totalSize, int keyOffset, int nextOffset, int bucketOffset) + private void AllocateBuffer(int totalSize, int keyOffset, int nextOffset, int bucketOffset, AllocationOption allocationOption) { var alignSize = sizeof(TKey) > sizeof(int) ? AlignOf() : AlignOf(); - _buffer = AllocationManager.Allocate((uint)totalSize, (uint)alignSize, _allocator, _allocationOption); + _buffer = (byte*)_handle->Alloc(_handle->Allocator, (uint)totalSize, (uint)alignSize, allocationOption); _keys = (TKey*)(_buffer + keyOffset); _next = (int*)(_buffer + nextOffset); _buckets = (int*)(_buffer + bucketOffset); @@ -203,7 +202,7 @@ public unsafe struct HashMapHelper : IDisposable var oldBuckets = _buckets; var oldBucketCapacity = _bucketCapacity; - AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset); + AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset, AllocationOption.None); _capacity = newCapacity; _bucketCapacity = newBucketCapacity; @@ -218,7 +217,7 @@ public unsafe struct HashMapHelper : IDisposable } } - Free(oldBuffer); + _handle->Free(_handle->Allocator, oldBuffer); } internal void Resize(int newCapacity) @@ -240,7 +239,7 @@ public unsafe struct HashMapHelper : IDisposable ResizeExact(capacity, capacity * 2); } - public int Find(TKey key) + public int Find(in TKey key) { if (_allocatedIndex <= 0) { @@ -254,8 +253,6 @@ public unsafe struct HashMapHelper : IDisposable if ((uint)entryIdx < (uint)_capacity) { var nextPtrs = _next; - var test = UnsafeUtilities.ReadArrayElement(_keys, entryIdx); - var test2 = UnsafeUtilities.ReadArrayElement(_next, 0); while (!UnsafeUtilities.ReadArrayElement(_keys, entryIdx).Equals(key)) { entryIdx = nextPtrs[entryIdx]; @@ -273,7 +270,8 @@ public unsafe struct HashMapHelper : IDisposable public int TryAdd(in TKey key) { - if (Find(key) != -1) + var k = key; + if (Find(in key) != -1) { return -1; } @@ -284,7 +282,7 @@ public unsafe struct HashMapHelper : IDisposable if (_allocatedIndex >= _capacity && _firstFreeIndex < 0) { - var newCap = Math.Min(MINIMAL_CAPACITY, CalcCapacityCeilPow2(_capacity + (1 << _log2MinGrowth))); + var newCap = CalcCapacityCeilPow2(_capacity + (1 << _log2MinGrowth)); Resize(newCap); } @@ -313,7 +311,7 @@ public unsafe struct HashMapHelper : IDisposable return idx; } - public int TryRemove(TKey key) + public int TryRemove(in TKey key) { if (_capacity == 0) { @@ -332,7 +330,7 @@ public unsafe struct HashMapHelper : IDisposable { if (UnsafeUtilities.ReadArrayElement(_keys, entryIdx).Equals(key)) { - ++removed; + removed++; // Found matching element, remove it if (prevEntry < 0) @@ -363,7 +361,7 @@ public unsafe struct HashMapHelper : IDisposable return 0 != removed ? removed : -1; } - public bool TryGetValue(TKey key, out TValue item) + public bool TryGetValue(in TKey key, out TValue item) where TValue : unmanaged { var idx = Find(key); @@ -486,7 +484,7 @@ public unsafe struct HashMapHelper : IDisposable { if (IsCreated) { - AllocationManager.Free(_buffer, _allocator); + _handle->Free(_handle->Allocator, _buffer); _buffer = null; _keys = null; diff --git a/Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.Byte.cs b/Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.Byte.cs new file mode 100644 index 0000000..e96b5aa --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.Byte.cs @@ -0,0 +1,352 @@ +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace Misaki.HighPerformance.LowLevel.Helpers; + +public static unsafe partial class MemoryUtilities +{ + [DoesNotReturn] + private static void ThrowMustBeNullTerminatedString() + { + throw new ArgumentException("Arg_MustBeNullTerminatedString"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 LoadVector128(ref byte start, nuint offset) + => Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref start, offset)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 LoadVector256(ref byte start, nuint offset) + => Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref start, offset)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint GetByteVector128SpanLength(nuint offset, int length) + => (uint)((length - (int)offset) & ~(Vector128.Count - 1)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint GetByteVector256SpanLength(nuint offset, int length) + => (uint)((length - (int)offset) & ~(Vector256.Count - 1)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint GetByteVector512SpanLength(nuint offset, int length) + => (uint)((length - (int)offset) & ~(Vector512.Count - 1)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe nuint UnalignedCountVector128(byte* searchSpace) + { + var unaligned = (nint)searchSpace & (Vector128.Count - 1); + return (uint)((Vector128.Count - unaligned) & (Vector128.Count - 1)); + } + + /// + /// Searches for the first occurrence of a null byte (0x00) in a given byte array. + /// + /// A pointer to the byte array where the search will be performed. + /// Returns the index of the first null byte found in the array.. + /// Thrown if the byte array is not null-terminated." + public static unsafe int IndexOfNullByte(byte* searchSpace) + { + const int Length = int.MaxValue; + const uint uValue = 0; // Use uint for comparisons to avoid unnecessary 8->32 extensions + nuint offset = 0; // Use nuint for arithmetic to avoid unnecessary 64->32->64 truncations + var lengthToExamine = (nuint)(uint)Length; + + if (Vector128.IsHardwareAccelerated) + { + // Avx2 branch also operates on Sse2 sizes, so check is combined. + lengthToExamine = UnalignedCountVector128(searchSpace); + } + + SequentialScan: + while (lengthToExamine >= 8) + { + lengthToExamine -= 8; + + if (uValue == searchSpace[offset]) + goto Found; + if (uValue == searchSpace[offset + 1]) + goto Found1; + if (uValue == searchSpace[offset + 2]) + goto Found2; + if (uValue == searchSpace[offset + 3]) + goto Found3; + if (uValue == searchSpace[offset + 4]) + goto Found4; + if (uValue == searchSpace[offset + 5]) + goto Found5; + if (uValue == searchSpace[offset + 6]) + goto Found6; + if (uValue == searchSpace[offset + 7]) + goto Found7; + + offset += 8; + } + + if (lengthToExamine >= 4) + { + lengthToExamine -= 4; + + if (uValue == searchSpace[offset]) + goto Found; + if (uValue == searchSpace[offset + 1]) + goto Found1; + if (uValue == searchSpace[offset + 2]) + goto Found2; + if (uValue == searchSpace[offset + 3]) + goto Found3; + + offset += 4; + } + + while (lengthToExamine > 0) + { + lengthToExamine -= 1; + + if (uValue == searchSpace[offset]) + goto Found; + + offset += 1; + } + + // We get past SequentialScan only if IsHardwareAccelerated is true; and remain length is greater than Vector length. + // However, we still have the redundant check to allow the JIT to see that the code is unreachable and eliminate it when the platform does not + // have hardware accelerated. After processing Vector lengths we return to SequentialScan to finish any remaining. + if (Vector512.IsHardwareAccelerated) + { + if (offset < Length) + { + if ((((uint)searchSpace + offset) & (nuint)(Vector256.Count - 1)) != 0) + { + // Not currently aligned to Vector256 (is aligned to Vector128); this can cause a problem for searches + // with no upper bound e.g. String.strlen. + // Start with a check on Vector128 to align to Vector256, before moving to processing Vector256. + // This ensures we do not fault across memory pages while searching for an end of string. + var search = Vector128.Load(searchSpace + offset); + + // Same method as below + var matches = Vector128.Equals(Vector128.Zero, search).ExtractMostSignificantBits(); + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector128.Count; + } + else + { + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } + } + + if ((((uint)searchSpace + offset) & (nuint)(Vector512.Count - 1)) != 0) + { + // Not currently aligned to Vector512 (is aligned to Vector256); this can cause a problem for searches + // with no upper bound e.g. String.strlen. + // Start with a check on Vector256 to align to Vector512, before moving to processing Vector256. + // This ensures we do not fault across memory pages while searching for an end of string. + var search = Vector256.Load(searchSpace + offset); + + // Same method as below + var matches = Vector256.Equals(Vector256.Zero, search).ExtractMostSignificantBits(); + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector256.Count; + } + else + { + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } + } + lengthToExamine = GetByteVector512SpanLength(offset, Length); + if (lengthToExamine > offset) + { + do + { + var search = Vector512.Load(searchSpace + offset); + var matches = Vector512.Equals(Vector512.Zero, search).ExtractMostSignificantBits(); + // Note that MoveMask has converted the equal vector elements into a set of bit flags, + // So the bit position in 'matches' corresponds to the element offset. + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector512.Count; + continue; + } + + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } while (lengthToExamine > offset); + } + + lengthToExamine = GetByteVector256SpanLength(offset, Length); + if (lengthToExamine > offset) + { + var search = Vector256.Load(searchSpace + offset); + + // Same method as above + var matches = Vector256.Equals(Vector256.Zero, search).ExtractMostSignificantBits(); + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector256.Count; + } + else + { + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } + } + + lengthToExamine = GetByteVector128SpanLength(offset, Length); + if (lengthToExamine > offset) + { + var search = Vector128.Load(searchSpace + offset); + + // Same method as above + var matches = Vector128.Equals(Vector128.Zero, search).ExtractMostSignificantBits(); + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector128.Count; + } + else + { + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } + } + + if (offset < Length) + { + lengthToExamine = (Length - offset); + goto SequentialScan; + } + } + } + else if (Vector256.IsHardwareAccelerated) + { + if (offset < Length) + { + if ((((uint)searchSpace + offset) & (nuint)(Vector256.Count - 1)) != 0) + { + // Not currently aligned to Vector256 (is aligned to Vector128); this can cause a problem for searches + // with no upper bound e.g. String.strlen. + // Start with a check on Vector128 to align to Vector256, before moving to processing Vector256. + // This ensures we do not fault across memory pages while searching for an end of string. + var search = Vector128.Load(searchSpace + offset); + + // Same method as below + var matches = Vector128.Equals(Vector128.Zero, search).ExtractMostSignificantBits(); + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector128.Count; + } + else + { + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } + } + + lengthToExamine = GetByteVector256SpanLength(offset, Length); + if (lengthToExamine > offset) + { + do + { + var search = Vector256.Load(searchSpace + offset); + var matches = Vector256.Equals(Vector256.Zero, search).ExtractMostSignificantBits(); + // Note that MoveMask has converted the equal vector elements into a set of bit flags, + // So the bit position in 'matches' corresponds to the element offset. + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector256.Count; + continue; + } + + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } while (lengthToExamine > offset); + } + + lengthToExamine = GetByteVector128SpanLength(offset, Length); + if (lengthToExamine > offset) + { + var search = Vector128.Load(searchSpace + offset); + + // Same method as above + var matches = Vector128.Equals(Vector128.Zero, search).ExtractMostSignificantBits(); + if (matches == 0) + { + // Zero flags set so no matches + offset += (nuint)Vector128.Count; + } + else + { + // Find bitflag offset of first match and add to current offset + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } + } + + if (offset < Length) + { + lengthToExamine = (Length - offset); + goto SequentialScan; + } + } + } + else if (Vector128.IsHardwareAccelerated) + { + if (offset < Length) + { + lengthToExamine = GetByteVector128SpanLength(offset, Length); + + while (lengthToExamine > offset) + { + var search = Vector128.Load(searchSpace + offset); + + // Same method as above + var compareResult = Vector128.Equals(Vector128.Zero, search); + if (compareResult == Vector128.Zero) + { + // Zero flags set so no matches + offset += (nuint)Vector128.Count; + continue; + } + + // Find bitflag offset of first match and add to current offset + var matches = compareResult.ExtractMostSignificantBits(); + return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches)); + } + + if (offset < Length) + { + lengthToExamine = (Length - offset); + goto SequentialScan; + } + } + } + + ThrowMustBeNullTerminatedString(); + Found: // Workaround for https://github.com/dotnet/runtime/issues/8795 + return (int)offset; + Found1: + return (int)(offset + 1); + Found2: + return (int)(offset + 2); + Found3: + return (int)(offset + 3); + Found4: + return (int)(offset + 4); + Found5: + return (int)(offset + 5); + Found6: + return (int)(offset + 6); + Found7: + return (int)(offset + 7); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Helpers/MemoryUtilities.cs b/Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.cs similarity index 94% rename from Misaki.HighPerformance.Unsafe/Helpers/MemoryUtilities.cs rename to Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.cs index 07ddcec..3c2ea2c 100644 --- a/Misaki.HighPerformance.Unsafe/Helpers/MemoryUtilities.cs +++ b/Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.cs @@ -1,9 +1,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Misaki.HighPerformance.Unsafe.Helpers; +namespace Misaki.HighPerformance.LowLevel.Helpers; -public static unsafe class MemoryUtilities +public static unsafe partial class MemoryUtilities { [StructLayout(LayoutKind.Sequential)] private struct AlignOfHelper @@ -102,6 +102,11 @@ public static unsafe class MemoryUtilities [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MemClear(void* ptr, nuint size) { + if (ptr == null || size == 0) + { + return; + } + NativeMemory.Clear(ptr, size); } @@ -114,6 +119,11 @@ public static unsafe class MemoryUtilities [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MemSet(void* ptr, byte value, nuint size) { + if (ptr == null || size == 0) + { + return; + } + NativeMemory.Fill(ptr, size, value); } @@ -126,6 +136,11 @@ public static unsafe class MemoryUtilities [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MemCpy(void* source, void* destination, nuint size) { + if (source == null || destination == null || size == 0) + { + return; + } + NativeMemory.Copy(source, destination, size); } diff --git a/Misaki.HighPerformance.Unsafe/Helpers/UnsafeCollectionExtensions.cs b/Misaki.HighPerformance.LowLevel/Helpers/UnsafeCollectionExtensions.cs similarity index 64% rename from Misaki.HighPerformance.Unsafe/Helpers/UnsafeCollectionExtensions.cs rename to Misaki.HighPerformance.LowLevel/Helpers/UnsafeCollectionExtensions.cs index cc4417c..a7ce1fa 100644 --- a/Misaki.HighPerformance.Unsafe/Helpers/UnsafeCollectionExtensions.cs +++ b/Misaki.HighPerformance.LowLevel/Helpers/UnsafeCollectionExtensions.cs @@ -1,6 +1,8 @@ -using Misaki.HighPerformance.Unsafe.Collections.Contracts; +using Misaki.HighPerformance.LowLevel.Collections; +using Misaki.HighPerformance.LowLevel.Collections.Contracts; +using System.Runtime.InteropServices; -namespace Misaki.HighPerformance.Unsafe.Helpers; +namespace Misaki.HighPerformance.LowLevel.Helpers; /// /// Provides extension methods for copying elements between unsafe collections and spans, converting collections to @@ -49,7 +51,7 @@ public unsafe static class UnsafeCollectionExtensions fixed (T* ptr = destination) { - SystemUnsfae.CopyBlock(ptr + destinationIndex, (byte*)source.GetUnsafePtr() + (sourceIndex * sizeof(T)), (uint)(length * sizeof(T))); + SystemUnsfae.CopyBlock(ptr + destinationIndex, (byte*)source.GetUnsafePtr() + sourceIndex * sizeof(T), (uint)(length * sizeof(T))); } } @@ -94,45 +96,10 @@ public unsafe static class UnsafeCollectionExtensions fixed (T* ptr = source) { - SystemUnsfae.CopyBlock((byte*)destination.GetUnsafePtr() + (destinationIndex * sizeof(T)), ptr + sourceIndex, (uint)(length * sizeof(T))); + SystemUnsfae.CopyBlock((byte*)destination.GetUnsafePtr() + destinationIndex * sizeof(T), ptr + sourceIndex, (uint)(length * sizeof(T))); } } - /// - /// Converts an UnsafeCollection of unmanaged types into a standard collection. - /// - /// Represents a type that is unmanaged, allowing for direct memory manipulation. - /// The UnsafeCollection instance that contains the data to be converted. - /// A new collection containing the elements from the UnsafeCollection. - public static T[] ToArray(this IUnsafeCollection source) - where T : unmanaged - { - var array = new T[source.Count]; - fixed (T* ptr = array) - { - SystemUnsfae.CopyBlock(ptr, source.GetUnsafePtr(), (uint)(source.Count * sizeof(T))); - } - - return array; - } - - /// - /// Converts an unmanaged collection into a list by copying its elements into a new list. - /// - /// Represents a type that is unmanaged, allowing for direct memory manipulation. - /// The collection from which elements are copied to create the new list. - /// A list containing the elements from the specified unmanaged collection. - public static List ToList(this IUnsafeCollection source) - where T : unmanaged - { - var list = new List(source.Count); - fixed (T* ptr = list.ToArray()) - { - SystemUnsfae.CopyBlock(ptr, source.GetUnsafePtr(), (uint)(source.Count * sizeof(T))); - } - return list; - } - /// /// Converts an UnsafeCollection into a Span for efficient memory access. /// @@ -145,38 +112,27 @@ public unsafe static class UnsafeCollectionExtensions return new(source.GetUnsafePtr(), source.Count); } - /// - /// Finds the index of a specified value in a collection. Returns -1 if the value is not found. - /// - /// The type of elements in the collection, which must support equality comparison. - /// The collection to search for the specified value. - /// The value to locate within the collection. - /// Outputs the index of the found value or -1 if not found. - public static void IndexOf(this IUnsafeCollection source, T value, out int index) - where T : unmanaged, IEquatable + public static UnsafeArray ToUnsafeArray(this T[] source, Allocator allocator) + where T : unmanaged { - for (var i = 0; i < source.Count; i++) + var array = new UnsafeArray(source.Length, allocator); + fixed (T* ptr = source) { - if (UnsafeUtilities.ReadArrayElement(source.GetUnsafePtr(), i).Equals(value)) - { - index = i; - return; - } + MemCpy(array.GetUnsafePtr(), ptr, (uint)(source.Length * sizeof(T))); } - index = -1; + + return array; } - /// - /// Checks if a specified value exists within an unsafe collection of unmanaged types. - /// - /// Represents a type that is unmanaged and supports equality comparison. - /// The collection being searched for the specified value. - /// The value being searched for within the collection. - /// Returns true if the value is found; otherwise, returns false. - public static bool Conations(this IUnsafeCollection source, T value) - where T : unmanaged, IEquatable + public static UnsafeList ToUnsafeList(this List source, Allocator allocator) + where T : unmanaged { - source.IndexOf(value, out var index); - return index != -1; + var list = new UnsafeList(source.Count, allocator); + fixed (T* ptr = CollectionsMarshal.AsSpan(source)) + { + MemCpy(list.GetUnsafePtr(), ptr, (uint)(source.Count * sizeof(T))); + } + + return list; } } \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Helpers/UnsafeUtilities.cs b/Misaki.HighPerformance.LowLevel/Helpers/UnsafeUtilities.cs similarity index 96% rename from Misaki.HighPerformance.Unsafe/Helpers/UnsafeUtilities.cs rename to Misaki.HighPerformance.LowLevel/Helpers/UnsafeUtilities.cs index 48411c6..b1b61a0 100644 --- a/Misaki.HighPerformance.Unsafe/Helpers/UnsafeUtilities.cs +++ b/Misaki.HighPerformance.LowLevel/Helpers/UnsafeUtilities.cs @@ -1,7 +1,7 @@ -using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.LowLevel.Collections; using System.Runtime.CompilerServices; -namespace Misaki.HighPerformance.Unsafe.Helpers; +namespace Misaki.HighPerformance.LowLevel.Helpers; public static unsafe class UnsafeUtilities { @@ -39,7 +39,7 @@ public static unsafe class UnsafeUtilities [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T* ReadArrayElementUnsafe(void* ptr, int index) where T : unmanaged { - return (T*)((byte*)ptr + (index * sizeof(T))); + return (T*)((byte*)ptr + index * sizeof(T)); } /// diff --git a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj new file mode 100644 index 0000000..501a4a2 --- /dev/null +++ b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj @@ -0,0 +1,50 @@ + + + + net9.0 + enable + enable + True + + + + True + + + + True + + + + + + + + + FixedString.cs + TextTemplatingFileGenerator + + + TextTemplatingFileGenerator + FixedStackString.cs + + + + + + + + + + FixedString.tt + True + True + + + True + True + FixedStackString.tt + + + + diff --git a/Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj b/Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj new file mode 100644 index 0000000..a0ef131 --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj @@ -0,0 +1,51 @@ + + + + net9.0 + enable + enable + + + + + float4.gen.cs + TextTemplatingFileGenerator + + + float3.gen.cs + TextTemplatingFileGenerator + + + TextTemplatingFileGenerator + float2.gen.cs + + + + + + + + + + True + True + Float.tt + + + True + True + float2.tt + + + True + True + float3.tt + + + True + True + float4.tt + + + + diff --git a/Misaki.HighPerformance.Mathematics/Utilities.ttinclude b/Misaki.HighPerformance.Mathematics/Utilities.ttinclude new file mode 100644 index 0000000..c3b98c6 --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/Utilities.ttinclude @@ -0,0 +1,29 @@ +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Linq" #> +<#+ +public int[] dimensions = new[] { 2, 3, 4 }; + +public IEnumerable GenerateSwizzles(string[] pool, int maxLen) +{ + IEnumerable Recurse(string prefix, int depth) + { + if (depth == 0) + { + yield return prefix; + } + else + { + foreach (var c in pool) + { + foreach (var s in Recurse(prefix + c, depth - 1)) + { + yield return s; + } + } + } + } + + return Enumerable.Range(2, maxLen - 1).SelectMany(len => Recurse("", len)); +} +#> \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics/Vectorlize.cs b/Misaki.HighPerformance.Mathematics/Vectorlize.cs new file mode 100644 index 0000000..213dbdc --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/Vectorlize.cs @@ -0,0 +1,49 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace Misaki.HighPerformance.Mathematics; + +internal static class Vectorize +{ + public static Vector128 AsVector128(this float2 value) + { + Unsafe.SkipInit(out Vector128 result); + Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), value); + return result; + } + + public static Vector128 AsVector128(this float3 value) + { + Unsafe.SkipInit(out Vector128 result); + Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), value); + return result; + } + + public static Vector128 AsVector128(this float4 value) + { + Unsafe.SkipInit(out Vector128 result); + Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), value); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 AsFloat2(this Vector128 value) + { + ref var address = ref Unsafe.As, byte>(ref value); + return Unsafe.ReadUnaligned(ref address); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float3 AsFloat3(this Vector128 value) + { + ref var address = ref Unsafe.As, byte>(ref value); + return Unsafe.ReadUnaligned(ref address); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float4 AsFloat4(this Vector128 value) + { + ref var address = ref Unsafe.As, byte>(ref value); + return Unsafe.ReadUnaligned(ref address); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics/float2.gen.cs b/Misaki.HighPerformance.Mathematics/float2.gen.cs new file mode 100644 index 0000000..d02bb0f --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/float2.gen.cs @@ -0,0 +1,272 @@ + +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics; + +public struct float2 +{ + public float x; + public float y; + + public float2(float value) + { + this.x = value; + this.y = value; + } + + public float2(float x, float y) + { + this.x = x; + this.y = y; + } + + public float2(float3 value) + { + this.x = value.x; + this.y = value.y; + } + public float2(float4 value) + { + this.x = value.x; + this.y = value.y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator +(float2 lhs, float2 rhs) + { + return (lhs.AsVector128() + rhs.AsVector128()).AsFloat2(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator +(float2 lhs, float rhs) + { + return lhs + new float2(rhs); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator -(float2 lhs, float2 rhs) + { + return (lhs.AsVector128() - rhs.AsVector128()).AsFloat2(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator -(float2 lhs, float rhs) + { + return lhs - new float2(rhs); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator *(float2 lhs, float2 rhs) + { + return (lhs.AsVector128() * rhs.AsVector128()).AsFloat2(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator *(float2 lhs, float rhs) + { + return (lhs.AsVector128() * rhs).AsFloat2(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator /(float2 lhs, float2 rhs) + { + return (lhs.AsVector128() / rhs.AsVector128()).AsFloat2(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator /(float2 lhs, float rhs) + { + return (lhs.AsVector128() / rhs).AsFloat2(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float2 operator -(float2 value) + { + return (-value.AsVector128()).AsFloat2(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(float2 lhs, float2 rhs) + { + return lhs.AsVector128() == rhs.AsVector128(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(float2 lhs, float2 rhs) + { + return !(lhs == rhs); + } + + public readonly float2 xx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.x); + } + + public readonly float2 xy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.y); + } + + public readonly float2 yx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.x); + } + + public readonly float2 yy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.y); + } + + public readonly float3 xxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.x); + } + + public readonly float3 xxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.y); + } + + public readonly float3 xyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.x); + } + + public readonly float3 xyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.y); + } + + public readonly float3 yxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.x); + } + + public readonly float3 yxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.y); + } + + public readonly float3 yyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.x); + } + + public readonly float3 yyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.y); + } + + public readonly float4 xxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.x); + } + + public readonly float4 xxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.y); + } + + public readonly float4 xxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.x); + } + + public readonly float4 xxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.y); + } + + public readonly float4 xyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.x); + } + + public readonly float4 xyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.y); + } + + public readonly float4 xyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.x); + } + + public readonly float4 xyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.y); + } + + public readonly float4 yxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.x); + } + + public readonly float4 yxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.y); + } + + public readonly float4 yxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.x); + } + + public readonly float4 yxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.y); + } + + public readonly float4 yyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.x); + } + + public readonly float4 yyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.y); + } + + public readonly float4 yyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.x); + } + + public readonly float4 yyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.y); + } + + public override readonly string ToString() + { + return $"(x: {this.x}, y: {this.y})"; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics/float2.tt b/Misaki.HighPerformance.Mathematics/float2.tt new file mode 100644 index 0000000..6b2b197 --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/float2.tt @@ -0,0 +1,161 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ include file="Utilities.ttinclude" #> +<#@ output extension=".gen.cs" #> +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics; + +<# +var dimension = 2; +var components = new[] { "x", "y" }; +var structName = $"float{dimension}"; + +#> +public struct <#= structName #> +{ +<# for(int i = 0; i < dimension; i++) { #> + public float <#= components[i] #>; +<# } #> + + public <#= structName #>(float value) + { +<# for(int i = 0; i < dimension; i++) { #> + this.<#= components[i] #> = value; +<# } #> + } + + public <#= structName #>(<#= string.Join(", ", components.Take(dimension).Select(c => $"float {c}")) #>) + { +<# for(int i = 0; i < dimension; i++) { #> + this.<#= components[i] #> = <#= components[i] #>; +<# } #> + } + +<# foreach (var otherDim in dimensions.Where(d => d != dimension)) +{ + string otherStructName = $"float{otherDim}"; + + if (otherDim < dimension) + { +#> + public <#= structName #>(<#= otherStructName #> value) + { +<# for(int i = 0; i < Math.Min(dimension, otherDim); i++) { #> + this.<#= components[i] #> = value.<#= components[i] #>; +<# + } + + for(int i = otherDim; i < dimension; i++) { +#> + this.<#= components[i] #> = 0.0f; +<# } #> + } + +<# } +else +{ #> + public <#= structName #>(<#= otherStructName #> value) + { +<# + for(int i = 0; i < dimension; i++) { +#> + this.<#= components[i] #> = value.<#= components[i] #>; +<# } #> + } +<# + } +} +#> + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator +(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() + rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator +(<#= structName #> lhs, float rhs) + { + return lhs + new <#= structName #>(rhs); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator -(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() - rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator -(<#= structName #> lhs, float rhs) + { + return lhs - new <#= structName #>(rhs); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator *(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() * rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator *(<#= structName #> lhs, float rhs) + { + return (lhs.AsVector128() * rhs).AsFloat<#= dimension #>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator /(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() / rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator /(<#= structName #> lhs, float rhs) + { + return (lhs.AsVector128() / rhs).AsFloat<#= dimension #>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= structName #> operator -(<#= structName #> value) + { + return (-value.AsVector128()).AsFloat<#= dimension #>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(<#= structName #> lhs, <#= structName #> rhs) + { + return lhs.AsVector128() == rhs.AsVector128(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(<#= structName #> lhs, <#= structName #> rhs) + { + return !(lhs == rhs); + } + +<# +var validComponents = components.Take(dimension).ToArray(); +var swizzles = GenerateSwizzles(validComponents, 4); +foreach (var swizzle in swizzles) +{ + var targetDim = swizzle.Length; + var targetStruct = $"float{targetDim}"; +#> + public readonly <#= targetStruct #> <#= swizzle #> + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new <#= targetStruct #>(<#= string.Join(", ", swizzle.Select(c => $"this.{c}")) #>); + } + +<# +} +#> + public override readonly string ToString() + { + return $"(<#= string.Join(", ", components.Take(dimension).Select(c => $"{c}: {{this.{c}}}")) #>)"; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics/float3.gen.cs b/Misaki.HighPerformance.Mathematics/float3.gen.cs new file mode 100644 index 0000000..0885b84 --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/float3.gen.cs @@ -0,0 +1,791 @@ + +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics; + +public struct float3 +{ + public float x; + public float y; + public float z; + + public float3(float value) + { + this.x = value; + this.y = value; + this.z = value; + } + + public float3(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public float3(float2 value) + { + this.x = value.x; + this.y = value.y; + this.z = 0.0f; + } + + public float3(float4 value) + { + this.x = value.x; + this.y = value.y; + this.z = value.z; + } + + public static float3 operator +(float3 lhs, float3 rhs) + { + return (lhs.AsVector128() + rhs.AsVector128()).AsFloat3(); + } + + public static float3 operator +(float3 lhs, float rhs) + { + return lhs + new float3(rhs); + } + + public static float3 operator -(float3 lhs, float3 rhs) + { + return (lhs.AsVector128() - rhs.AsVector128()).AsFloat3(); + } + + public static float3 operator -(float3 lhs, float rhs) + { + return lhs - new float3(rhs); + } + + public static float3 operator *(float3 lhs, float3 rhs) + { + return (lhs.AsVector128() * rhs.AsVector128()).AsFloat3(); + } + + public static float3 operator *(float3 lhs, float rhs) + { + return (lhs.AsVector128() * rhs).AsFloat3(); + } + + public static float3 operator /(float3 lhs, float3 rhs) + { + return (lhs.AsVector128() / rhs.AsVector128()).AsFloat3(); + } + + public static float3 operator /(float3 lhs, float rhs) + { + return (lhs.AsVector128() / rhs).AsFloat3(); + } + + public static float3 operator -(float3 value) + { + return (-value.AsVector128()).AsFloat3(); + } + + public float2 xx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.x); + } + + public float2 xy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.y); + } + + public float2 xz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.z); + } + + public float2 yx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.x); + } + + public float2 yy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.y); + } + + public float2 yz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.z); + } + + public float2 zx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.z, this.x); + } + + public float2 zy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.z, this.y); + } + + public float2 zz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.z, this.z); + } + + public float3 xxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.x); + } + + public float3 xxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.y); + } + + public float3 xxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.z); + } + + public float3 xyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.x); + } + + public float3 xyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.y); + } + + public float3 xyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.z); + } + + public float3 xzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.z, this.x); + } + + public float3 xzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.z, this.y); + } + + public float3 xzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.z, this.z); + } + + public float3 yxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.x); + } + + public float3 yxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.y); + } + + public float3 yxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.z); + } + + public float3 yyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.x); + } + + public float3 yyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.y); + } + + public float3 yyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.z); + } + + public float3 yzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.z, this.x); + } + + public float3 yzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.z, this.y); + } + + public float3 yzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.z, this.z); + } + + public float3 zxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.x, this.x); + } + + public float3 zxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.x, this.y); + } + + public float3 zxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.x, this.z); + } + + public float3 zyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.y, this.x); + } + + public float3 zyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.y, this.y); + } + + public float3 zyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.y, this.z); + } + + public float3 zzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.z, this.x); + } + + public float3 zzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.z, this.y); + } + + public float3 zzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.z, this.z); + } + + public float4 xxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.x); + } + + public float4 xxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.y); + } + + public float4 xxxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.z); + } + + public float4 xxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.x); + } + + public float4 xxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.y); + } + + public float4 xxyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.z); + } + + public float4 xxzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.z, this.x); + } + + public float4 xxzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.z, this.y); + } + + public float4 xxzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.z, this.z); + } + + public float4 xyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.x); + } + + public float4 xyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.y); + } + + public float4 xyxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.z); + } + + public float4 xyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.x); + } + + public float4 xyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.y); + } + + public float4 xyyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.z); + } + + public float4 xyzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.z, this.x); + } + + public float4 xyzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.z, this.y); + } + + public float4 xyzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.z, this.z); + } + + public float4 xzxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.x, this.x); + } + + public float4 xzxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.x, this.y); + } + + public float4 xzxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.x, this.z); + } + + public float4 xzyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.y, this.x); + } + + public float4 xzyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.y, this.y); + } + + public float4 xzyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.y, this.z); + } + + public float4 xzzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.z, this.x); + } + + public float4 xzzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.z, this.y); + } + + public float4 xzzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.z, this.z); + } + + public float4 yxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.x); + } + + public float4 yxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.y); + } + + public float4 yxxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.z); + } + + public float4 yxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.x); + } + + public float4 yxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.y); + } + + public float4 yxyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.z); + } + + public float4 yxzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.z, this.x); + } + + public float4 yxzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.z, this.y); + } + + public float4 yxzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.z, this.z); + } + + public float4 yyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.x); + } + + public float4 yyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.y); + } + + public float4 yyxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.z); + } + + public float4 yyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.x); + } + + public float4 yyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.y); + } + + public float4 yyyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.z); + } + + public float4 yyzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.z, this.x); + } + + public float4 yyzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.z, this.y); + } + + public float4 yyzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.z, this.z); + } + + public float4 yzxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.x, this.x); + } + + public float4 yzxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.x, this.y); + } + + public float4 yzxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.x, this.z); + } + + public float4 yzyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.y, this.x); + } + + public float4 yzyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.y, this.y); + } + + public float4 yzyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.y, this.z); + } + + public float4 yzzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.z, this.x); + } + + public float4 yzzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.z, this.y); + } + + public float4 yzzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.z, this.z); + } + + public float4 zxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.x, this.x); + } + + public float4 zxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.x, this.y); + } + + public float4 zxxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.x, this.z); + } + + public float4 zxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.y, this.x); + } + + public float4 zxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.y, this.y); + } + + public float4 zxyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.y, this.z); + } + + public float4 zxzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.z, this.x); + } + + public float4 zxzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.z, this.y); + } + + public float4 zxzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.z, this.z); + } + + public float4 zyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.x, this.x); + } + + public float4 zyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.x, this.y); + } + + public float4 zyxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.x, this.z); + } + + public float4 zyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.y, this.x); + } + + public float4 zyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.y, this.y); + } + + public float4 zyyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.y, this.z); + } + + public float4 zyzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.z, this.x); + } + + public float4 zyzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.z, this.y); + } + + public float4 zyzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.z, this.z); + } + + public float4 zzxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.x, this.x); + } + + public float4 zzxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.x, this.y); + } + + public float4 zzxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.x, this.z); + } + + public float4 zzyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.y, this.x); + } + + public float4 zzyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.y, this.y); + } + + public float4 zzyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.y, this.z); + } + + public float4 zzzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.z, this.x); + } + + public float4 zzzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.z, this.y); + } + + public float4 zzzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.z, this.z); + } + + public override readonly string ToString() + { + return $"(x: {this.x}, y: {this.y}, z: {this.z})"; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics/float3.tt b/Misaki.HighPerformance.Mathematics/float3.tt new file mode 100644 index 0000000..6e08136 --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/float3.tt @@ -0,0 +1,140 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ include file="Utilities.ttinclude" #> +<#@ output extension=".gen.cs" #> +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics; + +<# +var dimension = 3; +var components = new[] { "x", "y", "z" }; +var structName = $"float{dimension}"; + +#> +public struct <#= structName #> +{ +<# for(int i = 0; i < dimension; i++) { #> + public float <#= components[i] #>; +<# } #> + + public <#= structName #>(float value) + { +<# for(int i = 0; i < dimension; i++) { #> + this.<#= components[i] #> = value; +<# } #> + } + + public <#= structName #>(<#= string.Join(", ", components.Take(dimension).Select(c => $"float {c}")) #>) + { +<# for(int i = 0; i < dimension; i++) { #> + this.<#= components[i] #> = <#= components[i] #>; +<# } #> + } + +<# foreach (var otherDim in dimensions.Where(d => d != dimension)) +{ + string otherStructName = $"float{otherDim}"; + + if (otherDim < dimension) + { +#> + public <#= structName #>(<#= otherStructName #> value) + { +<# for(int i = 0; i < Math.Min(dimension, otherDim); i++) { #> + this.<#= components[i] #> = value.<#= components[i] #>; +<# + } + // Fill remaining components with 0 + for(int i = otherDim; i < dimension; i++) { +#> + this.<#= components[i] #> = 0.0f; +<# } #> + } + +<# } +else +{ #> + public <#= structName #>(<#= otherStructName #> value) + { +<# + for(int i = 0; i < dimension; i++) { +#> + this.<#= components[i] #> = value.<#= components[i] #>; +<# } #> + } +<# + } +} +#> + + public static <#= structName #> operator +(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() + rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator +(<#= structName #> lhs, float rhs) + { + return lhs + new <#= structName #>(rhs); + } + + public static <#= structName #> operator -(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() - rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator -(<#= structName #> lhs, float rhs) + { + return lhs - new <#= structName #>(rhs); + } + + public static <#= structName #> operator *(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() * rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator *(<#= structName #> lhs, float rhs) + { + return (lhs.AsVector128() * rhs).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator /(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() / rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator /(<#= structName #> lhs, float rhs) + { + return (lhs.AsVector128() / rhs).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator -(<#= structName #> value) + { + return (-value.AsVector128()).AsFloat<#= dimension #>(); + } + +<# +var validComponents = components.Take(dimension).ToArray(); +var swizzles = GenerateSwizzles(validComponents, 4); +foreach (var swizzle in swizzles) +{ + var targetDim = swizzle.Length; + var targetStruct = $"float{targetDim}"; +#> + public <#= targetStruct #> <#= swizzle #> + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new <#= targetStruct #>(<#= string.Join(", ", swizzle.Select(c => $"this.{c}")) #>); + } + +<# +} +#> + public override readonly string ToString() + { + return $"(<#= string.Join(", ", components.Take(dimension).Select(c => $"{c}: {{this.{c}}}")) #>)"; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics/float4.gen.cs b/Misaki.HighPerformance.Mathematics/float4.gen.cs new file mode 100644 index 0000000..3db2b9a --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/float4.gen.cs @@ -0,0 +1,2111 @@ + +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics; + +public struct float4 +{ + public float x; + public float y; + public float z; + public float w; + + public float4(float value) + { + this.x = value; + this.y = value; + this.z = value; + this.w = value; + } + + public float4(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public float4(float2 value) + { + this.x = value.x; + this.y = value.y; + this.z = 0.0f; + this.w = 0.0f; + } + + public float4(float3 value) + { + this.x = value.x; + this.y = value.y; + this.z = value.z; + this.w = 0.0f; + } + + + public static float4 operator +(float4 lhs, float4 rhs) + { + return (lhs.AsVector128() + rhs.AsVector128()).AsFloat4(); + } + + public static float4 operator +(float4 lhs, float rhs) + { + return lhs + new float4(rhs); + } + + public static float4 operator -(float4 lhs, float4 rhs) + { + return (lhs.AsVector128() - rhs.AsVector128()).AsFloat4(); + } + + public static float4 operator -(float4 lhs, float rhs) + { + return lhs - new float4(rhs); + } + + public static float4 operator *(float4 lhs, float4 rhs) + { + return (lhs.AsVector128() * rhs.AsVector128()).AsFloat4(); + } + + public static float4 operator *(float4 lhs, float rhs) + { + return (lhs.AsVector128() * rhs).AsFloat4(); + } + + public static float4 operator /(float4 lhs, float4 rhs) + { + return (lhs.AsVector128() / rhs.AsVector128()).AsFloat4(); + } + + public static float4 operator /(float4 lhs, float rhs) + { + return (lhs.AsVector128() / rhs).AsFloat4(); + } + + public static float4 operator -(float4 value) + { + return (-value.AsVector128()).AsFloat4(); + } + + public float2 xx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.x); + } + + public float2 xy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.y); + } + + public float2 xz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.z); + } + + public float2 xw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.x, this.w); + } + + public float2 yx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.x); + } + + public float2 yy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.y); + } + + public float2 yz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.z); + } + + public float2 yw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.y, this.w); + } + + public float2 zx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.z, this.x); + } + + public float2 zy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.z, this.y); + } + + public float2 zz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.z, this.z); + } + + public float2 zw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.z, this.w); + } + + public float2 wx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.w, this.x); + } + + public float2 wy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.w, this.y); + } + + public float2 wz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.w, this.z); + } + + public float2 ww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float2(this.w, this.w); + } + + public float3 xxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.x); + } + + public float3 xxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.y); + } + + public float3 xxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.z); + } + + public float3 xxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.x, this.w); + } + + public float3 xyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.x); + } + + public float3 xyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.y); + } + + public float3 xyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.z); + } + + public float3 xyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.y, this.w); + } + + public float3 xzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.z, this.x); + } + + public float3 xzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.z, this.y); + } + + public float3 xzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.z, this.z); + } + + public float3 xzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.z, this.w); + } + + public float3 xwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.w, this.x); + } + + public float3 xwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.w, this.y); + } + + public float3 xwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.w, this.z); + } + + public float3 xww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.x, this.w, this.w); + } + + public float3 yxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.x); + } + + public float3 yxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.y); + } + + public float3 yxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.z); + } + + public float3 yxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.x, this.w); + } + + public float3 yyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.x); + } + + public float3 yyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.y); + } + + public float3 yyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.z); + } + + public float3 yyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.y, this.w); + } + + public float3 yzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.z, this.x); + } + + public float3 yzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.z, this.y); + } + + public float3 yzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.z, this.z); + } + + public float3 yzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.z, this.w); + } + + public float3 ywx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.w, this.x); + } + + public float3 ywy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.w, this.y); + } + + public float3 ywz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.w, this.z); + } + + public float3 yww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.y, this.w, this.w); + } + + public float3 zxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.x, this.x); + } + + public float3 zxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.x, this.y); + } + + public float3 zxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.x, this.z); + } + + public float3 zxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.x, this.w); + } + + public float3 zyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.y, this.x); + } + + public float3 zyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.y, this.y); + } + + public float3 zyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.y, this.z); + } + + public float3 zyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.y, this.w); + } + + public float3 zzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.z, this.x); + } + + public float3 zzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.z, this.y); + } + + public float3 zzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.z, this.z); + } + + public float3 zzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.z, this.w); + } + + public float3 zwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.w, this.x); + } + + public float3 zwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.w, this.y); + } + + public float3 zwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.w, this.z); + } + + public float3 zww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.z, this.w, this.w); + } + + public float3 wxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.x, this.x); + } + + public float3 wxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.x, this.y); + } + + public float3 wxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.x, this.z); + } + + public float3 wxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.x, this.w); + } + + public float3 wyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.y, this.x); + } + + public float3 wyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.y, this.y); + } + + public float3 wyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.y, this.z); + } + + public float3 wyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.y, this.w); + } + + public float3 wzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.z, this.x); + } + + public float3 wzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.z, this.y); + } + + public float3 wzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.z, this.z); + } + + public float3 wzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.z, this.w); + } + + public float3 wwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.w, this.x); + } + + public float3 wwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.w, this.y); + } + + public float3 wwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.w, this.z); + } + + public float3 www + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float3(this.w, this.w, this.w); + } + + public float4 xxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.x); + } + + public float4 xxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.y); + } + + public float4 xxxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.z); + } + + public float4 xxxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.x, this.w); + } + + public float4 xxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.x); + } + + public float4 xxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.y); + } + + public float4 xxyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.z); + } + + public float4 xxyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.y, this.w); + } + + public float4 xxzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.z, this.x); + } + + public float4 xxzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.z, this.y); + } + + public float4 xxzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.z, this.z); + } + + public float4 xxzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.z, this.w); + } + + public float4 xxwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.w, this.x); + } + + public float4 xxwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.w, this.y); + } + + public float4 xxwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.w, this.z); + } + + public float4 xxww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.x, this.w, this.w); + } + + public float4 xyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.x); + } + + public float4 xyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.y); + } + + public float4 xyxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.z); + } + + public float4 xyxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.x, this.w); + } + + public float4 xyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.x); + } + + public float4 xyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.y); + } + + public float4 xyyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.z); + } + + public float4 xyyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.y, this.w); + } + + public float4 xyzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.z, this.x); + } + + public float4 xyzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.z, this.y); + } + + public float4 xyzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.z, this.z); + } + + public float4 xyzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.z, this.w); + } + + public float4 xywx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.w, this.x); + } + + public float4 xywy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.w, this.y); + } + + public float4 xywz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.w, this.z); + } + + public float4 xyww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.y, this.w, this.w); + } + + public float4 xzxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.x, this.x); + } + + public float4 xzxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.x, this.y); + } + + public float4 xzxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.x, this.z); + } + + public float4 xzxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.x, this.w); + } + + public float4 xzyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.y, this.x); + } + + public float4 xzyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.y, this.y); + } + + public float4 xzyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.y, this.z); + } + + public float4 xzyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.y, this.w); + } + + public float4 xzzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.z, this.x); + } + + public float4 xzzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.z, this.y); + } + + public float4 xzzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.z, this.z); + } + + public float4 xzzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.z, this.w); + } + + public float4 xzwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.w, this.x); + } + + public float4 xzwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.w, this.y); + } + + public float4 xzwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.w, this.z); + } + + public float4 xzww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.z, this.w, this.w); + } + + public float4 xwxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.x, this.x); + } + + public float4 xwxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.x, this.y); + } + + public float4 xwxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.x, this.z); + } + + public float4 xwxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.x, this.w); + } + + public float4 xwyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.y, this.x); + } + + public float4 xwyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.y, this.y); + } + + public float4 xwyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.y, this.z); + } + + public float4 xwyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.y, this.w); + } + + public float4 xwzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.z, this.x); + } + + public float4 xwzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.z, this.y); + } + + public float4 xwzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.z, this.z); + } + + public float4 xwzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.z, this.w); + } + + public float4 xwwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.w, this.x); + } + + public float4 xwwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.w, this.y); + } + + public float4 xwwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.w, this.z); + } + + public float4 xwww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.x, this.w, this.w, this.w); + } + + public float4 yxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.x); + } + + public float4 yxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.y); + } + + public float4 yxxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.z); + } + + public float4 yxxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.x, this.w); + } + + public float4 yxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.x); + } + + public float4 yxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.y); + } + + public float4 yxyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.z); + } + + public float4 yxyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.y, this.w); + } + + public float4 yxzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.z, this.x); + } + + public float4 yxzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.z, this.y); + } + + public float4 yxzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.z, this.z); + } + + public float4 yxzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.z, this.w); + } + + public float4 yxwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.w, this.x); + } + + public float4 yxwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.w, this.y); + } + + public float4 yxwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.w, this.z); + } + + public float4 yxww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.x, this.w, this.w); + } + + public float4 yyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.x); + } + + public float4 yyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.y); + } + + public float4 yyxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.z); + } + + public float4 yyxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.x, this.w); + } + + public float4 yyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.x); + } + + public float4 yyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.y); + } + + public float4 yyyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.z); + } + + public float4 yyyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.y, this.w); + } + + public float4 yyzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.z, this.x); + } + + public float4 yyzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.z, this.y); + } + + public float4 yyzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.z, this.z); + } + + public float4 yyzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.z, this.w); + } + + public float4 yywx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.w, this.x); + } + + public float4 yywy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.w, this.y); + } + + public float4 yywz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.w, this.z); + } + + public float4 yyww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.y, this.w, this.w); + } + + public float4 yzxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.x, this.x); + } + + public float4 yzxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.x, this.y); + } + + public float4 yzxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.x, this.z); + } + + public float4 yzxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.x, this.w); + } + + public float4 yzyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.y, this.x); + } + + public float4 yzyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.y, this.y); + } + + public float4 yzyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.y, this.z); + } + + public float4 yzyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.y, this.w); + } + + public float4 yzzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.z, this.x); + } + + public float4 yzzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.z, this.y); + } + + public float4 yzzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.z, this.z); + } + + public float4 yzzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.z, this.w); + } + + public float4 yzwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.w, this.x); + } + + public float4 yzwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.w, this.y); + } + + public float4 yzwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.w, this.z); + } + + public float4 yzww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.z, this.w, this.w); + } + + public float4 ywxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.x, this.x); + } + + public float4 ywxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.x, this.y); + } + + public float4 ywxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.x, this.z); + } + + public float4 ywxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.x, this.w); + } + + public float4 ywyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.y, this.x); + } + + public float4 ywyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.y, this.y); + } + + public float4 ywyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.y, this.z); + } + + public float4 ywyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.y, this.w); + } + + public float4 ywzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.z, this.x); + } + + public float4 ywzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.z, this.y); + } + + public float4 ywzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.z, this.z); + } + + public float4 ywzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.z, this.w); + } + + public float4 ywwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.w, this.x); + } + + public float4 ywwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.w, this.y); + } + + public float4 ywwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.w, this.z); + } + + public float4 ywww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.y, this.w, this.w, this.w); + } + + public float4 zxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.x, this.x); + } + + public float4 zxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.x, this.y); + } + + public float4 zxxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.x, this.z); + } + + public float4 zxxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.x, this.w); + } + + public float4 zxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.y, this.x); + } + + public float4 zxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.y, this.y); + } + + public float4 zxyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.y, this.z); + } + + public float4 zxyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.y, this.w); + } + + public float4 zxzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.z, this.x); + } + + public float4 zxzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.z, this.y); + } + + public float4 zxzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.z, this.z); + } + + public float4 zxzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.z, this.w); + } + + public float4 zxwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.w, this.x); + } + + public float4 zxwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.w, this.y); + } + + public float4 zxwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.w, this.z); + } + + public float4 zxww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.x, this.w, this.w); + } + + public float4 zyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.x, this.x); + } + + public float4 zyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.x, this.y); + } + + public float4 zyxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.x, this.z); + } + + public float4 zyxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.x, this.w); + } + + public float4 zyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.y, this.x); + } + + public float4 zyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.y, this.y); + } + + public float4 zyyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.y, this.z); + } + + public float4 zyyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.y, this.w); + } + + public float4 zyzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.z, this.x); + } + + public float4 zyzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.z, this.y); + } + + public float4 zyzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.z, this.z); + } + + public float4 zyzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.z, this.w); + } + + public float4 zywx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.w, this.x); + } + + public float4 zywy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.w, this.y); + } + + public float4 zywz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.w, this.z); + } + + public float4 zyww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.y, this.w, this.w); + } + + public float4 zzxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.x, this.x); + } + + public float4 zzxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.x, this.y); + } + + public float4 zzxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.x, this.z); + } + + public float4 zzxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.x, this.w); + } + + public float4 zzyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.y, this.x); + } + + public float4 zzyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.y, this.y); + } + + public float4 zzyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.y, this.z); + } + + public float4 zzyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.y, this.w); + } + + public float4 zzzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.z, this.x); + } + + public float4 zzzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.z, this.y); + } + + public float4 zzzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.z, this.z); + } + + public float4 zzzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.z, this.w); + } + + public float4 zzwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.w, this.x); + } + + public float4 zzwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.w, this.y); + } + + public float4 zzwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.w, this.z); + } + + public float4 zzww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.z, this.w, this.w); + } + + public float4 zwxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.x, this.x); + } + + public float4 zwxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.x, this.y); + } + + public float4 zwxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.x, this.z); + } + + public float4 zwxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.x, this.w); + } + + public float4 zwyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.y, this.x); + } + + public float4 zwyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.y, this.y); + } + + public float4 zwyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.y, this.z); + } + + public float4 zwyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.y, this.w); + } + + public float4 zwzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.z, this.x); + } + + public float4 zwzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.z, this.y); + } + + public float4 zwzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.z, this.z); + } + + public float4 zwzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.z, this.w); + } + + public float4 zwwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.w, this.x); + } + + public float4 zwwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.w, this.y); + } + + public float4 zwwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.w, this.z); + } + + public float4 zwww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.z, this.w, this.w, this.w); + } + + public float4 wxxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.x, this.x); + } + + public float4 wxxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.x, this.y); + } + + public float4 wxxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.x, this.z); + } + + public float4 wxxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.x, this.w); + } + + public float4 wxyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.y, this.x); + } + + public float4 wxyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.y, this.y); + } + + public float4 wxyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.y, this.z); + } + + public float4 wxyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.y, this.w); + } + + public float4 wxzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.z, this.x); + } + + public float4 wxzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.z, this.y); + } + + public float4 wxzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.z, this.z); + } + + public float4 wxzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.z, this.w); + } + + public float4 wxwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.w, this.x); + } + + public float4 wxwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.w, this.y); + } + + public float4 wxwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.w, this.z); + } + + public float4 wxww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.x, this.w, this.w); + } + + public float4 wyxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.x, this.x); + } + + public float4 wyxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.x, this.y); + } + + public float4 wyxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.x, this.z); + } + + public float4 wyxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.x, this.w); + } + + public float4 wyyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.y, this.x); + } + + public float4 wyyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.y, this.y); + } + + public float4 wyyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.y, this.z); + } + + public float4 wyyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.y, this.w); + } + + public float4 wyzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.z, this.x); + } + + public float4 wyzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.z, this.y); + } + + public float4 wyzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.z, this.z); + } + + public float4 wyzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.z, this.w); + } + + public float4 wywx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.w, this.x); + } + + public float4 wywy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.w, this.y); + } + + public float4 wywz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.w, this.z); + } + + public float4 wyww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.y, this.w, this.w); + } + + public float4 wzxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.x, this.x); + } + + public float4 wzxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.x, this.y); + } + + public float4 wzxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.x, this.z); + } + + public float4 wzxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.x, this.w); + } + + public float4 wzyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.y, this.x); + } + + public float4 wzyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.y, this.y); + } + + public float4 wzyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.y, this.z); + } + + public float4 wzyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.y, this.w); + } + + public float4 wzzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.z, this.x); + } + + public float4 wzzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.z, this.y); + } + + public float4 wzzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.z, this.z); + } + + public float4 wzzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.z, this.w); + } + + public float4 wzwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.w, this.x); + } + + public float4 wzwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.w, this.y); + } + + public float4 wzwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.w, this.z); + } + + public float4 wzww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.z, this.w, this.w); + } + + public float4 wwxx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.x, this.x); + } + + public float4 wwxy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.x, this.y); + } + + public float4 wwxz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.x, this.z); + } + + public float4 wwxw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.x, this.w); + } + + public float4 wwyx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.y, this.x); + } + + public float4 wwyy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.y, this.y); + } + + public float4 wwyz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.y, this.z); + } + + public float4 wwyw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.y, this.w); + } + + public float4 wwzx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.z, this.x); + } + + public float4 wwzy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.z, this.y); + } + + public float4 wwzz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.z, this.z); + } + + public float4 wwzw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.z, this.w); + } + + public float4 wwwx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.w, this.x); + } + + public float4 wwwy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.w, this.y); + } + + public float4 wwwz + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.w, this.z); + } + + public float4 wwww + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new float4(this.w, this.w, this.w, this.w); + } + + public override readonly string ToString() + { + return $"(x: {this.x}, y: {this.y}, z: {this.z}, w: {this.w})"; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics/float4.tt b/Misaki.HighPerformance.Mathematics/float4.tt new file mode 100644 index 0000000..0d76646 --- /dev/null +++ b/Misaki.HighPerformance.Mathematics/float4.tt @@ -0,0 +1,140 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ include file="Utilities.ttinclude" #> +<#@ output extension=".gen.cs" #> +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics; + +<# +var dimension = 4; +var components = new[] { "x", "y", "z", "w" }; +var structName = $"float{dimension}"; + +#> +public struct <#= structName #> +{ +<# for(int i = 0; i < dimension; i++) { #> + public float <#= components[i] #>; +<# } #> + + public <#= structName #>(float value) + { +<# for(int i = 0; i < dimension; i++) { #> + this.<#= components[i] #> = value; +<# } #> + } + + public <#= structName #>(<#= string.Join(", ", components.Take(dimension).Select(c => $"float {c}")) #>) + { +<# for(int i = 0; i < dimension; i++) { #> + this.<#= components[i] #> = <#= components[i] #>; +<# } #> + } + +<# foreach (var otherDim in dimensions.Where(d => d != dimension)) +{ + string otherStructName = $"float{otherDim}"; + + if (otherDim < dimension) + { +#> + public <#= structName #>(<#= otherStructName #> value) + { +<# for(int i = 0; i < Math.Min(dimension, otherDim); i++) { #> + this.<#= components[i] #> = value.<#= components[i] #>; +<# + } + // Fill remaining components with 0 + for(int i = otherDim; i < dimension; i++) { +#> + this.<#= components[i] #> = 0.0f; +<# } #> + } + +<# } +else +{ #> + public <#= structName #>(<#= otherStructName #> value) + { +<# + for(int i = 0; i < dimension; i++) { +#> + this.<#= components[i] #> = value.<#= components[i] #>; +<# } #> + } +<# + } +} +#> + + public static <#= structName #> operator +(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() + rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator +(<#= structName #> lhs, float rhs) + { + return lhs + new <#= structName #>(rhs); + } + + public static <#= structName #> operator -(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() - rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator -(<#= structName #> lhs, float rhs) + { + return lhs - new <#= structName #>(rhs); + } + + public static <#= structName #> operator *(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() * rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator *(<#= structName #> lhs, float rhs) + { + return (lhs.AsVector128() * rhs).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator /(<#= structName #> lhs, <#= structName #> rhs) + { + return (lhs.AsVector128() / rhs.AsVector128()).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator /(<#= structName #> lhs, float rhs) + { + return (lhs.AsVector128() / rhs).AsFloat<#= dimension #>(); + } + + public static <#= structName #> operator -(<#= structName #> value) + { + return (-value.AsVector128()).AsFloat<#= dimension #>(); + } + +<# +var validComponents = components.Take(dimension).ToArray(); +var swizzles = GenerateSwizzles(validComponents, 4); +foreach (var swizzle in swizzles) +{ + var targetDim = swizzle.Length; + var targetStruct = $"float{targetDim}"; +#> + public <#= targetStruct #> <#= swizzle #> + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new <#= targetStruct #>(<#= string.Join(", ", swizzle.Select(c => $"this.{c}")) #>); + } + +<# +} +#> + public override readonly string ToString() + { + return $"(<#= string.Join(", ", components.Take(dimension).Select(c => $"{c}: {{this.{c}}}")) #>)"; + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Test/CollectionBenchmark.cs b/Misaki.HighPerformance.Test/CollectionBenchmark.cs index 442510a..1314e1d 100644 --- a/Misaki.HighPerformance.Test/CollectionBenchmark.cs +++ b/Misaki.HighPerformance.Test/CollectionBenchmark.cs @@ -1,6 +1,6 @@ using BenchmarkDotNet.Attributes; -using Misaki.HighPerformance.Unsafe.Buffer; -using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections; namespace Misaki.HighPerformance.Test; @@ -13,7 +13,6 @@ public unsafe class CollectionBenchmark [GlobalSetup] public void Setup() { - AllocationManager.Initialize(); } [Benchmark] @@ -34,7 +33,8 @@ public unsafe class CollectionBenchmark { array[i] = i; } - AllocationManager.ResetCurrent(); + + AllocationManager.TempAllocator.Reset(); } [Benchmark] diff --git a/Misaki.HighPerformance.Test/FunctionPtrBenchmark.cs b/Misaki.HighPerformance.Test/FunctionPtrBenchmark.cs new file mode 100644 index 0000000..b2b366f --- /dev/null +++ b/Misaki.HighPerformance.Test/FunctionPtrBenchmark.cs @@ -0,0 +1,47 @@ +using BenchmarkDotNet.Attributes; +using Misaki.HighPerformance.LowLevel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.Test; + +[MemoryDiagnoser] +public unsafe class FunctionPtrBenchmark +{ + private delegate float FunctionPointerDelegate(float a, float b); + + private float _sink; + + private FunctionPointer _addManaged; + private delegate* unmanaged _addUnmanaged; + + public FunctionPtrBenchmark() + { + _addManaged = new(Marshal.GetFunctionPointerForDelegate(Add)); + _addUnmanaged = &AddUnmanaged; + } + + private float Add(float a, float b) + { + return a + b; + } + + [UnmanagedCallersOnly] + private static float AddUnmanaged(float a, float b) + { + return a + b; + } + + [Benchmark(Baseline = true)] + [MethodImpl(MethodImplOptions.NoInlining)] + public void InvokeManaged() + { + _sink = _addManaged.Invoke(1.0f, 2.0f); + } + + [Benchmark] + public void InvokeUnmanaged() + { + _sink = _addUnmanaged(1.0f, 2.0f); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Test/HashMapBenchmark.cs b/Misaki.HighPerformance.Test/HashMapBenchmark.cs new file mode 100644 index 0000000..6d31e81 --- /dev/null +++ b/Misaki.HighPerformance.Test/HashMapBenchmark.cs @@ -0,0 +1,114 @@ +using BenchmarkDotNet.Attributes; +using Misaki.HighPerformance.LowLevel.Collections; + +namespace Misaki.HighPerformance.Test; + +public class HashMapBenchmark +{ + private UnsafeHashMap _unsafeHashMap; + private Dictionary _dictionary = null!; + + [Params(10, 100, 1000)] + public int count; + + [IterationSetup] + public void Setup() + { + //_unsafeHashMap = new UnsafeHashMap(count, Allocator.Persistent); + _dictionary = new Dictionary(count); + + for (var i = 0; i < count; i++) + { + //_unsafeHashMap.Add(i, i); + _dictionary.Add(i, i); + } + } + + [Benchmark] + public void UnsafeHashMapAdd() + { + for (var i = 0; i < count; i++) + { + _unsafeHashMap.Add(count + i, i); + } + } + + [Benchmark(Baseline = true)] + public void DictionaryAdd() + { + for (var i = 0; i < count; i++) + { + _dictionary.Add(count + i, i); + } + } + + public void UnsafeHashMapRemove() + { + for (var i = 0; i < count; i++) + { + _unsafeHashMap.Remove(i); + } + } + + public void DictionaryRemove() + { + for (var i = 0; i < count; i++) + { + _dictionary.Remove(i); + } + } + + public void UnsafeHashMapRandomRead() + { + for (var i = 0; i < count; i++) + { + var value = Random.Shared.Next(0, count); + if (_unsafeHashMap.TryGetValue(value, out var result)) + { + var r2 = result + result; + } + } + } + + public void DictionaryRandomRead() + { + for (var i = 0; i < count; i++) + { + var value = Random.Shared.Next(0, count); + if (_dictionary.TryGetValue(value, out var result)) + { + var r2 = result + result; + } + } + } + + public void UnsafeHashMapRandomWrite() + { + for (var i = 0; i < count; i++) + { + var value = Random.Shared.Next(0, count); + if (_unsafeHashMap.TryGetValue(value, out var result)) + { + _unsafeHashMap[value] = result + 1; + } + } + } + + public void DictionaryRandomWrite() + { + for (var i = 0; i < count; i++) + { + var value = Random.Shared.Next(0, count); + if (_dictionary.TryGetValue(value, out var result)) + { + _dictionary[value] = result + 1; + } + } + } + + [IterationCleanup] + public void Cleanup() + { + //_unsafeHashMap.Dispose(); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Test/Jobs/NoiseJob.cs b/Misaki.HighPerformance.Test/Jobs/NoiseJob.cs index e1d9ac5..925319d 100644 --- a/Misaki.HighPerformance.Test/Jobs/NoiseJob.cs +++ b/Misaki.HighPerformance.Test/Jobs/NoiseJob.cs @@ -1,5 +1,5 @@ using Misaki.HighPerformance.Jobs; -using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.LowLevel.Collections; using System.Numerics; using System.Runtime.CompilerServices; diff --git a/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj b/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj index ded352b..456ce7d 100644 --- a/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj +++ b/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj @@ -14,7 +14,10 @@ - + + + + diff --git a/Misaki.HighPerformance.Test/ParallelNoiseBenchmark.cs b/Misaki.HighPerformance.Test/ParallelNoiseBenchmark.cs index 82efad0..b531a94 100644 --- a/Misaki.HighPerformance.Test/ParallelNoiseBenchmark.cs +++ b/Misaki.HighPerformance.Test/ParallelNoiseBenchmark.cs @@ -1,7 +1,7 @@ using BenchmarkDotNet.Attributes; using Misaki.HighPerformance.Jobs; +using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.Test.Jobs; -using Misaki.HighPerformance.Unsafe.Collections; using System.Numerics; namespace Misaki.HighPerformance.Test; diff --git a/Misaki.HighPerformance.Test/Program.cs b/Misaki.HighPerformance.Test/Program.cs index 8ed5009..e459c25 100644 --- a/Misaki.HighPerformance.Test/Program.cs +++ b/Misaki.HighPerformance.Test/Program.cs @@ -1,4 +1,4 @@ using BenchmarkDotNet.Running; using Misaki.HighPerformance.Test; -BenchmarkRunner.Run(); +BenchmarkRunner.Run(); diff --git a/Misaki.HighPerformance.Unsafe/AssemblyInfo.cs b/Misaki.HighPerformance.Unsafe/AssemblyInfo.cs deleted file mode 100644 index a601d3e..0000000 --- a/Misaki.HighPerformance.Unsafe/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -global using static Misaki.HighPerformance.Unsafe.Helpers.MemoryUtilities; -global using SystemUnsfae = System.Runtime.CompilerServices.Unsafe; \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Buffer/AllocationHandler.cs b/Misaki.HighPerformance.Unsafe/Buffer/AllocationHandler.cs deleted file mode 100644 index 7f3fd3a..0000000 --- a/Misaki.HighPerformance.Unsafe/Buffer/AllocationHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Misaki.HighPerformance.Unsafe.Collections; -using Misaki.HighPerformance.Unsafe.Collections.Contracts; -using System.Runtime.InteropServices; - -namespace Misaki.HighPerformance.Unsafe.Buffer; - -[StructLayout(LayoutKind.Sequential)] -public unsafe struct AllocationHandler : IAllocator -{ - public unsafe T* Allocate(uint size, uint alignSize, AllocationOption allocationOption) - where T : unmanaged - { - throw new NotImplementedException(); - } - - public unsafe T* Reallocate(T* buffer, uint size, uint alignSize) - where T : unmanaged - { - throw new NotImplementedException(); - } - - public unsafe void Free(T* buffer, uint size, uint alignSize) - where T : unmanaged - { - throw new NotImplementedException(); - } - - public void Dispose() - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Buffer/AllocationManager.cs b/Misaki.HighPerformance.Unsafe/Buffer/AllocationManager.cs deleted file mode 100644 index 111afa1..0000000 --- a/Misaki.HighPerformance.Unsafe/Buffer/AllocationManager.cs +++ /dev/null @@ -1,207 +0,0 @@ -#define UNSAFE_COLLECTION_CHECK - -using Misaki.HighPerformance.Unsafe.Collections; -using System.Runtime.CompilerServices; -#if UNSAFE_COLLECTION_CHECK -#if DEBUG -using System.Diagnostics; -#endif -#endif - -namespace Misaki.HighPerformance.Unsafe.Buffer; - -// TODO: Custom allocator -public static unsafe class AllocationManager -{ - private const uint _DEFAULT_ARENA_SIZE = 512 * 1024; - - private static ThreadLocal? _threadLocalArena; -#if UNSAFE_COLLECTION_CHECK - private static Dictionary _allocated = null!; -#endif - - /// - /// Initializes the AllocationManager with a specified initial size for the memory arena. - /// - /// The initial size in bytes for the memory arena. - public static void Initialize(uint initialSize = _DEFAULT_ARENA_SIZE) - { - if (initialSize <= 0) - { - return; - } - - _threadLocalArena = new ThreadLocal(() => new DynamicArena(initialSize), true); -#if UNSAFE_COLLECTION_CHECK - _allocated = new Dictionary(32); -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void EnsureInitialized() - { - if (_threadLocalArena == null) - { - throw new InvalidOperationException("The AllocationManager has not been initialized."); - } - } - - public static T* Allocate(uint size, uint alignSize, Allocator allocator, AllocationOption allocationOption) - where T : unmanaged - { - if (allocationOption.HasFlag(AllocationOption.UnTracked)) - { - return (T*)AlignedAlloc(size, alignSize); - } - - EnsureInitialized(); - - T* buffer; - switch (allocator) - { - case Allocator.Temp: - buffer = (T*)_threadLocalArena!.Value.Allocate(size * (uint)sizeof(T), alignSize, allocationOption); - break; - - case Allocator.Persistent: - var allocationSize = size * (nuint)sizeof(T); - buffer = (T*)AlignedAlloc(allocationSize, alignSize); - -#if UNSAFE_COLLECTION_CHECK - _allocated[(IntPtr)buffer] = new MemoryLeakExceptionInfo - { - Size = allocationSize, -#if DEBUG - StackTrace = new StackTrace(true) -#endif - }; -#endif - - if (allocationOption.HasFlag(AllocationOption.Clear)) - { - MemClear(buffer, allocationSize); - } - - break; - - default: - throw new ArgumentOutOfRangeException(nameof(allocator), "Invalid allocator type."); - } - - return buffer; - } - - public static T* Realloc(T* buffer, uint size, uint alignSize, Allocator allocator) - where T : unmanaged - { - EnsureInitialized(); - - T* newBuffer; - switch (allocator) - { - case Allocator.Temp: - newBuffer = (T*)_threadLocalArena!.Value.Allocate(size * (uint)sizeof(T), alignSize, AllocationOption.None); - break; - - case Allocator.Persistent: - var allocationSize = size * (nuint)sizeof(T); - newBuffer = (T*)AlignedRealloc(buffer, allocationSize, alignSize); - -#if UNSAFE_COLLECTION_CHECK - // If the allocation map can not find the old value, it means that it was a untracked allocation - if (_allocated.Remove((IntPtr)buffer)) - { - _allocated[(IntPtr)newBuffer] = new MemoryLeakExceptionInfo - { - Size = allocationSize, -#if DEBUG - StackTrace = new StackTrace(true) -#endif - }; - } -#endif - break; - - default: - throw new ArgumentOutOfRangeException(nameof(allocator), "Invalid allocator type."); - } - - return newBuffer; - } - - public static void Free(void* ptr, Allocator allocator) - { - if (allocator == Allocator.Persistent) - { - AlignedFree(ptr); -#if UNSAFE_COLLECTION_CHECK - _allocated.Remove((IntPtr)ptr); -#endif - } - } - - /// - /// Resets the memory arenas on all of the threads. - /// - public static void ResetAll() - { - if (_threadLocalArena == null) - { - return; - } - - foreach (var arena in _threadLocalArena.Values) - { - arena.Reset(); - } - } - - /// - /// Resets the current thread-local arena. - /// - public static void ResetCurrent() - { - if (_threadLocalArena == null) - { - return; - } - _threadLocalArena.Value.Reset(); - } - - /// - /// Disposes of the AllocationManager, freeing all allocated memory and resources. - /// -#if UNSAFE_COLLECTION_CHECK - /// Thrown if there are still allocated buffers that have not been freed. -#endif - public static void Dispose() - { - if (_threadLocalArena == null) - { - return; - } - - foreach (var arena in _threadLocalArena.Values) - { - arena.Dispose(); - } - - _threadLocalArena.Dispose(); - -#if UNSAFE_COLLECTION_CHECK - nuint unfreeBytes = 0u; - foreach (var pair in _allocated) - { - unfreeBytes += pair.Value.Size; - AlignedFree((void*)pair.Key); - } - - if (unfreeBytes > 0u) - { - throw new MemoryLeakException([.. _allocated.Values]); - } - - _allocated.Clear(); -#endif - } -} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Collections/Contracts/IAllocator.cs b/Misaki.HighPerformance.Unsafe/Collections/Contracts/IAllocator.cs deleted file mode 100644 index df2b22a..0000000 --- a/Misaki.HighPerformance.Unsafe/Collections/Contracts/IAllocator.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Misaki.HighPerformance.Unsafe.Collections.Contracts; - -internal unsafe interface IAllocator : IDisposable -{ - public T* Allocate(uint size, uint alignSize, AllocationOption allocationOption) - where T : unmanaged; - - public T* Reallocate(T* buffer, uint size, uint alignSize) - where T : unmanaged; - - public void Free(T* buffer, uint size, uint alignSize) - where T : unmanaged; -} \ No newline at end of file diff --git a/Misaki.HighPerformance.Unsafe/Misaki.HighPerformance.Unsafe.csproj b/Misaki.HighPerformance.Unsafe/Misaki.HighPerformance.Unsafe.csproj deleted file mode 100644 index c938ceb..0000000 --- a/Misaki.HighPerformance.Unsafe/Misaki.HighPerformance.Unsafe.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net9.0 - enable - enable - True - - - - True - - - - True - - - - - - - - - - - diff --git a/Misaki.HighPerformance.sln b/Misaki.HighPerformance.sln index 1ed6d0a..b1bb61e 100644 --- a/Misaki.HighPerformance.sln +++ b/Misaki.HighPerformance.sln @@ -5,10 +5,16 @@ VisualStudioVersion = 17.14.35821.62 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance", "Misaki.HighPerformance\Misaki.HighPerformance.csproj", "{275B2E80-9B2A-4567-A157-F147A6B28A0F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Unsafe", "Misaki.HighPerformance.Unsafe\Misaki.HighPerformance.Unsafe.csproj", "{0DD1B42E-BA40-4F22-9565-5A3977139B66}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.LowLevel", "Misaki.HighPerformance.LowLevel\Misaki.HighPerformance.LowLevel.csproj", "{0DD1B42E-BA40-4F22-9565-5A3977139B66}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Test", "Misaki.HighPerformance.Test\Misaki.HighPerformance.Test.csproj", "{90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Jobs", "Misaki.HighPerformance.Jobs\Misaki.HighPerformance.Jobs.csproj", "{1E8D7815-8C5A-4799-B573-65D9B4D5D95C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Image", "Misaki.HighPerformance.Image\Misaki.HighPerformance.Image.csproj", "{35E6E7FD-0DC4-4D28-93C1-2D17EF92F535}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Mathematics", "Misaki.HighPerformance.Mathematics\Misaki.HighPerformance.Mathematics.csproj", "{29B2478E-D823-4761-B87F-E249D66E33F3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +33,18 @@ Global {90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Release|Any CPU.Build.0 = Release|Any CPU + {1E8D7815-8C5A-4799-B573-65D9B4D5D95C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E8D7815-8C5A-4799-B573-65D9B4D5D95C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E8D7815-8C5A-4799-B573-65D9B4D5D95C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E8D7815-8C5A-4799-B573-65D9B4D5D95C}.Release|Any CPU.Build.0 = Release|Any CPU + {35E6E7FD-0DC4-4D28-93C1-2D17EF92F535}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35E6E7FD-0DC4-4D28-93C1-2D17EF92F535}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35E6E7FD-0DC4-4D28-93C1-2D17EF92F535}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35E6E7FD-0DC4-4D28-93C1-2D17EF92F535}.Release|Any CPU.Build.0 = Release|Any CPU + {29B2478E-D823-4761-B87F-E249D66E33F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29B2478E-D823-4761-B87F-E249D66E33F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29B2478E-D823-4761-B87F-E249D66E33F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29B2478E-D823-4761-B87F-E249D66E33F3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE