Commit f54f790a authored by Rob Pike's avatar Rob Pike

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
parent 5bc1cef8
......@@ -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")
......
......@@ -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))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment