Skip to content

Commit 2308bc3

Browse files
committed
reduced dependencies
1 parent 65c1b20 commit 2308bc3

File tree

5 files changed

+175
-64
lines changed

5 files changed

+175
-64
lines changed

common/cache.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package common
2+
3+
import (
4+
"sync"
5+
"time"
6+
)
7+
8+
type CacheMap[T any] struct {
9+
value map[string]T
10+
err map[string]error
11+
lastUse map[string]time.Time
12+
mu sync.Mutex
13+
null T
14+
}
15+
16+
func NewCache[T any]() CacheMap[T] {
17+
return CacheMap[T]{
18+
value: map[string]T{},
19+
err: map[string]error{},
20+
lastUse: map[string]time.Time{},
21+
}
22+
}
23+
24+
// get returns a value or an error if it exists
25+
//
26+
// if the object key does not exist, it will return both a nil/zero value (of the relevant type) and nil error
27+
func (cache *CacheMap[T]) Get(key string) (T, error) {
28+
cache.mu.Lock()
29+
defer cache.mu.Unlock()
30+
31+
if err, ok := cache.err[key]; ok {
32+
cache.lastUse[key] = time.Now()
33+
return cache.null, err
34+
}else if val, ok := cache.value[key]; ok {
35+
cache.lastUse[key] = time.Now()
36+
return val, nil
37+
}
38+
39+
return cache.null, nil
40+
}
41+
42+
// set sets or adds a new key with either a value, or an error
43+
func (cache *CacheMap[T]) Set(key string, value T, err error) {
44+
cache.mu.Lock()
45+
defer cache.mu.Unlock()
46+
47+
if err != nil {
48+
cache.err[key] = err
49+
delete(cache.value, key)
50+
cache.lastUse[key] = time.Now()
51+
}else{
52+
cache.value[key] = value
53+
delete(cache.err, key)
54+
cache.lastUse[key] = time.Now()
55+
}
56+
}
57+
58+
// delOld removes old cache items
59+
func (cache *CacheMap[T]) DelOld(cacheTime time.Duration){
60+
cache.mu.Lock()
61+
defer cache.mu.Unlock()
62+
63+
if cacheTime == 0 {
64+
for key := range cache.lastUse {
65+
delete(cache.value, key)
66+
delete(cache.err, key)
67+
delete(cache.lastUse, key)
68+
}
69+
return
70+
}
71+
72+
now := time.Now().UnixNano()
73+
74+
for key, lastUse := range cache.lastUse {
75+
if now - lastUse.UnixNano() > int64(cacheTime) {
76+
delete(cache.value, key)
77+
delete(cache.err, key)
78+
delete(cache.lastUse, key)
79+
}
80+
}
81+
}

common/common.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package common
22

33
import (
44
"math"
5+
"syscall"
56
)
67

78
// JoinBytes is an easy way to join multiple values into a single []byte
@@ -13,7 +14,16 @@ func JoinBytes(bytes ...interface{}) []byte {
1314
return res
1415
}
1516

16-
// formatMemoryUsage converts bytes to megabytes
17-
func FormatMemoryUsage(b uint64) float64 {
18-
return math.Round(float64(b) / 1024 / 1024 * 100) / 100
17+
// SysFreeMemory returns the amount of memory available in megabytes
18+
func SysFreeMemory() float64 {
19+
in := &syscall.Sysinfo_t{}
20+
err := syscall.Sysinfo(in)
21+
if err != nil {
22+
return 0
23+
}
24+
25+
// If this is a 32-bit system, then these fields are
26+
// uint32 instead of uint64.
27+
// So we always convert to uint64 to match signature.
28+
return math.Round(float64(uint64(in.Freeram) * uint64(in.Unit)) / 1024 / 1024 * 100) / 100
1929
}

go.mod

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,4 @@ module github.com/AspieSoft/go-regex/v8
22

33
go 1.18
44

5-
require (
6-
github.com/AspieSoft/go-syncterval v1.0.5
7-
github.com/AspieSoft/go-ttlcache v1.2.2
8-
github.com/GRbit/go-pcre v1.0.1
9-
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
10-
)
11-
12-
require (
13-
github.com/alphadose/haxmap v1.3.0 // indirect
14-
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
15-
)
5+
require github.com/GRbit/go-pcre v1.0.1

go.sum

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,2 @@
1-
github.com/AspieSoft/go-syncterval v1.0.5 h1:fzSNZofSX/7iBkLrWizMzGLGcBl3d+76cHyr8M9tjGg=
2-
github.com/AspieSoft/go-syncterval v1.0.5/go.mod h1:r+mTZPOWvfS0Y5YAxKINGNt8eX76i2Lib0VeqAw3SW4=
3-
github.com/AspieSoft/go-ttlcache v1.2.2 h1:U6MMYY5PKANsB4/vhTiQsQ2U/y1P1yqMCxLw+qFNmCo=
4-
github.com/AspieSoft/go-ttlcache v1.2.2/go.mod h1:czwXaDat1SKWGJDXkoMZWZFip97MXxuU0KzqJ9zVLDo=
51
github.com/GRbit/go-pcre v1.0.1 h1:8F7Wj1rxIq8ejKSXVVW2wE+4I4VnZbuOemrMk8kn3hc=
62
github.com/GRbit/go-pcre v1.0.1/go.mod h1:0g7qVGbMpd2Odevd92x1RpaLpR3c3F/Gv2HEnI7CwEA=
7-
github.com/alphadose/haxmap v1.3.0 h1:C/2LboOnPCZP27GmmSXOcwx360st0P8N0fTJ3voefKc=
8-
github.com/alphadose/haxmap v1.3.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
9-
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
10-
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
11-
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
12-
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=

regex.go

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/AspieSoft/go-regex/v8/common"
13-
"github.com/AspieSoft/go-syncterval"
14-
"github.com/AspieSoft/go-ttlcache"
1513
"github.com/GRbit/go-pcre"
16-
"github.com/pbnjay/memory"
1714
)
1815

