XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Runtime.ExceptionServices;
using System.Text.RegularExpressions;

namespace XFESystem.Collections;

/// <summary>
/// Retries only pure regex operations interrupted by a measured managed GC pause.
/// Active time already spent is deducted from the next match's timeout. Callback
/// replacements and lazy match collections are deliberately never replayed.
/// </summary>
public static class RegexPauseRecovery
{
    private static long recoveries, lastPauseTicks, lastActiveTicks;
    public static long RecoveryCount => Interlocked.Read(ref recoveries);
    public static TimeSpan LastRecoveredPause => TimeSpan.FromTicks(Interlocked.Read(ref lastPauseTicks));
    public static TimeSpan LastRecoveredActiveTime => TimeSpan.FromTicks(Interlocked.Read(ref lastActiveTicks));
    public static Match Match(Regex regex, string input) => Match(regex, input, regex.RightToLeft ? input.Length : 0);
    public static Match Match(Regex regex, string input, int start)
    {
        long began = Stopwatch.GetTimestamp(); var pause = GC.GetTotalPauseDuration();
        try { return regex.Match(input, start); }
        catch (RegexMatchTimeoutException error) { return Retry(regex, input.Length, began, pause, error, next => next.Match(input, start)); }
    }
    public static bool IsMatch(Regex regex, string input)
    {
        long began = Stopwatch.GetTimestamp(); var pause = GC.GetTotalPauseDuration();
        try { return regex.IsMatch(input); }
        catch (RegexMatchTimeoutException error) { return Retry(regex, input.Length, began, pause, error, next => next.IsMatch(input)); }
    }
    public static string Replace(Regex regex, string input, string replacement, int count)
    {
        long began = Stopwatch.GetTimestamp(); var pause = GC.GetTotalPauseDuration();
        try { return regex.Replace(input, replacement, count); }
        catch (RegexMatchTimeoutException error) { return Retry(regex, input.Length, began, pause, error, next => next.Replace(input, replacement, count)); }
    }
    private static T Retry<T>(Regex original, int inputLength, long began, TimeSpan paused, RegexMatchTimeoutException error, Func<Regex, T> execute)
    {
        var remaining = original.MatchTimeout; TimeSpan totalPause = default, totalActive = default;
        for (int attempt = 0; attempt < 2; attempt++)
        {
            var wall = Stopwatch.GetElapsedTime(began); var pause = GC.GetTotalPauseDuration() - paused;
            var active = wall > pause ? wall - pause : TimeSpan.Zero;
            totalPause += pause; totalActive += active; remaining -= active;
            error.Data["XfeObservedGcPauseMilliseconds"] = pause.TotalMilliseconds;
            error.Data["XfeObservedActiveMilliseconds"] = active.TotalMilliseconds;
            if (pause <= TimeSpan.Zero || remaining <= TimeSpan.Zero || original.ToString().Length > 4096 || inputLength > 131_072 ||
                (original.Options & RegexOptions.IgnoreCase) != 0 && (original.Options & RegexOptions.CultureInvariant) == 0) break;
            // The retry receives only the unspent active-time budget, never a
            // fresh full timeout. A finite attempt cap also bounds wall time.
            began = Stopwatch.GetTimestamp(); paused = GC.GetTotalPauseDuration();
            try
            {
                var result = execute(new Regex(original.ToString(), original.Options, remaining));
                Interlocked.Exchange(ref lastPauseTicks, totalPause.Ticks);
                Interlocked.Exchange(ref lastActiveTicks, totalActive.Ticks + Stopwatch.GetElapsedTime(began).Ticks);
                Interlocked.Increment(ref recoveries);
                return result;
            }
            catch (RegexMatchTimeoutException next) { error = next; }
        }
        ExceptionDispatchInfo.Capture(error).Throw(); return default!;
    }
}