1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:04:44 -07:00

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 <rsc@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Michael Matloob 2015-04-06 13:33:47 -07:00 committed by Brad Fitzpatrick
parent 92189a2be2
commit 485f348979
2 changed files with 30 additions and 13 deletions

View File

@ -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()

View File

@ -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