1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package regexp
6
7import (
8	"reflect"
9	"regexp/syntax"
10	"strings"
11	"testing"
12	"unicode/utf8"
13)
14
15var goodRe = []string{
16	``,
17	`.`,
18	`^.$`,
19	`a`,
20	`a*`,
21	`a+`,
22	`a?`,
23	`a|b`,
24	`a*|b*`,
25	`(a*|b)(c*|d)`,
26	`[a-z]`,
27	`[a-abc-c\-\]\[]`,
28	`[a-z]+`,
29	`[abc]`,
30	`[^1234]`,
31	`[^\n]`,
32	`\!\\`,
33}
34
35type stringError struct {
36	re  string
37	err string
38}
39
40var badRe = []stringError{
41	{`*`, "missing argument to repetition operator: `*`"},
42	{`+`, "missing argument to repetition operator: `+`"},
43	{`?`, "missing argument to repetition operator: `?`"},
44	{`(abc`, "missing closing ): `(abc`"},
45	{`abc)`, "unexpected ): `abc)`"},
46	{`x[a-z`, "missing closing ]: `[a-z`"},
47	{`[z-a]`, "invalid character class range: `z-a`"},
48	{`abc\`, "trailing backslash at end of expression"},
49	{`a**`, "invalid nested repetition operator: `**`"},
50	{`a*+`, "invalid nested repetition operator: `*+`"},
51	{`\x`, "invalid escape sequence: `\\x`"},
52}
53
54func compileTest(t *testing.T, expr string, error string) *Regexp {
55	re, err := Compile(expr)
56	if error == "" && err != nil {
57		t.Error("compiling `", expr, "`; unexpected error: ", err.Error())
58	}
59	if error != "" && err == nil {
60		t.Error("compiling `", expr, "`; missing error")
61	} else if error != "" && !strings.Contains(err.Error(), error) {
62		t.Error("compiling `", expr, "`; wrong error: ", err.Error(), "; want ", error)
63	}
64	return re
65}
66
67func TestGoodCompile(t *testing.T) {
68	for i := 0; i < len(goodRe); i++ {
69		compileTest(t, goodRe[i], "")
70	}
71}
72
73func TestBadCompile(t *testing.T) {
74	for i := 0; i < len(badRe); i++ {
75		compileTest(t, badRe[i].re, badRe[i].err)
76	}
77}
78
79func matchTest(t *testing.T, test *FindTest) {
80	re := compileTest(t, test.pat, "")
81	if re == nil {
82		return
83	}
84	m := re.MatchString(test.text)
85	if m != (len(test.matches) > 0) {
86		t.Errorf("MatchString failure on %s: %t should be %t", test, m, len(test.matches) > 0)
87	}
88	// now try bytes
89	m = re.Match([]byte(test.text))
90	if m != (len(test.matches) > 0) {
91		t.Errorf("Match failure on %s: %t should be %t", test, m, len(test.matches) > 0)
92	}
93}
94
95func TestMatch(t *testing.T) {
96	for _, test := range findTests {
97		matchTest(t, &test)
98	}
99}
100
101func matchFunctionTest(t *testing.T, test *FindTest) {
102	m, err := MatchString(test.pat, test.text)
103	if err == nil {
104		return
105	}
106	if m != (len(test.matches) > 0) {
107		t.Errorf("Match failure on %s: %t should be %t", test, m, len(test.matches) > 0)
108	}
109}
110
111func TestMatchFunction(t *testing.T) {
112	for _, test := range findTests {
113		matchFunctionTest(t, &test)
114	}
115}
116
117func copyMatchTest(t *testing.T, test *FindTest) {
118	re := compileTest(t, test.pat, "")
119	if re == nil {
120		return
121	}
122	m1 := re.MatchString(test.text)
123	m2 := re.Copy().MatchString(test.text)
124	if m1 != m2 {
125		t.Errorf("Copied Regexp match failure on %s: original gave %t; copy gave %t; should be %t",
126			test, m1, m2, len(test.matches) > 0)
127	}
128}
129
130func TestCopyMatch(t *testing.T) {
131	for _, test := range findTests {
132		copyMatchTest(t, &test)
133	}
134}
135
136type ReplaceTest struct {
137	pattern, replacement, input, output string
138}
139
140var replaceTests = []ReplaceTest{
141	// Test empty input and/or replacement, with pattern that matches the empty string.
142	{"", "", "", ""},
143	{"", "x", "", "x"},
144	{"", "", "abc", "abc"},
145	{"", "x", "abc", "xaxbxcx"},
146
147	// Test empty input and/or replacement, with pattern that does not match the empty string.
148	{"b", "", "", ""},
149	{"b", "x", "", ""},
150	{"b", "", "abc", "ac"},
151	{"b", "x", "abc", "axc"},
152	{"y", "", "", ""},
153	{"y", "x", "", ""},
154	{"y", "", "abc", "abc"},
155	{"y", "x", "abc", "abc"},
156
157	// Multibyte characters -- verify that we don't try to match in the middle
158	// of a character.
159	{"[a-c]*", "x", "\u65e5", "x\u65e5x"},
160	{"[^\u65e5]", "x", "abc\u65e5def", "xxx\u65e5xxx"},
161
162	// Start and end of a string.
163	{"^[a-c]*", "x", "abcdabc", "xdabc"},
164	{"[a-c]*$", "x", "abcdabc", "abcdx"},
165	{"^[a-c]*$", "x", "abcdabc", "abcdabc"},
166	{"^[a-c]*", "x", "abc", "x"},
167	{"[a-c]*$", "x", "abc", "x"},
168	{"^[a-c]*$", "x", "abc", "x"},
169	{"^[a-c]*", "x", "dabce", "xdabce"},
170	{"[a-c]*$", "x", "dabce", "dabcex"},
171	{"^[a-c]*$", "x", "dabce", "dabce"},
172	{"^[a-c]*", "x", "", "x"},
173	{"[a-c]*$", "x", "", "x"},
174	{"^[a-c]*$", "x", "", "x"},
175
176	{"^[a-c]+", "x", "abcdabc", "xdabc"},
177	{"[a-c]+$", "x", "abcdabc", "abcdx"},
178	{"^[a-c]+$", "x", "abcdabc", "abcdabc"},
179	{"^[a-c]+", "x", "abc", "x"},
180	{"[a-c]+$", "x", "abc", "x"},
181	{"^[a-c]+$", "x", "abc", "x"},
182	{"^[a-c]+", "x", "dabce", "dabce"},
183	{"[a-c]+$", "x", "dabce", "dabce"},
184	{"^[a-c]+$", "x", "dabce", "dabce"},
185	{"^[a-c]+", "x", "", ""},
186	{"[a-c]+$", "x", "", ""},
187	{"^[a-c]+$", "x", "", ""},
188
189	// Other cases.
190	{"abc", "def", "abcdefg", "defdefg"},
191	{"bc", "BC", "abcbcdcdedef", "aBCBCdcdedef"},
192	{"abc", "", "abcdabc", "d"},
193	{"x", "xXx", "xxxXxxx", "xXxxXxxXxXxXxxXxxXx"},
194	{"abc", "d", "", ""},
195	{"abc", "d", "abc", "d"},
196	{".+", "x", "abc", "x"},
197	{"[a-c]*", "x", "def", "xdxexfx"},
198	{"[a-c]+", "x", "abcbcdcdedef", "xdxdedef"},
199	{"[a-c]*", "x", "abcbcdcdedef", "xdxdxexdxexfx"},
200
201	// Substitutions
202	{"a+", "($0)", "banana", "b(a)n(a)n(a)"},
203	{"a+", "(${0})", "banana", "b(a)n(a)n(a)"},
204	{"a+", "(${0})$0", "banana", "b(a)an(a)an(a)a"},
205	{"a+", "(${0})$0", "banana", "b(a)an(a)an(a)a"},
206	{"hello, (.+)", "goodbye, ${1}", "hello, world", "goodbye, world"},
207	{"hello, (.+)", "goodbye, $1x", "hello, world", "goodbye, "},
208	{"hello, (.+)", "goodbye, ${1}x", "hello, world", "goodbye, worldx"},
209	{"hello, (.+)", "<$0><$1><$2><$3>", "hello, world", "<hello, world><world><><>"},
210	{"hello, (?P<noun>.+)", "goodbye, $noun!", "hello, world", "goodbye, world!"},
211	{"hello, (?P<noun>.+)", "goodbye, ${noun}", "hello, world", "goodbye, world"},
212	{"(?P<x>hi)|(?P<x>bye)", "$x$x$x", "hi", "hihihi"},
213	{"(?P<x>hi)|(?P<x>bye)", "$x$x$x", "bye", "byebyebye"},
214	{"(?P<x>hi)|(?P<x>bye)", "$xyz", "hi", ""},
215	{"(?P<x>hi)|(?P<x>bye)", "${x}yz", "hi", "hiyz"},
216	{"(?P<x>hi)|(?P<x>bye)", "hello $$x", "hi", "hello $x"},
217	{"a+", "${oops", "aaa", "${oops"},
218	{"a+", "$$", "aaa", "$"},
219	{"a+", "$", "aaa", "$"},
220
221	// Substitution when subexpression isn't found
222	{"(x)?", "$1", "123", "123"},
223	{"abc", "$1", "123", "123"},
224
225	// Substitutions involving a (x){0}
226	{"(a)(b){0}(c)", ".$1|$3.", "xacxacx", "x.a|c.x.a|c.x"},
227	{"(a)(((b))){0}c", ".$1.", "xacxacx", "x.a.x.a.x"},
228	{"((a(b){0}){3}){5}(h)", "y caramb$2", "say aaaaaaaaaaaaaaaah", "say ay caramba"},
229	{"((a(b){0}){3}){5}h", "y caramb$2", "say aaaaaaaaaaaaaaaah", "say ay caramba"},
230}
231
232var replaceLiteralTests = []ReplaceTest{
233	// Substitutions
234	{"a+", "($0)", "banana", "b($0)n($0)n($0)"},
235	{"a+", "(${0})", "banana", "b(${0})n(${0})n(${0})"},
236	{"a+", "(${0})$0", "banana", "b(${0})$0n(${0})$0n(${0})$0"},
237	{"a+", "(${0})$0", "banana", "b(${0})$0n(${0})$0n(${0})$0"},
238	{"hello, (.+)", "goodbye, ${1}", "hello, world", "goodbye, ${1}"},
239	{"hello, (?P<noun>.+)", "goodbye, $noun!", "hello, world", "goodbye, $noun!"},
240	{"hello, (?P<noun>.+)", "goodbye, ${noun}", "hello, world", "goodbye, ${noun}"},
241	{"(?P<x>hi)|(?P<x>bye)", "$x$x$x", "hi", "$x$x$x"},
242	{"(?P<x>hi)|(?P<x>bye)", "$x$x$x", "bye", "$x$x$x"},
243	{"(?P<x>hi)|(?P<x>bye)", "$xyz", "hi", "$xyz"},
244	{"(?P<x>hi)|(?P<x>bye)", "${x}yz", "hi", "${x}yz"},
245	{"(?P<x>hi)|(?P<x>bye)", "hello $$x", "hi", "hello $$x"},
246	{"a+", "${oops", "aaa", "${oops"},
247	{"a+", "$$", "aaa", "$$"},
248	{"a+", "$", "aaa", "$"},
249}
250
251type ReplaceFuncTest struct {
252	pattern       string
253	replacement   func(string) string
254	input, output string
255}
256
257var replaceFuncTests = []ReplaceFuncTest{
258	{"[a-c]", func(s string) string { return "x" + s + "y" }, "defabcdef", "defxayxbyxcydef"},
259	{"[a-c]+", func(s string) string { return "x" + s + "y" }, "defabcdef", "defxabcydef"},
260	{"[a-c]*", func(s string) string { return "x" + s + "y" }, "defabcdef", "xydxyexyfxabcydxyexyfxy"},
261}
262
263func TestReplaceAll(t *testing.T) {
264	for _, tc := range replaceTests {
265		re, err := Compile(tc.pattern)
266		if err != nil {
267			t.Errorf("Unexpected error compiling %q: %v", tc.pattern, err)
268			continue
269		}
270		actual := re.ReplaceAllString(tc.input, tc.replacement)
271		if actual != tc.output {
272			t.Errorf("%q.ReplaceAllString(%q,%q) = %q; want %q",
273				tc.pattern, tc.input, tc.replacement, actual, tc.output)
274		}
275		// now try bytes
276		actual = string(re.ReplaceAll([]byte(tc.input), []byte(tc.replacement)))
277		if actual != tc.output {
278			t.Errorf("%q.ReplaceAll(%q,%q) = %q; want %q",
279				tc.pattern, tc.input, tc.replacement, actual, tc.output)
280		}
281	}
282}
283
284func TestReplaceAllLiteral(t *testing.T) {
285	// Run ReplaceAll tests that do not have $ expansions.
286	for _, tc := range replaceTests {
287		if strings.Contains(tc.replacement, "$") {
288			continue
289		}
290		re, err := Compile(tc.pattern)
291		if err != nil {
292			t.Errorf("Unexpected error compiling %q: %v", tc.pattern, err)
293			continue
294		}
295		actual := re.ReplaceAllLiteralString(tc.input, tc.replacement)
296		if actual != tc.output {
297			t.Errorf("%q.ReplaceAllLiteralString(%q,%q) = %q; want %q",
298				tc.pattern, tc.input, tc.replacement, actual, tc.output)
299		}
300		// now try bytes
301		actual = string(re.ReplaceAllLiteral([]byte(tc.input), []byte(tc.replacement)))
302		if actual != tc.output {
303			t.Errorf("%q.ReplaceAllLiteral(%q,%q) = %q; want %q",
304				tc.pattern, tc.input, tc.replacement, actual, tc.output)
305		}
306	}
307
308	// Run literal-specific tests.
309	for _, tc := range replaceLiteralTests {
310		re, err := Compile(tc.pattern)
311		if err != nil {
312			t.Errorf("Unexpected error compiling %q: %v", tc.pattern, err)
313			continue
314		}
315		actual := re.ReplaceAllLiteralString(tc.input, tc.replacement)
316		if actual != tc.output {
317			t.Errorf("%q.ReplaceAllLiteralString(%q,%q) = %q; want %q",
318				tc.pattern, tc.input, tc.replacement, actual, tc.output)
319		}
320		// now try bytes
321		actual = string(re.ReplaceAllLiteral([]byte(tc.input), []byte(tc.replacement)))
322		if actual != tc.output {
323			t.Errorf("%q.ReplaceAllLiteral(%q,%q) = %q; want %q",
324				tc.pattern, tc.input, tc.replacement, actual, tc.output)
325		}
326	}
327}
328
329func TestReplaceAllFunc(t *testing.T) {
330	for _, tc := range replaceFuncTests {
331		re, err := Compile(tc.pattern)
332		if err != nil {
333			t.Errorf("Unexpected error compiling %q: %v", tc.pattern, err)
334			continue
335		}
336		actual := re.ReplaceAllStringFunc(tc.input, tc.replacement)
337		if actual != tc.output {
338			t.Errorf("%q.ReplaceFunc(%q,fn) = %q; want %q",
339				tc.pattern, tc.input, actual, tc.output)
340		}
341		// now try bytes
342		actual = string(re.ReplaceAllFunc([]byte(tc.input), func(s []byte) []byte { return []byte(tc.replacement(string(s))) }))
343		if actual != tc.output {
344			t.Errorf("%q.ReplaceFunc(%q,fn) = %q; want %q",
345				tc.pattern, tc.input, actual, tc.output)
346		}
347	}
348}
349
350type MetaTest struct {
351	pattern, output, literal string
352	isLiteral                bool
353}
354
355var metaTests = []MetaTest{
356	{``, ``, ``, true},
357	{`foo`, `foo`, `foo`, true},
358	{`日本語+`, `日本語\+`, `日本語`, false},
359	{`foo\.\$`, `foo\\\.\\\$`, `foo.$`, true}, // has meta but no operator
360	{`foo.\$`, `foo\.\\\$`, `foo`, false},     // has escaped operators and real operators
361	{`!@#$%^&*()_+-=[{]}\|,<.>/?~`, `!@#\$%\^&\*\(\)_\+-=\[\{\]\}\\\|,<\.>/\?~`, `!@#`, false},
362}
363
364var literalPrefixTests = []MetaTest{
365	// See golang.org/issue/11175.
366	// output is unused.
367	{`^0^0$`, ``, `0`, false},
368	{`^0^`, ``, ``, false},
369	{`^0$`, ``, `0`, true},
370	{`$0^`, ``, ``, false},
371	{`$0$`, ``, ``, false},
372	{`^^0$$`, ``, ``, false},
373	{`^$^$`, ``, ``, false},
374	{`$$0^^`, ``, ``, false},
375}
376
377func TestQuoteMeta(t *testing.T) {
378	for _, tc := range metaTests {
379		// Verify that QuoteMeta returns the expected string.
380		quoted := QuoteMeta(tc.pattern)
381		if quoted != tc.output {
382			t.Errorf("QuoteMeta(`%s`) = `%s`; want `%s`",
383				tc.pattern, quoted, tc.output)
384			continue
385		}
386
387		// Verify that the quoted string is in fact treated as expected
388		// by Compile -- i.e. that it matches the original, unquoted string.
389		if tc.pattern != "" {
390			re, err := Compile(quoted)
391			if err != nil {
392				t.Errorf("Unexpected error compiling QuoteMeta(`%s`): %v", tc.pattern, err)
393				continue
394			}
395			src := "abc" + tc.pattern + "def"
396			repl := "xyz"
397			replaced := re.ReplaceAllString(src, repl)
398			expected := "abcxyzdef"
399			if replaced != expected {
400				t.Errorf("QuoteMeta(`%s`).Replace(`%s`,`%s`) = `%s`; want `%s`",
401					tc.pattern, src, repl, replaced, expected)
402			}
403		}
404	}
405}
406
407func TestLiteralPrefix(t *testing.T) {
408	for _, tc := range append(metaTests, literalPrefixTests...) {
409		// Literal method needs to scan the pattern.
410		re := MustCompile(tc.pattern)
411		str, complete := re.LiteralPrefix()
412		if complete != tc.isLiteral {
413			t.Errorf("LiteralPrefix(`%s`) = %t; want %t", tc.pattern, complete, tc.isLiteral)
414		}
415		if str != tc.literal {
416			t.Errorf("LiteralPrefix(`%s`) = `%s`; want `%s`", tc.pattern, str, tc.literal)
417		}
418	}
419}
420
421type subexpCase struct {
422	input string
423	num   int
424	names []string
425}
426
427var subexpCases = []subexpCase{
428	{``, 0, nil},
429	{`.*`, 0, nil},
430	{`abba`, 0, nil},
431	{`ab(b)a`, 1, []string{"", ""}},
432	{`ab(.*)a`, 1, []string{"", ""}},
433	{`(.*)ab(.*)a`, 2, []string{"", "", ""}},
434	{`(.*)(ab)(.*)a`, 3, []string{"", "", "", ""}},
435	{`(.*)((a)b)(.*)a`, 4, []string{"", "", "", "", ""}},
436	{`(.*)(\(ab)(.*)a`, 3, []string{"", "", "", ""}},
437	{`(.*)(\(a\)b)(.*)a`, 3, []string{"", "", "", ""}},
438	{`(?P<foo>.*)(?P<bar>(a)b)(?P<foo>.*)a`, 4, []string{"", "foo", "bar", "", "foo"}},
439}
440
441func TestSubexp(t *testing.T) {
442	for _, c := range subexpCases {
443		re := MustCompile(c.input)
444		n := re.NumSubexp()
445		if n != c.num {
446			t.Errorf("%q: NumSubexp = %d, want %d", c.input, n, c.num)
447			continue
448		}
449		names := re.SubexpNames()
450		if len(names) != 1+n {
451			t.Errorf("%q: len(SubexpNames) = %d, want %d", c.input, len(names), n)
452			continue
453		}
454		if c.names != nil {
455			for i := 0; i < 1+n; i++ {
456				if names[i] != c.names[i] {
457					t.Errorf("%q: SubexpNames[%d] = %q, want %q", c.input, i, names[i], c.names[i])
458				}
459			}
460		}
461	}
462}
463
464var splitTests = []struct {
465	s   string
466	r   string
467	n   int
468	out []string
469}{
470	{"foo:and:bar", ":", -1, []string{"foo", "and", "bar"}},
471	{"foo:and:bar", ":", 1, []string{"foo:and:bar"}},
472	{"foo:and:bar", ":", 2, []string{"foo", "and:bar"}},
473	{"foo:and:bar", "foo", -1, []string{"", ":and:bar"}},
474	{"foo:and:bar", "bar", -1, []string{"foo:and:", ""}},
475	{"foo:and:bar", "baz", -1, []string{"foo:and:bar"}},
476	{"baabaab", "a", -1, []string{"b", "", "b", "", "b"}},
477	{"baabaab", "a*", -1, []string{"b", "b", "b"}},
478	{"baabaab", "ba*", -1, []string{"", "", "", ""}},
479	{"foobar", "f*b*", -1, []string{"", "o", "o", "a", "r"}},
480	{"foobar", "f+.*b+", -1, []string{"", "ar"}},
481	{"foobooboar", "o{2}", -1, []string{"f", "b", "boar"}},
482	{"a,b,c,d,e,f", ",", 3, []string{"a", "b", "c,d,e,f"}},
483	{"a,b,c,d,e,f", ",", 0, nil},
484	{",", ",", -1, []string{"", ""}},
485	{",,,", ",", -1, []string{"", "", "", ""}},
486	{"", ",", -1, []string{""}},
487	{"", ".*", -1, []string{""}},
488	{"", ".+", -1, []string{""}},
489	{"", "", -1, []string{}},
490	{"foobar", "", -1, []string{"f", "o", "o", "b", "a", "r"}},
491	{"abaabaccadaaae", "a*", 5, []string{"", "b", "b", "c", "cadaaae"}},
492	{":x:y:z:", ":", -1, []string{"", "x", "y", "z", ""}},
493}
494
495func TestSplit(t *testing.T) {
496	for i, test := range splitTests {
497		re, err := Compile(test.r)
498		if err != nil {
499			t.Errorf("#%d: %q: compile error: %s", i, test.r, err.Error())
500			continue
501		}
502
503		split := re.Split(test.s, test.n)
504		if !reflect.DeepEqual(split, test.out) {
505			t.Errorf("#%d: %q: got %q; want %q", i, test.r, split, test.out)
506		}
507
508		if QuoteMeta(test.r) == test.r {
509			strsplit := strings.SplitN(test.s, test.r, test.n)
510			if !reflect.DeepEqual(split, strsplit) {
511				t.Errorf("#%d: Split(%q, %q, %d): regexp vs strings mismatch\nregexp=%q\nstrings=%q", i, test.s, test.r, test.n, split, strsplit)
512			}
513		}
514	}
515}
516
517// The following sequence of Match calls used to panic. See issue #12980.
518func TestParseAndCompile(t *testing.T) {
519	expr := "a$"
520	s := "a\nb"
521
522	for i, tc := range []struct {
523		reFlags  syntax.Flags
524		expMatch bool
525	}{
526		{syntax.Perl | syntax.OneLine, false},
527		{syntax.Perl &^ syntax.OneLine, true},
528	} {
529		parsed, err := syntax.Parse(expr, tc.reFlags)
530		if err != nil {
531			t.Fatalf("%d: parse: %v", i, err)
532		}
533		re, err := Compile(parsed.String())
534		if err != nil {
535			t.Fatalf("%d: compile: %v", i, err)
536		}
537		if match := re.MatchString(s); match != tc.expMatch {
538			t.Errorf("%d: %q.MatchString(%q)=%t; expected=%t", i, re, s, match, tc.expMatch)
539		}
540	}
541}
542
543// Check that one-pass cutoff does trigger.
544func TestOnePassCutoff(t *testing.T) {
545	re, err := syntax.Parse(`^x{1,1000}y{1,1000}$`, syntax.Perl)
546	if err != nil {
547		t.Fatalf("parse: %v", err)
548	}
549	p, err := syntax.Compile(re.Simplify())
550	if err != nil {
551		t.Fatalf("compile: %v", err)
552	}
553	if compileOnePass(p) != notOnePass {
554		t.Fatalf("makeOnePass succeeded; wanted notOnePass")
555	}
556}
557
558// Check that the same machine can be used with the standard matcher
559// and then the backtracker when there are no captures.
560func TestSwitchBacktrack(t *testing.T) {
561	re := MustCompile(`a|b`)
562	long := make([]byte, maxBacktrackVector+1)
563
564	// The following sequence of Match calls used to panic. See issue #10319.
565	re.Match(long)     // triggers standard matcher
566	re.Match(long[:1]) // triggers backtracker
567}
568
569func BenchmarkFind(b *testing.B) {
570	b.StopTimer()
571	re := MustCompile("a+b+")
572	wantSubs := "aaabb"
573	s := []byte("acbb" + wantSubs + "dd")
574	b.StartTimer()
575	b.ReportAllocs()
576	for i := 0; i < b.N; i++ {
577		subs := re.Find(s)
578		if string(subs) != wantSubs {
579			b.Fatalf("Find(%q) = %q; want %q", s, subs, wantSubs)
580		}
581	}
582}
583
584func BenchmarkFindString(b *testing.B) {
585	b.StopTimer()
586	re := MustCompile("a+b+")
587	wantSubs := "aaabb"
588	s := "acbb" + wantSubs + "dd"
589	b.StartTimer()
590	b.ReportAllocs()
591	for i := 0; i < b.N; i++ {
592		subs := re.FindString(s)
593		if subs != wantSubs {
594			b.Fatalf("FindString(%q) = %q; want %q", s, subs, wantSubs)
595		}
596	}
597}
598
599func BenchmarkFindSubmatch(b *testing.B) {
600	b.StopTimer()
601	re := MustCompile("a(a+b+)b")
602	wantSubs := "aaabb"
603	s := []byte("acbb" + wantSubs + "dd")
604	b.StartTimer()
605	b.ReportAllocs()
606	for i := 0; i < b.N; i++ {
607		subs := re.FindSubmatch(s)
608		if string(subs[0]) != wantSubs {
609			b.Fatalf("FindSubmatch(%q)[0] = %q; want %q", s, subs[0], wantSubs)
610		}
611		if string(subs[1]) != "aab" {
612			b.Fatalf("FindSubmatch(%q)[1] = %q; want %q", s, subs[1], "aab")
613		}
614	}
615}
616
617func BenchmarkFindStringSubmatch(b *testing.B) {
618	b.StopTimer()
619	re := MustCompile("a(a+b+)b")
620	wantSubs := "aaabb"
621	s := "acbb" + wantSubs + "dd"
622	b.StartTimer()
623	b.ReportAllocs()
624	for i := 0; i < b.N; i++ {
625		subs := re.FindStringSubmatch(s)
626		if subs[0] != wantSubs {
627			b.Fatalf("FindStringSubmatch(%q)[0] = %q; want %q", s, subs[0], wantSubs)
628		}
629		if subs[1] != "aab" {
630			b.Fatalf("FindStringSubmatch(%q)[1] = %q; want %q", s, subs[1], "aab")
631		}
632	}
633}
634
635func BenchmarkLiteral(b *testing.B) {
636	x := strings.Repeat("x", 50) + "y"
637	b.StopTimer()
638	re := MustCompile("y")
639	b.StartTimer()
640	for i := 0; i < b.N; i++ {
641		if !re.MatchString(x) {
642			b.Fatalf("no match!")
643		}
644	}
645}
646
647func BenchmarkNotLiteral(b *testing.B) {
648	x := strings.Repeat("x", 50) + "y"
649	b.StopTimer()
650	re := MustCompile(".y")
651	b.StartTimer()
652	for i := 0; i < b.N; i++ {
653		if !re.MatchString(x) {
654			b.Fatalf("no match!")
655		}
656	}
657}
658
659func BenchmarkMatchClass(b *testing.B) {
660	b.StopTimer()
661	x := strings.Repeat("xxxx", 20) + "w"
662	re := MustCompile("[abcdw]")
663	b.StartTimer()
664	for i := 0; i < b.N; i++ {
665		if !re.MatchString(x) {
666			b.Fatalf("no match!")
667		}
668	}
669}
670
671func BenchmarkMatchClass_InRange(b *testing.B) {
672	b.StopTimer()
673	// 'b' is between 'a' and 'c', so the charclass
674	// range checking is no help here.
675	x := strings.Repeat("bbbb", 20) + "c"
676	re := MustCompile("[ac]")
677	b.StartTimer()
678	for i := 0; i < b.N; i++ {
679		if !re.MatchString(x) {
680			b.Fatalf("no match!")
681		}
682	}
683}
684
685func BenchmarkReplaceAll(b *testing.B) {
686	x := "abcdefghijklmnopqrstuvwxyz"
687	b.StopTimer()
688	re := MustCompile("[cjrw]")
689	b.StartTimer()
690	for i := 0; i < b.N; i++ {
691		re.ReplaceAllString(x, "")
692	}
693}
694
695func BenchmarkAnchoredLiteralShortNonMatch(b *testing.B) {
696	b.StopTimer()
697	x := []byte("abcdefghijklmnopqrstuvwxyz")
698	re := MustCompile("^zbc(d|e)")
699	b.StartTimer()
700	for i := 0; i < b.N; i++ {
701		re.Match(x)
702	}
703}
704
705func BenchmarkAnchoredLiteralLongNonMatch(b *testing.B) {
706	b.StopTimer()
707	x := []byte("abcdefghijklmnopqrstuvwxyz")
708	for i := 0; i < 15; i++ {
709		x = append(x, x...)
710	}
711	re := MustCompile("^zbc(d|e)")
712	b.StartTimer()
713	for i := 0; i < b.N; i++ {
714		re.Match(x)
715	}
716}
717
718func BenchmarkAnchoredShortMatch(b *testing.B) {
719	b.StopTimer()
720	x := []byte("abcdefghijklmnopqrstuvwxyz")
721	re := MustCompile("^.bc(d|e)")
722	b.StartTimer()
723	for i := 0; i < b.N; i++ {
724		re.Match(x)
725	}
726}
727
728func BenchmarkAnchoredLongMatch(b *testing.B) {
729	b.StopTimer()
730	x := []byte("abcdefghijklmnopqrstuvwxyz")
731	for i := 0; i < 15; i++ {
732		x = append(x, x...)
733	}
734	re := MustCompile("^.bc(d|e)")
735	b.StartTimer()
736	for i := 0; i < b.N; i++ {
737		re.Match(x)
738	}
739}
740
741func BenchmarkOnePassShortA(b *testing.B) {
742	b.StopTimer()
743	x := []byte("abcddddddeeeededd")
744	re := MustCompile("^.bc(d|e)*$")
745	b.StartTimer()
746	for i := 0; i < b.N; i++ {
747		re.Match(x)
748	}
749}
750
751func BenchmarkNotOnePassShortA(b *testing.B) {
752	b.StopTimer()
753	x := []byte("abcddddddeeeededd")
754	re := MustCompile(".bc(d|e)*$")
755	b.StartTimer()
756	for i := 0; i < b.N; i++ {
757		re.Match(x)
758	}
759}
760
761func BenchmarkOnePassShortB(b *testing.B) {
762	b.StopTimer()
763	x := []byte("abcddddddeeeededd")
764	re := MustCompile("^.bc(?:d|e)*$")
765	b.StartTimer()
766	for i := 0; i < b.N; i++ {
767		re.Match(x)
768	}
769}
770
771func BenchmarkNotOnePassShortB(b *testing.B) {
772	b.StopTimer()
773	x := []byte("abcddddddeeeededd")
774	re := MustCompile(".bc(?:d|e)*$")
775	b.StartTimer()
776	for i := 0; i < b.N; i++ {
777		re.Match(x)
778	}
779}
780
781func BenchmarkOnePassLongPrefix(b *testing.B) {
782	b.StopTimer()
783	x := []byte("abcdefghijklmnopqrstuvwxyz")
784	re := MustCompile("^abcdefghijklmnopqrstuvwxyz.*$")
785	b.StartTimer()
786	for i := 0; i < b.N; i++ {
787		re.Match(x)
788	}
789}
790
791func BenchmarkOnePassLongNotPrefix(b *testing.B) {
792	b.StopTimer()
793	x := []byte("abcdefghijklmnopqrstuvwxyz")
794	re := MustCompile("^.bcdefghijklmnopqrstuvwxyz.*$")
795	b.StartTimer()
796	for i := 0; i < b.N; i++ {
797		re.Match(x)
798	}
799}
800
801func BenchmarkMatchParallelShared(b *testing.B) {
802	x := []byte("this is a long line that contains foo bar baz")
803	re := MustCompile("foo (ba+r)? baz")
804	b.ResetTimer()
805	b.RunParallel(func(pb *testing.PB) {
806		for pb.Next() {
807			re.Match(x)
808		}
809	})
810}
811
812func BenchmarkMatchParallelCopied(b *testing.B) {
813	x := []byte("this is a long line that contains foo bar baz")
814	re := MustCompile("foo (ba+r)? baz")
815	b.ResetTimer()
816	b.RunParallel(func(pb *testing.PB) {
817		re := re.Copy()
818		for pb.Next() {
819			re.Match(x)
820		}
821	})
822}
823
824var sink string
825
826func BenchmarkQuoteMetaAll(b *testing.B) {
827	specials := make([]byte, 0)
828	for i := byte(0); i < utf8.RuneSelf; i++ {
829		if special(i) {
830			specials = append(specials, i)
831		}
832	}
833	s := string(specials)
834	b.SetBytes(int64(len(s)))
835	b.ResetTimer()
836	for i := 0; i < b.N; i++ {
837		sink = QuoteMeta(s)
838	}
839}
840
841func BenchmarkQuoteMetaNone(b *testing.B) {
842	s := "abcdefghijklmnopqrstuvwxyz"
843	b.SetBytes(int64(len(s)))
844	b.ResetTimer()
845	for i := 0; i < b.N; i++ {
846		sink = QuoteMeta(s)
847	}
848}
849