internal Match Scan(Regex regex, String text, int textbeg, int textend, int textstart, int prevlen, bool quick, TimeSpan timeout) { int bump; int stoppos; bool initted = false; // We need to re-validate timeout here because Scan is historically protected and // thus there is a possibility it is called from user code: Regex.ValidateMatchTimeout(timeout); _ignoreTimeout = (Regex.InfiniteMatchTimeout == timeout); _timeout = _ignoreTimeout ? (Int32)Regex.InfiniteMatchTimeout.TotalMilliseconds : (Int32)(timeout.TotalMilliseconds + 0.5); // Round _runregex = regex; _runtext = text; _runtextbeg = textbeg; _runtextend = textend; _runtextstart = textstart; bump = _runregex.RightToLeft ? -1 : 1; stoppos = _runregex.RightToLeft ? _runtextbeg : _runtextend; _runtextpos = textstart; // If previous match was empty or failed, advance by one before matching if (prevlen == 0) { if (_runtextpos == stoppos) { return(Match.Empty); } _runtextpos += bump; } StartTimeoutWatch(); for (; ;) { #if DBG if (_runregex.Debug) { Debug.WriteLine(""); Debug.WriteLine("Search range: from " + _runtextbeg.ToString(CultureInfo.InvariantCulture) + " to " + _runtextend.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine("Firstchar search starting at " + _runtextpos.ToString(CultureInfo.InvariantCulture) + " stopping at " + stoppos.ToString(CultureInfo.InvariantCulture)); } #endif if (FindFirstChar()) { CheckTimeout(); if (!initted) { InitMatch(); initted = true; } #if DBG if (_runregex.Debug) { Debug.WriteLine("Executing engine starting at " + _runtextpos.ToString(CultureInfo.InvariantCulture)); Debug.WriteLine(""); } #endif Go(); if (_runmatch._matchcount[0] > 0) { // We'll return a match even if it touches a previous empty match return(TidyMatch(quick)); } // reset state for another go _runtrackpos = _runtrack.Length; _runstackpos = _runstack.Length; _runcrawlpos = _runcrawl.Length; } // failure! if (_runtextpos == stoppos) { TidyMatch(true); return(Match.Empty); } // Recognize leading []* and various anchors, and bump on failure accordingly // Bump by one and start again _runtextpos += bump; } // We never get here }
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; } }