import (
"context"
"errors"
- "sort"
- "sync"
-)
+ "maps"
+ "sync/atomic"
-var (
- ErrWrongType = errors.New("ErrWrongType")
+ psync "github.com/qydysky/part/sync"
)
-type CItem struct {
- Key string
- Deal func(ctx context.Context, ptr any) error
-}
-
-func NewCI[T any](key string, f func(ctx context.Context, ptr *T) error) *CItem {
- return &CItem{
- Key: key,
- Deal: func(ctx context.Context, ptr any) error {
- if item, ok := ptr.(*T); ok {
- return f(ctx, item)
- }
- return ErrWrongType
- },
- }
-}
-
var (
- ErrNotExist = errors.New("ErrNotExist")
- ErrCItemErr = errors.New("ErrCItemErr")
- ErrConflict = errors.New("ErrConflict")
+ ErrNoLink = errors.New("ErrNoLink")
+ ErrLinked = errors.New("ErrLinked")
+ ErrNoExist = errors.New("ErrNoExist")
+ ErrConflict = errors.New("ErrConflict")
+ ErrWrongType = errors.New("ErrWrongType")
)
type components struct {
- MatchF func(mkey, key string) bool
-
- withLock bool
- m []*CItem
- mm map[string]int
- sync.RWMutex
+ m psync.Map
+ link map[string][]string
+ loadLink atomic.Bool
}
-// strings.HasPrefix
-//
-// strings.HasSuffix
-//
-// DotMatch
-func NewComp(withLock bool, matchf func(mkey, key string) bool) *components {
- return &components{
- MatchF: matchf,
- withLock: withLock,
- mm: make(map[string]int),
- }
+func NewComp() *components {
+ return &components{link: make(map[string][]string)}
}
-func (t *components) Put(item *CItem) error {
- if t.withLock {
- t.Lock()
- defer t.Unlock()
- }
- 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
- })
- for i := 0; i < len(t.m); i++ {
- t.mm[t.m[i].Key] = i
+func (t *components) Put(Key string, Deal func(ctx context.Context, ptr any) error) error {
+ _, loaded := t.m.LoadOrStore(Key, Deal)
+ if loaded {
+ return ErrConflict
}
return nil
}
-func (t *components) Del(key string) {
- if t.withLock {
- t.Lock()
- defer t.Unlock()
- }
- for i := 0; i < len(t.m); i++ {
- 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
- }
+func (t *components) Del(Key string) {
+ t.m.Delete(Key)
}
func (t *components) Run(key string, ctx context.Context, ptr any) error {
- if t.withLock {
- t.RLock()
- defer t.RUnlock()
+ if !t.loadLink.Load() {
+ return ErrNoLink
}
-
- var (
- i = 0
- got = false
- )
-
- if mi, ok := t.mm[key]; ok {
- i = mi
+ links := t.link[key]
+ if len(links) == 0 {
+ return ErrNoLink
}
- 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)
+ for i := 0; i < len(links); i++ {
+ if deal, ok := t.m.LoadV(links[i]).(func(ctx context.Context, ptr any) error); ok {
+ if e := deal(ctx, ptr); e != nil {
+ return e
+ }
}
}
+ return nil
+}
- if !got {
- return errors.Join(ErrNotExist, errors.New(key))
+func (t *components) Link(link map[string][]string) error {
+ if t.loadLink.CompareAndSwap(false, true) {
+ t.link = maps.Clone(link)
}
-
- return nil
+ return ErrLinked
}
-func Put[T any](key string, f func(ctx context.Context, ptr *T) error) error {
- return Comp.Put(&CItem{
- Key: key,
- Deal: func(ctx context.Context, ptr any) error {
- if item, ok := ptr.(*T); ok {
- return f(ctx, item)
- }
- return errors.Join(ErrWrongType, errors.New(key))
- },
+func Put[T any](key string, deal func(ctx context.Context, ptr *T) error) error {
+ return Comp.Put(key, func(ctx context.Context, ptr any) error {
+ if item, ok := ptr.(*T); ok {
+ return deal(ctx, item)
+ }
+ return errors.Join(ErrWrongType, errors.New(key))
})
}
return Comp.Run(key, ctx, ptr)
}
-func Init(withLock bool, f func(mkey, key string) bool) {
- Comp = NewComp(withLock, f)
-}
-
-func DotMatch(mkey, key string) bool {
- return mkey == key || (len(mkey) > len(key) && mkey[0:len(key)] == key && mkey[len(key)] == '.')
+func Link(link map[string][]string) error {
+ return Comp.Link(link)
}
-var Comp *components = NewComp(false, DotMatch)
+var Comp *components = NewComp()
)
func Test1(t *testing.T) {
- Init(false, DotMatch)
-
Put(`1`, func(ctx context.Context, ptr *int) error {
if *ptr > 1 {
return nil
t.Fatal(e)
}
- Comp.Put(&CItem{
- Key: `1.2`,
- Deal: func(_ context.Context, ptr any) error {
- if sp, ok := ptr.(*int); ok && *sp >= 2 {
- return nil
- }
- return errors.New("1.2")
- },
+ Comp.Put(`1.2`, func(_ context.Context, ptr any) error {
+ if sp, ok := ptr.(*int); ok && *sp >= 2 {
+ return nil
+ }
+ return errors.New("1.2")
})
- Comp.Put(&CItem{
- Key: `1.2.1`,
- Deal: func(_ context.Context, ptr any) error {
- if sp, ok := ptr.(*int); ok && *sp >= 3 {
- return nil
- }
- return errors.New("1.2.1")
- },
+ Comp.Put(`1.2.1`, func(_ context.Context, ptr any) error {
+ if sp, ok := ptr.(*int); ok && *sp >= 3 {
+ return nil
+ }
+ return errors.New("1.2.1")
+ })
+
+ Comp.Link(map[string][]string{
+ `1`: {`1`},
})
var s = 3
Comp.Del(`1.2`)
- 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()
}
}
func TestDot(t *testing.T) {
- Init(false, DotMatch)
Put[int](`1`, func(ctx context.Context, ptr *int) error {
if *ptr == 1 {
return nil
Put[int](`1.2`, func(ctx context.Context, ptr *int) error {
return errors.New("1.2")
})
+ Link(map[string][]string{
+ `1`: {`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) {
- Init(false, DotMatch)
sumup := func(ctx context.Context, ptr *int) error {
return nil
}
} else {
println("bili_danmu.Reply.wsmsg.preparing.sumup")
}
+ Link(map[string][]string{
+ `bili_danmu.Reply.wsmsg.preparing`: {`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(false, DotMatch)
- b.ResetTimer()
for i := 0; i < b.N; i++ {
Put[int](strconv.Itoa(i), func(ctx context.Context, ptr *int) error {
return nil
}
func Benchmark1(b *testing.B) {
- Init(false, DotMatch)
for i := 0; i < 1000; i++ {
Put[int](`1`, func(ctx context.Context, ptr *int) error {
return nil
})
}
+ Link(map[string][]string{
+ `1`: {`1`},
+ })
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {