world timer supports numa and thread affinity binding
This commit is contained in:
@@ -6,9 +6,11 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
@@ -122,6 +124,24 @@ namespace StgSharp.Collections
|
||||
_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(
|
||||
out T value
|
||||
)
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace StgSharp
|
||||
|
||||
internal const int ssdSegmentLength = 16;
|
||||
|
||||
private static byte[] _currentHash = [];
|
||||
|
||||
private static bool _inited;
|
||||
|
||||
public static byte[] CurrentAssemblyHash
|
||||
@@ -32,12 +30,10 @@ namespace StgSharp
|
||||
if (_currentHash == null)
|
||||
{
|
||||
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);
|
||||
_currentHash = sh.ComputeHash(bytes);
|
||||
}
|
||||
_currentHash = SHA256.HashData(bytes);
|
||||
}
|
||||
return _currentHash;
|
||||
}
|
||||
@@ -52,6 +48,8 @@ namespace StgSharp
|
||||
internal set => _mainThreadID = value;
|
||||
}
|
||||
|
||||
private static byte[] _currentHash { get; set; } = [];
|
||||
|
||||
private static int _mainThreadID { get; set; } = -1;
|
||||
|
||||
public static class GlobalSetting
|
||||
@@ -62,9 +60,7 @@ namespace StgSharp
|
||||
public static bool VSyncActivated
|
||||
{
|
||||
get => vsyncActivated;
|
||||
set =>
|
||||
// GraphicFramework.glfwSwapInterval(value ? 1 : 0);
|
||||
vsyncActivated = value;
|
||||
set => vsyncActivated = value; // GraphicFramework.glfwSwapInterval(value ? 1 : 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ namespace StgSharp
|
||||
/// Performs all initialization work for this module using the provided profile.
|
||||
/// </summary>
|
||||
/// <param name="profile">
|
||||
/// The configuration profile supplied at registration time. Use <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> if no configuration is required.
|
||||
/// The configuration profile supplied at registration time. Use <see ///
|
||||
/// cref="IModuleInitializeConfiguration.Empty" /> if no configuration is required.
|
||||
/// </param>
|
||||
void InitializeModule(
|
||||
IModuleInitializeProfile profile
|
||||
IModuleInitializeConfiguration profile
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
@@ -46,15 +46,15 @@ namespace StgSharp
|
||||
/// Marker interface for module initialization configuration. Implementations are plain data
|
||||
/// carriers; no static factory is required.
|
||||
/// </summary>
|
||||
public interface IModuleInitializeProfile
|
||||
public interface IModuleInitializeConfiguration
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A shared, allocation-free empty profile for modules that require no configuration.
|
||||
/// </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();
|
||||
@@ -66,8 +66,8 @@ namespace StgSharp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />,
|
||||
/// ensuring the two are never stored or iterated independently.
|
||||
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeConfiguration"
|
||||
/// />, ensuring the two are never stored or iterated independently.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if
|
||||
@@ -77,20 +77,20 @@ namespace StgSharp
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ModuleDescriptor" /> using <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> as the profile.
|
||||
/// Initializes a new <see cref="ModuleDescriptor" /> using <see ///
|
||||
/// cref="IModuleInitializeConfiguration.Empty" /> as the profile.
|
||||
/// </summary>
|
||||
public ModuleDescriptor(
|
||||
IStaticModule module
|
||||
)
|
||||
: this(module, IModuleInitializeProfile.Empty) { }
|
||||
: this(module, IModuleInitializeConfiguration.Empty) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile.
|
||||
/// </summary>
|
||||
public ModuleDescriptor(
|
||||
IStaticModule module,
|
||||
IModuleInitializeProfile profile
|
||||
IModuleInitializeConfiguration profile
|
||||
)
|
||||
{
|
||||
Module = module;
|
||||
@@ -100,7 +100,7 @@ namespace StgSharp
|
||||
/// <summary>
|
||||
/// Gets the configuration profile associated with the module.
|
||||
/// </summary>
|
||||
public IModuleInitializeProfile Profile { get; }
|
||||
public IModuleInitializeConfiguration Profile { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module to be initialized.
|
||||
@@ -128,7 +128,7 @@ namespace StgSharp
|
||||
/// <summary>
|
||||
/// An ordered collection of modules to be initialized during application startup and
|
||||
/// 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>
|
||||
public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule
|
||||
{
|
||||
@@ -136,10 +136,11 @@ namespace StgSharp
|
||||
private readonly List<ModuleDescriptor> _descriptors = [];
|
||||
|
||||
internal ModuleToInitializeCollection(
|
||||
World.Initializer i
|
||||
World.Initializer init,
|
||||
World.InitializeConfiguration config
|
||||
)
|
||||
{
|
||||
_descriptors.Add(new ModuleDescriptor(i));
|
||||
_descriptors.Add(new ModuleDescriptor(init, config));
|
||||
}
|
||||
|
||||
// ── IEnumerable<IStaticModule> ───────────────────────────────────────────
|
||||
@@ -189,11 +190,11 @@ namespace StgSharp
|
||||
/// public parameterless constructor.
|
||||
/// </typeparam>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// Optional initialization profile. Defaults to <see ///
|
||||
/// cref="IModuleInitializeConfiguration.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule<T>(
|
||||
IModuleInitializeProfile? profile = null
|
||||
IModuleInitializeConfiguration? profile = null
|
||||
) where T : IStaticModule, new()
|
||||
{
|
||||
return UseModule(static() => new T(), profile);
|
||||
@@ -206,17 +207,17 @@ namespace StgSharp
|
||||
/// </summary>
|
||||
/// <param name="factory">
|
||||
/// 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
|
||||
/// and the collection is returned unchanged.
|
||||
/// </param>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// Optional initialization profile. Defaults to <see ///
|
||||
/// cref="IModuleInitializeConfiguration.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule(
|
||||
Func<IStaticModule> factory,
|
||||
IModuleInitializeProfile? profile = null
|
||||
IModuleInitializeConfiguration? profile = null
|
||||
)
|
||||
{
|
||||
if (factory is null) {
|
||||
@@ -228,7 +229,7 @@ namespace StgSharp
|
||||
}
|
||||
_descriptors.Add(new ModuleDescriptor(
|
||||
mod,
|
||||
profile ?? IModuleInitializeProfile.Empty));
|
||||
profile ?? IModuleInitializeConfiguration.Empty));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -240,17 +241,17 @@ namespace StgSharp
|
||||
/// The module instance to register.
|
||||
/// </param>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// Optional initialization profile. Defaults to <see ///
|
||||
/// cref="IModuleInitializeConfiguration.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule(
|
||||
IStaticModule module,
|
||||
IModuleInitializeProfile? profile = null
|
||||
IModuleInitializeConfiguration? profile = null
|
||||
)
|
||||
{
|
||||
_descriptors.Add(new ModuleDescriptor(
|
||||
module,
|
||||
profile ?? IModuleInitializeProfile.Empty));
|
||||
profile ?? IModuleInitializeConfiguration.Empty));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -263,12 +264,12 @@ namespace StgSharp
|
||||
// ── IStaticModule explicit implementation ────────────────────────────────
|
||||
|
||||
/// <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
|
||||
/// its own registered profile.
|
||||
/// </summary>
|
||||
void IStaticModule.InitializeModule(
|
||||
IModuleInitializeProfile profile
|
||||
IModuleInitializeConfiguration profile
|
||||
)
|
||||
{
|
||||
InitializeModule();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
using StgSharp.HighPerformance;
|
||||
using StgSharp.HighPerformance.ProcessorAbstraction;
|
||||
using StgSharp.Timing;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -36,9 +37,11 @@ namespace StgSharp
|
||||
private static uint markCount = 0;
|
||||
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)]
|
||||
@@ -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
|
||||
MainTimeProvider.StartProvidingTime();
|
||||
MainTimeProvider.StartProvidingTime(config.TimingConfig);
|
||||
DefaultLog.InternalAppendLog("\n\n\n");
|
||||
DefaultLog.InternalWriteLog(
|
||||
$"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
|
||||
{
|
||||
|
||||
public string ModuleName => "StgSharpCore";
|
||||
|
||||
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()
|
||||
|
||||
@@ -40,19 +40,19 @@ namespace StgSharp.HighPerformance.Memory
|
||||
_buffer = buffer;
|
||||
|
||||
_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,
|
||||
> 64 => 64,
|
||||
_ => (int)count
|
||||
};
|
||||
Span<nuint> span = stackalloc nuint[initCount];
|
||||
for (int i = 0; i < initCount; i++) {
|
||||
span[i] = _buffer + ((nuint)(i * _elementSize));
|
||||
for (int i = 0; i < arr.Length; i++) {
|
||||
arr[i] = (nuint)i;
|
||||
}
|
||||
}
|
||||
_recycle = CapacityFixedStackBuilder.Create(span);
|
||||
_Capacity = count;
|
||||
_currentIndex = (nuint)initCount;
|
||||
}
|
||||
|
||||
public override nuint Allocate()
|
||||
|
||||
@@ -123,7 +123,8 @@ namespace StgSharp.HighPerformance.ProcessorAbstraction
|
||||
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
|
||||
internal static partial void SetThreadAffinity(
|
||||
int coreId
|
||||
int coreId,
|
||||
int numaId
|
||||
);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using StgSharp.Collections;
|
||||
using StgSharp.HighPerformance.ProcessorAbstraction;
|
||||
using StgSharp.Timing;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
@@ -39,7 +40,9 @@ namespace StgSharp
|
||||
return mainProvider.ElapsedTicks;
|
||||
}
|
||||
|
||||
public sealed override void StartProvidingTime()
|
||||
public sealed override void StartProvidingTime(
|
||||
TimeSourceStartupConfiguration threadConfig
|
||||
)
|
||||
{
|
||||
if (mainProvider.IsRunning) {
|
||||
return;
|
||||
@@ -49,9 +52,10 @@ namespace StgSharp
|
||||
timeProvideThread = new Thread(ProvideTime)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "StgSharpTime"
|
||||
Name = "StgSharpTime",
|
||||
Priority = threadConfig.Priority,
|
||||
};
|
||||
timeProvideThread.Start();
|
||||
timeProvideThread.Start(threadConfig);
|
||||
}
|
||||
|
||||
public override void StopProvidingTime()
|
||||
@@ -82,13 +86,18 @@ namespace StgSharp
|
||||
_subscribers.Remove(subscriber);
|
||||
}
|
||||
|
||||
private void ProvideTime()
|
||||
private void ProvideTime(
|
||||
object? param
|
||||
)
|
||||
{
|
||||
TimeSourceStartupConfiguration threadConfig = (TimeSourceStartupConfiguration)param!;
|
||||
long internalFrequency = Stopwatch.Frequency; // ticks per second
|
||||
lock (mainProvider) {
|
||||
mainProvider.Restart();
|
||||
}
|
||||
|
||||
Thread.Sleep(10);
|
||||
Numa.SetThreadAffinity((int)threadConfig.BindedCoreIndex, (int)threadConfig.BindedNumaNode);
|
||||
while (mainProvider.IsRunning)
|
||||
{
|
||||
long elapsedTicks;
|
||||
|
||||
@@ -6,13 +6,28 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using StgSharp;
|
||||
|
||||
using StgSharp.HighPerformance.ProcessorAbstraction;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
@@ -40,7 +55,9 @@ namespace StgSharp.Timing
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void StartProvidingTime();
|
||||
public abstract void StartProvidingTime(
|
||||
TimeSourceStartupConfiguration threadConfig
|
||||
);
|
||||
|
||||
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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace StgSharp.Graphics
|
||||
public string ModuleName => "Graphics";
|
||||
|
||||
public void InitializeModule(
|
||||
IModuleInitializeProfile profile
|
||||
IModuleInitializeConfiguration profile
|
||||
)
|
||||
{
|
||||
GraphicFramework.LoadGlfw();
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace StgSharp.Graphics
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void InitializeModule(
|
||||
IModuleInitializeProfile profile
|
||||
IModuleInitializeConfiguration profile
|
||||
)
|
||||
{
|
||||
GraphicFramework.LoadGlfw();
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace StgSharp.Mathematics.Numeric
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void InitializeModule(
|
||||
IModuleInitializeProfile profile
|
||||
IModuleInitializeConfiguration profile
|
||||
)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
AbstractSyntaxTree<RegexAstNode, RegexElementLabel> _tree = new();
|
||||
RegexTokenReader reader = new(_source);
|
||||
TokenParser<RegexElementLabel, RegexElementLabel> lexer = reader.Pipe(() => new RegexTokenParser());
|
||||
Stack<RegexElementLabel> outerLastTokens = [];
|
||||
RegexElementLabel lastToken = RegexElementLabel.NONE;
|
||||
|
||||
while (lexer.TryReadToken(out Token<RegexElementLabel> token))
|
||||
{
|
||||
@@ -38,8 +40,13 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
{
|
||||
_stack.PushOperator(new RegexAstNode(token));
|
||||
_stack.IncreaseDepth(0);
|
||||
outerLastTokens.Push(lastToken);
|
||||
lastToken = RegexElementLabel.GROUP_BEGIN;
|
||||
} else if ((token.Flag & RegexElementLabel.GROUP_END) != 0)
|
||||
{
|
||||
if (lastToken == RegexElementLabel.ALT) {
|
||||
PushEpsilon(token.Line, token.Column);
|
||||
}
|
||||
// close all nodes here
|
||||
RegexAstNode op;
|
||||
while (_stack.TryPopOperator(out op)) {
|
||||
@@ -58,8 +65,17 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
op.Right = _stack.PopOperand();
|
||||
_stack.PushOperand(op);
|
||||
}
|
||||
_ = outerLastTokens.Pop();
|
||||
lastToken = RegexElementLabel.GROUP_END;
|
||||
} 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)
|
||||
{
|
||||
// the first operator in stack
|
||||
@@ -87,6 +103,13 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
_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)
|
||||
{
|
||||
@@ -104,6 +127,13 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
}
|
||||
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(
|
||||
RegexAstNode op
|
||||
|
||||
@@ -145,21 +145,22 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
TreeEnumerator<RegexAstNode, RegexElementLabel> enumerator = new(tree.Root);
|
||||
RegexAstNode current;
|
||||
|
||||
// phase1: ALT flatten
|
||||
// phase2: CONCAT normalize
|
||||
int i = 0;
|
||||
// phase1: normalize associative binary operators, then flatten ALT
|
||||
List<RegexAstNode> altNodes = [];
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
current = enumerator.Current;
|
||||
if (current.EqualityTypeConvert is RegexElementLabel.CONCAT or
|
||||
RegexElementLabel.ALT)
|
||||
{
|
||||
RotateAssociativeOperator(current, current.EqualityTypeConvert);
|
||||
}
|
||||
if (current.EqualityTypeConvert == RegexElementLabel.ALT)
|
||||
{
|
||||
RegexAstNode union = FlattenAlt(current);
|
||||
altNodes.Add(current);
|
||||
current.Right = union;
|
||||
current.Left = RegexAstNode.Empty;
|
||||
} else if (current.EqualityTypeConvert == RegexElementLabel.CONCAT) {
|
||||
RotateConcat(current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,13 +196,28 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
}
|
||||
}
|
||||
|
||||
private static void RotateConcat(
|
||||
RegexAstNode root
|
||||
/// <summary>
|
||||
/// 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;
|
||||
while (!RegexAstNode.IsNullOrEmpty(left) &&
|
||||
left.EqualityTypeConvert == RegexElementLabel.CONCAT)
|
||||
left.EqualityTypeConvert == operatorLabel)
|
||||
{
|
||||
/*
|
||||
* root → root
|
||||
|
||||
@@ -23,6 +23,15 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
public sealed record RegexEmptyPayload() : RegexAstPayload(string.Empty, 0, 0,
|
||||
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>
|
||||
/// A deterministic literal sequence. Escapes represented as literals are consumed here.
|
||||
/// </summary>
|
||||
|
||||
@@ -26,12 +26,13 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
COUNT = 32,
|
||||
CONCAT = 64,
|
||||
ALT = 128,
|
||||
EPSILON = 256,
|
||||
SEQUENCE = UNIT | UNIT_SPAN | UNIT_SET,
|
||||
UNIT_SINGLE = UNIT | UNIT_SET,
|
||||
OPERATOR = COUNT | ALT | CONCAT,
|
||||
VAST_OPERATOR = OPERATOR | GROUP_BEGIN,
|
||||
ATOM_BEGIN = SEQUENCE | GROUP_BEGIN ,
|
||||
ATOM_END = SEQUENCE | GROUP_END | COUNT,
|
||||
ATOM_BEGIN = SEQUENCE | GROUP_BEGIN | EPSILON,
|
||||
ATOM_END = SEQUENCE | GROUP_END | COUNT | EPSILON,
|
||||
|
||||
#pragma warning restore CA1707
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
)
|
||||
{
|
||||
RegexAstNode root = ast;
|
||||
if (root.Label == RegexElementLabel.EPSILON) {
|
||||
return;
|
||||
}
|
||||
RegexElementLabel op = root.Label & (RegexElementLabel.VAST_OPERATOR);
|
||||
if (op != 0)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ using StgSharp.Mathematics.Numeric;
|
||||
using StgSharp.Timing;
|
||||
using StgSharp.Timing;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
@@ -29,12 +30,21 @@ namespace StgSharp
|
||||
{
|
||||
try
|
||||
{
|
||||
World.Initialize()
|
||||
World.Initialize(new()
|
||||
{
|
||||
TimingConfig = new TimeSourceStartupConfiguration()
|
||||
})
|
||||
|
||||
// .UseModule<NumericalModule>()
|
||||
// .UseModule<GraphicModule>()
|
||||
.InitializeModule();
|
||||
|
||||
_ = SlabAllocator.FromBuffer<int>(
|
||||
(nuint)NativeMemory.AlignedAlloc(65536 * 4, 4),
|
||||
65536,
|
||||
buffer => NativeMemory.AlignedFree((void*)buffer)
|
||||
);
|
||||
|
||||
TestRegexAnalyse.Run();
|
||||
|
||||
// TestHlsf();
|
||||
|
||||
@@ -46,11 +46,6 @@ namespace StgSharp
|
||||
Console.WriteLine($"\t{node.Value}\t{node.Source.Flag}");
|
||||
}
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user