diff --git a/go.mod b/go.mod index 6e96fa770..29540fe24 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( codeberg.org/gruf/go-runners v1.6.3 codeberg.org/gruf/go-sched v1.2.4 codeberg.org/gruf/go-storage v0.2.0 - codeberg.org/gruf/go-structr v0.8.9 + codeberg.org/gruf/go-structr v0.8.10 codeberg.org/superseriousbusiness/exif-terminator v0.9.0 github.com/DmitriyVTitov/size v1.5.0 github.com/KimMachineGun/automemlimit v0.6.1 diff --git a/go.sum b/go.sum index 864a662a5..b9f83b0ee 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,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-storage v0.2.0 h1:mKj3Lx6AavEkuXXtxqPhdq+akW9YwrnP16yQBF7K5ZI= codeberg.org/gruf/go-storage v0.2.0/go.mod h1:o3GzMDE5QNUaRnm/daUzFqvuAaC4utlgXDXYO79sWKU= -codeberg.org/gruf/go-structr v0.8.9 h1:OyiSspWYCeJOm356fFPd+bDRumPrard2VAUXAPqZiJ0= -codeberg.org/gruf/go-structr v0.8.9/go.mod h1:zkoXVrAnKosh8VFAsbP/Hhs8FmLBjbVVy5w/Ngm8ApM= +codeberg.org/gruf/go-structr v0.8.10 h1:uSapW97/StRnYEhCtycaM0isCsEMYC+tx/knYr6SiVo= +codeberg.org/gruf/go-structr v0.8.10/go.mod h1:zkoXVrAnKosh8VFAsbP/Hhs8FmLBjbVVy5w/Ngm8ApM= codeberg.org/superseriousbusiness/exif-terminator v0.9.0 h1:/EfyGI6HIrbkhFwgXGSjZ9o1kr/+k8v4mKdfXTH02Go= codeberg.org/superseriousbusiness/exif-terminator v0.9.0/go.mod h1:gCWKduudUWFzsnixoMzu0FYVdxHWG+AbXnZ50DqxsUE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= diff --git a/internal/api/client/accounts/statuses_test.go b/internal/api/client/accounts/statuses_test.go index 2c32e6571..f7a304967 100644 --- a/internal/api/client/accounts/statuses_test.go +++ b/internal/api/client/accounts/statuses_test.go @@ -73,7 +73,7 @@ func (suite *AccountStatusesTestSuite) TestGetStatusesPublicOnly() { suite.Equal(apimodel.VisibilityPublic, s.Visibility) } - suite.Equal(`; rel="next", ; rel="prev"`, result.Header.Get("link")) + suite.Equal(`; rel="next", ; rel="prev"`, result.Header.Get("link")) } func (suite *AccountStatusesTestSuite) TestGetStatusesPublicOnlyMediaOnly() { diff --git a/internal/api/client/statuses/statusboost_test.go b/internal/api/client/statuses/statusboost_test.go index 8642ba7aa..1f92d8b3f 100644 --- a/internal/api/client/statuses/statusboost_test.go +++ b/internal/api/client/statuses/statusboost_test.go @@ -591,7 +591,7 @@ func (suite *StatusBoostTestSuite) TestPostBoostImplicitAccept() { "text": "Hi @1happyturtle, can I reply?", "uri": "http://localhost:8080/some/determinate/url", "url": "http://localhost:8080/some/determinate/url", - "visibility": "unlisted" + "visibility": "public" }, "reblogged": true, "reblogs_count": 0, @@ -601,7 +601,7 @@ func (suite *StatusBoostTestSuite) TestPostBoostImplicitAccept() { "tags": [], "uri": "http://localhost:8080/some/determinate/url", "url": "http://localhost:8080/some/determinate/url", - "visibility": "unlisted" + "visibility": "public" }`, out) // Target status should no diff --git a/internal/api/client/statuses/statusfave_test.go b/internal/api/client/statuses/statusfave_test.go index fdc8741c7..bd81c0cf9 100644 --- a/internal/api/client/statuses/statusfave_test.go +++ b/internal/api/client/statuses/statusfave_test.go @@ -27,6 +27,7 @@ import ( "github.com/gin-gonic/gin" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses" + "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" @@ -185,13 +186,24 @@ func (suite *StatusFaveTestSuite) TestPostUnfaveable() { // Fave a status that's pending approval by us. func (suite *StatusFaveTestSuite) TestPostFaveImplicitAccept() { var ( + ctx = context.Background() targetStatus = suite.testStatuses["admin_account_status_5"] app = suite.testApplications["application_1"] token = suite.testTokens["local_account_2"] user = suite.testUsers["local_account_2"] account = suite.testAccounts["local_account_2"] + visFilter = visibility.NewFilter(&suite.state) ) + // Check visibility of status to public before posting fave. + visible, err := visFilter.StatusVisible(ctx, nil, targetStatus) + if err != nil { + suite.FailNow(err.Error()) + } + if visible { + suite.FailNow("status should not be visible yet") + } + out, recorder := suite.postStatusFave( targetStatus.ID, app, @@ -268,30 +280,40 @@ func (suite *StatusFaveTestSuite) TestPostFaveImplicitAccept() { "text": "Hi @1happyturtle, can I reply?", "uri": "http://localhost:8080/some/determinate/url", "url": "http://localhost:8080/some/determinate/url", - "visibility": "unlisted" + "visibility": "public" }`, out) // Target status should no // longer be pending approval. dbStatus, err := suite.state.DB.GetStatusByID( - context.Background(), + ctx, targetStatus.ID, ) if err != nil { suite.FailNow(err.Error()) } suite.False(*dbStatus.PendingApproval) + suite.NotEmpty(dbStatus.ApprovedByURI) // There should be an Accept // stored for the target status. intReq, err := suite.state.DB.GetInteractionRequestByInteractionURI( - context.Background(), targetStatus.URI, + ctx, targetStatus.URI, ) if err != nil { suite.FailNow(err.Error()) } suite.NotZero(intReq.AcceptedAt) suite.NotEmpty(intReq.URI) + + // Check visibility of status to public after posting fave. + visible, err = visFilter.StatusVisible(ctx, nil, dbStatus) + if err != nil { + suite.FailNow(err.Error()) + } + if !visible { + suite.FailNow("status should be visible") + } } func TestStatusFaveTestSuite(t *testing.T) { diff --git a/internal/cache/util.go b/internal/cache/util.go index 924869aad..fde2f9ada 100644 --- a/internal/cache/util.go +++ b/internal/cache/util.go @@ -18,7 +18,6 @@ package cache import ( - "database/sql" "errors" "time" @@ -42,7 +41,6 @@ func ignoreErrors(err error) bool { // (until invalidation). db.ErrNoEntries, db.ErrAlreadyExists, - sql.ErrNoRows, ) } diff --git a/internal/cache/visibility.go b/internal/cache/visibility.go index e280054ec..a424ca5ac 100644 --- a/internal/cache/visibility.go +++ b/internal/cache/visibility.go @@ -48,9 +48,15 @@ func (c *Caches) initVisibility() { {Fields: "RequesterID", Multiple: true}, {Fields: "Type,RequesterID,ItemID"}, }, - MaxSize: cap, - IgnoreErr: ignoreErrors, - Copy: copyF, + MaxSize: cap, + IgnoreErr: func(err error) bool { + // don't cache any errors, + // it gets a little too tricky + // otherwise with ensuring + // errors are cleared out + return true + }, + Copy: copyF, }) } diff --git a/internal/db/bundb/interaction.go b/internal/db/bundb/interaction.go index 78abcc763..88a044b6f 100644 --- a/internal/db/bundb/interaction.go +++ b/internal/db/bundb/interaction.go @@ -26,8 +26,10 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/paging" "github.com/superseriousbusiness/gotosocial/internal/state" + "github.com/superseriousbusiness/gotosocial/internal/util" "github.com/uptrace/bun" ) @@ -84,6 +86,53 @@ func (i *interactionDB) GetInteractionRequestByURI(ctx context.Context, uri stri ) } +func (i *interactionDB) GetInteractionRequestsByIDs(ctx context.Context, ids []string) ([]*gtsmodel.InteractionRequest, error) { + // Load all interaction request IDs via cache loader callbacks. + requests, err := i.state.Caches.DB.InteractionRequest.LoadIDs("ID", + ids, + func(uncached []string) ([]*gtsmodel.InteractionRequest, error) { + // Preallocate expected length of uncached interaction requests. + requests := make([]*gtsmodel.InteractionRequest, 0, len(uncached)) + + // Perform database query scanning + // the remaining (uncached) IDs. + if err := i.db.NewSelect(). + Model(&requests). + Where("? IN (?)", bun.Ident("id"), bun.In(uncached)). + Scan(ctx); err != nil { + return nil, err + } + + return requests, nil + }, + ) + if err != nil { + return nil, err + } + + // Reorder the requests by their + // IDs to ensure in correct order. + getID := func(r *gtsmodel.InteractionRequest) string { return r.ID } + util.OrderBy(requests, ids, getID) + + if gtscontext.Barebones(ctx) { + // no need to fully populate. + return requests, nil + } + + // Populate all loaded interaction requests, removing those we + // fail to populate (removes needing so many nil checks everywhere). + requests = slices.DeleteFunc(requests, func(request *gtsmodel.InteractionRequest) bool { + if err := i.PopulateInteractionRequest(ctx, request); err != nil { + log.Errorf(ctx, "error populating %s: %v", request.ID, err) + return true + } + return false + }) + + return requests, nil +} + func (i *interactionDB) getInteractionRequest( ctx context.Context, lookup string, @@ -205,13 +254,18 @@ func (i *interactionDB) UpdateInteractionRequest(ctx context.Context, request *g } func (i *interactionDB) DeleteInteractionRequestByID(ctx context.Context, id string) error { - defer i.state.Caches.DB.InteractionRequest.Invalidate("ID", id) + // Delete interaction request by ID. + if _, err := i.db.NewDelete(). + Table("interaction_requests"). + Where("? = ?", bun.Ident("id"), id). + Exec(ctx); err != nil { + return err + } - _, err := i.db.NewDelete(). - TableExpr("? AS ?", bun.Ident("interaction_requests"), bun.Ident("interaction_request")). - Where("? = ?", bun.Ident("interaction_request.id"), id). - Exec(ctx) - return err + // Invalidate cached interaction request with ID. + i.state.Caches.DB.InteractionRequest.Invalidate("ID", id) + + return nil } func (i *interactionDB) GetInteractionsRequestsForAcct( @@ -317,19 +371,8 @@ func (i *interactionDB) GetInteractionsRequestsForAcct( slices.Reverse(reqIDs) } - // For each interaction request ID, - // select the interaction request. - reqs := make([]*gtsmodel.InteractionRequest, 0, len(reqIDs)) - for _, id := range reqIDs { - req, err := i.GetInteractionRequestByID(ctx, id) - if err != nil { - return nil, err - } - - reqs = append(reqs, req) - } - - return reqs, nil + // Load all interaction requests by their IDs. + return i.GetInteractionRequestsByIDs(ctx, reqIDs) } func (i *interactionDB) IsInteractionRejected(ctx context.Context, interactionURI string) (bool, error) { diff --git a/internal/db/bundb/timeline_test.go b/internal/db/bundb/timeline_test.go index 50747b50d..00df2b3a6 100644 --- a/internal/db/bundb/timeline_test.go +++ b/internal/db/bundb/timeline_test.go @@ -74,7 +74,8 @@ func (suite *TimelineTestSuite) publicCount() int { var publicCount int for _, status := range suite.testStatuses { if status.Visibility == gtsmodel.VisibilityPublic && - status.BoostOfID == "" { + status.BoostOfID == "" && + !util.PtrOrZero(status.PendingApproval) { publicCount++ } } diff --git a/internal/filter/visibility/status_test.go b/internal/filter/visibility/status_test.go index 9b210e500..795441e7f 100644 --- a/internal/filter/visibility/status_test.go +++ b/internal/filter/visibility/status_test.go @@ -168,6 +168,9 @@ func (suite *StatusVisibleTestSuite) TestVisiblePending() { testStatus := new(gtsmodel.Status) *testStatus = *suite.testStatuses["admin_account_status_3"] testStatus.PendingApproval = util.Ptr(true) + if err := suite.state.DB.UpdateStatus(ctx, testStatus); err != nil { + suite.FailNow(err.Error()) + } for _, testCase := range []struct { acct *gtsmodel.Account @@ -198,6 +201,43 @@ func (suite *StatusVisibleTestSuite) TestVisiblePending() { suite.NoError(err) suite.Equal(testCase.visible, visible) } + + // Update the status to mark it as approved. + testStatus.PendingApproval = util.Ptr(false) + testStatus.ApprovedByURI = "http://localhost:8080/some/accept/uri" + if err := suite.state.DB.UpdateStatus(ctx, testStatus); err != nil { + suite.FailNow(err.Error()) + } + + for _, testCase := range []struct { + acct *gtsmodel.Account + visible bool + }{ + { + acct: suite.testAccounts["admin_account"], + visible: true, // Own status, always visible. + }, + { + acct: suite.testAccounts["local_account_1"], + visible: true, // Reply to zork, always visible. + }, + { + acct: suite.testAccounts["local_account_2"], + visible: true, // Should be visible now. + }, + { + acct: suite.testAccounts["remote_account_1"], + visible: true, // Should be visible now. + }, + { + acct: nil, // Unauthed request. + visible: true, // Should be visible now (public status). + }, + } { + visible, err := suite.filter.StatusVisible(ctx, testCase.acct, testStatus) + suite.NoError(err) + suite.Equal(testCase.visible, visible) + } } func (suite *StatusVisibleTestSuite) TestVisibleLocalOnly() { diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index 61faf1c21..57c401e6f 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -1744,7 +1744,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToAPIStatusPendingApproval() "in_reply_to_account_id": "01F8MH5NBDF2MV7CTC4Q5128HF", "sensitive": false, "spoiler_text": "", - "visibility": "unlisted", + "visibility": "public", "language": null, "uri": "http://localhost:8080/users/admin/statuses/01J5QVB9VC76NPPRQ207GG4DRZ", "url": "http://localhost:8080/@admin/statuses/01J5QVB9VC76NPPRQ207GG4DRZ", @@ -3177,7 +3177,7 @@ func (suite *InternalToFrontendTestSuite) TestIntReqToAPI() { "in_reply_to_account_id": null, "sensitive": true, "spoiler_text": "you won't be able to reply to this without my approval", - "visibility": "unlisted", + "visibility": "public", "language": "en", "uri": "http://localhost:8080/users/1happyturtle/statuses/01F8MHC8VWDRBQR0N1BATDDEM5", "url": "http://localhost:8080/@1happyturtle/statuses/01F8MHC8VWDRBQR0N1BATDDEM5", @@ -3269,7 +3269,7 @@ func (suite *InternalToFrontendTestSuite) TestIntReqToAPI() { "in_reply_to_account_id": "01F8MH5NBDF2MV7CTC4Q5128HF", "sensitive": false, "spoiler_text": "", - "visibility": "unlisted", + "visibility": "public", "language": null, "uri": "http://localhost:8080/users/admin/statuses/01J5QVB9VC76NPPRQ207GG4DRZ", "url": "http://localhost:8080/@admin/statuses/01J5QVB9VC76NPPRQ207GG4DRZ", diff --git a/testrig/testmodels.go b/testrig/testmodels.go index ea3d96a04..47457393a 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -1531,7 +1531,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status { BoostOfID: "", BoostOfAccountID: "", ThreadID: "01HCWE4P0EW9HBA5WHW97D5YV0", - Visibility: gtsmodel.VisibilityUnlocked, + Visibility: gtsmodel.VisibilityPublic, Sensitive: util.Ptr(false), CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F", Federated: util.Ptr(true), @@ -1811,7 +1811,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status { BoostOfID: "", ThreadID: "01HCWE4P0EW9HBA5WHW97D5YV0", ContentWarning: "you won't be able to reply to this without my approval", - Visibility: gtsmodel.VisibilityUnlocked, + Visibility: gtsmodel.VisibilityPublic, Sensitive: util.Ptr(true), Language: "en", CreatedWithApplicationID: "01F8MGYG9E893WRHW0TAEXR8GJ", diff --git a/vendor/codeberg.org/gruf/go-structr/cache.go b/vendor/codeberg.org/gruf/go-structr/cache.go index e73db58f8..4bea32c89 100644 --- a/vendor/codeberg.org/gruf/go-structr/cache.go +++ b/vendor/codeberg.org/gruf/go-structr/cache.go @@ -575,8 +575,9 @@ func (c *Cache[T]) store_value(index *Index, key string, value T) { item.data = value if index != nil { - // Append item to index. - index.append(key, item) + // Append item to index a key + // was already generated for. + index.append(&c.lru, key, item) } // Get ptr to value data. @@ -607,8 +608,8 @@ func (c *Cache[T]) store_value(index *Index, key string, value T) { continue } - // Append item to index. - idx.append(key, item) + // Append item to this index. + idx.append(&c.lru, key, item) } // Add item to main lru list. @@ -645,8 +646,9 @@ func (c *Cache[T]) store_error(index *Index, key string, err error) { // Set error val. item.data = err - // Append item to index. - index.append(key, item) + // Append item to index a key + // was already generated for. + index.append(&c.lru, key, item) // Add item to main lru list. c.lru.push_front(&item.elem) diff --git a/vendor/codeberg.org/gruf/go-structr/index.go b/vendor/codeberg.org/gruf/go-structr/index.go index 8605cdb25..127bddb1f 100644 --- a/vendor/codeberg.org/gruf/go-structr/index.go +++ b/vendor/codeberg.org/gruf/go-structr/index.go @@ -174,7 +174,7 @@ func (i *Index) init(t reflect.Type, cfg IndexConfig, cap int) { // get_one will fetch one indexed item under key. func (i *Index) get_one(key Key) *indexed_item { // Get list at hash. - l, _ := i.data.Get(key.key) + l := i.data.Get(key.key) if l == nil { return nil } @@ -192,7 +192,7 @@ func (i *Index) get(key string, hook func(*indexed_item)) { } // Get list at hash. - l, _ := i.data.Get(key) + l := i.data.Get(key) if l == nil { return } @@ -237,11 +237,12 @@ func (i *Index) key(buf *byteutil.Buffer, parts []unsafe.Pointer) string { } // append will append the given index entry to appropriate -// doubly-linked-list in index hashmap. this handles case -// of key collisions and overwriting 'unique' entries. -func (i *Index) append(key string, item *indexed_item) { +// doubly-linked-list in index hashmap. this handles case of +// overwriting "unique" index entries, and removes from given +// outer linked-list in the case that it is no longer indexed. +func (i *Index) append(ll *list, key string, item *indexed_item) { // Look for existing. - l, _ := i.data.Get(key) + l := i.data.Get(key) if l == nil { @@ -255,12 +256,21 @@ func (i *Index) append(key string, item *indexed_item) { elem := l.head l.remove(elem) - // Drop index from inner item. + // Drop index from inner item, + // catching the evicted item. e := (*index_entry)(elem.data) - e.item.drop_index(e) + evicted := e.item + evicted.drop_index(e) // Free unused entry. free_index_entry(e) + + if len(evicted.indexed) == 0 { + // Evicted item is not indexed, + // remove from outer linked list. + ll.remove(&evicted.elem) + free_indexed_item(evicted) + } } // Prepare new index entry. @@ -283,7 +293,7 @@ func (i *Index) delete(key string, hook func(*indexed_item)) { } // Get list at hash. - l, _ := i.data.Get(key) + l := i.data.Get(key) if l == nil { return } @@ -292,10 +302,9 @@ func (i *Index) delete(key string, hook func(*indexed_item)) { i.data.Delete(key) // Iterate entries in list. - for x := 0; x < l.len; x++ { + l.rangefn(func(elem *list_elem) { - // Pop list head. - elem := l.head + // Remove elem. l.remove(elem) // Extract element entry + item. @@ -310,7 +319,7 @@ func (i *Index) delete(key string, hook func(*indexed_item)) { // Pass to hook. hook(item) - } + }) // Release list. free_list(l) @@ -319,7 +328,7 @@ func (i *Index) delete(key string, hook func(*indexed_item)) { // delete_entry deletes the given index entry. func (i *Index) delete_entry(entry *index_entry) { // Get list at hash sum. - l, _ := i.data.Get(entry.key) + l := i.data.Get(entry.key) if l == nil { return } diff --git a/vendor/codeberg.org/gruf/go-structr/item.go b/vendor/codeberg.org/gruf/go-structr/item.go index bf83f1444..3191f2beb 100644 --- a/vendor/codeberg.org/gruf/go-structr/item.go +++ b/vendor/codeberg.org/gruf/go-structr/item.go @@ -50,12 +50,9 @@ func (i *indexed_item) drop_index(entry *index_entry) { continue } - // Unset tptr value to - // ensure GC can take it. - i.indexed[x] = nil - // Move all index entries down + reslice. _ = copy(i.indexed[x:], i.indexed[x+1:]) + i.indexed[len(i.indexed)-1] = nil i.indexed = i.indexed[:len(i.indexed)-1] break } diff --git a/vendor/codeberg.org/gruf/go-structr/list.go b/vendor/codeberg.org/gruf/go-structr/list.go index 17e1899ad..764036d47 100644 --- a/vendor/codeberg.org/gruf/go-structr/list.go +++ b/vendor/codeberg.org/gruf/go-structr/list.go @@ -48,27 +48,17 @@ func free_list(list *list) { // push_front will push the given elem to front (head) of list. func (l *list) push_front(elem *list_elem) { - if l.len == 0 { - // Set new tail + head - l.head = elem - l.tail = elem - - // Link elem to itself - elem.next = elem - elem.prev = elem - } else { - oldHead := l.head + // Set new head. + oldHead := l.head + l.head = elem + if oldHead != nil { // Link to old head elem.next = oldHead oldHead.prev = elem - - // Link up to tail - elem.prev = l.tail - l.tail.next = elem - - // Set new head - l.head = elem + } else { + // First in list. + l.tail = elem } // Incr count @@ -77,27 +67,17 @@ func (l *list) push_front(elem *list_elem) { // push_back will push the given elem to back (tail) of list. func (l *list) push_back(elem *list_elem) { - if l.len == 0 { - // Set new tail + head - l.head = elem - l.tail = elem - - // Link elem to itself - elem.next = elem - elem.prev = elem - } else { - oldTail := l.tail + // Set new tail. + oldTail := l.tail + l.tail = elem + if oldTail != nil { // Link to old tail elem.prev = oldTail oldTail.next = elem - - // Link up to head - elem.next = l.head - l.head.prev = elem - - // Set new tail - l.tail = elem + } else { + // First in list. + l.head = elem } // Incr count @@ -105,53 +85,57 @@ func (l *list) push_back(elem *list_elem) { } // move_front will move given elem to front (head) of list. +// if it is already at front this call is a no-op. func (l *list) move_front(elem *list_elem) { + if elem == l.head { + return + } l.remove(elem) l.push_front(elem) } -// move_back will move given elem to back (tail) of list. +// move_back will move given elem to back (tail) of list, +// if it is already at back this call is a no-op. func (l *list) move_back(elem *list_elem) { + if elem == l.tail { + return + } l.remove(elem) l.push_back(elem) } // remove will remove given elem from list. func (l *list) remove(elem *list_elem) { - if l.len <= 1 { - // Drop elem's links - elem.next = nil - elem.prev = nil - - // Only elem in list - l.head = nil - l.tail = nil - l.len = 0 - return - } - - // Get surrounding elems + // Get linked elems. next := elem.next prev := elem.prev - // Relink chain - next.prev = prev - prev.next = next - - switch elem { - // Set new head - case l.head: - l.head = next - - // Set new tail - case l.tail: - l.tail = prev - } - - // Drop elem's links + // Unset elem. elem.next = nil elem.prev = nil + switch { + // elem is ONLY one in list. + case next == nil && prev == nil: + l.head = nil + l.tail = nil + + // elem is front in list. + case next != nil && prev == nil: + l.head = next + next.prev = nil + + // elem is last in list. + case prev != nil && next == nil: + l.tail = prev + prev.next = nil + + // elem in middle of list. + default: + next.prev = prev + prev.next = next + } + // Decr count l.len-- } @@ -161,9 +145,11 @@ func (l *list) rangefn(fn func(*list_elem)) { if fn == nil { panic("nil fn") } - elem := l.head - for i := 0; i < l.len; i++ { - fn(elem) - elem = elem.next + for e := l.head; // + e != nil; // + { + n := e.next + fn(e) + e = n } } diff --git a/vendor/codeberg.org/gruf/go-structr/map.go b/vendor/codeberg.org/gruf/go-structr/map.go index a31574641..316a8e528 100644 --- a/vendor/codeberg.org/gruf/go-structr/map.go +++ b/vendor/codeberg.org/gruf/go-structr/map.go @@ -10,9 +10,8 @@ func (m *hashmap) init(cap int) { m.n = cap } -func (m *hashmap) Get(key string) (*list, bool) { - list, ok := m.m[key] - return list, ok +func (m *hashmap) Get(key string) *list { + return m.m[key] } func (m *hashmap) Put(key string, list *list) { diff --git a/vendor/codeberg.org/gruf/go-structr/queue.go b/vendor/codeberg.org/gruf/go-structr/queue.go index f48d1530c..0e4f4e3cf 100644 --- a/vendor/codeberg.org/gruf/go-structr/queue.go +++ b/vendor/codeberg.org/gruf/go-structr/queue.go @@ -308,8 +308,8 @@ func (q *Queue[T]) index(value T) *indexed_item { continue } - // Append item to index. - idx.append(key, item) + // Append item to this index. + idx.append(&q.queue, key, item) } // Done with buf. diff --git a/vendor/modules.txt b/vendor/modules.txt index a9a9cbd14..3a8c091d5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -66,7 +66,7 @@ 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.8.9 +# codeberg.org/gruf/go-structr v0.8.10 ## explicit; go 1.21 codeberg.org/gruf/go-structr # codeberg.org/superseriousbusiness/exif-terminator v0.9.0