From: Michael Matloob Date: Mon, 6 Apr 2015 20:33:47 +0000 (-0700) Subject: regexp: set b.cap[0] and b.cap[1] only when captures requested X-Git-Tag: go1.5beta1~1046 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=485f3489791cf3bd79c870e8066a4f541e397841;p=gostls13.git regexp: set b.cap[0] and b.cap[1] only when captures requested Fixes #10319 Change-Id: I96015b0e1dff30a72de11fea3837638b5c672891 Reviewed-on: https://go-review.googlesource.com/8501 Reviewed-by: Russ Cox Run-TryBot: Brad Fitzpatrick --- diff --git a/src/regexp/all_test.go b/src/regexp/all_test.go index 01ea3742a8..d78ae6a4cd 100644 --- a/src/regexp/all_test.go +++ b/src/regexp/all_test.go @@ -489,6 +489,17 @@ func TestOnePassCutoff(t *testing.T) { } } +// Check that the same machine can be used with the standard matcher +// and then the backtracker when there are no captures. +func TestSwitchBacktrack(t *testing.T) { + re := MustCompile(`a|b`) + long := make([]byte, maxBacktrackVector+1) + + // The following sequence of Match calls used to panic. See issue #10319. + re.Match(long) // triggers standard matcher + re.Match(long[:1]) // triggers backtracker +} + func BenchmarkLiteral(b *testing.B) { x := strings.Repeat("x", 50) + "y" b.StopTimer() diff --git a/src/regexp/backtrack.go b/src/regexp/backtrack.go index a387fa66ac..fd95604fe4 100644 --- a/src/regexp/backtrack.go +++ b/src/regexp/backtrack.go @@ -36,7 +36,6 @@ type bitState struct { end int cap []int - reqcap bool // whether any captures are requested input input jobs []job visited []uint32 @@ -72,12 +71,10 @@ func shouldBacktrack(prog *syntax.Prog) bool { } // reset resets the state of the backtracker. -// end is the end position in the input. ncap and reqcap are the number -// of the machine's capture registers and the number of user-requested -// captures respectively. -func (b *bitState) reset(end int, ncap int, reqcap int) { +// end is the end position in the input. +// ncap is the number of captures. +func (b *bitState) reset(end int, ncap int) { b.end = end - b.reqcap = reqcap > 0 if cap(b.jobs) == 0 { b.jobs = make([]job, 0, 256) @@ -95,8 +92,10 @@ func (b *bitState) reset(end int, ncap int, reqcap int) { } } - if len(b.cap) < ncap { + if cap(b.cap) < ncap { b.cap = make([]int, ncap) + } else { + b.cap = b.cap[:ncap] } for i := range b.cap { b.cap[i] = -1 @@ -271,7 +270,7 @@ func (m *machine) tryBacktrack(b *bitState, i input, pc uint32, pos int) bool { case syntax.InstMatch: // We found a match. If the caller doesn't care // where the match is, no point going further. - if !b.reqcap { + if len(b.cap) == 0 { m.matched = true return m.matched } @@ -279,7 +278,9 @@ func (m *machine) tryBacktrack(b *bitState, i input, pc uint32, pos int) bool { // Record best match so far. // Only need to check end point, because this entire // call is only considering one start position. - b.cap[1] = pos + if len(b.cap) > 1 { + b.cap[1] = pos + } if !m.matched || (longest && pos > 0 && pos > m.matchcap[1]) { copy(m.matchcap, b.cap) } @@ -305,7 +306,7 @@ func (m *machine) tryBacktrack(b *bitState, i input, pc uint32, pos int) bool { } // backtrack runs a backtracking search of prog on the input starting at pos. -func (m *machine) backtrack(i input, pos int, end int, reqcap int) bool { +func (m *machine) backtrack(i input, pos int, end int, ncap int) bool { if !i.canCheckPrefix() { panic("backtrack called for a RuneReader") } @@ -320,15 +321,18 @@ func (m *machine) backtrack(i input, pos int, end int, reqcap int) bool { } b := m.b - b.reset(end, len(m.matchcap), reqcap) + b.reset(end, ncap) + m.matchcap = m.matchcap[:ncap] for i := range m.matchcap { m.matchcap[i] = -1 } // Anchored search must start at the beginning of the input if startCond&syntax.EmptyBeginText != 0 { - b.cap[0] = pos + if len(b.cap) > 0 { + b.cap[0] = pos + } return m.tryBacktrack(b, i, uint32(m.p.Start), pos) } @@ -349,7 +353,9 @@ func (m *machine) backtrack(i input, pos int, end int, reqcap int) bool { pos += advance } - b.cap[0] = pos + if len(b.cap) > 0 { + b.cap[0] = pos + } if m.tryBacktrack(b, i, uint32(m.p.Start), pos) { // Match must be leftmost; done. return true