// TODO https://github.com/dotnet/runtime/issues/62573: Obsolete this. /// <summary> /// This method's body is only kept since it is a protected member that could be called by someone outside /// the assembly. /// </summary> protected internal Match?Scan(Regex regex, string text, int textbeg, int textend, int textstart, int prevlen, bool quick, TimeSpan timeout) { InitializeTimeout(timeout); RegexRunnerMode mode = quick ? RegexRunnerMode.ExistenceRequired : RegexRunnerMode.FullMatchRequired; // We set runtext before calling InitializeForScan so that runmatch object is initialized with the text runtext = text; InitializeForScan(regex, text, textstart, mode); // InitializeForScan will default runtextstart and runtextend to 0 and length of string // since it is configured to work over a sliced portion of text so we adjust those values. runtextstart = textstart; runtextend = textend; // Configure the additional value to "bump" the position along each time we loop around // to call FindFirstChar again, as well as the stopping position for the loop. We generally // bump by 1 and stop at textend, but if we're examining right-to-left, we instead bump // by -1 and stop at textbeg. int bump = 1, stoppos = textend; if (regex.RightToLeft) { bump = -1; stoppos = textbeg; } // If previous match was empty or failed, advance by one before matching. if (prevlen == 0) { if (textstart == stoppos) { return(Match.Empty); } runtextpos += bump; } Match match = InternalScan(regex, textbeg, textend); runtext = null; //drop reference if (match.FoundMatch) { if (quick) { return(null); } runmatch = null; match.Tidy(runtextpos, 0, mode); } else { runmatch !.Text = null; } return(match); }
private Match TidyMatch(bool quick) { if (!quick) { Match runmatch = this.runmatch; this.runmatch = null; runmatch.Tidy(this.runtextpos); return(runmatch); } return(null); }
/* * Put match in its canonical form before returning it. */ private Match TidyMatch(bool quick) { if (!quick) { Match match = _runmatch; _runmatch = null; match.Tidy(_runtextpos); return(match); } else { // in quick mode, a successful match returns null, and // the allocated match object is left in the cache return(null); } }
protected internal Match?Scan(Regex regex, string text, int textbeg, int textend, int textstart, int prevlen, bool quick, TimeSpan timeout) { // Store arguments into fields for derived runner to examine runregex = regex; runtext = text; runtextbeg = textbeg; runtextend = textend; runtextpos = runtextstart = textstart; // Handle timeout argument _timeout = -1; // (int)Regex.InfiniteMatchTimeout.TotalMilliseconds bool ignoreTimeout = _ignoreTimeout = Regex.InfiniteMatchTimeout == timeout; if (!ignoreTimeout) { // We are using Environment.TickCount and not Stopwatch for performance reasons. // Environment.TickCount is an int that cycles. We intentionally let timeoutOccursAt // overflow it will still stay ahead of Environment.TickCount for comparisons made // in DoCheckTimeout(). Regex.ValidateMatchTimeout(timeout); // validate timeout as this could be called from user code due to being protected _timeout = (int)(timeout.TotalMilliseconds + 0.5); // Round; _timeoutOccursAt = Environment.TickCount + _timeout; _timeoutChecksToSkip = TimeoutCheckFrequency; } // Configure the additional value to "bump" the position along each time we loop around // to call FindFirstChar again, as well as the stopping position for the loop. We generally // bump by 1 and stop at runtextend, but if we're examining right-to-left, we instead bump // by -1 and stop at runtextbeg. int bump = 1, stoppos = runtextend; if (runregex.RightToLeft) { bump = -1; stoppos = runtextbeg; } // If previous match was empty or failed, advance by one before matching. if (prevlen == 0) { if (runtextpos == stoppos) { return(Match.Empty); } runtextpos += bump; } // Main loop: FindFirstChar/Go + bump until the ending position. bool initialized = false; while (true) { #if DEBUG if (runregex.IsDebug) { Debug.WriteLine(""); Debug.WriteLine($"Search range: from {runtextbeg} to {runtextend}"); Debug.WriteLine($"Firstchar search starting at {runtextpos} stopping at {stoppos}"); } #endif // Find the next potential location for a match in the input. if (FindFirstChar()) { if (!ignoreTimeout) { DoCheckTimeout(); } // Ensure that the runner is initialized. This includes initializing all of the state in the runner // that Go might use, such as the backtracking stack, as well as a Match object for it to populate. if (!initialized) { InitializeForGo(); initialized = true; } #if DEBUG if (runregex.IsDebug) { Debug.WriteLine($"Executing engine starting at {runtextpos}"); Debug.WriteLine(""); } #endif // See if there's a match at this position. Go(); // If we got a match, we're done. Match match = runmatch !; if (match._matchcount[0] > 0) { if (quick) { return(null); } // Return the match in its canonical form. runmatch = null; match.Tidy(runtextpos); return(match); } // Reset state for another iteration. runtrackpos = runtrack !.Length; runstackpos = runstack !.Length; runcrawlpos = runcrawl !.Length; } // We failed to match at this position. If we're at the stopping point, we're done. if (runtextpos == stoppos) { return(Match.Empty); } // Bump by one (in whichever direction is appropriate) and loop to go again. runtextpos += bump; } }
/// <summary>Enumerates all of the matches with the specified regex, invoking the callback for each.</summary> /// <remarks> /// This optionally repeatedly hands out the same Match instance, updated with new information. /// <paramref name="reuseMatchObject"/> should be set to false if the Match object is handed out to user code. /// </remarks> internal void Scan <TState>(Regex regex, string text, int textstart, ref TState state, MatchCallback <TState> callback, bool reuseMatchObject, TimeSpan timeout) { // Handle timeout argument _timeout = -1; // (int)Regex.InfiniteMatchTimeout.TotalMilliseconds bool ignoreTimeout = _ignoreTimeout = Regex.InfiniteMatchTimeout == timeout; if (!ignoreTimeout) { // We are using Environment.TickCount and not Stopwatch for performance reasons. // Environment.TickCount is an int that cycles. We intentionally let timeoutOccursAt // overflow it will still stay ahead of Environment.TickCount for comparisons made // in DoCheckTimeout(). _timeout = (int)(timeout.TotalMilliseconds + 0.5); // Round; _timeoutOccursAt = Environment.TickCount + _timeout; _timeoutChecksToSkip = TimeoutCheckFrequency; } // Configure the additional value to "bump" the position along each time we loop around // to call FindFirstChar again, as well as the stopping position for the loop. We generally // bump by 1 and stop at text.Length, but if we're examining right-to-left, we instead bump // by -1 and stop at 0. int bump = 1, stoppos = text.Length; if (regex.RightToLeft) { bump = -1; stoppos = 0; } // Store remaining arguments into fields now that we're going to start the scan. // These are referenced by the derived runner. runregex = regex; runtextstart = runtextpos = textstart; runtext = text; runtextend = text.Length; runtextbeg = 0; // Main loop: FindFirstChar/Go + bump until the ending position. bool initialized = false; while (true) { #if DEBUG if (regex.IsDebug) { Debug.WriteLine(""); Debug.WriteLine($"Search range: from {runtextbeg} to {runtextend}"); Debug.WriteLine($"Firstchar search starting at {runtextpos} stopping at {stoppos}"); } #endif // Find the next potential location for a match in the input. if (FindFirstChar()) { if (!ignoreTimeout) { DoCheckTimeout(); } // Ensure that the runner is initialized. This includes initializing all of the state in the runner // that Go might use, such as the backtracking stack, as well as a Match object for it to populate. if (!initialized) { InitializeForGo(); initialized = true; } #if DEBUG if (regex.IsDebug) { Debug.WriteLine($"Executing engine starting at {runtextpos}"); Debug.WriteLine(""); } #endif // See if there's a match at this position. Go(); // See if we have a match. Match match = runmatch !; if (match._matchcount[0] > 0) { // Hand it out to the callback in canonical form. if (!reuseMatchObject) { // We're not reusing match objects, so null out our field reference to the instance. // It'll be recreated the next time one is needed. runmatch = null; } match.Tidy(runtextpos); initialized = false; if (!callback(ref state, match)) { // If the callback returns false, we're done. // Drop reference to text to avoid keeping it alive in a cache. runtext = null !; if (reuseMatchObject) { // We're reusing the single match instance, so clear out its text as well. // We don't do this if we're not reusing instances, as in that case we're // dropping the whole reference to the match, and we no longer own the instance // having handed it out to the callback. match.Text = null !; } return; } // Now that we've matched successfully, update the starting position to reflect // the current position, just as Match.NextMatch() would pass in _textpos as textstart. runtextstart = runtextpos; // Reset state for another iteration. runtrackpos = runtrack !.Length; runstackpos = runstack !.Length; runcrawlpos = runcrawl !.Length; if (match.Length == 0) { if (runtextpos == stoppos) { // Drop reference to text to avoid keeping it alive in a cache. runtext = null !; if (reuseMatchObject) { // See above comment. match.Text = null !; } return; } runtextpos += bump; } // Loop around to perform next match from where we left off. continue; } // Ran Go but it didn't find a match. Reset state for another iteration. runtrackpos = runtrack !.Length; runstackpos = runstack !.Length; runcrawlpos = runcrawl !.Length; } // We failed to match at this position. If we're at the stopping point, we're done. if (runtextpos == stoppos) { runtext = null; // drop reference to text to avoid keeping it alive in a cache if (runmatch != null) { runmatch.Text = null !; } return; } // Bump by one (in whichever direction is appropriate) and loop to go again. runtextpos += bump; } }
private Match TidyMatch(bool quick) { if (!quick) { Match runmatch = this.runmatch; this.runmatch = null; runmatch.Tidy(this.runtextpos); return runmatch; } return null; }