mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 00:11:04 +00:00 
			
		
		
		
	* update github.com/alecthomas/chroma v0.8.0 -> v0.8.1 * github.com/blevesearch/bleve v1.0.10 -> v1.0.12 * editorconfig-core-go v2.1.1 -> v2.3.7 * github.com/gliderlabs/ssh v0.2.2 -> v0.3.1 * migrate editorconfig.ParseBytes to Parse * github.com/shurcooL/vfsgen to 0d455de96546 * github.com/go-git/go-git/v5 v5.1.0 -> v5.2.0 * github.com/google/uuid v1.1.1 -> v1.1.2 * github.com/huandu/xstrings v1.3.0 -> v1.3.2 * github.com/klauspost/compress v1.10.11 -> v1.11.1 * github.com/markbates/goth v1.61.2 -> v1.65.0 * github.com/mattn/go-sqlite3 v1.14.0 -> v1.14.4 * github.com/mholt/archiver v3.3.0 -> v3.3.2 * github.com/microcosm-cc/bluemonday 4f7140c49acb -> v1.0.4 * github.com/minio/minio-go v7.0.4 -> v7.0.5 * github.com/olivere/elastic v7.0.9 -> v7.0.20 * github.com/urfave/cli v1.20.0 -> v1.22.4 * github.com/prometheus/client_golang v1.1.0 -> v1.8.0 * github.com/xanzy/go-gitlab v0.37.0 -> v0.38.1 * mvdan.cc/xurls v2.1.0 -> v2.2.0 Co-authored-by: Lauris BH <lauris@nix.lv>
		
			
				
	
	
		
			1634 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			1634 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
package regexp2
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"math"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
	"unicode"
 | 
						|
 | 
						|
	"github.com/dlclark/regexp2/syntax"
 | 
						|
)
 | 
						|
 | 
						|
type runner struct {
 | 
						|
	re   *Regexp
 | 
						|
	code *syntax.Code
 | 
						|
 | 
						|
	runtextstart int // starting point for search
 | 
						|
 | 
						|
	runtext    []rune // text to search
 | 
						|
	runtextpos int    // current position in text
 | 
						|
	runtextend int
 | 
						|
 | 
						|
	// The backtracking stack.  Opcodes use this to store data regarding
 | 
						|
	// what they have matched and where to backtrack to.  Each "frame" on
 | 
						|
	// the stack takes the form of [CodePosition Data1 Data2...], where
 | 
						|
	// CodePosition is the position of the current opcode and
 | 
						|
	// the data values are all optional.  The CodePosition can be negative, and
 | 
						|
	// these values (also called "back2") are used by the BranchMark family of opcodes
 | 
						|
	// to indicate whether they are backtracking after a successful or failed
 | 
						|
	// match.
 | 
						|
	// When we backtrack, we pop the CodePosition off the stack, set the current
 | 
						|
	// instruction pointer to that code position, and mark the opcode
 | 
						|
	// with a backtracking flag ("Back").  Each opcode then knows how to
 | 
						|
	// handle its own data.
 | 
						|
	runtrack    []int
 | 
						|
	runtrackpos int
 | 
						|
 | 
						|
	// This stack is used to track text positions across different opcodes.
 | 
						|
	// For example, in /(a*b)+/, the parentheses result in a SetMark/CaptureMark
 | 
						|
	// pair. SetMark records the text position before we match a*b.  Then
 | 
						|
	// CaptureMark uses that position to figure out where the capture starts.
 | 
						|
	// Opcodes which push onto this stack are always paired with other opcodes
 | 
						|
	// which will pop the value from it later.  A successful match should mean
 | 
						|
	// that this stack is empty.
 | 
						|
	runstack    []int
 | 
						|
	runstackpos int
 | 
						|
 | 
						|
	// The crawl stack is used to keep track of captures.  Every time a group
 | 
						|
	// has a capture, we push its group number onto the runcrawl stack.  In
 | 
						|
	// the case of a balanced match, we push BOTH groups onto the stack.
 | 
						|
	runcrawl    []int
 | 
						|
	runcrawlpos int
 | 
						|
 | 
						|
	runtrackcount int // count of states that may do backtracking
 | 
						|
 | 
						|
	runmatch *Match // result object
 | 
						|
 | 
						|
	ignoreTimeout       bool
 | 
						|
	timeout             time.Duration // timeout in milliseconds (needed for actual)
 | 
						|
	timeoutChecksToSkip int
 | 
						|
	timeoutAt           time.Time
 | 
						|
 | 
						|
	operator        syntax.InstOp
 | 
						|
	codepos         int
 | 
						|
	rightToLeft     bool
 | 
						|
	caseInsensitive bool
 | 
						|
}
 | 
						|
 | 
						|
// run searches for matches and can continue from the previous match
 | 
						|
//
 | 
						|
// quick is usually false, but can be true to not return matches, just put it in caches
 | 
						|
// textstart is -1 to start at the "beginning" (depending on Right-To-Left), otherwise an index in input
 | 
						|
// input is the string to search for our regex pattern
 | 
						|
