mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-15 16:30:55 +01:00
ed324aad66
This prevents from possible corruption of the internal cache map when the underlying byte slice used by the string key is modified. Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3227
55 lines
1.5 KiB
Go
55 lines
1.5 KiB
Go
package bytesutil
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// FastStringMatcher implements fast matcher for strings.
|
|
//
|
|
// It caches string match results and returns them back on the next calls
|
|
// without calling the matchFunc, which may be expensive.
|
|
type FastStringMatcher struct {
|
|
m atomic.Value
|
|
mLen uint64
|
|
|
|
matchFunc func(s string) bool
|
|
}
|
|
|
|
// NewFastStringMatcher creates new matcher, which applies matchFunc to strings passed to Match()
|
|
//
|
|
// matchFunc must return the same result for the same input.
|
|
func NewFastStringMatcher(matchFunc func(s string) bool) *FastStringMatcher {
|
|
var fsm FastStringMatcher
|
|
fsm.m.Store(&sync.Map{})
|
|
fsm.matchFunc = matchFunc
|
|
return &fsm
|
|
}
|
|
|
|
// Match applies matchFunc to s and returns the result.
|
|
func (fsm *FastStringMatcher) Match(s string) bool {
|
|
m := fsm.m.Load().(*sync.Map)
|
|
v, ok := m.Load(s)
|
|
if ok {
|
|
// Fast path - s match result is found in the cache.
|
|
bp := v.(*bool)
|
|
return *bp
|
|
}
|
|
// Slow path - run matchFunc for s and store the result in the cache.
|
|
b := fsm.matchFunc(s)
|
|
bp := &b
|
|
// Make a copy of s in order to limit memory usage to the s length,
|
|
// since the s may point to bigger string.
|
|
// This also protects from the case when s contains unsafe string, which points to a temporary byte slice.
|
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3227
|
|
s = strings.Clone(s)
|
|
m.Store(s, bp)
|
|
n := atomic.AddUint64(&fsm.mLen, 1)
|
|
if n > 100e3 {
|
|
atomic.StoreUint64(&fsm.mLen, 0)
|
|
fsm.m.Store(&sync.Map{})
|
|
}
|
|
return b
|
|
}
|