diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-structr')
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/README.md | 70 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/cache.go | 83 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/hash.go | 370 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/hasher.go | 176 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/index.go | 26 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/key.go | 204 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/util.go | 50 |
7 files changed, 680 insertions, 299 deletions
diff --git a/vendor/codeberg.org/gruf/go-structr/README.md b/vendor/codeberg.org/gruf/go-structr/README.md index e2a9bdc15..125b20090 100644 --- a/vendor/codeberg.org/gruf/go-structr/README.md +++ b/vendor/codeberg.org/gruf/go-structr/README.md @@ -2,4 +2,74 @@ A performant struct caching library with automated indexing by arbitrary combinations of fields, including support for negative results (errors!). An example use case is in database lookups. +Some example code of how you can use `go-structr` in your application: +```golang +type Cached struct { + Username string + Domain string + URL string + CountryCode int +} + +var c structr.Cache[*Cached] + +c.Init(structr.Config[*Cached]{ + + // Fields this cached struct type + // will be indexed and stored under. + Indices: []structr.IndexConfig{ + {Fields: "Username,Domain", AllowZero: true}, + {Fields: "URL"}, + {Fields: "CountryCode", Multiple: true}, + }, + + // Maximum LRU cache size before + // new entries cause evictions. + MaxSize: 1000, + + // User provided value copy function to + // reduce need for reflection + ensure + // concurrency safety for returned values. + CopyValue: func(c *Cached) *Cached { + c2 := new(Cached) + *c2 = *c + return c2 + }, + + // User defined invalidation hook. + Invalidate: func(c *Cached) { + log.Println("invalidated:", c) + }, +}) + +var url string + +// Load value from cache, with callback function to hydrate +// cache if value cannot be found under index name with key. +// Negative (error) results are also cached, with user definable +// errors to ignore from caching (e.g. context deadline errs). +value, err := c.LoadOne("URL", func() (*Cached, error) { + return dbType.SelectByURL(url) +}, url) +if err != nil { + return nil, err +} + +// Store value in cache, only if provided callback +// function returns without error. Passes value through +// invalidation hook regardless of error return value. +// +// On success value will be automatically added to and +// accessible under all initially configured indices. +if err := c.Store(value, func() error { + return dbType.Insert(value) +}); err != nil { + return nil, err +} + +// Invalidate all cached results stored under +// provided index name with give field value(s). +c.Invalidate("CountryCode", 42) +``` + This is a core underpinning of [GoToSocial](https://github.com/superseriousbusiness/gotosocial)'s performance.
\ No newline at end of file diff --git a/vendor/codeberg.org/gruf/go-structr/cache.go b/vendor/codeberg.org/gruf/go-structr/cache.go index b958fdfdb..fb52f0d8d 100644 --- a/vendor/codeberg.org/gruf/go-structr/cache.go +++ b/vendor/codeberg.org/gruf/go-structr/cache.go @@ -111,8 +111,8 @@ func (c *Cache[T]) Init(config Config[T]) { // provided config. c.mutex.Lock() c.indices = make([]Index[T], len(config.Indices)) - for i, config := range config.Indices { - c.indices[i].init(config) + for i, cfg := range config.Indices { + c.indices[i].init(cfg, config.MaxSize) } c.ignore = config.IgnoreErr c.copy = config.CopyValue @@ -138,7 +138,7 @@ func (c *Cache[T]) GetOne(index string, keyParts ...any) (T, bool) { idx := c.Index(index) // Generate index key from provided parts. - key, ok := idx.keygen.FromParts(keyParts...) + key, ok := idx.hasher.FromParts(keyParts...) if !ok { var zero T return zero, false @@ -149,7 +149,7 @@ func (c *Cache[T]) GetOne(index string, keyParts ...any) (T, bool) { } // GetOneBy fetches value from cache stored under index, using precalculated index key. -func (c *Cache[T]) GetOneBy(index *Index[T], key string) (T, bool) { +func (c *Cache[T]) GetOneBy(index *Index[T], key uint64) (T, bool) { if index == nil { panic("no index given") } else if !index.unique { @@ -170,37 +170,33 @@ func (c *Cache[T]) Get(index string, keysParts ...[]any) []T { idx := c.Index(index) // Preallocate expected keys slice length. - keys := make([]string, 0, len(keysParts)) + keys := make([]uint64, 0, len(keysParts)) - // Acquire buf. - buf := getBuf() + // Acquire hasher. + h := getHasher() for _, parts := range keysParts { - // Reset buf. - buf.Reset() + h.Reset() // Generate key from provided parts into buffer. - if !idx.keygen.AppendFromParts(buf, parts...) { + key, ok := idx.hasher.fromParts(h, parts...) + if !ok { continue } - // Get string copy of - // genarated idx key. - key := string(buf.B) - - // Append key to keys. + // Append hash sum to keys. keys = append(keys, key) } - // Done with buf. - putBuf(buf) + // Done with h. + putHasher(h) // Continue fetching values. return c.GetBy(idx, keys...) } // GetBy fetches values from the cache stored under index, using precalculated index keys. -func (c *Cache[T]) GetBy(index *Index[T], keys ...string) []T { +func (c *Cache[T]) GetBy(index *Index[T], keys ...uint64) []T { if index == nil { panic("no index given") } @@ -265,7 +261,7 @@ func (c *Cache[T]) Put(values ...T) { // Store all the passed values. for _, value := range values { - c.store(nil, "", value, nil) + c.store(nil, 0, value, nil) } // Done with lock. @@ -288,7 +284,7 @@ func (c *Cache[T]) LoadOne(index string, load func() (T, error), keyParts ...any idx := c.Index(index) // Generate cache from from provided parts. - key, _ := idx.keygen.FromParts(keyParts...) + key, _ := idx.hasher.FromParts(keyParts...) // Continue loading this result. return c.LoadOneBy(idx, load, key) @@ -296,7 +292,7 @@ func (c *Cache[T]) LoadOne(index string, load func() (T, error), keyParts ...any // LoadOneBy fetches one result from the cache stored under index, using precalculated index key. // In the case that no result is found, provided load callback will be used to hydrate the cache. -func (c *Cache[T]) LoadOneBy(index *Index[T], load func() (T, error), key string) (T, error) { +func (c *Cache[T]) LoadOneBy(index *Index[T], load func() (T, error), key uint64) (T, error) { if index == nil { panic("no index given") } else if !index.unique { @@ -421,26 +417,21 @@ func (c *Cache[T]) LoadBy(index *Index[T], get func(load func(keyParts ...any) b } }() - // Acquire buf. - buf := getBuf() + // Acquire hasher. + h := getHasher() // Pass cache check to user func. get(func(keyParts ...any) bool { - - // Reset buf. - buf.Reset() + h.Reset() // Generate index key from provided key parts. - if !index.keygen.AppendFromParts(buf, keyParts...) { + key, ok := index.hasher.fromParts(h, keyParts...) + if !ok { return false } - // Get temp generated key str, - // (not needed after return). - keyStr := buf.String() - // Get all indexed results. - list := index.data[keyStr] + list := index.data[key] if list != nil && list.len > 0 { // Value length before @@ -471,8 +462,8 @@ func (c *Cache[T]) LoadBy(index *Index[T], get func(load func(keyParts ...any) b return false }) - // Done with buf. - putBuf(buf) + // Done with h. + putHasher(h) // Done with lock. c.mutex.Unlock() @@ -528,7 +519,7 @@ func (c *Cache[T]) Invalidate(index string, keyParts ...any) { idx := c.Index(index) // Generate cache from from provided parts. - key, ok := idx.keygen.FromParts(keyParts...) + key, ok := idx.hasher.FromParts(keyParts...) if !ok { return } @@ -538,7 +529,7 @@ func (c *Cache[T]) Invalidate(index string, keyParts ...any) { } // InvalidateBy invalidates all results stored under index key. -func (c *Cache[T]) InvalidateBy(index *Index[T], key string) { +func (c *Cache[T]) InvalidateBy(index *Index[T], key uint64) { if index == nil { panic("no index given") } @@ -639,7 +630,7 @@ func (c *Cache[T]) Cap() int { // store will store the given value / error result in the cache, storing it under the // already provided index + key if provided, else generating keys from provided value. -func (c *Cache[T]) store(index *Index[T], key string, value T, err error) { +func (c *Cache[T]) store(index *Index[T], key uint64, value T, err error) { // Acquire new result. res := result_acquire(c) @@ -671,8 +662,8 @@ func (c *Cache[T]) store(index *Index[T], key string, value T, err error) { // value, used during cache key gen. rvalue := reflect.ValueOf(value) - // Acquire buf. - buf := getBuf() + // Acquire hasher. + h := getHasher() for i := range c.indices { // Get current index ptr. @@ -684,22 +675,20 @@ func (c *Cache[T]) store(index *Index[T], key string, value T, err error) { continue } - // Generate key from reflect value, + // Generate hash from reflect value, // (this ignores zero value keys). - buf.Reset() // reset buf first - if !idx.keygen.appendFromRValue(buf, rvalue) { + h.Reset() // reset buf first + key, ok := idx.hasher.fromRValue(h, rvalue) + if !ok { continue } - // Alloc key copy. - key := string(buf.B) - // Append result to index at key. index_append(c, idx, key, res) } - // Done with buf. - putBuf(buf) + // Done with h. + putHasher(h) } if c.lruList.len > c.maxSize { diff --git a/vendor/codeberg.org/gruf/go-structr/hash.go b/vendor/codeberg.org/gruf/go-structr/hash.go new file mode 100644 index 000000000..84f0e62fc --- /dev/null +++ b/vendor/codeberg.org/gruf/go-structr/hash.go @@ -0,0 +1,370 @@ +package structr + +import ( + "reflect" + "unsafe" + + "github.com/zeebo/xxh3" +) + +func hasher(t reflect.Type) func(*xxh3.Hasher, any) bool { + switch t.Kind() { + case reflect.Int, + reflect.Uint, + reflect.Uintptr: + switch unsafe.Sizeof(int(0)) { + case 4: + return hash32bit + case 8: + return hash64bit + default: + panic("unexpected platform int size") + } + + case reflect.Int8, + reflect.Uint8: + return hash8bit + + case reflect.Int16, + reflect.Uint16: + return hash16bit + + case reflect.Int32, + reflect.Uint32, + reflect.Float32: + return hash32bit + + case reflect.Int64, + reflect.Uint64, + reflect.Float64, + reflect.Complex64: + return hash64bit + + case reflect.String: + return hashstring + + case reflect.Pointer: + switch t.Elem().Kind() { + case reflect.Int, + reflect.Uint, + reflect.Uintptr: + switch unsafe.Sizeof(int(0)) { + case 4: + return hash32bitptr + case 8: + return hash64bitptr + default: + panic("unexpected platform int size") + } + + case reflect.Int8, + reflect.Uint8: + return hash8bitptr + + case reflect.Int16, + reflect.Uint16: + return hash16bitptr + + case reflect.Int32, + reflect.Uint32, + reflect.Float32: + return hash32bitptr + + case reflect.Int64, + reflect.Uint64, + reflect.Float64, + reflect.Complex64: + return hash64bitptr + + case reflect.String: + return hashstringptr + } + + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Int, + reflect.Uint, + reflect.Uintptr: + switch unsafe.Sizeof(int(0)) { + case 4: + return hash32bitslice + case 8: + return hash64bitslice + default: + panic("unexpected platform int size") + } + + case reflect.Int8, + reflect.Uint8: + return hash8bitslice + + case reflect.Int16, + reflect.Uint16: + return hash16bitslice + + case reflect.Int32, + reflect.Uint32, + reflect.Float32: + return hash32bitslice + + case reflect.Int64, + reflect.Uint64, + reflect.Float64, + reflect.Complex64: + return hash64bitslice + + case reflect.String: + return hashstringslice + } + } + switch { + case t.Implements(reflect.TypeOf((*interface{ MarshalBinary() ([]byte, error) })(nil)).Elem()): + return hashbinarymarshaler + + case t.Implements(reflect.TypeOf((*interface{ Bytes() []byte })(nil)).Elem()): + return hashbytesmethod + + case t.Implements(reflect.TypeOf((*interface{ String() string })(nil)).Elem()): + return hashstringmethod + + case t.Implements(reflect.TypeOf((*interface{ MarshalText() ([]byte, error) })(nil)).Elem()): + return hashtextmarshaler + + case t.Implements(reflect.TypeOf((*interface{ MarshalJSON() ([]byte, error) })(nil)).Elem()): + return hashjsonmarshaler + } + panic("unhashable type") +} + +func hash8bit(h *xxh3.Hasher, a any) bool { + u := *(*uint8)(iface_value(a)) + _, _ = h.Write([]byte{u}) + return u == 0 +} + +func hash8bitptr(h *xxh3.Hasher, a any) bool { + u := (*uint8)(iface_value(a)) + if u == nil { + _, _ = h.Write([]byte{ + 0, + }) + return true + } else { + _, _ = h.Write([]byte{ + 1, + byte(*u), + }) + return false + } +} + +func hash8bitslice(h *xxh3.Hasher, a any) bool { + b := *(*[]byte)(iface_value(a)) + _, _ = h.Write(b) + return b == nil +} + +func hash16bit(h *xxh3.Hasher, a any) bool { + u := *(*uint16)(iface_value(a)) + _, _ = h.Write([]byte{ + byte(u), + byte(u >> 8), + }) + return u == 0 +} + +func hash16bitptr(h *xxh3.Hasher, a any) bool { + u := (*uint16)(iface_value(a)) + if u == nil { + _, _ = h.Write([]byte{ + 0, + }) + return true + } else { + _, _ = h.Write([]byte{ + 1, + byte(*u), + byte(*u >> 8), + }) + return false + } +} + +func hash16bitslice(h *xxh3.Hasher, a any) bool { + u := *(*[]uint16)(iface_value(a)) + for i := range u { + _, _ = h.Write([]byte{ + byte(u[i]), + byte(u[i] >> 8), + }) + } + return u == nil +} + +func hash32bit(h *xxh3.Hasher, a any) bool { + u := *(*uint32)(iface_value(a)) + _, _ = h.Write([]byte{ + byte(u), + byte(u >> 8), + byte(u >> 16), + byte(u >> 24), + }) + return u == 0 +} + +func hash32bitptr(h *xxh3.Hasher, a any) bool { + u := (*uint32)(iface_value(a)) + if u == nil { + _, _ = h.Write([]byte{ + 0, + }) + return true + } else { + _, _ = h.Write([]byte{ + 1, + byte(*u), + byte(*u >> 8), + byte(*u >> 16), + byte(*u >> 24), + }) + return false + } +} + +func hash32bitslice(h *xxh3.Hasher, a any) bool { + u := *(*[]uint32)(iface_value(a)) + for i := range u { + _, _ = h.Write([]byte{ + byte(u[i]), + byte(u[i] >> 8), + byte(u[i] >> 16), + byte(u[i] >> 24), + }) + } + return u == nil +} + +func hash64bit(h *xxh3.Hasher, a any) bool { + u := *(*uint64)(iface_value(a)) + _, _ = h.Write([]byte{ + byte(u), + byte(u >> 8), + byte(u >> 16), + byte(u >> 24), + byte(u >> 32), + byte(u >> 40), + byte(u >> 48), + byte(u >> 56), + }) + return u == 0 +} + +func hash64bitptr(h *xxh3.Hasher, a any) bool { + u := (*uint64)(iface_value(a)) + if u == nil { + _, _ = h.Write([]byte{ + 0, + }) + return true + } else { + _, _ = h.Write([]byte{ + 1, + byte(*u), + byte(*u >> 8), + byte(*u >> 16), + byte(*u >> 24), + byte(*u >> 32), + byte(*u >> 40), + byte(*u >> 48), + byte(*u >> 56), + }) + return false + } +} + +func hash64bitslice(h *xxh3.Hasher, a any) bool { + u := *(*[]uint64)(iface_value(a)) + for i := range u { + _, _ = h.Write([]byte{ + byte(u[i]), + byte(u[i] >> 8), + byte(u[i] >> 16), + byte(u[i] >> 24), + byte(u[i] >> 32), + byte(u[i] >> 40), + byte(u[i] >> 48), + byte(u[i] >> 56), + }) + } + return u == nil +} + +func hashstring(h *xxh3.Hasher, a any) bool { + s := *(*string)(iface_value(a)) + _, _ = h.WriteString(s) + return s == "" +} + +func hashstringptr(h *xxh3.Hasher, a any) bool { + s := (*string)(iface_value(a)) + if s == nil { + _, _ = h.Write([]byte{ + 0, + }) + return true + } else { + _, _ = h.Write([]byte{ + 1, + }) + _, _ = h.WriteString(*s) + return false + } +} + +func hashstringslice(h *xxh3.Hasher, a any) bool { + s := *(*[]string)(iface_value(a)) + for i := range s { + _, _ = h.WriteString(s[i]) + } + return s == nil +} + +func hashbinarymarshaler(h *xxh3.Hasher, a any) bool { + i := a.(interface{ MarshalBinary() ([]byte, error) }) + b, _ := i.MarshalBinary() + _, _ = h.Write(b) + return b == nil +} + +func hashbytesmethod(h *xxh3.Hasher, a any) bool { + i := a.(interface{ Bytes() []byte }) + b := i.Bytes() + _, _ = h.Write(b) + return b == nil +} + +func hashstringmethod(h *xxh3.Hasher, a any) bool { + i := a.(interface{ String() string }) + s := i.String() + _, _ = h.WriteString(s) + return s == "" +} + +func hashtextmarshaler(h *xxh3.Hasher, a any) bool { + i := a.(interface{ MarshalText() ([]byte, error) }) + b, _ := i.MarshalText() + _, _ = h.Write(b) + return b == nil +} + +func hashjsonmarshaler(h *xxh3.Hasher, a any) bool { + i := a.(interface{ MarshalJSON() ([]byte, error) }) + b, _ := i.MarshalJSON() + _, _ = h.Write(b) + return b == nil +} + +func iface_value(a any) unsafe.Pointer { + type eface struct{ _, v unsafe.Pointer } + return (*eface)(unsafe.Pointer(&a)).v +} diff --git a/vendor/codeberg.org/gruf/go-structr/hasher.go b/vendor/codeberg.org/gruf/go-structr/hasher.go new file mode 100644 index 000000000..77b8a0991 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-structr/hasher.go @@ -0,0 +1,176 @@ +package structr + +import ( + "reflect" + "strings" + + "github.com/zeebo/xxh3" +) + +// Hasher provides hash checksumming for a configured +// index, based on an arbitrary combination of generic +// paramter struct type's fields. This provides hashing +// both by input of the fields separately, or passing +// an instance of the generic paramter struct type. +// +// Supported field types by the hasher include: +// - ~int +// - ~int8 +// - ~int16 +// - ~int32 +// - ~int64 +// - ~float32 +// - ~float64 +// - ~string +// - slices / ptrs of the above +type Hasher[StructType any] struct { + + // fields contains our representation + // of struct fields contained in the + // creation of sums by this hasher. + fields []structfield + + // zero specifies whether zero + // value fields are permitted. + zero bool +} + +// NewHasher returns a new initialized Hasher for the receiving generic +// parameter type, comprising of the given field strings, and whether to +// allow zero values to be incldued within generated hash checksum values. +func NewHasher[T any](fields []string, allowZero bool) Hasher[T] { + var h Hasher[T] + + // Preallocate expected struct field slice. + h.fields = make([]structfield, len(fields)) + + // Get the reflected struct ptr type. + t := reflect.TypeOf((*T)(nil)).Elem() + + for i, fieldName := range fields { + // Split name to account for nesting. + names := strings.Split(fieldName, ".") + + // Look for a usable struct field from type. + sfield, ok := findField(t, names, allowZero) + if !ok { + panicf("failed finding field: %s", fieldName) + } + + // Set parsed struct field. + h.fields[i] = sfield + } + + // Set config flags. + h.zero = allowZero + + return h +} + +// FromParts generates hash checksum (used as index key) from individual key parts. +func (h *Hasher[T]) FromParts(parts ...any) (sum uint64, ok bool) { + hh := getHasher() + sum, ok = h.fromParts(hh, parts...) + putHasher(hh) + return + +} + +func (h *Hasher[T]) fromParts(hh *xxh3.Hasher, parts ...any) (sum uint64, ok bool) { + if len(parts) != len(h.fields) { + // User must provide correct number of parts for key. + panicf("incorrect number key parts: want=%d received=%d", + len(parts), + len(h.fields), + ) + } + + if h.zero { + // Zero values are permitted, + // mangle all values and ignore + // zero value return booleans. + for i, part := range parts { + + // Write mangled part to hasher. + _ = h.fields[i].hasher(hh, part) + } + } else { + // Zero values are NOT permitted. + for i, part := range parts { + + // Write mangled field to hasher. + z := h.fields[i].hasher(hh, part) + + if z { + // The value was zero for + // this type, return early. + return 0, false + } + } + } + + return hh.Sum64(), true +} + +// FromValue generates hash checksum (used as index key) from a value, via reflection. +func (h *Hasher[T]) FromValue(value T) (sum uint64, ok bool) { + rvalue := reflect.ValueOf(value) + hh := getHasher() + sum, ok = h.fromRValue(hh, rvalue) + putHasher(hh) + return +} + +func (h *Hasher[T]) fromRValue(hh *xxh3.Hasher, rvalue reflect.Value) (uint64, bool) { + // Follow any ptrs leading to value. + for rvalue.Kind() == reflect.Pointer { + rvalue = rvalue.Elem() + } + + if h.zero { + // Zero values are permitted, + // mangle all values and ignore + // zero value return booleans. + for i := range h.fields { + + // Get the reflect value's field at idx. + fv := rvalue.FieldByIndex(h.fields[i].index) + fi := fv.Interface() + + // Write mangled field to hasher. + _ = h.fields[i].hasher(hh, fi) + } + } else { + // Zero values are NOT permitted. + for i := range h.fields { + + // Get the reflect value's field at idx. + fv := rvalue.FieldByIndex(h.fields[i].index) + fi := fv.Interface() + + // Write mangled field to hasher. + z := h.fields[i].hasher(hh, fi) + + if z { + // The value was zero for + // this type, return early. + return 0, false + } + } + } + + return hh.Sum64(), true +} + +type structfield struct { + // index is the reflected index + // of this field (this takes into + // account struct nesting). + index []int + + // hasher is the relevant function + // for hashing value of structfield + // into the supplied hashbuf, where + // return value indicates if zero. + hasher func(*xxh3.Hasher, any) bool +} diff --git a/vendor/codeberg.org/gruf/go-structr/index.go b/vendor/codeberg.org/gruf/go-structr/index.go index bacf6142e..4999249f5 100644 --- a/vendor/codeberg.org/gruf/go-structr/index.go +++ b/vendor/codeberg.org/gruf/go-structr/index.go @@ -45,12 +45,12 @@ type Index[StructType any] struct { // string value of contained fields. name string - // struct field key serializer. - keygen KeyGen[StructType] + // struct field key hasher. + hasher Hasher[StructType] // backing in-memory data store of // generated index keys to result lists. - data map[string]*list[*result[StructType]] + data map[uint64]*list[*result[StructType]] // whether to allow // multiple results @@ -59,20 +59,20 @@ type Index[StructType any] struct { } // init initializes this index with the given configuration. -func (i *Index[T]) init(config IndexConfig) { +func (i *Index[T]) init(config IndexConfig, max int) { fields := strings.Split(config.Fields, ",") i.name = config.Fields - i.keygen = NewKeyGen[T](fields, config.AllowZero) + i.hasher = NewHasher[T](fields, config.AllowZero) i.unique = !config.Multiple - i.data = make(map[string]*list[*result[T]]) + i.data = make(map[uint64]*list[*result[T]], max+1) } -// KeyGen returns the key generator associated with this index. -func (i *Index[T]) KeyGen() *KeyGen[T] { - return &i.keygen +// Hasher returns the hash checksummer associated with this index. +func (i *Index[T]) Hasher() *Hasher[T] { + return &i.hasher } -func index_append[T any](c *Cache[T], i *Index[T], key string, res *result[T]) { +func index_append[T any](c *Cache[T], i *Index[T], key uint64, res *result[T]) { // Acquire + setup indexkey. ikey := indexkey_acquire(c) ikey.entry.Value = res @@ -138,7 +138,7 @@ func index_deleteOne[T any](c *Cache[T], i *Index[T], ikey *indexkey[T]) { } } -func index_delete[T any](c *Cache[T], i *Index[T], key string, fn func(*result[T])) { +func index_delete[T any](c *Cache[T], i *Index[T], key uint64, fn func(*result[T])) { if fn == nil { panic("nil fn") } @@ -180,7 +180,7 @@ type indexkey[T any] struct { // key is the generated index key // the related result is indexed // under, in the below index. - key string + key uint64 // index is the index that the // related result is indexed in. @@ -205,7 +205,7 @@ func indexkey_acquire[T any](c *Cache[T]) *indexkey[T] { func indexkey_release[T any](c *Cache[T], ikey *indexkey[T]) { // Reset indexkey. ikey.entry.Value = nil - ikey.key = "" + ikey.key = 0 ikey.index = nil // Release indexkey to memory pool. diff --git a/vendor/codeberg.org/gruf/go-structr/key.go b/vendor/codeberg.org/gruf/go-structr/key.go deleted file mode 100644 index 557a5f033..000000000 --- a/vendor/codeberg.org/gruf/go-structr/key.go +++ /dev/null @@ -1,204 +0,0 @@ -package structr - -import ( - "reflect" - "strings" - - "codeberg.org/gruf/go-byteutil" - "codeberg.org/gruf/go-mangler" -) - -// KeyGen is the underlying index key generator -// used within Index, and therefore Cache itself. -type KeyGen[StructType any] struct { - - // fields contains our representation of - // the struct fields contained in the - // creation of keys by this generator. - fields []structfield - - // zero specifies whether zero - // value fields are permitted. - zero bool -} - -// NewKeyGen returns a new initialized KeyGen for the receiving generic -// parameter type, comprising of the given field strings, and whether to -// allow zero values to be included within generated output strings. -func NewKeyGen[T any](fields []string, allowZero bool) KeyGen[T] { - var kgen KeyGen[T] - - // Preallocate expected struct field slice. - kgen.fields = make([]structfield, len(fields)) - - // Get the reflected struct ptr type. - t := reflect.TypeOf((*T)(nil)).Elem() - - for i, fieldName := range fields { - // Split name to account for nesting. - names := strings.Split(fieldName, ".") - - // Look for a usable struct field from type. - sfield, ok := findField(t, names, allowZero) - if !ok { - panicf("failed finding field: %s", fieldName) - } - - // Set parsed struct field. - kgen.fields[i] = sfield - } - - // Set config flags. - kgen.zero = allowZero - - return kgen -} - -// FromParts generates key string from individual key parts. -func (kgen *KeyGen[T]) FromParts(parts ...any) (key string, ok bool) { - buf := getBuf() - if ok = kgen.AppendFromParts(buf, parts...); ok { - key = string(buf.B) - } - putBuf(buf) - return -} - -// FromValue generates key string from a value, via reflection. -func (kgen *KeyGen[T]) FromValue(value T) (key string, ok bool) { - buf := getBuf() - rvalue := reflect.ValueOf(value) - if ok = kgen.appendFromRValue(buf, rvalue); ok { - key = string(buf.B) - } - putBuf(buf) - return -} - -// AppendFromParts generates key string into provided buffer, from individual key parts. -func (kgen *KeyGen[T]) AppendFromParts(buf *byteutil.Buffer, parts ...any) bool { - if len(parts) != len(kgen.fields) { - // User must provide correct number of parts for key. - panicf("incorrect number key parts: want=%d received=%d", - len(parts), - len(kgen.fields), - ) - } - - if kgen.zero { - // Zero values are permitted, - // mangle all values and ignore - // zero value return booleans. - for i, part := range parts { - - // Mangle this value into buffer. - _ = kgen.fields[i].Mangle(buf, part) - - // Append part separator. - buf.B = append(buf.B, '.') - } - } else { - // Zero values are NOT permitted. - for i, part := range parts { - - // Mangle this value into buffer. - z := kgen.fields[i].Mangle(buf, part) - - if z { - // The value was zero for - // this type, return early. - return false - } - - // Append part separator. - buf.B = append(buf.B, '.') - } - } - - // Drop the last separator. - buf.B = buf.B[:len(buf.B)-1] - - return true -} - -// AppendFromValue generates key string into provided buffer, from a value via reflection. -func (kgen *KeyGen[T]) AppendFromValue(buf *byteutil.Buffer, value T) bool { - return kgen.appendFromRValue(buf, reflect.ValueOf(value)) -} - -// appendFromRValue is the underlying generator function for the exported ___FromValue() functions, -// accepting a reflected input. We do not expose this as the reflected value is EXPECTED to be right. -func (kgen *KeyGen[T]) appendFromRValue(buf *byteutil.Buffer, rvalue reflect.Value) bool { - // Follow any ptrs leading to value. - for rvalue.Kind() == reflect.Pointer { - rvalue = rvalue.Elem() - } - - if kgen.zero { - // Zero values are permitted, - // mangle all values and ignore - // zero value return booleans. - for i := range kgen.fields { - - // Get the reflect value's field at idx. - fv := rvalue.FieldByIndex(kgen.fields[i].index) - fi := fv.Interface() - - // Mangle this value into buffer. - _ = kgen.fields[i].Mangle(buf, fi) - - // Append part separator. - buf.B = append(buf.B, '.') - } - } else { - // Zero values are NOT permitted. - for i := range kgen.fields { - - // Get the reflect value's field at idx. - fv := rvalue.FieldByIndex(kgen.fields[i].index) - fi := fv.Interface() - - // Mangle this value into buffer. - z := kgen.fields[i].Mangle(buf, fi) - - if z { - // The value was zero for - // this type, return early. - return false - } - - // Append part separator. - buf.B = append(buf.B, '.') - } - } - - // Drop the last separator. - buf.B = buf.B[:len(buf.B)-1] - - return true -} - -type structfield struct { - // index is the reflected index - // of this field (this takes into - // account struct nesting). - index []int - - // zero is the possible mangled - // zero value for this field. - zero string - - // mangler is the mangler function for - // serializing values of this field. - mangler mangler.Mangler -} - -// Mangle mangles the given value, using the determined type-appropriate -// field's type. The returned boolean indicates whether this is a zero value. -func (f *structfield) Mangle(buf *byteutil.Buffer, value any) (isZero bool) { - s := len(buf.B) // start pos. - buf.B = f.mangler(buf.B, value) - e := len(buf.B) // end pos. - isZero = (f.zero == string(buf.B[s:e])) - return -} diff --git a/vendor/codeberg.org/gruf/go-structr/util.go b/vendor/codeberg.org/gruf/go-structr/util.go index d8f227baf..01ac06cf1 100644 --- a/vendor/codeberg.org/gruf/go-structr/util.go +++ b/vendor/codeberg.org/gruf/go-structr/util.go @@ -7,8 +7,7 @@ import ( "unicode" "unicode/utf8" - "codeberg.org/gruf/go-byteutil" - "codeberg.org/gruf/go-mangler" + "github.com/zeebo/xxh3" ) // findField will search for a struct field with given set of names, where names is a len > 0 slice of names account for nesting. @@ -68,22 +67,8 @@ func findField(t reflect.Type, names []string, allowZero bool) (sfield structfie t = field.Type } - // Get final type mangler func. - sfield.mangler = mangler.Get(t) - - if allowZero { - var buf []byte - - // Allocate field instance. - v := reflect.New(field.Type) - v = v.Elem() - - // Serialize this zero value into buf. - buf = sfield.mangler(buf, v.Interface()) - - // Set zero value str. - sfield.zero = string(buf) - } + // Get final type hash func. + sfield.hasher = hasher(t) return } @@ -93,26 +78,21 @@ func panicf(format string, args ...any) { panic(fmt.Sprintf(format, args...)) } -// bufpool provides a memory pool of byte -// buffers used when encoding key types. -var bufPool sync.Pool +// hashPool provides a memory pool of xxh3 +// hasher objects used indexing field vals. +var hashPool sync.Pool -// getBuf fetches buffer from memory pool. -func getBuf() *byteutil.Buffer { - v := bufPool.Get() +// gethashbuf fetches hasher from memory pool. +func getHasher() *xxh3.Hasher { + v := hashPool.Get() if v == nil { - buf := new(byteutil.Buffer) - buf.B = make([]byte, 0, 512) - v = buf + v = new(xxh3.Hasher) } - return v.(*byteutil.Buffer) + return v.(*xxh3.Hasher) } -// putBuf replaces buffer in memory pool. -func putBuf(buf *byteutil.Buffer) { - if buf.Cap() > int(^uint16(0)) { - return // drop large bufs - } - buf.Reset() - bufPool.Put(buf) +// putHasher replaces hasher in memory pool. +func putHasher(h *xxh3.Hasher) { + h.Reset() + hashPool.Put(h) } |