// A Regexp is safe for concurrent use by multiple goroutines,
// except for configuration methods, such as Longest.
type Regexp struct {
- // read-only after Compile
- regexpRO
+ // cache of machines for running regexp. This is a shared pointer across
+ // all copies of the original Regexp object to decrease the overall
+ // memory footprint of the regexps (since there will be one machine
+ // cached per thread instead of one per thread per copy).
+ machines *sync.Pool
- // cache of machines for running regexp
- mu sync.Mutex
- machine []*machine
-}
-
-type regexpRO struct {
+ // everything below is read-only after Compile
expr string // as passed to Compile
prog *syntax.Prog // compiled program
onepass *onePassProg // onepass program or nil
// Copy returns a new Regexp object copied from re.
//
-// When using a Regexp in multiple goroutines, giving each goroutine
-// its own copy helps to avoid lock contention.
+// Deprecated: This exists for historical reasons.
func (re *Regexp) Copy() *Regexp {
- // It is not safe to copy Regexp by value
- // since it contains a sync.Mutex.
- return &Regexp{
- regexpRO: re.regexpRO,
- }
+ re2 := *re
+ return &re2
}
// Compile parses a regular expression and returns, if successful,
if err != nil {
return nil, err
}
+ onepass := compileOnePass(prog)
regexp := &Regexp{
- regexpRO: regexpRO{
- expr: expr,
- prog: prog,
- onepass: compileOnePass(prog),
- numSubexp: maxCap,
- subexpNames: capNames,
- cond: prog.StartCond(),
- longest: longest,
+ expr: expr,
+ prog: prog,
+ onepass: onepass,
+ numSubexp: maxCap,
+ subexpNames: capNames,
+ cond: prog.StartCond(),
+ longest: longest,
+ }
+ regexp.machines = &sync.Pool{
+ New: func() interface{} {
+ z := progMachine(prog, onepass)
+ z.re = regexp
+ return z
},
}
if regexp.onepass == notOnePass {
// It uses the re's machine cache if possible, to avoid
// unnecessary allocation.
func (re *Regexp) get() *machine {
- re.mu.Lock()
- if n := len(re.machine); n > 0 {
- z := re.machine[n-1]
- re.machine = re.machine[:n-1]
- re.mu.Unlock()
- return z
- }
- re.mu.Unlock()
- z := progMachine(re.prog, re.onepass)
- z.re = re
- return z
+ return re.machines.Get().(*machine)
}
// put returns a machine to the re's machine cache.
z.inputString.str = ""
z.inputReader.r = nil
- re.mu.Lock()
- re.machine = append(re.machine, z)
- re.mu.Unlock()
+ re.machines.Put(z)
}
// MustCompile is like Compile but panics if the expression cannot be parsed.