mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
update go-structr to v0.9.0 with new Timeline{} cache type (#3903)
This commit is contained in:
4
go.mod
4
go.mod
@ -27,7 +27,7 @@ require (
|
|||||||
codeberg.org/gruf/go-runners v1.6.3
|
codeberg.org/gruf/go-runners v1.6.3
|
||||||
codeberg.org/gruf/go-sched v1.2.4
|
codeberg.org/gruf/go-sched v1.2.4
|
||||||
codeberg.org/gruf/go-storage v0.2.0
|
codeberg.org/gruf/go-storage v0.2.0
|
||||||
codeberg.org/gruf/go-structr v0.8.11
|
codeberg.org/gruf/go-structr v0.9.0
|
||||||
codeberg.org/superseriousbusiness/activity v1.12.0-gts
|
codeberg.org/superseriousbusiness/activity v1.12.0-gts
|
||||||
codeberg.org/superseriousbusiness/exif-terminator v0.10.0
|
codeberg.org/superseriousbusiness/exif-terminator v0.10.0
|
||||||
codeberg.org/superseriousbusiness/httpsig v1.3.0-SSB
|
codeberg.org/superseriousbusiness/httpsig v1.3.0-SSB
|
||||||
@ -95,7 +95,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect
|
codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect
|
||||||
codeberg.org/gruf/go-mangler v1.4.1 // indirect
|
codeberg.org/gruf/go-mangler v1.4.3 // indirect
|
||||||
codeberg.org/gruf/go-maps v1.0.4 // indirect
|
codeberg.org/gruf/go-maps v1.0.4 // indirect
|
||||||
codeberg.org/superseriousbusiness/go-jpeg-image-structure/v2 v2.1.0-SSB // indirect
|
codeberg.org/superseriousbusiness/go-jpeg-image-structure/v2 v2.1.0-SSB // indirect
|
||||||
codeberg.org/superseriousbusiness/go-png-image-structure/v2 v2.1.0-SSB // indirect
|
codeberg.org/superseriousbusiness/go-png-image-structure/v2 v2.1.0-SSB // indirect
|
||||||
|
8
go.sum
generated
8
go.sum
generated
@ -24,8 +24,8 @@ codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f h1:Ss6Z+vygy+jOGhj9
|
|||||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f/go.mod h1:F9pl4h34iuVN7kucKam9fLwsItTc+9mmaKt7pNXRd/4=
|
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f/go.mod h1:F9pl4h34iuVN7kucKam9fLwsItTc+9mmaKt7pNXRd/4=
|
||||||
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4 h1:IXwfoU7f2whT6+JKIKskNl/hBlmWmnF1vZd84Eb3cyA=
|
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4 h1:IXwfoU7f2whT6+JKIKskNl/hBlmWmnF1vZd84Eb3cyA=
|
||||||
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4/go.mod h1:fiO8HE1wjZCephcYmRRsVnNI/i0+mhy44Z5dQalS0rM=
|
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4/go.mod h1:fiO8HE1wjZCephcYmRRsVnNI/i0+mhy44Z5dQalS0rM=
|
||||||
codeberg.org/gruf/go-mangler v1.4.1 h1:Dv58jFfy9On49L11ji6tpADUknwoJA46iaiZvnNXecs=
|
codeberg.org/gruf/go-mangler v1.4.3 h1:mdtcbGDyj0AS9LE/H1imQreICVn6BQiks554jzdAozc=
|
||||||
codeberg.org/gruf/go-mangler v1.4.1/go.mod h1:mDmW8Ia352RvNFaXoP9K60TgcmCZJtX0j6wm3vjAsJE=
|
codeberg.org/gruf/go-mangler v1.4.3/go.mod h1:mDmW8Ia352RvNFaXoP9K60TgcmCZJtX0j6wm3vjAsJE=
|
||||||
codeberg.org/gruf/go-maps v1.0.4 h1:K+Ww4vvR3TZqm5jqrKVirmguZwa3v1VUvmig2SE8uxY=
|
codeberg.org/gruf/go-maps v1.0.4 h1:K+Ww4vvR3TZqm5jqrKVirmguZwa3v1VUvmig2SE8uxY=
|
||||||
codeberg.org/gruf/go-maps v1.0.4/go.mod h1:ASX7osM7kFwt5O8GfGflcFjrwYGD8eIuRLl/oMjhEi8=
|
codeberg.org/gruf/go-maps v1.0.4/go.mod h1:ASX7osM7kFwt5O8GfGflcFjrwYGD8eIuRLl/oMjhEi8=
|
||||||
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 h1:m2/UCRXhjDwAg4vyji6iKCpomKw6P4PmBOUi5DvAMH4=
|
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 h1:m2/UCRXhjDwAg4vyji6iKCpomKw6P4PmBOUi5DvAMH4=
|
||||||
@ -38,8 +38,8 @@ codeberg.org/gruf/go-sched v1.2.4 h1:ddBB9o0D/2oU8NbQ0ldN5aWxogpXPRBATWi58+p++Hw
|
|||||||
codeberg.org/gruf/go-sched v1.2.4/go.mod h1:wad6l+OcYGWMA2TzNLMmLObsrbBDxdJfEy5WvTgBjNk=
|
codeberg.org/gruf/go-sched v1.2.4/go.mod h1:wad6l+OcYGWMA2TzNLMmLObsrbBDxdJfEy5WvTgBjNk=
|
||||||
codeberg.org/gruf/go-storage v0.2.0 h1:mKj3Lx6AavEkuXXtxqPhdq+akW9YwrnP16yQBF7K5ZI=
|
codeberg.org/gruf/go-storage v0.2.0 h1:mKj3Lx6AavEkuXXtxqPhdq+akW9YwrnP16yQBF7K5ZI=
|
||||||
codeberg.org/gruf/go-storage v0.2.0/go.mod h1:o3GzMDE5QNUaRnm/daUzFqvuAaC4utlgXDXYO79sWKU=
|
codeberg.org/gruf/go-storage v0.2.0/go.mod h1:o3GzMDE5QNUaRnm/daUzFqvuAaC4utlgXDXYO79sWKU=
|
||||||
codeberg.org/gruf/go-structr v0.8.11 h1:I3cQCHpK3fQSXWaaUfksAJRN4+efULiuF11Oi/m8c+o=
|
codeberg.org/gruf/go-structr v0.9.0 h1:UYw8igp3I4UBnlsRyDR2AbF3g7NPEP7HBrQs1I15218=
|
||||||
codeberg.org/gruf/go-structr v0.8.11/go.mod h1:zkoXVrAnKosh8VFAsbP/Hhs8FmLBjbVVy5w/Ngm8ApM=
|
codeberg.org/gruf/go-structr v0.9.0/go.mod h1:mUvBvn4q1iM/I+d3Fj1w/gxGUU/Ve9GpiNo6dPmBJnk=
|
||||||
codeberg.org/superseriousbusiness/activity v1.12.0-gts h1:frNGTENLmOIQHKfOw/jj3UVj/GjHBljDx+CFAAK+m6Q=
|
codeberg.org/superseriousbusiness/activity v1.12.0-gts h1:frNGTENLmOIQHKfOw/jj3UVj/GjHBljDx+CFAAK+m6Q=
|
||||||
codeberg.org/superseriousbusiness/activity v1.12.0-gts/go.mod h1:enxU1Lva4OcK6b/NBXscoHSEgEMsKJvdHrQFifQxp4o=
|
codeberg.org/superseriousbusiness/activity v1.12.0-gts/go.mod h1:enxU1Lva4OcK6b/NBXscoHSEgEMsKJvdHrQFifQxp4o=
|
||||||
codeberg.org/superseriousbusiness/exif-terminator v0.10.0 h1:FiLX/AK07tzceS36I+kOP2aEH+aytjPSIlFoYePMEyg=
|
codeberg.org/superseriousbusiness/exif-terminator v0.10.0 h1:FiLX/AK07tzceS36I+kOP2aEH+aytjPSIlFoYePMEyg=
|
||||||
|
48
vendor/codeberg.org/gruf/go-mangler/helpers.go
generated
vendored
48
vendor/codeberg.org/gruf/go-mangler/helpers.go
generated
vendored
@ -1,3 +1,5 @@
|
|||||||
|
//go:build go1.19 || go1.20 || go1.21 || go1.22 || go1.23
|
||||||
|
|
||||||
package mangler
|
package mangler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -35,25 +37,32 @@ func append_uint64(b []byte, u uint64) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type typecontext struct {
|
type typecontext struct {
|
||||||
|
isptr bool
|
||||||
|
direct bool
|
||||||
ntype reflect.Type
|
ntype reflect.Type
|
||||||
rtype reflect.Type
|
rtype reflect.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *typecontext) set_nested(direct bool) {
|
||||||
|
ctx.direct = ctx.direct && direct && !ctx.isptr
|
||||||
|
ctx.ntype = ctx.rtype
|
||||||
|
ctx.rtype = nil
|
||||||
|
ctx.isptr = false
|
||||||
|
}
|
||||||
|
|
||||||
func deref_ptr_mangler(ctx typecontext, mangle Mangler, n uint) Mangler {
|
func deref_ptr_mangler(ctx typecontext, mangle Mangler, n uint) Mangler {
|
||||||
if mangle == nil || n == 0 {
|
if mangle == nil || n == 0 {
|
||||||
panic("bad input")
|
panic("bad input")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-nested value types,
|
// If this is a direct value type, i.e. non-nested primitive,
|
||||||
// i.e. just direct ptrs to
|
// or part of a single-field struct / single element array
|
||||||
// primitives require one
|
// then it can be treated as a direct ptr with 1 less deref.
|
||||||
// less dereference to ptr.
|
if ctx.direct {
|
||||||
if ctx.ntype == nil {
|
|
||||||
n--
|
n--
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(buf []byte, ptr unsafe.Pointer) []byte {
|
return func(buf []byte, ptr unsafe.Pointer) []byte {
|
||||||
|
|
||||||
// Deref n number times.
|
// Deref n number times.
|
||||||
for i := n; i > 0; i-- {
|
for i := n; i > 0; i-- {
|
||||||
|
|
||||||
@ -117,6 +126,15 @@ func iter_array_mangler(ctx typecontext, mangle Mangler) Mangler {
|
|||||||
// no. array elements.
|
// no. array elements.
|
||||||
n := ctx.ntype.Len()
|
n := ctx.ntype.Len()
|
||||||
|
|
||||||
|
// Optimize
|
||||||
|
// easy cases.
|
||||||
|
switch n {
|
||||||
|
case 0:
|
||||||
|
return empty_mangler
|
||||||
|
case 1:
|
||||||
|
return mangle
|
||||||
|
}
|
||||||
|
|
||||||
// memory size of elem.
|
// memory size of elem.
|
||||||
esz := ctx.rtype.Size()
|
esz := ctx.rtype.Size()
|
||||||
|
|
||||||
@ -139,19 +157,27 @@ func iter_array_mangler(ctx typecontext, mangle Mangler) Mangler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func iter_struct_mangler(ctx typecontext, manglers []Mangler) Mangler {
|
func iter_struct_mangler(ctx typecontext, manglers []Mangler) Mangler {
|
||||||
if ctx.rtype == nil || len(manglers) != ctx.rtype.NumField() {
|
if ctx.rtype == nil || len(manglers) != ctx.ntype.NumField() {
|
||||||
panic("bad input")
|
panic("bad input")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optimized easy cases.
|
||||||
|
switch len(manglers) {
|
||||||
|
case 0:
|
||||||
|
return empty_mangler
|
||||||
|
case 1:
|
||||||
|
return manglers[0]
|
||||||
|
}
|
||||||
|
|
||||||
type field struct {
|
type field struct {
|
||||||
mangle Mangler
|
mangle Mangler
|
||||||
offset uintptr
|
offset uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bundle together the fields and manglers.
|
// Bundle together the fields and manglers.
|
||||||
fields := make([]field, ctx.rtype.NumField())
|
fields := make([]field, ctx.ntype.NumField())
|
||||||
for i := range fields {
|
for i := range fields {
|
||||||
rfield := ctx.rtype.FieldByIndex([]int{i})
|
rfield := ctx.ntype.Field(i)
|
||||||
fields[i].offset = rfield.Offset
|
fields[i].offset = rfield.Offset
|
||||||
fields[i].mangle = manglers[i]
|
fields[i].mangle = manglers[i]
|
||||||
if fields[i].mangle == nil {
|
if fields[i].mangle == nil {
|
||||||
@ -178,6 +204,10 @@ func iter_struct_mangler(ctx typecontext, manglers []Mangler) Mangler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func empty_mangler(buf []byte, _ unsafe.Pointer) []byte {
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
// array_at returns ptr to index in array at ptr, given element size.
|
// array_at returns ptr to index in array at ptr, given element size.
|
||||||
func array_at(ptr unsafe.Pointer, esz uintptr, i int) unsafe.Pointer {
|
func array_at(ptr unsafe.Pointer, esz uintptr, i int) unsafe.Pointer {
|
||||||
return unsafe.Pointer(uintptr(ptr) + esz*uintptr(i))
|
return unsafe.Pointer(uintptr(ptr) + esz*uintptr(i))
|
||||||
|
33
vendor/codeberg.org/gruf/go-mangler/load.go
generated
vendored
33
vendor/codeberg.org/gruf/go-mangler/load.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
|||||||
// function will be returned for given value interface{} and reflected type. Else panics.
|
// function will be returned for given value interface{} and reflected type. Else panics.
|
||||||
func loadMangler(t reflect.Type) Mangler {
|
func loadMangler(t reflect.Type) Mangler {
|
||||||
ctx := typecontext{rtype: t}
|
ctx := typecontext{rtype: t}
|
||||||
|
ctx.direct = true
|
||||||
|
|
||||||
// Load mangler fn
|
// Load mangler fn
|
||||||
mng := load(ctx)
|
mng := load(ctx)
|
||||||
@ -103,6 +104,9 @@ func loadReflectPtr(ctx typecontext) Mangler {
|
|||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set ptr type.
|
||||||
|
ctx.isptr = true
|
||||||
|
|
||||||
// Search for elemn type mangler.
|
// Search for elemn type mangler.
|
||||||
if mng := load(ctx); mng != nil {
|
if mng := load(ctx); mng != nil {
|
||||||
return deref_ptr_mangler(ctx, mng, n)
|
return deref_ptr_mangler(ctx, mng, n)
|
||||||
@ -157,11 +161,13 @@ func loadReflectKnownSlice(ctx typecontext) Mangler {
|
|||||||
|
|
||||||
// loadReflectSlice ...
|
// loadReflectSlice ...
|
||||||
func loadReflectSlice(ctx typecontext) Mangler {
|
func loadReflectSlice(ctx typecontext) Mangler {
|
||||||
// Set nesting type.
|
|
||||||
ctx.ntype = ctx.rtype
|
|
||||||
|
|
||||||
// Get nested element type.
|
// Get nested element type.
|
||||||
ctx.rtype = ctx.rtype.Elem()
|
elem := ctx.rtype.Elem()
|
||||||
|
|
||||||
|
// Set this as nested type.
|
||||||
|
ctx.set_nested(false)
|
||||||
|
ctx.rtype = elem
|
||||||
|
|
||||||
// Preferably look for known slice mangler func
|
// Preferably look for known slice mangler func
|
||||||
if mng := loadReflectKnownSlice(ctx); mng != nil {
|
if mng := loadReflectKnownSlice(ctx); mng != nil {
|
||||||
@ -178,11 +184,14 @@ func loadReflectSlice(ctx typecontext) Mangler {
|
|||||||
|
|
||||||
// loadReflectArray ...
|
// loadReflectArray ...
|
||||||
func loadReflectArray(ctx typecontext) Mangler {
|
func loadReflectArray(ctx typecontext) Mangler {
|
||||||
// Set nesting type.
|
|
||||||
ctx.ntype = ctx.rtype
|
|
||||||
|
|
||||||
// Get nested element type.
|
// Get nested element type.
|
||||||
ctx.rtype = ctx.rtype.Elem()
|
elem := ctx.rtype.Elem()
|
||||||
|
|
||||||
|
// Set this as a nested value type.
|
||||||
|
direct := ctx.rtype.Len() <= 1
|
||||||
|
ctx.set_nested(direct)
|
||||||
|
ctx.rtype = elem
|
||||||
|
|
||||||
// Use manglers for nested iteration.
|
// Use manglers for nested iteration.
|
||||||
if mng := load(ctx); mng != nil {
|
if mng := load(ctx); mng != nil {
|
||||||
@ -196,17 +205,15 @@ func loadReflectArray(ctx typecontext) Mangler {
|
|||||||
func loadReflectStruct(ctx typecontext) Mangler {
|
func loadReflectStruct(ctx typecontext) Mangler {
|
||||||
var mngs []Mangler
|
var mngs []Mangler
|
||||||
|
|
||||||
// Set nesting type.
|
// Set this as a nested value type.
|
||||||
ctx.ntype = ctx.rtype
|
direct := ctx.rtype.NumField() <= 1
|
||||||
|
ctx.set_nested(direct)
|
||||||
|
|
||||||
// Gather manglers for all fields.
|
// Gather manglers for all fields.
|
||||||
for i := 0; i < ctx.ntype.NumField(); i++ {
|
for i := 0; i < ctx.ntype.NumField(); i++ {
|
||||||
|
|
||||||
// Field typectx.
|
// Update context with field at index.
|
||||||
ctx := typecontext{
|
ctx.rtype = ctx.ntype.Field(i).Type
|
||||||
ntype: ctx.ntype,
|
|
||||||
rtype: ctx.ntype.Field(i).Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load mangler.
|
// Load mangler.
|
||||||
mng := load(ctx)
|
mng := load(ctx)
|
||||||
|
136
vendor/codeberg.org/gruf/go-structr/README.md
generated
vendored
136
vendor/codeberg.org/gruf/go-structr/README.md
generated
vendored
@ -2,141 +2,9 @@
|
|||||||
|
|
||||||
A library with a series of performant data types with automated struct value indexing. Indexing is supported via arbitrary combinations of fields, and in the case of the cache type, negative results (errors!) are also supported.
|
A library with a series of performant data types with automated struct value indexing. Indexing is supported via arbitrary combinations of fields, and in the case of the cache type, negative results (errors!) are also supported.
|
||||||
|
|
||||||
Under the hood, go-structr maintains a hashmap per index, where each hashmap is a hashmap keyed by serialized input key type. This is handled by the incredibly performant serialization library [go-mangler](https://codeberg.org/gruf/go-mangler), which at this point in time supports just about **any** arbitrary type, so feel free to index by *anything*!
|
Under the hood, go-structr maintains a hashmap per index, where each hashmap is a hashmap keyed by serialized input key type. This is handled by the incredibly performant serialization library [go-mangler](https://codeberg.org/gruf/go-mangler), which at this point in time supports *most* arbitrary types (other than maps, channels, functions), so feel free to index by by almost *anything*!
|
||||||
|
|
||||||
## Cache example
|
See the [docs](https://pkg.go.dev/codeberg.org/gruf/go-structr) for more API information.
|
||||||
|
|
||||||
```golang
|
|
||||||
type Cached struct {
|
|
||||||
Username string
|
|
||||||
Domain string
|
|
||||||
URL string
|
|
||||||
CountryCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
var c structr.Cache[*Cached]
|
|
||||||
|
|
||||||
c.Init(structr.CacheConfig[*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.
|
|
||||||
Copy: func(c *Cached) *Cached {
|
|
||||||
c2 := new(Cached)
|
|
||||||
*c2 = *c
|
|
||||||
return c2
|
|
||||||
},
|
|
||||||
|
|
||||||
// User defined invalidation hook.
|
|
||||||
Invalidate: func(c *Cached) {
|
|
||||||
log.Println("invalidated:", c)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Access and store indexes ahead-of-time for perf.
|
|
||||||
usernameDomainIndex := c.Index("Username,Domain")
|
|
||||||
urlIndex := c.Index("URL")
|
|
||||||
countryCodeIndex := c.Index("CountryCode")
|
|
||||||
|
|
||||||
var url string
|
|
||||||
|
|
||||||
// Generate URL index key.
|
|
||||||
urlKey := urlIndex.Key(url)
|
|
||||||
|
|
||||||
// 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(urlIndex, func() (*Cached, error) {
|
|
||||||
return dbType.SelectByURL(url)
|
|
||||||
}, urlKey)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate country code index key.
|
|
||||||
countryCodeKey := countryCodeIndex.Key(42)
|
|
||||||
|
|
||||||
// Invalidate all cached results stored under
|
|
||||||
// provided index name with give field value(s).
|
|
||||||
c.Invalidate(countryCodeIndex, countryCodeKey)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Queue example
|
|
||||||
|
|
||||||
```golang
|
|
||||||
|
|
||||||
type Queued struct{
|
|
||||||
Username string
|
|
||||||
Domain string
|
|
||||||
URL string
|
|
||||||
CountryCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
var q structr.Queue[*Queued]
|
|
||||||
|
|
||||||
q.Init(structr.QueueConfig[*Cached]{
|
|
||||||
|
|
||||||
// Fields this queued struct type
|
|
||||||
// will be indexed and stored under.
|
|
||||||
Indices: []structr.IndexConfig{
|
|
||||||
{Fields: "Username,Domain", AllowZero: true},
|
|
||||||
{Fields: "URL"},
|
|
||||||
{Fields: "CountryCode", Multiple: true},
|
|
||||||
},
|
|
||||||
|
|
||||||
// User defined pop hook.
|
|
||||||
Pop: func(c *Cached) {
|
|
||||||
log.Println("popped:", c)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Access and store indexes ahead-of-time for perf.
|
|
||||||
usernameDomainIndex := q.Index("Username,Domain")
|
|
||||||
urlIndex := q.Index("URL")
|
|
||||||
countryCodeIndex := q.Index("CountryCode")
|
|
||||||
|
|
||||||
// ...
|
|
||||||
q.PushBack(Queued{
|
|
||||||
Username: "billybob",
|
|
||||||
Domain: "google.com",
|
|
||||||
URL: "https://some.website.here",
|
|
||||||
CountryCode: 42,
|
|
||||||
})
|
|
||||||
|
|
||||||
// ...
|
|
||||||
queued, ok := q.PopFront()
|
|
||||||
|
|
||||||
// Generate country code index key.
|
|
||||||
countryCodeKey := countryCodeIndex.Key(42)
|
|
||||||
|
|
||||||
// ...
|
|
||||||
queuedByCountry := q.Pop(countryCodeIndex, countryCodeKey)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
5
vendor/codeberg.org/gruf/go-structr/TODO.md
generated
vendored
Normal file
5
vendor/codeberg.org/gruf/go-structr/TODO.md
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
## Timeline Todos
|
||||||
|
|
||||||
|
- optimize store() to operate on sorted list
|
||||||
|
|
||||||
|
- finish writing code comments
|
81
vendor/codeberg.org/gruf/go-structr/cache.go
generated
vendored
81
vendor/codeberg.org/gruf/go-structr/cache.go
generated
vendored
@ -17,7 +17,7 @@ func DefaultIgnoreErr(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CacheConfig defines config vars
|
// CacheConfig defines config vars
|
||||||
// for initializing a struct cache.
|
// for initializing a Cache{} type.
|
||||||
type CacheConfig[StructType any] struct {
|
type CacheConfig[StructType any] struct {
|
||||||
|
|
||||||
// IgnoreErr defines which errors to
|
// IgnoreErr defines which errors to
|
||||||
@ -70,14 +70,13 @@ type Cache[StructType any] struct {
|
|||||||
indices []Index
|
indices []Index
|
||||||
|
|
||||||
// max cache size, imposes size
|
// max cache size, imposes size
|
||||||
// limit on the lruList in order
|
// limit on the lru list in order
|
||||||
// to evict old entries.
|
// to evict old entries.
|
||||||
maxSize int
|
maxSize int
|
||||||
|
|
||||||
// protective mutex, guards:
|
// protective mutex, guards:
|
||||||
// - Cache{}.lruList
|
// - Cache{}.*
|
||||||
// - Index{}.data
|
// - Index{}.data
|
||||||
// - Cache{} hook fns
|
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +104,7 @@ func (c *Cache[T]) Init(config CacheConfig[T]) {
|
|||||||
// Safely copy over
|
// Safely copy over
|
||||||
// provided config.
|
// provided config.
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
c.indices = make([]Index, len(config.Indices))
|
c.indices = make([]Index, len(config.Indices))
|
||||||
for i, cfg := range config.Indices {
|
for i, cfg := range config.Indices {
|
||||||
c.indices[i].ptr = unsafe.Pointer(c)
|
c.indices[i].ptr = unsafe.Pointer(c)
|
||||||
@ -114,7 +114,6 @@ func (c *Cache[T]) Init(config CacheConfig[T]) {
|
|||||||
c.copy = config.Copy
|
c.copy = config.Copy
|
||||||
c.invalid = config.Invalidate
|
c.invalid = config.Invalidate
|
||||||
c.maxSize = config.MaxSize
|
c.maxSize = config.MaxSize
|
||||||
c.mutex.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index selects index with given name from cache, else panics.
|
// Index selects index with given name from cache, else panics.
|
||||||
@ -161,6 +160,7 @@ func (c *Cache[T]) Get(index *Index, keys ...Key) []T {
|
|||||||
// Concatenate all *values* from cached items.
|
// Concatenate all *values* from cached items.
|
||||||
index.get(keys[i].key, func(item *indexed_item) {
|
index.get(keys[i].key, func(item *indexed_item) {
|
||||||
if value, ok := item.data.(T); ok {
|
if value, ok := item.data.(T); ok {
|
||||||
|
|
||||||
// Append value COPY.
|
// Append value COPY.
|
||||||
value = c.copy(value)
|
value = c.copy(value)
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
@ -431,6 +431,7 @@ func (c *Cache[T]) Store(value T, store func() error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate invalidates all results stored under index keys.
|
// Invalidate invalidates all results stored under index keys.
|
||||||
|
// Note that if set, this will call the invalidate hook on each.
|
||||||
func (c *Cache[T]) Invalidate(index *Index, keys ...Key) {
|
func (c *Cache[T]) Invalidate(index *Index, keys ...Key) {
|
||||||
if index == nil {
|
if index == nil {
|
||||||
panic("no index given")
|
panic("no index given")
|
||||||
@ -455,7 +456,7 @@ func (c *Cache[T]) Invalidate(index *Index, keys ...Key) {
|
|||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete cached.
|
// Delete item.
|
||||||
c.delete(item)
|
c.delete(item)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -478,6 +479,7 @@ func (c *Cache[T]) Invalidate(index *Index, keys ...Key) {
|
|||||||
// Trim will truncate the cache to ensure it
|
// Trim will truncate the cache to ensure it
|
||||||
// stays within given percentage of MaxSize.
|
// stays within given percentage of MaxSize.
|
||||||
func (c *Cache[T]) Trim(perc float64) {
|
func (c *Cache[T]) Trim(perc float64) {
|
||||||
|
|
||||||
// Acquire lock.
|
// Acquire lock.
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
|
|
||||||
@ -572,7 +574,14 @@ func (c *Cache[T]) store_value(index *Index, key string, value T) {
|
|||||||
if index != nil {
|
if index != nil {
|
||||||
// Append item to index a key
|
// Append item to index a key
|
||||||
// was already generated for.
|
// was already generated for.
|
||||||
index.append(&c.lru, key, item)
|
evicted := index.append(key, item)
|
||||||
|
if evicted != nil {
|
||||||
|
|
||||||
|
// This item is no longer
|
||||||
|
// indexed, remove from list.
|
||||||
|
c.lru.remove(&evicted.elem)
|
||||||
|
free_indexed_item(evicted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get ptr to value data.
|
// Get ptr to value data.
|
||||||
@ -593,9 +602,6 @@ func (c *Cache[T]) store_value(index *Index, key string, value T) {
|
|||||||
|
|
||||||
// Extract fields comprising index key.
|
// Extract fields comprising index key.
|
||||||
parts := extract_fields(ptr, idx.fields)
|
parts := extract_fields(ptr, idx.fields)
|
||||||
if parts == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate index key.
|
// Calculate index key.
|
||||||
key := idx.key(buf, parts)
|
key := idx.key(buf, parts)
|
||||||
@ -604,15 +610,29 @@ func (c *Cache[T]) store_value(index *Index, key string, value T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append item to this index.
|
// Append item to this index.
|
||||||
idx.append(&c.lru, key, item)
|
evicted := idx.append(key, item)
|
||||||
|
if evicted != nil {
|
||||||
|
|
||||||
|
// This item is no longer
|
||||||
|
// indexed, remove from list.
|
||||||
|
c.lru.remove(&evicted.elem)
|
||||||
|
free_indexed_item(evicted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done with buf.
|
||||||
|
free_buffer(buf)
|
||||||
|
|
||||||
|
if len(item.indexed) == 0 {
|
||||||
|
// Item was not stored under
|
||||||
|
// any index. Drop this item.
|
||||||
|
free_indexed_item(item)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add item to main lru list.
|
// Add item to main lru list.
|
||||||
c.lru.push_front(&item.elem)
|
c.lru.push_front(&item.elem)
|
||||||
|
|
||||||
// Done with buf.
|
|
||||||
free_buffer(buf)
|
|
||||||
|
|
||||||
if c.lru.len > c.maxSize {
|
if c.lru.len > c.maxSize {
|
||||||
// Cache has hit max size!
|
// Cache has hit max size!
|
||||||
// Drop the oldest element.
|
// Drop the oldest element.
|
||||||
@ -643,7 +663,14 @@ func (c *Cache[T]) store_error(index *Index, key string, err error) {
|
|||||||
|
|
||||||
// Append item to index a key
|
// Append item to index a key
|
||||||
// was already generated for.
|
// was already generated for.
|
||||||
index.append(&c.lru, key, item)
|
evicted := index.append(key, item)
|
||||||
|
if evicted != nil {
|
||||||
|
|
||||||
|
// This item is no longer
|
||||||
|
// indexed, remove from list.
|
||||||
|
c.lru.remove(&evicted.elem)
|
||||||
|
free_indexed_item(evicted)
|
||||||
|
}
|
||||||
|
|
||||||
// Add item to main lru list.
|
// Add item to main lru list.
|
||||||
c.lru.push_front(&item.elem)
|
c.lru.push_front(&item.elem)
|
||||||
@ -657,19 +684,23 @@ func (c *Cache[T]) store_error(index *Index, key string, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache[T]) delete(item *indexed_item) {
|
func (c *Cache[T]) delete(i *indexed_item) {
|
||||||
for len(item.indexed) != 0 {
|
for len(i.indexed) != 0 {
|
||||||
// Pop last indexed entry from list.
|
// Pop last indexed entry from list.
|
||||||
entry := item.indexed[len(item.indexed)-1]
|
entry := i.indexed[len(i.indexed)-1]
|
||||||
item.indexed = item.indexed[:len(item.indexed)-1]
|
i.indexed[len(i.indexed)-1] = nil
|
||||||
|
i.indexed = i.indexed[:len(i.indexed)-1]
|
||||||
|
|
||||||
// Drop index_entry from index.
|
// Get entry's index.
|
||||||
entry.index.delete_entry(entry)
|
index := entry.index
|
||||||
|
|
||||||
|
// Drop this index_entry.
|
||||||
|
index.delete_entry(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop entry from lru list.
|
// Drop from lru list.
|
||||||
c.lru.remove(&item.elem)
|
c.lru.remove(&i.elem)
|
||||||
|
|
||||||
// Free now-unused item.
|
// Free unused item.
|
||||||
free_indexed_item(item)
|
free_indexed_item(i)
|
||||||
}
|
}
|
||||||
|
18
vendor/codeberg.org/gruf/go-structr/index.go
generated
vendored
18
vendor/codeberg.org/gruf/go-structr/index.go
generated
vendored
@ -168,7 +168,7 @@ func (i *Index) init(t reflect.Type, cfg IndexConfig, cap int) {
|
|||||||
|
|
||||||
// Initialize store for
|
// Initialize store for
|
||||||
// index_entry lists.
|
// index_entry lists.
|
||||||
i.data.init(cap)
|
i.data.Init(cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_one will fetch one indexed item under key.
|
// get_one will fetch one indexed item under key.
|
||||||
@ -248,7 +248,7 @@ func (i *Index) key(buf *byteutil.Buffer, parts []unsafe.Pointer) string {
|
|||||||
// doubly-linked-list in index hashmap. this handles case of
|
// doubly-linked-list in index hashmap. this handles case of
|
||||||
// overwriting "unique" index entries, and removes from given
|
// overwriting "unique" index entries, and removes from given
|
||||||
// outer linked-list in the case that it is no longer indexed.
|
// outer linked-list in the case that it is no longer indexed.
|
||||||
func (i *Index) append(ll *list, key string, item *indexed_item) {
|
func (i *Index) append(key string, item *indexed_item) (evicted *indexed_item) {
|
||||||
// Look for existing.
|
// Look for existing.
|
||||||
l := i.data.Get(key)
|
l := i.data.Get(key)
|
||||||
|
|
||||||
@ -267,17 +267,16 @@ func (i *Index) append(ll *list, key string, item *indexed_item) {
|
|||||||
// Drop index from inner item,
|
// Drop index from inner item,
|
||||||
// catching the evicted item.
|
// catching the evicted item.
|
||||||
e := (*index_entry)(elem.data)
|
e := (*index_entry)(elem.data)
|
||||||
evicted := e.item
|
evicted = e.item
|
||||||
evicted.drop_index(e)
|
evicted.drop_index(e)
|
||||||
|
|
||||||
// Free unused entry.
|
// Free unused entry.
|
||||||
free_index_entry(e)
|
free_index_entry(e)
|
||||||
|
|
||||||
if len(evicted.indexed) == 0 {
|
if len(evicted.indexed) != 0 {
|
||||||
// Evicted item is not indexed,
|
// Evicted is still stored
|
||||||
// remove from outer linked list.
|
// under index, don't return.
|
||||||
ll.remove(&evicted.elem)
|
evicted = nil
|
||||||
free_indexed_item(evicted)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,6 +291,7 @@ func (i *Index) append(ll *list, key string, item *indexed_item) {
|
|||||||
|
|
||||||
// Add entry to index list.
|
// Add entry to index list.
|
||||||
l.push_front(&entry.elem)
|
l.push_front(&entry.elem)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete will remove all indexed items under key, passing each to hook.
|
// delete will remove all indexed items under key, passing each to hook.
|
||||||
@ -403,7 +403,7 @@ func new_index_entry() *index_entry {
|
|||||||
func free_index_entry(entry *index_entry) {
|
func free_index_entry(entry *index_entry) {
|
||||||
if entry.elem.next != nil ||
|
if entry.elem.next != nil ||
|
||||||
entry.elem.prev != nil {
|
entry.elem.prev != nil {
|
||||||
should_not_reach()
|
should_not_reach(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
entry.key = ""
|
entry.key = ""
|
||||||
|
2
vendor/codeberg.org/gruf/go-structr/item.go
generated
vendored
2
vendor/codeberg.org/gruf/go-structr/item.go
generated
vendored
@ -37,7 +37,7 @@ func free_indexed_item(item *indexed_item) {
|
|||||||
if len(item.indexed) > 0 ||
|
if len(item.indexed) > 0 ||
|
||||||
item.elem.next != nil ||
|
item.elem.next != nil ||
|
||||||
item.elem.prev != nil {
|
item.elem.prev != nil {
|
||||||
should_not_reach()
|
should_not_reach(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.data = nil
|
item.data = nil
|
||||||
|
2
vendor/codeberg.org/gruf/go-structr/key.go
generated
vendored
2
vendor/codeberg.org/gruf/go-structr/key.go
generated
vendored
@ -33,7 +33,7 @@ func (k Key) Values() []any {
|
|||||||
|
|
||||||
// Zero indicates a zero value key.
|
// Zero indicates a zero value key.
|
||||||
func (k Key) Zero() bool {
|
func (k Key) Zero() bool {
|
||||||
return (k.raw == nil)
|
return (k.key == "")
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf_pool sync.Pool
|
var buf_pool sync.Pool
|
||||||
|
36
vendor/codeberg.org/gruf/go-structr/list.go
generated
vendored
36
vendor/codeberg.org/gruf/go-structr/list.go
generated
vendored
@ -43,7 +43,7 @@ func free_list(list *list) {
|
|||||||
if list.head != nil ||
|
if list.head != nil ||
|
||||||
list.tail != nil ||
|
list.tail != nil ||
|
||||||
list.len != 0 {
|
list.len != 0 {
|
||||||
should_not_reach()
|
should_not_reach(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
list_pool.Put(list)
|
list_pool.Put(list)
|
||||||
@ -107,6 +107,32 @@ func (l *list) move_back(elem *list_elem) {
|
|||||||
l.push_back(elem)
|
l.push_back(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert will insert given element at given location in list.
|
||||||
|
func (l *list) insert(elem *list_elem, at *list_elem) {
|
||||||
|
if elem == at {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new 'next'.
|
||||||
|
oldNext := at.next
|
||||||
|
at.next = elem
|
||||||
|
|
||||||
|
// Link to 'at'.
|
||||||
|
elem.prev = at
|
||||||
|
|
||||||
|
if oldNext == nil {
|
||||||
|
// Set new tail
|
||||||
|
l.tail = elem
|
||||||
|
} else {
|
||||||
|
// Link to 'prev'.
|
||||||
|
oldNext.prev = elem
|
||||||
|
elem.next = oldNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incr count
|
||||||
|
l.len++
|
||||||
|
}
|
||||||
|
|
||||||
// remove will remove given elem from list.
|
// remove will remove given elem from list.
|
||||||
func (l *list) remove(elem *list_elem) {
|
func (l *list) remove(elem *list_elem) {
|
||||||
// Get linked elems.
|
// Get linked elems.
|
||||||
@ -149,3 +175,11 @@ func (l *list) remove(elem *list_elem) {
|
|||||||
// Decr count
|
// Decr count
|
||||||
l.len--
|
l.len--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func (l *list) range_up(yield func(*list_elem) bool) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (l *list) range_down(yield func(*list_elem) bool) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
6
vendor/codeberg.org/gruf/go-structr/map.go
generated
vendored
6
vendor/codeberg.org/gruf/go-structr/map.go
generated
vendored
@ -5,7 +5,7 @@ type hashmap struct {
|
|||||||
n int
|
n int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *hashmap) init(cap int) {
|
func (m *hashmap) Init(cap int) {
|
||||||
m.m = make(map[string]*list, cap)
|
m.m = make(map[string]*list, cap)
|
||||||
m.n = cap
|
m.n = cap
|
||||||
}
|
}
|
||||||
@ -43,6 +43,10 @@ func (m *hashmap) Compact() {
|
|||||||
// So we apply the inverse/2, once
|
// So we apply the inverse/2, once
|
||||||
// $maxLoad/2 % of hmap is empty we
|
// $maxLoad/2 % of hmap is empty we
|
||||||
// compact the map to drop buckets.
|
// compact the map to drop buckets.
|
||||||
|
//
|
||||||
|
// TODO: this is still a relatively
|
||||||
|
// good approximation, but it has
|
||||||
|
// changed a little with swiss maps.
|
||||||
if 2*16*diff > m.n*13 {
|
if 2*16*diff > m.n*13 {
|
||||||
|
|
||||||
// Create new map only big as required.
|
// Create new map only big as required.
|
||||||
|
180
vendor/codeberg.org/gruf/go-structr/ordered_list.bak
generated
vendored
180
vendor/codeberg.org/gruf/go-structr/ordered_list.bak
generated
vendored
@ -1,180 +0,0 @@
|
|||||||
package structr
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
type Timeline[StructType any, PK comparable] struct {
|
|
||||||
|
|
||||||
// hook functions.
|
|
||||||
pkey func(StructType) PK
|
|
||||||
gte func(PK, PK) bool
|
|
||||||
lte func(PK, PK) bool
|
|
||||||
copy func(StructType) StructType
|
|
||||||
|
|
||||||
// main underlying
|
|
||||||
// ordered item list.
|
|
||||||
list list
|
|
||||||
|
|
||||||
// indices used in storing passed struct
|
|
||||||
// types by user defined sets of fields.
|
|
||||||
indices []Index
|
|
||||||
|
|
||||||
// protective mutex, guards:
|
|
||||||
// - TODO
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timeline[T, PK]) Init(config any) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timeline[T, PK]) Index(name string) *Index {
|
|
||||||
for i := range t.indices {
|
|
||||||
if t.indices[i].name == name {
|
|
||||||
return &t.indices[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("unknown index: " + name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timeline[T, PK]) Insert(values ...T) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timeline[T, PK]) LoadTop(min, max PK, length int, load func(min, max PK, length int) ([]T, error)) ([]T, error) {
|
|
||||||
// Allocate expected no. values.
|
|
||||||
values := make([]T, 0, length)
|
|
||||||
|
|
||||||
// Acquire lock.
|
|
||||||
t.mutex.Lock()
|
|
||||||
|
|
||||||
// Wrap unlock to only do once.
|
|
||||||
unlock := once(t.mutex.Unlock)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
// Check init'd.
|
|
||||||
if t.copy == nil {
|
|
||||||
panic("not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through linked list from top (i.e. head).
|
|
||||||
for next := t.list.head; next != nil; next = next.next {
|
|
||||||
|
|
||||||
// Check if we've gathered
|
|
||||||
// enough values from timeline.
|
|
||||||
if len(values) >= length {
|
|
||||||
return values, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
item := (*indexed_item)(next.data)
|
|
||||||
value := item.data.(T)
|
|
||||||
pkey := t.pkey(value)
|
|
||||||
|
|
||||||
// Check if below min.
|
|
||||||
if t.lte(pkey, min) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update min.
|
|
||||||
min = pkey
|
|
||||||
|
|
||||||
// Check if above max.
|
|
||||||
if t.gte(pkey, max) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append value copy.
|
|
||||||
value = t.copy(value)
|
|
||||||
values = append(values, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timeline[T, PK]) LoadBottom(min, max PK, length int, load func(min, max PK, length int) ([]T, error)) ([]T, error) {
|
|
||||||
// Allocate expected no. values.
|
|
||||||
values := make([]T, 0, length)
|
|
||||||
|
|
||||||
// Acquire lock.
|
|
||||||
t.mutex.Lock()
|
|
||||||
|
|
||||||
// Wrap unlock to only do once.
|
|
||||||
unlock := once(t.mutex.Unlock)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
// Check init'd.
|
|
||||||
if t.copy == nil {
|
|
||||||
panic("not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through linked list from bottom (i.e. tail).
|
|
||||||
for next := t.list.tail; next != nil; next = next.prev {
|
|
||||||
|
|
||||||
// Check if we've gathered
|
|
||||||
// enough values from timeline.
|
|
||||||
if len(values) >= length {
|
|
||||||
return values, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
item := (*indexed_item)(next.data)
|
|
||||||
value := item.data.(T)
|
|
||||||
pkey := t.pkey(value)
|
|
||||||
|
|
||||||
// Check if above max.
|
|
||||||
if t.gte(pkey, max) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update max.
|
|
||||||
max = pkey
|
|
||||||
|
|
||||||
// Check if below min.
|
|
||||||
if t.lte(pkey, min) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append value copy.
|
|
||||||
value = t.copy(value)
|
|
||||||
values = append(values, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done with
|
|
||||||
// the lock.
|
|
||||||
unlock()
|
|
||||||
|
|
||||||
// Attempt to load values up to given length.
|
|
||||||
next, err := load(min, max, length-len(values))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acquire lock.
|
|
||||||
t.mutex.Lock()
|
|
||||||
|
|
||||||
// Store uncached values.
|
|
||||||
for i := range next {
|
|
||||||
t.store_value(
|
|
||||||
nil, "",
|
|
||||||
uncached[i],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done with lock.
|
|
||||||
t.mutex.Unlock()
|
|
||||||
|
|
||||||
// Append uncached to return values.
|
|
||||||
values = append(values, next...)
|
|
||||||
|
|
||||||
return values, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timeline[T, PK]) index(value T) *indexed_item {
|
|
||||||
pk := t.pkey(value)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case t.list.len == 0:
|
|
||||||
|
|
||||||
case pk < t.list.head.data:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timeline[T, PK]) delete(item *indexed_item) {
|
|
||||||
|
|
||||||
}
|
|
37
vendor/codeberg.org/gruf/go-structr/queue.go
generated
vendored
37
vendor/codeberg.org/gruf/go-structr/queue.go
generated
vendored
@ -57,13 +57,13 @@ func (q *Queue[T]) Init(config QueueConfig[T]) {
|
|||||||
// Safely copy over
|
// Safely copy over
|
||||||
// provided config.
|
// provided config.
|
||||||
q.mutex.Lock()
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
q.indices = make([]Index, len(config.Indices))
|
q.indices = make([]Index, len(config.Indices))
|
||||||
for i, cfg := range config.Indices {
|
for i, cfg := range config.Indices {
|
||||||
q.indices[i].ptr = unsafe.Pointer(q)
|
q.indices[i].ptr = unsafe.Pointer(q)
|
||||||
q.indices[i].init(t, cfg, 0)
|
q.indices[i].init(t, cfg, 0)
|
||||||
}
|
}
|
||||||
q.pop = config.Pop
|
q.pop = config.Pop
|
||||||
q.mutex.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index selects index with given name from queue, else panics.
|
// Index selects index with given name from queue, else panics.
|
||||||
@ -133,7 +133,7 @@ func (q *Queue[T]) Pop(index *Index, keys ...Key) []T {
|
|||||||
value := item.data.(T)
|
value := item.data.(T)
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
|
|
||||||
// Delete queued.
|
// Delete item.
|
||||||
q.delete(item)
|
q.delete(item)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -253,7 +253,7 @@ func (q *Queue[T]) pop_n(n int, next func() *list_elem) []T {
|
|||||||
value := item.data.(T)
|
value := item.data.(T)
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
|
|
||||||
// Delete queued.
|
// Delete item.
|
||||||
q.delete(item)
|
q.delete(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,9 +298,6 @@ func (q *Queue[T]) index(value T) *indexed_item {
|
|||||||
|
|
||||||
// Extract fields comprising index key.
|
// Extract fields comprising index key.
|
||||||
parts := extract_fields(ptr, idx.fields)
|
parts := extract_fields(ptr, idx.fields)
|
||||||
if parts == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate index key.
|
// Calculate index key.
|
||||||
key := idx.key(buf, parts)
|
key := idx.key(buf, parts)
|
||||||
@ -309,7 +306,14 @@ func (q *Queue[T]) index(value T) *indexed_item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append item to this index.
|
// Append item to this index.
|
||||||
idx.append(&q.queue, key, item)
|
evicted := idx.append(key, item)
|
||||||
|
if evicted != nil {
|
||||||
|
|
||||||
|
// This item is no longer
|
||||||
|
// indexed, remove from list.
|
||||||
|
q.queue.remove(&evicted.elem)
|
||||||
|
free_indexed_item(evicted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done with buf.
|
// Done with buf.
|
||||||
@ -318,11 +322,12 @@ func (q *Queue[T]) index(value T) *indexed_item {
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue[T]) delete(item *indexed_item) {
|
func (q *Queue[T]) delete(i *indexed_item) {
|
||||||
for len(item.indexed) != 0 {
|
for len(i.indexed) != 0 {
|
||||||
// Pop last indexed entry from list.
|
// Pop last indexed entry from list.
|
||||||
entry := item.indexed[len(item.indexed)-1]
|
entry := i.indexed[len(i.indexed)-1]
|
||||||
item.indexed = item.indexed[:len(item.indexed)-1]
|
i.indexed[len(i.indexed)-1] = nil
|
||||||
|
i.indexed = i.indexed[:len(i.indexed)-1]
|
||||||
|
|
||||||
// Get entry's index.
|
// Get entry's index.
|
||||||
index := entry.index
|
index := entry.index
|
||||||
@ -330,13 +335,13 @@ func (q *Queue[T]) delete(item *indexed_item) {
|
|||||||
// Drop this index_entry.
|
// Drop this index_entry.
|
||||||
index.delete_entry(entry)
|
index.delete_entry(entry)
|
||||||
|
|
||||||
// Check compact map.
|
// Compact index map.
|
||||||
index.data.Compact()
|
index.data.Compact()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop entry from queue list.
|
// Drop from queue list.
|
||||||
q.queue.remove(&item.elem)
|
q.queue.remove(&i.elem)
|
||||||
|
|
||||||
// Free now-unused item.
|
// Free unused item.
|
||||||
free_indexed_item(item)
|
free_indexed_item(i)
|
||||||
}
|
}
|
||||||
|
2
vendor/codeberg.org/gruf/go-structr/queue_ctx.go
generated
vendored
2
vendor/codeberg.org/gruf/go-structr/queue_ctx.go
generated
vendored
@ -133,7 +133,7 @@ func (q *QueueCtx[T]) pop(ctx context.Context, next func() *list_elem) (T, bool)
|
|||||||
// Extract item value.
|
// Extract item value.
|
||||||
value := item.data.(T)
|
value := item.data.(T)
|
||||||
|
|
||||||
// Delete queued.
|
// Delete item.
|
||||||
q.delete(item)
|
q.delete(item)
|
||||||
|
|
||||||
// Get func ptrs.
|
// Get func ptrs.
|
||||||
|
80
vendor/codeberg.org/gruf/go-structr/runtime.go
generated
vendored
80
vendor/codeberg.org/gruf/go-structr/runtime.go
generated
vendored
@ -1,3 +1,5 @@
|
|||||||
|
//go:build go1.22 || go1.23
|
||||||
|
|
||||||
package structr
|
package structr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -36,6 +38,11 @@ type struct_field struct {
|
|||||||
// offsets defines whereabouts in
|
// offsets defines whereabouts in
|
||||||
// memory this field is located.
|
// memory this field is located.
|
||||||
offsets []next_offset
|
offsets []next_offset
|
||||||
|
|
||||||
|
// determines whether field type
|
||||||
|
// is ptr-like in-memory, and so
|
||||||
|
// requires a further dereference.
|
||||||
|
likeptr bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// next_offset defines a next offset location
|
// next_offset defines a next offset location
|
||||||
@ -107,6 +114,9 @@ func find_field(t reflect.Type, names []string) (sfield struct_field) {
|
|||||||
t = field.Type
|
t = field.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if ptr-like in-memory.
|
||||||
|
sfield.likeptr = like_ptr(t)
|
||||||
|
|
||||||
// Set final type.
|
// Set final type.
|
||||||
sfield.rtype = t
|
sfield.rtype = t
|
||||||
|
|
||||||
@ -126,10 +136,14 @@ func find_field(t reflect.Type, names []string) (sfield struct_field) {
|
|||||||
// extract_fields extracts given structfields from the provided value type,
|
// extract_fields extracts given structfields from the provided value type,
|
||||||
// this is done using predetermined struct field memory offset locations.
|
// this is done using predetermined struct field memory offset locations.
|
||||||
func extract_fields(ptr unsafe.Pointer, fields []struct_field) []unsafe.Pointer {
|
func extract_fields(ptr unsafe.Pointer, fields []struct_field) []unsafe.Pointer {
|
||||||
|
|
||||||
// Prepare slice of field value pointers.
|
// Prepare slice of field value pointers.
|
||||||
ptrs := make([]unsafe.Pointer, len(fields))
|
ptrs := make([]unsafe.Pointer, len(fields))
|
||||||
for i, field := range fields {
|
if len(ptrs) != len(fields) {
|
||||||
|
panic("BCE")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, field := range fields {
|
||||||
// loop scope.
|
// loop scope.
|
||||||
fptr := ptr
|
fptr := ptr
|
||||||
|
|
||||||
@ -145,7 +159,7 @@ func extract_fields(ptr unsafe.Pointer, fields []struct_field) []unsafe.Pointer
|
|||||||
offset.offset)
|
offset.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
if like_ptr(field.rtype) && fptr != nil {
|
if field.likeptr && fptr != nil {
|
||||||
// Further dereference value ptr.
|
// Further dereference value ptr.
|
||||||
fptr = *(*unsafe.Pointer)(fptr)
|
fptr = *(*unsafe.Pointer)(fptr)
|
||||||
}
|
}
|
||||||
@ -162,9 +176,63 @@ func extract_fields(ptr unsafe.Pointer, fields []struct_field) []unsafe.Pointer
|
|||||||
return ptrs
|
return ptrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// like_ptr returns whether type's kind is ptr-like.
|
// pkey_field contains pre-prepared type
|
||||||
|
// information about a primary key struct's
|
||||||
|
// field member, including memory offset.
|
||||||
|
type pkey_field struct {
|
||||||
|
rtype reflect.Type
|
||||||
|
|
||||||
|
// offsets defines whereabouts in
|
||||||
|
// memory this field is located.
|
||||||
|
offsets []next_offset
|
||||||
|
|
||||||
|
// determines whether field type
|
||||||
|
// is ptr-like in-memory, and so
|
||||||
|
// requires a further dereference.
|
||||||
|
likeptr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract_pkey will extract a pointer from 'ptr', to
|
||||||
|
// the primary key struct field defined by 'field'.
|
||||||
|
func extract_pkey(ptr unsafe.Pointer, field pkey_field) unsafe.Pointer {
|
||||||
|
for _, offset := range field.offsets {
|
||||||
|
// Dereference any ptrs to offset.
|
||||||
|
ptr = deref(ptr, offset.derefs)
|
||||||
|
if ptr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump forward by offset to next ptr.
|
||||||
|
ptr = unsafe.Pointer(uintptr(ptr) +
|
||||||
|
offset.offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.likeptr && ptr != nil {
|
||||||
|
// Further dereference value ptr.
|
||||||
|
ptr = *(*unsafe.Pointer)(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// like_ptr returns whether type's kind is ptr-like in-memory,
|
||||||
|
// which indicates it may need a final additional dereference.
|
||||||
func like_ptr(t reflect.Type) bool {
|
func like_ptr(t reflect.Type) bool {
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
|
case reflect.Array:
|
||||||
|
switch n := t.Len(); n {
|
||||||
|
case 1:
|
||||||
|
// specifically single elem arrays
|
||||||
|
// follow like_ptr for contained type.
|
||||||
|
return like_ptr(t.Elem())
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
switch n := t.NumField(); n {
|
||||||
|
case 1:
|
||||||
|
// specifically single field structs
|
||||||
|
// follow like_ptr for contained type.
|
||||||
|
return like_ptr(t.Field(0).Type)
|
||||||
|
}
|
||||||
case reflect.Pointer,
|
case reflect.Pointer,
|
||||||
reflect.Map,
|
reflect.Map,
|
||||||
reflect.Chan,
|
reflect.Chan,
|
||||||
@ -201,7 +269,7 @@ func panicf(format string, args ...any) {
|
|||||||
// else it prints callsite info with a BUG report.
|
// else it prints callsite info with a BUG report.
|
||||||
//
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func should_not_reach() {
|
func should_not_reach(exit bool) {
|
||||||
pcs := make([]uintptr, 1)
|
pcs := make([]uintptr, 1)
|
||||||
_ = runtime.Callers(2, pcs)
|
_ = runtime.Callers(2, pcs)
|
||||||
fn := runtime.FuncForPC(pcs[0])
|
fn := runtime.FuncForPC(pcs[0])
|
||||||
@ -212,5 +280,9 @@ func should_not_reach() {
|
|||||||
funcname = funcname[i+1:]
|
funcname = funcname[i+1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if exit {
|
||||||
|
panic("BUG: assertion failed in " + funcname)
|
||||||
|
} else {
|
||||||
os.Stderr.WriteString("BUG: assertion failed in " + funcname + "\n")
|
os.Stderr.WriteString("BUG: assertion failed in " + funcname + "\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
vendor/codeberg.org/gruf/go-structr/test.sh
generated
vendored
5
vendor/codeberg.org/gruf/go-structr/test.sh
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
go test -v -tags=structr_32bit_hash .
|
|
||||||
go test -v -tags=structr_48bit_hash .
|
|
||||||
go test -v -tags=structr_64bit_hash .
|
|
1008
vendor/codeberg.org/gruf/go-structr/timeline.go
generated
vendored
Normal file
1008
vendor/codeberg.org/gruf/go-structr/timeline.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
@ -38,7 +38,7 @@ codeberg.org/gruf/go-kv/format
|
|||||||
# codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f
|
# codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f
|
||||||
## explicit; go 1.21.3
|
## explicit; go 1.21.3
|
||||||
codeberg.org/gruf/go-list
|
codeberg.org/gruf/go-list
|
||||||
# codeberg.org/gruf/go-mangler v1.4.1
|
# codeberg.org/gruf/go-mangler v1.4.3
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
codeberg.org/gruf/go-mangler
|
codeberg.org/gruf/go-mangler
|
||||||
# codeberg.org/gruf/go-maps v1.0.4
|
# codeberg.org/gruf/go-maps v1.0.4
|
||||||
@ -63,8 +63,8 @@ codeberg.org/gruf/go-storage/disk
|
|||||||
codeberg.org/gruf/go-storage/internal
|
codeberg.org/gruf/go-storage/internal
|
||||||
codeberg.org/gruf/go-storage/memory
|
codeberg.org/gruf/go-storage/memory
|
||||||
codeberg.org/gruf/go-storage/s3
|
codeberg.org/gruf/go-storage/s3
|
||||||
# codeberg.org/gruf/go-structr v0.8.11
|
# codeberg.org/gruf/go-structr v0.9.0
|
||||||
## explicit; go 1.21
|
## explicit; go 1.22
|
||||||
codeberg.org/gruf/go-structr
|
codeberg.org/gruf/go-structr
|
||||||
# codeberg.org/superseriousbusiness/activity v1.12.0-gts
|
# codeberg.org/superseriousbusiness/activity v1.12.0-gts
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
|
Reference in New Issue
Block a user