//----------------------------------------------------------------------- // ----------------------------------------------------------------------- // file="RegexInterpreter.IROptimize" // Project: StgSharp // AuthorGroup: Nitload // Copyright (c) Nitload. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace StgSharp.RegularAnalysis.Text { public partial class RegexAnalyzer { // this array is not thread safe so opt process must be done in single thread. private RegexIR[] _opt_buffer = new RegexIR[16]; internal RegexInfo ScanAndOptimizeIR( List baseIR, out List optIR ) { int index = 0; Span origin_span = CollectionsMarshal.AsSpan(baseIR); List optimizedIR = new(baseIR.Count); List segment = []; RegexInfo info = new(); do { OptimizeIR(origin_span, ref index, segment, info); optimizedIR.AddRange(segment); index++; } while (index < baseIR.Count); optIR = optimizedIR; return info; } private int ExpandCount( string pattern, int min, int max, bool is_greedy, in Span span ) { // expand count operation to less cycles. const int fast_seq_length = 32; int fast_count = fast_seq_length / pattern.Length; fast_count = int.Min(fast_count, min); if (fast_count > 0) { string fast_pattern = string.Concat(Enumerable.Repeat(pattern, fast_count)); int fast_min = min / fast_count; int consumed_count = fast_min * fast_count; int remain_min = min - consumed_count; int remain_max = max - consumed_count; remain_max = int.Max(-1, remain_max); //for negative stands for infinitive if (remain_max == 0) { // a{2} RegexCountSeqIR fast = new(fast_min, fast_min, is_greedy, fast_pattern); span[0] = fast; return 1; } else { // a{2,5} RegexCountSeqIR fast = new(fast_min, fast_min, is_greedy, fast_pattern); RegexCountSeqIR tail = new(remain_min, remain_max, is_greedy, pattern); span[0] = fast; span[1] = tail; return 2; } } else if (fast_count == 0) { // least count is 0 span[0] = new RegexCountSeqIR(0, max, is_greedy, pattern); return 1; } else { // least count is infinitive span[0] = new RegexCountSeqIR(-1, -1, is_greedy, pattern); return 1; } } private void OptimizeDot( RegexCountSetIR root, Span ir, ref int index, List optimizedIR, RegexInfo info ) { int next = index + 1; optimizedIR.Clear(); if (next >= ir.Length) { optimizedIR.Add(root); return; } RegexIR nextIR = ir[next]; if (nextIR is RegexMatchSeqIR match_seq) { // opt .*abc style RegexFindSeqIR find = new(match_seq.Pattern); optimizedIR.Add(find); info.MinLength += find.Pattern.Length; return; } else if (nextIR is RegexCountSeqIR count_seq) { // opt .* (abc)* or .*(abc){2,5} style string pattern = count_seq.Sequence; int min = count_seq.Min; int max = count_seq.Max; switch ((min, max)) { case (0, _): break; default: RegexFindSeqIR find = new(pattern); // RegexCountSeqIR break; } } } private void OptimizeIR( Span ir, ref int index, List optimizedIR, RegexInfo info ) { optimizedIR.Clear(); // TODO optimize here RegexIR root = ir[index]; switch (root.Command) { case RegexIRCommand.Count_Set: RegexCountSetIR count = (root as RegexCountSetIR)!; if (count.CharSet == ".") { // opt for .+ / .* / .{} pattern OptimizeDot(count, ir, ref index, optimizedIR, info); } break; case RegexIRCommand.Match_Seq: info.MinLength += (root as RegexMatchSeqIR)!.Pattern.Length; optimizedIR.Add(root); break; default: optimizedIR.Add(root); break; } } internal class RegexInfo { public int MinLength { get; set; } public int TryDepth { get; set; } public int GroupCount { get; set; } } } }