world timer supports numa and thread affinity binding

This commit is contained in:
Youmu Liu
2026-08-20 16:53:34 +08:00
parent 843eb6b237
commit 968a6afe1a
19 changed files with 223 additions and 88 deletions
@@ -6,9 +6,11 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
using System; using System;
using System.Buffers;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading; using System.Threading;
namespace StgSharp.Collections namespace StgSharp.Collections
@@ -122,6 +124,24 @@ namespace StgSharp.Collections
_index += span.Length; _index += span.Length;
} }
public void PushRange(
SpanAction<T, int> filling,
int count
)
{
if (count == 0) {
return;
}
if (_index + count > _values.Length) {
throw new InvalidOperationException("Not enough space in the stack to push the entire span.");
}
if (filling is not null)
{
filling(_values.AsSpan()[_index..(_index + count)], count);
_index += count;
}
}
public bool TryPop( public bool TryPop(
out T value out T value
) )
@@ -163,4 +183,4 @@ namespace StgSharp.Collections
} }
} }
} }
@@ -21,8 +21,6 @@ namespace StgSharp
internal const int ssdSegmentLength = 16; internal const int ssdSegmentLength = 16;
private static byte[] _currentHash = [];
private static bool _inited; private static bool _inited;
public static byte[] CurrentAssemblyHash public static byte[] CurrentAssemblyHash
@@ -32,12 +30,10 @@ namespace StgSharp
if (_currentHash == null) if (_currentHash == null)
{ {
string route = Assembly.GetExecutingAssembly().Location; string route = Assembly.GetExecutingAssembly().Location;
string time = DateTime.UtcNow.ToString();
using (SHA256 sh = SHA256.Create()) // string time = DateTime.UtcNow.ToString();
{ byte[] bytes = Encoding.UTF8.GetBytes(route);
byte[] bytes = Encoding.UTF8.GetBytes(route); _currentHash = SHA256.HashData(bytes);
_currentHash = sh.ComputeHash(bytes);
}
} }
return _currentHash; return _currentHash;
} }
@@ -52,6 +48,8 @@ namespace StgSharp
internal set => _mainThreadID = value; internal set => _mainThreadID = value;
} }
private static byte[] _currentHash { get; set; } = [];
private static int _mainThreadID { get; set; } = -1; private static int _mainThreadID { get; set; } = -1;
public static class GlobalSetting public static class GlobalSetting
@@ -62,12 +60,10 @@ namespace StgSharp
public static bool VSyncActivated public static bool VSyncActivated
{ {
get => vsyncActivated; get => vsyncActivated;
set => set => vsyncActivated = value; // GraphicFramework.glfwSwapInterval(value ? 1 : 0);
// GraphicFramework.glfwSwapInterval(value ? 1 : 0);
vsyncActivated = value;
} }
} }
}//-------------------------------------- End of Class ---------------------------------------// }//-------------------------------------- End of Class ---------------------------------------//
} }
@@ -27,11 +27,11 @@ namespace StgSharp
/// Performs all initialization work for this module using the provided profile. /// Performs all initialization work for this module using the provided profile.
/// </summary> /// </summary>
/// <param name="profile"> /// <param name="profile">
/// The configuration profile supplied at registration time. Use <see /// The configuration profile supplied at registration time. Use <see ///
/// cref="IModuleInitializeProfile.Empty" /> if no configuration is required. /// cref="IModuleInitializeConfiguration.Empty" /> if no configuration is required.
/// </param> /// </param>
void InitializeModule( void InitializeModule(
IModuleInitializeProfile profile IModuleInitializeConfiguration profile
); );
/// <summary> /// <summary>
@@ -46,15 +46,15 @@ namespace StgSharp
/// Marker interface for module initialization configuration. Implementations are plain data /// Marker interface for module initialization configuration. Implementations are plain data
/// carriers; no static factory is required. /// carriers; no static factory is required.
/// </summary> /// </summary>
public interface IModuleInitializeProfile public interface IModuleInitializeConfiguration
{ {
/// <summary> /// <summary>
/// A shared, allocation-free empty profile for modules that require no configuration. /// A shared, allocation-free empty profile for modules that require no configuration.
/// </summary> /// </summary>
static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance; static readonly IModuleInitializeConfiguration Empty = EmptyModuleInitializeProfile.Instance;
private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile private sealed class EmptyModuleInitializeProfile : IModuleInitializeConfiguration
{ {
public static readonly EmptyModuleInitializeProfile Instance = new(); public static readonly EmptyModuleInitializeProfile Instance = new();
@@ -66,8 +66,8 @@ namespace StgSharp
} }
/// <summary> /// <summary>
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />, /// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeConfiguration"
/// ensuring the two are never stored or iterated independently. /// />, ensuring the two are never stored or iterated independently.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if /// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if
@@ -77,20 +77,20 @@ namespace StgSharp
{ {
/// <summary> /// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> using <see /// Initializes a new <see cref="ModuleDescriptor" /> using <see ///
/// cref="IModuleInitializeProfile.Empty" /> as the profile. /// cref="IModuleInitializeConfiguration.Empty" /> as the profile.
/// </summary> /// </summary>
public ModuleDescriptor( public ModuleDescriptor(
IStaticModule module IStaticModule module
) )
: this(module, IModuleInitializeProfile.Empty) { } : this(module, IModuleInitializeConfiguration.Empty) { }
/// <summary> /// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile. /// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile.
/// </summary> /// </summary>
public ModuleDescriptor( public ModuleDescriptor(
IStaticModule module, IStaticModule module,
IModuleInitializeProfile profile IModuleInitializeConfiguration profile
) )
{ {
Module = module; Module = module;
@@ -100,7 +100,7 @@ namespace StgSharp
/// <summary> /// <summary>
/// Gets the configuration profile associated with the module. /// Gets the configuration profile associated with the module.
/// </summary> /// </summary>
public IModuleInitializeProfile Profile { get; } public IModuleInitializeConfiguration Profile { get; }
/// <summary> /// <summary>
/// Gets the module to be initialized. /// Gets the module to be initialized.
@@ -127,8 +127,8 @@ namespace StgSharp
/// <summary> /// <summary>
/// An ordered collection of modules to be initialized during application startup and /// An ordered collection of modules to be initialized during application startup and
/// uninitialized on shutdown. Supports three registration strategies so that the <c> new() /// uninitialized on shutdown. Supports three registration strategies so that the <c>new()
/// </c> generic constraint is never a hard requirement. /// ///</c> generic constraint is never a hard requirement.
/// </summary> /// </summary>
public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule
{ {
@@ -136,10 +136,11 @@ namespace StgSharp
private readonly List<ModuleDescriptor> _descriptors = []; private readonly List<ModuleDescriptor> _descriptors = [];
internal ModuleToInitializeCollection( internal ModuleToInitializeCollection(
World.Initializer i World.Initializer init,
World.InitializeConfiguration config
) )
{ {
_descriptors.Add(new ModuleDescriptor(i)); _descriptors.Add(new ModuleDescriptor(init, config));
} }
// ── IEnumerable<IStaticModule> ─────────────────────────────────────────── // ── IEnumerable<IStaticModule> ───────────────────────────────────────────
@@ -189,11 +190,11 @@ namespace StgSharp
/// public parameterless constructor. /// public parameterless constructor.
/// </typeparam> /// </typeparam>
/// <param name="profile"> /// <param name="profile">
/// Optional initialization profile. Defaults to <see /// Optional initialization profile. Defaults to <see ///
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />. /// cref="IModuleInitializeConfiguration.Empty" /> when <see langword="null" />.
/// </param> /// </param>
public ModuleToInitializeCollection UseModule<T>( public ModuleToInitializeCollection UseModule<T>(
IModuleInitializeProfile? profile = null IModuleInitializeConfiguration? profile = null
) where T : IStaticModule, new() ) where T : IStaticModule, new()
{ {
return UseModule(static() => new T(), profile); return UseModule(static() => new T(), profile);
@@ -206,17 +207,17 @@ namespace StgSharp
/// </summary> /// </summary>
/// <param name="factory"> /// <param name="factory">
/// A delegate that constructs and returns the module instance. The delegate is invoked /// A delegate that constructs and returns the module instance. The delegate is invoked
/// once, immediately at registration time. If <paramref name="factory" /> is <see /// once, immediately at registration time. If <paramref name="factory" /> is <see ///
/// langword="null" /> or returns <see langword="null" />, the call is silently ignored /// langword="null" /> or returns <see langword="null" />, the call is silently ignored
/// and the collection is returned unchanged. /// and the collection is returned unchanged.
/// </param> /// </param>
/// <param name="profile"> /// <param name="profile">
/// Optional initialization profile. Defaults to <see /// Optional initialization profile. Defaults to <see ///
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />. /// cref="IModuleInitializeConfiguration.Empty" /> when <see langword="null" />.
/// </param> /// </param>
public ModuleToInitializeCollection UseModule( public ModuleToInitializeCollection UseModule(
Func<IStaticModule> factory, Func<IStaticModule> factory,
IModuleInitializeProfile? profile = null IModuleInitializeConfiguration? profile = null
) )
{ {
if (factory is null) { if (factory is null) {
@@ -228,7 +229,7 @@ namespace StgSharp
} }
_descriptors.Add(new ModuleDescriptor( _descriptors.Add(new ModuleDescriptor(
mod, mod,
profile ?? IModuleInitializeProfile.Empty)); profile ?? IModuleInitializeConfiguration.Empty));
return this; return this;
} }
@@ -240,17 +241,17 @@ namespace StgSharp
/// The module instance to register. /// The module instance to register.
/// </param> /// </param>
/// <param name="profile"> /// <param name="profile">
/// Optional initialization profile. Defaults to <see /// Optional initialization profile. Defaults to <see ///
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />. /// cref="IModuleInitializeConfiguration.Empty" /> when <see langword="null" />.
/// </param> /// </param>
public ModuleToInitializeCollection UseModule( public ModuleToInitializeCollection UseModule(
IStaticModule module, IStaticModule module,
IModuleInitializeProfile? profile = null IModuleInitializeConfiguration? profile = null
) )
{ {
_descriptors.Add(new ModuleDescriptor( _descriptors.Add(new ModuleDescriptor(
module, module,
profile ?? IModuleInitializeProfile.Empty)); profile ?? IModuleInitializeConfiguration.Empty));
return this; return this;
} }
@@ -263,12 +264,12 @@ namespace StgSharp
// ── IStaticModule explicit implementation ──────────────────────────────── // ── IStaticModule explicit implementation ────────────────────────────────
/// <summary> /// <summary>
/// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see /// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see ///
/// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses /// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses
/// its own registered profile. /// its own registered profile.
/// </summary> /// </summary>
void IStaticModule.InitializeModule( void IStaticModule.InitializeModule(
IModuleInitializeProfile profile IModuleInitializeConfiguration profile
) )
{ {
InitializeModule(); InitializeModule();
@@ -278,4 +279,4 @@ namespace StgSharp
string IStaticModule.ModuleName => "Module Initializer"; string IStaticModule.ModuleName => "Module Initializer";
} }
} }
+21 -6
View File
@@ -7,6 +7,7 @@
using StgSharp.HighPerformance; using StgSharp.HighPerformance;
using StgSharp.HighPerformance.ProcessorAbstraction; using StgSharp.HighPerformance.ProcessorAbstraction;
using StgSharp.Timing;
using System; using System;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -36,9 +37,11 @@ namespace StgSharp
private static uint markCount = 0; private static uint markCount = 0;
internal static GraphicAPI API = default; internal static GraphicAPI API = default;
public static ModuleToInitializeCollection Initialize() public static ModuleToInitializeCollection Initialize(
InitializeConfiguration config
)
{ {
return new ModuleToInitializeCollection(new Initializer()); return new ModuleToInitializeCollection(new Initializer(), config);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -177,10 +180,12 @@ namespace StgSharp
} }
} }
internal static unsafe void InternalInitialize() internal static unsafe void InternalInitialize(
InitializeConfiguration config
)
{ {
// Initialize time provider and start providing time // Initialize time provider and start providing time
MainTimeProvider.StartProvidingTime(); MainTimeProvider.StartProvidingTime(config.TimingConfig);
DefaultLog.InternalAppendLog("\n\n\n"); DefaultLog.InternalAppendLog("\n\n\n");
DefaultLog.InternalWriteLog( DefaultLog.InternalWriteLog(
$"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info); $"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info);
@@ -197,16 +202,26 @@ namespace StgSharp
/**/ /**/
} }
public class InitializeConfiguration : IModuleInitializeConfiguration
{
public TimeSourceStartupConfiguration TimingConfig { get; set; }
}
internal class Initializer : IStaticModule internal class Initializer : IStaticModule
{ {
public string ModuleName => "StgSharpCore"; public string ModuleName => "StgSharpCore";
public void InitializeModule( public void InitializeModule(
IModuleInitializeProfile profile IModuleInitializeConfiguration config
) )
{ {
World.InternalInitialize(); if (config is not InitializeConfiguration world_init) {
throw new InvalidCastException();
}
World.InternalInitialize(world_init);
} }
public void UninitializeModule() public void UninitializeModule()
@@ -40,19 +40,19 @@ namespace StgSharp.HighPerformance.Memory
_buffer = buffer; _buffer = buffer;
_Capacity = count; _Capacity = count;
int initCount = count switch _recycle = new((int)_Capacity);
_recycle.PushRange(init, (int)count);
_currentIndex = _Capacity;
static void init(
Span<nuint> arr,
int _
)
{ {
<= 4 => 4, for (int i = 0; i < arr.Length; i++) {
> 64 => 64, arr[i] = (nuint)i;
_ => (int)count }
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = _buffer + ((nuint)(i * _elementSize));
} }
_recycle = CapacityFixedStackBuilder.Create(span);
_Capacity = count;
_currentIndex = (nuint)initCount;
} }
public override nuint Allocate() public override nuint Allocate()
@@ -123,7 +123,8 @@ namespace StgSharp.HighPerformance.ProcessorAbstraction
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
internal static partial void SetThreadAffinity( internal static partial void SetThreadAffinity(
int coreId int coreId,
int numaId
); );
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
+13 -4
View File
@@ -6,6 +6,7 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
using StgSharp.Collections; using StgSharp.Collections;
using StgSharp.HighPerformance.ProcessorAbstraction;
using StgSharp.Timing; using StgSharp.Timing;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@@ -39,7 +40,9 @@ namespace StgSharp
return mainProvider.ElapsedTicks; return mainProvider.ElapsedTicks;
} }
public sealed override void StartProvidingTime() public sealed override void StartProvidingTime(
TimeSourceStartupConfiguration threadConfig
)
{ {
if (mainProvider.IsRunning) { if (mainProvider.IsRunning) {
return; return;
@@ -49,9 +52,10 @@ namespace StgSharp
timeProvideThread = new Thread(ProvideTime) timeProvideThread = new Thread(ProvideTime)
{ {
IsBackground = true, IsBackground = true,
Name = "StgSharpTime" Name = "StgSharpTime",
Priority = threadConfig.Priority,
}; };
timeProvideThread.Start(); timeProvideThread.Start(threadConfig);
} }
public override void StopProvidingTime() public override void StopProvidingTime()
@@ -82,13 +86,18 @@ namespace StgSharp
_subscribers.Remove(subscriber); _subscribers.Remove(subscriber);
} }
private void ProvideTime() private void ProvideTime(
object? param
)
{ {
TimeSourceStartupConfiguration threadConfig = (TimeSourceStartupConfiguration)param!;
long internalFrequency = Stopwatch.Frequency; // ticks per second long internalFrequency = Stopwatch.Frequency; // ticks per second
lock (mainProvider) { lock (mainProvider) {
mainProvider.Restart(); mainProvider.Restart();
} }
Thread.Sleep(10);
Numa.SetThreadAffinity((int)threadConfig.BindedCoreIndex, (int)threadConfig.BindedNumaNode);
while (mainProvider.IsRunning) while (mainProvider.IsRunning)
{ {
long elapsedTicks; long elapsedTicks;
@@ -6,13 +6,28 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
using StgSharp; using StgSharp;
using StgSharp.HighPerformance.ProcessorAbstraction;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
namespace StgSharp.Timing namespace StgSharp.Timing
{ {
public class TimeSourceStartupConfiguration(
nuint bindedNumaNode = 0,
nuint bindedCoreIndex = 0,
ThreadPriority threadPriority = ThreadPriority.Highest
)
{
public nuint BindedNumaNode { get; } = bindedNumaNode;
public nuint BindedCoreIndex { get; } = bindedCoreIndex;
public ThreadPriority Priority { get; } = threadPriority;
}
public abstract class TimeSourceProviderBase public abstract class TimeSourceProviderBase
{ {
@@ -40,7 +55,9 @@ namespace StgSharp.Timing
} }
} }
public abstract void StartProvidingTime(); public abstract void StartProvidingTime(
TimeSourceStartupConfiguration threadConfig
);
public abstract void StopProvidingTime(); public abstract void StopProvidingTime();
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Common.Timing
{
internal class TimeThreadProfile
{
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ namespace StgSharp.Graphics
public string ModuleName => "Graphics"; public string ModuleName => "Graphics";
public void InitializeModule( public void InitializeModule(
IModuleInitializeProfile profile IModuleInitializeConfiguration profile
) )
{ {
GraphicFramework.LoadGlfw(); GraphicFramework.LoadGlfw();
+1 -1
View File
@@ -20,7 +20,7 @@ namespace StgSharp.Graphics
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InitializeModule( public void InitializeModule(
IModuleInitializeProfile profile IModuleInitializeConfiguration profile
) )
{ {
GraphicFramework.LoadGlfw(); GraphicFramework.LoadGlfw();
@@ -52,7 +52,7 @@ namespace StgSharp.Mathematics.Numeric
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InitializeModule( public void InitializeModule(
IModuleInitializeProfile profile IModuleInitializeConfiguration profile
) )
{ {
try try
@@ -26,6 +26,8 @@ namespace StgSharp.RegularAnalysis.Text
AbstractSyntaxTree<RegexAstNode, RegexElementLabel> _tree = new(); AbstractSyntaxTree<RegexAstNode, RegexElementLabel> _tree = new();
RegexTokenReader reader = new(_source); RegexTokenReader reader = new(_source);
TokenParser<RegexElementLabel, RegexElementLabel> lexer = reader.Pipe(() => new RegexTokenParser()); TokenParser<RegexElementLabel, RegexElementLabel> lexer = reader.Pipe(() => new RegexTokenParser());
Stack<RegexElementLabel> outerLastTokens = [];
RegexElementLabel lastToken = RegexElementLabel.NONE;
while (lexer.TryReadToken(out Token<RegexElementLabel> token)) while (lexer.TryReadToken(out Token<RegexElementLabel> token))
{ {
@@ -38,8 +40,13 @@ namespace StgSharp.RegularAnalysis.Text
{ {
_stack.PushOperator(new RegexAstNode(token)); _stack.PushOperator(new RegexAstNode(token));
_stack.IncreaseDepth(0); _stack.IncreaseDepth(0);
outerLastTokens.Push(lastToken);
lastToken = RegexElementLabel.GROUP_BEGIN;
} else if ((token.Flag & RegexElementLabel.GROUP_END) != 0) } else if ((token.Flag & RegexElementLabel.GROUP_END) != 0)
{ {
if (lastToken == RegexElementLabel.ALT) {
PushEpsilon(token.Line, token.Column);
}
// close all nodes here // close all nodes here
RegexAstNode op; RegexAstNode op;
while (_stack.TryPopOperator(out op)) { while (_stack.TryPopOperator(out op)) {
@@ -58,8 +65,17 @@ namespace StgSharp.RegularAnalysis.Text
op.Right = _stack.PopOperand(); op.Right = _stack.PopOperand();
_stack.PushOperand(op); _stack.PushOperand(op);
} }
_ = outerLastTokens.Pop();
lastToken = RegexElementLabel.GROUP_END;
} else if ((token.Flag & RegexElementLabel.OPERATOR) != 0) } else if ((token.Flag & RegexElementLabel.OPERATOR) != 0)
{ {
if (token.Flag == RegexElementLabel.ALT &&
lastToken is RegexElementLabel.NONE or
RegexElementLabel.GROUP_BEGIN or
RegexElementLabel.ALT)
{
PushEpsilon(token.Line, token.Column);
}
if (_stack.OperatorInDepthCount == 0) if (_stack.OperatorInDepthCount == 0)
{ {
// the first operator in stack // the first operator in stack
@@ -87,6 +103,13 @@ namespace StgSharp.RegularAnalysis.Text
_stack.PushOperator(new RegexAstNode(token)); _stack.PushOperator(new RegexAstNode(token));
} }
} }
if (token.Flag != RegexElementLabel.GROUP_BEGIN &&
token.Flag != RegexElementLabel.GROUP_END) {
lastToken = token.Flag;
}
}
if (lastToken == RegexElementLabel.ALT) {
PushEpsilon(0, _source.Length);
} }
if (_stack.Depth == 1) if (_stack.Depth == 1)
{ {
@@ -104,6 +127,13 @@ namespace StgSharp.RegularAnalysis.Text
} }
throw new InvalidOperationException("Invalid regular expression syntax."); throw new InvalidOperationException("Invalid regular expression syntax.");
void PushEpsilon(int row, int column)
{
RegexAstNode epsilon = new(new RegexEpsilonPayload(row, column));
_stack.PushOperand(epsilon);
_ = _tree.AddNode(epsilon);
}
void ProcessOperator( void ProcessOperator(
RegexAstNode op RegexAstNode op
@@ -145,21 +145,22 @@ namespace StgSharp.RegularAnalysis.Text
TreeEnumerator<RegexAstNode, RegexElementLabel> enumerator = new(tree.Root); TreeEnumerator<RegexAstNode, RegexElementLabel> enumerator = new(tree.Root);
RegexAstNode current; RegexAstNode current;
// phase1: ALT flatten // phase1: normalize associative binary operators, then flatten ALT
// phase2: CONCAT normalize
int i = 0;
List<RegexAstNode> altNodes = []; List<RegexAstNode> altNodes = [];
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {
current = enumerator.Current; current = enumerator.Current;
if (current.EqualityTypeConvert is RegexElementLabel.CONCAT or
RegexElementLabel.ALT)
{
RotateAssociativeOperator(current, current.EqualityTypeConvert);
}
if (current.EqualityTypeConvert == RegexElementLabel.ALT) if (current.EqualityTypeConvert == RegexElementLabel.ALT)
{ {
RegexAstNode union = FlattenAlt(current); RegexAstNode union = FlattenAlt(current);
altNodes.Add(current); altNodes.Add(current);
current.Right = union; current.Right = union;
current.Left = RegexAstNode.Empty; current.Left = RegexAstNode.Empty;
} else if (current.EqualityTypeConvert == RegexElementLabel.CONCAT) {
RotateConcat(current);
} }
} }
@@ -195,13 +196,28 @@ namespace StgSharp.RegularAnalysis.Text
} }
} }
private static void RotateConcat( /// <summary>
RegexAstNode root /// Converts a left-associated binary operator tree to a right spine while preserving
/// operand order. For example, <c>(a op b) op c</c> becomes
/// <c>a op (b op c)</c>.
/// </summary>
/// <remarks>
/// A child with another label is an operand and is not rotated across. Keeping the target
/// label explicit allows later associative operators to reuse the same tree operation.
/// </remarks>
private static void RotateAssociativeOperator(
RegexAstNode root,
RegexElementLabel operatorLabel
) )
{ {
if (root.EqualityTypeConvert != operatorLabel) {
throw new ArgumentException("The root does not match the requested operator.",
nameof(operatorLabel));
}
RegexAstNode left = root.Left; RegexAstNode left = root.Left;
while (!RegexAstNode.IsNullOrEmpty(left) && while (!RegexAstNode.IsNullOrEmpty(left) &&
left.EqualityTypeConvert == RegexElementLabel.CONCAT) left.EqualityTypeConvert == operatorLabel)
{ {
/* /*
* root → root * root → root
@@ -23,6 +23,15 @@ namespace StgSharp.RegularAnalysis.Text
public sealed record RegexEmptyPayload() : RegexAstPayload(string.Empty, 0, 0, public sealed record RegexEmptyPayload() : RegexAstPayload(string.Empty, 0, 0,
RegexElementLabel.NONE); RegexElementLabel.NONE);
/// <summary>
/// A semantic empty expression. Unlike <see cref="RegexEmptyPayload"/>, this is a real AST
/// node and represents a successful match that consumes no input.
/// </summary>
public sealed record RegexEpsilonPayload(
int Row,
int Column
) : RegexAstPayload(string.Empty, Row, Column, RegexElementLabel.EPSILON);
/// <summary> /// <summary>
/// A deterministic literal sequence. Escapes represented as literals are consumed here. /// A deterministic literal sequence. Escapes represented as literals are consumed here.
/// </summary> /// </summary>
@@ -26,12 +26,13 @@ namespace StgSharp.RegularAnalysis.Text
COUNT = 32, COUNT = 32,
CONCAT = 64, CONCAT = 64,
ALT = 128, ALT = 128,
EPSILON = 256,
SEQUENCE = UNIT | UNIT_SPAN | UNIT_SET, SEQUENCE = UNIT | UNIT_SPAN | UNIT_SET,
UNIT_SINGLE = UNIT | UNIT_SET, UNIT_SINGLE = UNIT | UNIT_SET,
OPERATOR = COUNT | ALT | CONCAT, OPERATOR = COUNT | ALT | CONCAT,
VAST_OPERATOR = OPERATOR | GROUP_BEGIN, VAST_OPERATOR = OPERATOR | GROUP_BEGIN,
ATOM_BEGIN = SEQUENCE | GROUP_BEGIN , ATOM_BEGIN = SEQUENCE | GROUP_BEGIN | EPSILON,
ATOM_END = SEQUENCE | GROUP_END | COUNT, ATOM_END = SEQUENCE | GROUP_END | COUNT | EPSILON,
#pragma warning restore CA1707 #pragma warning restore CA1707
} }
@@ -29,6 +29,9 @@ namespace StgSharp.RegularAnalysis.Text
) )
{ {
RegexAstNode root = ast; RegexAstNode root = ast;
if (root.Label == RegexElementLabel.EPSILON) {
return;
}
RegexElementLabel op = root.Label & (RegexElementLabel.VAST_OPERATOR); RegexElementLabel op = root.Label & (RegexElementLabel.VAST_OPERATOR);
if (op != 0) if (op != 0)
{ {
+11 -1
View File
@@ -13,6 +13,7 @@ using StgSharp.Mathematics.Numeric;
using StgSharp.Timing; using StgSharp.Timing;
using StgSharp.Timing; using StgSharp.Timing;
using System; using System;
using System.Runtime.InteropServices;
namespace StgSharp namespace StgSharp
{ {
@@ -29,12 +30,21 @@ namespace StgSharp
{ {
try try
{ {
World.Initialize() World.Initialize(new()
{
TimingConfig = new TimeSourceStartupConfiguration()
})
// .UseModule<NumericalModule>() // .UseModule<NumericalModule>()
// .UseModule<GraphicModule>() // .UseModule<GraphicModule>()
.InitializeModule(); .InitializeModule();
_ = SlabAllocator.FromBuffer<int>(
(nuint)NativeMemory.AlignedAlloc(65536 * 4, 4),
65536,
buffer => NativeMemory.AlignedFree((void*)buffer)
);
TestRegexAnalyse.Run(); TestRegexAnalyse.Run();
// TestHlsf(); // TestHlsf();
-5
View File
@@ -46,11 +46,6 @@ namespace StgSharp
Console.WriteLine($"\t{node.Value}\t{node.Source.Flag}"); Console.WriteLine($"\t{node.Value}\t{node.Source.Flag}");
} }
Console.WriteLine(interpreterSource.AnalyseException?.ToString() ?? "No error"); Console.WriteLine(interpreterSource.AnalyseException?.ToString() ?? "No error");
RegexInfo info = interpreterSource.Info;
Console.WriteLine("-----Regex Info-----");
Console.WriteLine($"Min length: {info.MinPredictLength}");
Console.WriteLine($"Region count: {info.RegionCount}");
Console.WriteLine($"Single line dependency: {info.SingleLineResultCount}");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine(); Console.WriteLine();
Console.WriteLine(); Console.WriteLine();