"context"
"errors"
"sort"
- "strings"
"sync"
)
type CItem struct {
Key string
- Lock bool
Deal func(ctx context.Context, ptr any) error
- l sync.Mutex
}
-func (t *CItem) deal(ctx context.Context, ptr any) error {
- t.l.Lock()
- defer t.l.Unlock()
- return t.Deal(ctx, ptr)
-}
-
-func NewCI[T any](key string, lock bool, f func(ctx context.Context, ptr *T) error) *CItem {
+func NewCI[T any](key string, f func(ctx context.Context, ptr *T) error) *CItem {
return &CItem{
- Key: key,
- Lock: lock,
+ Key: key,
Deal: func(ctx context.Context, ptr any) error {
if item, ok := ptr.(*T); ok {
return f(ctx, item)
ErrConflict = errors.New("ErrConflict")
)
-type Components struct {
+type components struct {
+ MatchF func(mkey, key string) bool
+
m []*CItem
mm map[string]int
sync.RWMutex
}
-func (t *Components) Put(item *CItem) error {
+// strings.HasPrefix
+//
+// strings.HasSuffix
+//
+// DotMatch
+func NewComp(matchf func(mkey, key string) bool) *components {
+ return &components{
+ MatchF: matchf,
+ mm: make(map[string]int),
+ }
+}
+
+func (t *components) Put(item *CItem) error {
t.Lock()
defer t.Unlock()
- for i := 0; i < len(t.m); i++ {
- if t.m[i].Key == item.Key {
- return ErrConflict
- }
+ if _, ok := t.mm[item.Key]; ok {
+ return errors.Join(ErrConflict, errors.New(item.Key))
}
t.m = append(t.m, item)
sort.Slice(t.m, func(i, j int) bool {
return t.m[i].Key < t.m[j].Key
})
- if t.mm == nil {
- t.mm = make(map[string]int)
- }
for i := 0; i < len(t.m); i++ {
- if strings.HasPrefix(item.Key, t.m[i].Key) {
- t.mm[t.m[i].Key] = i
- }
+ t.mm[t.m[i].Key] = i
}
return nil
}
-func (t *Components) Del(key string) {
+func (t *components) Del(key string) {
t.Lock()
for i := 0; i < len(t.m); i++ {
- if strings.HasPrefix(t.m[i].Key, key) {
+ if t.MatchF(t.m[i].Key, key) {
delete(t.mm, t.m[i].Key)
t.m = append(t.m[:i], t.m[i+1:]...)
i -= 1
}
}
+ for i := 0; i < len(t.m); i++ {
+ t.mm[t.m[i].Key] = i
+ }
t.Unlock()
}
-func (t *Components) Run(key string, ctx context.Context, ptr any) error {
+func (t *components) Run(key string, ctx context.Context, ptr any) error {
t.RLock()
defer t.RUnlock()
- if i, ok := t.mm[key]; ok {
- for ; i < len(t.m) && strings.HasPrefix(t.m[i].Key, key); i++ {
- if e := t.m[i].deal(ctx, ptr); e != nil {
- return errors.Join(ErrCItemErr, e)
- }
+ var (
+ i = 0
+ got = false
+ )
+
+ if mi, ok := t.mm[key]; ok {
+ i = mi
+ }
+ for ; i < len(t.m) && t.MatchF(t.m[i].Key, key); i++ {
+ got = true
+ if e := t.m[i].Deal(ctx, ptr); e != nil {
+ return errors.Join(ErrCItemErr, e)
}
- } else {
- return ErrNotExist
+ }
+ if !got {
+ return errors.Join(ErrNotExist, errors.New(key))
}
return nil
}
-func Put[T any](key string, lock bool, f func(ctx context.Context, ptr *T) error) error {
+func Put[T any](key string, f func(ctx context.Context, ptr *T) error) error {
return Comp.Put(&CItem{
- Key: key,
- Lock: lock,
+ Key: key,
Deal: func(ctx context.Context, ptr any) error {
if item, ok := ptr.(*T); ok {
return f(ctx, item)
}
- return ErrWrongType
+ return errors.Join(ErrWrongType, errors.New(key))
},
})
}
return Comp.Run(key, ctx, ptr)
}
-var Comp Components
+func Init(f func(mkey, key string) bool) {
+ Comp = NewComp(f)
+}
+
+func DotMatch(mkey, key string) bool {
+ return mkey == key || (len(mkey) > len(key) && mkey[0:len(key)] == key && mkey[len(key)] == '.')
+}
+
+var Comp *components = NewComp(DotMatch)
import (
"context"
"errors"
+ "strconv"
+ "strings"
"testing"
)
func Test1(t *testing.T) {
- Put(`1`, false, func(ctx context.Context, ptr *int) error {
+ Init(DotMatch)
+
+ Put(`1`, func(ctx context.Context, ptr *int) error {
if *ptr > 1 {
return nil
}
return errors.New("1")
})
- if e := Put(`1`, false, func(ctx context.Context, ptr *int) error {
+ if e := Put(`1`, func(ctx context.Context, ptr *int) error {
if *ptr > 1 {
return nil
}
return errors.New("1")
- }); e != ErrConflict {
- t.Fatal()
+ }); !errors.Is(e, ErrConflict) {
+ t.Fatal(e)
}
Comp.Put(&CItem{
Comp.Del(`1.2`)
- if e := Comp.Run(`1.2.1`, context.Background(), &s); e != ErrNotExist {
- t.Fatal(e)
+ for i := 0; i < len(Comp.m); i++ {
+ t.Log(Comp.m[i])
+ }
+ if e := Comp.Run(`1.2.1`, context.Background(), &s); e == nil {
+ t.Fatal()
}
if e := Comp.Run(`1`, context.Background(), &s); e != nil {
}
}
+func TestDot(t *testing.T) {
+ Init(DotMatch)
+ Put[int](`1`, func(ctx context.Context, ptr *int) error {
+ if *ptr == 1 {
+ return nil
+ } else {
+ return errors.New("1")
+ }
+ })
+ Put[int](`12`, func(ctx context.Context, ptr *int) error {
+ return errors.New("12")
+ })
+ Put[int](`1.2`, func(ctx context.Context, ptr *int) error {
+ return errors.New("1.2")
+ })
+ i := 1
+ if e := Run(`1`, context.Background(), &i); !strings.Contains(e.Error(), "1.2") {
+ t.Fatal(e)
+ }
+}
+
+func Test3(t *testing.T) {
+ sumup := func(ctx context.Context, ptr *int) error {
+ return nil
+ }
+ if e := Put[int](`bili_danmu.Reply.wsmsg.preparing.sumup`, sumup); e != nil {
+ panic(e)
+ } else {
+ println("bili_danmu.Reply.wsmsg.preparing.sumup")
+ }
+ i := 1
+ if e := Run(`bili_danmu.Reply.wsmsg.preparing`, context.Background(), &i); e != nil {
+ t.Fatal(e)
+ }
+}
+
func Benchmark2(b *testing.B) {
+ Init(DotMatch)
b.ResetTimer()
for i := 0; i < b.N; i++ {
- Put[int](`1`, false, func(ctx context.Context, ptr *int) error {
+ Put[int](strconv.Itoa(i), func(ctx context.Context, ptr *int) error {
return nil
})
}
}
func Benchmark1(b *testing.B) {
- Put[int](`1`, false, func(ctx context.Context, ptr *int) error {
- return nil
- })
- Put[int](`1.2`, false, func(ctx context.Context, ptr *int) error {
- return nil
- })
+ Init(DotMatch)
+ for i := 0; i < 1000; i++ {
+ Put[int](strconv.Itoa(i), func(ctx context.Context, ptr *int) error {
+ return nil
+ })
+ }
+ ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
- if e := Run(`1.2`, context.Background(), &i); e != nil {
- b.Fatal()
+ if e := Run(`1`, ctx, &i); e != nil {
+ b.Fatal(e)
}
}
}