]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Add a common regexp cache
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 27 Jul 2023 17:20:48 +0000 (19:20 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 27 Jul 2023 17:23:12 +0000 (19:23 +0200)
```
BenchmarkGetOrCompileRegexp-10     73959368         13.71 ns/op        0 B/op        0 allocs/op
BenchmarkCompileRegexp-10           3143529        380.1 ns/op      872 B/op       10 allocs/op
```

common/hstrings/strings.go
common/hstrings/strings_test.go
tpl/strings/regexp.go

index 6c0f820feac881d88aae2be311e61b8ee3fe1edc..2fd791f43ba9aa1f782acf735bd0687a36ca745b 100644 (file)
@@ -15,7 +15,9 @@ package hstrings
 
 import (
        "fmt"
+       "regexp"
        "strings"
+       "sync"
 
        "github.com/gohugoio/hugo/compare"
 )
@@ -55,3 +57,45 @@ func EqualAny(a string, b ...string) bool {
        }
        return false
 }
+
+// regexpCache represents a cache of regexp objects protected by a mutex.
+type regexpCache struct {
+       mu sync.RWMutex
+       re map[string]*regexp.Regexp
+}
+
+func (rc *regexpCache) getOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) {
+       var ok bool
+
+       if re, ok = rc.get(pattern); !ok {
+               re, err = regexp.Compile(pattern)
+               if err != nil {
+                       return nil, err
+               }
+               rc.set(pattern, re)
+       }
+
+       return re, nil
+}
+
+func (rc *regexpCache) get(key string) (re *regexp.Regexp, ok bool) {
+       rc.mu.RLock()
+       re, ok = rc.re[key]
+       rc.mu.RUnlock()
+       return
+}
+
+func (rc *regexpCache) set(key string, re *regexp.Regexp) {
+       rc.mu.Lock()
+       rc.re[key] = re
+       rc.mu.Unlock()
+}
+
+var reCache = regexpCache{re: make(map[string]*regexp.Regexp)}
+
+// GetOrCompileRegexp retrieves a regexp object from the cache based upon the pattern.
+// If the pattern is not found in the cache, the pattern is compiled and added to
+// the cache.
+func GetOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) {
+       return reCache.getOrCompileRegexp(pattern)
+}
index dc2eae6f2bf7cebd26e6b48383c6bb005a15cef5..85068bdf97964da8911b595e5a7db6a33d1107c3 100644 (file)
@@ -14,6 +14,7 @@
 package hstrings
 
 import (
+       "regexp"
        "testing"
 
        qt "github.com/frankban/quicktest"
@@ -34,3 +35,24 @@ func TestStringEqualFold(t *testing.T) {
        c.Assert(StringEqualFold(s1).Eq("b"), qt.Equals, false)
 
 }
+
+func TestGetOrCompileRegexp(t *testing.T) {
+       c := qt.New(t)
+
+       re, err := GetOrCompileRegexp(`\d+`)
+       c.Assert(err, qt.IsNil)
+       c.Assert(re.MatchString("123"), qt.Equals, true)
+
+}
+
+func BenchmarkGetOrCompileRegexp(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               GetOrCompileRegexp(`\d+`)
+       }
+}
+
+func BenchmarkCompileRegexp(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               regexp.MustCompile(`\d+`)
+       }
+}
index 8d53b23b32e5e0e51e4e61fa0185e2999494da1b..df323c2161649e525ad4571828773962689fb4a9 100644 (file)
 package strings
 
 import (
-       "regexp"
-       "sync"
-
+       "github.com/gohugoio/hugo/common/hstrings"
        "github.com/spf13/cast"
 )
 
 // FindRE returns a list of strings that match the regular expression. By default all matches
 // will be included. The number of matches can be limited with an optional third parameter.
 func (ns *Namespace) FindRE(expr string, content any, limit ...any) ([]string, error) {
-       re, err := reCache.Get(expr)
+       re, err := hstrings.GetOrCompileRegexp(expr)
        if err != nil {
                return nil, err
        }
@@ -54,7 +52,7 @@ func (ns *Namespace) FindRE(expr string, content any, limit ...any) ([]string, e
 // limited with the optional limit parameter. A return value of nil indicates
 // no match.
 func (ns *Namespace) FindRESubmatch(expr string, content any, limit ...any) ([][]string, error) {
-       re, err := reCache.Get(expr)
+       re, err := hstrings.GetOrCompileRegexp(expr)
        if err != nil {
                return nil, err
        }
@@ -102,7 +100,7 @@ func (ns *Namespace) ReplaceRE(pattern, repl, s any, n ...any) (_ string, err er
                }
        }
 
-       re, err := reCache.Get(sp)
+       re, err := hstrings.GetOrCompileRegexp(sp)
        if err != nil {
                return "", err
        }
@@ -116,40 +114,3 @@ func (ns *Namespace) ReplaceRE(pattern, repl, s any, n ...any) (_ string, err er
                return re.ReplaceAllString(str, sr)
        }), nil
 }
-
-// regexpCache represents a cache of regexp objects protected by a mutex.
-type regexpCache struct {
-       mu sync.RWMutex
-       re map[string]*regexp.Regexp
-}
-
-// Get retrieves a regexp object from the cache based upon the pattern.
-// If the pattern is not found in the cache, create one
-func (rc *regexpCache) Get(pattern string) (re *regexp.Regexp, err error) {
-       var ok bool
-
-       if re, ok = rc.get(pattern); !ok {
-               re, err = regexp.Compile(pattern)
-               if err != nil {
-                       return nil, err
-               }
-               rc.set(pattern, re)
-       }
-
-       return re, nil
-}
-
-func (rc *regexpCache) get(key string) (re *regexp.Regexp, ok bool) {
-       rc.mu.RLock()
-       re, ok = rc.re[key]
-       rc.mu.RUnlock()
-       return
-}
-
-func (rc *regexpCache) set(key string, re *regexp.Regexp) {
-       rc.mu.Lock()
-       rc.re[key] = re
-       rc.mu.Unlock()
-}
-
-var reCache = regexpCache{re: make(map[string]*regexp.Regexp)}