Skip to content

Commit 98b8be8

Browse files
committed
first commit
0 parents  commit 98b8be8

File tree

14 files changed

+1384
-0
lines changed

14 files changed

+1384
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.git
2+
.svn
3+
.vscode
4+
.idea

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2025 web@tkdeng.com
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

buffer.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package regex
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"math/rand"
7+
"strconv"
8+
"testing"
9+
"time"
10+
)
11+
12+
func TestCompile(t *testing.T) {
13+
reC := Comp("this is test %1", "a")
14+
if reC.RE.ReplaceAllString(`this is test a`, `this is test b`) != `this is test b` {
15+
t.Error(`[this is test %1] [a]`, "\n", errors.New("failed to compile params"))
16+
}
17+
18+
re := `test .*`
19+
reEscaped := Escape(re)
20+
if re == reEscaped || Comp(reEscaped).Match([]byte(`test 1`)) {
21+
t.Error("[", reEscaped, "]\n", errors.New("escape function failed"))
22+
}
23+
24+
r := Comp(`test %1`, "%2", "a")
25+
if r.Match([]byte(`test a`)) {
26+
t.Error(`[test %1] [%2, a]`, "\n", errors.New("escape function failed to escape '%' char"))
27+
}
28+
}
29+
30+
func TestReplaceStr(t *testing.T) {
31+
var check = func(s string, re, r string, e string) {
32+
res := Comp(re).RepLit([]byte(s), []byte(r))
33+
if !bytes.Equal(res, []byte(e)) {
34+
t.Error("[", string(res), "]\n", errors.New("result does not match expected result"))
35+
}
36+
}
37+
38+
check("this is a test", `(?#a\s+)test`, "", "this is a ")
39+
check("string with `block` quotes", `\'.*?\'`, "'single'", "string with 'single' quotes")
40+
}
41+
42+
func TestReplaceStrComplex(t *testing.T) {
43+
var check = func(s string, re, r string, e string) {
44+
res := Comp(re).Rep([]byte(s), []byte(r))
45+
if !bytes.Equal(res, []byte(e)) {
46+
t.Error("[", string(res), "]\n", errors.New("result does not match expected result"))
47+
}
48+
}
49+
50+
check("this is a Test", `(?i)a (test)`, "some $1", "this is some Test")
51+
check("I Need Coffee!!!", `Coffee(!*)`, "More Coffee$1", "I Need More Coffee!!!")
52+
}
53+
54+
func TestReplaceFunc(t *testing.T) {
55+
var check = func(s string, re, r string, e string) {
56+
res := Comp(re).RepFunc([]byte(s), func(data func(int) []byte) []byte {
57+
return JoinBytes(data(1), ' ', r)
58+
})
59+
if !bytes.Equal(res, []byte(e)) {
60+
t.Error("[", string(res), "]\n", errors.New("result does not match expected result"))
61+
}
62+
}
63+
64+
check("this is a new test", `(new) test`, "pizza", "this is a new pizza")
65+
check("a random string", `(a) random`, "not so random", "a not so random string")
66+
}
67+
68+
func TestConcurrent(t *testing.T) {
69+
for i := 0; i < 10; i++ {
70+
for j := 0; j < 10; j++ {
71+
go (func() {
72+
res := Comp(`(t)`).RepFunc([]byte("test"), func(data func(int) []byte) []byte {
73+
return data(1)
74+
})
75+
_ = res
76+
time.Sleep(10 * time.Nanosecond)
77+
})()
78+
}
79+
80+
// time.Sleep(1000000 * 1000) // 1 second
81+
time.Sleep(1000000 * 100) // 0.1 second
82+
}
83+
}
84+
85+
func TestCache(t *testing.T) {
86+
var check = func(s string, re, r string, e string) {
87+
res := Comp(re).RepLit([]byte(s), []byte(r))
88+
if !bytes.Equal(res, []byte(e)) {
89+
t.Error("[", string(res), "]\n", errors.New("result does not match expected result"))
90+
}
91+
}
92+
93+
check("this is a test", `\sis\s`, " was ", "this was a test")
94+
check("this is a test", `\sis\s`, " was ", "this was a test")
95+
}
96+
97+
func TestFlags(t *testing.T) {
98+
var check = func(s string, re, r string, e string) {
99+
res := Comp(re).RepLit([]byte(s), []byte(r))
100+
if !bytes.Equal(res, []byte(e)) {
101+
t.Error("[", string(res), "]\n", errors.New("result does not match expected result"))
102+
}
103+
}
104+
105+
check("this is a\nmultiline text", `(?s)a\s*multiline`, "", "this is text")
106+
check("list line 1\nlist line 2\n list line 3", `(?m)^list`, "a list", "a list line 1\na list line 2\n list line 3")
107+
check("a MultiCase text", `(?i)multicase`, "", "a text")
108+
check("a MultiCase text no flag", `multicase`, "", "a MultiCase text no flag")
109+
110+
// check("a multi\nline text", `multi\s*line`, "", "a multi\nline text")
111+
}
112+
113+
func TestPerformance(t *testing.T) {
114+
for i := 0; i < 10000; i++ {
115+
Comp(strconv.Itoa(rand.Int()))
116+
}
117+
}
118+
119+
func TestValid(t *testing.T) {
120+
var check = func(re string, e bool) {
121+
res := IsValid(re)
122+
if res != e {
123+
t.Error("[", string(re), "]\n", errors.New("result does not match expected result"))
124+
}
125+
}
126+
127+
check(`[\w_\-]+`, true)
128+
check(`[\w_-.]+`, false)
129+
check(`(?<test>)`, true)
130+
check(`(?i)test`, true)
131+
}

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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package common
2+
3+
import (
4+
"math"
5+
"syscall"
6+
)
7+
8+
// JoinBytes is an easy way to join multiple values into a single []byte
9+
func JoinBytes(bytes ...any) []byte {
10+
res := []byte{}
11+
for _, b := range bytes {
12+
res = append(res, ToString[[]byte](b)...)
13+
}
14+
return res
15+
}
16+
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
29+
}

0 commit comments

Comments
 (0)