chroma-markdown/vendor/github.com/dlclark/regexp2/regexp_performance_test.go

308 lines
9.1 KiB
Go

package regexp2
import (
"strings"
"testing"
)
func BenchmarkLiteral(b *testing.B) {
x := strings.Repeat("x", 50) + "y"
b.StopTimer()
re := MustCompile("y", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkNotLiteral(b *testing.B) {
x := strings.Repeat("x", 50) + "y"
b.StopTimer()
re := MustCompile(".y", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkMatchClass(b *testing.B) {
b.StopTimer()
x := strings.Repeat("xxxx", 20) + "w"
re := MustCompile("[abcdw]", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkMatchClass_InRange(b *testing.B) {
b.StopTimer()
// 'b' is between 'a' and 'c', so the charclass
// range checking is no help here.
x := strings.Repeat("bbbb", 20) + "c"
re := MustCompile("[ac]", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
/*
func BenchmarkReplaceAll(b *testing.B) {
x := "abcdefghijklmnopqrstuvwxyz"
b.StopTimer()
re := MustCompile("[cjrw]", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
re.ReplaceAllString(x, "")
}
}
*/
func BenchmarkAnchoredLiteralShortNonMatch(b *testing.B) {
b.StopTimer()
x := "abcdefghijklmnopqrstuvwxyz"
re := MustCompile("^zbc(d|e)", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); m || err != nil {
b.Fatalf("unexpected match or error! %v", err)
}
}
}
func BenchmarkAnchoredLiteralLongNonMatch(b *testing.B) {
b.StopTimer()
data := "abcdefghijklmnopqrstuvwxyz"
x := make([]rune, 32768*len(data))
for i := 0; i < 32768; /*(2^15)*/ i++ {
for j := 0; j < len(data); j++ {
x[i*len(data)+j] = rune(data[j])
}
}
re := MustCompile("^zbc(d|e)", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchRunes(x); m || err != nil {
b.Fatalf("unexpected match or error! %v", err)
}
}
}
func BenchmarkAnchoredShortMatch(b *testing.B) {
b.StopTimer()
x := "abcdefghijklmnopqrstuvwxyz"
re := MustCompile("^.bc(d|e)", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkAnchoredLongMatch(b *testing.B) {
b.StopTimer()
data := "abcdefghijklmnopqrstuvwxyz"
x := make([]rune, 32768*len(data))
for i := 0; i < 32768; /*(2^15)*/ i++ {
for j := 0; j < len(data); j++ {
x[i*len(data)+j] = rune(data[j])
}
}
re := MustCompile("^.bc(d|e)", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchRunes(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkOnePassShortA(b *testing.B) {
b.StopTimer()
x := "abcddddddeeeededd"
re := MustCompile("^.bc(d|e)*$", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkNotOnePassShortA(b *testing.B) {
b.StopTimer()
x := "abcddddddeeeededd"
re := MustCompile(".bc(d|e)*$", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkOnePassShortB(b *testing.B) {
b.StopTimer()
x := "abcddddddeeeededd"
re := MustCompile("^.bc(?:d|e)*$", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkNotOnePassShortB(b *testing.B) {
b.StopTimer()
x := "abcddddddeeeededd"
re := MustCompile(".bc(?:d|e)*$", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkOnePassLongPrefix(b *testing.B) {
b.StopTimer()
x := "abcdefghijklmnopqrstuvwxyz"
re := MustCompile("^abcdefghijklmnopqrstuvwxyz.*$", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
func BenchmarkOnePassLongNotPrefix(b *testing.B) {
b.StopTimer()
x := "abcdefghijklmnopqrstuvwxyz"
re := MustCompile("^.bcdefghijklmnopqrstuvwxyz.*$", 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := re.MatchString(x); !m || err != nil {
b.Fatalf("no match or error! %v", err)
}
}
}
var text []rune
func makeText(n int) []rune {
if len(text) >= n {
return text[:n]
}
text = make([]rune, n)
x := ^uint32(0)
for i := range text {
x += x
x ^= 1
if int32(x) < 0 {
x ^= 0x88888eef
}
if x%31 == 0 {
text[i] = '\n'
} else {
text[i] = rune(x%(0x7E+1-0x20) + 0x20)
}
}
return text
}
func benchmark(b *testing.B, re string, n int) {
r := MustCompile(re, 0)
t := makeText(n)
b.ResetTimer()
b.SetBytes(int64(n))
for i := 0; i < b.N; i++ {
if m, err := r.MatchRunes(t); m {
b.Fatal("match!")
} else if err != nil {
b.Fatalf("Err %v", err)
}
}
}
const (
easy0 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
easy1 = "A[AB]B[BC]C[CD]D[DE]E[EF]F[FG]G[GH]H[HI]I[IJ]J$"
medium = "[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
hard = "[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
hard1 = "ABCD|CDEF|EFGH|GHIJ|IJKL|KLMN|MNOP|OPQR|QRST|STUV|UVWX|WXYZ"
parens = "([ -~])*(A)(B)(C)(D)(E)(F)(G)(H)(I)(J)(K)(L)(M)" +
"(N)(O)(P)(Q)(R)(S)(T)(U)(V)(W)(X)(Y)(Z)$"
)
func BenchmarkMatchEasy0_32(b *testing.B) { benchmark(b, easy0, 32<<0) }
func BenchmarkMatchEasy0_1K(b *testing.B) { benchmark(b, easy0, 1<<10) }
func BenchmarkMatchEasy0_32K(b *testing.B) { benchmark(b, easy0, 32<<10) }
func BenchmarkMatchEasy0_1M(b *testing.B) { benchmark(b, easy0, 1<<20) }
func BenchmarkMatchEasy0_32M(b *testing.B) { benchmark(b, easy0, 32<<20) }
func BenchmarkMatchEasy1_32(b *testing.B) { benchmark(b, easy1, 32<<0) }
func BenchmarkMatchEasy1_1K(b *testing.B) { benchmark(b, easy1, 1<<10) }
func BenchmarkMatchEasy1_32K(b *testing.B) { benchmark(b, easy1, 32<<10) }
func BenchmarkMatchEasy1_1M(b *testing.B) { benchmark(b, easy1, 1<<20) }
func BenchmarkMatchEasy1_32M(b *testing.B) { benchmark(b, easy1, 32<<20) }
func BenchmarkMatchMedium_32(b *testing.B) { benchmark(b, medium, 32<<0) }
func BenchmarkMatchMedium_1K(b *testing.B) { benchmark(b, medium, 1<<10) }
func BenchmarkMatchMedium_32K(b *testing.B) { benchmark(b, medium, 32<<10) }
func BenchmarkMatchMedium_1M(b *testing.B) { benchmark(b, medium, 1<<20) }
func BenchmarkMatchMedium_32M(b *testing.B) { benchmark(b, medium, 32<<20) }
func BenchmarkMatchHard_32(b *testing.B) { benchmark(b, hard, 32<<0) }
func BenchmarkMatchHard_1K(b *testing.B) { benchmark(b, hard, 1<<10) }
func BenchmarkMatchHard_32K(b *testing.B) { benchmark(b, hard, 32<<10) }
func BenchmarkMatchHard_1M(b *testing.B) { benchmark(b, hard, 1<<20) }
func BenchmarkMatchHard_32M(b *testing.B) { benchmark(b, hard, 32<<20) }
func BenchmarkMatchHard1_32(b *testing.B) { benchmark(b, hard1, 32<<0) }
func BenchmarkMatchHard1_1K(b *testing.B) { benchmark(b, hard1, 1<<10) }
func BenchmarkMatchHard1_32K(b *testing.B) { benchmark(b, hard1, 32<<10) }
func BenchmarkMatchHard1_1M(b *testing.B) { benchmark(b, hard1, 1<<20) }
func BenchmarkMatchHard1_32M(b *testing.B) { benchmark(b, hard1, 32<<20) }
// TestProgramTooLongForBacktrack tests that a regex which is too long
// for the backtracker still executes properly.
func TestProgramTooLongForBacktrack(t *testing.T) {
longRegex := MustCompile(`(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|twentyone|twentytwo|twentythree|twentyfour|twentyfive|twentysix|twentyseven|twentyeight|twentynine|thirty|thirtyone|thirtytwo|thirtythree|thirtyfour|thirtyfive|thirtysix|thirtyseven|thirtyeight|thirtynine|forty|fortyone|fortytwo|fortythree|fortyfour|fortyfive|fortysix|fortyseven|fortyeight|fortynine|fifty|fiftyone|fiftytwo|fiftythree|fiftyfour|fiftyfive|fiftysix|fiftyseven|fiftyeight|fiftynine|sixty|sixtyone|sixtytwo|sixtythree|sixtyfour|sixtyfive|sixtysix|sixtyseven|sixtyeight|sixtynine|seventy|seventyone|seventytwo|seventythree|seventyfour|seventyfive|seventysix|seventyseven|seventyeight|seventynine|eighty|eightyone|eightytwo|eightythree|eightyfour|eightyfive|eightysix|eightyseven|eightyeight|eightynine|ninety|ninetyone|ninetytwo|ninetythree|ninetyfour|ninetyfive|ninetysix|ninetyseven|ninetyeight|ninetynine|onehundred)`, 0)
if m, err := longRegex.MatchString("two"); !m {
t.Errorf("longRegex.MatchString(\"two\") was false, want true")
} else if err != nil {
t.Errorf("Error: %v", err)
}
if m, err := longRegex.MatchString("xxx"); m {
t.Errorf("longRegex.MatchString(\"xxx\") was true, want false")
} else if err != nil {
t.Errorf("Error: %v", err)
}
}
func BenchmarkLeading(b *testing.B) {
b.StopTimer()
r := MustCompile("[a-q][^u-z]{13}x", 0)
inp := makeText(1000000)
b.StartTimer()
for i := 0; i < b.N; i++ {
if m, err := r.MatchRunes(inp); !m {
b.Errorf("Expected match")
} else if err != nil {
b.Errorf("Error: %v", err)
}
}
}