diff --git a/go.mod b/go.mod index 246d30897..fae1d2cba 100644 --- a/go.mod +++ b/go.mod @@ -14,24 +14,25 @@ require ( code.superseriousbusiness.org/httpsig v1.4.0 code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384 codeberg.org/gruf/go-bitutil v1.1.0 - codeberg.org/gruf/go-bytesize v1.0.3 + codeberg.org/gruf/go-bytesize v1.0.4 codeberg.org/gruf/go-byteutil v1.3.0 codeberg.org/gruf/go-cache/v3 v3.6.1 codeberg.org/gruf/go-caller v0.0.0-20250806133437-db8d0b1f71cf codeberg.org/gruf/go-debug v1.3.0 codeberg.org/gruf/go-errors/v2 v2.3.2 codeberg.org/gruf/go-fastcopy v1.1.3 - codeberg.org/gruf/go-ffmpreg v0.6.8 + codeberg.org/gruf/go-fastpath/v2 v2.0.0 + codeberg.org/gruf/go-ffmpreg v0.6.9 codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf - codeberg.org/gruf/go-kv/v2 v2.0.6 + codeberg.org/gruf/go-kv/v2 v2.0.7 codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 - codeberg.org/gruf/go-mutexes v1.5.2 + codeberg.org/gruf/go-mutexes v1.5.3 codeberg.org/gruf/go-runners v1.6.3 codeberg.org/gruf/go-sched v1.2.4 codeberg.org/gruf/go-split v1.2.0 codeberg.org/gruf/go-storage v0.3.1 - codeberg.org/gruf/go-structr v0.9.7 + codeberg.org/gruf/go-structr v0.9.8 github.com/DmitriyVTitov/size v1.5.0 github.com/KimMachineGun/automemlimit v0.7.4 github.com/SherClockHolmes/webpush-go v1.4.0 @@ -66,7 +67,7 @@ require ( github.com/technologize/otel-go-contrib v1.1.1 github.com/temoto/robotstxt v1.1.2 github.com/tetratelabs/wazero v1.9.0 - github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 + github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e github.com/ulule/limiter/v3 v3.11.2 github.com/uptrace/bun v1.2.15 github.com/uptrace/bun/dialect/pgdialect v1.2.15 @@ -97,9 +98,8 @@ require ( require ( code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 // indirect code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 // indirect - codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect codeberg.org/gruf/go-kv v1.6.5 // indirect - codeberg.org/gruf/go-mangler v1.4.4 // indirect + codeberg.org/gruf/go-mangler/v2 v2.0.6 // indirect codeberg.org/gruf/go-maps v1.0.4 // indirect codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73 // indirect github.com/Masterminds/goutils v1.1.1 // indirect diff --git a/go.sum b/go.sum index c5b4d8c54..83da1a9ff 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384 h1: code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384/go.mod h1:4x61i4C725jkXOpnUPFNfmiEthF6FZc/byADbalp+F8= codeberg.org/gruf/go-bitutil v1.1.0 h1:U1Q+A1mtnPk+npqYrlRBc9ar2C5hYiBd17l1Wrp2Bt8= codeberg.org/gruf/go-bitutil v1.1.0/go.mod h1:rGibFevYTQfYKcPv0Df5KpG8n5xC3AfD4d/UgYeoNy0= -codeberg.org/gruf/go-bytesize v1.0.3 h1:Tz8tCxhPLeyM5VryuBNjUHgKmLj4Bx9RbPaUSA3qg6g= -codeberg.org/gruf/go-bytesize v1.0.3/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs= +codeberg.org/gruf/go-bytesize v1.0.4 h1:LEojK46lUoE748Om7yldx6kLe6jCCuiytz5IZ8vH35g= +codeberg.org/gruf/go-bytesize v1.0.4/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs= codeberg.org/gruf/go-byteutil v1.3.0 h1:nRqJnCcRQ7xbfU6azw7zOzJrSMDIJHBqX6FL9vEMYmU= codeberg.org/gruf/go-byteutil v1.3.0/go.mod h1:chgnZz1LUcfaObaIFglxF5MRYQkJGjQf4WwVz95ccCM= codeberg.org/gruf/go-cache/v3 v3.6.1 h1:sY1XhYeskjZAuYeMm5R0o4Qymru5taNbzmZPSn1oXLE= @@ -28,26 +28,26 @@ codeberg.org/gruf/go-fastcopy v1.1.3 h1:Jo9VTQjI6KYimlw25PPc7YLA3Xm+XMQhaHwKnM7x codeberg.org/gruf/go-fastcopy v1.1.3/go.mod h1:GDDYR0Cnb3U/AIfGM3983V/L+GN+vuwVMvrmVABo21s= codeberg.org/gruf/go-fastpath/v2 v2.0.0 h1:iAS9GZahFhyWEH0KLhFEJR+txx1ZhMXxYzu2q5Qo9c0= codeberg.org/gruf/go-fastpath/v2 v2.0.0/go.mod h1:3pPqu5nZjpbRrOqvLyAK7puS1OfEtQvjd6342Cwz56Q= -codeberg.org/gruf/go-ffmpreg v0.6.8 h1:WkChcYFa8KjLyStoi5CYxldvOCeIG93BVn0tyn7MVo4= -codeberg.org/gruf/go-ffmpreg v0.6.8/go.mod h1:tGqIMh/I2cizqauxxNAN+WGkICI0j5G3xwF1uBkyw1E= +codeberg.org/gruf/go-ffmpreg v0.6.9 h1:EbadyKAekYwwUlKC+4VBZhhN0iPm2uP3T1nPFSWkFb4= +codeberg.org/gruf/go-ffmpreg v0.6.9/go.mod h1:tGqIMh/I2cizqauxxNAN+WGkICI0j5G3xwF1uBkyw1E= codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf h1:84s/ii8N6lYlskZjHH+DG6jyia8w2mXMZlRwFn8Gs3A= codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf/go.mod h1:zZAICsp5rY7+hxnws2V0ePrWxE0Z2Z/KXcN3p/RQCfk= codeberg.org/gruf/go-kv v1.6.5 h1:ttPf0NA8F79pDqBttSudPTVCZmGncumeNIxmeM9ztz0= codeberg.org/gruf/go-kv v1.6.5/go.mod h1:c4PsGqw05bDScvISpK+d31SiDEpBorweCL50hsiK3dc= -codeberg.org/gruf/go-kv/v2 v2.0.6 h1:X4NEiTn6b17kJmfrVd+5tuo3owHUGKTha9GsSkqvNZ8= -codeberg.org/gruf/go-kv/v2 v2.0.6/go.mod h1:uo6rPR14/ll+SDSU3K7DfINNmWD5NJ0EiahPayOguy0= +codeberg.org/gruf/go-kv/v2 v2.0.7 h1:RdTY28NX1N/lc3/ivuasnyqnMdQKwV0es3iqSM/DG44= +codeberg.org/gruf/go-kv/v2 v2.0.7/go.mod h1:uo6rPR14/ll+SDSU3K7DfINNmWD5NJ0EiahPayOguy0= codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f h1:Ss6Z+vygy+jOGhj96d/GwsYYDd22QmIcH74zM7/nQkw= 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/go.mod h1:fiO8HE1wjZCephcYmRRsVnNI/i0+mhy44Z5dQalS0rM= -codeberg.org/gruf/go-mangler v1.4.4 h1:moQl7FSSLLaByS7w5UP7b3Z7r2ex/F4IpvSp+PyRWK4= -codeberg.org/gruf/go-mangler v1.4.4/go.mod h1:mDmW8Ia352RvNFaXoP9K60TgcmCZJtX0j6wm3vjAsJE= +codeberg.org/gruf/go-mangler/v2 v2.0.6 h1:c3cwnI6Mi17EAwGSYGNMN6+9PMzaIj2GLAKx9DKZwoI= +codeberg.org/gruf/go-mangler/v2 v2.0.6/go.mod h1:CXIm7zAWPdNmZVAGM1NRiF/ekJTPE7YTb8kiRxiEFaQ= 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-mempool v0.0.0-20240507125005-cef10d64a760 h1:m2/UCRXhjDwAg4vyji6iKCpomKw6P4PmBOUi5DvAMH4= codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760/go.mod h1:E3RcaCFNq4zXpvaJb8lfpPqdUAmSkP5F1VmMiEUYTEk= -codeberg.org/gruf/go-mutexes v1.5.2 h1:rp2o774ApGUVtOHDksqtBiqIcvniVfgFWSazszDluy0= -codeberg.org/gruf/go-mutexes v1.5.2/go.mod h1:AnhagsMzUISL/nBVwhnHwDwTZOAxMILwCOG8/wKOblg= +codeberg.org/gruf/go-mutexes v1.5.3 h1:RIEy1UuDxKgAiINRMrPxTUWSGW6pFx9DzeJN4WPqra8= +codeberg.org/gruf/go-mutexes v1.5.3/go.mod h1:AnhagsMzUISL/nBVwhnHwDwTZOAxMILwCOG8/wKOblg= codeberg.org/gruf/go-runners v1.6.3 h1:To/AX7eTrWuXrTkA3RA01YTP5zha1VZ68LQ+0D4RY7E= codeberg.org/gruf/go-runners v1.6.3/go.mod h1:oXAaUmG2VxoKttpCqZGv5nQBeSvZSR2BzIk7h1yTRlU= codeberg.org/gruf/go-sched v1.2.4 h1:ddBB9o0D/2oU8NbQ0ldN5aWxogpXPRBATWi58+p++Hw= @@ -56,8 +56,8 @@ codeberg.org/gruf/go-split v1.2.0 h1:PmzL23nVEVHm8VxjsJmv4m4wGQz2bGgQw52dgSSj65c codeberg.org/gruf/go-split v1.2.0/go.mod h1:0rejWJpqvOoFAd7nwm5tIXYKaAqjtFGOXmTqQV+VO38= codeberg.org/gruf/go-storage v0.3.1 h1:g66UIM/xXnEk9ejT+W0T9s/PODBZhXa/8ajzeY/MELI= codeberg.org/gruf/go-storage v0.3.1/go.mod h1:r43n/zi7YGOCl2iSl7AMI27D1zcWS65Bi2+5xDzypeo= -codeberg.org/gruf/go-structr v0.9.7 h1:yQeIxTjYb6reNdgESk915twyjolydYBqat/mlZrP7bg= -codeberg.org/gruf/go-structr v0.9.7/go.mod h1:9k5hYztZ4PsBS+m1v5hUTeFiVUBTLF5VA7d9cd1OEMs= +codeberg.org/gruf/go-structr v0.9.8 h1:IouxZ+HPJznegpBmqsJmTu+7ZURwUbnwby/VO6PMaAs= +codeberg.org/gruf/go-structr v0.9.8/go.mod h1:5dsazOsIeJyV8Dl2DdSXqCDEZUx3e3dc41N6f2mPtgw= codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73 h1:pRaOwIOS1WSZoPCAvE0H1zpv+D4gF37OVppybffqdI8= codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73/go.mod h1:9wkq+dmHjUhB/0ZxDUWAwsWuXwwGyx5N1dDCB9hpWs8= codeberg.org/superseriousbusiness/go-swagger v0.32.3-gts-go1.23-fix h1:k76/Th+bruqU/d+dB0Ru466ctTF2aVjKpisy/471ILE= @@ -463,8 +463,8 @@ github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= -github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= +github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e h1:tD38/4xg4nuQCASJ/JxcvCHNb46w0cdAaJfkzQOO1bA= +github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e/go.mod h1:krvJ5AY/MjdPkTeRgMYbIDhbbbVvnPQPzsIsDJO8xrY= github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= diff --git a/internal/cache/timeline/status.go b/internal/cache/timeline/status.go index 59c339c91..6fe16c4a9 100644 --- a/internal/cache/timeline/status.go +++ b/internal/cache/timeline/status.go @@ -586,6 +586,9 @@ func (t *StatusTimeline) InsertOne(status *gtsmodel.Status, prepared *apimodel.S // status ID, including those that may be a boost of the given status. func (t *StatusTimeline) RemoveByStatusIDs(statusIDs ...string) { keys := make([]structr.Key, len(statusIDs)) + if len(keys) != len(statusIDs) { + panic(gtserror.New("BCE")) + } // Nil check indices outside loops. if t.idx_ID == nil || @@ -595,17 +598,12 @@ func (t *StatusTimeline) RemoveByStatusIDs(statusIDs ...string) { // Convert statusIDs to index keys. for i, id := range statusIDs { - keys[i] = t.idx_ID.Key(id) + keys[i] = structr.MakeKey(id) } // Invalidate all cached entries with IDs. t.cache.Invalidate(t.idx_ID, keys...) - // Convert statusIDs to index keys. - for i, id := range statusIDs { - keys[i] = t.idx_BoostOfID.Key(id) - } - // Invalidate all cached entries as boost of IDs. t.cache.Invalidate(t.idx_BoostOfID, keys...) } @@ -614,6 +612,9 @@ func (t *StatusTimeline) RemoveByStatusIDs(statusIDs ...string) { // account ID, including those that may be boosted by account ID. func (t *StatusTimeline) RemoveByAccountIDs(accountIDs ...string) { keys := make([]structr.Key, len(accountIDs)) + if len(keys) != len(accountIDs) { + panic(gtserror.New("BCE")) + } // Nil check indices outside loops. if t.idx_AccountID == nil || @@ -623,17 +624,12 @@ func (t *StatusTimeline) RemoveByAccountIDs(accountIDs ...string) { // Convert accountIDs to index keys. for i, id := range accountIDs { - keys[i] = t.idx_AccountID.Key(id) + keys[i] = structr.MakeKey(id) } // Invalidate all cached entries as by IDs. t.cache.Invalidate(t.idx_AccountID, keys...) - // Convert accountIDs to index keys. - for i, id := range accountIDs { - keys[i] = t.idx_BoostOfAccountID.Key(id) - } - // Invalidate all cached entries as boosted by IDs. t.cache.Invalidate(t.idx_BoostOfAccountID, keys...) } @@ -642,6 +638,9 @@ func (t *StatusTimeline) RemoveByAccountIDs(accountIDs ...string) { // timeline entries pertaining to status ID, including boosts of given status. func (t *StatusTimeline) UnprepareByStatusIDs(statusIDs ...string) { keys := make([]structr.Key, len(statusIDs)) + if len(keys) != len(statusIDs) { + panic(gtserror.New("BCE")) + } // Nil check indices outside loops. if t.idx_ID == nil || @@ -651,7 +650,7 @@ func (t *StatusTimeline) UnprepareByStatusIDs(statusIDs ...string) { // Convert statusIDs to index keys. for i, id := range statusIDs { - keys[i] = t.idx_ID.Key(id) + keys[i] = structr.MakeKey(id) } // Unprepare all statuses stored under StatusMeta.ID. @@ -659,11 +658,6 @@ func (t *StatusTimeline) UnprepareByStatusIDs(statusIDs ...string) { meta.prepared = nil } - // Convert statusIDs to index keys. - for i, id := range statusIDs { - keys[i] = t.idx_BoostOfID.Key(id) - } - // Unprepare all statuses stored under StatusMeta.BoostOfID. for meta := range t.cache.RangeKeysUnsafe(t.idx_BoostOfID, keys...) { meta.prepared = nil @@ -683,7 +677,7 @@ func (t *StatusTimeline) UnprepareByAccountIDs(accountIDs ...string) { // Convert accountIDs to index keys. for i, id := range accountIDs { - keys[i] = t.idx_AccountID.Key(id) + keys[i] = structr.MakeKey(id) } // Unprepare all statuses stored under StatusMeta.AccountID. @@ -691,11 +685,6 @@ func (t *StatusTimeline) UnprepareByAccountIDs(accountIDs ...string) { meta.prepared = nil } - // Convert accountIDs to index keys. - for i, id := range accountIDs { - keys[i] = t.idx_BoostOfAccountID.Key(id) - } - // Unprepare all statuses stored under StatusMeta.BoostOfAccountID. for meta := range t.cache.RangeKeysUnsafe(t.idx_BoostOfAccountID, keys...) { meta.prepared = nil diff --git a/internal/cache/wrappers.go b/internal/cache/wrappers.go index 34d7cb8db..6e3175b69 100644 --- a/internal/cache/wrappers.go +++ b/internal/cache/wrappers.go @@ -20,6 +20,7 @@ package cache import ( "slices" + "code.superseriousbusiness.org/gotosocial/internal/gtserror" "codeberg.org/gruf/go-cache/v3/simple" "codeberg.org/gruf/go-structr" ) @@ -86,22 +87,19 @@ func (c *StructCache[T]) Init(config structr.CacheConfig[T]) { // GetOne calls structr.Cache{}.GetOne(), using a cached structr.Index{} by 'index' name. // Note: this also handles conversion of the untyped (any) keys to structr.Key{} via structr.Index{}. func (c *StructCache[T]) GetOne(index string, key ...any) (T, bool) { - i := c.index[index] - return c.Cache.GetOne(i, i.Key(key...)) + return c.Cache.GetOne(c.index[index], structr.MakeKey(key...)) } // Get calls structr.Cache{}.Get(), using a cached structr.Index{} by 'index' name. // Note: this also handles conversion of the untyped (any) keys to structr.Key{} via structr.Index{}. func (c *StructCache[T]) Get(index string, keys ...[]any) []T { - i := c.index[index] - return c.Cache.Get(i, i.Keys(keys...)...) + return c.Cache.Get(c.index[index], structr.MakeKeys(keys...)...) } // LoadOne calls structr.Cache{}.LoadOne(), using a cached structr.Index{} by 'index' name. // Note: this also handles conversion of the untyped (any) keys to structr.Key{} via structr.Index{}. func (c *StructCache[T]) LoadOne(index string, load func() (T, error), key ...any) (T, error) { - i := c.index[index] - return c.Cache.LoadOne(i, i.Key(key...), load) + return c.Cache.LoadOne(c.index[index], structr.MakeKey(key...), load) } // LoadIDs calls structr.Cache{}.Load(), using a cached structr.Index{} by 'index' name. Note: this also handles @@ -109,28 +107,26 @@ func (c *StructCache[T]) LoadOne(index string, load func() (T, error), key ...an // // If you need to load multiple cache keys other than by ID strings, please create another convenience wrapper. func (c *StructCache[T]) LoadIDs(index string, ids []string, load func([]string) ([]T, error)) ([]T, error) { - i := c.index[index] - if i == nil { - // we only perform this check here as - // we're going to use the index before - // passing it to cache in main .Load(). - panic("missing index for cache type") - } // Generate cache keys for ID types. keys := make([]structr.Key, len(ids)) + if len(keys) != len(ids) { + panic(gtserror.New("BCE")) + } for x, id := range ids { - keys[x] = i.Key(id) + keys[x] = structr.MakeKey(id) } - // Pass loader callback with wrapper onto main cache load function. - return c.Cache.Load(i, keys, func(uncached []structr.Key) ([]T, error) { - uncachedIDs := make([]string, len(uncached)) - for i := range uncached { - uncachedIDs[i] = uncached[i].Values()[0].(string) - } - return load(uncachedIDs) - }) + // Pass loader callback with + // wrapper onto main cache load function. + return c.Cache.Load(c.index[index], keys, + func(uncached []structr.Key) ([]T, error) { + uncachedIDs := make([]string, len(uncached)) + for i := range uncached { + uncachedIDs[i] = uncached[i].Values()[0].(string) + } + return load(uncachedIDs) + }) } // LoadIDs2Part works as LoadIDs, except using a two-part key, @@ -147,8 +143,11 @@ func (c *StructCache[T]) LoadIDs2Part(index string, id1 string, id2s []string, l // Generate cache keys for two-part IDs. keys := make([]structr.Key, len(id2s)) + if len(keys) != len(id2s) { + panic(gtserror.New("BCE")) + } for x, id2 := range id2s { - keys[x] = i.Key(id1, id2) + keys[x] = structr.MakeKey(id1, id2) } // Pass loader callback with wrapper onto main cache load function. @@ -164,8 +163,7 @@ func (c *StructCache[T]) LoadIDs2Part(index string, id1 string, id2s []string, l // Invalidate calls structr.Cache{}.Invalidate(), using a cached structr.Index{} by 'index' name. // Note: this also handles conversion of the untyped (any) keys to structr.Key{} via structr.Index{}. func (c *StructCache[T]) Invalidate(index string, key ...any) { - i := c.index[index] - c.Cache.Invalidate(i, i.Key(key...)) + c.Cache.Invalidate(c.index[index], structr.MakeKey(key...)) } // InvalidateIDs calls structr.Cache{}.Invalidate(), using a cached structr.Index{} by 'index' name. Note: this also @@ -173,20 +171,17 @@ func (c *StructCache[T]) Invalidate(index string, key ...any) { // // If you need to invalidate multiple cache keys other than by ID strings, please create another convenience wrapper. func (c *StructCache[T]) InvalidateIDs(index string, ids []string) { - i := c.index[index] - if i == nil { - // we only perform this check here as - // we're going to use the index before - // passing it to cache in main .Load(). - panic("missing index for cache type") - } // Generate cache keys for ID types. keys := make([]structr.Key, len(ids)) + if len(keys) != len(ids) { + panic(gtserror.New("BCE")) + } for x, id := range ids { - keys[x] = i.Key(id) + keys[x] = structr.MakeKey(id) } // Pass to main invalidate func. - c.Cache.Invalidate(i, keys...) + c.Cache.Invalidate(c.index[index], + keys...) } diff --git a/internal/queue/struct.go b/internal/queue/struct.go index 27139daec..6296eb4c2 100644 --- a/internal/queue/struct.go +++ b/internal/queue/struct.go @@ -62,8 +62,7 @@ func (q *StructQueue[T]) Push(values ...T) { // Delete pops (and drops!) all queued entries under index with key. func (q *StructQueue[T]) Delete(index string, key ...any) { - i := q.index[index] - _ = q.queue.Pop(i, i.Key(key...)) + _ = q.queue.Pop(q.index[index], structr.MakeKey(key...)) } // Len: see structr.Queue{}.Len(). diff --git a/vendor/codeberg.org/gruf/go-bytesize/bytesize.go b/vendor/codeberg.org/gruf/go-bytesize/bytesize.go index d24ff5f0f..25283d8b6 100644 --- a/vendor/codeberg.org/gruf/go-bytesize/bytesize.go +++ b/vendor/codeberg.org/gruf/go-bytesize/bytesize.go @@ -94,8 +94,10 @@ var ( // methods for byte sizes in both IEC and SI units. type Size uint64 -// ParseSize will parse a valid Size from given string. Both IEC and SI units are supported. +// ParseSize will parse a valid Size from given +// string. Both IEC and SI units are supported. func ParseSize(s string) (Size, error) { + // Parse units from string unit, l, err := parseUnit(s) if err != nil { @@ -121,10 +123,15 @@ func (sz *Size) Set(in string) error { return nil } +// AppendText implements encoding.TextAppender{}. +func (sz Size) AppendText(b []byte) ([]byte, error) { + return sz.AppendFormatIEC(b), nil +} + // MarshalText implements encoding.TextMarshaler{}. -func (sz *Size) MarshalText() ([]byte, error) { +func (sz Size) MarshalText() ([]byte, error) { const maxLen = 7 // max IEC string length - return sz.AppendFormatIEC(make([]byte, 0, maxLen)), nil + return sz.AppendText(make([]byte, 0, maxLen)) } // UnmarshalText implements encoding.TextUnmarshaler{}. @@ -143,8 +150,12 @@ func (sz Size) AppendFormatSI(dst []byte) []byte { dst = itoa(dst, uint64(sz)) dst = append(dst, 'B') return dst - } // above is fast-path, .appendFormat() is outlined - return sz.appendFormat(dst, 1000, &sipows, "B") + } + f, u := sztof(sz, 1000, sipows) + dst = ftoa(dst, f) + dst = append(dst, u) + dst = append(dst, 'B') + return dst } // AppendFormatIEC will append IEC formatted size to 'dst'. @@ -153,35 +164,11 @@ func (sz Size) AppendFormatIEC(dst []byte) []byte { dst = itoa(dst, uint64(sz)) dst = append(dst, 'B') return dst - } // above is fast-path, .appendFormat() is outlined - return sz.appendFormat(dst, 1024, &iecpows, "iB") -} - -// appendFormat will append formatted Size to 'dst', depending on base, powers table and single unit suffix. -func (sz Size) appendFormat(dst []byte, base uint64, pows *[6]float64, sunit string) []byte { - const ( - // min "small" unit threshold - min = 0.75 - - // binary unit chars. - units = `kMGTPE` - ) - - // Larger number: get value of - // i / unit size. We have a 'min' - // threshold after which we prefer - // using the unit 1 down - n := bits.Len64(uint64(sz)) / 10 - f := float64(sz) / pows[n-1] - if f < min { - f *= float64(base) - n-- } - - // Append formatted float with units + f, u := sztof(sz, 1024, iecpows) dst = ftoa(dst, f) - dst = append(dst, units[n-1]) - dst = append(dst, sunit...) + dst = append(dst, u) + dst = append(dst, 'i', 'B') return dst } @@ -261,6 +248,31 @@ func parseUnit(s string) (float64, int, error) { return sivals[c], l, nil } +// sztof divides a Size with base and power units to a float value with power. +func sztof(sz Size, base float64, pows [6]float64) (float64, byte) { + const ( + // min "small" + // unit threshold. + min = 0.75 + + // binary unit chars. + units = `kMGTPE` + ) + + // Larger number: get value of + // i / unit size. We have a 'min' + // threshold after which we prefer + // using the unit 1 down + n := bits.Len64(uint64(sz)) / 10 + f := float64(sz) / pows[n-1] + if f < min { + f *= base + n-- + } + + return f, units[n-1] +} + // ftoa appends string formatted 'f' to 'dst', assumed < ~800. func ftoa(dst []byte, f float64) []byte { switch i := uint64(f); { diff --git a/vendor/codeberg.org/gruf/go-ffmpreg/embed/ffmpreg.wasm.gz b/vendor/codeberg.org/gruf/go-ffmpreg/embed/ffmpreg.wasm.gz index 83f2ccb0c..6b35e3aba 100644 Binary files a/vendor/codeberg.org/gruf/go-ffmpreg/embed/ffmpreg.wasm.gz and b/vendor/codeberg.org/gruf/go-ffmpreg/embed/ffmpreg.wasm.gz differ diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/README.md b/vendor/codeberg.org/gruf/go-kv/v2/format/README.md new file mode 100644 index 000000000..5d11cc6b9 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-kv/v2/format/README.md @@ -0,0 +1,32 @@ +# format + +a low-level string formatting library that takes arbitrary input types as interfaces, and arguments as a struct. this does not contain any printf-like argument parsing, only log-friendly serialization of arbitrary input arguments. (noting that our output is noticably more log-friendly for struct / map types than stdlib "fmt"). + +benchmarks: +```shell +goos: linux +goarch: amd64 +pkg: codeberg.org/gruf/go-kv/v2/format +cpu: AMD Ryzen 7 7840U w/ Radeon 780M Graphics + +# go-kv/v2/format (i.e. latest) +BenchmarkFormatV2Append +BenchmarkFormatV2Append-16 590422 1977 ns/op 488 B/op 23 allocs/op +BenchmarkFormatV2AppendVerbose +BenchmarkFormatV2AppendVerbose-16 375628 2981 ns/op 1704 B/op 45 allocs/op + +# go-kv/format (i.e. v1) +BenchmarkFormatAppend +BenchmarkFormatAppend-16 208357 5883 ns/op 2624 B/op 169 allocs/op +BenchmarkFormatAppendVerbose +BenchmarkFormatAppendVerbose-16 35916 33563 ns/op 3734 B/op 208 allocs/op + +# fmt (i.e. stdlib) +BenchmarkFmtAppend +BenchmarkFmtAppend-16 147722 8418 ns/op 4747 B/op 191 allocs/op +BenchmarkFmtAppendVerbose +BenchmarkFmtAppendVerbose-16 167112 7238 ns/op 4401 B/op 178 allocs/op + +PASS +ok codeberg.org/gruf/go-kv/v2/format +``` diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/methods.go b/vendor/codeberg.org/gruf/go-kv/v2/format/methods.go index 8febd4a1d..4c0c1dbda 100644 --- a/vendor/codeberg.org/gruf/go-kv/v2/format/methods.go +++ b/vendor/codeberg.org/gruf/go-kv/v2/format/methods.go @@ -84,8 +84,8 @@ func getInterfaceStringerType(t xunsafe.TypeIter) FormatFunc { // (i.e. non-interface{}) type that has a Stringer{} method receiver. func getConcreteStringerType(t xunsafe.TypeIter) FormatFunc { itab := xunsafe.GetIfaceITab[Stringer](t.Type) - switch t.Indirect() && !t.IfaceIndir() { - case true: + switch { + case t.Indirect() && !t.IfaceIndir(): return with_typestr_ptrs(t, func(s *State) { s.P = *(*unsafe.Pointer)(s.P) if s.P == nil { @@ -95,13 +95,23 @@ func getConcreteStringerType(t xunsafe.TypeIter) FormatFunc { v := *(*Stringer)(xunsafe.PackIface(itab, s.P)) appendString(s, v.String()) }) - case false: + case t.Type.Kind() == reflect.Pointer && t.Type.Implements(stringerType): + // if the interface implementation is received by + // value type, the pointer type will also support + // it but it requires an extra dereference check. return with_typestr_ptrs(t, func(s *State) { + if s.P == nil { + appendNil(s) + return + } v := *(*Stringer)(xunsafe.PackIface(itab, s.P)) appendString(s, v.String()) }) default: - panic("unreachable") + return with_typestr_ptrs(t, func(s *State) { + v := *(*Stringer)(xunsafe.PackIface(itab, s.P)) + appendString(s, v.String()) + }) } } @@ -137,8 +147,8 @@ func getInterfaceFormattableType(t xunsafe.TypeIter) FormatFunc { // (i.e. non-interface{}) type that has a Formattable{} method receiver. func getConcreteFormattableType(t xunsafe.TypeIter) FormatFunc { itab := xunsafe.GetIfaceITab[Formattable](t.Type) - switch t.Indirect() && !t.IfaceIndir() { - case true: + switch { + case t.Indirect() && !t.IfaceIndir(): return with_typestr_ptrs(t, func(s *State) { s.P = *(*unsafe.Pointer)(s.P) if s.P == nil { @@ -148,13 +158,23 @@ func getConcreteFormattableType(t xunsafe.TypeIter) FormatFunc { v := *(*Formattable)(xunsafe.PackIface(itab, s.P)) v.Format(s) }) - case false: + case t.Type.Kind() == reflect.Pointer && t.Type.Implements(formattableType): + // if the interface implementation is received by + // value type, the pointer type will also support + // it but it requires an extra dereference check. return with_typestr_ptrs(t, func(s *State) { + if s.P == nil { + appendNil(s) + return + } v := *(*Formattable)(xunsafe.PackIface(itab, s.P)) v.Format(s) }) default: - panic("unreachable") + return with_typestr_ptrs(t, func(s *State) { + v := *(*Formattable)(xunsafe.PackIface(itab, s.P)) + v.Format(s) + }) } } @@ -190,8 +210,8 @@ func getInterfaceErrorType(t xunsafe.TypeIter) FormatFunc { // (i.e. non-interface{}) type that has an error{} method receiver. func getConcreteErrorType(t xunsafe.TypeIter) FormatFunc { itab := xunsafe.GetIfaceITab[error](t.Type) - switch t.Indirect() && !t.IfaceIndir() { - case true: + switch { + case t.Indirect() && !t.IfaceIndir(): return with_typestr_ptrs(t, func(s *State) { s.P = *(*unsafe.Pointer)(s.P) if s.P == nil { @@ -201,12 +221,22 @@ func getConcreteErrorType(t xunsafe.TypeIter) FormatFunc { v := *(*error)(xunsafe.PackIface(itab, s.P)) appendString(s, v.Error()) }) - case false: + case t.Type.Kind() == reflect.Pointer && t.Type.Implements(errorType): + // if the interface implementation is received by + // value type, the pointer type will also support + // it but it requires an extra dereference check. return with_typestr_ptrs(t, func(s *State) { + if s.P == nil { + appendNil(s) + return + } v := *(*error)(xunsafe.PackIface(itab, s.P)) appendString(s, v.Error()) }) default: - panic("unreachable") + return with_typestr_ptrs(t, func(s *State) { + v := *(*error)(xunsafe.PackIface(itab, s.P)) + appendString(s, v.Error()) + }) } } diff --git a/vendor/codeberg.org/gruf/go-mangler/helpers.go b/vendor/codeberg.org/gruf/go-mangler/helpers.go deleted file mode 100644 index 71d45e201..000000000 --- a/vendor/codeberg.org/gruf/go-mangler/helpers.go +++ /dev/null @@ -1,230 +0,0 @@ -//go:build go1.19 && !go1.25 - -package mangler - -import ( - "reflect" - "unsafe" -) - -func append_uint16(b []byte, u uint16) []byte { - return append(b, // LE - byte(u), - byte(u>>8), - ) -} - -func append_uint32(b []byte, u uint32) []byte { - return append(b, // LE - byte(u), - byte(u>>8), - byte(u>>16), - byte(u>>24), - ) -} - -func append_uint64(b []byte, u uint64) []byte { - return append(b, // LE - byte(u), - byte(u>>8), - byte(u>>16), - byte(u>>24), - byte(u>>32), - byte(u>>40), - byte(u>>48), - byte(u>>56), - ) -} - -type typecontext struct { - isptr bool - direct bool - ntype 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 { - if mangle == nil || n == 0 { - panic("bad input") - } - - // If this is a direct value type, i.e. non-nested primitive, - // or part of a single-field struct / single element array - // then it can be treated as a direct ptr with 1 less deref. - if ctx.direct { - n-- - } - - return func(buf []byte, ptr unsafe.Pointer) []byte { - // Deref n number times. - for i := n; i > 0; i-- { - - if ptr == nil { - // Check for nil values - buf = append(buf, '0') - return buf - } - - // Further deref ptr - buf = append(buf, '1') - ptr = *(*unsafe.Pointer)(ptr) - } - - if ptr == nil { - // Final nil val check - buf = append(buf, '0') - return buf - } - - // Mangle fully deref'd - buf = append(buf, '1') - buf = mangle(buf, ptr) - return buf - } -} - -func iter_slice_mangler(ctx typecontext, mangle Mangler) Mangler { - if ctx.rtype == nil || mangle == nil { - panic("bad input") - } - - // memory size of elem. - esz := ctx.rtype.Size() - - return func(buf []byte, ptr unsafe.Pointer) []byte { - // Get data as slice hdr. - hdr := (*slice_header)(ptr) - - for i := 0; i < hdr.len; i++ { - // Mangle data at slice index. - eptr := array_at(hdr.data, esz, i) - buf = mangle(buf, eptr) - buf = append(buf, ',') - } - - if hdr.len > 0 { - // Drop final comma. - buf = buf[:len(buf)-1] - } - - return buf - } -} - -func iter_array_mangler(ctx typecontext, mangle Mangler) Mangler { - if ctx.rtype == nil || mangle == nil { - panic("bad input") - } - - // no. array elements. - n := ctx.ntype.Len() - - // Optimize - // easy cases. - switch n { - case 0: - return empty_mangler - case 1: - return mangle - } - - // memory size of elem. - esz := ctx.rtype.Size() - - return func(buf []byte, ptr unsafe.Pointer) []byte { - for i := 0; i < n; i++ { - // Mangle data at array index. - offset := esz * uintptr(i) - eptr := add(ptr, offset) - buf = mangle(buf, eptr) - buf = append(buf, ',') - } - - if n > 0 { - // Drop final comma. - buf = buf[:len(buf)-1] - } - - return buf - } -} - -func iter_struct_mangler(ctx typecontext, manglers []Mangler) Mangler { - if ctx.rtype == nil || len(manglers) != ctx.ntype.NumField() { - panic("bad input") - } - - // Optimized easy cases. - switch len(manglers) { - case 0: - return empty_mangler - case 1: - return manglers[0] - } - - type field struct { - mangle Mangler - offset uintptr - } - - // Bundle together the fields and manglers. - fields := make([]field, ctx.ntype.NumField()) - for i := range fields { - rfield := ctx.ntype.Field(i) - fields[i].offset = rfield.Offset - fields[i].mangle = manglers[i] - if fields[i].mangle == nil { - panic("bad input") - } - } - - return func(buf []byte, ptr unsafe.Pointer) []byte { - for i := range fields { - // Get struct field ptr via offset. - fptr := add(ptr, fields[i].offset) - - // Mangle the struct field data. - buf = fields[i].mangle(buf, fptr) - buf = append(buf, ',') - } - - if len(fields) > 0 { - // Drop final comma. - buf = buf[:len(buf)-1] - } - - return buf - } -} - -func empty_mangler(buf []byte, _ unsafe.Pointer) []byte { - return buf -} - -// 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 { - return unsafe.Pointer(uintptr(ptr) + esz*uintptr(i)) -} - -// add returns the ptr addition of starting ptr and a delta. -func add(ptr unsafe.Pointer, delta uintptr) unsafe.Pointer { - return unsafe.Pointer(uintptr(ptr) + delta) -} - -type slice_header struct { - data unsafe.Pointer - len int - cap int -} - -func eface_data(a any) unsafe.Pointer { - type eface struct{ _, data unsafe.Pointer } - return (*eface)(unsafe.Pointer(&a)).data -} diff --git a/vendor/codeberg.org/gruf/go-mangler/load.go b/vendor/codeberg.org/gruf/go-mangler/load.go deleted file mode 100644 index ba2b9351b..000000000 --- a/vendor/codeberg.org/gruf/go-mangler/load.go +++ /dev/null @@ -1,230 +0,0 @@ -package mangler - -import ( - "reflect" -) - -// loadMangler is the top-most Mangler load function. It guarantees that a Mangler -// function will be returned for given value interface{} and reflected type. Else panics. -func loadMangler(t reflect.Type) Mangler { - ctx := typecontext{rtype: t} - ctx.direct = true - - // Load mangler fn - mng := load(ctx) - if mng != nil { - return mng - } - - // No mangler function could be determined - panic("cannot mangle type: " + t.String()) -} - -// load will load a Mangler or reflect Mangler for given type and iface 'a'. -// Note: allocates new interface value if nil provided, i.e. if coming via reflection. -func load(ctx typecontext) Mangler { - if ctx.rtype == nil { - // There is no reflect type to search by - panic("cannot mangle nil interface{} type") - } - - // Search by reflection. - mng := loadReflect(ctx) - if mng != nil { - return mng - } - - return nil -} - -// loadReflect will load a Mangler (or rMangler) function for the given reflected type info. -// NOTE: this is used as the top level load function for nested reflective searches. -func loadReflect(ctx typecontext) Mangler { - switch ctx.rtype.Kind() { - case reflect.Pointer: - return loadReflectPtr(ctx) - - case reflect.String: - return mangle_string - - case reflect.Struct: - return loadReflectStruct(ctx) - - case reflect.Array: - return loadReflectArray(ctx) - - case reflect.Slice: - return loadReflectSlice(ctx) - - case reflect.Bool: - return mangle_bool - - case reflect.Int, - reflect.Uint, - reflect.Uintptr: - return mangle_int - - case reflect.Int8, reflect.Uint8: - return mangle_8bit - - case reflect.Int16, reflect.Uint16: - return mangle_16bit - - case reflect.Int32, reflect.Uint32: - return mangle_32bit - - case reflect.Int64, reflect.Uint64: - return mangle_64bit - - case reflect.Float32: - return mangle_32bit - - case reflect.Float64: - return mangle_64bit - - case reflect.Complex64: - return mangle_64bit - - case reflect.Complex128: - return mangle_128bit - - default: - return nil - } -} - -// loadReflectPtr loads a Mangler (or rMangler) function for a ptr's element type. -// This also handles further dereferencing of any further ptr indrections (e.g. ***int). -func loadReflectPtr(ctx typecontext) Mangler { - var n uint - - // Iteratively dereference ptrs - for ctx.rtype.Kind() == reflect.Pointer { - ctx.rtype = ctx.rtype.Elem() - n++ - } - - // Set ptr type. - ctx.isptr = true - - // Search for elemn type mangler. - if mng := load(ctx); mng != nil { - return deref_ptr_mangler(ctx, mng, n) - } - - return nil -} - -// loadReflectKnownSlice loads a Mangler function for a -// known slice-of-element type (in this case, primtives). -func loadReflectKnownSlice(ctx typecontext) Mangler { - switch ctx.rtype.Kind() { - case reflect.String: - return mangle_string_slice - - case reflect.Bool: - return mangle_bool_slice - - case reflect.Int, - reflect.Uint, - reflect.Uintptr: - return mangle_int_slice - - case reflect.Int8, reflect.Uint8: - return mangle_8bit_slice - - case reflect.Int16, reflect.Uint16: - return mangle_16bit_slice - - case reflect.Int32, reflect.Uint32: - return mangle_32bit_slice - - case reflect.Int64, reflect.Uint64: - return mangle_64bit_slice - - case reflect.Float32: - return mangle_32bit_slice - - case reflect.Float64: - return mangle_64bit_slice - - case reflect.Complex64: - return mangle_64bit_slice - - case reflect.Complex128: - return mangle_128bit_slice - - default: - return nil - } -} - -// loadReflectSlice ... -func loadReflectSlice(ctx typecontext) Mangler { - - // Get nested element type. - elem := ctx.rtype.Elem() - - // Set this as nested type. - ctx.set_nested(false) - ctx.rtype = elem - - // Preferably look for known slice mangler func - if mng := loadReflectKnownSlice(ctx); mng != nil { - return mng - } - - // Use nested mangler iteration. - if mng := load(ctx); mng != nil { - return iter_slice_mangler(ctx, mng) - } - - return nil -} - -// loadReflectArray ... -func loadReflectArray(ctx typecontext) Mangler { - - // Get nested element type. - 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. - if mng := load(ctx); mng != nil { - return iter_array_mangler(ctx, mng) - } - - return nil -} - -// loadReflectStruct ... -func loadReflectStruct(ctx typecontext) Mangler { - var mngs []Mangler - - // Set this as a nested value type. - direct := ctx.rtype.NumField() <= 1 - ctx.set_nested(direct) - - // Gather manglers for all fields. - for i := 0; i < ctx.ntype.NumField(); i++ { - - // Update context with field at index. - ctx.rtype = ctx.ntype.Field(i).Type - - // Load mangler. - mng := load(ctx) - if mng == nil { - return nil - } - - // Append next to map. - mngs = append(mngs, mng) - } - - // Use manglers for nested iteration. - return iter_struct_mangler(ctx, mngs) -} diff --git a/vendor/codeberg.org/gruf/go-mangler/mangle.go b/vendor/codeberg.org/gruf/go-mangler/mangle.go deleted file mode 100644 index 576286682..000000000 --- a/vendor/codeberg.org/gruf/go-mangler/mangle.go +++ /dev/null @@ -1,130 +0,0 @@ -package mangler - -import ( - "reflect" - "sync" - "unsafe" -) - -// manglers is a map of runtime -// type ptrs => Mangler functions. -var manglers sync.Map - -// Mangler is a function that will take an input interface value of known -// type, and append it in mangled serialized form to the given byte buffer. -// While the value type is an interface, the Mangler functions are accessed -// by the value's runtime type pointer, allowing the input value type to be known. -type Mangler func(buf []byte, ptr unsafe.Pointer) []byte - -// Get will fetch the Mangler function for given runtime type. -// Note that the returned mangler will be a no-op in the case -// that an incorrect type is passed as the value argument. -func Get(t reflect.Type) Mangler { - var mng Mangler - - // Get raw runtime type ptr - uptr := uintptr(eface_data(t)) - - // Look for a cached mangler - v, ok := manglers.Load(uptr) - - if !ok { - // Load mangler function - mng = loadMangler(t) - } else { - // cast cached value - mng = v.(Mangler) - } - - return func(buf []byte, ptr unsafe.Pointer) []byte { - // First write the type ptr (this adds - // a unique prefix for each runtime type). - buf = append_uint64(buf, uint64(uptr)) - - // Finally, mangle value - return mng(buf, ptr) - } -} - -// Register will register the given Mangler function for use with vars of given runtime type. This allows -// registering performant manglers for existing types not implementing Mangled (e.g. std library types). -// NOTE: panics if there already exists a Mangler function for given type. Register on init(). -func Register(t reflect.Type, m Mangler) { - if t == nil { - // Nil interface{} types cannot be searched by, do not accept - panic("cannot register mangler for nil interface{} type") - } - - // Get raw runtime type ptr - uptr := uintptr(eface_data(t)) - - // Ensure this is a unique encoder - if _, ok := manglers.Load(uptr); ok { - panic("already registered mangler for type: " + t.String()) - } - - // Cache this encoder func - manglers.Store(uptr, m) -} - -// Append will append the mangled form of input value 'a' to buffer 'b'. -// See mangler.String() for more information on mangled output. -func Append(b []byte, a any) []byte { - var mng Mangler - - // Get reflect type of 'a' - t := reflect.TypeOf(a) - - // Get raw runtime type ptr - uptr := uintptr(eface_data(t)) - - // Look for a cached mangler - v, ok := manglers.Load(uptr) - - if !ok { - // Load into cache - mng = loadMangler(t) - manglers.Store(uptr, mng) - } else { - // cast cached value - mng = v.(Mangler) - } - - // First write the type ptr (this adds - // a unique prefix for each runtime type). - b = append_uint64(b, uint64(uptr)) - - // Finally, mangle value - ptr := eface_data(a) - return mng(b, ptr) -} - -// String will return the mangled format of input value 'a'. This -// mangled output will be unique for all default supported input types -// during a single runtime instance. Uniqueness cannot be guaranteed -// between separate runtime instances (whether running concurrently, or -// the same application running at different times). -// -// The exact formatting of the output data should not be relied upon, -// only that it is unique given the above constraints. Generally though, -// the mangled output is the binary formatted text of given input data. -// -// Uniqueness is guaranteed for similar input data of differing types -// (e.g. string("hello world") vs. []byte("hello world")) by prefixing -// mangled output with the input data's runtime type pointer. -// -// Default supported types include: -// - string -// - bool -// - int,int8,int16,int32,int64 -// - uint,uint8,uint16,uint32,uint64,uintptr -// - float32,float64 -// - complex64,complex128 -// - arbitrary structs -// - all type aliases of above -// - all pointers to the above -// - all slices / arrays of the above -func String(a any) string { - b := Append(make([]byte, 0, 32), a) - return *(*string)(unsafe.Pointer(&b)) -} diff --git a/vendor/codeberg.org/gruf/go-mangler/LICENSE b/vendor/codeberg.org/gruf/go-mangler/v2/LICENSE similarity index 100% rename from vendor/codeberg.org/gruf/go-mangler/LICENSE rename to vendor/codeberg.org/gruf/go-mangler/v2/LICENSE diff --git a/vendor/codeberg.org/gruf/go-mangler/README.md b/vendor/codeberg.org/gruf/go-mangler/v2/README.md similarity index 100% rename from vendor/codeberg.org/gruf/go-mangler/README.md rename to vendor/codeberg.org/gruf/go-mangler/v2/README.md diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/array.go b/vendor/codeberg.org/gruf/go-mangler/v2/array.go new file mode 100644 index 000000000..6638a6e2d --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/array.go @@ -0,0 +1,55 @@ +package mangler + +import ( + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// iterArrayType returns a Mangler capable of iterating +// and mangling the given array type currently in TypeIter{}. +// note this will fetch sub-Mangler for array element type. +func iterArrayType(t xunsafe.TypeIter) Mangler { + + // Array element type. + elem := t.Type.Elem() + + // Get nested elem TypeIter with appropriate flags. + flags := xunsafe.ReflectArrayElemFlags(t.Flag, elem) + et := t.Child(elem, flags) + + // Get elem mangler. + fn := loadOrGet(et) + if fn == nil { + return nil + } + + // Array element in-memory size. + esz := t.Type.Elem().Size() + + // No of elements. + n := t.Type.Len() + switch n { + case 0: + return empty_mangler + case 1: + return fn + default: + return func(buf []byte, ptr unsafe.Pointer) []byte { + for i := 0; i < n; i++ { + // Mangle data at array index. + offset := esz * uintptr(i) + eptr := add(ptr, offset) + buf = fn(buf, eptr) + buf = append(buf, ',') + } + + if n > 0 { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/cache.go b/vendor/codeberg.org/gruf/go-mangler/v2/cache.go new file mode 100644 index 000000000..0f6f71cd0 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/cache.go @@ -0,0 +1,62 @@ +package mangler + +import ( + "sync/atomic" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +var manglers cache + +// cache is a concurrency-safe map[xunsafe.TypeInfo]Mangler +// cache, designed for heavy reads but with unfortunately expensive +// writes. it is designed such that after some initial load period +// in which functions are cached by types, all future ops are reads. +type cache struct{ p unsafe.Pointer } + +// Get will check cache for mangler func under key. +func (c *cache) Get(t xunsafe.TypeInfo) Mangler { + if p := c.load(); p != nil { + return (*p)[t] + } + return nil +} + +// Put will place given mangler func in cache under key, if not already exists. +func (c *cache) Put(t xunsafe.TypeInfo, fn Mangler) { + for { + p := c.load() + + var cache map[xunsafe.TypeInfo]Mangler + + if p != nil { + if _, ok := (*p)[t]; ok { + return + } + + cache = make(map[xunsafe.TypeInfo]Mangler, len(*p)+1) + for key, value := range *p { + cache[key] = value + } + } else { + cache = make(map[xunsafe.TypeInfo]Mangler, 1) + } + + cache[t] = fn + + if c.cas(p, &cache) { + return + } + } +} + +// load is a typed wrapper around atomic.LoadPointer(). +func (c *cache) load() *map[xunsafe.TypeInfo]Mangler { + return (*map[xunsafe.TypeInfo]Mangler)(atomic.LoadPointer(&c.p)) +} + +// cas is a typed wrapper around atomic.CompareAndSwapPointer(). +func (c *cache) cas(old, new *map[xunsafe.TypeInfo]Mangler) bool { + return atomic.CompareAndSwapPointer(&c.p, unsafe.Pointer(old), unsafe.Pointer(new)) +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/helpers.go b/vendor/codeberg.org/gruf/go-mangler/v2/helpers.go new file mode 100644 index 000000000..0b07bba13 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/helpers.go @@ -0,0 +1,43 @@ +package mangler + +import ( + "unsafe" +) + +func append_uint16(b []byte, u uint16) []byte { + return append(b, // LE + byte(u), + byte(u>>8), + ) +} + +func append_uint32(b []byte, u uint32) []byte { + return append(b, // LE + byte(u), + byte(u>>8), + byte(u>>16), + byte(u>>24), + ) +} + +func append_uint64(b []byte, u uint64) []byte { + return append(b, // LE + byte(u), + byte(u>>8), + byte(u>>16), + byte(u>>24), + byte(u>>32), + byte(u>>40), + byte(u>>48), + byte(u>>56), + ) +} + +func empty_mangler(buf []byte, _ unsafe.Pointer) []byte { + return buf +} + +// add returns the ptr addition of starting ptr and a delta. +func add(ptr unsafe.Pointer, delta uintptr) unsafe.Pointer { + return unsafe.Pointer(uintptr(ptr) + delta) +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/load.go b/vendor/codeberg.org/gruf/go-mangler/v2/load.go new file mode 100644 index 000000000..8355c5f3c --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/load.go @@ -0,0 +1,150 @@ +package mangler + +import ( + "reflect" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// loadOrStore first checks the cache for a Mangler +// function, else generates one by calling get(). +// note: this does store generated funcs in cache. +func loadOrStore(t xunsafe.TypeIter) Mangler { + + // Get cache key. + key := t.TypeInfo + + // Check cache for func. + fn := manglers.Get(key) + + if fn == nil { + // Generate new mangler + // func for this type. + fn = get(t) + if fn == nil { + return nil + } + + // Store func in cache. + manglers.Put(key, fn) + } + + return fn +} + +// loadOrGet first checks the cache for a Mangler +// function, else generates one by calling get(). +// note: it does not store the function in cache. +func loadOrGet(t xunsafe.TypeIter) Mangler { + + // Check cache for mangler func. + fn := manglers.Get(t.TypeInfo) + + if fn == nil { + // Generate new mangler + // func for this type. + fn = get(t) + } + + return fn +} + +var ( + // reflectTypeType is the reflected type of the reflect type, + // used in fmt.get() to prevent iter of internal ABI structs. + reflectTypeType = reflect.TypeOf(reflect.TypeOf(0)) +) + +// get attempts to generate a new Mangler function +// capable of mangling a ptr of given type information. +func get(t xunsafe.TypeIter) (fn Mangler) { + defer func() { + if fn == nil { + // nothing more + // we can do. + return + } + + if t.Parent != nil { + // We're only interested + // in wrapping top-level. + return + } + + // Get reflected type ptr for prefix. + ptr := xunsafe.ReflectTypeData(t.Type) + uptr := uintptr(ptr) + + // Outer fn. + mng := fn + + // Wrap the mangler func to prepend type pointer. + fn = func(buf []byte, ptr unsafe.Pointer) []byte { + buf = append_uint64(buf, uint64(uptr)) + return mng(buf, ptr) + } + }() + + if t.Type == nil { + // nil type. + return nil + } + + if t.Type == reflectTypeType { + // DO NOT iterate down internal ABI + // types, some are in non-GC memory. + return nil + } + + // Check supports known method receiver. + if fn := getMethodType(t); fn != nil { + return fn + } + + if !visit(t) { + // On type recursion simply + // mangle as raw pointer. + return mangle_int + } + + // Get func for type kind. + switch t.Type.Kind() { + case reflect.Pointer: + return derefPointerType(t) + case reflect.Struct: + return iterStructType(t) + case reflect.Array: + return iterArrayType(t) + case reflect.Slice: + return iterSliceType(t) + case reflect.Map: + return iterMapType(t) + case reflect.String: + return mangle_string + case reflect.Bool: + return mangle_bool + case reflect.Int, + reflect.Uint, + reflect.Uintptr: + return mangle_int + case reflect.Int8, reflect.Uint8: + return mangle_8bit + case reflect.Int16, reflect.Uint16: + return mangle_16bit + case reflect.Int32, reflect.Uint32: + return mangle_32bit + case reflect.Int64, reflect.Uint64: + return mangle_64bit + case reflect.Float32: + return mangle_32bit + case reflect.Float64: + return mangle_64bit + case reflect.Complex64: + return mangle_64bit + case reflect.Complex128: + return mangle_128bit + default: + return nil + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/mangle.go b/vendor/codeberg.org/gruf/go-mangler/v2/mangle.go new file mode 100644 index 000000000..0c746fc95 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/mangle.go @@ -0,0 +1,93 @@ +package mangler + +import ( + "fmt" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// Mangler is a function that will take an input value of known type, +// and append it in mangled serialized form to the given byte buffer. +type Mangler func(buf []byte, ptr unsafe.Pointer) []byte + +// Get will fetch the Mangler function for given runtime type information. +// The required argument is of type xunsafe.TypeIter{} as unsafe pointer +// access requires further contextual information like type nesting. +func Get(t xunsafe.TypeIter) Mangler { + t.Parent = nil // enforce type prefix + fn := loadOrStore(t) + if fn == nil { + panic(fmt.Sprintf("cannot mangle type: %s", t.Type)) + } + return fn +} + +// GetNoLoad is functionally similar to Get(), +// without caching the resulting Mangler. +func GetNoLoad(t xunsafe.TypeIter) Mangler { + t.Parent = nil // enforce type prefix + fn := loadOrGet(t) + if fn == nil { + panic(fmt.Sprintf("cannot mangle type: %s", t.Type)) + } + return fn +} + +// Append will append the mangled form of input value 'a' to buffer 'b'. +// +// See mangler.String() for more information on mangled output. +func Append(b []byte, a any) []byte { + t := xunsafe.TypeIterFrom(a) + p := xunsafe.UnpackEface(a) + return Get(t)(b, p) +} + +// AppendMulti appends all mangled forms of input value(s) 'a' to buffer 'b' +// separated by colon characters. When all type manglers are currently cached +// for all types in 'a', this will be faster than multiple calls to Append(). +// +// See mangler.String() for more information on mangled output. +func AppendMulti(b []byte, a ...any) []byte { + if p := manglers.load(); p != nil { + b4 := len(b) + for _, a := range a { + t := xunsafe.TypeIterFrom(a) + m := (*p)[t.TypeInfo] + if m == nil { + b = b[:b4] + goto slow + } + b = m(b, xunsafe.UnpackEface(a)) + b = append(b, '.') + } + return b + } +slow: + for _, a := range a { + b = Append(b, a) + b = append(b, '.') + } + return b +} + +// String will return the mangled format of input value 'a'. This +// mangled output will be unique for all default supported input types +// during a single runtime instance. Uniqueness cannot be guaranteed +// between separate runtime instances (whether running concurrently, or +// the same application running at different times). +// +// The exact formatting of the output data should not be relied upon, +// only that it is unique given the above constraints. Generally though, +// the mangled output is the binary formatted text of given input data. +// +// Uniqueness is guaranteed for similar input data of differing types +// (e.g. string("hello world") vs. []byte("hello world")) by prefixing +// mangled output with the input data's runtime type pointer. +// +// Default supported types include all concrete (i.e. non-interface{}) +// data types, and interfaces implementing Mangleable{}. +func String(a any) string { + b := Append(make([]byte, 0, 32), a) + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/vendor/codeberg.org/gruf/go-mangler/manglers.go b/vendor/codeberg.org/gruf/go-mangler/v2/manglers.go similarity index 100% rename from vendor/codeberg.org/gruf/go-mangler/manglers.go rename to vendor/codeberg.org/gruf/go-mangler/v2/manglers.go diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/map.go b/vendor/codeberg.org/gruf/go-mangler/v2/map.go new file mode 100644 index 000000000..986aca4b0 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/map.go @@ -0,0 +1,80 @@ +package mangler + +import ( + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// iterMapType returns a Mangler capable of iterating +// and mangling the given map type currently in TypeIter{}. +// note this will fetch sub-Manglers for key / value types. +func iterMapType(t xunsafe.TypeIter) Mangler { + + // Key / value types. + key := t.Type.Key() + elem := t.Type.Elem() + + // Get nested k / v TypeIters with appropriate flags. + flagsKey := xunsafe.ReflectMapKeyFlags(key) + flagsVal := xunsafe.ReflectMapElemFlags(elem) + kt := t.Child(key, flagsKey) + vt := t.Child(elem, flagsVal) + + // Get key mangler. + kfn := loadOrGet(kt) + if kfn == nil { + return nil + } + + // Get value mangler. + vfn := loadOrGet(vt) + if vfn == nil { + return nil + } + + // Final map type. + rtype := t.Type + flags := t.Flag + + return func(buf []byte, ptr unsafe.Pointer) []byte { + if ptr == nil || *(*unsafe.Pointer)(ptr) == nil { + // Append nil indicator. + buf = append(buf, '0') + return buf + } + + // Build reflect value, and then a map iterator. + v := xunsafe.BuildReflectValue(rtype, ptr, flags) + i := xunsafe.GetMapIter(v) + + // Before len. + l := len(buf) + + // Append not-nil flag. + buf = append(buf, '1') + + for i.Next() { + // Pass to map key func. + ptr = xunsafe.Map_Key(i) + buf = kfn(buf, ptr) + + // Add key seperator. + buf = append(buf, ':') + + // Pass to map elem func. + ptr = xunsafe.Map_Elem(i) + buf = vfn(buf, ptr) + + // Add comma seperator. + buf = append(buf, ',') + } + + if len(buf) != l { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/method.go b/vendor/codeberg.org/gruf/go-mangler/v2/method.go new file mode 100644 index 000000000..7a938d6ae --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/method.go @@ -0,0 +1,105 @@ +package mangler + +import ( + "reflect" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +var ( + // mangleable type for implement checks. + mangleableType = reflect.TypeFor[Mangleable]() +) + +type Mangleable interface { + Mangle(dst []byte) []byte +} + +// getMethodType returns a *possible* Mangler to handle case +// of a type that implements any known interface{} types, else nil. +func getMethodType(t xunsafe.TypeIter) Mangler { + switch { + case t.Type.Implements(mangleableType): + switch t.Type.Kind() { + case reflect.Interface: + return getInterfaceMangleableType(t) + default: + return getConcreteMangleableType(t) + } + default: + return nil + } +} + +// getInterfaceMangleableType returns a Mangler to handle case of an interface{} +// type that implements Mangleable{}, i.e. Mangleable{} itself and any superset of. +func getInterfaceMangleableType(t xunsafe.TypeIter) Mangler { + switch t.Indirect() && !t.IfaceIndir() { + case true: + return func(buf []byte, ptr unsafe.Pointer) []byte { + ptr = *(*unsafe.Pointer)(ptr) + if ptr == nil || (*xunsafe.Abi_NonEmptyInterface)(ptr).Data == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(ptr) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + case false: + return func(buf []byte, ptr unsafe.Pointer) []byte { + if ptr == nil || (*xunsafe.Abi_NonEmptyInterface)(ptr).Data == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(ptr) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + default: + panic("unreachable") + } +} + +// getConcreteMangleableType returns a Manlger to handle case of concrete +// (i.e. non-interface{}) type that has a Mangleable{} method receiver. +func getConcreteMangleableType(t xunsafe.TypeIter) Mangler { + itab := xunsafe.GetIfaceITab[Mangleable](t.Type) + switch { + case t.Indirect() && !t.IfaceIndir(): + return func(buf []byte, ptr unsafe.Pointer) []byte { + ptr = *(*unsafe.Pointer)(ptr) + if ptr == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(xunsafe.PackIface(itab, ptr)) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + case t.Type.Kind() == reflect.Pointer && t.Type.Implements(mangleableType): + // if the interface implementation is received by + // value type, the pointer type will also support + // it but it requires an extra dereference check. + return func(buf []byte, ptr unsafe.Pointer) []byte { + if ptr == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(xunsafe.PackIface(itab, ptr)) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + default: + return func(buf []byte, ptr unsafe.Pointer) []byte { + v := *(*Mangleable)(xunsafe.PackIface(itab, ptr)) + buf = v.Mangle(buf) + return buf + } + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/pointer.go b/vendor/codeberg.org/gruf/go-mangler/v2/pointer.go new file mode 100644 index 000000000..d732b2f67 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/pointer.go @@ -0,0 +1,81 @@ +package mangler + +import ( + "reflect" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// derefPointerType returns a Mangler capable of dereferencing +// and formatting the given pointer type currently in TypeIter{}. +// note this will fetch a sub-Mangler for resulting value type. +func derefPointerType(t xunsafe.TypeIter) Mangler { + var derefs int + var indirects int64 + rtype := t.Type + flags := t.Flag + + // Iteratively dereference pointer types. + for rtype.Kind() == reflect.Pointer { + + // Only if this is actual indirect memory do we + // perform a derefence, otherwise we just skip over + // and increase the dereference indicator, i.e. '1'. + if flags&xunsafe.Reflect_flagIndir != 0 { + indirects |= 1 << derefs + } + derefs++ + + // Get next elem type. + rtype = rtype.Elem() + + // Get next set of dereferenced element type flags. + flags = xunsafe.ReflectPointerElemFlags(flags, rtype) + } + + // Ensure this is a reasonable number of derefs. + if derefs > 4*int(unsafe.Sizeof(indirects)) { + return nil + } + + // Wrap value as TypeIter. + vt := t.Child(rtype, flags) + + // Get value mangler. + fn := loadOrGet(vt) + if fn == nil { + return nil + } + + return func(buf []byte, ptr unsafe.Pointer) []byte { + for i := 0; i < derefs; i++ { + switch { + case indirects&1< 0 { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } +} + +// mangleKnownSlice loads a Mangler function for a +// known slice-of-element type (in this case, primtives). +func mangleKnownSlice(t xunsafe.TypeIter) Mangler { + switch t.Type.Kind() { + case reflect.String: + return mangle_string_slice + case reflect.Bool: + return mangle_bool_slice + case reflect.Int, + reflect.Uint, + reflect.Uintptr: + return mangle_int_slice + case reflect.Int8, reflect.Uint8: + return mangle_8bit_slice + case reflect.Int16, reflect.Uint16: + return mangle_16bit_slice + case reflect.Int32, reflect.Uint32: + return mangle_32bit_slice + case reflect.Int64, reflect.Uint64: + return mangle_64bit_slice + case reflect.Float32: + return mangle_32bit_slice + case reflect.Float64: + return mangle_64bit_slice + case reflect.Complex64: + return mangle_64bit_slice + case reflect.Complex128: + return mangle_128bit_slice + default: + return nil + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/struct.go b/vendor/codeberg.org/gruf/go-mangler/v2/struct.go new file mode 100644 index 000000000..e9672bf2b --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/struct.go @@ -0,0 +1,75 @@ +package mangler + +import ( + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// field stores the minimum necessary +// data for iterating and mangling +// each field in a given struct. +type field struct { + mangle Mangler + offset uintptr +} + +// iterStructType returns a Mangler capable of iterating +// and mangling the given struct type currently in TypeIter{}. +// note this will fetch sub-Manglers for each struct field. +func iterStructType(t xunsafe.TypeIter) Mangler { + + // Number of struct fields. + n := t.Type.NumField() + + // Gather mangler functions. + fields := make([]field, n) + for i := 0; i < n; i++ { + + // Get struct field at index. + sfield := t.Type.Field(i) + rtype := sfield.Type + + // Get nested field TypeIter with appropriate flags. + flags := xunsafe.ReflectStructFieldFlags(t.Flag, rtype) + ft := t.Child(sfield.Type, flags) + + // Get field mangler. + fn := loadOrGet(ft) + if fn == nil { + return nil + } + + // Set field info. + fields[i] = field{ + mangle: fn, + offset: sfield.Offset, + } + } + + // Handle no. fields. + switch len(fields) { + case 0: + return empty_mangler + case 1: + return fields[0].mangle + default: + return func(buf []byte, ptr unsafe.Pointer) []byte { + for i := range fields { + // Get struct field ptr via offset. + fptr := add(ptr, fields[i].offset) + + // Mangle the struct field data. + buf = fields[i].mangle(buf, fptr) + buf = append(buf, ',') + } + + if len(fields) > 0 { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/type.go b/vendor/codeberg.org/gruf/go-mangler/v2/type.go new file mode 100644 index 000000000..71be7f0b4 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/type.go @@ -0,0 +1,20 @@ +package mangler + +import ( + "codeberg.org/gruf/go-xunsafe" +) + +// visit checks if current type has already +// appeared in the TypeIter{}'s parent heirarchy. +func visit(iter xunsafe.TypeIter) bool { + t := iter.Type + + // Check if type is already encountered further up tree. + for node := iter.Parent; node != nil; node = node.Parent { + if node.Type == t { + return false + } + } + + return true +} diff --git a/vendor/codeberg.org/gruf/go-mutexes/map_unsafe.go b/vendor/codeberg.org/gruf/go-mutexes/map_unsafe.go index a59c13015..829b2597f 100644 --- a/vendor/codeberg.org/gruf/go-mutexes/map_unsafe.go +++ b/vendor/codeberg.org/gruf/go-mutexes/map_unsafe.go @@ -1,4 +1,4 @@ -//go:build go1.22 && !go1.25 +//go:build go1.22 && !go1.26 package mutexes @@ -21,7 +21,7 @@ func syncCond_last_ticket(c *sync.Cond) uint32 { // this does not alter the first // 2 fields which are all we need. type notifyList struct { - _ atomic.Uint32 + _ uint32 notify uint32 // ... other fields } diff --git a/vendor/codeberg.org/gruf/go-structr/README.md b/vendor/codeberg.org/gruf/go-structr/README.md index f0b235c98..922dc1a58 100644 --- a/vendor/codeberg.org/gruf/go-structr/README.md +++ b/vendor/codeberg.org/gruf/go-structr/README.md @@ -2,7 +2,7 @@ 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 *most* arbitrary types (other than maps, channels, functions), so feel free to index by by almost *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/v2](https://codeberg.org/gruf/go-mangler), which at this point in time supports *most* arbitrary types (other than channels, functions), so feel free to index by by almost *anything*! See the [docs](https://pkg.go.dev/codeberg.org/gruf/go-structr) for more API information. diff --git a/vendor/codeberg.org/gruf/go-structr/cache.go b/vendor/codeberg.org/gruf/go-structr/cache.go index 4b32d6821..6ae6ff3ed 100644 --- a/vendor/codeberg.org/gruf/go-structr/cache.go +++ b/vendor/codeberg.org/gruf/go-structr/cache.go @@ -3,7 +3,6 @@ package structr import ( "context" "errors" - "reflect" "sync" "unsafe" ) @@ -83,7 +82,7 @@ type Cache[StructType any] struct { // Init initializes the cache with given configuration // including struct fields to index, and necessary fns. func (c *Cache[T]) Init(config CacheConfig[T]) { - t := reflect.TypeOf((*T)(nil)).Elem() + t := get_type_iter[T]() if len(config.Indices) == 0 { panic("no indices provided") @@ -182,9 +181,14 @@ func (c *Cache[T]) Put(values ...T) { // Acquire lock. c.mutex.Lock() - // Wrap unlock to only do once. - unlock := once(c.mutex.Unlock) - defer unlock() + // Ensure mutex + // gets unlocked. + var unlocked bool + defer func() { + if !unlocked { + c.mutex.Unlock() + } + }() // Check cache init. if c.copy == nil { @@ -202,9 +206,9 @@ func (c *Cache[T]) Put(values ...T) { // Get func ptrs. invalid := c.invalid - // Done with - // the lock. - unlock() + // Done with lock. + c.mutex.Unlock() + unlocked = true if invalid != nil { // Pass all invalidated values @@ -241,9 +245,14 @@ func (c *Cache[T]) LoadOne(index *Index, key Key, load func() (T, error)) (T, er // Acquire lock. c.mutex.Lock() - // Wrap unlock to only do once. - unlock := once(c.mutex.Unlock) - defer unlock() + // Ensure mutex + // gets unlocked. + var unlocked bool + defer func() { + if !unlocked { + c.mutex.Unlock() + } + }() // Check init'd. if c.copy == nil || @@ -276,9 +285,9 @@ func (c *Cache[T]) LoadOne(index *Index, key Key, load func() (T, error)) (T, er // Get func ptrs. ignore := c.ignore - // Done with - // the lock. - unlock() + // Done with lock. + c.mutex.Unlock() + unlocked = true if ok { // item found! @@ -295,6 +304,7 @@ func (c *Cache[T]) LoadOne(index *Index, key Key, load func() (T, error)) (T, er // Acquire lock. c.mutex.Lock() + unlocked = false // Index this new loaded item. // Note this handles copying of @@ -308,6 +318,7 @@ func (c *Cache[T]) LoadOne(index *Index, key Key, load func() (T, error)) (T, er // Done with lock. c.mutex.Unlock() + unlocked = true return val, err } @@ -328,9 +339,14 @@ func (c *Cache[T]) Load(index *Index, keys []Key, load func([]Key) ([]T, error)) // Acquire lock. c.mutex.Lock() - // Wrap unlock to only do once. - unlock := once(c.mutex.Unlock) - defer unlock() + // Ensure mutex + // gets unlocked. + var unlocked bool + defer func() { + if !unlocked { + c.mutex.Unlock() + } + }() // Check init'd. if c.copy == nil { @@ -366,9 +382,9 @@ func (c *Cache[T]) Load(index *Index, keys []Key, load func([]Key) ([]T, error)) } } - // Done with - // the lock. - unlock() + // Done with lock. + c.mutex.Unlock() + unlocked = true if len(toLoad) == 0 { // We loaded everything! @@ -383,6 +399,7 @@ func (c *Cache[T]) Load(index *Index, keys []Key, load func([]Key) ([]T, error)) // Acquire lock. c.mutex.Lock() + unlocked = false // Store all uncached values. for i := range uncached { @@ -394,6 +411,7 @@ func (c *Cache[T]) Load(index *Index, keys []Key, load func([]Key) ([]T, error)) // Done with lock. c.mutex.Unlock() + unlocked = true // Append uncached to return values. values = append(values, uncached...) @@ -685,7 +703,7 @@ func (c *Cache[T]) store_error(index *Index, key string, err error) { } func (c *Cache[T]) delete(i *indexed_item) { - for len(i.indexed) != 0 { + for len(i.indexed) > 0 { // Pop last indexed entry from list. entry := i.indexed[len(i.indexed)-1] i.indexed[len(i.indexed)-1] = nil diff --git a/vendor/codeberg.org/gruf/go-structr/index.go b/vendor/codeberg.org/gruf/go-structr/index.go index acb58f7a8..9fbdcf112 100644 --- a/vendor/codeberg.org/gruf/go-structr/index.go +++ b/vendor/codeberg.org/gruf/go-structr/index.go @@ -1,7 +1,6 @@ package structr import ( - "fmt" "os" "reflect" "strings" @@ -9,6 +8,7 @@ import ( "unsafe" "codeberg.org/gruf/go-byteutil" + "codeberg.org/gruf/go-xunsafe" ) // IndexConfig defines config variables @@ -29,7 +29,7 @@ type IndexConfig struct { // is nil then it will not be indexed. // // Field types supported include any of those - // supported by the `go-mangler` library. + // supported by the `go-mangler/v2` library. Fields string // Multiple indicates whether to accept multiple @@ -58,7 +58,7 @@ type IndexConfig struct { type Index struct { // ptr is a pointer to - // the source Cache/Queue + // the source type this // index is attached to. ptr unsafe.Pointer @@ -68,14 +68,12 @@ type Index struct { name string // backing data store of the index, containing - // the cached results contained within wrapping - // 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. + // list{}s of index_entry{}s which each contain + // the exact key each result is stored under. data hashmap - // struct fields encompassed by - // keys (+ hashes) of this index. + // struct fields encompassed + // by keys of this index. fields []struct_field // index flags: @@ -89,55 +87,14 @@ 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 { - ptrs := make([]unsafe.Pointer, len(parts)) - for x, part := range parts { - ptrs[x] = eface_data(part) - } - buf := new_buffer() - key := i.key(buf, ptrs) - free_buffer(buf) - return Key{ - raw: parts, - key: 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 { - ptrs := make([]unsafe.Pointer, len(parts)) - for x, part := range parts { - ptrs[x] = eface_data(part) - } - key := i.key(buf, ptrs) - if key == "" { - continue - } - keys = append(keys, Key{ - raw: parts, - key: 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) { +func (i *Index) init(t xunsafe.TypeIter, 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: + case t.Type.Kind() == reflect.Struct: + case t.Type.Kind() == reflect.Pointer && + t.Type.Elem().Kind() == reflect.Struct: default: panic("index only support struct{} and *struct{}") } @@ -164,8 +121,8 @@ func (i *Index) init(t reflect.Type, cfg IndexConfig, cap int) { // Split name to account for nesting. names := strings.Split(name, ".") - // Look for usable struct field. - i.fields[x] = find_field(t, names) + // Look for struct field by names. + i.fields[x], _ = find_field(t, names) } // Initialize store for @@ -219,15 +176,12 @@ func (i *Index) get(key string, hook func(*indexed_item)) { } } -// key uses hasher to generate Key{} from given raw parts. +// key ... func (i *Index) key(buf *byteutil.Buffer, parts []unsafe.Pointer) string { - buf.B = buf.B[:0] if len(parts) != len(i.fields) { - panic(fmt.Sprintf("incorrect number key parts: want=%d received=%d", - len(i.fields), - len(parts), - )) + panic(assert("len(parts) = len(i.fields)")) } + buf.B = buf.B[:0] if !allow_zero(i.flags) { for x, field := range i.fields { before := len(buf.B) @@ -401,7 +355,7 @@ func (i *Index) delete_entry(entry *index_entry) { // index_entry represents a single entry // in an Index{}, where it will be accessible -// by Key{} pointing to a containing list{}. +// by .key pointing to a containing list{}. type index_entry struct { // list elem that entry is stored diff --git a/vendor/codeberg.org/gruf/go-structr/key.go b/vendor/codeberg.org/gruf/go-structr/key.go index 11e595b80..ad591f8ac 100644 --- a/vendor/codeberg.org/gruf/go-structr/key.go +++ b/vendor/codeberg.org/gruf/go-structr/key.go @@ -4,6 +4,7 @@ import ( "sync" "codeberg.org/gruf/go-byteutil" + "codeberg.org/gruf/go-mangler/v2" ) // Key represents one key to @@ -14,6 +15,37 @@ type Key struct { raw []any } +// MakeKey generates Key{} from given parts. +func MakeKey(parts ...any) Key { + buf := new_buffer() + buf.B = mangler.AppendMulti(buf.B[:0], parts...) + key := string(buf.B) + free_buffer(buf) + return Key{ + raw: parts, + key: key, + } +} + +// MakeKeys generates []Key{} from given (multiple) parts. +func MakeKeys(parts ...[]any) []Key { + keys := make([]Key, len(parts)) + if len(keys) != len(parts) { + panic(assert("BCE")) + } + buf := new_buffer() + for x, parts := range parts { + buf.B = mangler.AppendMulti(buf.B[:0], parts...) + key := string(buf.B) + keys[x] = Key{ + raw: parts, + key: key, + } + } + free_buffer(buf) + return keys +} + // Key returns the underlying cache key string. // NOTE: this will not be log output friendly. func (k Key) Key() string { @@ -31,11 +63,6 @@ func (k Key) Values() []any { return k.raw } -// Zero indicates a zero value key. -func (k Key) Zero() bool { - return (k.key == "") -} - var buf_pool sync.Pool // new_buffer returns a new initialized byte buffer. diff --git a/vendor/codeberg.org/gruf/go-structr/list.go b/vendor/codeberg.org/gruf/go-structr/list.go index 3f69a1309..a2cb5b658 100644 --- a/vendor/codeberg.org/gruf/go-structr/list.go +++ b/vendor/codeberg.org/gruf/go-structr/list.go @@ -177,11 +177,3 @@ func (l *list) remove(elem *list_elem) { // Decr count l.len-- } - -// func (l *list) range_up(yield func(*list_elem) bool) { - -// } - -// func (l *list) range_down(yield func(*list_elem) bool) { - -// } diff --git a/vendor/codeberg.org/gruf/go-structr/queue.go b/vendor/codeberg.org/gruf/go-structr/queue.go index 2860e5f4b..674967d74 100644 --- a/vendor/codeberg.org/gruf/go-structr/queue.go +++ b/vendor/codeberg.org/gruf/go-structr/queue.go @@ -1,7 +1,6 @@ package structr import ( - "reflect" "sync" "unsafe" ) @@ -48,7 +47,7 @@ type Queue[StructType any] struct { // Init initializes the queue with given configuration // including struct fields to index, and necessary fns. func (q *Queue[T]) Init(config QueueConfig[T]) { - t := reflect.TypeOf((*T)(nil)).Elem() + t := get_type_iter[T]() if len(config.Indices) == 0 { panic("no indices provided") @@ -323,7 +322,7 @@ func (q *Queue[T]) index(value T) *indexed_item { } func (q *Queue[T]) delete(i *indexed_item) { - for len(i.indexed) != 0 { + for len(i.indexed) > 0 { // Pop last indexed entry from list. entry := i.indexed[len(i.indexed)-1] i.indexed[len(i.indexed)-1] = nil diff --git a/vendor/codeberg.org/gruf/go-structr/runtime.go b/vendor/codeberg.org/gruf/go-structr/runtime.go index 1d379b70d..c1a883462 100644 --- a/vendor/codeberg.org/gruf/go-structr/runtime.go +++ b/vendor/codeberg.org/gruf/go-structr/runtime.go @@ -1,5 +1,3 @@ -//go:build go1.22 && !go1.25 - package structr import ( @@ -11,17 +9,16 @@ import ( "unicode/utf8" "unsafe" - "codeberg.org/gruf/go-mangler" + "codeberg.org/gruf/go-mangler/v2" + "codeberg.org/gruf/go-xunsafe" ) // struct_field contains pre-prepared type // information about a struct's field member, // including memory offset and hash function. type struct_field struct { - rtype reflect.Type - // struct field type mangling - // (i.e. fast serializing) fn. + // mangle ... mangle mangler.Mangler // zero value data, used when @@ -30,18 +27,13 @@ type struct_field struct { zero unsafe.Pointer // mangled zero value string, - // if set this indicates zero - // values of field not allowed + // to check zero value keys. zerostr string // offsets defines whereabouts in - // memory this field is located. + // memory this field is located, + // and after how many dereferences. 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 @@ -49,13 +41,22 @@ type struct_field struct { // derefences required, then by offset from // that final memory location. type next_offset struct { - derefs uint + derefs int offset uintptr } +// get_type_iter returns a prepared xunsafe.TypeIter{} for generic parameter type, +// with flagIndir specifically set as we always take a reference to value type. +func get_type_iter[T any]() xunsafe.TypeIter { + rtype := reflect.TypeOf((*T)(nil)).Elem() + flags := xunsafe.Reflect_flag(xunsafe.Abi_Type_Kind(rtype)) + flags |= xunsafe.Reflect_flagIndir // always comes from unsafe ptr + return xunsafe.ToTypeIter(rtype, flags) +} + // find_field will search for a struct field with given set of names, // where names is a len > 0 slice of names account for struct nesting. -func find_field(t reflect.Type, names []string) (sfield struct_field) { +func find_field(t xunsafe.TypeIter, names []string) (sfield struct_field, ftype reflect.Type) { var ( // is_exported returns whether name is exported // from a package; can be func or struct field. @@ -84,23 +85,42 @@ func find_field(t reflect.Type, names []string) (sfield struct_field) { // Pop next name. name := pop_name() - var off next_offset + var n int + rtype := t.Type + flags := t.Flag - // Dereference any ptrs to struct. - for t.Kind() == reflect.Pointer { - t = t.Elem() - off.derefs++ + // Iteratively dereference pointer types. + for rtype.Kind() == reflect.Pointer { + + // If this actual indirect memory, + // increase dereferences counter. + if flags&xunsafe.Reflect_flagIndir != 0 { + n++ + } + + // Get next elem type. + rtype = rtype.Elem() + + // Get next set of dereferenced element type flags. + flags = xunsafe.ReflectPointerElemFlags(flags, rtype) + + // Update type iter info. + t = t.Child(rtype, flags) } // Check for valid struct type. - if t.Kind() != reflect.Struct { - panic(fmt.Sprintf("field %s is not struct (or ptr-to): %s", t, name)) + if rtype.Kind() != reflect.Struct { + panic(fmt.Sprintf("field %s is not struct (or ptr-to): %s", rtype, name)) } + // Set offset info. + var off next_offset + off.derefs = n + var ok bool - // Look for next field by name. - field, ok = t.FieldByName(name) + // Look for the next field by name. + field, ok = rtype.FieldByName(name) if !ok { panic(fmt.Sprintf("unknown field: %s", name)) } @@ -109,24 +129,29 @@ func find_field(t reflect.Type, names []string) (sfield struct_field) { off.offset = field.Offset sfield.offsets = append(sfield.offsets, off) - // Set the next type. - t = field.Type + // Calculate value flags, and set next nested field type. + flags = xunsafe.ReflectStructFieldFlags(t.Flag, field.Type) + t = t.Child(field.Type, flags) } - // Check if ptr-like in-memory. - sfield.likeptr = like_ptr(t) + // Set final field type. + ftype = t.TypeInfo.Type - // Set final type. - sfield.rtype = t - - // Find mangler for field type. + // Get mangler from type info. sfield.mangle = mangler.Get(t) - // Get new zero value data ptr. - v := reflect.New(t).Elem() - zptr := eface_data(v.Interface()) - zstr := sfield.mangle(nil, zptr) - sfield.zerostr = string(zstr) + // Get field type as zero interface. + v := reflect.New(t.Type).Elem() + vi := v.Interface() + + // Get argument mangler from iface. + ti := xunsafe.TypeIterFrom(vi) + mangleArg := mangler.Get(ti) + + // Calculate zero value string. + zptr := xunsafe.UnpackEface(vi) + zstr := string(mangleArg(nil, zptr)) + sfield.zerostr = zstr sfield.zero = zptr return @@ -158,11 +183,6 @@ func extract_fields(ptr unsafe.Pointer, fields []struct_field) []unsafe.Pointer offset.offset) } - if field.likeptr && fptr != nil { - // Further dereference value ptr. - fptr = *(*unsafe.Pointer)(fptr) - } - if fptr == nil { // Use zero value. fptr = field.zero @@ -179,26 +199,26 @@ func extract_fields(ptr unsafe.Pointer, fields []struct_field) []unsafe.Pointer // information about a primary key struct's // field member, including memory offset. type pkey_field struct { - rtype reflect.Type + + // zero value data, used when + // nil encountered during ptr + // offset following. + zero unsafe.Pointer // 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 + break } // Jump forward by offset to next ptr. @@ -206,43 +226,16 @@ func extract_pkey(ptr unsafe.Pointer, field pkey_field) unsafe.Pointer { offset.offset) } - if field.likeptr && ptr != nil { - // Further dereference value ptr. - ptr = *(*unsafe.Pointer)(ptr) + if ptr == nil { + // Use zero value. + ptr = field.zero } 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 { - 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, - reflect.Map, - reflect.Chan, - reflect.Func: - return true - } - return false -} - // deref will dereference ptr 'n' times (or until nil). -func deref(p unsafe.Pointer, n uint) unsafe.Pointer { +func deref(p unsafe.Pointer, n int) unsafe.Pointer { for ; n > 0; n-- { if p == nil { return nil @@ -252,24 +245,16 @@ func deref(p unsafe.Pointer, n uint) unsafe.Pointer { return p } -// eface_data returns the data ptr from an empty interface. -func eface_data(a any) unsafe.Pointer { - type eface struct{ _, data unsafe.Pointer } - return (*eface)(unsafe.Pointer(&a)).data -} - // assert can be called to indicated a block // of code should not be able to be reached, // it returns a BUG report with callsite. -// -//go:noinline func assert(assert string) string { pcs := make([]uintptr, 1) _ = runtime.Callers(2, pcs) - fn := runtime.FuncForPC(pcs[0]) funcname := "go-structr" // by default use just our library name - if fn != nil { - funcname = fn.Name() + if frames := runtime.CallersFrames(pcs); frames != nil { + frame, _ := frames.Next() + funcname = frame.Function if i := strings.LastIndexByte(funcname, '/'); i != -1 { funcname = funcname[i+1:] } diff --git a/vendor/codeberg.org/gruf/go-structr/timeline.go b/vendor/codeberg.org/gruf/go-structr/timeline.go index 0014e69ee..1e9703fca 100644 --- a/vendor/codeberg.org/gruf/go-structr/timeline.go +++ b/vendor/codeberg.org/gruf/go-structr/timeline.go @@ -5,6 +5,7 @@ import ( "os" "reflect" "slices" + "strings" "sync" "unsafe" ) @@ -89,7 +90,7 @@ type Timeline[StructType any, PK cmp.Ordered] struct { // Init initializes the timeline with given configuration // including struct fields to index, and necessary fns. func (t *Timeline[T, PK]) Init(config TimelineConfig[T, PK]) { - rt := reflect.TypeOf((*T)(nil)).Elem() + ti := get_type_iter[T]() if len(config.Indices) == 0 { panic("no indices provided") @@ -99,6 +100,17 @@ func (t *Timeline[T, PK]) Init(config TimelineConfig[T, PK]) { panic("copy function must be provided") } + if strings.Contains(config.PKey.Fields, ",") { + panic("primary key must contain only 1 field") + } + + // Verify primary key parameter type is correct. + names := strings.Split(config.PKey.Fields, ".") + if _, ftype := find_field(ti, names); // + ftype != reflect.TypeFor[PK]() { + panic("primary key field path and generic parameter type do not match") + } + // Safely copy over // provided config. t.mutex.Lock() @@ -108,21 +120,17 @@ func (t *Timeline[T, PK]) Init(config TimelineConfig[T, PK]) { // other indices are created as expected. t.indices = make([]Index, len(config.Indices)+1) t.indices[0].ptr = unsafe.Pointer(t) - t.indices[0].init(rt, config.PKey, 0) - if len(t.indices[0].fields) > 1 { - panic("primary key must contain only 1 field") - } + t.indices[0].init(ti, config.PKey, 0) for i, cfg := range config.Indices { t.indices[i+1].ptr = unsafe.Pointer(t) - t.indices[i+1].init(rt, cfg, 0) + t.indices[i+1].init(ti, cfg, 0) } // Extract pkey details from index. field := t.indices[0].fields[0] t.pkey = pkey_field{ - rtype: field.rtype, + zero: field.zero, offsets: field.offsets, - likeptr: field.likeptr, } // Copy over remaining. @@ -220,15 +228,7 @@ func (t *Timeline[T, PK]) Insert(values ...T) int { // Extract primary key from vptr. kptr := extract_pkey(vptr, t.pkey) - - var pkey PK - if kptr != nil { - // Cast as PK type. - pkey = *(*PK)(kptr) - } else { - // Use zero value pointer. - kptr = unsafe.Pointer(&pkey) - } + pkey := *(*PK)(kptr) // Append wrapped value to slice with // the acquire pointers and primary key. @@ -241,10 +241,8 @@ func (t *Timeline[T, PK]) Insert(values ...T) int { } } - var last *list_elem - // BEFORE inserting the prepared slice of value copies w/ primary - // keys, sort them by their primary key, ascending. This permits + // keys, sort them by their primary key, descending. This permits // us to re-use the 'last' timeline position as next insert cursor. // Otherwise we would have to iterate from 'head' every single time. slices.SortFunc(with_keys, func(a, b value_with_pk[T, PK]) int { @@ -259,6 +257,8 @@ func (t *Timeline[T, PK]) Insert(values ...T) int { } }) + var last *list_elem + // Store each value in the timeline, // updating the last used list element // each time so we don't have to iter @@ -1071,7 +1071,7 @@ indexing: } func (t *Timeline[T, PK]) delete(i *timeline_item) { - for len(i.indexed) != 0 { + for len(i.indexed) > 0 { // Pop last indexed entry from list. entry := i.indexed[len(i.indexed)-1] i.indexed[len(i.indexed)-1] = nil @@ -1126,9 +1126,9 @@ func from_timeline_item(item *timeline_item) *indexed_item { func to_timeline_item(item *indexed_item) *timeline_item { to := (*timeline_item)(unsafe.Pointer(item)) if to.ck != ^uint(0) { - // ensure check bits are set indicating + // ensure check bits set, indicating // it was a timeline_item originally. - panic(assert("check bits are set")) + panic(assert("t.ck = ^uint(0)")) } return to } diff --git a/vendor/codeberg.org/gruf/go-structr/util.go b/vendor/codeberg.org/gruf/go-structr/util.go deleted file mode 100644 index 46535fcff..000000000 --- a/vendor/codeberg.org/gruf/go-structr/util.go +++ /dev/null @@ -1,13 +0,0 @@ -package structr - -// once only executes 'fn' once. -func once(fn func()) func() { - var once int32 - return func() { - if once != 0 { - return - } - once = 1 - fn() - } -} diff --git a/vendor/github.com/tomnomnom/linkheader/.travis.yml b/vendor/github.com/tomnomnom/linkheader/.travis.yml deleted file mode 100644 index cfda08659..000000000 --- a/vendor/github.com/tomnomnom/linkheader/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: go - -go: - - 1.6 - - 1.7 - - tip diff --git a/vendor/modules.txt b/vendor/modules.txt index 466c264fb..20a6b48e7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -221,7 +221,7 @@ code.superseriousbusiness.org/oauth2/v4/server # codeberg.org/gruf/go-bitutil v1.1.0 ## explicit; go 1.19 codeberg.org/gruf/go-bitutil -# codeberg.org/gruf/go-bytesize v1.0.3 +# codeberg.org/gruf/go-bytesize v1.0.4 ## explicit; go 1.17 codeberg.org/gruf/go-bytesize # codeberg.org/gruf/go-byteutil v1.3.0 @@ -247,7 +247,7 @@ codeberg.org/gruf/go-fastcopy # codeberg.org/gruf/go-fastpath/v2 v2.0.0 ## explicit; go 1.14 codeberg.org/gruf/go-fastpath/v2 -# codeberg.org/gruf/go-ffmpreg v0.6.8 +# codeberg.org/gruf/go-ffmpreg v0.6.9 ## explicit; go 1.22.0 codeberg.org/gruf/go-ffmpreg/embed codeberg.org/gruf/go-ffmpreg/wasm @@ -258,23 +258,23 @@ codeberg.org/gruf/go-iotools ## explicit; go 1.20 codeberg.org/gruf/go-kv codeberg.org/gruf/go-kv/format -# codeberg.org/gruf/go-kv/v2 v2.0.6 +# codeberg.org/gruf/go-kv/v2 v2.0.7 ## explicit; go 1.24.5 codeberg.org/gruf/go-kv/v2 codeberg.org/gruf/go-kv/v2/format # codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f ## explicit; go 1.21.3 codeberg.org/gruf/go-list -# codeberg.org/gruf/go-mangler v1.4.4 -## explicit; go 1.19 -codeberg.org/gruf/go-mangler +# codeberg.org/gruf/go-mangler/v2 v2.0.6 +## explicit; go 1.24.5 +codeberg.org/gruf/go-mangler/v2 # codeberg.org/gruf/go-maps v1.0.4 ## explicit; go 1.20 codeberg.org/gruf/go-maps # codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 ## explicit; go 1.22.2 codeberg.org/gruf/go-mempool -# codeberg.org/gruf/go-mutexes v1.5.2 +# codeberg.org/gruf/go-mutexes v1.5.3 ## explicit; go 1.22.2 codeberg.org/gruf/go-mutexes # codeberg.org/gruf/go-runners v1.6.3 @@ -293,8 +293,8 @@ codeberg.org/gruf/go-storage/disk codeberg.org/gruf/go-storage/internal codeberg.org/gruf/go-storage/memory codeberg.org/gruf/go-storage/s3 -# codeberg.org/gruf/go-structr v0.9.7 -## explicit; go 1.22 +# codeberg.org/gruf/go-structr v0.9.8 +## explicit; go 1.24.5 codeberg.org/gruf/go-structr # codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73 ## explicit; go 1.24.5 @@ -916,8 +916,8 @@ github.com/tinylib/msgp/msgp # github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc ## explicit github.com/tmthrgd/go-hex -# github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 -## explicit +# github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e +## explicit; go 1.13 github.com/tomnomnom/linkheader # github.com/toqueteos/webbrowser v1.2.0 ## explicit; go 1.12