mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[chore] bump go structr cache version -> v0.6.0 (#2773)
* update go-structr library -> v0.6.0, add necessary wrapping types + code changes to support these changes * update readme with go-structr package changes * improved wrapping of the SliceCache type * add code comments for the cache wrapper types * remove test.out 😇 --------- Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
This commit is contained in:
612
vendor/codeberg.org/gruf/go-structr/index.go
generated
vendored
612
vendor/codeberg.org/gruf/go-structr/index.go
generated
vendored
@ -6,7 +6,7 @@ import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/zeebo/xxh3"
|
||||
"codeberg.org/gruf/go-byteutil"
|
||||
)
|
||||
|
||||
// IndexConfig defines config variables
|
||||
@ -55,7 +55,12 @@ type IndexConfig struct {
|
||||
// case that you would like to manually provide the used
|
||||
// index via the Cache.___By() series of functions, or
|
||||
// access the underlying index key generator.
|
||||
type Index[StructType any] struct {
|
||||
type Index struct {
|
||||
|
||||
// ptr is a pointer to
|
||||
// the source Cache/Queue
|
||||
// index is attached to.
|
||||
ptr unsafe.Pointer
|
||||
|
||||
// name is the actual name of this
|
||||
// index, which is the unparsed
|
||||
@ -67,11 +72,11 @@ type Index[StructType any] struct {
|
||||
// index_entry{} which also contains the exact
|
||||
// key each result is stored under. the hash map
|
||||
// only keys by the xxh3 hash checksum for speed.
|
||||
data map[Hash]*list //[*index_entry[StructType]]
|
||||
data map[string]*list // [*index_entry]
|
||||
|
||||
// struct fields encompassed by
|
||||
// keys (+ hashes) of this index.
|
||||
fields []structfield
|
||||
fields []struct_field
|
||||
|
||||
// index flags:
|
||||
// - 1 << 0 = unique
|
||||
@ -79,12 +84,315 @@ type Index[StructType any] struct {
|
||||
flags uint8
|
||||
}
|
||||
|
||||
// Key returns the configured fields as key, and hash sum of key.
|
||||
func (i *Index[T]) Key(value T) ([]any, Hash, bool) {
|
||||
h := get_hasher()
|
||||
key, sum, ok := index_key(i, h, value)
|
||||
hash_pool.Put(h)
|
||||
return key, sum, ok
|
||||
// Name returns the receiving Index name.
|
||||
func (i *Index) Name() string {
|
||||
return i.name
|
||||
}
|
||||
|
||||
// Key generates Key{} from given parts for
|
||||
// the type of lookup this Index uses in cache.
|
||||
// NOTE: panics on incorrect no. parts / types given.
|
||||
func (i *Index) Key(parts ...any) Key {
|
||||
buf := new_buffer()
|
||||
key := i.key(buf, parts)
|
||||
free_buffer(buf)
|
||||
return key
|
||||
}
|
||||
|
||||
// Keys generates []Key{} from given (multiple) parts
|
||||
// for the type of lookup this Index uses in the cache.
|
||||
// NOTE: panics on incorrect no. parts / types given.
|
||||
func (i *Index) Keys(parts ...[]any) []Key {
|
||||
keys := make([]Key, 0, len(parts))
|
||||
buf := new_buffer()
|
||||
for _, parts := range parts {
|
||||
key := i.key(buf, parts)
|
||||
if key.Zero() {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
free_buffer(buf)
|
||||
return keys
|
||||
}
|
||||
|
||||
// init will initialize the cache with given type, config and capacity.
|
||||
func (i *Index) init(t reflect.Type, cfg IndexConfig, cap int) {
|
||||
switch {
|
||||
// The only 2 types we support are
|
||||
// structs, and ptrs to a struct.
|
||||
case t.Kind() == reflect.Struct:
|
||||
case t.Kind() == reflect.Pointer &&
|
||||
t.Elem().Kind() == reflect.Struct:
|
||||
t = t.Elem()
|
||||
default:
|
||||
panic("index only support struct{} and *struct{}")
|
||||
}
|
||||
|
||||
// Set name from the raw
|
||||
// struct fields string.
|
||||
i.name = cfg.Fields
|
||||
|
||||
// Set struct flags.
|
||||
if cfg.AllowZero {
|
||||
set_allow_zero(&i.flags)
|
||||
}
|
||||
if !cfg.Multiple {
|
||||
set_is_unique(&i.flags)
|
||||
}
|
||||
|
||||
// Split to get containing struct fields.
|
||||
fields := strings.Split(cfg.Fields, ",")
|
||||
|
||||
// Preallocate expected struct field slice.
|
||||
i.fields = make([]struct_field, len(fields))
|
||||
|
||||
for x, fieldName := range fields {
|
||||
// Split name to account for nesting.
|
||||
names := strings.Split(fieldName, ".")
|
||||
|
||||
// Look for usable struct field.
|
||||
i.fields[x] = find_field(t, names)
|
||||
}
|
||||
|
||||
// Initialize index_entry list store.
|
||||
i.data = make(map[string]*list, cap+1)
|
||||
}
|
||||
|
||||
// get_one will fetch one indexed item under key.
|
||||
func (i *Index) get_one(key Key) *indexed_item {
|
||||
// Get list at hash.
|
||||
l := i.data[key.key]
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract entry from first list elem.
|
||||
entry := (*index_entry)(l.head.data)
|
||||
|
||||
// Check contains expected key.
|
||||
if !entry.key.Equal(key) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return entry.item
|
||||
}
|
||||
|
||||
// get will fetch all indexed items under key, passing each to hook.
|
||||
func (i *Index) get(key Key, hook func(*indexed_item)) {
|
||||
if hook == nil {
|
||||
panic("nil hook")
|
||||
}
|
||||
|
||||
// Get list at hash.
|
||||
l := i.data[key.key]
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Extract entry from first list elem.
|
||||
entry := (*index_entry)(l.head.data)
|
||||
|
||||
// Check contains expected key.
|
||||
if !entry.key.Equal(key) {
|
||||
return
|
||||
}
|
||||
|
||||
// Iterate all entries in list.
|
||||
l.rangefn(func(elem *list_elem) {
|
||||
|
||||
// Extract element entry + item.
|
||||
entry := (*index_entry)(elem.data)
|
||||
item := entry.item
|
||||
|
||||
// Pass to hook.
|
||||
hook(item)
|
||||
})
|
||||
}
|
||||
|
||||
// key uses hasher to generate Key{} from given raw parts.
|
||||
func (i *Index) key(buf *byteutil.Buffer, parts []any) Key {
|
||||
if len(parts) != len(i.fields) {
|
||||
panicf("incorrect number key parts: want=%d received=%d",
|
||||
len(parts),
|
||||
len(i.fields),
|
||||
)
|
||||
}
|
||||
buf.B = buf.B[:0]
|
||||
if !allow_zero(i.flags) {
|
||||
for x := range parts {
|
||||
before := len(buf.B)
|
||||
buf.B = i.fields[x].mangle(buf.B, parts[x])
|
||||
if string(buf.B[before:]) == i.fields[x].zero {
|
||||
return Key{}
|
||||
}
|
||||
buf.B = append(buf.B, '.')
|
||||
}
|
||||
} else {
|
||||
for x := range parts {
|
||||
buf.B = i.fields[x].mangle(buf.B, parts[x])
|
||||
buf.B = append(buf.B, '.')
|
||||
}
|
||||
}
|
||||
return Key{
|
||||
raw: parts,
|
||||
key: string(buf.B),
|
||||
}
|
||||
}
|
||||
|
||||
// append will append the given index entry to appropriate
|
||||
// doubly-linked-list in index hashmap. this handles case
|
||||
// of key collisions and overwriting 'unique' entries.
|
||||
func (i *Index) append(key Key, item *indexed_item) {
|
||||
// Look for existing.
|
||||
l := i.data[key.key]
|
||||
|
||||
if l == nil {
|
||||
|
||||
// Allocate new.
|
||||
l = new_list()
|
||||
i.data[key.key] = l
|
||||
|
||||
} else if is_unique(i.flags) {
|
||||
|
||||
// Remove head.
|
||||
elem := l.head
|
||||
l.remove(elem)
|
||||
|
||||
// Drop index from inner item.
|
||||
e := (*index_entry)(elem.data)
|
||||
e.item.drop_index(e)
|
||||
|
||||
// Free unused entry.
|
||||
free_index_entry(e)
|
||||
}
|
||||
|
||||
// Prepare new index entry.
|
||||
entry := new_index_entry()
|
||||
entry.item = item
|
||||
entry.key = key
|
||||
entry.index = i
|
||||
|
||||
// Add ourselves to item's index tracker.
|
||||
item.indexed = append(item.indexed, entry)
|
||||
|
||||
// Add entry to index list.
|
||||
l.push_front(&entry.elem)
|
||||
}
|
||||
|
||||
// delete will remove all indexed items under key, passing each to hook.
|
||||
func (i *Index) delete(key Key, hook func(*indexed_item)) {
|
||||
if hook == nil {
|
||||
panic("nil hook")
|
||||
}
|
||||
|
||||
// Get list at hash.
|
||||
l := i.data[key.key]
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Extract entry from first list elem.
|
||||
entry := (*index_entry)(l.head.data)
|
||||
|
||||
// Check contains expected key.
|
||||
if !entry.key.Equal(key) {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete data at hash.
|
||||
delete(i.data, key.key)
|
||||
|
||||
// Iterate entries in list.
|
||||
for x := 0; x < l.len; x++ {
|
||||
|
||||
// Pop list head.
|
||||
elem := l.head
|
||||
l.remove(elem)
|
||||
|
||||
// Extract element entry + item.
|
||||
entry := (*index_entry)(elem.data)
|
||||
item := entry.item
|
||||
|
||||
// Drop index from item.
|
||||
item.drop_index(entry)
|
||||
|
||||
// Free now-unused entry.
|
||||
free_index_entry(entry)
|
||||
|
||||
// Pass to hook.
|
||||
hook(item)
|
||||
}
|
||||
|
||||
// Release list.
|
||||
free_list(l)
|
||||
}
|
||||
|
||||
// delete_entry deletes the given index entry.
|
||||
func (i *Index) delete_entry(entry *index_entry) {
|
||||
// Get list at hash sum.
|
||||
l := i.data[entry.key.key]
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove list entry.
|
||||
l.remove(&entry.elem)
|
||||
|
||||
if l.len == 0 {
|
||||
// Remove entry list from map.
|
||||
delete(i.data, entry.key.key)
|
||||
|
||||
// Release list.
|
||||
free_list(l)
|
||||
}
|
||||
|
||||
// Drop this index from item.
|
||||
entry.item.drop_index(entry)
|
||||
}
|
||||
|
||||
// index_entry represents a single entry
|
||||
// in an Index{}, where it will be accessible
|
||||
// by Key{} pointing to a containing list{}.
|
||||
type index_entry struct {
|
||||
|
||||
// list elem that entry is stored
|
||||
// within, under containing index.
|
||||
// elem.data is ptr to index_entry.
|
||||
elem list_elem
|
||||
|
||||
// hash checksum
|
||||
// + raw key data
|
||||
key Key
|
||||
|
||||
// index this is stored in.
|
||||
index *Index
|
||||
|
||||
// underlying indexed item.
|
||||
item *indexed_item
|
||||
}
|
||||
|
||||
var index_entry_pool sync.Pool
|
||||
|
||||
// new_index_entry returns a new prepared index_entry.
|
||||
func new_index_entry() *index_entry {
|
||||
v := index_entry_pool.Get()
|
||||
if v == nil {
|
||||
v = new(index_entry)
|
||||
}
|
||||
entry := v.(*index_entry)
|
||||
ptr := unsafe.Pointer(entry)
|
||||
entry.elem.data = ptr
|
||||
return entry
|
||||
}
|
||||
|
||||
// free_index_entry releases the index_entry.
|
||||
func free_index_entry(entry *index_entry) {
|
||||
entry.elem.data = nil
|
||||
entry.key = Key{}
|
||||
entry.index = nil
|
||||
entry.item = nil
|
||||
index_entry_pool.Put(entry)
|
||||
}
|
||||
|
||||
func is_unique(f uint8) bool {
|
||||
@ -106,287 +414,3 @@ func set_allow_zero(f *uint8) {
|
||||
const mask = uint8(1) << 1
|
||||
(*f) |= mask
|
||||
}
|
||||
|
||||
func init_index[T any](i *Index[T], config IndexConfig, max int) {
|
||||
// Set name from the raw
|
||||
// struct fields string.
|
||||
i.name = config.Fields
|
||||
|
||||
// Set struct flags.
|
||||
if config.AllowZero {
|
||||
set_allow_zero(&i.flags)
|
||||
}
|
||||
if !config.Multiple {
|
||||
set_is_unique(&i.flags)
|
||||
}
|
||||
|
||||
// Split to get the containing struct fields.
|
||||
fields := strings.Split(config.Fields, ",")
|
||||
|
||||
// Preallocate expected struct field slice.
|
||||
i.fields = make([]structfield, len(fields))
|
||||
|
||||
// Get the reflected struct ptr type.
|
||||
t := reflect.TypeOf((*T)(nil)).Elem()
|
||||
|
||||
for x, fieldName := range fields {
|
||||
// Split name to account for nesting.
|
||||
names := strings.Split(fieldName, ".")
|
||||
|
||||
// Look for usable struct field.
|
||||
i.fields[x] = find_field(t, names)
|
||||
}
|
||||
|
||||
// Initialize index_entry list store.
|
||||
i.data = make(map[Hash]*list, max+1)
|
||||
}
|
||||
|
||||
func index_key[T any](i *Index[T], h *xxh3.Hasher, value T) ([]any, Hash, bool) {
|
||||
key := extract_fields(value, i.fields)
|
||||
sum, zero := hash_sum(i.fields, h, key)
|
||||
if zero && !allow_zero(i.flags) {
|
||||
var zero Hash
|
||||
return nil, zero, false
|
||||
}
|
||||
return key, sum, true
|
||||
}
|
||||
|
||||
func index_hash[T any](i *Index[T], h *xxh3.Hasher, key []any) (Hash, bool) {
|
||||
sum, zero := hash_sum(i.fields, h, key)
|
||||
if zero && !allow_zero(i.flags) {
|
||||
var zero Hash
|
||||
return zero, false
|
||||
}
|
||||
return sum, true
|
||||
}
|
||||
|
||||
func index_get[T any](i *Index[T], hash Hash, key []any) *list {
|
||||
l := i.data[hash]
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
entry := (*index_entry)(l.head.data)
|
||||
if !is_equal(entry.key, key) {
|
||||
return l
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func index_append[T any](c *Cache[T], i *Index[T], hash Hash, key []any, res *result) {
|
||||
// Get list at key.
|
||||
l := i.data[hash]
|
||||
|
||||
if l == nil {
|
||||
|
||||
// Allocate new list.
|
||||
l = list_acquire()
|
||||
i.data[hash] = l
|
||||
|
||||
} else if entry := (*index_entry)(l.head.data); //nocollapse
|
||||
!is_equal(entry.key, key) {
|
||||
|
||||
// Collision! Drop all.
|
||||
delete(i.data, hash)
|
||||
|
||||
// Iterate entries in list.
|
||||
for x := 0; x < l.len; x++ {
|
||||
|
||||
// Pop current head.
|
||||
list_remove(l, l.head)
|
||||
|
||||
// Extract result.
|
||||
res := entry.result
|
||||
|
||||
// Drop index entry from res.
|
||||
result_drop_index(res, i)
|
||||
if len(res.indexed) == 0 {
|
||||
|
||||
// Old res now unused,
|
||||
// release to mem pool.
|
||||
result_release(c, res)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
} else if is_unique(i.flags) {
|
||||
|
||||
// Remove current
|
||||
// indexed entry.
|
||||
list_remove(l, l.head)
|
||||
|
||||
// Get ptr to old
|
||||
// entry before we
|
||||
// release to pool.
|
||||
res := entry.result
|
||||
|
||||
// Drop this index's key from
|
||||
// old res now not indexed here.
|
||||
result_drop_index(res, i)
|
||||
if len(res.indexed) == 0 {
|
||||
|
||||
// Old res now unused,
|
||||
// release to mem pool.
|
||||
result_release(c, res)
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire + setup index entry.
|
||||
entry := index_entry_acquire()
|
||||
entry.index = unsafe.Pointer(i)
|
||||
entry.result = res
|
||||
entry.key = key
|
||||
entry.hash = hash
|
||||
|
||||
// Append to result's indexed entries.
|
||||
res.indexed = append(res.indexed, entry)
|
||||
|
||||
// Add index entry to index list.
|
||||
list_push_front(l, &entry.elem)
|
||||
}
|
||||
|
||||
func index_delete[T any](c *Cache[T], i *Index[T], hash Hash, key []any, fn func(*result)) {
|
||||
if fn == nil {
|
||||
panic("nil fn")
|
||||
}
|
||||
|
||||
// Get list at hash.
|
||||
l := i.data[hash]
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
|
||||
entry := (*index_entry)(l.head.data)
|
||||
|
||||
// Check contains expected key for hash.
|
||||
if !is_equal(entry.key, key) {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete data at hash.
|
||||
delete(i.data, hash)
|
||||
|
||||
// Iterate entries in list.
|
||||
for x := 0; x < l.len; x++ {
|
||||
|
||||
// Pop current head.
|
||||
entry := (*index_entry)(l.head.data)
|
||||
list_remove(l, l.head)
|
||||
|
||||
// Extract result.
|
||||
res := entry.result
|
||||
|
||||
// Call hook.
|
||||
fn(res)
|
||||
|
||||
// Drop index entry from res.
|
||||
result_drop_index(res, i)
|
||||
}
|
||||
|
||||
// Release to pool.
|
||||
list_release(l)
|
||||
}
|
||||
|
||||
func index_delete_entry[T any](c *Cache[T], entry *index_entry) {
|
||||
// Get from entry.
|
||||
i := (*Index[T])(entry.index)
|
||||
|
||||
// Get list at hash sum.
|
||||
l := i.data[entry.hash]
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove entry from list.
|
||||
list_remove(l, &entry.elem)
|
||||
if l.len == 0 {
|
||||
|
||||
// Remove list from map.
|
||||
delete(i.data, entry.hash)
|
||||
|
||||
// Release to pool.
|
||||
list_release(l)
|
||||
}
|
||||
|
||||
// Extract result.
|
||||
res := entry.result
|
||||
|
||||
// Drop index entry from res.
|
||||
result_drop_index(res, i)
|
||||
}
|
||||
|
||||
var entry_pool sync.Pool
|
||||
|
||||
type index_entry struct {
|
||||
// elem contains the list element
|
||||
// appended to each per-hash list
|
||||
// within the Index{} type. the
|
||||
// contained value is a self-ref.
|
||||
elem list_elem
|
||||
|
||||
// index is the Index{} this
|
||||
// index_entry{} is stored in.
|
||||
index unsafe.Pointer
|
||||
|
||||
// result is the actual
|
||||
// underlying result stored
|
||||
// within the index. this
|
||||
// also contains a ref to
|
||||
// this *index_entry in order
|
||||
// to track indices each result
|
||||
// is currently stored under.
|
||||
result *result
|
||||
|
||||
// key contains the actual
|
||||
// key this item was stored
|
||||
// under, used for collision
|
||||
// check.
|
||||
key []any
|
||||
|
||||
// hash contains computed
|
||||
// hash checksum of .key.
|
||||
hash Hash
|
||||
}
|
||||
|
||||
func index_entry_acquire() *index_entry {
|
||||
// Acquire from pool.
|
||||
v := entry_pool.Get()
|
||||
if v == nil {
|
||||
v = new(index_entry)
|
||||
}
|
||||
|
||||
// Cast index_entry value.
|
||||
entry := v.(*index_entry)
|
||||
|
||||
// Set index list elem entry on itself.
|
||||
entry.elem.data = unsafe.Pointer(entry)
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
func index_entry_release(entry *index_entry) {
|
||||
var zero Hash
|
||||
|
||||
// Reset index entry.
|
||||
entry.elem.data = nil
|
||||
entry.index = nil
|
||||
entry.result = nil
|
||||
entry.key = nil
|
||||
entry.hash = zero
|
||||
|
||||
// Release to pool.
|
||||
entry_pool.Put(entry)
|
||||
}
|
||||
|
||||
// is_equal returns whether 2 key slices are equal.
|
||||
func is_equal(k1, k2 []any) bool {
|
||||
if len(k1) != len(k2) {
|
||||
return false
|
||||
}
|
||||
for i := range k1 {
|
||||
if k1[i] != k2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
Reference in New Issue
Block a user