diff --git a/.cr/team/Editor/Formatting/Braces.CSharp.xml b/.cr/team/Editor/Formatting/Braces.CSharp.xml new file mode 100644 index 00000000..1c194ef8 --- /dev/null +++ b/.cr/team/Editor/Formatting/Braces.CSharp.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md index 1e106800..6900daee 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,16 +1,18 @@ -# StgSharp +# Nitload -![StgSharp Logo](STG%23LOGO.png) +![Nitload Logo](STG%23LOGO.png) -StgSharp is an experimental .NET framework for graphics, numerical computing, language analysis, and other performance-sensitive applications. It began as the foundation of a next-generation STG (shoot 'em up) engine, but the current repository focuses on reusable low-level infrastructure rather than a complete, ready-to-use game engine. +Nitload (formerly StgSharp) is an experimental .NET framework for graphics, numerical computing, language analysis, and other performance-sensitive applications. It began as the foundation of a next-generation STG (shoot 'em up) engine, but the current repository focuses on reusable low-level infrastructure rather than a complete, ready-to-use game engine. + +The managed libraries now use the `Nitload.*` namespace and project prefix. Repository URLs, the logo filename, and some legacy test artifacts still retain the StgSharp name. ## Overview -The framework combines managed C# APIs with a C native library. Its current work includes OpenGL bindings and rendering infrastructure, generic matrix and vector types, SIMD-accelerated native kernels, custom allocators and collections, regular-language analysis, and state-oriented UI infrastructure. StgSharp targets .NET 8; platform and architecture coverage varies by module, with Windows and Linux as the principal native targets. +The framework combines managed C# APIs with a C native library. Its current work includes OpenGL bindings and rendering infrastructure, generic matrix and vector types, SIMD-accelerated native kernels, custom allocators and collections, regular-language analysis, and state-oriented UI infrastructure. Nitload targets .NET 8; platform and architecture coverage varies by module, with Windows and Linux as the principal native targets. ## Core Modules -### StgSharp.Common +### Nitload.Common The foundational library containing shared functionality: @@ -20,7 +22,7 @@ The foundational library containing shared functionality: - **Pipeline and State**: Processing pipelines and reusable state-machine primitives - **Environment**: Platform abstraction and system integration -### StgSharp.Mathematics (Numeric) +### Nitload.Mathematics (Numeric) Numerics and linear algebra built on top of the core library: @@ -28,7 +30,7 @@ Numerics and linear algebra built on top of the core library: - **Numeric Utilities**: Core numeric helpers and algorithms - **SIMD-Friendly Kernels**: Data layouts optimized for hardware acceleration -### StgSharp.RegularAnalysis +### Nitload.RegularAnalysis Nitload General Regular Analysis (NGRA) infrastructure for recognizing and processing regular languages: @@ -36,9 +38,9 @@ Nitload General Regular Analysis (NGRA) infrastructure for recognizing and proce - **Parsing Primitives**: Reusable tokenizer and grammar building blocks - **Analysis Pipeline**: Structured processing for language-like inputs -NGRA is a foundation for higher-level language tooling. It does not replace `StgSharp.Script`; the planned Script implementation depends on NGRA and has not started yet. The source currently under `StgSharp.Script` is retained from the earlier EXPRESS implementation and is not part of the active core solution. +NGRA is a foundation for higher-level language tooling. It does not replace `Nitload.Script`; the planned Script implementation depends on NGRA and has not started yet. The source currently under `Nitload.Script` is retained from the earlier EXPRESS implementation and is not part of the active core solution. -### StgSharp.Graphics +### Nitload.Graphics Managed graphics infrastructure built on Common and Mathematics: @@ -47,7 +49,7 @@ Managed graphics infrastructure built on Common and Mathematics: - **Shader Support**: Shader compilation, program management, and shader-generation utilities - **Window Integration**: GLFW-based window and input access -### StgSharp.Native +### Nitload.Native Native library integration: @@ -56,7 +58,7 @@ Native library integration: - **Matrix Kernels**: Optimized matrix computation routines - **Platform Runtime**: Native thread and platform support for Windows and Linux -### StgSharp.TerminalDialogue +### Nitload.TerminalDialogue Terminal-based user interface application and components: @@ -64,7 +66,7 @@ Terminal-based user interface application and components: - **Dialogue System**: User interaction and input handling - **Terminal Graphics**: Text-based UI rendering -> Note: `StgSharp.Script` is planned as a consumer of NGRA and has not started yet. Its existing EXPRESS sources belong to the earlier implementation. `StgSharp.Model` is currently an empty placeholder while its design is being reconsidered. +> Note: `Nitload.Script` is planned as a consumer of NGRA and has not started yet. Its existing EXPRESS sources belong to the earlier implementation. `Nitload.Model` is currently an empty placeholder while its design is being reconsidered. ## Key Features @@ -113,7 +115,7 @@ The benchmark reports normalized time per allocation/free operation. Each iterat ### Data Processing - **NGRA**: Regex analysis, intermediate representation, optimization, and source generation -- **EXPRESS (legacy/inactive)**: The repository retains an earlier experimental EXPRESS parser/compiler under `StgSharp.Script` +- **EXPRESS (legacy/inactive)**: The repository retains an earlier experimental EXPRESS parser/compiler under `Nitload.Script` - **Script (planned)**: A future language layer intended to build on NGRA; development has not started ## Requirements @@ -125,7 +127,7 @@ The benchmark reports normalized time per allocation/free operation. Each iterat ## Installation -StgSharp is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code. +Nitload is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code. ### Prerequisites @@ -159,7 +161,7 @@ StgSharp is currently in development and requires compilation from source. **NuG dotnet build Nitload.sln ``` - Managed builds copy an existing native binary into their output directory. They do not invoke CMake automatically, so build `StgSharp.Native` first when native functionality is required. + Managed builds copy an existing native binary into their output directory. They do not invoke CMake automatically, so build `Nitload.Native` first when native functionality is required. ### Build Configuration @@ -168,7 +170,7 @@ StgSharp is currently in development and requires compilation from source. **NuG ### Notes -- CMake is the maintained build path for `StgSharp.Native`; the checked-in presets primarily target Clang +- CMake is the maintained build path for `Nitload.Native`; the checked-in presets primarily target Clang - The Visual Studio C project may be opened with MSVC tooling, but MSVC IntelliSense behavior and MSVC-produced binaries are not maintained or guaranteed - Windows and Linux platform implementations exist; actual feature and architecture coverage may differ - x86-64 currently has the most complete optimized kernel coverage @@ -187,8 +189,8 @@ StgSharp is currently in development and requires compilation from source. **NuG ### Matrix Operations ```csharp -using StgSharp.HighPerformance.Memory; -using StgSharp.Mathematics.Numeric; +using Nitload.Common.HighPerformance.Memory; +using Nitload.Mathematics.Numeric; // Matrix storage is allocated explicitly from a native-memory arena. using var allocator = new TwoLayerSegregatedFitAllocator(64 * 1024 * 1024); @@ -207,7 +209,7 @@ General matrix arithmetic does not currently provide operator overloads. Because ### Memory Management ```csharp -using StgSharp.HighPerformance.Memory; +using Nitload.Common.HighPerformance.Memory; // Create a 64 MiB TLSF arena using var allocator = new TwoLayerSegregatedFitAllocator(64 * 1024 * 1024); @@ -231,7 +233,7 @@ allocator.Free(handle); ## Contributing -StgSharp is developed by Nitload Space. Contributions are welcome, particularly in: +Nitload is developed by Nitload Space. Contributions are welcome, particularly in: - Performance optimizations - Cross-platform compatibility @@ -240,7 +242,7 @@ StgSharp is developed by Nitload Space. Contributions are welcome, particularly ## License -StgSharp is licensed under the MIT License. See [LICENSE](LICENSE) for details. +Nitload is licensed under the MIT License. See [LICENSE](LICENSE) for details. ## Future Plans diff --git a/src/Nitload.Graphics/Camera.cs b/src/Nitload.Graphics/Camera.cs index 8af8bb60..6a387a7d 100644 --- a/src/Nitload.Graphics/Camera.cs +++ b/src/Nitload.Graphics/Camera.cs @@ -15,11 +15,11 @@ namespace Nitload.Mathematics.Numeric.Graphics { private bool _isLookAtAvailable; - internal GraphicsMatrix _lookAt; - internal GraphicsMatrix _projection; - internal GraphicsMatrix cameraAtt; - internal GraphicsMatrix rotation; - internal GraphicsMatrix rotationAtt; + internal GMatrix44 _lookAt; + internal GMatrix44 _projection; + internal GMatrix44 cameraAtt; + internal GMatrix44 rotation; + internal GMatrix44 rotationAtt; internal Radius _pitch; internal Radius _row; internal Radius _yaw; @@ -27,13 +27,13 @@ namespace Nitload.Mathematics.Numeric.Graphics public Camera() { - cameraAtt = GraphicsMatrix.Unit; + cameraAtt = GMatrix44.Unit; _target = Vec3.Zero; up = Vec3.Zero; _isLookAtAvailable = false; - _lookAt = GraphicsMatrix.Unit; - rotationAtt = GraphicsMatrix.Unit; - _projection = new GraphicsMatrix(); + _lookAt = GMatrix44.Unit; + rotationAtt = GMatrix44.Unit; + _projection = new GMatrix44(); } public Camera( @@ -43,32 +43,32 @@ namespace Nitload.Mathematics.Numeric.Graphics ) : this() { - rotationAtt = GraphicsMatrix.Unit; - cameraAtt = new GraphicsMatrix(); + rotationAtt = GMatrix44.Unit; + cameraAtt = new GMatrix44(); SetViewDirection(position, target, up); _pitch = Radius.Zero; _row = Radius.Zero; _yaw = Radius.Zero; } - public GraphicsMatrix Projection => _projection; + public GMatrix44 Projection => _projection; - public GraphicsMatrix View + public GMatrix44 View { get { if (_isLookAtAvailable) { return _lookAt; } - GraphicsMatrix move = GraphicsMatrix.Unit; - move.column[3] -= cameraAtt.column[3]; + GMatrix44 move = GMatrix44.Unit; + move[3] = move[3] - cameraAtt[3]; _lookAt = rotationAtt.Transpose * move; _isLookAtAvailable = true; return _lookAt; } } - public GraphicsMatrix CameraMatrix() + public GMatrix44 CameraMatrix() { return Projection * View; } @@ -77,7 +77,7 @@ namespace Nitload.Mathematics.Numeric.Graphics float distance ) { - cameraAtt.column[3].Z -= distance; + cameraAtt[3, 2] -= distance; _isLookAtAvailable = false; } @@ -85,7 +85,7 @@ namespace Nitload.Mathematics.Numeric.Graphics float distance ) { - cameraAtt.column[3].X -= distance; + cameraAtt[3, 0] -= distance; _isLookAtAvailable = false; } @@ -93,7 +93,7 @@ namespace Nitload.Mathematics.Numeric.Graphics float distance ) { - cameraAtt.column[3].Y -= distance; + cameraAtt[3, 1] -= distance; _isLookAtAvailable = false; } @@ -104,9 +104,9 @@ namespace Nitload.Mathematics.Numeric.Graphics ) { Vec3 direction = position - target; - if ((position == cameraAtt.column[3].XYZ) && + if ((position == new Vec3(cameraAtt[3, 0], cameraAtt[3, 1], cameraAtt[3, 2])) && (this.up == up) && - (cameraAtt.column[3].XYZ == direction)) { + (new Vec3(cameraAtt[3, 0], cameraAtt[3, 1], cameraAtt[3, 2]) == direction)) { return; } if (direction.GetLength() == 0) @@ -126,10 +126,10 @@ namespace Nitload.Mathematics.Numeric.Graphics Vec3 right = Linear.Orthogonalize(Vec3.Cross(up, direction)); - cameraAtt.column[0].XYZ = right; - cameraAtt.column[1].XYZ = up; - cameraAtt.column[2].XYZ = direction; - cameraAtt.column[3].XYZ = position; + cameraAtt[0] = new Vec4(right, 0); + cameraAtt[1] = new Vec4(up, 0); + cameraAtt[2] = new Vec4(direction, 0); + cameraAtt[3] = new Vec4(position, 1); InternalPitch(); InternalRow(); @@ -152,13 +152,13 @@ namespace Nitload.Mathematics.Numeric.Graphics width = MathF.Abs(GeometryScaler.Tan(fovRadius / 2) * near * 2), height = (width * size.Y) / size.X; - _projection.column[0].X = (2 * near) / width; - _projection.column[1].Y = (2 * near) / height; - _projection.column[2].X = (2 * offsetX) / width; - _projection.column[2].Y = (2 * offsetY) / height; - _projection.column[2].Z = (far + near) / (near - far); - _projection.column[2].W = -1; - _projection.column[3].Z = (2 * near * far) / (near - far); + _projection[0].X = (2 * near) / width; + _projection[1].Y = (2 * near) / height; + _projection[2].X = (2 * offsetX) / width; + _projection[2].Y = (2 * offsetY) / height; + _projection[2].Z = (far + near) / (near - far); + _projection[2].W = -1; + _projection[3].Z = (2 * near * far) / (near - far); } public void Test( diff --git a/src/Nitload.Graphics/OpenGL/Buffer/EBO.cs b/src/Nitload.Graphics/OpenGL/Buffer/EBO.cs deleted file mode 100644 index e5cec88b..00000000 --- a/src/Nitload.Graphics/OpenGL/Buffer/EBO.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ----------------------------------------------------------------------------- -// file="EBO" -// Project: StgSharp -// Copyright (c) Nitload. -// SPDX-License-Identifier: MIT -// ----------------------------------------------------------------------------- - -namespace Nitload.Graphics.OpenGL -{ - /// A set of OpenGL element-buffer handles. - public sealed record class ElementBuffer( - GlHandle[] Handles - ) - { - - public int Count => Handles.Length; - - public GlHandle this[int index] => Handles[index]; - - } -} diff --git a/src/Nitload.Graphics/OpenGL/Buffer/FBO.cs b/src/Nitload.Graphics/OpenGL/Buffer/FBO.cs deleted file mode 100644 index 82355654..00000000 --- a/src/Nitload.Graphics/OpenGL/Buffer/FBO.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ----------------------------------------------------------------------------- -// file="FBO" -// Project: StgSharp -// Copyright (c) Nitload. -// SPDX-License-Identifier: MIT -// ----------------------------------------------------------------------------- - -namespace Nitload.Graphics.OpenGL -{ - /// A set of OpenGL framebuffer handles. - public sealed record class FrameBuffer( - GlHandle[] Handles - ) - { - - public int Count => Handles.Length; - - public GlHandle this[int index] => Handles[index]; - - } -} diff --git a/src/Nitload.Graphics/OpenGL/Buffer/RBO.cs b/src/Nitload.Graphics/OpenGL/Buffer/RBO.cs deleted file mode 100644 index 82712b88..00000000 --- a/src/Nitload.Graphics/OpenGL/Buffer/RBO.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ----------------------------------------------------------------------------- -// file="RBO" -// Project: StgSharp -// Copyright (c) Nitload. -// SPDX-License-Identifier: MIT -// ----------------------------------------------------------------------------- - -namespace Nitload.Graphics.OpenGL -{ - /// A set of OpenGL renderbuffer handles. - public sealed record class RenderBuffer( - GlHandle[] Handles - ) - { - - public int Count => Handles.Length; - - public GlHandle this[int index] => Handles[index]; - - } -} diff --git a/src/Nitload.Graphics/OpenGL/Buffer/TextureGL.cs b/src/Nitload.Graphics/OpenGL/Buffer/TextureGL.cs deleted file mode 100644 index 75f62bf2..00000000 --- a/src/Nitload.Graphics/OpenGL/Buffer/TextureGL.cs +++ /dev/null @@ -1,54 +0,0 @@ -// ----------------------------------------------------------------------------- -// file="TextureGL" -// Project: StgSharp -// Copyright (c) Nitload. -// SPDX-License-Identifier: MIT -// ----------------------------------------------------------------------------- - -namespace Nitload.Graphics.OpenGL -{ - /// A set of OpenGL texture handles. - public sealed record class TextureGL( - GlHandle[] Handles - ) - { - - public int Count => Handles.Length; - - public GlHandle this[int index] => Handles[index]; - - } - - public readonly record struct TextureProperty( - TextureFilter FilterOnMagnify, - TextureFilter FilterOnMinify, - TextureWrap WrapOnHorizontal, - TextureWrap WrapOnVertical - ); - - #pragma warning disable CA1008 - public enum TextureFilter - #pragma warning restore CA1008 - { - - Nearest = glConst.NEAREST, - Linear = glConst.LINEAR, - NearestLinearMipmap = glConst.NEAREST_MIPMAP_LINEAR, - LinearLinearMipmap = glConst.LINEAR_MIPMAP_LINEAR, - NearestNearestMipmap = glConst.NEAREST_MIPMAP_NEAREST, - LinearNearestMipmap = glConst.LINEAR_MIPMAP_NEAREST, - - } - - #pragma warning disable CA1008 - public enum TextureWrap - #pragma warning restore CA1008 - { - - Repeat = glConst.REPEAT, - MirroredRepeat = glConst.MIRRORED_REPEAT, - ClampToEdge = glConst.CLAMP_TO_EDGE, - ClampToBorder = glConst.CLAMP_TO_BORDER, - - } -} diff --git a/src/Nitload.Graphics/OpenGL/Buffer/VAO.cs b/src/Nitload.Graphics/OpenGL/Buffer/VAO.cs deleted file mode 100644 index 297d9a04..00000000 --- a/src/Nitload.Graphics/OpenGL/Buffer/VAO.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ----------------------------------------------------------------------------- -// file="VAO" -// Project: StgSharp -// Copyright (c) Nitload. -// SPDX-License-Identifier: MIT -// ----------------------------------------------------------------------------- - -namespace Nitload.Graphics.OpenGL -{ - /// A set of OpenGL vertex-array handles. - public sealed record class VertexArray( - GlHandle[] Handles - ) - { - - public int Count => Handles.Length; - - public GlHandle this[int index] => Handles[index]; - - } -} diff --git a/src/Nitload.Graphics/OpenGL/Buffer/VBO.cs b/src/Nitload.Graphics/OpenGL/Buffer/VBO.cs deleted file mode 100644 index 26117906..00000000 --- a/src/Nitload.Graphics/OpenGL/Buffer/VBO.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ----------------------------------------------------------------------------- -// file="VBO" -// Project: StgSharp -// Copyright (c) Nitload. -// SPDX-License-Identifier: MIT -// ----------------------------------------------------------------------------- - -namespace Nitload.Graphics.OpenGL -{ - /// A set of OpenGL vertex-buffer handles. - public sealed record class VertexBuffer( - GlHandle[] Handles - ) - { - - public int Count => Handles.Length; - - public GlHandle this[int index] => Handles[index]; - - } -} diff --git a/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.Base.g.cs b/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.Base.g.cs index 292d8f00..6889c48b 100644 --- a/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.Base.g.cs +++ b/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.Base.g.cs @@ -1783,9 +1783,12 @@ namespace Nitload.Graphics.OpenGL /// See docs.gl. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Uniform(int location, bool transpose, GraphicsMatrix value) + public void Uniform(int location, bool transpose, GMatrix44 value) { - __context->glUniformMatrix4fv(location, 1, transpose ? (byte)1 : (byte)0, (float*)&value); + fixed (GMatrix44* __value = &value) + { + __context->glUniformMatrix4fv(location, 1, transpose ? (byte)1 : (byte)0, (float*)__value); + } } /// @@ -1794,9 +1797,9 @@ namespace Nitload.Graphics.OpenGL /// See docs.gl. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Uniform(int location, bool transpose, ReadOnlySpan values) + public void Uniform(int location, bool transpose, ReadOnlySpan> values) { - fixed (GraphicsMatrix* __value = values) + fixed (GMatrix44* __value = values) { __context->glUniformMatrix4fv(location, values.Length, transpose ? (byte)1 : (byte)0, (float*)__value); } diff --git a/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.v4_1.g.cs b/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.v4_1.g.cs index d823b332..512d07be 100644 --- a/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.v4_1.g.cs +++ b/src/Nitload.Graphics/OpenGL/Generated/OpenGLFunction.v4_1.g.cs @@ -631,9 +631,12 @@ namespace Nitload.Graphics.OpenGL /// See docs.gl. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ProgramUniform(GlHandle program, int location, bool transpose, GraphicsMatrix value) + public void ProgramUniform(GlHandle program, int location, bool transpose, GMatrix44 value) { - __context->glProgramUniformMatrix4fv(program.Value, location, 1, transpose ? (byte)1 : (byte)0, (float*)&value); + fixed (GMatrix44* __value = &value) + { + __context->glProgramUniformMatrix4fv(program.Value, location, 1, transpose ? (byte)1 : (byte)0, (float*)__value); + } } /// @@ -642,9 +645,9 @@ namespace Nitload.Graphics.OpenGL /// See docs.gl. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ProgramUniform(GlHandle program, int location, bool transpose, ReadOnlySpan values) + public void ProgramUniform(GlHandle program, int location, bool transpose, ReadOnlySpan> values) { - fixed (GraphicsMatrix* __value = values) + fixed (GMatrix44* __value = values) { __context->glProgramUniformMatrix4fv(program.Value, location, values.Length, transpose ? (byte)1 : (byte)0, (float*)__value); } diff --git a/src/Nitload.Mathematics/Geometries/Coordination/CoordinationBase.cs b/src/Nitload.Mathematics/Geometries/Coordination/CoordinationBase.cs index 8d8c25d5..fe89098f 100644 --- a/src/Nitload.Mathematics/Geometries/Coordination/CoordinationBase.cs +++ b/src/Nitload.Mathematics/Geometries/Coordination/CoordinationBase.cs @@ -16,41 +16,61 @@ namespace Nitload.Geometries private bool globalCoordAvailable; private CoordinationBase localCoordination; - private GraphicsMatrix coordMat; + private GMatrix44 coordMat; public CoordinationBase( CoordinationBase localCoordination ) { this.localCoordination = localCoordination; - coordMat = new GraphicsMatrix(); + coordMat = new GMatrix44(); } public virtual Point LocalOrigin { - get => new Point(coordMat.column[3].XYZ); - internal set => coordMat.column[3].XYZ = value.Coord; + get => new Point(new Vec3(coordMat[3, 0], coordMat[3, 1], coordMat[3, 2])); + internal set + { + coordMat[3, 0] = value.Coord.X; + coordMat[3, 1] = value.Coord.Y; + coordMat[3, 2] = value.Coord.Z; + } } public virtual Vec3 LocalX { - get => coordMat.column[3].XYZ; - internal set => coordMat.column[3].XYZ = value; + get => new Vec3(coordMat[3, 0], coordMat[3, 1], coordMat[3, 2]); + internal set + { + coordMat[3, 0] = value.X; + coordMat[3, 1] = value.Y; + coordMat[3, 2] = value.Z; + } } public virtual Vec3 LocalY { - get => coordMat.column[3].XYZ; - internal set => coordMat.column[3].XYZ = value; + get => new Vec3(coordMat[3, 0], coordMat[3, 1], coordMat[3, 2]); + internal set + { + coordMat[3, 0] = value.X; + coordMat[3, 1] = value.Y; + coordMat[3, 2] = value.Z; + } } public virtual Vec3 LocalZ { - get => coordMat.column[3].XYZ; - set => coordMat.column[3].XYZ = value; + get => new Vec3(coordMat[3, 0], coordMat[3, 1], coordMat[3, 2]); + set + { + coordMat[3, 0] = value.X; + coordMat[3, 1] = value.Y; + coordMat[3, 2] = value.Z; + } } - protected ref GraphicsMatrix CoordMat => ref coordMat; + protected ref GMatrix44 CoordMat => ref coordMat; } } diff --git a/src/Nitload.Mathematics/Geometries/Coordination/PlainCoordinate.cs b/src/Nitload.Mathematics/Geometries/Coordination/PlainCoordinate.cs index d3bd8002..5262d425 100644 --- a/src/Nitload.Mathematics/Geometries/Coordination/PlainCoordinate.cs +++ b/src/Nitload.Mathematics/Geometries/Coordination/PlainCoordinate.cs @@ -26,11 +26,11 @@ namespace Nitload.Geometries ) : base(local) { - CoordMat = GraphicsMatrix.Unit; + CoordMat = GMatrix44.Unit; - CoordMat.column[0] = new Vec4(xAxis, 0); - CoordMat.column[1] = new Vec4(yAxis, 0); - CoordMat.column[2] = new Vec4(Linear.Normalize(Vec3.Cross(xAxis, yAxis)), 0); + CoordMat[0] = new Vec4(xAxis, 0); + CoordMat[1] = new Vec4(yAxis, 0); + CoordMat[2] = new Vec4(Linear.Normalize(Vec3.Cross(xAxis, yAxis)), 0); LocalOrigin = origin; } diff --git a/src/Nitload.Mathematics/Geometries/IInstancing.cs b/src/Nitload.Mathematics/Geometries/IInstancing.cs index 1fa00c3d..8d50e54d 100644 --- a/src/Nitload.Mathematics/Geometries/IInstancing.cs +++ b/src/Nitload.Mathematics/Geometries/IInstancing.cs @@ -41,13 +41,11 @@ namespace Nitload.Geometries public Vec3 CenterPositionGlobal { get; internal set; } - public Vec3 Coord// - { get => GlobalBuffer.CoordAndRotationList[BufferId].XYZ;// - set - { - Vec4 v = new(value, 0); - GlobalBuffer.CoordAndRotationList[BufferId] = v; - } } + public Vec3 Coord { get => GlobalBuffer.CoordAndRotationList[BufferId].XYZ; set + { + Vec4 v = new(value, 0); + GlobalBuffer.CoordAndRotationList[BufferId] = v; + } } public void Move() { diff --git a/src/Nitload.Mathematics/Numeric/Graphics/GMatrix44_T.cs b/src/Nitload.Mathematics/Numeric/Graphics/GMatrix44_T.cs new file mode 100644 index 00000000..4824e1e2 --- /dev/null +++ b/src/Nitload.Mathematics/Numeric/Graphics/GMatrix44_T.cs @@ -0,0 +1,190 @@ +// ----------------------------------------------------------------------------- +// file="GMatrix44_T" +// Project: StgSharp +// Copyright (c) Nitload. +// SPDX-License-Identifier: MIT +// ----------------------------------------------------------------------------- + +using Nitload.Mathematics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Text; +using System.Threading.Tasks; + +namespace Nitload.Mathematics.Numeric.Graphics +{ + [StructLayout(LayoutKind.Sequential, Pack = 16)] + public struct GMatrix44 where T : unmanaged, INumber + { + + private Buffer _buffer; + + public Vec4 this[ + int x + ] + { + readonly get => _buffer[x]; + set => _buffer[x] = value; + } + + public T this[ + int x, + int y + ] + { + readonly get => _buffer[x][y]; + set => _buffer[x][y] = value; + } + + public static GMatrix44 Unit + { + get + { + GMatrix44 result = new(); + result[0] = new Vec4(T.One, T.Zero, T.Zero, T.Zero); + result[1] = new Vec4(T.Zero, T.One, T.Zero, T.Zero); + result[2] = new Vec4(T.Zero, T.Zero, T.One, T.Zero); + result[3] = new Vec4(T.Zero, T.Zero, T.Zero, T.One); + return result; + } + } + + public GMatrix44 Transpose + { + get + { + GMatrix44 transpose = new(); + + // Native SK transpose is intentionally deferred until the module + // startup path can guarantee that GlobalContext has been initialized. + + if (Unsafe.SizeOf() == 4) + { + Vector128 row0Source = _buffer[0].AsVector128Unsafe(); + Vector128 row1Source = _buffer[1].AsVector128Unsafe(); + Vector128 row2Source = _buffer[2].AsVector128Unsafe(); + Vector128 row3Source = _buffer[3].AsVector128Unsafe(); + Vector128 row0 = Unsafe.As, Vector128>(ref row0Source); + Vector128 row1 = Unsafe.As, Vector128>(ref row1Source); + Vector128 row2 = Unsafe.As, Vector128>(ref row2Source); + Vector128 row3 = Unsafe.As, Vector128>(ref row3Source); + + Vector128 pairMask = Vector128.Create(-1, 0, -1, 0); + Vector128 firstPairMask = Vector128.Create(-1, -1, 0, 0); + Vector128 lowShuffle = Vector128.Create(0, 0, 1, 1); + Vector128 highShuffle = Vector128.Create(2, 2, 3, 3); + Vector128 secondLaneShuffle = Vector128.Create(2, 3, 2, 3); + + Vector128 pair01Low = Vector128.ConditionalSelect( + pairMask, + Vector128.Shuffle(row0, lowShuffle), + Vector128.Shuffle(row1, lowShuffle)); + Vector128 pair23Low = Vector128.ConditionalSelect( + pairMask, + Vector128.Shuffle(row2, lowShuffle), + Vector128.Shuffle(row3, lowShuffle)); + Vector128 pair01High = Vector128.ConditionalSelect( + pairMask, + Vector128.Shuffle(row0, highShuffle), + Vector128.Shuffle(row1, highShuffle)); + Vector128 pair23High = Vector128.ConditionalSelect( + pairMask, + Vector128.Shuffle(row2, highShuffle), + Vector128.Shuffle(row3, highShuffle)); + + Vector128 result0 = Vector128.ConditionalSelect( + firstPairMask, pair01Low, pair23Low); + Vector128 result1 = Vector128.ConditionalSelect( + firstPairMask, + Vector128.Shuffle(pair01Low, secondLaneShuffle), + Vector128.Shuffle(pair23Low, secondLaneShuffle)); + Vector128 result2 = Vector128.ConditionalSelect( + firstPairMask, pair01High, pair23High); + Vector128 result3 = Vector128.ConditionalSelect( + firstPairMask, + Vector128.Shuffle(pair01High, secondLaneShuffle), + Vector128.Shuffle(pair23High, secondLaneShuffle)); + transpose._buffer[0] = Vec4.FromVector128Unsafe( + Unsafe.As, Vector128>(ref result0)); + transpose._buffer[1] = Vec4.FromVector128Unsafe( + Unsafe.As, Vector128>(ref result1)); + transpose._buffer[2] = Vec4.FromVector128Unsafe( + Unsafe.As, Vector128>(ref result2)); + transpose._buffer[3] = Vec4.FromVector128Unsafe( + Unsafe.As, Vector128>(ref result3)); + return transpose; + } + + if (Unsafe.SizeOf() == 8) + { + Vector256 row0Source = _buffer[0].AsVector256Unsafe(); + Vector256 row1Source = _buffer[1].AsVector256Unsafe(); + Vector256 row2Source = _buffer[2].AsVector256Unsafe(); + Vector256 row3Source = _buffer[3].AsVector256Unsafe(); + Vector256 row0 = Unsafe.As, Vector256>(ref row0Source); + Vector256 row1 = Unsafe.As, Vector256>(ref row1Source); + Vector256 row2 = Unsafe.As, Vector256>(ref row2Source); + Vector256 row3 = Unsafe.As, Vector256>(ref row3Source); + + Vector256 pairMask = Vector256.Create(-1L, 0L, -1L, 0L); + Vector256 firstPairMask = Vector256.Create(-1L, -1L, 0L, 0L); + Vector256 firstShuffle = Vector256.Create(0L, 0L, 1L, 1L); + Vector256 secondShuffle = Vector256.Create(2L, 2L, 3L, 3L); + Vector256 pair01First = Vector256.ConditionalSelect( + pairMask, + Vector256.Shuffle(row0, firstShuffle), + Vector256.Shuffle(row1, firstShuffle)); + Vector256 pair23First = Vector256.ConditionalSelect( + pairMask, + Vector256.Shuffle(row2, firstShuffle), + Vector256.Shuffle(row3, firstShuffle)); + Vector256 pair01Second = Vector256.ConditionalSelect( + pairMask, + Vector256.Shuffle(row0, secondShuffle), + Vector256.Shuffle(row1, secondShuffle)); + Vector256 pair23Second = Vector256.ConditionalSelect( + pairMask, + Vector256.Shuffle(row2, secondShuffle), + Vector256.Shuffle(row3, secondShuffle)); + Vector256 result0 = Vector256.ConditionalSelect( + firstPairMask, pair01First, pair23First); + Vector256 result1 = Vector256.ConditionalSelect( + firstPairMask, + Vector256.Shuffle(pair01First, secondShuffle), + Vector256.Shuffle(pair23First, secondShuffle)); + Vector256 result2 = Vector256.ConditionalSelect( + firstPairMask, pair01Second, pair23Second); + Vector256 result3 = Vector256.ConditionalSelect( + firstPairMask, + Vector256.Shuffle(pair01Second, secondShuffle), + Vector256.Shuffle(pair23Second, secondShuffle)); + transpose._buffer[0] = Vec4.FromVector256Unsafe( + Unsafe.As, Vector256>(ref result0)); + transpose._buffer[1] = Vec4.FromVector256Unsafe( + Unsafe.As, Vector256>(ref result1)); + transpose._buffer[2] = Vec4.FromVector256Unsafe( + Unsafe.As, Vector256>(ref result2)); + transpose._buffer[3] = Vec4.FromVector256Unsafe( + Unsafe.As, Vector256>(ref result3)); + return transpose; + } + + throw GraphicVector.ThrowUnsupportedTypeException(); + } + } + + [InlineArray(4)] + private struct Buffer + { + + public Vec4 _column; + + } + + } +} diff --git a/src/Nitload.Mathematics/Numeric/Graphics/GraphicsMatrix.cs b/src/Nitload.Mathematics/Numeric/Graphics/GraphicsMatrix.cs deleted file mode 100644 index 2054a690..00000000 --- a/src/Nitload.Mathematics/Numeric/Graphics/GraphicsMatrix.cs +++ /dev/null @@ -1,261 +0,0 @@ -// ----------------------------------------------------------------------------- -// file="GraphicsMatrix" -// Project: StgSharp -// Copyright (c) Nitload. -// SPDX-License-Identifier: MIT -// ----------------------------------------------------------------------------- - -using Nitload.Mathematics; -using Nitload.Mathematics.Internal; -using Nitload.Mathematics.Numeric; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics.X86; - -namespace Nitload.Mathematics.Numeric.Graphics -{ - [StructLayout(LayoutKind.Explicit, Size = 16 * sizeof(float), Pack = 16)] - public unsafe struct GraphicsMatrix : IEquatable - { - - [FieldOffset(0 * sizeof(float))] internal Column column; - - [FieldOffset(0)] internal ColumnSet4 mat; - [FieldOffset(0)] internal MatrixKernel kernel; - - internal GraphicsMatrix( - ColumnSet4 mat - ) - { - Unsafe.SkipInit(out this); - this.mat = mat; - } - - internal GraphicsMatrix( - Vector4 vec0, - Vector4 vec1, - Vector4 vec2, - Vector4 vec3 - ) - { - Unsafe.SkipInit(out this); - mat.colum0 = vec0; - mat.colum1 = vec1; - mat.colum2 = vec2; - mat.colum3 = vec3; - } - - public GraphicsMatrix() - { - Unsafe.SkipInit(out this); - } - - public unsafe GraphicsMatrix( - float a00, - float a01, - float a02, - float a03, - float a10, - float a11, - float a12, - float a13, - float a20, - float a21, - float a22, - float a23, - float a30, - float a31, - float a32, - float a33 - ) - { - Unsafe.SkipInit(out this); - mat.colum0 = new Vector4(a00, a10, a20, a30); - mat.colum1 = new Vector4(a01, a11, a21, a31); - mat.colum2 = new Vector4(a02, a12, a22, a32); - mat.colum3 = new Vector4(a03, a13, a23, a33); - } - - public unsafe float this[ - int rowNum, - int columNum - ] - { - get - { -#if DEBUG - if (rowNum is > 3 or < 0) - { - throw new ArgumentOutOfRangeException(nameof(columNum)); - } - if (columNum is > 3 or < 0) { - throw new ArgumentOutOfRangeException(nameof(columNum)); - } -#endif - return kernel[rowNum + (4 * columNum)]; - } - set - { -#if DEBUG - if (rowNum is > 3 or < 0) - { - throw new ArgumentOutOfRangeException(nameof(columNum)); - } - if (columNum is > 3 or < 0) { - throw new ArgumentOutOfRangeException(nameof(columNum)); - } -#endif - kernel[rowNum + (4 * columNum)] = value; - } - } - - public GraphicsMatrix Transpose - { - get - { - GraphicsMatrix transpose = new(); - InternalTranspose(ref transpose); - return transpose; - } - } - - public static GraphicsMatrix Unit => new GraphicsMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 1); - - public static GraphicsMatrix WNegativeUnit => new GraphicsMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 0, -1); - - public static GraphicsMatrix XNegativeUnit => new GraphicsMatrix(-1, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1); - - public static GraphicsMatrix YNegativeUnit => new GraphicsMatrix(1, 0, 0, 0, 0, -1, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1); - - public static GraphicsMatrix ZNegativeUnit => new GraphicsMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, - 0, -1, 0, 0, 0, 0, 1); - - public ReadOnlySpan AsSpan() - { - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref mat), 16); - } - - public override bool Equals( - object? obj - ) - { - return (obj is GraphicsMatrix x) && Equals(x); - } - - public bool Equals( - GraphicsMatrix other - ) - { - return mat.Equals(other.mat); - } - - public override int GetHashCode() - { - return HashCode.Combine(mat.colum0.GetHashCode(), mat.colum1.GetHashCode(), - mat.colum2.GetHashCode(), mat.colum3.GetHashCode()); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal unsafe void InternalTranspose( - ref GraphicsMatrix transpose - ) - { - const int f32 = 0; - fixed (MatrixKernel* source = &this.kernel, target = &transpose.kernel) { - NumericalModule.GlobalContext.mat_sk[f32].Call(MatrixIntrinsicHandle.Transpose, - null, (MatrixKernel*)source, - (MatrixKernel*)target, 0); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe GraphicsMatrix operator -( - GraphicsMatrix left, - GraphicsMatrix right - ) - { - GraphicsMatrix ret = new(); - int f32 = MatrixElementType.F32.IntrinsicNode; - NumericalModule.GlobalContext.mat_sk[f32].Call(MatrixIntrinsicHandle.Sub, - (MatrixKernel*)&left.kernel, - (MatrixKernel*)&right.kernel, - (MatrixKernel*)&ret.kernel, 1); - return ret; - } - - public static bool operator !=( - GraphicsMatrix left, - GraphicsMatrix right - ) - { - return !(left == right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe GraphicsMatrix operator *( - GraphicsMatrix mat, - float value - ) - { - int f32 = MatrixElementType.F32.IntrinsicNode; - return new GraphicsMatrix(mat.mat.colum0 * value, mat.mat.colum1 * value, - mat.mat.colum2 * value, mat.mat.colum3 * value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GraphicsMatrix operator *( - GraphicsMatrix left, - GraphicsMatrix right - ) - { - GraphicsMatrix ret = new(); - int f32 = MatrixElementType.F32.IntrinsicNode; - NumericalModule.GlobalContext.mat_sk[f32].Call(MatrixIntrinsicHandle.Fma, - (MatrixKernel*)&left, - (MatrixKernel*)&right, - (MatrixKernel*)&ret, 0); - return ret; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe GraphicsMatrix operator +( - GraphicsMatrix left, - GraphicsMatrix right - ) - { - GraphicsMatrix ret = new(); - int f32 = MatrixElementType.F32.IntrinsicNode; - NumericalModule.GlobalContext.mat_sk[f32].Call(MatrixIntrinsicHandle.Add, - (MatrixKernel*)&left, - (MatrixKernel*)&right, - (MatrixKernel*)&ret, 1); - return ret; - } - - public static bool operator ==( - GraphicsMatrix left, - GraphicsMatrix right - ) - { - return left.Equals(right); - } - - [InlineArray(4)] - internal struct Column - { - - private Vec4 value; - - } - - } -} - diff --git a/src/Nitload.Mathematics/Numeric/Graphics/Vec3.cs b/src/Nitload.Mathematics/Numeric/Graphics/Vec3.cs index 5494d769..1ef78a69 100644 --- a/src/Nitload.Mathematics/Numeric/Graphics/Vec3.cs +++ b/src/Nitload.Mathematics/Numeric/Graphics/Vec3.cs @@ -16,38 +16,83 @@ namespace Nitload.Mathematics.Numeric.Graphics { [MethodImpl(MethodImplOptions.AggressiveInlining)] +<<<<<<< HEAD public static Vec3 ConvertToDouble(Vec3 source) +======= + public static Vec3 ConvertToDouble( + Vec3 source + ) +>>>>>>> 8755185e78a8091ca252c1342249110180446be3 { return FromVector256Unsafe(Vector256.ConvertToDouble(source.AsVector256Safe())); } [MethodImpl(MethodImplOptions.AggressiveInlining)] +<<<<<<< HEAD public static Vec3 ConvertToDouble(Vec3 source) +======= + public static Vec3 ConvertToDouble( + Vec3 source + ) +>>>>>>> 8755185e78a8091ca252c1342249110180446be3 { return FromVector256Unsafe(Vector256.ConvertToDouble(source.AsVector256Safe())); } +<<<<<<< HEAD #region cast [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vec3 ConvertToSingle(Vec3 source) +======= + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec3 ConvertToSingle( + Vec3 source + ) +>>>>>>> 8755185e78a8091ca252c1342249110180446be3 { return FromVector128Unsafe(Vector128.ConvertToSingle(source.AsVector128Safe())); } [MethodImpl(MethodImplOptions.AggressiveInlining)] +<<<<<<< HEAD public static Vec3 ConvertToSingle(Vec3 source) +======= + public static Vec3 ConvertToSingle( + Vec3 source + ) +>>>>>>> 8755185e78a8091ca252c1342249110180446be3 { return FromVector128Unsafe(Vector128.ConvertToSingle(source.AsVector128Safe())); } +<<<<<<< HEAD public static bool IsParallel(Vec3 left, Vec3 right) where T: unmanaged,INumber +======= + public static Vec3 FromSpan( + ReadOnlySpan span + ) where T : unmanaged, INumber + { + if (span.Length < 3) { + throw new ArgumentException("Span length must be at least 3.", nameof(span)); + } + + return new(span[0], span[1], span[2]); + } + + public static bool IsParallel( + Vec3 left, + Vec3 right + ) where T : unmanaged,INumber +>>>>>>> 8755185e78a8091ca252c1342249110180446be3 { return Cross(left, right).Equals(T.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector128 AsVector128Safe(this Vec3 source) where T: unmanaged, INumber + internal static Vector128 AsVector128Safe( + this Vec3 source + ) where T : unmanaged, INumber { Vector128 result = Vector128.Zero; Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), source); @@ -55,7 +100,9 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector128 AsVector128Unsafe(this Vec3 source) where T: unmanaged, INumber + internal static Vector128 AsVector128Unsafe( + this Vec3 source + ) where T : unmanaged, INumber { Unsafe.SkipInit(out Vector128 result); Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), source); @@ -63,7 +110,9 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector256 AsVector256Safe(this Vec3 source) where T: unmanaged, INumber + internal static Vector256 AsVector256Safe( + this Vec3 source + ) where T : unmanaged, INumber { Vector256 result = Vector256.Zero; Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), source); @@ -71,7 +120,9 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector256 AsVector256Unsafe(this Vec3 source) where T: unmanaged, INumber + internal static Vector256 AsVector256Unsafe( + this Vec3 source + ) where T : unmanaged, INumber { Unsafe.SkipInit(out Vector256 result); Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), source); @@ -79,23 +130,33 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vec3 FromVector128Unsafe(Vector128 source) where T: unmanaged, INumber + internal static Vec3 FromVector128Unsafe( + Vector128 source + ) where T : unmanaged, INumber { return Unsafe.As, Vec3>(ref source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vec3 FromVector256Unsafe(Vector256 source) where T: unmanaged, INumber + internal static Vec3 FromVector256Unsafe( + Vector256 source + ) where T : unmanaged, INumber { return Unsafe.As, Vec3>(ref source); } +<<<<<<< HEAD #ednregion +======= +>>>>>>> 8755185e78a8091ca252c1342249110180446be3 #region cross [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec3 Cross(Vec3 left, Vec3 right) where T: unmanaged, INumber + public static Vec3 Cross( + Vec3 left, + Vec3 right + ) where T : unmanaged, INumber { if (typeof(T) == typeof(float)) { @@ -144,7 +205,10 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vec3 CrossDouble(Vec3 left, Vec3 right) + private static Vec3 CrossDouble( + Vec3 left, + Vec3 right + ) { Vector256 l = left.AsVector256Safe(); Vector256 r = right.AsVector256Safe(); @@ -155,7 +219,10 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vec3 CrossInt32(Vec3 left, Vec3 right) + private static Vec3 CrossInt32( + Vec3 left, + Vec3 right + ) { Vector128 l = left.AsVector128Safe(); Vector128 r = right.AsVector128Safe(); @@ -166,7 +233,10 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vec3 CrossSingle(Vec3 left, Vec3 right) + private static Vec3 CrossSingle( + Vec3 left, + Vec3 right + ) { Vector128 l = left.AsVector128Safe(); Vector128 r = right.AsVector128Safe(); @@ -177,7 +247,10 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vec3 CrossUInt32(Vec3 left, Vec3 right) + private static Vec3 CrossUInt32( + Vec3 left, + Vec3 right + ) { Vector128 l = left.AsVector128Safe(); Vector128 r = right.AsVector128Safe(); @@ -188,7 +261,10 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vec3 CrossUInt64(Vec3 left, Vec3 right) + private static Vec3 CrossUInt64( + Vec3 left, + Vec3 right + ) { Vector256 l = left.AsVector256Safe(); Vector256 r = right.AsVector256Safe(); @@ -199,7 +275,10 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vec3 CrossInt64(Vec3 left, Vec3 right) + private static Vec3 CrossInt64( + Vec3 left, + Vec3 right + ) { Vector256 l = left.AsVector256Safe(); Vector256 r = right.AsVector256Safe(); diff --git a/src/Nitload.Mathematics/Numeric/Graphics/Vec3_T.cs b/src/Nitload.Mathematics/Numeric/Graphics/Vec3_T.cs index be430de6..68b0d1ad 100644 --- a/src/Nitload.Mathematics/Numeric/Graphics/Vec3_T.cs +++ b/src/Nitload.Mathematics/Numeric/Graphics/Vec3_T.cs @@ -21,21 +21,28 @@ namespace Nitload.Mathematics.Numeric.Graphics /// [StructLayout(LayoutKind.Sequential, Pack = 16)] public struct Vec3 : IUnmanagedVector>, IEquatable>, IEnumerable - where T: unmanaged, INumber + where T : unmanaged, INumber { public T X; public T Y; public T Z; - public Vec3(Vec2 xy, T z) + public Vec3( + Vec2 xy, + T z + ) { X = xy.X; Y = xy.Y; Z = z; } - public Vec3(T x, T y, T z) + public Vec3( + T x, + T y, + T z + ) { X = x; Y = y; @@ -73,7 +80,9 @@ namespace Nitload.Mathematics.Numeric.Graphics public static Vec3 UnitZ => new(T.Zero, T.Zero, T.One); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly T Dot(Vec3 right) + public readonly T Dot( + Vec3 right + ) { if (Unsafe.SizeOf() == 8) { @@ -89,31 +98,28 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Vec3 other) + public readonly bool Equals( + Vec3 other + ) { - if (Unsafe.SizeOf() == 4) { + if (Unsafe.SizeOf() == 4) + { return this.AsVector128Safe() == other.AsVector128Safe(); - } - if (Unsafe.SizeOf() == 8) { + } else if (Unsafe.SizeOf() == 8) { return this.AsVector256Safe() == other.AsVector256Safe(); } +#pragma warning disable CA1065 throw GraphicVector.ThrowUnsupportedTypeException(); +#pragma warning restore CA1065 } - public override readonly bool Equals(object? obj) + public override readonly bool Equals( + object? obj + ) { return obj is Vec3 other && Equals(other); } - public static Vec3 FromSpan(ReadOnlySpan span) - { - if (span.Length < 3) { - throw new ArgumentException("Span length must be at least 3.", nameof(span)); - } - - return new(span[0], span[1], span[2]); - } - public readonly IEnumerator GetEnumerator() { yield return X; @@ -132,33 +138,40 @@ namespace Nitload.Mathematics.Numeric.Graphics return T.Max(X, T.Max(Y, Z)); } - public static bool IsParallel(Vec3 left, Vec3 right) + public static bool IsParallel( + Vec3 left, + Vec3 right + ) { return Vec3.Cross(left, right) == Zero; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec3 operator -(Vec3 value) + public static Vec3 operator -( + Vec3 value + ) { - if (Unsafe.SizeOf() == 4) { + if (Unsafe.SizeOf() == 4) + { return Vec3.FromVector128Unsafe(Vector128.Zero - value.AsVector128Unsafe()); - } - if (Unsafe.SizeOf() == 8) { + } else if (Unsafe.SizeOf() == 8) { return Vec3.FromVector256Unsafe(Vector256.Zero - value.AsVector256Unsafe()); } throw GraphicVector.ThrowUnsupportedTypeException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec3 operator -(Vec3 left, Vec3 right) + public static Vec3 operator -( + Vec3 left, + Vec3 right + ) { if (Unsafe.SizeOf() == 4) { Vector128 left_v128 = left.AsVector128Unsafe(); Vector128 right_v128 = right.AsVector128Unsafe(); return Vec3.FromVector128Unsafe(left_v128 - right_v128); - } - if (Unsafe.SizeOf() == 8) + } else if (Unsafe.SizeOf() == 8) { Vector256 left_v256 = left.AsVector256Unsafe(); Vector256 right_v256 = right.AsVector256Unsafe(); @@ -168,21 +181,26 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vec3 left, Vec3 right) + public static bool operator !=( + Vec3 left, + Vec3 right + ) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec3 operator *(Vec3 vector, T scalar) + public static Vec3 operator *( + Vec3 vector, + T scalar + ) { if (Unsafe.SizeOf() == 4) { Vector128 vector_v128 = vector.AsVector128Unsafe(); Vector128 scalar_v128 = Vector128.Create(scalar); return Vec3.FromVector128Unsafe(vector_v128 * scalar_v128); - } - if (Unsafe.SizeOf() == 8) + } else if (Unsafe.SizeOf() == 8) { Vector256 vector_v256 = vector.AsVector256Unsafe(); Vector256 scalar_v256 = Vector256.Create(scalar); @@ -192,27 +210,35 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec3 operator *(T scalar, Vec3 vector) + public static Vec3 operator *( + T scalar, + Vec3 vector + ) { return vector * scalar; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T operator *(Vec3 left, Vec3 right) + public static T operator *( + Vec3 left, + Vec3 right + ) { return left.Dot(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec3 operator /(Vec3 vector, T scalar) + public static Vec3 operator /( + Vec3 vector, + T scalar + ) { if (Unsafe.SizeOf() == 4) { Vector128 vector_v128 = vector.AsVector128Unsafe(); Vector128 scalar_v128 = Vector128.Create(scalar); return Vec3.FromVector128Unsafe(vector_v128 / scalar_v128); - } - if (Unsafe.SizeOf() == 8) + } else if (Unsafe.SizeOf() == 8) { Vector256 vector_v256 = vector.AsVector256Unsafe(); Vector256 scalar_v256 = Vector256.Create(scalar); @@ -222,15 +248,17 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec3 operator +(Vec3 left, Vec3 right) + public static Vec3 operator +( + Vec3 left, + Vec3 right + ) { if (Unsafe.SizeOf() == 4) { Vector128 left_v128 = left.AsVector128Unsafe(); Vector128 right_v128 = right.AsVector128Unsafe(); return Vec3.FromVector128Unsafe(left_v128 + right_v128); - } - if (Unsafe.SizeOf() == 8) + } else if (Unsafe.SizeOf() == 8) { Vector256 left_v256 = left.AsVector256Unsafe(); Vector256 right_v256 = right.AsVector256Unsafe(); @@ -240,13 +268,18 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Vec3 left, Vec3 right) + public static bool operator ==( + Vec3 left, + Vec3 right + ) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vec3((T X, T Y, T Z) tuple) + public static implicit operator Vec3( + (T X, T Y, T Z) tuple + ) { return new(tuple.X, tuple.Y, tuple.Z); } @@ -256,5 +289,13 @@ namespace Nitload.Mathematics.Numeric.Graphics return GetEnumerator(); } + [InlineArray(3)] + private struct Buffer + { + + internal T _element; + + } + } } diff --git a/src/Nitload.Mathematics/Numeric/Graphics/Vec4.cs b/src/Nitload.Mathematics/Numeric/Graphics/Vec4.cs index 85136c19..504396f4 100644 --- a/src/Nitload.Mathematics/Numeric/Graphics/Vec4.cs +++ b/src/Nitload.Mathematics/Numeric/Graphics/Vec4.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Intrinsics; namespace Nitload.Mathematics.Numeric.Graphics @@ -15,7 +16,55 @@ namespace Nitload.Mathematics.Numeric.Graphics { [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector128 AsVector128Unsafe(this Vec4 source) where T: unmanaged, INumber + public static Vec4 ConvertToDouble( + Vec4 source + ) + { + return FromVector256Unsafe(Vector256.ConvertToDouble(source.AsVector256Unsafe())); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec4 ConvertToDouble( + Vec4 source + ) + { + return FromVector256Unsafe(Vector256.ConvertToDouble(source.AsVector256Unsafe())); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec4 ConvertToSingle( + Vec4 source + ) + { + return FromVector128Unsafe(Vector128.ConvertToSingle(source.AsVector128Unsafe())); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec4 ConvertToSingle( + Vec4 source + ) + { + return FromVector128Unsafe(Vector128.ConvertToSingle(source.AsVector128Unsafe())); + } + + public static Vec4 FromSpan( + ReadOnlySpan span + ) where T : unmanaged, INumber + { + if (span.Length < 4) { + throw new ArgumentException("Span length must be at least 4.", nameof(span)); + } + + Unsafe.SkipInit(out Vec4 result); + Span target = MemoryMarshal.CreateSpan(ref Unsafe.As, T>(ref result), 4); + span[..4].CopyTo(target); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector128 AsVector128Unsafe( + this Vec4 source + ) where T : unmanaged, INumber { Unsafe.SkipInit(out Vector128 result); Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), source); @@ -23,7 +72,9 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector256 AsVector256Unsafe(this Vec4 source) where T: unmanaged, INumber + internal static Vector256 AsVector256Unsafe( + this Vec4 source + ) where T : unmanaged, INumber { Unsafe.SkipInit(out Vector256 result); Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), source); @@ -31,40 +82,20 @@ namespace Nitload.Mathematics.Numeric.Graphics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vec4 FromVector128Unsafe(Vector128 source) where T: unmanaged, INumber + internal static Vec4 FromVector128Unsafe( + Vector128 source + ) where T : unmanaged, INumber { return Unsafe.As, Vec4>(ref source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vec4 FromVector256Unsafe(Vector256 source) where T: unmanaged, INumber + internal static Vec4 FromVector256Unsafe( + Vector256 source + ) where T : unmanaged, INumber { return Unsafe.As, Vec4>(ref source); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec4 ConvertToSingle(Vec4 source) - { - return FromVector128Unsafe(Vector128.ConvertToSingle(source.AsVector128Unsafe())); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec4 ConvertToSingle(Vec4 source) - { - return FromVector128Unsafe(Vector128.ConvertToSingle(source.AsVector128Unsafe())); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec4 ConvertToDouble(Vec4 source) - { - return FromVector256Unsafe(Vector256.ConvertToDouble(source.AsVector256Unsafe())); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vec4 ConvertToDouble(Vec4 source) - { - return FromVector256Unsafe(Vector256.ConvertToDouble(source.AsVector256Unsafe())); - } - } } diff --git a/src/Nitload.Mathematics/Numeric/Graphics/Vec4_T.cs b/src/Nitload.Mathematics/Numeric/Graphics/Vec4_T.cs index 35dd4893..c265742c 100644 --- a/src/Nitload.Mathematics/Numeric/Graphics/Vec4_T.cs +++ b/src/Nitload.Mathematics/Numeric/Graphics/Vec4_T.cs @@ -20,11 +20,7 @@ namespace Nitload.Mathematics.Numeric.Graphics public struct Vec4 : IUnmanagedVector> where T : unmanaged, INumber { - public T W; - - public T X; - public T Y; - public T Z; + private Buffer _buffer; public Vec4( Vec3 v3, @@ -50,9 +46,41 @@ namespace Nitload.Mathematics.Numeric.Graphics W = w; } + public T this[ + int index + ] + { + get => _buffer[index]; + set => _buffer[index] = value; + } + + public T X + { + get => _buffer[0]; + set => _buffer[0] = value; + } + + public T Y + { + get => _buffer[1]; + set => _buffer[1] = value; + } + + public T Z + { + get => _buffer[2]; + set => _buffer[2] = value; + } + + public T W + { + get => _buffer[3]; + set => _buffer[3] = value; + } + public Vec2 XY { - get => Unsafe.As>(ref X); + get => Unsafe.As>(ref _buffer[0]); set { X = value.X; @@ -62,11 +90,60 @@ namespace Nitload.Mathematics.Numeric.Graphics public Vec3 XYZ { - get => Unsafe.As>(ref X); + get => Unsafe.As>(ref _buffer[0]); set { T w = W; - Unsafe.As>(ref X) = value; + Unsafe.As>(ref _buffer[0]) = value; + W = w; + } + } + + public Vec3 ZXY + { + readonly get + { + if (Unsafe.SizeOf() == 4) + { + Vector128 source = this.AsVector128Unsafe(); + Vector128 bits = Unsafe.As, Vector128>(ref source); + Vector128 result = Vector128.Shuffle(bits, Vector128.Create(2, 0, 1, 3)); + Vector128 shuffled = Unsafe.As, Vector128>(ref result); + return Vec3.FromVector128Unsafe(shuffled); + } + if (Unsafe.SizeOf() == 8) + { + Vector256 source = this.AsVector256Unsafe(); + Vector256 bits = Unsafe.As, Vector256>(ref source); + Vector256 result = Vector256.Shuffle(bits, Vector256.Create(2L, 0L, 1L, + 3L)); + Vector256 shuffled = Unsafe.As, Vector256>(ref result); + return Vec3.FromVector256Unsafe(shuffled); + } + throw GraphicVector.ThrowUnsupportedTypeException(); + } + set + { + T w = W; + if (Unsafe.SizeOf() == 4) + { + Vector128 source = value.AsVector128Unsafe(); + Vector128 bits = Unsafe.As, Vector128>(ref source); + Vector128 result = Vector128.Shuffle(bits, Vector128.Create(1, 2, 0, 3)); + Vector128 shuffled = Unsafe.As, Vector128>(ref result); + Unsafe.As>(ref _buffer[0]) = Vec3.FromVector128Unsafe(shuffled); + } else if (Unsafe.SizeOf() == 8) + { + Vector256 source = value.AsVector256Unsafe(); + Vector256 bits = Unsafe.As, Vector256>(ref source); + Vector256 result = Vector256.Shuffle(bits, Vector256.Create(1L, 2L, 0L, + 3L)); + Vector256 shuffled = Unsafe.As, Vector256>(ref result); + Unsafe.As>(ref _buffer[0]) = Vec3.FromVector256Unsafe(shuffled); + } else + { + throw GraphicVector.ThrowUnsupportedTypeException(); + } W = w; } } @@ -86,7 +163,7 @@ namespace Nitload.Mathematics.Numeric.Graphics public ReadOnlySpan AsSpan() { - return MemoryMarshal.CreateReadOnlySpan(ref X, 4); + return MemoryMarshal.CreateReadOnlySpan(ref _buffer[0], 4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -98,8 +175,7 @@ namespace Nitload.Mathematics.Numeric.Graphics { Vec4 product = Vec4.FromVector128Unsafe(this.AsVector128Unsafe() * right.AsVector128Unsafe()); return product.X + product.Y + product.Z + product.W; - } - if (Unsafe.SizeOf() == 8) + } else if (Unsafe.SizeOf() == 8) { Vec4 product = Vec4.FromVector256Unsafe(this.AsVector256Unsafe() * right.AsVector256Unsafe()); return product.X + product.Y + product.Z + product.W; @@ -114,20 +190,6 @@ namespace Nitload.Mathematics.Numeric.Graphics return (obj is Vec4 v) && (v == this); } - public static Vec4 FromSpan( - ReadOnlySpan span - ) - { - if (span.Length < 4) { - throw new ArgumentException("Span length must be at least 4.", nameof(span)); - } - - Unsafe.SkipInit(out Vec4 result); - Span target = MemoryMarshal.CreateSpan(ref result.X, 4); - span[..4].CopyTo(target); - return result; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan.Enumerator GetEnumerator() { @@ -139,16 +201,41 @@ namespace Nitload.Mathematics.Numeric.Graphics return HashCode.Combine(X, Y, Z, W); } + public Vec4 Shuffle( + int t0, + int t1, + int t2, + int t3 + ) + { + if (Unsafe.SizeOf() == 4) + { + Vector128 v = this.AsVector128Unsafe(); + Vector128 bits = Unsafe.As, Vector128>(ref v); + Vector128 result = Vector128.Shuffle(bits, Vector128.Create(t0, t1, t2, t3)); + return Vec4.FromVector128Unsafe(Unsafe.As, Vector128>(ref result)); + } else if (Unsafe.SizeOf() == 8) + { + Vector256 v = this.AsVector256Unsafe(); + Vector256 bits = Unsafe.As, Vector256>(ref v); + Vector256 result = Vector256.Shuffle(bits, Vector256.Create(t0, t1, t2, t3)); + return Vec4.FromVector256Unsafe(Unsafe.As, Vector256>(ref result)); + } else + { + throw GraphicVector.ThrowUnsupportedTypeException(); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vec4 operator -( Vec4 left, Vec4 right ) { - if (Unsafe.SizeOf() == 4) { + if (Unsafe.SizeOf() == 4) + { return Vec4.FromVector128Unsafe(left.AsVector128Unsafe() - right.AsVector128Unsafe()); - } - if (Unsafe.SizeOf() == 8) { + } else if (Unsafe.SizeOf() == 8) { return Vec4.FromVector256Unsafe(left.AsVector256Unsafe() - right.AsVector256Unsafe()); } throw GraphicVector.ThrowUnsupportedTypeException(); @@ -168,10 +255,10 @@ namespace Nitload.Mathematics.Numeric.Graphics T scalar ) { - if (Unsafe.SizeOf() == 4) { + if (Unsafe.SizeOf() == 4) + { return Vec4.FromVector128Unsafe(vector.AsVector128Unsafe() * Vector128.Create(scalar)); - } - if (Unsafe.SizeOf() == 8) { + } else if (Unsafe.SizeOf() == 8) { return Vec4.FromVector256Unsafe(vector.AsVector256Unsafe() * Vector256.Create(scalar)); } throw GraphicVector.ThrowUnsupportedTypeException(); @@ -186,6 +273,14 @@ namespace Nitload.Mathematics.Numeric.Graphics return vector * scalar; } + public static Vec4 operator *( + GMatrix44 mat, + Vec4 vec + ) + { + return (mat[0] * vec.X) + (mat[1] * vec.Y) + (mat[2] * vec.Z) + (mat[3] * vec.W); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T operator *( Vec4 left, @@ -201,10 +296,10 @@ namespace Nitload.Mathematics.Numeric.Graphics T scalar ) { - if (Unsafe.SizeOf() == 4) { + if (Unsafe.SizeOf() == 4) + { return Vec4.FromVector128Unsafe(vector.AsVector128Unsafe() / Vector128.Create(scalar)); - } - if (Unsafe.SizeOf() == 8) { + } else if (Unsafe.SizeOf() == 8) { return Vec4.FromVector256Unsafe(vector.AsVector256Unsafe() / Vector256.Create(scalar)); } throw GraphicVector.ThrowUnsupportedTypeException(); @@ -216,10 +311,10 @@ namespace Nitload.Mathematics.Numeric.Graphics Vec4 right ) { - if (Unsafe.SizeOf() == 4) { + if (Unsafe.SizeOf() == 4) + { return Vec4.FromVector128Unsafe(left.AsVector128Unsafe() + right.AsVector128Unsafe()); - } - if (Unsafe.SizeOf() == 8) { + } else if (Unsafe.SizeOf() == 8) { return Vec4.FromVector256Unsafe(left.AsVector256Unsafe() + right.AsVector256Unsafe()); } throw GraphicVector.ThrowUnsupportedTypeException(); @@ -230,10 +325,10 @@ namespace Nitload.Mathematics.Numeric.Graphics Vec4 right ) { - if (Unsafe.SizeOf() == 4) { + if (Unsafe.SizeOf() == 4) + { return left.AsVector128Unsafe() == right.AsVector128Unsafe(); - } - if (Unsafe.SizeOf() == 8) { + } else if (Unsafe.SizeOf() == 8) { return left.AsVector256Unsafe() == right.AsVector256Unsafe(); } #pragma warning disable CA1065 @@ -241,5 +336,13 @@ namespace Nitload.Mathematics.Numeric.Graphics #pragma warning restore CA1065 } + [InlineArray(4)] + private struct Buffer + { + + private T _element; + + } + } } diff --git a/src/Nitload.Native/README.md b/src/Nitload.Native/README.md index d9b10776..1b19e6fe 100644 --- a/src/Nitload.Native/README.md +++ b/src/Nitload.Native/README.md @@ -1,4 +1,4 @@ -# StgSharp.Native +# Nitload.Native ## COMPILE diff --git a/src/Nitload.RegularAnalysis/Text/RegexAstNode.cs b/src/Nitload.RegularAnalysis/Text/RegexAstNode.cs index a953d59b..68db8b59 100644 --- a/src/Nitload.RegularAnalysis/Text/RegexAstNode.cs +++ b/src/Nitload.RegularAnalysis/Text/RegexAstNode.cs @@ -101,6 +101,27 @@ namespace Nitload.RegularAnalysis.Text { return node is null || ReferenceEquals(node, Empty); } + + /// + /// Recognizes zero-or-more repetitions of one non-negated Any atom, including lazy + /// repetition. Callers inspect the COUNT payload's IsGreedy flag separately. + /// + public static bool IsDotStar(RegexAstNode? node) + { + if (node?.Payload is not RegexCountPayload { IsStar: true }) { + return false; + } + + bool left_empty = IsNullOrEmpty(node.Left); + bool right_empty = IsNullOrEmpty(node.Right); + if (left_empty == right_empty) { + return false; + } + + RegexAstNode operand = left_empty ? node.Right : node.Left; + return operand.Payload is RegexCharSetPayload { IsAny: true, Accept: true } set && + set.Set[0].Accept; + } /**/ public void PrependNode(RegexAstNode previousNode) diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.ExitLabel.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.ExitLabel.cs new file mode 100644 index 00000000..766bd8e7 --- /dev/null +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.ExitLabel.cs @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------------- +// file="TextRegexSourceGen.ExitLabel" +// Project: StgSharp +// Copyright (c) Nitload. +// SPDX-License-Identifier: MIT +// ----------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nitload.RegularAnalysis.Text +{ + internal partial class TextRegexSourceGen + { + + internal enum ExitMode + { + + Break, + Continue, + Goto, + + } + + internal readonly record struct ExitLabel + { + + public ExitLabel( + ExitMode mode, + int order + ) + { + Mode = mode; + LabelOrder = order; + } + + public ExitMode Mode { get; init; } + + public int LabelOrder { get; init; } + + public string GetLabelName() + { + return Mode switch + { + ExitMode.Break => $@"__break_{LabelOrder}", + ExitMode.Continue => $@"__continue_{LabelOrder}", + ExitMode.Goto => $@"__goto_{LabelOrder}", + _ => throw new NotImplementedException(), + }; + } + + } + + } +} diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs index 7248cf27..26da0a18 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs @@ -7,15 +7,7 @@ using Microsoft.CodeAnalysis.CSharp; using Nitload.RegularAnalysis.Abstraction; -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection.Metadata.Ecma335; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace Nitload.RegularAnalysis.Text { @@ -27,21 +19,24 @@ namespace Nitload.RegularAnalysis.Text in SequenceEmitter sc, in List> func_list, in SourceGenContext context, + in ExitLabel exit_label = default, bool is_find = false - ) { RegexAstNode root = ast; if (root.Label == RegexElementLabel.EPSILON) { return; } + if (TryGenerateSpecialSource(root, sc, func_list, context, exit_label)) { + return; + } RegexElementLabel op = root.Label & (RegexElementLabel.VAST_OPERATOR); if (op != 0) { switch (op) { case RegexElementLabel.CONCAT: - GenerateConcat(root, sc, func_list, context); + GenerateConcat(root, sc, func_list, context, exit_label); break; case RegexElementLabel.COUNT: GenerateCount(root, sc, func_list, context); @@ -64,25 +59,20 @@ namespace Nitload.RegularAnalysis.Text switch (op) { case RegexElementLabel.UNIT: - - // TODO(NGRA): Emit the remaining-span length guard, literal comparison, - // cursor advance, and failure transfer for one character. + GenerateUnit(root, sc, context, exit_label); break; case RegexElementLabel.UNIT_SET: - - // TODO(NGRA): Emit the remaining-span length guard, charset predicate, - // cursor advance, and failure transfer. + GenerateUnitSet(root, sc, context, exit_label); break; case RegexElementLabel.UNIT_SPAN: - - // TODO(NGRA): Emit StartsWith for the complete literal span and advance by - // the literal length; integrate is_find candidate retry semantics. + GenerateUnitSpan(root, sc, context, exit_label); break; default: - - // TODO(NGRA): Diagnose malformed leaf nodes instead of generating no code. break; } + + // TODO(NGRA): Integrate is_find candidate search and continuation retries. + // Leaf emission currently matches only at the active cursor. } } @@ -120,221 +110,105 @@ namespace Nitload.RegularAnalysis.Text case RegexCharSetType.Single: return $"{accept}({FormatStringLiteral(set.Value)}.{nameof(MemoryExtensions.Contains)}({remain_span}[0]))"; case RegexCharSetType.Range: + if (set.Value.Length != 2) { + throw new InvalidOperationException("A charset range requires two endpoints."); + } return $"{accept}({remain_span}[0] >= {FormatCharLiteral(set.Value[0])} && {remain_span}[0] <= {FormatCharLiteral(set.Value[1])})"; case RegexCharSetType.Set: - switch (set.Value[0]) + switch (set.Value) { - case 's': + case "s": return $"{accept}char.IsWhiteSpace({remain_span}[0])"; - case 'w': - return $"{accept}{nameof(TextRegex.IsCharWord)}({remain_span}[0])"; - case 'd': + case "w": + return $"{accept}{Text_Regex}.{nameof(TextRegex.IsCharWord)}({remain_span}[0])"; + case "d": return $"{accept}char.IsDigit({remain_span}[0])"; default: break; } goto default; + case RegexCharSetType.Any: + return set.Accept ? "true" : "false"; default: - - // TODO(NGRA): Unsupported/empty charset rules must become an analyzer - // diagnostic; returning an empty expression produces invalid generated C#. - return string.Empty; + throw new NotSupportedException($"Unsupported charset rule: {set.Type} ({set.Value})."); } } - private static bool IsNamedFind( - RegexAstNode node - ) - { - // (?.*)abc - - if (node is null) - { - return false; - } - if ((node.Label & RegexElementLabel.CONCAT) != 0) { - return false; - } - RegexAstNode group = node.Left; - - // TODO(NGRA): Validate the normalized CONCAT/GROUP/COUNT shape with Empty sentinels - // before dereferencing children; this helper currently assumes every edge exists. - if ((group.Label & RegexElementLabel.GROUP_BEGIN) == 0) - { - return false; - } - RegexAstNode count = group.Right; - - // .* - return (count.Label & RegexElementLabel.COUNT) != 0 && - (count.Right.Label & RegexElementLabel.UNIT_SINGLE) != 0 && - count.Right.Source.Source == "."; - } - #endregion - #region basic verb generate + #region UNIT direct matching - private static void GenerateAlt( - RegexAstNode node, + private static void GenerateUnit( + RegexAstNode value, [NotNull] in SequenceEmitter sc, - in List> func_list, - in SourceGenContext context + in SourceGenContext context, + in ExitLabel exit ) { - RegexAstNode current = node.Right; - string exit_label = context.RequestExitLabel(); - - // TODO(NGRA): Walk every normalized ALT candidate exactly once. The final traversal - // shape and failure continuation are not wired yet. - while (RegexAstNode.IsNullOrEmpty(current)) - { - RegexElementLabel op = current.Label & RegexElementLabel.SEQUENCE; - if (op != 0) - { - string code = current.SourceCode; - switch (op) - { - case RegexElementLabel.UNIT: - - // TODO(NGRA): Guard against an empty remaining span before indexing it. - _ = sc.AppendLine(@$"if({context.RemainSpan}[0] == {FormatCharLiteral(code[0])})") - .AppendLine("{") - .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);") - .AppendLine($@"goto {exit_label};") - .AppendLine("}"); - break; - case RegexElementLabel.UNIT_SET: - - // TODO(NGRA): Guard the remaining span, handle empty/Any sets, and - // validate negated-set composition before emitting this predicate. - RegexCharSetPayload payload = current.PayloadAs(); - IReadOnlyList split = payload.Set; - bool accept = payload.Accept; - RegexCharSet _set = split[0]; - _ = sc.AppendLine($"if({(accept ? string.Empty : "!(")} {GenerateCharSetExpression(_set)} ||"); - for (int i = 1; i < split.Count - 1; i++) - { - _set = split[i]; - _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ||"); - } - _set = split[^1]; - _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ){(accept ? string.Empty : ')')}") - .AppendLine("{") - .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);") - .AppendLine($@"goto {exit_label};") - .AppendLine("}"); - break; - case RegexElementLabel.UNIT_SPAN: - - // TODO(NGRA): Advance by code.Length rather than one character and - // preserve the ALT rollback cursor on failure. - _ = sc.AppendLine(@$"if({context.RemainSpan}.{MemoryExtensions.StartsWith}({FormatStringLiteral(code)}))") - .AppendLine("{") - .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);") - .AppendLine($@"goto {exit_label};") - .AppendLine("}"); - - break; - default: - - // TODO(NGRA): Route unsupported ALT candidates through the recursive - // matcher or report a source-generation diagnostic. - break; - } - - // TODO(NGRA): Advance to the next ALT candidate after a scalar candidate - // misses; the current sequence branch does not update current. - } else - { - GenerateMethodSource(current, sc, func_list, context); - current = current.Next; - } + string literal = value.Value; + if (literal.Length != 1) { + throw new InvalidOperationException( + $"UNIT requires one decoded character, but received {literal.Length}."); } - // TODO(NGRA): Emit the all-alternatives-failed continuation before the shared success - // label, including restoration of the entry cursor and capture state. - _ = sc.AppendLine($@"{exit_label}: {{ }}"); + string remain_span = context.RemainSpan; + _ = sc.AppendLine($"if ({remain_span}.IsEmpty || {remain_span}[0] != {FormatCharLiteral(literal[0])})") + .AppendLine("{") + .AppendLine($"goto {exit.GetLabelName()};") + .AppendLine("}") + .AppendLine($"{remain_span} = {remain_span}.Slice(1);"); } - private static void GenerateGroup( - RegexAstNode node, + private static void GenerateUnitSet( + RegexAstNode value, [NotNull] in SequenceEmitter sc, - in List> func_list, - in SourceGenContext context + in SourceGenContext context, + in ExitLabel exit ) { - RegexAstNode left = node.Left; - RegexAstNode right = node.Right; - - RegexGroupPayload config = node.PayloadAs(); - - // TODO(NGRA): Select the owned group body using Empty-sentinel checks, snapshot its - // start cursor, and restore it when the child matcher fails. - if (left is not null) - { - GenerateMethodSource(left, sc, func_list, context); - } else - if (right is not null) { - GenerateMethodSource(right, sc, func_list, context); + RegexCharSetPayload payload = value.PayloadAs(); + string[] rules = new string[payload.Set.Count]; + for (int i = 0; i < rules.Length; i++) { + rules[i] = GenerateCharSetExpression(payload.Set[i]); } - string group_name = config.Name; - - // TODO(NGRA): Allocate numeric/named capture slots and emit the successful capture - // span. Non-capturing groups still need a rollback scope but no stored capture. - throw new NotImplementedException(); + // Combine rule-level complements before applying the enclosing charset complement. + // An empty union accepts nothing; its complement accepts any available character. + string predicate = rules.Length == 0 ? "false" : string.Join(" || ", rules); + string mismatch = payload.Accept ? $"!({predicate})" : $"({predicate})"; + string remain_span = context.RemainSpan; + _ = sc.AppendLine($"if ({remain_span}.IsEmpty || {mismatch})") + .AppendLine("{") + .AppendLine($"goto {exit.GetLabelName()};") + .AppendLine("}") + .AppendLine($"{remain_span} = {remain_span}.Slice(1);"); } - private static void GenerateConcat( - RegexAstNode node, + private static void GenerateUnitSpan( + RegexAstNode value, [NotNull] in SequenceEmitter sc, - in List> func_list, - in SourceGenContext context + in SourceGenContext context, + in ExitLabel exit ) { - RegexAstNode left = node.Left; - RegexAstNode right = node.Right; - - if (left is not null) - { - // find - if ((left.Label & RegexElementLabel.COUNT) == RegexElementLabel.COUNT && - (left.Right.Label & RegexElementLabel.UNIT_SINGLE) == RegexElementLabel.UNIT_SINGLE && - left.Right.Source.Source == "." && - right is not null) - { - // TODO(NGRA): Emit the unnamed FIND candidate loop and retry the right subtree - // until it succeeds or no anchor candidate remains. - GenerateMethodSource(right, sc, func_list, context, true); - } else if (IsNamedFind(node)) - { - if (right is not null) - { - // TODO(NGRA): Emit named FIND retry and capture the text preceding the - // successful anchor candidate. - GenerateMethodSource(right, sc, func_list, context, true); - } else - { - // TODO(NGRA): Capture the remaining input for a terminal named dot-star. - } - } else - { - GenerateMethodSource(left, sc, func_list, context); - if (right is not null) { - GenerateMethodSource(right, sc, func_list, context); - } - } + string literal = value.Value; + if (literal.Length == 0) { + throw new InvalidOperationException("UNIT_SPAN requires a non-empty literal."); } - // TODO(NGRA): Normalize ownership so the right subtree is emitted exactly once. It is - // currently reachable both inside the ordinary CONCAT branch and here. - if (right is not null) - { - GenerateMethodSource(right, sc, func_list, context); - } + // StartsWith also rejects input shorter than the decoded literal. + string remain_span = context.RemainSpan; + _ = sc.AppendLine($"if (!{remain_span}.{nameof(MemoryExtensions.StartsWith)}({FormatStringLiteral(literal)}))") + .AppendLine("{") + .AppendLine($"goto {exit.GetLabelName()};") + .AppendLine("}") + .AppendLine($"{remain_span} = {remain_span}.Slice({literal.Length});"); } + #endregion + + #region UNIT count matching + private const int flattened_unit_count_threshold = 32; private static void GenerateUnitCount( @@ -343,7 +217,8 @@ namespace Nitload.RegularAnalysis.Text int max, bool is_greedy, [NotNull] in SequenceEmitter sc, - in SourceGenContext context + in SourceGenContext context, + in ExitLabel exit ) { if (min < 0 || (max >= 0 && max < min)) { @@ -359,16 +234,18 @@ namespace Nitload.RegularAnalysis.Text char expected = literal[0]; string expected_literal = FormatCharLiteral(expected); string remain_span = context.RemainSpan; - int scratch_scope_index = context.RentScratchScopeIndex(); + + // TODO(NGRA-NAMING): Allocate required_rest, required_matched, required_chunk_index, + // and additional_count through the exclusive-name pool, including loop-local names. + int scratch_scope_index = context.RequestScratchScopeIndex(); + string exit_label_node = exit.GetLabelName(); string required_rest = $"__required_rest_{scratch_scope_index}"; string required_matched = $"__required_matched_{scratch_scope_index}"; string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}"; string additional_count = $"__additional_count_{scratch_scope_index}"; - // TODO(NGRA): Move ownership of this work region to the enclosing CONCAT or match - // method. Until then, break demonstrates the local failure path but cannot prevent a - // later sibling node from being emitted and executed. - _ = sc.AppendLine("do").AppendLine("{"); + // TODO(NGRA): Replace this local COUNT exit with the inherited failure ExitLabel. + // The temporary goto preserves the former local exit but still lets siblings execute. if (min <= flattened_unit_count_threshold) { @@ -377,7 +254,7 @@ namespace Nitload.RegularAnalysis.Text string required_prefix = new(expected, min); _ = sc.AppendLine($"if (!{remain_span}.{nameof(MemoryExtensions.StartsWith)}({FormatStringLiteral(required_prefix)}))") .AppendLine("{") - .AppendLine("break;") + .AppendLine($"goto {exit_label_node};") .AppendLine("}") .AppendLine($"{remain_span} = {remain_span}.Slice({min});"); } @@ -392,7 +269,7 @@ namespace Nitload.RegularAnalysis.Text _ = sc.AppendLine($"if ({remain_span}.Length < {min})") .AppendLine("{") - .AppendLine("break;") + .AppendLine($"goto {exit_label_node};") .AppendLine("}") .AppendLine($"{ROS_char} {required_rest} = {remain_span};") .AppendLine($"bool {required_matched} = true;") @@ -417,7 +294,7 @@ namespace Nitload.RegularAnalysis.Text _ = sc.AppendLine($"if (!{required_matched})") .AppendLine("{") - .AppendLine("break;") + .AppendLine($"goto {exit_label_node};") .AppendLine("}") .AppendLine($"{remain_span} = {remain_span}.Slice({min});"); } @@ -425,28 +302,21 @@ namespace Nitload.RegularAnalysis.Text int additional_limit = max < 0 ? -1 : max - min; if (is_greedy && additional_limit != 0) { - if (additional_limit < 0) - { - _ = sc.AppendLine($"while (!{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal})") - .AppendLine("{") - .AppendLine($"{remain_span} = {remain_span}.Slice(1);") - .AppendLine("}"); - } else - { - _ = sc.AppendLine($"for (int {additional_count} = 0; {additional_count} < {additional_limit} && !{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal}; {additional_count}++)") - .AppendLine("{") - .AppendLine($"{remain_span} = {remain_span}.Slice(1);") - .AppendLine("}"); - } + _ = additional_limit < 0 ? + sc.AppendLine($"while (!{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal})") + .AppendLine("{") + .AppendLine($"{remain_span} = {remain_span}.Slice(1);") + .AppendLine("}") : + sc.AppendLine($"for (int {additional_count} = 0; {additional_count} < {additional_limit} && !{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal}; {additional_count}++)") + .AppendLine("{") + .AppendLine($"{remain_span} = {remain_span}.Slice(1);") + .AppendLine("}"); } else if (!is_greedy && additional_limit != 0) { // Lazy COUNT deliberately stops at Min. Its optional tail is consumed only when // a future continuation attempt fails and requests another repetition. _ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation."); } - - _ = sc.AppendLine("} while (false);"); - context.ReturnScratchScopeIndex(scratch_scope_index); } private static void GenerateUnitSetCount( @@ -455,7 +325,8 @@ namespace Nitload.RegularAnalysis.Text int max, bool is_greedy, [NotNull] in SequenceEmitter sc, - in SourceGenContext context + in SourceGenContext context, + in ExitLabel exit_label ) { if (min < 0 || (max >= 0 && max < min)) { @@ -472,34 +343,54 @@ namespace Nitload.RegularAnalysis.Text IReadOnlyList charset = payload.Set; string remain_span = context.RemainSpan; - int scratch_scope_index = context.RentScratchScopeIndex(); + + // TODO(NGRA-NAMING): Allocate required_matched and additional_count through the + // exclusive-name pool. Reserve required_rest/required_chunk_index if emitted later. + int scratch_scope_index = context.RequestScratchScopeIndex(); string required_rest = $"__required_rest_{scratch_scope_index}"; string required_matched = $"__required_matched_{scratch_scope_index}"; string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}"; string additional_count = $"__additional_count_{scratch_scope_index}"; - // TODO(NGRA): Move ownership of this work region to the enclosing CONCAT or match - // method. Until then, break demonstrates the local failure path but cannot prevent a - // later sibling node from being emitted and executed. + // TODO(NGRA): Route a failed minimum check through the inherited failure ExitLabel. + // The break below only stops charset scanning and must remain local to that loop. bool accept = payload.Accept; RegexCharSet _set = charset[0]; _ = sc.AppendLine($"{ROS_char} {required_matched} = {remain_span}") .AppendLine($"int {additional_count} = 0;") - .AppendLine() - .AppendLine() //TODO loop head - .AppendLine($"if({(accept ? string.Empty : "!(")} {GenerateCharSetExpression(_set)} ||"); - for (int i = 1; i < charset.Count - 1; i++) + .AppendLine($"for(; {additional_count} < {max}; {additional_count} ++)") + .AppendLine("{"); + if (charset.Count == 1) { - _set = charset[i]; - _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ||"); + // single charset rule + _ = sc.AppendLine($"if({(accept ? string.Empty : "!(")} {GenerateCharSetExpression(_set)})"); + } else + { + // multi charset rule + _ = sc.AppendLine($"if({(accept ? string.Empty : "!(")} {GenerateCharSetExpression(_set)} ||"); + for (int i = 1; i < charset.Count - 1; i++) + { + _set = charset[i]; + _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ||"); + } + _set = charset[^1]; + _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ){(accept ? string.Empty : ')')}"); } - _set = charset[^1]; - _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ){(accept ? string.Empty : ')')}") - .AppendLine("{") - .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);") - .AppendLine($@"break;") - .AppendLine("}"); + _ = sc .AppendLine("{") + .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);") + .AppendLine("}") + .AppendLine("else") + .AppendLine("{") + .AppendLine($@"break;") + .AppendLine("}") + .AppendLine("}") + .AppendLine($"if({additional_count} < {min})") + .AppendLine("{") + .AppendLine("}") + .AppendLine("else") + .AppendLine("{") + .AppendLine("}"); } private static void GenerateUnitSpanCount( @@ -508,7 +399,8 @@ namespace Nitload.RegularAnalysis.Text int max, bool is_greedy, [NotNull] in SequenceEmitter sc, - in SourceGenContext context + in SourceGenContext context, + in ExitLabel exit ) { if (min < 0 || (max >= 0 && max < min)) { @@ -523,7 +415,12 @@ namespace Nitload.RegularAnalysis.Text string expected_literal = FormatStringLiteral(literal); string remain_span = context.RemainSpan; - int scratch_scope_index = context.RentScratchScopeIndex(); + + // TODO(NGRA-NAMING): Allocate required_rest, required_matched, required_chunk_index, + // required_count, and additional_count through the exclusive-name pool. Flattened + // sibling COUNT nodes share the declaration space even after their exit labels. + int scratch_scope_index = context.RequestScratchScopeIndex(); + string exit_label_node = exit.GetLabelName(); string required_rest = $"__required_rest_{scratch_scope_index}"; string required_matched = $"__required_matched_{scratch_scope_index}"; string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}"; @@ -531,10 +428,8 @@ namespace Nitload.RegularAnalysis.Text string additional_count = $"__additional_count_{scratch_scope_index}"; string starts_with = $"{nameof(MemoryExtensions.StartsWith)}"; - // TODO(NGRA): Move ownership of this work region to the enclosing CONCAT or match - // method. Until then, break demonstrates the local failure path but cannot prevent a - // later sibling node from being emitted and executed. - _ = sc.AppendLine("do").AppendLine("{"); + // TODO(NGRA): Replace this local COUNT exit with the inherited failure ExitLabel. + // The temporary goto preserves the former local exit but still lets siblings execute. if (literal.Length > flattened_unit_count_threshold) { @@ -549,7 +444,7 @@ namespace Nitload.RegularAnalysis.Text .AppendLine("}") .AppendLine($"if ({required_count} != {min})") .AppendLine("{") - .AppendLine("break;") + .AppendLine($"goto {exit_label_node};") .AppendLine("}") .AppendLine($"{remain_span} = {required_rest};"); } else if (min <= flattened_unit_count_threshold / literal.Length) @@ -563,7 +458,7 @@ namespace Nitload.RegularAnalysis.Text string required_prefix = required_prefix_span.ToString(); _ = sc.AppendLine($"if (!{remain_span}.{starts_with}({FormatStringLiteral(required_prefix)}))") .AppendLine("{") - .AppendLine("break;") + .AppendLine($"goto {exit_label_node};") .AppendLine("}") .AppendLine($"{remain_span} = {remain_span}.Slice({min * literal.Length});"); } @@ -612,7 +507,7 @@ namespace Nitload.RegularAnalysis.Text _ = sc.AppendLine($"if (!{required_matched})") .AppendLine("{") - .AppendLine("break;") + .AppendLine($"goto {exit_label_node};") .AppendLine("}") .AppendLine($"{remain_span} = {required_rest};"); } @@ -639,9 +534,151 @@ namespace Nitload.RegularAnalysis.Text // a future continuation attempt fails and requests another repetition. _ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation."); } + } - _ = sc.AppendLine("} while (false);"); - context.ReturnScratchScopeIndex(scratch_scope_index); + #endregion + + #region UNIT find matching + + // TODO(NGRA): Add UNIT, UNIT_SET, and UNIT_SPAN candidate search emitters here. + // Wire is_find dispatch and continuation retries when FIND matching is implemented. + + #endregion + + #region Operator generation + + private static void GenerateAlt( + RegexAstNode node, + [NotNull] in SequenceEmitter sc, + in List> func_list, + in SourceGenContext context + ) + { + RegexAstNode current = node.Right; + + // TODO(NGRA-NAMING): Reserve exclusive names for future ALT cursor/capture snapshots; + // their lifetime extends through retry labels, not just the candidate's emitted block. + ExitLabel exit_label = context.RequestExitLabel(ExitMode.Goto); + + // TODO(NGRA): Walk every normalized ALT candidate exactly once. The final traversal + // shape and failure continuation are not wired yet. + while (RegexAstNode.IsNullOrEmpty(current)) + { + RegexElementLabel op = current.Label & RegexElementLabel.SEQUENCE; + if (op != 0) + { + string code = current.SourceCode; + switch (op) + { + case RegexElementLabel.UNIT: + + // TODO(NGRA): Guard against an empty remaining span before indexing it. + _ = sc.AppendLine(@$"if({context.RemainSpan}[0] == {FormatCharLiteral(code[0])})") + .AppendLine("{") + .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);") + .AppendLine($@"goto {exit_label};") + .AppendLine("}"); + break; + case RegexElementLabel.UNIT_SET: + + // TODO(NGRA): Guard the remaining span, handle empty/Any sets, and + // validate negated-set composition before emitting this predicate. + RegexCharSetPayload payload = current.PayloadAs(); + IReadOnlyList split = payload.Set; + bool accept = payload.Accept; + RegexCharSet _set = split[0]; + _ = sc.AppendLine($"if({(accept ? string.Empty : "!(")} {GenerateCharSetExpression(_set)} ||"); + for (int i = 1; i < split.Count - 1; i++) + { + _set = split[i]; + _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ||"); + } + _set = split[^1]; + _ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ){(accept ? string.Empty : ')')}") + .AppendLine("{") + .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);") + .AppendLine($@"goto {exit_label};") + .AppendLine("}"); + break; + case RegexElementLabel.UNIT_SPAN: + + // TODO(NGRA): Advance by code.Length rather than one character and + // preserve the ALT rollback cursor on failure. + string literal = FormatStringLiteral(code); + _ = sc.AppendLine(@$"if({context.RemainSpan}.{MemoryExtensions.StartsWith}({literal}))") + .AppendLine("{") + .AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice({code.Length});") + .AppendLine($@"goto {exit_label};") + .AppendLine("}"); + + break; + default: + + // TODO(NGRA): Route unsupported ALT candidates through the recursive + // matcher or report a source-generation diagnostic. + break; + } + + // TODO(NGRA): Advance to the next ALT candidate after a scalar candidate + // misses; the current sequence branch does not update current. + } else + { + GenerateMethodSource(current, sc, func_list, context); + current = current.Next; + } + } + + // TODO(NGRA): Emit the all-alternatives-failed continuation before the shared success + // label, including restoration of the entry cursor and capture state. + _ = sc.AppendLine($@"{exit_label}: {{ }}"); + } + + private static void GenerateGroup( + RegexAstNode node, + [NotNull] in SequenceEmitter sc, + in List> func_list, + in SourceGenContext context + ) + { + RegexAstNode left = node.Left; + RegexAstNode right = node.Right; + + RegexGroupPayload config = node.PayloadAs(); + + // TODO(NGRA-NAMING): Reserve exclusive names for generated group snapshots and capture + // temporaries. A capture's user-provided name is not a unique generated local name. + + // TODO(NGRA): Select the owned group body using Empty-sentinel checks, snapshot its + // start cursor, and restore it when the child matcher fails. + if (left is not null) + { + GenerateMethodSource(left, sc, func_list, context); + } else + if (right is not null) { + GenerateMethodSource(right, sc, func_list, context); + } + + string group_name = config.Name; + + // TODO(NGRA): Allocate numeric/named capture slots and emit the successful capture + // span. Non-capturing groups still need a rollback scope but no stored capture. + throw new NotImplementedException(); + } + + private static void GenerateConcat( + RegexAstNode node, + [NotNull] in SequenceEmitter sc, + in List> func_list, + in SourceGenContext context, + in ExitLabel exit_label = default + ) + { + if (!RegexAstNode.IsNullOrEmpty(node.Left)) { + GenerateMethodSource(node.Left, sc, func_list, context, exit_label); + } + if (!RegexAstNode.IsNullOrEmpty(node.Right)) { + GenerateMethodSource(node.Right, sc, func_list, context, exit_label); + } } private static void GenerateCount( @@ -659,7 +696,10 @@ namespace Nitload.RegularAnalysis.Text int min = payload.Min; int max = payload.Max; bool is_greedy = payload.IsGreedy; - string exit_label = context.RequestExitLabel(); + + // TODO(NGRA-NAMING): Reserve exclusive names for future repetition/rollback state + // across complex COUNT retries; recursive children must use the same method pool. + ExitLabel exit_label = context.RequestExitLabel(ExitMode.Goto); if ((value.Label & RegexElementLabel.SEQUENCE) == 0) { // TODO(NGRA): Emit min/max repetition, zero-width progress protection, greedy vs. @@ -670,15 +710,13 @@ namespace Nitload.RegularAnalysis.Text switch (value.Label & RegexElementLabel.SEQUENCE) { case RegexElementLabel.UNIT: - GenerateUnitCount(value, min, max, is_greedy, sc, context); + GenerateUnitCount(value, min, max, is_greedy, sc, context, exit_label); break; case RegexElementLabel.UNIT_SET: - - // TODO(NGRA): Emit a bounded/unbounded charset counting loop, preferably - // using IndexOfAnyExcept where the charset shape permits it. + GenerateUnitSetCount(value, min, max, is_greedy, sc, context, exit_label); break; case RegexElementLabel.UNIT_SPAN: - GenerateUnitSpanCount(value, min, max, is_greedy, sc, context); + GenerateUnitSpanCount(value, min, max, is_greedy, sc, context, exit_label); break; default: diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs index 5c594c7c..b7642b11 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs @@ -26,8 +26,8 @@ namespace Nitload.RegularAnalysis.Text /// /// Method level state is limited to the active cursor and the result of the most recent /// instruction. Everything else is either a region slot, a line slot, a capture slot, or - /// a block scoped scratch variable. Scratch indices remain unique while rented and are - /// recycled after their generated lexical scope has closed. + /// a scratch variable. Scratch indices remain unique throughout the generated method; + /// flattened regions cannot release their names for reuse by later nodes. /// internal sealed class SourceGenContext { @@ -36,12 +36,10 @@ namespace Nitload.RegularAnalysis.Text public const string _remain_span = "__remain"; - private int _exit_alt_count; + private int _max_exit_lable; private int _scratch_scope_count; - private readonly Stack _free_scratch_scope_indices = []; - public string RemainSpan { get; } = _remain_span; public string FirstOfRemain { get; } = $@"{_remain_span}[0]"; @@ -55,38 +53,24 @@ namespace Nitload.RegularAnalysis.Text return sc.AppendLine($@"{ROS_char} {RemainSpan};"); } - public string RequestExitLabel() + public ExitLabel RequestExitLabel( + ExitMode exitMode + ) { - string code = $@"_exit_alt_{_exit_alt_count}"; - _exit_alt_count++; - return code; + int index = _max_exit_lable; + _max_exit_lable++; + return new(exitMode, index); } - public int RentScratchScopeIndex() + public int RequestScratchScopeIndex() { - if (_free_scratch_scope_indices.TryPop(out int recycled_index)) { - return recycled_index; - } - + // TODO(NGRA-NAMING): Integrate scratch names with the method-wide exclusive-name + // allocator. Until then, reserve each suffix permanently for this method. int index = _scratch_scope_count; _scratch_scope_count++; return index; } - public void ReturnScratchScopeIndex( - int index - ) - { - if ((uint)index >= (uint)_scratch_scope_count) { - throw new ArgumentOutOfRangeException(nameof(index)); - } - if (_free_scratch_scope_indices.Contains(index)) { - throw new InvalidOperationException($"Scratch scope {index} was returned twice."); - } - - _free_scratch_scope_indices.Push(index); - } - } } diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenFile.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenFile.cs index 1eb88613..3bb41635 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenFile.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenFile.cs @@ -60,7 +60,7 @@ namespace Nitload.RegularAnalysis.Text string static_modifier; int brace_count = 0; - do + while (type is not null) { _ = file_name_sb.Append(CultureInfo.InvariantCulture, $@"{type.TypeName}."); string declare_type = type.DeclType switch @@ -74,7 +74,7 @@ namespace Nitload.RegularAnalysis.Text .AppendLine("{"); brace_count++; type = type.InnerType!; - } while (type is not null); + } static_modifier = gen_source.IsMethodStatic ? "static" : string.Empty; _ = ce.AppendLine(@$"{ToKeyword(gen_source.MethodAccessibility)} {static_modifier} {Text_Regex} {gen_source.MethodName}({ROS_char} {regex_gen_context.InputString})") @@ -90,8 +90,7 @@ namespace Nitload.RegularAnalysis.Text brace_count = 0; foreach (string line in ce) { - bool is_right_brace = (line == "}") || - line.StartsWith("} while", StringComparison.InvariantCulture); + bool is_right_brace = line == "}"; brace_count -= is_right_brace ? 1 : 0; for (int i = 0; i < brace_count; i++) { _ = code_source.Append(" "); diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs new file mode 100644 index 00000000..1fbeb591 --- /dev/null +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs @@ -0,0 +1,111 @@ +// ----------------------------------------------------------------------------- +// file="TextRegexSourceGen.GenSpecial" +// Project: Nitload +// Copyright (c) Nitload. +// SPDX-License-Identifier: MIT +// ----------------------------------------------------------------------------- + +using Nitload.RegularAnalysis.Abstraction; + +namespace Nitload.RegularAnalysis.Text +{ + internal partial class TextRegexSourceGen + { + + private static bool TryGenerateSpecialSource( + RegexAstNode node, + in SequenceEmitter sc, + in List> func_list, + in SourceGenContext context, + in ExitLabel exit_label + ) + { + // A special rule owns the entire matched subtree. Returning false leaves emission + // to the general path; returning true prevents that subtree from being emitted twice. + return node.Label switch + { + RegexElementLabel.CONCAT => TryGenerateFindConcat(node, sc, func_list, context, exit_label), + _ => false, + }; + } + + #region FIND concatenation + + private static bool TryGenerateFindConcat( + RegexAstNode node, + in SequenceEmitter sc, + in List> func_list, + in SourceGenContext context, + in ExitLabel exit_label + ) + { + RegexAstNode left = node.Left; + RegexAstNode right = node.Right; + if (RegexAstNode.IsNullOrEmpty(left)) { + return false; + } + + // Existing FIND dispatch scaffold; candidate search and retries are not implemented. + // TODO(NGRA): Use the COUNT payload's IsGreedy flag to order FIND candidates. + if (RegexAstNode.IsDotStar(left) && !RegexAstNode.IsNullOrEmpty(right)) + { + // TODO(NGRA): Emit the unnamed FIND candidate loop and retry the right subtree + // until it succeeds or no anchor candidate remains. + GenerateMethodSource(right, sc, func_list, context, exit_label, true); + return true; + } + + if (IsNamedFind(node)) + { + if (!RegexAstNode.IsNullOrEmpty(right)) + { + // TODO(NGRA): Emit named FIND retry and capture the text preceding the + // successful anchor candidate. + GenerateMethodSource(right, sc, func_list, context, exit_label, true); + } else + { + // TODO(NGRA): Capture the remaining input for a terminal named dot-star. + } + return true; + } + + return false; + } + + private static bool IsNamedFind( + RegexAstNode node + ) + { + // (?.*)abc + + if (RegexAstNode.IsNullOrEmpty(node) || node.Label != RegexElementLabel.CONCAT) { + return false; + } + RegexAstNode group = node.Left; + if (RegexAstNode.IsNullOrEmpty(group) || + group.Payload is not RegexGroupPayload { IsCapturing: true } config || + string.IsNullOrEmpty(config.Name)) { + return false; + } + + bool left_empty = RegexAstNode.IsNullOrEmpty(group.Left); + bool right_empty = RegexAstNode.IsNullOrEmpty(group.Right); + return left_empty != right_empty && + RegexAstNode.IsDotStar(left_empty ? group.Right : group.Left); + } + + #endregion + + #region Repeated capture rules + + // TODO(NGRA): Recognize and implement repeated captures such as (?.*){m,n} + // and (?.*a){m,n}. Keep these distinct from (?.*)a FIND concatenation. + // A later repetition may need to reopen an earlier repetition's inner search, restoring + // its cursor, candidate position, and capture state. Do not claim these subtrees in + // TryGenerateSpecialSource until the complete retry/rollback strategy is implemented. + // An escaped star is a literal and must not be classified as dot-star repetition. + + #endregion + + } +} diff --git a/src/README.md b/src/README.md index 30cc5ca6..c18375f8 100644 --- a/src/README.md +++ b/src/README.md @@ -1,22 +1,22 @@ # Source Projects -This directory contains the production source code, native runtime, supporting applications, and benchmarks for StgSharp. Most managed projects target .NET 8. The native project is built separately with CMake and provides platform and SIMD functionality consumed by managed projects. +This directory contains the production source code, native runtime, supporting applications, and benchmarks for Nitload. Most managed projects target .NET 8. The native project is built separately with CMake and provides platform and SIMD functionality consumed by managed projects. ## Directory map | Directory | Purpose | | --- | --- | -| `StgSharp.Common` | Shared collections, memory management, threading, pipelines, platform abstractions, and other foundational APIs. | -| `StgSharp.Mathematics` | Numeric types, vectors, matrices, geometry, and managed access to accelerated math operations. | -| `StgSharp.Graphics` | OpenGL bindings, rendering infrastructure, shaders, textures, windows, and input integration. | -| `StgSharp.Native` | C native runtime, GLFW/STBI integration, allocators, platform code, and optimized SIMD/matrix kernels. See its own `README.md` for native build details. | -| `StgSharp.RegularAnalysis` | NGRA regular-language analysis, regex processing, intermediate representations, and source-generation infrastructure. | -| `StgSharp.UserInterface` | State-oriented user-interface framework and its incremental source generator. | -| `StgSharp.TerminalDialogue` | Terminal user-interface components and an interactive terminal application. | -| `StgSharp.Benchmark` | BenchmarkDotNet workloads for measuring selected runtime and allocation behavior. | -| `StgSharp.Script` | Retained sources from the earlier EXPRESS implementation. The planned Script layer has not started and is expected to build on NGRA. | -| `StgSharp.Model` | Placeholder project whose design is being reconsidered. | -| `StgSharp.Tools` | Small retained comparison and inspection artifacts; it is not currently a buildable project. | +| `Nitload.Common` | Shared collections, memory management, threading, pipelines, platform abstractions, and other foundational APIs. | +| `Nitload.Mathematics` | Numeric types, vectors, matrices, geometry, and managed access to accelerated math operations. | +| `Nitload.Graphics` | OpenGL bindings, rendering infrastructure, shaders, textures, windows, and input integration. | +| `Nitload.Native` | C native runtime, GLFW/STBI integration, allocators, platform code, and optimized SIMD/matrix kernels. See its own `README.md` for native build details. | +| `Nitload.RegularAnalysis` | NGRA regular-language analysis, regex processing, intermediate representations, and source-generation infrastructure. | +| `Nitload.UserInterface` | State-oriented user-interface framework and its incremental source generator. | +| `Nitload.TerminalDialogue` | Terminal user-interface components and an interactive terminal application. | +| `Nitload.Benchmark` | BenchmarkDotNet workloads for measuring selected runtime and allocation behavior. | +| `Nitload.Script` | Retained sources from the earlier EXPRESS implementation. The planned Script layer has not started and is expected to build on NGRA. | +| `Nitload.Model` | Placeholder project whose design is being reconsidered. | +| `Nitload.Tools` | Small retained comparison and inspection artifacts; it is not currently a buildable project. | ## Build notes diff --git a/tools/Nitload.GenerateGL/Generation/GlOverloadApiFilter.cs b/tools/Nitload.GenerateGL/Generation/GlOverloadApiFilter.cs index f9783252..6f1729af 100644 --- a/tools/Nitload.GenerateGL/Generation/GlOverloadApiFilter.cs +++ b/tools/Nitload.GenerateGL/Generation/GlOverloadApiFilter.cs @@ -232,13 +232,13 @@ namespace StgSharp.GenerateGL.Generation List singleParameters = new List(prefix) { new ManagedParameterDefinition("bool", "transpose", transposeSource), - new ManagedParameterDefinition("GraphicsMatrix", "value", valueSource), + new ManagedParameterDefinition("GMatrix44", "value", valueSource), }; List spanParameters = new List(prefix) { new ManagedParameterDefinition("bool", "transpose", transposeSource), new ManagedParameterDefinition( - "ReadOnlySpan", + "ReadOnlySpan>", "values", valueSource), }; diff --git a/tools/Nitload.GenerateGL/Generation/OpenGlGenerator.cs b/tools/Nitload.GenerateGL/Generation/OpenGlGenerator.cs index 40dc9334..0755f5b1 100644 --- a/tools/Nitload.GenerateGL/Generation/OpenGlGenerator.cs +++ b/tools/Nitload.GenerateGL/Generation/OpenGlGenerator.cs @@ -740,7 +740,7 @@ namespace StgSharp.GenerateGL.Generation string matrixName = EscapeIdentifier(matrix.Name); if (invocation.IsSpan) { - builder.Append(" fixed (GraphicsMatrix* __value = "); + builder.Append(" fixed (GMatrix44* __value = "); builder.Append(matrixName); builder.Append(")\n"); builder.Append(" {\n"); diff --git a/tools/Nitload.GenerateGL/README.md b/tools/Nitload.GenerateGL/README.md index 9633f44d..69db9860 100644 --- a/tools/Nitload.GenerateGL/README.md +++ b/tools/Nitload.GenerateGL/README.md @@ -1,9 +1,13 @@ -# StgSharp.GenerateGL +# Nitload.GenerateGL -`StgSharp.GenerateGL` generates the StgSharp desktop OpenGL C# bindings from the Khronos `gl.xml` registry. +`Nitload.GenerateGL` generates the Nitload desktop OpenGL C# bindings from the Khronos `gl.xml` registry. The current target is OpenGL 4.6 Core. The generator does not emit OpenGL ES or mix extension entry points into the core API. `gl.xml` is the authoritative generation input; the retained `gl.h` is reference material only. +## Namespace migration + +The project and directory are named `Nitload.GenerateGL`, but the implementation namespace and generated-source templates still contain `StgSharp.*`. Update the templates in `Generation/OpenGlGenerator.cs` to emit `Nitload.Graphics.OpenGL` and import `Nitload.Mathematics.Numeric.Graphics` before regenerating the renamed graphics library. The CLI commands below use the current project paths; they do not automatically migrate generated namespaces. + ## Usage Start the interactive CLI: @@ -92,7 +96,7 @@ The generator merges a family only when it can validate the resulting signatures | `glUniform1*v` | `Uniform(..., ReadOnlySpan)` | | `glUniform4*v` | `Uniform(..., ReadOnlySpan>)` | | `glUniform2*v` and `glUniform3*v` | Omitted because `Vec2` and `Vec3` storage is not a tightly packed C array | -| `glUniformMatrix4fv` | `Uniform(..., GraphicsMatrix)` and matrix-span overloads | +| `glUniformMatrix4fv` | `Uniform(..., GMatrix44)` and matrix-span overloads | | Other matrix sizes and double matrices | Omitted because only the graphics-oriented 4x4 float layout is defined | | `glGetBooleanv`, `glGetFloatv`, `glGetIntegerv`, and related queries | `GetValue(..., Span)` | | `glVertexAttrib1f/2f/3f/4f` and equivalent scalar forms | `VertexAttribute(...)`; component count is expressed by the parameter list | diff --git a/tools/Nitload.GenerateGL/xml/README.md b/tools/Nitload.GenerateGL/xml/README.md index 32166f51..07a71b33 100644 --- a/tools/Nitload.GenerateGL/xml/README.md +++ b/tools/Nitload.GenerateGL/xml/README.md @@ -1,6 +1,6 @@ # Khronos OpenGL Registry input -This directory intentionally does not track the third-party Khronos registry files. Before building or running `StgSharp.GenerateGL`, supply a registry snapshot from the canonical [KhronosGroup/OpenGL-Registry](https://github.com/KhronosGroup/OpenGL-Registry) repository. +This directory intentionally does not track the third-party Khronos registry files. Before building or running `Nitload.GenerateGL`, supply a registry snapshot from the canonical [KhronosGroup/OpenGL-Registry](https://github.com/KhronosGroup/OpenGL-Registry) repository. 1. Clone or download one coherent revision of `KhronosGroup/OpenGL-Registry`. 2. Copy the contents of its `xml/` directory directly into this directory. diff --git a/tools/README.md b/tools/README.md index aa6a27ae..e1e00783 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,14 +1,14 @@ # Development Tools -This directory contains repository-maintenance and source-generation programs. Tools are kept outside `src` because they help build or maintain StgSharp but are not shipped as part of the runtime libraries. +This directory contains repository-maintenance and source-generation programs. Tools are kept outside `src` because they help build or maintain Nitload but are not shipped as part of the runtime libraries. ## Directory map | Directory | Purpose | | --- | --- | -| `StgSharp.GenerateGL` | Reads the Khronos `gl.xml` registry, projects the OpenGL 4.6 Core API, analyzes function families, and generates the managed OpenGL bindings used by `StgSharp.Graphics`. | +| `Nitload.GenerateGL` | Reads the Khronos `gl.xml` registry, projects the OpenGL 4.6 Core API, analyzes function families, and generates the managed OpenGL bindings used by `Nitload.Graphics`. | -`StgSharp.GenerateGL` has its own `README.md` with its pipeline, naming rules, and interactive commands. The common direct-generation command is: +`Nitload.GenerateGL` has its own `README.md` with its pipeline, naming rules, and interactive commands. The common direct-generation command is: ```powershell dotnet run --project tools/Nitload.GenerateGL/Nitload.GenerateGL.csproj -- generate all src/Nitload.Graphics/OpenGL/Generated