func (re *Regexp) run(quick bool, textstart int, input []rune) (*Match, error) {
 | 
						|
 | 
						|
	// get a cached runner
 | 
						|
	runner := re.getRunner()
 | 
						|
	defer re.putRunner(runner)
 | 
						|
 | 
						|
	if textstart < 0 {
 | 
						|
		if re.RightToLeft() {
 | 
						|
			textstart = len(input)
 | 
						|
		} else {
 | 
						|
			textstart = 0
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return runner.scan(input, textstart, quick, re.MatchTimeout)
 | 
						|
}
 | 
						|
 | 
						|
// Scans the string to find the first match. Uses the Match object
 | 
						|
// both to feed text in and as a place to store matches that come out.
 | 
						|
//
 | 
						|
// All the action is in the Go() method. Our
 | 
						|
// responsibility is to load up the class members before
 | 
						|
// calling Go.
 | 
						|
//
 | 
						|
// The optimizer can compute a set of candidate starting characters,
 | 
						|
// and we could use a separate method Skip() that will quickly scan past
 | 
						|
// any characters that we know can't match.
 | 
						|
func (r *runner) scan(rt []rune, textstart int, quick bool, timeout time.Duration) (*Match, error) {
 | 
						|
	r.timeout = timeout
 | 
						|
	r.ignoreTimeout = (time.Duration(math.MaxInt64) == timeout)
 | 
						|
	r.runtextstart = textstart
 | 
						|
	r.runtext = rt
 | 
						|
	r.runtextend = len(rt)
 | 
						|
 | 
						|
	stoppos := r.runtextend
 | 
						|
	bump := 1
 | 
						|
 | 
						|
	if r.re.RightToLeft() {
 | 
						|
		bump = -1
 | 
						|
		stoppos = 0
 | 
						|
	}
 | 
						|
 | 
						|
	r.runtextpos = textstart
 | 
						|
	initted := false
 | 
						|
 | 
						|
	r.startTimeoutWatch()
 | 
						|
	for {
 | 
						|
		if r.re.Debug() {
 | 
						|
			//fmt.Printf("\nSearch content: %v\n", string(r.runtext))
 | 
						|
			fmt.Printf("\nSearch range: from 0 to %v\n", r.runtextend)
 | 
						|
			fmt.Printf("Firstchar search starting at %v stopping at %v\n", r.runtextpos, stoppos)
 | 
						|
		}
 | 
						|
 | 
						|
		if r.findFirstChar() {
 | 
						|
			if err := r.checkTimeout(); err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
 | 
						|
			if !initted {
 | 
						|
				r.initMatch()
 | 
						|
				initted = true
 | 
						|
			}
 | 
						|
 | 
						|
			if r.re.Debug() {
 | 
						|
				fmt.Printf("Executing engine starting at %v\n\n", r.runtextpos)
 | 
						|
			}
 | 
						|
 | 
						|
			if err := r.execute(); err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
 | 
						|
			if r.runmatch.matchcount[0] > 0 {
 | 
						|
				// We'll return a match even if it touches a previous empty match
 | 
						|
				return r.tidyMatch(quick), nil
 | 
						|
			}
 | 
						|
 | 
						|
			// reset state for another go
 | 
						|
			r.runtrackpos = len(r.runtrack)
 | 
						|
			r.runstackpos = len(r.runstack)
 | 
						|
			r.runcrawlpos = len(r.runcrawl)
 | 
						|
		}
 | 
						|
 | 
						|
		// failure!
 | 
						|
 | 
						|
		if r.runtextpos == stoppos {
 | 
						|
			r.tidyMatch(true)
 | 
						|
			return nil, nil
 | 
						|
		}
 | 
						|
 | 
						|
		// Recognize leading []* and various anchors, and bump on failure accordingly
 | 
						|
 | 
						|
		// r.bump by one and start again
 | 
						|
 | 
						|
		r.runtextpos += bump
 | 
						|
	}
 | 
						|
	// We never get here
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) execute() error {
 | 
						|
 | 
						|
	r.goTo(0)
 | 
						|
 | 
						|
	for {
 | 
						|
 | 
						|
		if r.re.Debug() {
 | 
						|
			r.dumpState()
 | 
						|
		}
 | 
						|
 | 
						|
		if err := r.checkTimeout(); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		switch r.operator {
 | 
						|
		case syntax.Stop:
 | 
						|
			return nil
 | 
						|
 | 
						|
		case syntax.Nothing:
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Goto:
 | 
						|
			r.goTo(r.operand(0))
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Testref:
 | 
						|
			if !r.runmatch.isMatched(r.operand(0)) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Lazybranch:
 | 
						|
			r.trackPush1(r.textPos())
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Lazybranch | syntax.Back:
 | 
						|
			r.trackPop()
 | 
						|
			r.textto(r.trackPeek())
 | 
						|
			r.goTo(r.operand(0))
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setmark:
 | 
						|
			r.stackPush(r.textPos())
 | 
						|
			r.trackPush()
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Nullmark:
 | 
						|
			r.stackPush(-1)
 | 
						|
			r.trackPush()
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setmark | syntax.Back, syntax.Nullmark | syntax.Back:
 | 
						|
			r.stackPop()
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Getmark:
 | 
						|
			r.stackPop()
 | 
						|
			r.trackPush1(r.stackPeek())
 | 
						|
			r.textto(r.stackPeek())
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Getmark | syntax.Back:
 | 
						|
			r.trackPop()
 | 
						|
			r.stackPush(r.trackPeek())
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Capturemark:
 | 
						|
			if r.operand(1) != -1 && !r.runmatch.isMatched(r.operand(1)) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.stackPop()
 | 
						|
			if r.operand(1) != -1 {
 | 
						|
				r.transferCapture(r.operand(0), r.operand(1), r.stackPeek(), r.textPos())
 | 
						|
			} else {
 | 
						|
				r.capture(r.operand(0), r.stackPeek(), r.textPos())
 | 
						|
			}
 | 
						|
			r.trackPush1(r.stackPeek())
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Capturemark | syntax.Back:
 | 
						|
			r.trackPop()
 | 
						|
			r.stackPush(r.trackPeek())
 | 
						|
			r.uncapture()
 | 
						|
			if r.operand(0) != -1 && r.operand(1) != -1 {
 | 
						|
				r.uncapture()
 | 
						|
			}
 | 
						|
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Branchmark:
 | 
						|
			r.stackPop()
 | 
						|
 | 
						|
			matched := r.textPos() - r.stackPeek()
 | 
						|
 | 
						|
			if matched != 0 { // Nonempty match -> loop now
 | 
						|
				r.trackPush2(r.stackPeek(), r.textPos()) // Save old mark, textpos
 | 
						|
				r.stackPush(r.textPos())                 // Make new mark
 | 
						|
				r.goTo(r.operand(0))                     // Loop
 | 
						|
			} else { // Empty match -> straight now
 | 
						|
				r.trackPushNeg1(r.stackPeek()) // Save old mark
 | 
						|
				r.advance(1)                   // Straight
 | 
						|
			}
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Branchmark | syntax.Back:
 | 
						|
			r.trackPopN(2)
 | 
						|
			r.stackPop()
 | 
						|
			r.textto(r.trackPeekN(1))      // Recall position
 | 
						|
			r.trackPushNeg1(r.trackPeek()) // Save old mark
 | 
						|
			r.advance(1)                   // Straight
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Branchmark | syntax.Back2:
 | 
						|
			r.trackPop()
 | 
						|
			r.stackPush(r.trackPeek()) // Recall old mark
 | 
						|
			break                      // Backtrack
 | 
						|
 | 
						|
		case syntax.Lazybranchmark:
 | 
						|
			{
 | 
						|
				// We hit this the first time through a lazy loop and after each
 | 
						|
				// successful match of the inner expression.  It simply continues
 | 
						|
				// on and doesn't loop.
 | 
						|
				r.stackPop()
 | 
						|
 | 
						|
				oldMarkPos := r.stackPeek()
 | 
						|
 | 
						|
				if r.textPos() != oldMarkPos { // Nonempty match -> try to loop again by going to 'back' state
 | 
						|
					if oldMarkPos != -1 {
 | 
						|
						r.trackPush2(oldMarkPos, r.textPos()) // Save old mark, textpos
 | 
						|
					} else {
 | 
						|
						r.trackPush2(r.textPos(), r.textPos())
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					// The inner expression found an empty match, so we'll go directly to 'back2' if we
 | 
						|
					// backtrack.  In this case, we need to push something on the stack, since back2 pops.
 | 
						|
					// However, in the case of ()+? or similar, this empty match may be legitimate, so push the text
 | 
						|
					// position associated with that empty match.
 | 
						|
					r.stackPush(oldMarkPos)
 | 
						|
 | 
						|
					r.trackPushNeg1(r.stackPeek()) // Save old mark
 | 
						|
				}
 | 
						|
				r.advance(1)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
		case syntax.Lazybranchmark | syntax.Back:
 | 
						|
 | 
						|
			// After the first time, Lazybranchmark | syntax.Back occurs
 | 
						|
			// with each iteration of the loop, and therefore with every attempted
 | 
						|
			// match of the inner expression.  We'll try to match the inner expression,
 | 
						|
			// then go back to Lazybranchmark if successful.  If the inner expression
 | 
						|
			// fails, we go to Lazybranchmark | syntax.Back2
 | 
						|
 | 
						|
			r.trackPopN(2)
 | 
						|
			pos := r.trackPeekN(1)
 | 
						|
			r.trackPushNeg1(r.trackPeek()) // Save old mark
 | 
						|
			r.stackPush(pos)               // Make new mark
 | 
						|
			r.textto(pos)                  // Recall position
 | 
						|
			r.goTo(r.operand(0))           // Loop
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Lazybranchmark | syntax.Back2:
 | 
						|
			// The lazy loop has failed.  We'll do a true backtrack and
 | 
						|
			// start over before the lazy loop.
 | 
						|
			r.stackPop()
 | 
						|
			r.trackPop()
 | 
						|
			r.stackPush(r.trackPeek()) // Recall old mark
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Setcount:
 | 
						|
			r.stackPush2(r.textPos(), r.operand(0))
 | 
						|
			r.trackPush()
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Nullcount:
 | 
						|
			r.stackPush2(-1, r.operand(0))
 | 
						|
			r.trackPush()
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setcount | syntax.Back:
 | 
						|
			r.stackPopN(2)
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Nullcount | syntax.Back:
 | 
						|
			r.stackPopN(2)
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Branchcount:
 | 
						|
			// r.stackPush:
 | 
						|
			//  0: Mark
 | 
						|
			//  1: Count
 | 
						|
 | 
						|
			r.stackPopN(2)
 | 
						|
			mark := r.stackPeek()
 | 
						|
			count := r.stackPeekN(1)
 | 
						|
			matched := r.textPos() - mark
 | 
						|
 | 
						|
			if count >= r.operand(1) || (matched == 0 && count >= 0) { // Max loops or empty match -> straight now
 | 
						|
				r.trackPushNeg2(mark, count) // Save old mark, count
 | 
						|
				r.advance(2)                 // Straight
 | 
						|
			} else { // Nonempty match -> count+loop now
 | 
						|
				r.trackPush1(mark)                 // remember mark
 | 
						|
				r.stackPush2(r.textPos(), count+1) // Make new mark, incr count
 | 
						|
				r.goTo(r.operand(0))               // Loop
 | 
						|
			}
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Branchcount | syntax.Back:
 | 
						|
			// r.trackPush:
 | 
						|
			//  0: Previous mark
 | 
						|
			// r.stackPush:
 | 
						|
			//  0: Mark (= current pos, discarded)
 | 
						|
			//  1: Count
 | 
						|
			r.trackPop()
 | 
						|
			r.stackPopN(2)
 | 
						|
			if r.stackPeekN(1) > 0 { // Positive -> can go straight
 | 
						|
				r.textto(r.stackPeek())                           // Zap to mark
 | 
						|
				r.trackPushNeg2(r.trackPeek(), r.stackPeekN(1)-1) // Save old mark, old count
 | 
						|
				r.advance(2)                                      // Straight
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			r.stackPush2(r.trackPeek(), r.stackPeekN(1)-1) // recall old mark, old count
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Branchcount | syntax.Back2:
 | 
						|
			// r.trackPush:
 | 
						|
			//  0: Previous mark
 | 
						|
			//  1: Previous count
 | 
						|
			r.trackPopN(2)
 | 
						|
			r.stackPush2(r.trackPeek(), r.trackPeekN(1)) // Recall old mark, old count
 | 
						|
			break                                        // Backtrack
 | 
						|
 | 
						|
		case syntax.Lazybranchcount:
 | 
						|
			// r.stackPush:
 | 
						|
			//  0: Mark
 | 
						|
			//  1: Count
 | 
						|
 | 
						|
			r.stackPopN(2)
 | 
						|
			mark := r.stackPeek()
 | 
						|
			count := r.stackPeekN(1)
 | 
						|
 | 
						|
			if count < 0 { // Negative count -> loop now
 | 
						|
				r.trackPushNeg1(mark)              // Save old mark
 | 
						|
				r.stackPush2(r.textPos(), count+1) // Make new mark, incr count
 | 
						|
				r.goTo(r.operand(0))               // Loop
 | 
						|
			} else { // Nonneg count -> straight now
 | 
						|
				r.trackPush3(mark, count, r.textPos()) // Save mark, count, position
 | 
						|
				r.advance(2)                           // Straight
 | 
						|
			}
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Lazybranchcount | syntax.Back:
 | 
						|
			// r.trackPush:
 | 
						|
			//  0: Mark
 | 
						|
			//  1: Count
 | 
						|
			//  2: r.textPos
 | 
						|
 | 
						|
			r.trackPopN(3)
 | 
						|
			mark := r.trackPeek()
 | 
						|
			textpos := r.trackPeekN(2)
 | 
						|
 | 
						|
			if r.trackPeekN(1) < r.operand(1) && textpos != mark { // Under limit and not empty match -> loop
 | 
						|
				r.textto(textpos)                        // Recall position
 | 
						|
				r.stackPush2(textpos, r.trackPeekN(1)+1) // Make new mark, incr count
 | 
						|
				r.trackPushNeg1(mark)                    // Save old mark
 | 
						|
				r.goTo(r.operand(0))                     // Loop
 | 
						|
				continue
 | 
						|
			} else { // Max loops or empty match -> backtrack
 | 
						|
				r.stackPush2(r.trackPeek(), r.trackPeekN(1)) // Recall old mark, count
 | 
						|
				break                                        // backtrack
 | 
						|
			}
 | 
						|
 | 
						|
		case syntax.Lazybranchcount | syntax.Back2:
 | 
						|
			// r.trackPush:
 | 
						|
			//  0: Previous mark
 | 
						|
			// r.stackPush:
 | 
						|
			//  0: Mark (== current pos, discarded)
 | 
						|
			//  1: Count
 | 
						|
			r.trackPop()
 | 
						|
			r.stackPopN(2)
 | 
						|
			r.stackPush2(r.trackPeek(), r.stackPeekN(1)-1) // Recall old mark, count
 | 
						|
			break                                          // Backtrack
 | 
						|
 | 
						|
		case syntax.Setjump:
 | 
						|
			r.stackPush2(r.trackpos(), r.crawlpos())
 | 
						|
			r.trackPush()
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setjump | syntax.Back:
 | 
						|
			r.stackPopN(2)
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Backjump:
 | 
						|
			// r.stackPush:
 | 
						|
			//  0: Saved trackpos
 | 
						|
			//  1: r.crawlpos
 | 
						|
			r.stackPopN(2)
 | 
						|
			r.trackto(r.stackPeek())
 | 
						|
 | 
						|
			for r.crawlpos() != r.stackPeekN(1) {
 | 
						|
				r.uncapture()
 | 
						|
			}
 | 
						|
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Forejump:
 | 
						|
			// r.stackPush:
 | 
						|
			//  0: Saved trackpos
 | 
						|
			//  1: r.crawlpos
 | 
						|
			r.stackPopN(2)
 | 
						|
			r.trackto(r.stackPeek())
 | 
						|
			r.trackPush1(r.stackPeekN(1))
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Forejump | syntax.Back:
 | 
						|
			// r.trackPush:
 | 
						|
			//  0: r.crawlpos
 | 
						|
			r.trackPop()
 | 
						|
 | 
						|
			for r.crawlpos() != r.trackPeek() {
 | 
						|
				r.uncapture()
 | 
						|
			}
 | 
						|
 | 
						|
			break
 | 
						|
 | 
						|
		case syntax.Bol:
 | 
						|
			if r.leftchars() > 0 && r.charAt(r.textPos()-1) != '\n' {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Eol:
 | 
						|
			if r.rightchars() > 0 && r.charAt(r.textPos()) != '\n' {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Boundary:
 | 
						|
			if !r.isBoundary(r.textPos(), 0, r.runtextend) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Nonboundary:
 | 
						|
			if r.isBoundary(r.textPos(), 0, r.runtextend) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.ECMABoundary:
 | 
						|
			if !r.isECMABoundary(r.textPos(), 0, r.runtextend) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.NonECMABoundary:
 | 
						|
			if r.isECMABoundary(r.textPos(), 0, r.runtextend) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Beginning:
 | 
						|
			if r.leftchars() > 0 {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Start:
 | 
						|
			if r.textPos() != r.textstart() {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.EndZ:
 | 
						|
			rchars := r.rightchars()
 | 
						|
			if rchars > 1 {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			// RE2 and EcmaScript define $ as "asserts position at the end of the string"
 | 
						|
			// PCRE/.NET adds "or before the line terminator right at the end of the string (if any)"
 | 
						|
			if (r.re.options & (RE2 | ECMAScript)) != 0 {
 | 
						|
				// RE2/Ecmascript mode
 | 
						|
				if rchars > 0 {
 | 
						|
					break
 | 
						|
				}
 | 
						|
			} else if rchars == 1 && r.charAt(r.textPos()) != '\n' {
 | 
						|
				// "regular" mode
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.End:
 | 
						|
			if r.rightchars() > 0 {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			r.advance(0)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.One:
 | 
						|
			if r.forwardchars() < 1 || r.forwardcharnext() != rune(r.operand(0)) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Notone:
 | 
						|
			if r.forwardchars() < 1 || r.forwardcharnext() == rune(r.operand(0)) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Set:
 | 
						|
 | 
						|
			if r.forwardchars() < 1 || !r.code.Sets[r.operand(0)].CharIn(r.forwardcharnext()) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Multi:
 | 
						|
			if !r.runematch(r.code.Strings[r.operand(0)]) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Ref:
 | 
						|
 | 
						|
			capnum := r.operand(0)
 | 
						|
 | 
						|
			if r.runmatch.isMatched(capnum) {
 | 
						|
				if !r.refmatch(r.runmatch.matchIndex(capnum), r.runmatch.matchLength(capnum)) {
 | 
						|
					break
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				if (r.re.options & ECMAScript) == 0 {
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(1)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Onerep:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if r.forwardchars() < c {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			ch := rune(r.operand(0))
 | 
						|
 | 
						|
			for c > 0 {
 | 
						|
				if r.forwardcharnext() != ch {
 | 
						|
					goto BreakBackward
 | 
						|
				}
 | 
						|
				c--
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Notonerep:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if r.forwardchars() < c {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			ch := rune(r.operand(0))
 | 
						|
 | 
						|
			for c > 0 {
 | 
						|
				if r.forwardcharnext() == ch {
 | 
						|
					goto BreakBackward
 | 
						|
				}
 | 
						|
				c--
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setrep:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if r.forwardchars() < c {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			set := r.code.Sets[r.operand(0)]
 | 
						|
 | 
						|
			for c > 0 {
 | 
						|
				if !set.CharIn(r.forwardcharnext()) {
 | 
						|
					goto BreakBackward
 | 
						|
				}
 | 
						|
				c--
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Oneloop:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if c > r.forwardchars() {
 | 
						|
				c = r.forwardchars()
 | 
						|
			}
 | 
						|
 | 
						|
			ch := rune(r.operand(0))
 | 
						|
			i := c
 | 
						|
 | 
						|
			for ; i > 0; i-- {
 | 
						|
				if r.forwardcharnext() != ch {
 | 
						|
					r.backwardnext()
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if c > i {
 | 
						|
				r.trackPush2(c-i-1, r.textPos()-r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Notoneloop:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if c > r.forwardchars() {
 | 
						|
				c = r.forwardchars()
 | 
						|
			}
 | 
						|
 | 
						|
			ch := rune(r.operand(0))
 | 
						|
			i := c
 | 
						|
 | 
						|
			for ; i > 0; i-- {
 | 
						|
				if r.forwardcharnext() == ch {
 | 
						|
					r.backwardnext()
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if c > i {
 | 
						|
				r.trackPush2(c-i-1, r.textPos()-r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setloop:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if c > r.forwardchars() {
 | 
						|
				c = r.forwardchars()
 | 
						|
			}
 | 
						|
 | 
						|
			set := r.code.Sets[r.operand(0)]
 | 
						|
			i := c
 | 
						|
 | 
						|
			for ; i > 0; i-- {
 | 
						|
				if !set.CharIn(r.forwardcharnext()) {
 | 
						|
					r.backwardnext()
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if c > i {
 | 
						|
				r.trackPush2(c-i-1, r.textPos()-r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Oneloop | syntax.Back, syntax.Notoneloop | syntax.Back:
 | 
						|
 | 
						|
			r.trackPopN(2)
 | 
						|
			i := r.trackPeek()
 | 
						|
			pos := r.trackPeekN(1)
 | 
						|
 | 
						|
			r.textto(pos)
 | 
						|
 | 
						|
			if i > 0 {
 | 
						|
				r.trackPush2(i-1, pos-r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setloop | syntax.Back:
 | 
						|
 | 
						|
			r.trackPopN(2)
 | 
						|
			i := r.trackPeek()
 | 
						|
			pos := r.trackPeekN(1)
 | 
						|
 | 
						|
			r.textto(pos)
 | 
						|
 | 
						|
			if i > 0 {
 | 
						|
				r.trackPush2(i-1, pos-r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Onelazy, syntax.Notonelazy:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if c > r.forwardchars() {
 | 
						|
				c = r.forwardchars()
 | 
						|
			}
 | 
						|
 | 
						|
			if c > 0 {
 | 
						|
				r.trackPush2(c-1, r.textPos())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setlazy:
 | 
						|
 | 
						|
			c := r.operand(1)
 | 
						|
 | 
						|
			if c > r.forwardchars() {
 | 
						|
				c = r.forwardchars()
 | 
						|
			}
 | 
						|
 | 
						|
			if c > 0 {
 | 
						|
				r.trackPush2(c-1, r.textPos())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Onelazy | syntax.Back:
 | 
						|
 | 
						|
			r.trackPopN(2)
 | 
						|
			pos := r.trackPeekN(1)
 | 
						|
			r.textto(pos)
 | 
						|
 | 
						|
			if r.forwardcharnext() != rune(r.operand(0)) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			i := r.trackPeek()
 | 
						|
 | 
						|
			if i > 0 {
 | 
						|
				r.trackPush2(i-1, pos+r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Notonelazy | syntax.Back:
 | 
						|
 | 
						|
			r.trackPopN(2)
 | 
						|
			pos := r.trackPeekN(1)
 | 
						|
			r.textto(pos)
 | 
						|
 | 
						|
			if r.forwardcharnext() == rune(r.operand(0)) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			i := r.trackPeek()
 | 
						|
 | 
						|
			if i > 0 {
 | 
						|
				r.trackPush2(i-1, pos+r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		case syntax.Setlazy | syntax.Back:
 | 
						|
 | 
						|
			r.trackPopN(2)
 | 
						|
			pos := r.trackPeekN(1)
 | 
						|
			r.textto(pos)
 | 
						|
 | 
						|
			if !r.code.Sets[r.operand(0)].CharIn(r.forwardcharnext()) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
 | 
						|
			i := r.trackPeek()
 | 
						|
 | 
						|
			if i > 0 {
 | 
						|
				r.trackPush2(i-1, pos+r.bump())
 | 
						|
			}
 | 
						|
 | 
						|
			r.advance(2)
 | 
						|
			continue
 | 
						|
 | 
						|
		default:
 | 
						|
			return errors.New("unknown state in regex runner")
 | 
						|
		}
 | 
						|
 | 
						|
	BreakBackward:
 | 
						|
		;
 | 
						|
 | 
						|
		// "break Backward" comes here:
 | 
						|
		r.backtrack()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// increase the size of stack and track storage
 | 
						|
func (r *runner) ensureStorage() {
 | 
						|
	if r.runstackpos < r.runtrackcount*4 {
 | 
						|
		doubleIntSlice(&r.runstack, &r.runstackpos)
 | 
						|
	}
 | 
						|
	if r.runtrackpos < r.runtrackcount*4 {
 | 
						|
		doubleIntSlice(&r.runtrack, &r.runtrackpos)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func doubleIntSlice(s *[]int, pos *int) {
 | 
						|
	oldLen := len(*s)
 | 
						|
	newS := make([]int, oldLen*2)
 | 
						|
 | 
						|
	copy(newS[oldLen:], *s)
 | 
						|
	*pos += oldLen
 | 
						|
	*s = newS
 | 
						|
}
 | 
						|
 | 
						|
// Save a number on the longjump unrolling stack
 | 
						|
func (r *runner) crawl(i int) {
 | 
						|
	if r.runcrawlpos == 0 {
 | 
						|
		doubleIntSlice(&r.runcrawl, &r.runcrawlpos)
 | 
						|
	}
 | 
						|
	r.runcrawlpos--
 | 
						|
	r.runcrawl[r.runcrawlpos] = i
 | 
						|
}
 | 
						|
 | 
						|
// Remove a number from the longjump unrolling stack
 | 
						|
func (r *runner) popcrawl() int {
 | 
						|
	val := r.runcrawl[r.runcrawlpos]
 | 
						|
	r.runcrawlpos++
 | 
						|
	return val
 | 
						|
}
 | 
						|
 | 
						|
// Get the height of the stack
 | 
						|
func (r *runner) crawlpos() int {
 | 
						|
	return len(r.runcrawl) - r.runcrawlpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) advance(i int) {
 | 
						|
	r.codepos += (i + 1)
 | 
						|
	r.setOperator(r.code.Codes[r.codepos])
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) goTo(newpos int) {
 | 
						|
	// when branching backward or in place, ensure storage
 | 
						|
	if newpos <= r.codepos {
 | 
						|
		r.ensureStorage()
 | 
						|
	}
 | 
						|
 | 
						|
	r.setOperator(r.code.Codes[newpos])
 | 
						|
	r.codepos = newpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) textto(newpos int) {
 | 
						|
	r.runtextpos = newpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackto(newpos int) {
 | 
						|
	r.runtrackpos = len(r.runtrack) - newpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) textstart() int {
 | 
						|
	return r.runtextstart
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) textPos() int {
 | 
						|
	return r.runtextpos
 | 
						|
}
 | 
						|
 | 
						|
// push onto the backtracking stack
 | 
						|
func (r *runner) trackpos() int {
 | 
						|
	return len(r.runtrack) - r.runtrackpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackPush() {
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = r.codepos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackPush1(I1 int) {
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I1
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = r.codepos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackPush2(I1, I2 int) {
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I1
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I2
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = r.codepos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackPush3(I1, I2, I3 int) {
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I1
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I2
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I3
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = r.codepos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackPushNeg1(I1 int) {
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I1
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = -r.codepos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackPushNeg2(I1, I2 int) {
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I1
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = I2
 | 
						|
	r.runtrackpos--
 | 
						|
	r.runtrack[r.runtrackpos] = -r.codepos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) backtrack() {
 | 
						|
	newpos := r.runtrack[r.runtrackpos]
 | 
						|
	r.runtrackpos++
 | 
						|
 | 
						|
	if r.re.Debug() {
 | 
						|
		if newpos < 0 {
 | 
						|
			fmt.Printf("       Backtracking (back2) to code position %v\n", -newpos)
 | 
						|
		} else {
 | 
						|
			fmt.Printf("       Backtracking to code position %v\n", newpos)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if newpos < 0 {
 | 
						|
		newpos = -newpos
 | 
						|
		r.setOperator(r.code.Codes[newpos] | syntax.Back2)
 | 
						|
	} else {
 | 
						|
		r.setOperator(r.code.Codes[newpos] | syntax.Back)
 | 
						|
	}
 | 
						|
 | 
						|
	// When branching backward, ensure storage
 | 
						|
	if newpos < r.codepos {
 | 
						|
		r.ensureStorage()
 | 
						|
	}
 | 
						|
 | 
						|
	r.codepos = newpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) setOperator(op int) {
 | 
						|
	r.caseInsensitive = (0 != (op & syntax.Ci))
 | 
						|
	r.rightToLeft = (0 != (op & syntax.Rtl))
 | 
						|
	r.operator = syntax.InstOp(op & ^(syntax.Rtl | syntax.Ci))
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) trackPop() {
 | 
						|
	r.runtrackpos++
 | 
						|
}
 | 
						|
 | 
						|
// pop framesize items from the backtracking stack
 | 
						|
func (r *runner) trackPopN(framesize int) {
 | 
						|
	r.runtrackpos += framesize
 | 
						|
}
 | 
						|
 | 
						|
// Technically we are actually peeking at items already popped.  So if you want to
 | 
						|
// get and pop the top item from the stack, you do
 | 
						|
// r.trackPop();
 | 
						|
// r.trackPeek();
 | 
						|
func (r *runner) trackPeek() int {
 | 
						|
	return r.runtrack[r.runtrackpos-1]
 | 
						|
}
 | 
						|
 | 
						|
// get the ith element down on the backtracking stack
 | 
						|
func (r *runner) trackPeekN(i int) int {
 | 
						|
	return r.runtrack[r.runtrackpos-i-1]
 | 
						|
}
 | 
						|
 | 
						|
// Push onto the grouping stack
 | 
						|
func (r *runner) stackPush(I1 int) {
 | 
						|
	r.runstackpos--
 | 
						|
	r.runstack[r.runstackpos] = I1
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) stackPush2(I1, I2 int) {
 | 
						|
	r.runstackpos--
 | 
						|
	r.runstack[r.runstackpos] = I1
 | 
						|
	r.runstackpos--
 | 
						|
	r.runstack[r.runstackpos] = I2
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) stackPop() {
 | 
						|
	r.runstackpos++
 | 
						|
}
 | 
						|
 | 
						|
// pop framesize items from the grouping stack
 | 
						|
func (r *runner) stackPopN(framesize int) {
 | 
						|
	r.runstackpos += framesize
 | 
						|
}
 | 
						|
 | 
						|
// Technically we are actually peeking at items already popped.  So if you want to
 | 
						|
// get and pop the top item from the stack, you do
 | 
						|
// r.stackPop();
 | 
						|
// r.stackPeek();
 | 
						|
func (r *runner) stackPeek() int {
 | 
						|
	return r.runstack[r.runstackpos-1]
 | 
						|
}
 | 
						|
 | 
						|
// get the ith element down on the grouping stack
 | 
						|
func (r *runner) stackPeekN(i int) int {
 | 
						|
	return r.runstack[r.runstackpos-i-1]
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) operand(i int) int {
 | 
						|
	return r.code.Codes[r.codepos+i+1]
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) leftchars() int {
 | 
						|
	return r.runtextpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) rightchars() int {
 | 
						|
	return r.runtextend - r.runtextpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) bump() int {
 | 
						|
	if r.rightToLeft {
 | 
						|
		return -1
 | 
						|
	}
 | 
						|
	return 1
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) forwardchars() int {
 | 
						|
	if r.rightToLeft {
 | 
						|
		return r.runtextpos
 | 
						|
	}
 | 
						|
	return r.runtextend - r.runtextpos
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) forwardcharnext() rune {
 | 
						|
	var ch rune
 | 
						|
	if r.rightToLeft {
 | 
						|
		r.runtextpos--
 | 
						|
		ch = r.runtext[r.runtextpos]
 | 
						|
	} else {
 | 
						|
		ch = r.runtext[r.runtextpos]
 | 
						|
		r.runtextpos++
 | 
						|
	}
 | 
						|
 | 
						|
	if r.caseInsensitive {
 | 
						|
		return unicode.ToLower(ch)
 | 
						|
	}
 | 
						|
	return ch
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) runematch(str []rune) bool {
 | 
						|
	var pos int
 | 
						|
 | 
						|
	c := len(str)
 | 
						|
	if !r.rightToLeft {
 | 
						|
		if r.runtextend-r.runtextpos < c {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		pos = r.runtextpos + c
 | 
						|
	} else {
 | 
						|
		if r.runtextpos-0 < c {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		pos = r.runtextpos
 | 
						|
	}
 | 
						|
 | 
						|
	if !r.caseInsensitive {
 | 
						|
		for c != 0 {
 | 
						|
			c--
 | 
						|
			pos--
 | 
						|
			if str[c] != r.runtext[pos] {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		for c != 0 {
 | 
						|
			c--
 | 
						|
			pos--
 | 
						|
			if str[c] != unicode.ToLower(r.runtext[pos]) {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if !r.rightToLeft {
 | 
						|
		pos += len(str)
 | 
						|
	}
 | 
						|
 | 
						|
	r.runtextpos = pos
 | 
						|
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) refmatch(index, len int) bool {
 | 
						|
	var c, pos, cmpos int
 | 
						|
 | 
						|
	if !r.rightToLeft {
 | 
						|
		if r.runtextend-r.runtextpos < len {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		pos = r.runtextpos + len
 | 
						|
	} else {
 | 
						|
		if r.runtextpos-0 < len {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		pos = r.runtextpos
 | 
						|
	}
 | 
						|
	cmpos = index + len
 | 
						|
 | 
						|
	c = len
 | 
						|
 | 
						|
	if !r.caseInsensitive {
 | 
						|
		for c != 0 {
 | 
						|
			c--
 | 
						|
			cmpos--
 | 
						|
			pos--
 | 
						|
			if r.runtext[cmpos] != r.runtext[pos] {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		for c != 0 {
 | 
						|
			c--
 | 
						|
			cmpos--
 | 
						|
			pos--
 | 
						|
 | 
						|
			if unicode.ToLower(r.runtext[cmpos]) != unicode.ToLower(r.runtext[pos]) {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if !r.rightToLeft {
 | 
						|
		pos += len
 | 
						|
	}
 | 
						|
 | 
						|
	r.runtextpos = pos
 | 
						|
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) backwardnext() {
 | 
						|
	if r.rightToLeft {
 | 
						|
		r.runtextpos++
 | 
						|
	} else {
 | 
						|
		r.runtextpos--
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) charAt(j int) rune {
 | 
						|
	return r.runtext[j]
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) findFirstChar() bool {
 | 
						|
 | 
						|
	if 0 != (r.code.Anchors & (syntax.AnchorBeginning | syntax.AnchorStart | syntax.AnchorEndZ | syntax.AnchorEnd)) {
 | 
						|
		if !r.code.RightToLeft {
 | 
						|
			if (0 != (r.code.Anchors&syntax.AnchorBeginning) && r.runtextpos > 0) ||
 | 
						|
				(0 != (r.code.Anchors&syntax.AnchorStart) && r.runtextpos > r.runtextstart) {
 | 
						|
				r.runtextpos = r.runtextend
 | 
						|
				return false
 | 
						|
			}
 | 
						|
			if 0 != (r.code.Anchors&syntax.AnchorEndZ) && r.runtextpos < r.runtextend-1 {
 | 
						|
				r.runtextpos = r.runtextend - 1
 | 
						|
			} else if 0 != (r.code.Anchors&syntax.AnchorEnd) && r.runtextpos < r.runtextend {
 | 
						|
				r.runtextpos = r.runtextend
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			if (0 != (r.code.Anchors&syntax.AnchorEnd) && r.runtextpos < r.runtextend) ||
 | 
						|
				(0 != (r.code.Anchors&syntax.AnchorEndZ) && (r.runtextpos < r.runtextend-1 ||
 | 
						|
					(r.runtextpos == r.runtextend-1 && r.charAt(r.runtextpos) != '\n'))) ||
 | 
						|
				(0 != (r.code.Anchors&syntax.AnchorStart) && r.runtextpos < r.runtextstart) {
 | 
						|
				r.runtextpos = 0
 | 
						|
				return false
 | 
						|
			}
 | 
						|
			if 0 != (r.code.Anchors&syntax.AnchorBeginning) && r.runtextpos > 0 {
 | 
						|
				r.runtextpos = 0
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if r.code.BmPrefix != nil {
 | 
						|
			return r.code.BmPrefix.IsMatch(r.runtext, r.runtextpos, 0, r.runtextend)
 | 
						|
		}
 | 
						|
 | 
						|
		return true // found a valid start or end anchor
 | 
						|
	} else if r.code.BmPrefix != nil {
 | 
						|
		r.runtextpos = r.code.BmPrefix.Scan(r.runtext, r.runtextpos, 0, r.runtextend)
 | 
						|
 | 
						|
		if r.runtextpos == -1 {
 | 
						|
			if r.code.RightToLeft {
 | 
						|
				r.runtextpos = 0
 | 
						|
			} else {
 | 
						|
				r.runtextpos = r.runtextend
 | 
						|
			}
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		return true
 | 
						|
	} else if r.code.FcPrefix == nil {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	r.rightToLeft = r.code.RightToLeft
 | 
						|
	r.caseInsensitive = r.code.FcPrefix.CaseInsensitive
 | 
						|
 | 
						|
	set := r.code.FcPrefix.PrefixSet
 | 
						|
	if set.IsSingleton() {
 | 
						|
		ch := set.SingletonChar()
 | 
						|
		for i := r.forwardchars(); i > 0; i-- {
 | 
						|
			if ch == r.forwardcharnext() {
 | 
						|
				r.backwardnext()
 | 
						|
				return true
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		for i := r.forwardchars(); i > 0; i-- {
 | 
						|
			n := r.forwardcharnext()
 | 
						|
			//fmt.Printf("%v in %v: %v\n", string(n), set.String(), set.CharIn(n))
 | 
						|
			if set.CharIn(n) {
 | 
						|
				r.backwardnext()
 | 
						|
				return true
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) initMatch() {
 | 
						|
	// Use a hashtable'ed Match object if the capture numbers are sparse
 | 
						|
 | 
						|
	if r.runmatch == nil {
 | 
						|
		if r.re.caps != nil {
 | 
						|
			r.runmatch = newMatchSparse(r.re, r.re.caps, r.re.capsize, r.runtext, r.runtextstart)
 | 
						|
		} else {
 | 
						|
			r.runmatch = newMatch(r.re, r.re.capsize, r.runtext, r.runtextstart)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		r.runmatch.reset(r.runtext, r.runtextstart)
 | 
						|
	}
 | 
						|
 | 
						|
	// note we test runcrawl, because it is the last one to be allocated
 | 
						|
	// If there is an alloc failure in the middle of the three allocations,
 | 
						|
	// we may still return to reuse this instance, and we want to behave
 | 
						|
	// as if the allocations didn't occur. (we used to test _trackcount != 0)
 | 
						|
 | 
						|
	if r.runcrawl != nil {
 | 
						|
		r.runtrackpos = len(r.runtrack)
 | 
						|
		r.runstackpos = len(r.runstack)
 | 
						|
		r.runcrawlpos = len(r.runcrawl)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	r.initTrackCount()
 | 
						|
 | 
						|
	tracksize := r.runtrackcount * 8
 | 
						|
	stacksize := r.runtrackcount * 8
 | 
						|
 | 
						|
	if tracksize < 32 {
 | 
						|
		tracksize = 32
 | 
						|
	}
 | 
						|
	if stacksize < 16 {
 | 
						|
		stacksize = 16
 | 
						|
	}
 | 
						|
 | 
						|
	r.runtrack = make([]int, tracksize)
 | 
						|
	r.runtrackpos = tracksize
 | 
						|
 | 
						|
	r.runstack = make([]int, stacksize)
 | 
						|
	r.runstackpos = stacksize
 | 
						|
 | 
						|
	r.runcrawl = make([]int, 32)
 | 
						|
	r.runcrawlpos = 32
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) tidyMatch(quick bool) *Match {
 | 
						|
	if !quick {
 | 
						|
		match := r.runmatch
 | 
						|
 | 
						|
		r.runmatch = nil
 | 
						|
 | 
						|
		match.tidy(r.runtextpos)
 | 
						|
		return match
 | 
						|
	} else {
 | 
						|
		// send back our match -- it's not leaving the package, so it's safe to not clean it up
 | 
						|
		// this reduces allocs for frequent calls to the "IsMatch" bool-only functions
 | 
						|
		return r.runmatch
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// capture captures a subexpression. Note that the
 | 
						|
// capnum used here has already been mapped to a non-sparse
 | 
						|
// index (by the code generator RegexWriter).
 | 
						|
func (r *runner) capture(capnum, start, end int) {
 | 
						|
	if end < start {
 | 
						|
		T := end
 | 
						|
		end = start
 | 
						|
		start = T
 | 
						|
	}
 | 
						|
 | 
						|
	r.crawl(capnum)
 | 
						|
	r.runmatch.addMatch(capnum, start, end-start)
 | 
						|
}
 | 
						|
 | 
						|
// transferCapture captures a subexpression. Note that the
 | 
						|
// capnum used here has already been mapped to a non-sparse
 | 
						|
// index (by the code generator RegexWriter).
 | 
						|
func (r *runner) transferCapture(capnum, uncapnum, start, end int) {
 | 
						|
	var start2, end2 int
 | 
						|
 | 
						|
	// these are the two intervals that are cancelling each other
 | 
						|
 | 
						|
	if end < start {
 | 
						|
		T := end
 | 
						|
		end = start
 | 
						|
		start = T
 | 
						|
	}
 | 
						|
 | 
						|
	start2 = r.runmatch.matchIndex(uncapnum)
 | 
						|
	end2 = start2 + r.runmatch.matchLength(uncapnum)
 | 
						|
 | 
						|
	// The new capture gets the innermost defined interval
 | 
						|
 | 
						|
	if start >= end2 {
 | 
						|
		end = start
 | 
						|
		start = end2
 | 
						|
	} else if end <= start2 {
 | 
						|
		start = start2
 | 
						|
	} else {
 | 
						|
		if end > end2 {
 | 
						|
			end = end2
 | 
						|
		}
 | 
						|
		if start2 > start {
 | 
						|
			start = start2
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	r.crawl(uncapnum)
 | 
						|
	r.runmatch.balanceMatch(uncapnum)
 | 
						|
 | 
						|
	if capnum != -1 {
 | 
						|
		r.crawl(capnum)
 | 
						|
		r.runmatch.addMatch(capnum, start, end-start)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// revert the last capture
 | 
						|
func (r *runner) uncapture() {
 | 
						|
	capnum := r.popcrawl()
 | 
						|
	r.runmatch.removeMatch(capnum)
 | 
						|
}
 | 
						|
 | 
						|
//debug
 | 
						|
 | 
						|
func (r *runner) dumpState() {
 | 
						|
	back := ""
 | 
						|
	if r.operator&syntax.Back != 0 {
 | 
						|
		back = " Back"
 | 
						|
	}
 | 
						|
	if r.operator&syntax.Back2 != 0 {
 | 
						|
		back += " Back2"
 | 
						|
	}
 | 
						|
	fmt.Printf("Text:  %v\nTrack: %v\nStack: %v\n       %s%s\n\n",
 | 
						|
		r.textposDescription(),
 | 
						|
		r.stackDescription(r.runtrack, r.runtrackpos),
 | 
						|
		r.stackDescription(r.runstack, r.runstackpos),
 | 
						|
		r.code.OpcodeDescription(r.codepos),
 | 
						|
		back)
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) stackDescription(a []int, index int) string {
 | 
						|
	buf := &bytes.Buffer{}
 | 
						|
 | 
						|
	fmt.Fprintf(buf, "%v/%v", len(a)-index, len(a))
 | 
						|
	if buf.Len() < 8 {
 | 
						|
		buf.WriteString(strings.Repeat(" ", 8-buf.Len()))
 | 
						|
	}
 | 
						|
 | 
						|
	buf.WriteRune('(')
 | 
						|
	for i := index; i < len(a); i++ {
 | 
						|
		if i > index {
 | 
						|
			buf.WriteRune(' ')
 | 
						|
		}
 | 
						|
 | 
						|
		buf.WriteString(strconv.Itoa(a[i]))
 | 
						|
	}
 | 
						|
 | 
						|
	buf.WriteRune(')')
 | 
						|
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) textposDescription() string {
 | 
						|
	buf := &bytes.Buffer{}
 | 
						|
 | 
						|
	buf.WriteString(strconv.Itoa(r.runtextpos))
 | 
						|
 | 
						|
	if buf.Len() < 8 {
 | 
						|
		buf.WriteString(strings.Repeat(" ", 8-buf.Len()))
 | 
						|
	}
 | 
						|
 | 
						|
	if r.runtextpos > 0 {
 | 
						|
		buf.WriteString(syntax.CharDescription(r.runtext[r.runtextpos-1]))
 | 
						|
	} else {
 | 
						|
		buf.WriteRune('^')
 | 
						|
	}
 | 
						|
 | 
						|
	buf.WriteRune('>')
 | 
						|
 | 
						|
	for i := r.runtextpos; i < r.runtextend; i++ {
 | 
						|
		buf.WriteString(syntax.CharDescription(r.runtext[i]))
 | 
						|
	}
 | 
						|
	if buf.Len() >= 64 {
 | 
						|
		buf.Truncate(61)
 | 
						|
		buf.WriteString("...")
 | 
						|
	} else {
 | 
						|
		buf.WriteRune('$')
 | 
						|
	}
 | 
						|
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
// decide whether the pos
 | 
						|
// at the specified index is a boundary or not. It's just not worth
 | 
						|
// emitting inline code for this logic.
 | 
						|
func (r *runner) isBoundary(index, startpos, endpos int) bool {
 | 
						|
	return (index > startpos && syntax.IsWordChar(r.runtext[index-1])) !=
 | 
						|
		(index < endpos && syntax.IsWordChar(r.runtext[index]))
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) isECMABoundary(index, startpos, endpos int) bool {
 | 
						|
	return (index > startpos && syntax.IsECMAWordChar(r.runtext[index-1])) !=
 | 
						|
		(index < endpos && syntax.IsECMAWordChar(r.runtext[index]))
 | 
						|
}
 | 
						|
 | 
						|
// this seems like a comment to justify randomly picking 1000 :-P
 | 
						|
// We have determined this value in a series of experiments where x86 retail
 | 
						|
// builds (ono-lab-optimized) were run on different pattern/input pairs. Larger values
 | 
						|
// of TimeoutCheckFrequency did not tend to increase performance; smaller values
 | 
						|
// of TimeoutCheckFrequency tended to slow down the execution.
 | 
						|
const timeoutCheckFrequency int = 1000
 | 
						|
 | 
						|
func (r *runner) startTimeoutWatch() {
 | 
						|
	if r.ignoreTimeout {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	r.timeoutChecksToSkip = timeoutCheckFrequency
 | 
						|
	r.timeoutAt = time.Now().Add(r.timeout)
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) checkTimeout() error {
 | 
						|
	if r.ignoreTimeout {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	r.timeoutChecksToSkip--
 | 
						|
	if r.timeoutChecksToSkip != 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	r.timeoutChecksToSkip = timeoutCheckFrequency
 | 
						|
	return r.doCheckTimeout()
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) doCheckTimeout() error {
 | 
						|
	current := time.Now()
 | 
						|
 | 
						|
	if current.Before(r.timeoutAt) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if r.re.Debug() {
 | 
						|
		//Debug.WriteLine("")
 | 
						|
		//Debug.WriteLine("RegEx match timeout occurred!")
 | 
						|
		//Debug.WriteLine("Specified timeout:       " + TimeSpan.FromMilliseconds(_timeout).ToString())
 | 
						|
		//Debug.WriteLine("Timeout check frequency: " + TimeoutCheckFrequency)
 | 
						|
		//Debug.WriteLine("Search pattern:          " + _runregex._pattern)
 | 
						|
		//Debug.WriteLine("Input:                   " + r.runtext)
 | 
						|
		//Debug.WriteLine("About to throw RegexMatchTimeoutException.")
 | 
						|
	}
 | 
						|
 | 
						|
	return fmt.Errorf("match timeout after %v on input `%v`", r.timeout, string(r.runtext))
 | 
						|
}
 | 
						|
 | 
						|
func (r *runner) initTrackCount() {
 | 
						|
	r.runtrackcount = r.code.TrackCount
 | 
						|
}
 | 
						|
 | 
						|
// getRunner returns a run to use for matching re.
 | 
						|
// It uses the re's runner cache if possible, to avoid
 | 
						|
// unnecessary allocation.
 | 
						|
func (re *Regexp) getRunner() *runner {
 | 
						|
	re.muRun.Lock()
 | 
						|
	if n := len(re.runner); n > 0 {
 | 
						|
		z := re.runner[n-1]
 | 
						|
		re.runner = re.runner[:n-1]
 | 
						|
		re.muRun.Unlock()
 | 
						|
		return z
 | 
						|
	}
 | 
						|
	re.muRun.Unlock()
 | 
						|
	z := &runner{
 | 
						|
		re:   re,
 | 
						|
		code: re.code,
 | 
						|
	}
 | 
						|
	return z
 | 
						|
}
 | 
						|
 | 
						|
// putRunner returns a runner to the re's cache.
 | 
						|
// There is no attempt to limit the size of the cache, so it will
 | 
						|
// grow to the maximum number of simultaneous matches
 | 
						|
// run using re.  (The cache empties when re gets garbage collected.)
 | 
						|
func (re *Regexp) putRunner(r *runner) {
 | 
						|
	re.muRun.Lock()
 | 
						|
	re.runner = append(re.runner, r)
 | 
						|
	re.muRun.Unlock()
 | 
						|
}
 |