1916
type PCRE pcre.Regexp
@@ -38,27 +35,63 @@ var regCompBGRef *regexp.Regexp = regexp.MustCompile(`%!([0-9]+|o|c)!%`)
3835
var regComplexSel *Regexp
3936
var regEscape *Regexp
4037

41-
var cache *ttlcache.Cache[string, *Regexp] = ttlcache.New[string, *Regexp](2 * time.Hour, 1 * time.Hour)
42-
var compCache *ttlcache.Cache[string, []byte] = ttlcache.New[string, []byte](2 * time.Hour, 1 * time.Hour)
38+
// var cache *ttlcache.Cache[string, *Regexp] = ttlcache.New[string, *Regexp](2 * time.Hour, 1 * time.Hour)
39+
var cache common.CacheMap[*Regexp] = common.NewCache[*Regexp]()
40+
var compCache common.CacheMap[[]byte] = common.NewCache[[]byte]()
4341

4442
func init() {
4543
regComplexSel = Comp(`(\\|)\$([0-9]|\{[0-9]+\})`)
4644
regEscape = Comp(`[\\\^\$\.\|\?\*\+\(\)\[\]\{\}\%]`)
4745

4846
go func(){
49-
// clear cache items older than 10 minutes if there are only 200MB of free memory
50-
syncterval.New(10 * time.Second, func() {
51-
if common.FormatMemoryUsage(memory.FreeMemory()) < 200 {
52-
cache.ClearEarly(10 * time.Minute)
53-
compCache.ClearEarly(5 * time.Minute)
47+
for {
48+
time.Sleep(10 * time.Minute)
49+
50+
// default: remove cache items have not been accessed in over 2 hours
51+
cacheTime := 2 * time.Hour
52+
53+
// SysFreeMemory returns the total free system memory in megabytes
54+
mb := common.SysFreeMemory()
55+
if mb < 200 && mb != 0 {
56+
// low memory: remove cache items have not been accessed in over 10 minutes
57+
cacheTime = 10 * time.Minute
58+
}else if mb < 500 && mb != 0 {
59+
// low memory: remove cache items have not been accessed in over 30 minutes
60+
cacheTime = 30 * time.Minute
61+
}else if mb < 2000 && mb != 0 {
62+
// low memory: remove cache items have not been accessed in over 1 hour
63+
cacheTime = 1 * time.Hour
64+
}else if mb > 64000 {
65+
// high memory: remove cache items have not been accessed in over 12 hour
66+
cacheTime = 12 * time.Hour
67+
}else if mb > 32000 {
68+
// high memory: remove cache items have not been accessed in over 6 hour
69+
cacheTime = 6 * time.Hour
70+
}else if mb > 16000 {
71+
// high memory: remove cache items have not been accessed in over 3 hour
72+
cacheTime = 3 * time.Hour
5473
}
55-
})
74+
75+
cache.DelOld(cacheTime)
76+
compCache.DelOld(cacheTime)
77+
78+
time.Sleep(10 * time.Second)
79+
80+
// clear cache if were still critically low on available memory
81+
if mb := common.SysFreeMemory(); mb < 10 && mb != 0 {
82+
cache.DelOld(0)
83+
compCache.DelOld(0)
84+
}
85+
}
5686
}()
5787
}
5888

5989
// this method compiles the RE string to add more functionality to it
6090
func compRE(re string, params []string) string {
61-
if val, ok := compCache.Get(re); ok {
91+
if val, err := compCache.Get(re); val != nil || err != nil {
92+
if err != nil {
93+
return ""
94+
}
6295
return string(regCompParam.ReplaceAllFunc(val, func(b []byte) []byte {
6396
if b[1] == '{' && b[len(b)-1] == '}' {
6497
b = b[2:len(b)-1]
@@ -162,7 +195,7 @@ func compRE(re string, params []string) string {
162195
return []byte{}
163196
})
164197

165-
compCache.Set(re, reB)
198+
compCache.Set(re, reB, nil)
166199

167200
return string(regCompParam.ReplaceAllFunc(reB, func(b []byte) []byte {
168201
if b[1] == '{' && b[len(b)-1] == '}' {
@@ -185,49 +218,56 @@ func compRE(re string, params []string) string {
185218
func Comp(re string, params ...string) *Regexp {
186219
re = compRE(re, params)
187220

188-
if val, ok := cache.Get(re); ok {
221+
if val, err := cache.Get(re); val != nil || err != nil {
222+
if err != nil {
223+
panic(err)
224+
}
189225
return val
190-
} else {
191-
reg := pcre.MustCompile(re, pcre.UTF8)
226+
}
192227

193-
// commented below methods compiled 10000 times in 0.1s (above method being used finished in half of that time)
194-
// reg := pcre.MustCompileParse(re)
195-
// reg := pcre.MustCompileJIT(re, pcre.UTF8, pcre.STUDY_JIT_COMPILE)
196-
// reg := pcre.MustCompileJIT(re, pcre.EXTRA, pcre.STUDY_JIT_COMPILE)
197-
// reg := pcre.MustCompileJIT(re, pcre.JAVASCRIPT_COMPAT, pcre.STUDY_JIT_COMPILE)
198-
// reg := pcre.MustCompileParseJIT(re, pcre.STUDY_JIT_COMPILE)
228+
reg := pcre.MustCompile(re, pcre.UTF8)
199229

200-
compRe := Regexp{RE: reg, len: int64(len(re))}
230+
// commented below methods compiled 10000 times in 0.1s (above method being used finished in half of that time)
231+
// reg := pcre.MustCompileParse(re)
232+
// reg := pcre.MustCompileJIT(re, pcre.UTF8, pcre.STUDY_JIT_COMPILE)
233+
// reg := pcre.MustCompileJIT(re, pcre.EXTRA, pcre.STUDY_JIT_COMPILE)
234+
// reg := pcre.MustCompileJIT(re, pcre.JAVASCRIPT_COMPAT, pcre.STUDY_JIT_COMPILE)
235+
// reg := pcre.MustCompileParseJIT(re, pcre.STUDY_JIT_COMPILE)
201236

202-
cache.Set(re, &compRe)
203-
return &compRe
204-
}
237+
compRe := Regexp{RE: reg, len: int64(len(re))}
238+
239+
cache.Set(re, &compRe, nil)
240+
return &compRe
205241
}
206242

207243
// CompTry tries to compile or returns an error
208244
func CompTry(re string, params ...string) (*Regexp, error) {
209245
re = compRE(re, params)
210246

211-
if val, ok := cache.Get(re); ok {
212-
return val, nil
213-
} else {
214-
reg, err := pcre.Compile(re, pcre.UTF8)
247+
if val, err := cache.Get(re); val != nil || err != nil {
215248
if err != nil {
216249
return &Regexp{}, err
217250
}
251+
return val, nil
252+
}
218253

219-
// commented below methods compiled 10000 times in 0.1s (above method being used finished in half of that time)
220-
// reg := pcre.MustCompileParse(re)
221-
// reg := pcre.MustCompileJIT(re, pcre.UTF8, pcre.STUDY_JIT_COMPILE)
222-
// reg := pcre.MustCompileJIT(re, pcre.EXTRA, pcre.STUDY_JIT_COMPILE)
223-
// reg := pcre.MustCompileJIT(re, pcre.JAVASCRIPT_COMPAT, pcre.STUDY_JIT_COMPILE)
224-
// reg := pcre.MustCompileParseJIT(re, pcre.STUDY_JIT_COMPILE)
254+
reg, err := pcre.Compile(re, pcre.UTF8)
255+
if err != nil {
256+
cache.Set(re, nil, err)
257+
return &Regexp{}, err
258+
}
225259

226-
compRe := Regexp{RE: reg, len: int64(len(re))}
260+
// commented below methods compiled 10000 times in 0.1s (above method being used finished in half of that time)
261+
// reg := pcre.MustCompileParse(re)
262+
// reg := pcre.MustCompileJIT(re, pcre.UTF8, pcre.STUDY_JIT_COMPILE)
263+
// reg := pcre.MustCompileJIT(re, pcre.EXTRA, pcre.STUDY_JIT_COMPILE)
264+
// reg := pcre.MustCompileJIT(re, pcre.JAVASCRIPT_COMPAT, pcre.STUDY_JIT_COMPILE)
265+
// reg := pcre.MustCompileParseJIT(re, pcre.STUDY_JIT_COMPILE)
227266

228-
cache.Set(re, &compRe)
229-
return &compRe, nil
230-
}
267+
compRe := Regexp{RE: reg, len: int64(len(re))}
268+
269+
cache.Set(re, &compRe, nil)
270+
return &compRe, nil
231271
}
232272

233273

0 commit comments

Comments
 (0)