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.
This commit is contained in:
12
Misaki.HighPerformance.Image/AnimatedFrameResult.cs
Normal file
12
Misaki.HighPerformance.Image/AnimatedFrameResult.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Misaki.HighPerformance.Image
|
||||
{
|
||||
#if !STBSHARP_INTERNAL
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class AnimatedFrameResult : ImageResult
|
||||
{
|
||||
public int DelayInMs { get; set; }
|
||||
}
|
||||
}
|
||||
149
Misaki.HighPerformance.Image/AnimatedGifEnumerator.cs
Normal file
149
Misaki.HighPerformance.Image/AnimatedGifEnumerator.cs
Normal file
@@ -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<AnimatedFrameResult>
|
||||
{
|
||||
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<AnimatedFrameResult>
|
||||
{
|
||||
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<AnimatedFrameResult> GetEnumerator()
|
||||
{
|
||||
return new AnimatedGifEnumerator(_input, ColorComponents);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Misaki.HighPerformance.Image/ColorComponents.cs
Normal file
16
Misaki.HighPerformance.Image/ColorComponents.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Misaki.HighPerformance.Image
|
||||
{
|
||||
#if !STBSHARP_INTERNAL
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
enum ColorComponents
|
||||
{
|
||||
Default,
|
||||
R,
|
||||
RA,
|
||||
RGB,
|
||||
RGBA
|
||||
}
|
||||
}
|
||||
40
Misaki.HighPerformance.Image/ImageInfo.cs
Normal file
40
Misaki.HighPerformance.Image/ImageInfo.cs
Normal file
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
98
Misaki.HighPerformance.Image/ImageResult.cs
Normal file
98
Misaki.HighPerformance.Image/ImageResult.cs
Normal file
@@ -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<byte> 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<AnimatedFrameResult> 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;
|
||||
}
|
||||
}
|
||||
89
Misaki.HighPerformance.Image/ImageResultFloat.cs
Normal file
89
Misaki.HighPerformance.Image/ImageResultFloat.cs
Normal file
@@ -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<byte> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Authors>StbImageSharpTeam</Authors>
|
||||
<Product>StbImageSharp</Product>
|
||||
<PackageId>StbImageSharp</PackageId>
|
||||
<TargetFrameworks></TargetFrameworks>
|
||||
<Description>C# port of the stb_image.h</Description>
|
||||
<PackageLicenseUrl>Public Domain</PackageLicenseUrl>
|
||||
<Version>1.0.0</Version>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<WarningLevel>8</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<WarningLevel>8</WarningLevel>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
186
Misaki.HighPerformance.Image/Runtime/CRuntime.cs
Normal file
186
Misaki.HighPerformance.Image/Runtime/CRuntime.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Misaki.HighPerformance.Image/Runtime/MemoryStats.cs
Normal file
27
Misaki.HighPerformance.Image/Runtime/MemoryStats.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Misaki.HighPerformance.Image/Runtime/Utility.cs
Normal file
16
Misaki.HighPerformance.Image/Runtime/Utility.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Misaki.HighPerformance.Image.Runtime
|
||||
{
|
||||
internal class Utility
|
||||
{
|
||||
public static T[][] CreateArray<T>(int d1, int d2)
|
||||
{
|
||||
var result = new T[d1][];
|
||||
for (var i = 0; i < d1; i++)
|
||||
{
|
||||
result[i] = new T[d2];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
494
Misaki.HighPerformance.Image/StbImage.Generated.Bmp.cs
Normal file
494
Misaki.HighPerformance.Image/StbImage.Generated.Bmp.cs
Normal file
@@ -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<byte>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
770
Misaki.HighPerformance.Image/StbImage.Generated.Common.cs
Normal file
770
Misaki.HighPerformance.Image/StbImage.Generated.Common.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
545
Misaki.HighPerformance.Image/StbImage.Generated.Gif.cs
Normal file
545
Misaki.HighPerformance.Image/StbImage.Generated.Gif.cs
Normal file
@@ -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<byte>(256, 4);
|
||||
public int max_x;
|
||||
public int max_y;
|
||||
public byte[][] pal = Utility.CreateArray<byte>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
388
Misaki.HighPerformance.Image/StbImage.Generated.Hdr.cs
Normal file
388
Misaki.HighPerformance.Image/StbImage.Generated.Hdr.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1741
Misaki.HighPerformance.Image/StbImage.Generated.Jpg.cs
Normal file
1741
Misaki.HighPerformance.Image/StbImage.Generated.Jpg.cs
Normal file
File diff suppressed because it is too large
Load Diff
818
Misaki.HighPerformance.Image/StbImage.Generated.Png.cs
Normal file
818
Misaki.HighPerformance.Image/StbImage.Generated.Png.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
312
Misaki.HighPerformance.Image/StbImage.Generated.Psd.cs
Normal file
312
Misaki.HighPerformance.Image/StbImage.Generated.Psd.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
374
Misaki.HighPerformance.Image/StbImage.Generated.Tga.cs
Normal file
374
Misaki.HighPerformance.Image/StbImage.Generated.Tga.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
595
Misaki.HighPerformance.Image/StbImage.Generated.Zlib.cs
Normal file
595
Misaki.HighPerformance.Image/StbImage.Generated.Zlib.cs
Normal file
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
95
Misaki.HighPerformance.Image/StbImage.cs
Normal file
95
Misaki.HighPerformance.Image/StbImage.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
6
Misaki.HighPerformance.Jobs/WorkerThreadPool.cs
Normal file
6
Misaki.HighPerformance.Jobs/WorkerThreadPool.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Misaki.HighPerformance.Jobs;
|
||||
|
||||
internal static class WorkerThreadPool
|
||||
{
|
||||
private static readonly int _workerThreadCount = Environment.ProcessorCount;
|
||||
}
|
||||
2
Misaki.HighPerformance.LowLevel/AssemblyInfo.cs
Normal file
2
Misaki.HighPerformance.LowLevel/AssemblyInfo.cs
Normal file
@@ -0,0 +1,2 @@
|
||||
global using static Misaki.HighPerformance.LowLevel.Helpers.MemoryUtilities;
|
||||
global using SystemUnsfae = System.Runtime.CompilerServices.Unsafe;
|
||||
263
Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs
Normal file
263
Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs
Normal file
@@ -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<void*, void*, void>;
|
||||
|
||||
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<nint, AllocationInfo>? 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<nint, AllocationInfo>(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the AllocationManager, freeing all allocated memory and resources.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Misaki.HighPerformance.Unsafe.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Misaki.HighPerformance.Unsafe.Buffer;
|
||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="size">Specifies the amount of memory to allocate in bytes.</param>
|
||||
/// <param name="alignSize">Defines the alignment requirement for the allocated memory.</param>
|
||||
/// <param name="alignment">Defines the alignment requirement for the allocated memory.</param>
|
||||
/// <param name="allocationOption">The option when allocating memory.</param>
|
||||
/// <returns>A pointer to the allocated memory block or null if the allocation cannot be fulfilled.</returns>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the arena has been disposed.</exception>
|
||||
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;
|
||||
@@ -1,6 +1,6 @@
|
||||
using Misaki.HighPerformance.Unsafe.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Misaki.HighPerformance.Unsafe.Buffer;
|
||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
/// <summary>
|
||||
/// 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<ArenaNode>());
|
||||
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.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the memory block to allocate in bytes.</param>
|
||||
/// <param name="alignSize">Alignment requirement for the memory block.</param>
|
||||
/// <param name="alignment">Alignment requirement for the memory block.</param>
|
||||
/// <returns>Pointer to the allocated memory block.</returns>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the arena has been disposed.</exception>
|
||||
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;
|
||||
926
Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.cs
Normal file
926
Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.cs
Normal file
@@ -0,0 +1,926 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 32 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 32 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString32"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 30));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString32(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString32(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 64 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 64 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString64"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 62));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString64(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString64(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 128 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 128 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString128"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 126));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString128(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString128(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 256 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 256 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString256"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 254));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString256(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString256(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 512 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 512 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString512"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 510));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString512(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString512(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 1024 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 1024 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString1024"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 1022));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString1024(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString1024(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 2048 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 2048 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString2048"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 2046));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString2048(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString2048(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length 4096 bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length 4096 bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString4096"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, 4094));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString4096(ReadOnlySpan<char> 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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString4096(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
129
Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.tt
Normal file
129
Misaki.HighPerformance.LowLevel/Buffer/FixedStackString.tt
Normal file
@@ -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) { #>
|
||||
/// <summary>
|
||||
/// Represents a stack allocated fixed-size UTF-8 encoded string of length <#= i #> bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is designed to hold data on the stack. Every copy of this struct causes a copy of the underlying data.
|
||||
/// If you need a heap allocated fixed-size UTF-8 encoded string of length <#= i #> bytes, consider using <see cref="Misaki.HighPerformance.Unsafe.Buffer.FixedString<#= i #>"/>.
|
||||
/// </remarks>
|
||||
[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<byte>(bufferPtr, <#= i - 2 #>));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FixedStackString<#= i #>(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > <#= i - 2 #>)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>.");
|
||||
}
|
||||
|
||||
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<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedStackString<#= i #>(ReadOnlySpan<byte> 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<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
<# } #>
|
||||
894
Misaki.HighPerformance.LowLevel/Buffer/FixedString.cs
Normal file
894
Misaki.HighPerformance.LowLevel/Buffer/FixedString.cs
Normal file
@@ -0,0 +1,894 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 32 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
||||
public unsafe struct FixedString32 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 30)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString32.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 30));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString32(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 30)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString32.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(30);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 30);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString32(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString32(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString32(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 30)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString32.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(30);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString32(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 64 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 64)]
|
||||
public unsafe struct FixedString64 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 62)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString64.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 62));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString64(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 62)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString64.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(62);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 62);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString64(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString64(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString64(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 62)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString64.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(62);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString64(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 128 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 128)]
|
||||
public unsafe struct FixedString128 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 126)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString128.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 126));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString128(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 126)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString128.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(126);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 126);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString128(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString128(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString128(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 126)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString128.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(126);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString128(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 256 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 256)]
|
||||
public unsafe struct FixedString256 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 254)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString256.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 254));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString256(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 254)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString256.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(254);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 254);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString256(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString256(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString256(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 254)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString256.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(254);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString256(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 512 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 512)]
|
||||
public unsafe struct FixedString512 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 510)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString512.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 510));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString512(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 510)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString512.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(510);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 510);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString512(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString512(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString512(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 510)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString512.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(510);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString512(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 1024 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 1024)]
|
||||
public unsafe struct FixedString1024 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 1022)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString1024.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 1022));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString1024(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 1022)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString1024.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(1022);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 1022);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString1024(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString1024(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString1024(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 1022)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString1024.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(1022);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString1024(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 2048 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 2048)]
|
||||
public unsafe struct FixedString2048 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 2046)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString2048.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 2046));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString2048(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 2046)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString2048.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(2046);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 2046);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString2048(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString2048(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString2048(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 2046)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString2048.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(2046);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString2048(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length 4096 bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 4096)]
|
||||
public unsafe struct FixedString4096 : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > 4094)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString4096.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, 4094));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString4096(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > 4094)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString4096.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(4094);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, 4094);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString4096(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString4096(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString4096(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > 4094)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString4096.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(4094);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString4096(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
125
Misaki.HighPerformance.LowLevel/Buffer/FixedString.tt
Normal file
125
Misaki.HighPerformance.LowLevel/Buffer/FixedString.tt
Normal file
@@ -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) { #>
|
||||
/// <summary>
|
||||
/// Represents a heap allocated fixed-size UTF-8 encoded string of length <#= i #> bytes.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = <#= i #>)]
|
||||
public unsafe struct FixedString<#= i #> : IDisposable
|
||||
{
|
||||
private ushort _length;
|
||||
private byte* _buffer;
|
||||
|
||||
public ushort Length => _length;
|
||||
public string Value
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return Encoding.UTF8.GetString(_buffer, _length);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(value);
|
||||
if (maxBytes > <#= i - 2 #>)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>.");
|
||||
}
|
||||
|
||||
_length = (ushort)Encoding.UTF8.GetBytes(value, new Span<byte>(_buffer, <#= i - 2 #>));
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString<#= i #>(ReadOnlySpan<char> input)
|
||||
{
|
||||
var maxBytes = Encoding.UTF8.GetByteCount(input);
|
||||
if (maxBytes > <#= i - 2 #>)
|
||||
{
|
||||
throw new ArgumentException("Input string is too long to fit in FixedString<#= i #>.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(<#= i - 2 #>);
|
||||
|
||||
fixed (char* inputPtr = input)
|
||||
{
|
||||
var actualByteCount = Encoding.UTF8.GetBytes(inputPtr, input.Length, _buffer, <#= i - 2 #>);
|
||||
_length = (ushort)actualByteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString<#= i #>(string input)
|
||||
: this(input.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString<#= i #>(char* input, ushort length)
|
||||
: this(new Span<char>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
public FixedString<#= i #>(ReadOnlySpan<byte> input)
|
||||
{
|
||||
if (input.Length > <#= i - 2 #>)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is too long to fit in FixedString<#= i #>.");
|
||||
}
|
||||
|
||||
_buffer = (byte*)NativeMemory.Alloc(<#= i - 2 #>);
|
||||
_length = (ushort)input.Length;
|
||||
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
SystemUnsfae.CopyBlockUnaligned(_buffer, inputPtr, _length);
|
||||
}
|
||||
}
|
||||
|
||||
public FixedString<#= i #>(byte* input, ushort length)
|
||||
: this(new ReadOnlySpan<byte>(input, length))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<byte> AsSpan()
|
||||
{
|
||||
return new(_buffer, _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly byte* GetUnsafePointer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
NativeMemory.Free(_buffer);
|
||||
|
||||
_length = 0;
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<# } #>
|
||||
@@ -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<T>.
|
||||
public unsafe static class UnsafeArrayPool
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Misaki.HighPerformance.Unsafe.Collections;
|
||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
[Flags]
|
||||
public enum AllocationOption : byte
|
||||
711
Misaki.HighPerformance.LowLevel/Collections/BitSet.cs
Normal file
711
Misaki.HighPerformance.LowLevel/Collections/BitSet.cs
Normal file
@@ -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<uint>.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically
|
||||
|
||||
/// <summary>
|
||||
/// Determines the required length of an <see cref="BitSet"/> to hold the passed id or bit.
|
||||
/// </summary>
|
||||
/// <param name="id">The id or bit.</param>
|
||||
/// <returns>A size of required <see cref="uint"/>s for the bitset.</returns>
|
||||
public static int RequiredLength(int id)
|
||||
{
|
||||
return (id >> 5) + int.Sign(id & _BIT_SIZE);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds the given length to the next padding size.
|
||||
/// </summary>
|
||||
/// <param name="length">The length to round.</param>
|
||||
/// <returns>The rounded length.</returns>
|
||||
public static int RoundToPadding(int length)
|
||||
{
|
||||
return (length + s_padding - 1) / s_padding * s_padding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bits from the bitset.
|
||||
/// </summary>
|
||||
private uint[] _bits;
|
||||
|
||||
/// <summary>
|
||||
/// The highest bit set.
|
||||
/// </summary>
|
||||
private int _highestBit;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum <see cref="_bits"/>-index current in use.
|
||||
/// </summary>
|
||||
private int _max;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
||||
/// </summary>
|
||||
public BitSet()
|
||||
{
|
||||
_bits = new uint[s_padding];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
||||
/// </summary>
|
||||
public BitSet(int minimalLength)
|
||||
{
|
||||
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
|
||||
var length = RoundToPadding(uints);
|
||||
_bits = new uint[length];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
||||
/// </summary>
|
||||
public BitSet(params Span<uint> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The highest uint index in use inside the <see cref="_bits"/>-array.
|
||||
/// </summary>
|
||||
public int HighestIndex
|
||||
{
|
||||
get => _max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The highest bit set.
|
||||
/// </summary>
|
||||
public int HighestBit
|
||||
{
|
||||
get => _highestBit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the bitset, how many ints it consists of.
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get => _bits.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a bit is set at the index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <returns>True if it is, otherwise false</returns>
|
||||
public bool IsSet(int index)
|
||||
{
|
||||
var b = index >> _INDEX_SIZE;
|
||||
if (b >= _bits.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (_bits[b] & 1 << (index & _BIT_SIZE)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a bit at the given index.
|
||||
/// Resizes its internal array if necessary.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the bit at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
public void ClearBit(int index)
|
||||
{
|
||||
var b = index >> _INDEX_SIZE;
|
||||
if (b >= _bits.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_bits[b] &= ~(1u << (index & _BIT_SIZE));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all bits.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all set bits.
|
||||
/// </summary>
|
||||
public void ClearAll()
|
||||
{
|
||||
Array.Clear(_bits);
|
||||
_highestBit = 0;
|
||||
_max = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the next set bit at or after `startIndex`, or -1 if none.
|
||||
/// </summary>
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if all bits from this instance match those of the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
||||
/// <returns>True if they match, false if not.</returns>
|
||||
[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<uint>(_bits.AsSpan()[i..]);
|
||||
var otherVector = new Vector<uint>(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<uint>(_bits.AsSpan()[i..]);
|
||||
if (!Vector.EqualsAll(vector, Vector<uint>.Zero)) // Vectors are not zero bits[0] != 0 basically
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any bits from this instance match those of the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
||||
/// <returns>True if they match, false if not.</returns>
|
||||
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<uint>(_bits.AsSpan()[i..]);
|
||||
var otherVector = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||
|
||||
var resultVector = Vector.BitwiseAnd(vector, otherVector);
|
||||
if (!Vector.EqualsAll(resultVector, Vector<uint>.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<uint>(_bits.AsSpan()[i..]);
|
||||
if (!Vector.EqualsAll(vector, Vector<uint>.Zero)) // Vectors are not zero bits[0] != 0 basically
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _highestBit <= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if none bits from this instance match those of the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
||||
/// <returns>True if none match, false if not.</returns>
|
||||
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<uint>(_bits.AsSpan()[i..]);
|
||||
var otherVector = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||
|
||||
var resultVector = Vector.BitwiseAnd(vector, otherVector);
|
||||
if (!Vector.EqualsAll(resultVector, Vector<uint>.Zero))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if exactly all bits from this instance match those of the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other <see cref="BitSet"/>.</param>
|
||||
/// <returns>True if they match, false if not.</returns>
|
||||
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<uint>(_bits.AsSpan()[i..]);
|
||||
var otherVector = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||
|
||||
var resultVector = Vector.Xor(vector, otherVector);
|
||||
if (!Vector.EqualsAll(resultVector, Vector<uint>.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<uint>(_bits.AsSpan()[i..]);
|
||||
if (!Vector.EqualsAll(vector, Vector<uint>.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<uint>(left._bits.AsSpan()[i..]);
|
||||
var vectorRight = new Vector<uint>(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<uint>(left._bits.AsSpan()[i..]);
|
||||
var vectorRight = new Vector<uint>(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<uint>(bitSet._bits.AsSpan()[i..]);
|
||||
var resultVector = ~vector;
|
||||
resultVector.CopyTo(bitSet._bits.AsSpan(i, s_padding));
|
||||
}
|
||||
}
|
||||
|
||||
return bitSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Span{T}"/> to access the <see cref="_bits"/>.
|
||||
/// </summary>
|
||||
/// <returns>The hash.</returns>
|
||||
public Span<uint> AsSpan()
|
||||
{
|
||||
var max = _highestBit / (_BIT_SIZE + 1) + 1;
|
||||
return _bits.AsSpan()[..max];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the bits into a <see cref="Span{T}"/> and returns a slice containing the copied <see cref="_bits"/>.
|
||||
/// </summary>
|
||||
/// <param name="span">The <see cref="Span{T}"/> to copy into.</param>
|
||||
/// <param name="zero">If true, it will zero the unused space from the <see cref="span"/>.</param>
|
||||
/// <returns>The <see cref="Span{T}"/>.</returns>
|
||||
public Span<uint> AsSpan(Span<uint> 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];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints the content of this instance.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
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}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SpanBitSet"/> struct
|
||||
/// represents a non resizable collection of bits.
|
||||
/// Used to set, check and clear bits on a allocated <see cref="BitSet"/> or on the stack.
|
||||
/// </summary>
|
||||
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)
|
||||
|
||||
/// <summary>
|
||||
/// The bits from the bitset.
|
||||
/// </summary>
|
||||
private readonly Span<uint> _bits;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitSet" /> class.
|
||||
/// </summary>
|
||||
public SpanBitSet(Span<uint> bits)
|
||||
{
|
||||
_bits = bits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a bit is set at the index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <returns>True if it is, otherwise false</returns>
|
||||
|
||||
public bool IsSet(int index)
|
||||
{
|
||||
var b = index >> _BYTE_SIZE;
|
||||
if (b >= _bits.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (_bits[b] & 1 << (index & _BIT_SIZE)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a bit at the given index.
|
||||
/// Resizes its internal array if necessary.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
|
||||
public void SetBit(int index)
|
||||
{
|
||||
var b = index >> _BYTE_SIZE;
|
||||
if (b >= _bits.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_bits[b] |= 1u << (index & _BIT_SIZE);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the bit at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
|
||||
public void ClearBit(int index)
|
||||
{
|
||||
var b = index >> _BYTE_SIZE;
|
||||
if (b >= _bits.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_bits[b] &= ~(1u << (index & _BIT_SIZE));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
||||
public void SetAll()
|
||||
{
|
||||
var count = _bits.Length;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
_bits[i] = 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all set bits.
|
||||
/// </summary>
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
_bits.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Span{T}"/> to access the <see cref="_bits"/>.
|
||||
/// </summary>
|
||||
/// <returns>The hash.</returns>
|
||||
|
||||
public Span<uint> AsSpan()
|
||||
{
|
||||
return _bits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the bits into a <see cref="Span{T}"/> and returns a slice containing the copied <see cref="_bits"/>.
|
||||
/// </summary>
|
||||
/// <param name=""></param>
|
||||
/// <returns>The hash.</returns>
|
||||
|
||||
public Span<uint> AsSpan(Span<uint> 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];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints the content of this instance.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
|
||||
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)}";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Misaki.HighPerformance.Unsafe.Collections.Contracts;
|
||||
namespace Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
||||
|
||||
public unsafe interface IUnsafeCollection<T> : IEnumerable<T>, IDisposable
|
||||
where T : unmanaged
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
||||
internal class IUnsafeSet<T>
|
||||
where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// A structure for managing an array of unmanaged types with unsafe memory operations.
|
||||
@@ -66,14 +67,22 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
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<T> : IUnsafeCollection<T>
|
||||
/// <summary>
|
||||
/// Constructs an UnsafeArray with a default size of 1 and uses the Persistent allocator.
|
||||
/// </summary>
|
||||
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<T>(), allocationOption);
|
||||
_count = count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of UnsafeArray with a specified number of elements and an allocation type.
|
||||
/// </summary>
|
||||
@@ -100,15 +122,8 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
/// <param name="allocationOption">Determines how the memory should be allocated.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception>
|
||||
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<T>((uint)count, (uint)AlignOf<T>(), allocator, allocationOption);
|
||||
_count = count;
|
||||
_allocator = allocator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -125,7 +140,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
{
|
||||
_buffer = (T*)buffer;
|
||||
_count = count;
|
||||
_allocator = Allocator.External;
|
||||
_handle = (AllocationHandle*)Unsafe.AsPointer(ref AllocationManager.EmptyAllocator.Handle);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -136,7 +151,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
return;
|
||||
}
|
||||
|
||||
_buffer = AllocationManager.Realloc(_buffer, (uint)newSize, (uint)AlignOf<T>(), _allocator);
|
||||
_buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (uint)newSize, (uint)AlignOf<T>());
|
||||
_count = newSize;
|
||||
}
|
||||
|
||||
@@ -162,10 +177,10 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
return;
|
||||
}
|
||||
|
||||
AllocationManager.Free(_buffer, _allocator);
|
||||
_handle->Free(_handle->Allocator, _buffer);
|
||||
|
||||
_handle = null;
|
||||
_buffer = null;
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TKey, TValue> : IUnsafeCollection<KeyValuePair<TKey, TValue>> where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged
|
||||
public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeCollection<KeyValuePair<TKey, TValue>>
|
||||
where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged
|
||||
{
|
||||
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
|
||||
{
|
||||
@@ -92,9 +95,14 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeCollection<KeyValuePai
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => new Enumerator((HashMapHelper<TKey>*)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<TKey>(capacity, sizeof(TValue), HashMapHelper<TKey>.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<TKey>(capacity, sizeof(TValue), HashMapHelper<TKey>.MINIMAL_CAPACITY, allocator, allocationOption);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 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<T> : IUnsafeCollection<T>, IEnumerable<T>
|
||||
public IEnumerator<T> GetEnumerator() => new Enumerator((HashMapHelper<T>*)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<T>(capacity, 0, HashMapHelper<T>.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<T>(capacity, 0, HashMapHelper<T>.MINIMAL_CAPACITY, allocator, allocationOption);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// A collection that allows for unsafe operations on a list of unmanaged types.
|
||||
@@ -118,12 +118,10 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
|
||||
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<T> GetEnumerator() => new Enumerator((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this));
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// A structure that implements a queue using unmanaged types for efficient memory management.
|
||||
@@ -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<T> : IUnsafeCollection<T>
|
||||
where T : unmanaged
|
||||
52
Misaki.HighPerformance.LowLevel/Contracts/IAllocator.cs
Normal file
52
Misaki.HighPerformance.LowLevel/Contracts/IAllocator.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel.Contracts;
|
||||
|
||||
using unsafe AllocFunc = delegate* unmanaged<void*, nuint, nuint, AllocationOption, void*>;
|
||||
using unsafe FreeFunc = delegate* unmanaged<void*, void*, void>;
|
||||
using unsafe ReallocFunc = delegate* unmanaged<void*, void*, nuint, nuint, void*>;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an allocator interface for managing memory allocations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The allocator must be static or pined to a specific memory region.
|
||||
/// </remarks>
|
||||
public unsafe interface IAllocator
|
||||
{
|
||||
public ref AllocationHandle Handle
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -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 nuint Size
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
#if DEBUG
|
||||
public StackTrace StackTrace
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public class MemoryLeakException(params MemoryLeakExceptionInfo[] Infos) : Exception
|
||||
public class MemoryLeakException(params AllocationManager.AllocationInfo[] 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
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Misaki.HighPerformance.LowLevel/FunctionPointer.cs
Normal file
34
Misaki.HighPerformance.LowLevel/FunctionPointer.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel;
|
||||
|
||||
public readonly struct FunctionPointer<T>
|
||||
where T : Delegate
|
||||
{
|
||||
private readonly nint _ptr;
|
||||
private readonly T _delegate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native function pointer associated with this function pointer instance.
|
||||
/// </summary>
|
||||
public readonly nint Pointer => _ptr;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delegate instance associated with the specified function pointer.
|
||||
/// </summary>
|
||||
/// <remarks>This property uses <see
|
||||
/// cref="Marshal.GetDelegateForFunctionPointer{TDelegate}"/> to convert the function
|
||||
/// pointer to a delegate. Ensure that the function pointer is valid and compatible with the delegate type
|
||||
/// <typeparamref name="T"/>.</remarks>
|
||||
public T Invoke => _delegate;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of this function pointer with the following native pointer.
|
||||
/// </summary>
|
||||
/// <param name="ptr"></param>
|
||||
public FunctionPointer(nint ptr)
|
||||
{
|
||||
_ptr = ptr;
|
||||
_delegate = Marshal.GetDelegateForFunctionPointer<T>(ptr);
|
||||
}
|
||||
}
|
||||
@@ -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<TKey> : IDisposable
|
||||
where TKey : unmanaged, IEquatable<TKey>
|
||||
@@ -75,8 +75,7 @@ public unsafe struct HashMapHelper<TKey> : 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<TKey> : 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<TKey> : 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<TKey> : 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<TKey> : 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<TKey>() : AlignOf<int>();
|
||||
|
||||
_buffer = AllocationManager.Allocate<byte>((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<TKey> : 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<TKey> : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
Free(oldBuffer);
|
||||
_handle->Free(_handle->Allocator, oldBuffer);
|
||||
}
|
||||
|
||||
internal void Resize(int newCapacity)
|
||||
@@ -240,7 +239,7 @@ public unsafe struct HashMapHelper<TKey> : 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<TKey> : IDisposable
|
||||
if ((uint)entryIdx < (uint)_capacity)
|
||||
{
|
||||
var nextPtrs = _next;
|
||||
var test = UnsafeUtilities.ReadArrayElement<TKey>(_keys, entryIdx);
|
||||
var test2 = UnsafeUtilities.ReadArrayElement<TKey>(_next, 0);
|
||||
while (!UnsafeUtilities.ReadArrayElement<TKey>(_keys, entryIdx).Equals(key))
|
||||
{
|
||||
entryIdx = nextPtrs[entryIdx];
|
||||
@@ -273,7 +270,8 @@ public unsafe struct HashMapHelper<TKey> : 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<TKey> : 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<TKey> : 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<TKey> : IDisposable
|
||||
{
|
||||
if (UnsafeUtilities.ReadArrayElement<TKey>(_keys, entryIdx).Equals(key))
|
||||
{
|
||||
++removed;
|
||||
removed++;
|
||||
|
||||
// Found matching element, remove it
|
||||
if (prevEntry < 0)
|
||||
@@ -363,7 +361,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
||||
return 0 != removed ? removed : -1;
|
||||
}
|
||||
|
||||
public bool TryGetValue<TValue>(TKey key, out TValue item)
|
||||
public bool TryGetValue<TValue>(in TKey key, out TValue item)
|
||||
where TValue : unmanaged
|
||||
{
|
||||
var idx = Find(key);
|
||||
@@ -486,7 +484,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
||||
{
|
||||
if (IsCreated)
|
||||
{
|
||||
AllocationManager.Free(_buffer, _allocator);
|
||||
_handle->Free(_handle->Allocator, _buffer);
|
||||
|
||||
_buffer = null;
|
||||
_keys = null;
|
||||
352
Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.Byte.cs
Normal file
352
Misaki.HighPerformance.LowLevel/Helpers/MemoryUtilities.Byte.cs
Normal file
@@ -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<byte> LoadVector128(ref byte start, nuint offset)
|
||||
=> Unsafe.ReadUnaligned<Vector128<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector256<byte> LoadVector256(ref byte start, nuint offset)
|
||||
=> Unsafe.ReadUnaligned<Vector256<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static nuint GetByteVector128SpanLength(nuint offset, int length)
|
||||
=> (uint)((length - (int)offset) & ~(Vector128<byte>.Count - 1));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static nuint GetByteVector256SpanLength(nuint offset, int length)
|
||||
=> (uint)((length - (int)offset) & ~(Vector256<byte>.Count - 1));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static nuint GetByteVector512SpanLength(nuint offset, int length)
|
||||
=> (uint)((length - (int)offset) & ~(Vector512<byte>.Count - 1));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe nuint UnalignedCountVector128(byte* searchSpace)
|
||||
{
|
||||
var unaligned = (nint)searchSpace & (Vector128<byte>.Count - 1);
|
||||
return (uint)((Vector128<byte>.Count - unaligned) & (Vector128<byte>.Count - 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for the first occurrence of a null byte (0x00) in a given byte array.
|
||||
/// </summary>
|
||||
/// <param name="searchSpace">A pointer to the byte array where the search will be performed.</param>
|
||||
/// <returns>Returns the index of the first null byte found in the array..</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the byte array is not null-terminated.</exception>"
|
||||
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<byte>.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<byte>.Zero, search).ExtractMostSignificantBits();
|
||||
if (matches == 0)
|
||||
{
|
||||
// Zero flags set so no matches
|
||||
offset += (nuint)Vector128<byte>.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<byte>.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<byte>.Zero, search).ExtractMostSignificantBits();
|
||||
if (matches == 0)
|
||||
{
|
||||
// Zero flags set so no matches
|
||||
offset += (nuint)Vector256<byte>.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<byte>.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<byte>.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<byte>.Zero, search).ExtractMostSignificantBits();
|
||||
if (matches == 0)
|
||||
{
|
||||
// Zero flags set so no matches
|
||||
offset += (nuint)Vector256<byte>.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<byte>.Zero, search).ExtractMostSignificantBits();
|
||||
if (matches == 0)
|
||||
{
|
||||
// Zero flags set so no matches
|
||||
offset += (nuint)Vector128<byte>.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<byte>.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<byte>.Zero, search).ExtractMostSignificantBits();
|
||||
if (matches == 0)
|
||||
{
|
||||
// Zero flags set so no matches
|
||||
offset += (nuint)Vector128<byte>.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<byte>.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<byte>.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<byte>.Zero, search).ExtractMostSignificantBits();
|
||||
if (matches == 0)
|
||||
{
|
||||
// Zero flags set so no matches
|
||||
offset += (nuint)Vector128<byte>.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<byte>.Zero, search);
|
||||
if (compareResult == Vector128<byte>.Zero)
|
||||
{
|
||||
// Zero flags set so no matches
|
||||
offset += (nuint)Vector128<byte>.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);
|
||||
}
|
||||
}
|
||||
@@ -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<T>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 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)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an UnsafeCollection of unmanaged types into a standard collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Represents a type that is unmanaged, allowing for direct memory manipulation.</typeparam>
|
||||
/// <param name="source">The UnsafeCollection instance that contains the data to be converted.</param>
|
||||
/// <returns>A new collection containing the elements from the UnsafeCollection.</returns>
|
||||
public static T[] ToArray<T>(this IUnsafeCollection<T> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an unmanaged collection into a list by copying its elements into a new list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Represents a type that is unmanaged, allowing for direct memory manipulation.</typeparam>
|
||||
/// <param name="source">The collection from which elements are copied to create the new list.</param>
|
||||
/// <returns>A list containing the elements from the specified unmanaged collection.</returns>
|
||||
public static List<T> ToList<T>(this IUnsafeCollection<T> source)
|
||||
where T : unmanaged
|
||||
{
|
||||
var list = new List<T>(source.Count);
|
||||
fixed (T* ptr = list.ToArray())
|
||||
{
|
||||
SystemUnsfae.CopyBlock(ptr, source.GetUnsafePtr(), (uint)(source.Count * sizeof(T)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an UnsafeCollection into a Span for efficient memory access.
|
||||
/// </summary>
|
||||
@@ -145,38 +112,27 @@ public unsafe static class UnsafeCollectionExtensions
|
||||
return new(source.GetUnsafePtr(), source.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the index of a specified value in a collection. Returns -1 if the value is not found.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the collection, which must support equality comparison.</typeparam>
|
||||
/// <param name="source">The collection to search for the specified value.</param>
|
||||
/// <param name="value">The value to locate within the collection.</param>
|
||||
/// <param name="index">Outputs the index of the found value or -1 if not found.</param>
|
||||
public static void IndexOf<T>(this IUnsafeCollection<T> source, T value, out int index)
|
||||
where T : unmanaged, IEquatable<T>
|
||||
public static UnsafeArray<T> ToUnsafeArray<T>(this T[] source, Allocator allocator)
|
||||
where T : unmanaged
|
||||
{
|
||||
for (var i = 0; i < source.Count; i++)
|
||||
var array = new UnsafeArray<T>(source.Length, allocator);
|
||||
fixed (T* ptr = source)
|
||||
{
|
||||
if (UnsafeUtilities.ReadArrayElement<T>(source.GetUnsafePtr(), i).Equals(value))
|
||||
{
|
||||
index = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
index = -1;
|
||||
MemCpy(array.GetUnsafePtr(), ptr, (uint)(source.Length * sizeof(T)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a specified value exists within an unsafe collection of unmanaged types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Represents a type that is unmanaged and supports equality comparison.</typeparam>
|
||||
/// <param name="source">The collection being searched for the specified value.</param>
|
||||
/// <param name="value">The value being searched for within the collection.</param>
|
||||
/// <returns>Returns true if the value is found; otherwise, returns false.</returns>
|
||||
public static bool Conations<T>(this IUnsafeCollection<T> source, T value)
|
||||
where T : unmanaged, IEquatable<T>
|
||||
return array;
|
||||
}
|
||||
|
||||
public static UnsafeList<T> ToUnsafeList<T>(this List<T> source, Allocator allocator)
|
||||
where T : unmanaged
|
||||
{
|
||||
source.IndexOf(value, out var index);
|
||||
return index != -1;
|
||||
var list = new UnsafeList<T>(source.Count, allocator);
|
||||
fixed (T* ptr = CollectionsMarshal.AsSpan(source))
|
||||
{
|
||||
MemCpy(list.GetUnsafePtr(), ptr, (uint)(source.Count * sizeof(T)));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -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<T>(void* ptr, int index) where T : unmanaged
|
||||
{
|
||||
return (T*)((byte*)ptr + (index * sizeof(T)));
|
||||
return (T*)((byte*)ptr + index * sizeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -0,0 +1,50 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Buffer\FixedString.tt">
|
||||
<LastGenOutput>FixedString.cs</LastGenOutput>
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
</None>
|
||||
<None Update="Buffer\FixedStackString.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>FixedStackString.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Buffer\FixedString.cs">
|
||||
<DependentUpon>FixedString.tt</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="Buffer\FixedStackString.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>FixedStackString.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="float4.tt">
|
||||
<LastGenOutput>float4.gen.cs</LastGenOutput>
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
</None>
|
||||
<None Update="float3.tt">
|
||||
<LastGenOutput>float3.gen.cs</LastGenOutput>
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
</None>
|
||||
<None Update="float2.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>float2.gen.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Float.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Float.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="float2.gen.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>float2.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="float3.gen.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>float3.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="float4.gen.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>float4.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
29
Misaki.HighPerformance.Mathematics/Utilities.ttinclude
Normal file
29
Misaki.HighPerformance.Mathematics/Utilities.ttinclude
Normal file
@@ -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<string> GenerateSwizzles(string[] pool, int maxLen)
|
||||
{
|
||||
IEnumerable<string> 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));
|
||||
}
|
||||
#>
|
||||
49
Misaki.HighPerformance.Mathematics/Vectorlize.cs
Normal file
49
Misaki.HighPerformance.Mathematics/Vectorlize.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace Misaki.HighPerformance.Mathematics;
|
||||
|
||||
internal static class Vectorize
|
||||
{
|
||||
public static Vector128<float> AsVector128(this float2 value)
|
||||
{
|
||||
Unsafe.SkipInit(out Vector128<float> result);
|
||||
Unsafe.WriteUnaligned(ref Unsafe.As<Vector128<float>, byte>(ref result), value);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Vector128<float> AsVector128(this float3 value)
|
||||
{
|
||||
Unsafe.SkipInit(out Vector128<float> result);
|
||||
Unsafe.WriteUnaligned(ref Unsafe.As<Vector128<float>, byte>(ref result), value);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Vector128<float> AsVector128(this float4 value)
|
||||
{
|
||||
Unsafe.SkipInit(out Vector128<float> result);
|
||||
Unsafe.WriteUnaligned(ref Unsafe.As<Vector128<float>, byte>(ref result), value);
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float2 AsFloat2(this Vector128<float> value)
|
||||
{
|
||||
ref var address = ref Unsafe.As<Vector128<float>, byte>(ref value);
|
||||
return Unsafe.ReadUnaligned<float2>(ref address);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float3 AsFloat3(this Vector128<float> value)
|
||||
{
|
||||
ref var address = ref Unsafe.As<Vector128<float>, byte>(ref value);
|
||||
return Unsafe.ReadUnaligned<float3>(ref address);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 AsFloat4(this Vector128<float> value)
|
||||
{
|
||||
ref var address = ref Unsafe.As<Vector128<float>, byte>(ref value);
|
||||
return Unsafe.ReadUnaligned<float4>(ref address);
|
||||
}
|
||||
}
|
||||
272
Misaki.HighPerformance.Mathematics/float2.gen.cs
Normal file
272
Misaki.HighPerformance.Mathematics/float2.gen.cs
Normal file
@@ -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})";
|
||||
}
|
||||
}
|
||||
161
Misaki.HighPerformance.Mathematics/float2.tt
Normal file
161
Misaki.HighPerformance.Mathematics/float2.tt
Normal file
@@ -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}}}")) #>)";
|
||||
}
|
||||
}
|
||||
791
Misaki.HighPerformance.Mathematics/float3.gen.cs
Normal file
791
Misaki.HighPerformance.Mathematics/float3.gen.cs
Normal file
@@ -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})";
|
||||
}
|
||||
}
|
||||
140
Misaki.HighPerformance.Mathematics/float3.tt
Normal file
140
Misaki.HighPerformance.Mathematics/float3.tt
Normal file
@@ -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}}}")) #>)";
|
||||
}
|
||||
}
|
||||
2111
Misaki.HighPerformance.Mathematics/float4.gen.cs
Normal file
2111
Misaki.HighPerformance.Mathematics/float4.gen.cs
Normal file
File diff suppressed because it is too large
Load Diff
140
Misaki.HighPerformance.Mathematics/float4.tt
Normal file
140
Misaki.HighPerformance.Mathematics/float4.tt
Normal file
@@ -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}}}")) #>)";
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
47
Misaki.HighPerformance.Test/FunctionPtrBenchmark.cs
Normal file
47
Misaki.HighPerformance.Test/FunctionPtrBenchmark.cs
Normal file
@@ -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<FunctionPointerDelegate> _addManaged;
|
||||
private delegate* unmanaged<float, float, float> _addUnmanaged;
|
||||
|
||||
public FunctionPtrBenchmark()
|
||||
{
|
||||
_addManaged = new(Marshal.GetFunctionPointerForDelegate<FunctionPointerDelegate>(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);
|
||||
}
|
||||
}
|
||||
114
Misaki.HighPerformance.Test/HashMapBenchmark.cs
Normal file
114
Misaki.HighPerformance.Test/HashMapBenchmark.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Misaki.HighPerformance.Test;
|
||||
|
||||
public class HashMapBenchmark
|
||||
{
|
||||
private UnsafeHashMap<int, float> _unsafeHashMap;
|
||||
private Dictionary<int, float> _dictionary = null!;
|
||||
|
||||
[Params(10, 100, 1000)]
|
||||
public int count;
|
||||
|
||||
[IterationSetup]
|
||||
public void Setup()
|
||||
{
|
||||
//_unsafeHashMap = new UnsafeHashMap<int, float>(count, Allocator.Persistent);
|
||||
_dictionary = new Dictionary<int, float>(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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Misaki.HighPerformance.Unsafe\Misaki.HighPerformance.Unsafe.csproj" />
|
||||
<ProjectReference Include="..\Misaki.HighPerformance.Image\Misaki.HighPerformance.Image.csproj" />
|
||||
<ProjectReference Include="..\Misaki.HighPerformance.Jobs\Misaki.HighPerformance.Jobs.csproj" />
|
||||
<ProjectReference Include="..\Misaki.HighPerformance.LowLevel\Misaki.HighPerformance.LowLevel.csproj" />
|
||||
<ProjectReference Include="..\Misaki.HighPerformance.Mathematics\Misaki.HighPerformance.Mathematics.csproj" />
|
||||
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
using Misaki.HighPerformance.Test;
|
||||
|
||||
BenchmarkRunner.Run<CollectionBenchmark>();
|
||||
BenchmarkRunner.Run<FunctionPtrBenchmark>();
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
global using static Misaki.HighPerformance.Unsafe.Helpers.MemoryUtilities;
|
||||
global using SystemUnsfae = System.Runtime.CompilerServices.Unsafe;
|
||||
@@ -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<T>(uint size, uint alignSize, AllocationOption allocationOption)
|
||||
where T : unmanaged
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public unsafe T* Reallocate<T>(T* buffer, uint size, uint alignSize)
|
||||
where T : unmanaged
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public unsafe void Free<T>(T* buffer, uint size, uint alignSize)
|
||||
where T : unmanaged
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -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<DynamicArena>? _threadLocalArena;
|
||||
#if UNSAFE_COLLECTION_CHECK
|
||||
private static Dictionary<IntPtr, MemoryLeakExceptionInfo> _allocated = null!;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the AllocationManager with a specified initial size for the memory arena.
|
||||
/// </summary>
|
||||
/// <param name="initialSize">The initial size in bytes for the memory arena.</param>
|
||||
public static void Initialize(uint initialSize = _DEFAULT_ARENA_SIZE)
|
||||
{
|
||||
if (initialSize <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_threadLocalArena = new ThreadLocal<DynamicArena>(() => new DynamicArena(initialSize), true);
|
||||
#if UNSAFE_COLLECTION_CHECK
|
||||
_allocated = new Dictionary<nint, MemoryLeakExceptionInfo>(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<T>(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>(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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the memory arenas on all of the threads.
|
||||
/// </summary>
|
||||
public static void ResetAll()
|
||||
{
|
||||
if (_threadLocalArena == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var arena in _threadLocalArena.Values)
|
||||
{
|
||||
arena.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the current thread-local arena.
|
||||
/// </summary>
|
||||
public static void ResetCurrent()
|
||||
{
|
||||
if (_threadLocalArena == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_threadLocalArena.Value.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the AllocationManager, freeing all allocated memory and resources.
|
||||
/// </summary>
|
||||
#if UNSAFE_COLLECTION_CHECK
|
||||
/// <exception cref="MemoryLeakException">Thrown if there are still allocated buffers that have not been freed.</exception>
|
||||
#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
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Misaki.HighPerformance.Unsafe.Collections.Contracts;
|
||||
|
||||
internal unsafe interface IAllocator : IDisposable
|
||||
{
|
||||
public T* Allocate<T>(uint size, uint alignSize, AllocationOption allocationOption)
|
||||
where T : unmanaged;
|
||||
|
||||
public T* Reallocate<T>(T* buffer, uint size, uint alignSize)
|
||||
where T : unmanaged;
|
||||
|
||||
public void Free<T>(T* buffer, uint size, uint alignSize)
|
||||
where T : unmanaged;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Collections\Allocator\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user