]> Cypherpunks repositories - gostls13.git/commitdiff
regexp: avoid alloc in QuoteMeta when not quoting
authorIngo Oeser <nightlyone@googlemail.com>
Tue, 18 Oct 2016 16:05:43 +0000 (18:05 +0200)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 19 Oct 2016 07:09:08 +0000 (07:09 +0000)
Many users quote literals in regular expressions just in case.
No need to allocate then.

Note: Also added benchmarks for quoting and not quoting.

name             old time/op    new time/op     delta
QuoteMetaAll-4      629ns ± 6%      654ns ± 5%    +4.01%        (p=0.001 n=20+19)
QuoteMetaNone-4    1.02µs ± 6%     0.20µs ± 0%   -80.73%        (p=0.000 n=18+20)

name             old speed      new speed       delta
QuoteMetaAll-4   22.3MB/s ± 6%   21.4MB/s ± 5%    -3.94%        (p=0.001 n=20+19)
QuoteMetaNone-4  25.3MB/s ± 3%  131.5MB/s ± 0%  +419.28%        (p=0.000 n=17+19)

name             old alloc/op   new alloc/op    delta
QuoteMetaAll-4      64.0B ± 0%      64.0B ± 0%      ~     (all samples are equal)
QuoteMetaNone-4     96.0B ± 0%      0.0B ±NaN%  -100.00%        (p=0.000 n=20+20)

name             old allocs/op  new allocs/op   delta
QuoteMetaAll-4       2.00 ± 0%       2.00 ± 0%      ~     (all samples are equal)
QuoteMetaNone-4      2.00 ± 0%      0.00 ±NaN%  -100.00%        (p=0.000 n=20+20)

Change-Id: I38d50f463cde463115d22534f8eb849e54d899af
Reviewed-on: https://go-review.googlesource.com/31395
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/regexp/all_test.go
src/regexp/regexp.go

index 31843d41f1fd96a6dcb2d54993f0868ed9a0f725..beb46e70995140c8e0a0a5b84064ca0f25010602 100644 (file)
@@ -818,3 +818,23 @@ func BenchmarkMatchParallelCopied(b *testing.B) {
                }
        })
 }
+
+var sink string
+
+func BenchmarkQuoteMetaAll(b *testing.B) {
+       s := string(specialBytes)
+       b.SetBytes(int64(len(s)))
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               sink = QuoteMeta(s)
+       }
+}
+
+func BenchmarkQuoteMetaNone(b *testing.B) {
+       s := "abcdefghijklmnopqrstuvwxyz"
+       b.SetBytes(int64(len(s)))
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               sink = QuoteMeta(s)
+       }
+}
index 87a3e88d9a7deee955b765fb23417e0907c82155..01093d4bd0d73d76d1cb47116defc5b3e1dbcc5c 100644 (file)
@@ -600,11 +600,22 @@ func special(b byte) bool {
 // inside the argument text; the returned string is a regular expression matching
 // the literal text. For example, QuoteMeta(`[foo]`) returns `\[foo\]`.
 func QuoteMeta(s string) string {
-       b := make([]byte, 2*len(s))
-
        // A byte loop is correct because all metacharacters are ASCII.
-       j := 0
-       for i := 0; i < len(s); i++ {
+       var i int
+       for i = 0; i < len(s); i++ {
+               if special(s[i]) {
+                       break
+               }
+       }
+       // No meta characters found, so return original string.
+       if i >= len(s) {
+               return s
+       }
+
+       b := make([]byte, 2*len(s)-i)
+       copy(b, s[:i])
+       j := i
+       for ; i < len(s); i++ {
                if special(s[i]) {
                        b[j] = '\\'
                        j++
@@ -612,7 +623,7 @@ func QuoteMeta(s string) string {
                b[j] = s[i]
                j++
        }
-       return string(b[0:j])
+       return string(b[:j])
 }
 
 // The number of capture values in the program may correspond