1
0
mirror of https://github.com/golang/go synced 2024-11-23 20:00:04 -07:00

regexp/syntax: don't waste time checking for one pass algorithm

The code recurs very deeply in cases like (?:x{1,1000}){1,1000}
Since if much time is spent checking whether one pass is possible, it's not
worth doing at all, a simple fix is proposed: Stop if the check takes too long.
To do this, we simply avoid machines with >1000 instructions.

Benchmarks show a percent or less change either way, effectively zero.

Fixes #7608.

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/92290043
This commit is contained in:
Rob Pike 2014-05-13 12:17:49 -07:00
parent 5bc1cef869
commit f54f790a77
2 changed files with 15 additions and 0 deletions

View File

@ -473,6 +473,11 @@ func TestSplit(t *testing.T) {
}
}
// This ran out of stack before issue 7608 was fixed.
func TestOnePassCutoff(t *testing.T) {
MustCompile(`^(?:x{1,1000}){1,1000}$`)
}
func BenchmarkLiteral(b *testing.B) {
x := strings.Repeat("x", 50) + "y"
b.StopTimer()
@ -588,6 +593,7 @@ func BenchmarkOnePassShortA(b *testing.B) {
re.Match(x)
}
}
func BenchmarkNotOnePassShortA(b *testing.B) {
b.StopTimer()
x := []byte("abcddddddeeeededd")
@ -597,6 +603,7 @@ func BenchmarkNotOnePassShortA(b *testing.B) {
re.Match(x)
}
}
func BenchmarkOnePassShortB(b *testing.B) {
b.StopTimer()
x := []byte("abcddddddeeeededd")
@ -606,6 +613,7 @@ func BenchmarkOnePassShortB(b *testing.B) {
re.Match(x)
}
}
func BenchmarkNotOnePassShortB(b *testing.B) {
b.StopTimer()
x := []byte("abcddddddeeeededd")
@ -615,6 +623,7 @@ func BenchmarkNotOnePassShortB(b *testing.B) {
re.Match(x)
}
}
func BenchmarkOnePassLongPrefix(b *testing.B) {
b.StopTimer()
x := []byte("abcdefghijklmnopqrstuvwxyz")
@ -624,6 +633,7 @@ func BenchmarkOnePassLongPrefix(b *testing.B) {
re.Match(x)
}
}
func BenchmarkOnePassLongNotPrefix(b *testing.B) {
b.StopTimer()
x := []byte("abcdefghijklmnopqrstuvwxyz")

View File

@ -600,6 +600,11 @@ func (p runeSlice) Sort() {
// onepass Prog, the Prog syntax.NotOnePass is returned. makeOnePass is recursive
// to the size of the Prog
func (p *Prog) makeOnePass() *Prog {
// If the machine is very long, it's not worth the time to check if we can use one pass.
if len(p.Inst) >= 1000 {
return NotOnePass
}
var (
instQueue = newQueue(len(p.Inst))
visitQueue = newQueue(len(p.Inst))