[chore]: Bump codeberg.org/gruf/go-structr from 0.3.0 to 0.5.0

Bumps codeberg.org/gruf/go-structr from 0.3.0 to 0.5.0.

---
updated-dependencies:
- dependency-name: codeberg.org/gruf/go-structr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2024-03-18 06:55:22 +00:00 committed by GitHub
parent 0362d49da0
commit e7226a6361
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 194 additions and 265 deletions

2
go.mod
View file

@ -19,7 +19,7 @@ require (
codeberg.org/gruf/go-runners v1.6.2
codeberg.org/gruf/go-sched v1.2.3
codeberg.org/gruf/go-store/v2 v2.2.4
codeberg.org/gruf/go-structr v0.3.0
codeberg.org/gruf/go-structr v0.5.0
codeberg.org/superseriousbusiness/exif-terminator v0.7.0
github.com/DmitriyVTitov/size v1.5.0
github.com/KimMachineGun/automemlimit v0.5.0

4
go.sum
View file

@ -68,8 +68,8 @@ codeberg.org/gruf/go-sched v1.2.3 h1:H5ViDxxzOBR3uIyGBCf0eH8b1L8wMybOXcdtUUTXZHk
codeberg.org/gruf/go-sched v1.2.3/go.mod h1:vT9uB6KWFIIwnG9vcPY2a0alYNoqdL1mSzRM8I+PK7A=
codeberg.org/gruf/go-store/v2 v2.2.4 h1:8HO1Jh2gg7boQKA3hsDAIXd9zwieu5uXwDXEcTOD9js=
codeberg.org/gruf/go-store/v2 v2.2.4/go.mod h1:zI4VWe5CpXAktYMtaBMrgA5QmO0sQH53LBRvfn1huys=
codeberg.org/gruf/go-structr v0.3.0 h1:qaQz40LVm6dWDDp0pGsHbsbO0+XbqsXZ9N5YgqMmG78=
codeberg.org/gruf/go-structr v0.3.0/go.mod h1:v9TsGsCBNNSVm/qeOuiblAeIS72YyxEIUoRpW8j4xm8=
codeberg.org/gruf/go-structr v0.5.0 h1:DBoT7EuP7Gne2ecO91BZiWqIQlJ84oNbISIwWkhKbkg=
codeberg.org/gruf/go-structr v0.5.0/go.mod h1:v9TsGsCBNNSVm/qeOuiblAeIS72YyxEIUoRpW8j4xm8=
codeberg.org/superseriousbusiness/exif-terminator v0.7.0 h1:Y6VApSXhKqExG0H2hZ2JelRK4xmWdjDQjn13CpEfzko=
codeberg.org/superseriousbusiness/exif-terminator v0.7.0/go.mod h1:gCWKduudUWFzsnixoMzu0FYVdxHWG+AbXnZ50DqxsUE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=

View file

@ -2,7 +2,7 @@
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.
Under the hood, go-structr maintains a hashmap per index, where each hashmap is a hashmap keyed with either 32bit, 48bit or 64bit (default) hash checksum of the inputted raw index keys. The hash checksum size can be controlled by the following Go build-tags: `structr_32bit_hash` `structr_48bit_hash`
Under the hood, go-structr maintains a hashmap per index, where each hashmap is a hashmap keyed with either 32bit or 64bit (default) hash checksum of the inputted raw index keys. The hash checksum size can be controlled by the following Go build-tags: `structr_32bit_hash`
Some example code of how you can use `go-structr` in your application:
```golang

View file

@ -124,20 +124,14 @@ func (c *Cache[T]) Index(name string) *Index[T] {
panic("unknown index: " + name)
}
// GetOne fetches one value from the cache stored under index, using key generated from key parts.
// Note that given number of key parts MUST match expected number and types of the given index name.
func (c *Cache[T]) GetOne(index string, key ...any) (T, bool) {
return c.GetOneBy(c.Index(index), key...)
}
// GetOneBy fetches value from cache stored under index, using precalculated index key.
func (c *Cache[T]) GetOneBy(index *Index[T], key ...any) (T, bool) {
// GetOne fetches value from cache stored under index, using precalculated index key.
func (c *Cache[T]) GetOne(index *Index[T], key Key) (T, bool) {
if index == nil {
panic("no index given")
} else if !is_unique(index.flags) {
panic("cannot get one by non-unique index")
}
values := c.GetBy(index, key)
values := c.Get(index, key)
if len(values) == 0 {
var zero T
return zero, false
@ -145,20 +139,14 @@ func (c *Cache[T]) GetOneBy(index *Index[T], key ...any) (T, bool) {
return values[0], true
}
// Get fetches values from the cache stored under index, using keys generated from given key parts.
// Note that each number of key parts MUST match expected number and types of the given index name.
func (c *Cache[T]) Get(index string, keys ...[]any) []T {
return c.GetBy(c.Index(index), keys...)
}
// GetBy fetches values from the cache stored under index, using precalculated index keys.
func (c *Cache[T]) GetBy(index *Index[T], keys ...[]any) []T {
// Get fetches values from the cache stored under index, using precalculated index keys.
func (c *Cache[T]) Get(index *Index[T], keys ...Key) []T {
if index == nil {
panic("no index given")
}
// Acquire hasher.
h := get_hasher()
// Preallocate expected ret slice.
values := make([]T, 0, len(keys))
// Acquire lock.
c.mutex.Lock()
@ -169,19 +157,10 @@ func (c *Cache[T]) GetBy(index *Index[T], keys ...[]any) []T {
panic("not initialized")
}
// Preallocate expected ret slice.
values := make([]T, 0, len(keys))
for _, key := range keys {
// Generate sum from provided key.
sum, ok := index_hash(index, h, key)
if !ok {
continue
}
for i := range keys {
// Get indexed results list at key.
list := index_get(index, sum, key)
list := index_get(index, keys[i])
if list == nil {
continue
}
@ -213,17 +192,12 @@ func (c *Cache[T]) GetBy(index *Index[T], keys ...[]any) []T {
// Done with lock.
c.mutex.Unlock()
// Done with h.
hash_pool.Put(h)
return values
}
// Put will insert the given values into cache,
// calling any invalidate hook on each value.
func (c *Cache[T]) Put(values ...T) {
var z Hash
// Acquire lock.
c.mutex.Lock()
@ -236,9 +210,9 @@ func (c *Cache[T]) Put(values ...T) {
panic("not initialized")
}
// Store all the passed values.
for _, value := range values {
c.store_value(nil, z, nil, value)
// Store all passed values.
for i := range values {
c.store_value(nil, Key{}, values[i])
}
// Done with lock.
@ -253,16 +227,9 @@ func (c *Cache[T]) Put(values ...T) {
}
}
// LoadOne fetches one result from the cache stored under index, using key generated from key parts.
// In the case that no result is found, the provided load callback will be used to hydrate the cache.
// Note that given number of key parts MUST match expected number and types of the given index name.
func (c *Cache[T]) LoadOne(index string, load func() (T, error), key ...any) (T, error) {
return c.LoadOneBy(c.Index(index), load, key...)
}
// 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 ...any) (T, error) {
func (c *Cache[T]) LoadOne(index *Index[T], key Key, load func() (T, error)) (T, error) {
if index == nil {
panic("no index given")
} else if !is_unique(index.flags) {
@ -281,15 +248,6 @@ func (c *Cache[T]) LoadOneBy(index *Index[T], load func() (T, error), key ...any
err error
)
// Acquire hasher.
h := get_hasher()
// Generate sum from provided key.
sum, _ := index_hash(index, h, key)
// Done with h.
hash_pool.Put(h)
// Acquire lock.
c.mutex.Lock()
@ -303,8 +261,8 @@ func (c *Cache[T]) LoadOneBy(index *Index[T], load func() (T, error), key ...any
panic("not initialized")
}
// Get indexed list at hash key.
list := index_get(index, sum, key)
// Get indexed result list at key.
list := index_get(index, key)
if ok = (list != nil); ok {
entry := (*index_entry)(list.head.data)
@ -350,9 +308,9 @@ func (c *Cache[T]) LoadOneBy(index *Index[T], load func() (T, error), key ...any
// the provided value, so it is
// safe for us to return as-is.
if err != nil {
c.store_error(index, sum, key, err)
c.store_error(index, key, err)
} else {
c.store_value(index, sum, key, val)
c.store_value(index, key, val)
}
// Done with lock.
@ -361,29 +319,16 @@ func (c *Cache[T]) LoadOneBy(index *Index[T], load func() (T, error), key ...any
return val, err
}
// Load fetches values from the cache stored under index, using keys generated from given key parts. The provided get callback is used
// to load groups of values from the cache by the key generated from the key parts provided to the inner callback func, where the returned
// boolean indicates whether any values are currently stored. After the get callback has returned, the cache will then call provided load
// callback to hydrate the cache with any other values. Example usage here is that you may see which values are cached using 'get', and load
// the remaining uncached values using 'load', to minimize database queries. Cached error results are not included or returned by this func.
// Note that given number of key parts MUST match expected number and types of the given index name, in those provided to the get callback.
func (c *Cache[T]) Load(index string, get func(load func(key ...any) bool), load func() ([]T, error)) (values []T, err error) {
return c.LoadBy(c.Index(index), get, load)
}
// LoadBy fetches values from the cache stored under index, using precalculated index key. The provided get callback is used to load
// groups of values from the cache by the key generated from the key parts provided to the inner callback func, where the returned boolea
// indicates whether any values are currently stored. After the get callback has returned, the cache will then call provided load callback
// to hydrate the cache with any other values. Example usage here is that you may see which values are cached using 'get', and load the
// remaining uncached values using 'load', to minimize database queries. Cached error results are not included or returned by this func.
// Note that given number of key parts MUST match expected number and types of the given index name, in those provided to the get callback.
func (c *Cache[T]) LoadBy(index *Index[T], get func(load func(key ...any) bool), load func() ([]T, error)) (values []T, err error) {
// Load fetches values from the cache stored under index, using precalculated index keys. The cache will attempt to
// results with values stored under keys, passing keys with uncached results to the provider load callback to further
// hydrate the cache with missing results. Cached error results not included or returned by this function.
func (c *Cache[T]) Load(index *Index[T], keys []Key, load func([]Key) ([]T, error)) ([]T, error) {
if index == nil {
panic("no index given")
}
// Acquire hasher.
h := get_hasher()
// Preallocate expected ret slice.
values := make([]T, 0, len(keys))
// Acquire lock.
c.mutex.Lock()
@ -394,35 +339,19 @@ func (c *Cache[T]) LoadBy(index *Index[T], get func(load func(key ...any) bool),
panic("not initialized")
}
var unlocked bool
defer func() {
// Deferred unlock to catch
// any user function panics.
if !unlocked {
c.mutex.Unlock()
}
}()
for i := 0; i < len(keys); i++ {
// Pass loader to user func.
get(func(key ...any) bool {
// Generate sum from provided key.
sum, ok := index_hash(index, h, key)
if !ok {
return false
}
// Get indexed results at hash key.
list := index_get(index, sum, key)
// Get indexed results at key.
list := index_get(index, keys[i])
if list == nil {
return false
continue
}
// Value length before
// any below appends.
before := len(values)
// Concatenate all *values* from non-err cached results.
// Concat all *values* from cached results.
list_rangefn(list, func(e *list_elem) {
entry := (*index_entry)(e.data)
res := entry.result
@ -447,18 +376,20 @@ func (c *Cache[T]) LoadBy(index *Index[T], get func(load func(key ...any) bool),
// Only if values changed did
// we actually find anything.
return len(values) != before
})
if len(values) != before {
// We found values at key,
// drop key from the slice.
copy(keys[i:], keys[i+1:])
keys = keys[:len(keys)-1]
}
}
// Done with lock.
c.mutex.Unlock()
unlocked = true
// Done with h.
hash_pool.Put(h)
// Load uncached values.
uncached, err := load()
uncached, err := load(keys)
if err != nil {
return nil, err
}
@ -469,7 +400,7 @@ func (c *Cache[T]) LoadBy(index *Index[T], get func(load func(key ...any) bool),
// Append uncached to return values.
values = append(values, uncached...)
return
return values, nil
}
// Store will call the given store callback, on non-error then
@ -501,51 +432,36 @@ func (c *Cache[T]) Store(value T, store func() error) error {
return nil
}
// Invalidate generates index key from parts and invalidates all stored under it.
func (c *Cache[T]) Invalidate(index string, key ...any) {
c.InvalidateBy(c.Index(index), key...)
}
// InvalidateBy invalidates all results stored under index key.
func (c *Cache[T]) InvalidateBy(index *Index[T], key ...any) {
// Invalidate invalidates all results stored under index keys.
func (c *Cache[T]) Invalidate(index *Index[T], keys ...Key) {
if index == nil {
panic("no index given")
}
// Acquire hasher.
h := get_hasher()
// Generate sum from provided key.
sum, ok := index_hash(index, h, key)
// Done with h.
hash_pool.Put(h)
if !ok {
return
}
var values []T
// Acquire lock.
c.mutex.Lock()
// Preallocate expected ret slice.
values := make([]T, 0, len(keys))
for i := range keys {
// Delete all results under key from index, collecting
// value results and dropping them from all their indices.
index_delete(index, keys[i], func(del *result) {
switch value := del.data.(type) {
case T:
// Append value COPY.
value = c.copy(value)
values = append(values, value)
case error:
}
c.delete(del)
})
}
// Get func ptrs.
invalid := c.invalid
// Delete all results under key from index, collecting
// value results and dropping them from all their indices.
index_delete(c, index, sum, key, func(del *result) {
switch value := del.data.(type) {
case T:
// Append value COPY.
value = c.copy(value)
values = append(values, value)
case error:
}
c.delete(del)
})
// Done with lock.
c.mutex.Unlock()
@ -614,14 +530,14 @@ func (c *Cache[T]) Cap() int {
return m
}
func (c *Cache[T]) store_value(index *Index[T], hash Hash, key []any, value T) {
func (c *Cache[T]) store_value(index *Index[T], key Key, value T) {
// Acquire new result.
res := result_acquire(c)
if index != nil {
// Append result to the provided index
// with precalculated key / its hash.
index_append(c, index, hash, key, res)
index_append(c, index, key, res)
}
// Create COPY of value.
@ -641,14 +557,18 @@ func (c *Cache[T]) store_value(index *Index[T], hash Hash, key []any, value T) {
continue
}
// Get key and hash sum for this index.
key, sum, ok := index_key(idx, h, value)
if !ok {
// Extract struct fields comprising
// key parts configured for this index.
parts := extract_fields(value, idx.fields)
// Calculate key for this index.
key := index_key(idx, h, parts)
if key.Zero() {
continue
}
// Append result to index at key.
index_append(c, idx, sum, key, res)
index_append(c, idx, key, res)
}
// Done with h.
@ -663,7 +583,7 @@ func (c *Cache[T]) store_value(index *Index[T], hash Hash, key []any, value T) {
}
}
func (c *Cache[T]) store_error(index *Index[T], hash Hash, key []any, err error) {
func (c *Cache[T]) store_error(index *Index[T], key Key, err error) {
if index == nil {
// nothing we
// can do here.
@ -676,7 +596,7 @@ func (c *Cache[T]) store_error(index *Index[T], hash Hash, key []any, err error)
// Append result to the provided index
// with precalculated key / its hash.
index_append(c, index, hash, key, res)
index_append(c, index, key, res)
if c.lruList.len > c.maxSize {
// Cache has hit max size!
@ -687,8 +607,6 @@ func (c *Cache[T]) store_error(index *Index[T], hash Hash, key []any, err error)
}
}
// delete will delete the given result from the cache, deleting
// it from all indices it is stored under, and main LRU list.
func (c *Cache[T]) delete(res *result) {
for len(res.indexed) != 0 {
@ -697,7 +615,7 @@ func (c *Cache[T]) delete(res *result) {
res.indexed = res.indexed[:len(res.indexed)-1]
// Drop entry from index.
index_delete_entry(c, entry)
index_delete_entry[T](entry)
// Release to memory pool.
index_entry_release(entry)

View file

@ -8,7 +8,7 @@ package structr
// checksum type. Here; uint32.
type Hash uint32
// uint64ToHash converts uint64 to currently Hash type.
// uint64ToHash converts uint64 to current Hash type.
func uint64ToHash(u uint64) Hash {
return Hash(u >> 32)
}

View file

@ -1,21 +0,0 @@
//go:build structr_48bit_hash
// +build structr_48bit_hash
package structr
// Hash is the current compiler
// flag defined cache key hash
// checksum type. Here; uint48.
type Hash [6]byte
// uint64ToHash converts uint64 to currently Hash type.
func uint64ToHash(u uint64) Hash {
return Hash{
0: byte(u),
1: byte(u >> 8),
2: byte(u >> 16),
3: byte(u >> 24),
4: byte(u >> 32),
5: byte(u >> 40),
}
}

View file

@ -8,7 +8,7 @@ package structr
// checksum type. Here; uint64.
type Hash uint64
// uint64ToHash converts uint64 to currently Hash type.
// uint64ToHash converts uint64 to current Hash type.
func uint64ToHash(u uint64) Hash {
return Hash(u)
}

View file

@ -9,6 +9,43 @@ import (
"github.com/zeebo/xxh3"
)
// Key represents one key to
// lookup (potentially) stored
// entries in an Index.
type Key struct {
raw []any
sum Hash
}
// Equal returns whether keys are equal.
func (k Key) Equal(o Key) bool {
if len(k.raw) != len(o.raw) {
return false
}
for i := range k.raw {
if k.raw[i] != o.raw[i] {
return false
}
}
return true
}
// Sum returns the hashsum of Key.
func (k Key) Sum() Hash {
return k.sum
}
// Value returns the raw slice of
// values that comprise this Key.
func (k Key) Values() []any {
return k.raw
}
// Zero indicates a zero value key.
func (k Key) Zero() bool {
return k.raw == nil
}
// IndexConfig defines config variables
// for initializing a struct index.
type IndexConfig struct {
@ -79,32 +116,36 @@ 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) {
// Name returns the receiving Index name.
func (i *Index[T]) 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[T]) ToKey(parts ...any) Key {
h := get_hasher()
key, sum, ok := index_key(i, h, value)
key := index_key(i, h, parts)
hash_pool.Put(h)
return key, sum, ok
return key
}
func is_unique(f uint8) bool {
const mask = uint8(1) << 0
return f&mask != 0
}
func set_is_unique(f *uint8) {
const mask = uint8(1) << 0
(*f) |= mask
}
func allow_zero(f uint8) bool {
const mask = uint8(1) << 1
return f&mask != 0
}
func set_allow_zero(f *uint8) {
const mask = uint8(1) << 1
(*f) |= mask
// ToKeys 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[T]) ToKeys(parts ...[]any) []Key {
keys := make([]Key, 0, len(parts))
h := get_hasher()
for _, parts := range parts {
key := index_key(i, h, parts)
if key.Zero() {
continue
}
keys = append(keys, key)
}
hash_pool.Put(h)
return keys
}
func init_index[T any](i *Index[T], config IndexConfig, max int) {
@ -141,52 +182,44 @@ func init_index[T any](i *Index[T], config IndexConfig, max int) {
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)
func index_key[T any](i *Index[T], h *xxh3.Hasher, parts []any) Key {
sum, zero := hash_sum(i.fields, h, parts)
if zero && !allow_zero(i.flags) {
var zero Hash
return nil, zero, false
return Key{}
}
return Key{
raw: parts,
sum: sum,
}
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]
func index_get[T any](i *Index[T], key Key) *list {
l := i.data[key.sum]
if l == nil {
return nil
}
entry := (*index_entry)(l.head.data)
if !is_equal(entry.key, key) {
if !entry.key.Equal(key) {
return l
}
return l
}
func index_append[T any](c *Cache[T], i *Index[T], hash Hash, key []any, res *result) {
func index_append[T any](c *Cache[T], i *Index[T], key Key, res *result) {
// Get list at key.
l := i.data[hash]
l := i.data[key.sum]
if l == nil {
// Allocate new list.
l = list_acquire()
i.data[hash] = l
i.data[key.sum] = l
} else if entry := (*index_entry)(l.head.data); //nocollapse
!is_equal(entry.key, key) {
!entry.key.Equal(key) {
// Collision! Drop all.
delete(i.data, hash)
delete(i.data, key.sum)
// Iterate entries in list.
for x := 0; x < l.len; x++ {
@ -236,7 +269,6 @@ func index_append[T any](c *Cache[T], i *Index[T], hash Hash, key []any, res *re
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)
@ -245,26 +277,27 @@ func index_append[T any](c *Cache[T], i *Index[T], hash Hash, key []any, res *re
list_push_front(l, &entry.elem)
}
func index_delete[T any](c *Cache[T], i *Index[T], hash Hash, key []any, fn func(*result)) {
func index_delete[T any](i *Index[T], key Key, fn func(*result)) {
if fn == nil {
panic("nil fn")
}
// Get list at hash.
l := i.data[hash]
l := i.data[key.sum]
if l == nil {
return
}
// Extract entry from first list elem.
entry := (*index_entry)(l.head.data)
// Check contains expected key for hash.
if !is_equal(entry.key, key) {
// Check contains expected key.
if !entry.key.Equal(key) {
return
}
// Delete data at hash.
delete(i.data, hash)
delete(i.data, key.sum)
// Iterate entries in list.
for x := 0; x < l.len; x++ {
@ -287,12 +320,12 @@ func index_delete[T any](c *Cache[T], i *Index[T], hash Hash, key []any, fn func
list_release(l)
}
func index_delete_entry[T any](c *Cache[T], entry *index_entry) {
// Get from entry.
func index_delete_entry[T any](entry *index_entry) {
// Get index from entry.
i := (*Index[T])(entry.index)
// Get list at hash sum.
l := i.data[entry.hash]
l := i.data[entry.key.sum]
if l == nil {
return
}
@ -301,8 +334,8 @@ func index_delete_entry[T any](c *Cache[T], entry *index_entry) {
list_remove(l, &entry.elem)
if l.len == 0 {
// Remove list from map.
delete(i.data, entry.hash)
// Remove entry list from map.
delete(i.data, entry.key.sum)
// Release to pool.
list_release(l)
@ -337,15 +370,10 @@ type index_entry struct {
// 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
// key contains the raw key
// data this was stored under,
// and the hash checksum.
key Key
}
func index_entry_acquire() *index_entry {
@ -365,28 +393,32 @@ func index_entry_acquire() *index_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
entry.key = Key{}
// 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
func is_unique(f uint8) bool {
const mask = uint8(1) << 0
return f&mask != 0
}
func set_is_unique(f *uint8) {
const mask = uint8(1) << 0
(*f) |= mask
}
func allow_zero(f uint8) bool {
const mask = uint8(1) << 1
return f&mask != 0
}
func set_allow_zero(f *uint8) {
const mask = uint8(1) << 1
(*f) |= mask
}

View file

@ -12,10 +12,10 @@ import (
)
type structfield struct {
// _type is the runtime type pointer
// type2 is the runtime type pointer
// underlying the struct field type.
// used for repacking our own erfaces.
_type reflect2.Type
type2 reflect2.Type
// offset is the offset in memory
// of this struct field from the
@ -92,7 +92,7 @@ func find_field(t reflect.Type, names []string) (sfield structfield) {
}
// Get field type as reflect2.
sfield._type = reflect2.Type2(t)
sfield.type2 = reflect2.Type2(t)
// Find hasher for type.
sfield.hasher = hasher(t)
@ -117,7 +117,7 @@ func extract_fields[T any](value T, fields []structfield) []any {
for i := 0; i < len(fields); i++ {
// Manually access field at memory offset and pack eface.
ptr := unsafe.Pointer(uintptr(ptr) + fields[i].offset)
ifaces[i] = fields[i]._type.UnsafeIndirect(ptr)
ifaces[i] = fields[i].type2.UnsafeIndirect(ptr)
}
return ifaces

2
vendor/modules.txt vendored
View file

@ -56,7 +56,7 @@ codeberg.org/gruf/go-sched
## explicit; go 1.19
codeberg.org/gruf/go-store/v2/storage
codeberg.org/gruf/go-store/v2/util
# codeberg.org/gruf/go-structr v0.3.0
# codeberg.org/gruf/go-structr v0.5.0
## explicit; go 1.21
codeberg.org/gruf/go-structr
# codeberg.org/superseriousbusiness/exif-terminator v0.7.0