//----------------------------------------------------------------------- // ----------------------------------------------------------------------- // 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 StgSharp.Collections; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace StgSharp.RegularAnalysis.Text { public partial class RegexAnalyzer { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static RegexInfo ScanAndOptimizeIR( List baseIR, out List optIR ) { int index = 0; List origin_ir = baseIR; Span origin_span = CollectionsMarshal.AsSpan(baseIR); List opt_ir = new(baseIR.Count); CapacityFixedList opt_window = new(8); Span opt_win_sp = [.. opt_window]; RegexInfo info = new(); do { int process_count = 0; int last = int.Min(origin_span.Length, index + 8); origin_span[index..last].CopyTo(opt_win_sp); do { int result = 0; if ((result = TryExpandPlus(opt_win_sp, opt_ir)) >= 0) { process_count++; if (result > 0) { RegexCountComplexIR cc = Unsafe.As(opt_win_sp[0]); Span sp = origin_span.Slice(index + 1, result); TryExpandComplexPlus(cc.IsGreedy, sp, opt_ir); process_count += result; } } else if ((result = TryOptDotCountCount(opt_win_sp, opt_ir)) >= 0) { process_count++; } else if (TryFlattenCount(opt_win_sp, opt_ir)) { process_count++; } } while (false); index += process_count; } while (index < baseIR.Count); optIR = opt_ir; return info; } private static RegexInfo ScanAndOptimizeIR( Span baseIR, List opt_ir ) { List originList = new(baseIR.Length); CollectionsMarshal.SetCount(originList, baseIR.Length); baseIR.CopyTo(CollectionsMarshal.AsSpan(originList)); return ScanAndOptimizeIR(originList, out opt_ir); } private static bool TryFlattenCount( Span window, List opt ) { const int falttern_size = 32; RegexIR ir = window[0]; if (ir is not RegexCountSeqIR seq) { return false; } string pattern = seq.Sequence; int pattern_size = pattern.Length; if (pattern_size > falttern_size) { opt.Add(ir); } int min = seq.Min; int max = seq.Max; int min_expand = falttern_size / pattern_size; min_expand = int.Min(min_expand, min); string expand = string.Concat(Enumerable.Repeat(pattern, min_expand)); int new_min = min / min_expand; int new_rest = min % min_expand; if (new_min == 1) { opt.Add(new RegexMatchSeqIR(pattern)); } else { opt.Add(new RegexCountSeqIR(new_min, new_min, seq.IsGreedy, pattern)); } if (new_rest > 0) { string expand_tail = string.Concat(Enumerable.Repeat(pattern, new_rest)); opt.Add(new RegexMatchSeqIR(expand_tail)); } int rest_count = max - min; rest_count = int.Max(rest_count, -1); opt.Add(new RegexCountSeqIR(0, rest_count, seq.IsGreedy, pattern)); return true; } #region dot count count private static int TryOptDotCountCount( Span window, List opt ) { /* .* * * convert to * .* */ RegexIR cur_ir = window[0]; if (cur_ir is not RegexCountSetIR { IsDotStar: true } dot) { return -1; } if (window[1] is not IRegexCount { IsStar: true } after) { return -1; } if (dot.IsGreedy) { opt.Add(window[0]); return 0; } return -1; } #endregion internal class RegexInfo { public int MinLength { get; set; } public int TryDepth { get; set; } public int GroupCount { get; set; } } #region expand plus private static bool TryExpandComplexPlus( bool is_greedy, Span complex, List opt ) { int count = complex.Length; List part_opt = new(count); RegexInfo info = ScanAndOptimizeIR(complex, part_opt); opt.AddRange(part_opt); RegexCountComplexIR c_ir = new(0, -1, is_greedy, part_opt.Count) { IsFullyOptimized = true }; opt.Add(c_ir); opt.AddRange(part_opt); return true; } /// /// /// /// /// /// /// /// /// /// /// Negative if cannot opt, 0 if match sucessfully, positive if meets an IRgexComplex /// interface, need to call TryExpandComplexPlus /// private static int TryExpandPlus( Span window, List opt ) { /* a+ * convert to * aa* */ RegexIR ir = window[0]; if (ir is not IRegexCount { IsPlus: true } count) { return -1; } bool is_greedy = count.IsGreedy; string pattern; switch (ir.Command & RegexIRCommand.Count) { case RegexIRCommand.Count_Complex: RegexCountComplexIR complex = Unsafe.As(ir); return complex.CommandCount; case RegexIRCommand.Count_Seq: RegexCountSeqIR seq = Unsafe.As(ir); pattern = seq.Sequence; opt.Add(new RegexMatchSeqIR(pattern)); opt.Add(new RegexCountSeqIR(0, -1, is_greedy, pattern)); return 0; case RegexIRCommand.Count_Set: RegexCountSeqIR set = Unsafe.As(ir); pattern = set.Sequence; opt.Add(new RegexMatchSeqIR(pattern)); opt.Add(new RegexCountSeqIR(0, -1, is_greedy, pattern)); return 0; default: return -1; } } #endregion } }