From e3c2b790fd4329494979bd27be7fa162600f1436 Mon Sep 17 00:00:00 2001 From: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:45:19 +0000 Subject: [PATCH] [performance] minimise log field allocations (#3529) * when appending log field only do so by minimal amount * move slice utils to separate package to fix import cycle, add GrowJust() and AppendJust() functions * fix GrowJust() not returning slice of same length * improved xslices tests * make AppendJust() test check for slice contents, fix AppendJust() final copying behaviour * add a +1 with field growth to try minimise allocation for log 'msg' field --- internal/db/bundb/account.go | 3 +- internal/db/bundb/application.go | 4 +- internal/db/bundb/conversation.go | 6 +- internal/db/bundb/emoji.go | 6 +- internal/db/bundb/filter.go | 4 +- internal/db/bundb/filterkeyword.go | 4 +- internal/db/bundb/filterstatus.go | 4 +- internal/db/bundb/interaction.go | 4 +- internal/db/bundb/list.go | 10 +-- internal/db/bundb/media.go | 4 +- internal/db/bundb/mention.go | 4 +- internal/db/bundb/notification.go | 4 +- internal/db/bundb/poll.go | 4 +- internal/db/bundb/relationship_block.go | 4 +- internal/db/bundb/relationship_follow.go | 6 +- internal/db/bundb/relationship_follow_req.go | 4 +- internal/db/bundb/relationship_mute.go | 4 +- internal/db/bundb/status.go | 4 +- internal/db/bundb/statusbookmark.go | 4 +- internal/db/bundb/statusfave.go | 6 +- internal/db/bundb/tag.go | 6 +- internal/federation/federatingprotocol.go | 4 +- internal/gtsmodel/conversation.go | 4 +- internal/log/log.go | 12 +-- internal/processing/account/alias.go | 6 +- internal/processing/status/create.go | 7 +- internal/typeutils/internaltoas.go | 4 +- internal/util/{ => xslices}/slices.go | 43 +++++++++- internal/util/{ => xslices}/slices_test.go | 86 ++++++++++++++++++-- 29 files changed, 189 insertions(+), 76 deletions(-) rename internal/util/{ => xslices}/slices.go (78%) rename internal/util/{ => xslices}/slices_test.go (52%) diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go index 16c82c08f..f054b1412 100644 --- a/internal/db/bundb/account.go +++ b/internal/db/bundb/account.go @@ -36,6 +36,7 @@ "github.com/superseriousbusiness/gotosocial/internal/paging" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect" ) @@ -86,7 +87,7 @@ func(uncached []string) ([]*gtsmodel.Account, error) { // Reorder the statuses by their // IDs to ensure in correct order. getID := func(a *gtsmodel.Account) string { return a.ID } - util.OrderBy(accounts, ids, getID) + xslices.OrderBy(accounts, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/application.go b/internal/db/bundb/application.go index fda0ba602..cbba499b0 100644 --- a/internal/db/bundb/application.go +++ b/internal/db/bundb/application.go @@ -22,7 +22,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -169,7 +169,7 @@ func(uncached []string) ([]*gtsmodel.Token, error) { // Reoroder the tokens by their // IDs to ensure in correct order. getID := func(t *gtsmodel.Token) string { return t.ID } - util.OrderBy(tokens, tokenIDs, getID) + xslices.OrderBy(tokens, tokenIDs, getID) return tokens, nil } diff --git a/internal/db/bundb/conversation.go b/internal/db/bundb/conversation.go index 22ff4fd79..354463111 100644 --- a/internal/db/bundb/conversation.go +++ b/internal/db/bundb/conversation.go @@ -31,7 +31,7 @@ "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/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect" ) @@ -209,7 +209,7 @@ func(accountID string, uncached []string) ([]*gtsmodel.Conversation, error) { // Reorder the conversations by their last status IDs to ensure correct order. getID := func(b *gtsmodel.Conversation) string { return b.ID } - util.OrderBy(conversations, conversationLastStatusIDs, getID) + xslices.OrderBy(conversations, conversationLastStatusIDs, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. @@ -558,7 +558,7 @@ func (c *conversationDB) DeleteStatusFromConversations(ctx context.Context, stat // Invalidate cache entries. updatedConversationIDs = append(updatedConversationIDs, deletedConversationIDs...) - updatedConversationIDs = util.Deduplicate(updatedConversationIDs) + updatedConversationIDs = xslices.Deduplicate(updatedConversationIDs) c.state.Caches.DB.Conversation.InvalidateIDs("ID", updatedConversationIDs) return nil diff --git a/internal/db/bundb/emoji.go b/internal/db/bundb/emoji.go index db9daf0aa..ee564317e 100644 --- a/internal/db/bundb/emoji.go +++ b/internal/db/bundb/emoji.go @@ -31,7 +31,7 @@ "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/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect" ) @@ -597,7 +597,7 @@ func(uncached []string) ([]*gtsmodel.Emoji, error) { // Reorder the emojis by their // IDs to ensure in correct order. getID := func(e *gtsmodel.Emoji) string { return e.ID } - util.OrderBy(emojis, ids, getID) + xslices.OrderBy(emojis, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. @@ -661,7 +661,7 @@ func(uncached []string) ([]*gtsmodel.EmojiCategory, error) { // Reorder the categories by their // IDs to ensure in correct order. getID := func(c *gtsmodel.EmojiCategory) string { return c.ID } - util.OrderBy(categories, ids, getID) + xslices.OrderBy(categories, ids, getID) return categories, nil } diff --git a/internal/db/bundb/filter.go b/internal/db/bundb/filter.go index e68a0bcd0..fe23bb405 100644 --- a/internal/db/bundb/filter.go +++ b/internal/db/bundb/filter.go @@ -27,7 +27,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -99,7 +99,7 @@ func(uncached []string) ([]*gtsmodel.Filter, error) { } // Put the filter structs in the same order as the filter IDs. - util.OrderBy(filters, filterIDs, func(filter *gtsmodel.Filter) string { return filter.ID }) + xslices.OrderBy(filters, filterIDs, func(filter *gtsmodel.Filter) string { return filter.ID }) if gtscontext.Barebones(ctx) { return filters, nil diff --git a/internal/db/bundb/filterkeyword.go b/internal/db/bundb/filterkeyword.go index 8a006d10f..0e1d8daeb 100644 --- a/internal/db/bundb/filterkeyword.go +++ b/internal/db/bundb/filterkeyword.go @@ -26,7 +26,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -140,7 +140,7 @@ func(uncached []string) ([]*gtsmodel.FilterKeyword, error) { } // Put the filter keyword structs in the same order as the filter keyword IDs. - util.OrderBy(filterKeywords, filterKeywordIDs, func(filterKeyword *gtsmodel.FilterKeyword) string { + xslices.OrderBy(filterKeywords, filterKeywordIDs, func(filterKeyword *gtsmodel.FilterKeyword) string { return filterKeyword.ID }) diff --git a/internal/db/bundb/filterstatus.go b/internal/db/bundb/filterstatus.go index 95919bd2c..1cd924d13 100644 --- a/internal/db/bundb/filterstatus.go +++ b/internal/db/bundb/filterstatus.go @@ -25,7 +25,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -116,7 +116,7 @@ func(uncached []string) ([]*gtsmodel.FilterStatus, error) { } // Put the filter status structs in the same order as the filter status IDs. - util.OrderBy(filterStatuses, filterStatusIDs, func(filterStatus *gtsmodel.FilterStatus) string { + xslices.OrderBy(filterStatuses, filterStatusIDs, func(filterStatus *gtsmodel.FilterStatus) string { return filterStatus.ID }) diff --git a/internal/db/bundb/interaction.go b/internal/db/bundb/interaction.go index a04e97905..9fbe00711 100644 --- a/internal/db/bundb/interaction.go +++ b/internal/db/bundb/interaction.go @@ -29,7 +29,7 @@ "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/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -113,7 +113,7 @@ func(uncached []string) ([]*gtsmodel.InteractionRequest, error) { // 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) + xslices.OrderBy(requests, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/list.go b/internal/db/bundb/list.go index 03dff95e3..f81c59c42 100644 --- a/internal/db/bundb/list.go +++ b/internal/db/bundb/list.go @@ -31,7 +31,7 @@ "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/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -333,7 +333,7 @@ func(uncached []string) ([]*gtsmodel.List, error) { // Reorder the lists by their // IDs to ensure in correct order. getID := func(l *gtsmodel.List) string { return l.ID } - util.OrderBy(lists, ids, getID) + xslices.OrderBy(lists, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. @@ -387,12 +387,12 @@ func (l *listDB) PutListEntries(ctx context.Context, entries []*gtsmodel.ListEnt } // Collect unique list IDs from the provided list entries. - listIDs := util.Collate(entries, func(e *gtsmodel.ListEntry) string { + listIDs := xslices.Collate(entries, func(e *gtsmodel.ListEntry) string { return e.ListID }) // Collect unique follow IDs from the provided list entries. - followIDs := util.Collate(entries, func(e *gtsmodel.ListEntry) string { + followIDs := xslices.Collate(entries, func(e *gtsmodel.ListEntry) string { return e.FollowID }) @@ -441,7 +441,7 @@ func (l *listDB) DeleteAllListEntriesByFollows(ctx context.Context, followIDs .. } // Deduplicate IDs before invalidate. - listIDs = util.Deduplicate(listIDs) + listIDs = xslices.Deduplicate(listIDs) // Invalidate all related list entry caches. l.invalidateEntryCaches(ctx, listIDs, followIDs) diff --git a/internal/db/bundb/media.go b/internal/db/bundb/media.go index de980a16a..453ad856a 100644 --- a/internal/db/bundb/media.go +++ b/internal/db/bundb/media.go @@ -28,7 +28,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/paging" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -78,7 +78,7 @@ func(uncached []string) ([]*gtsmodel.MediaAttachment, error) { // Reorder the media by their // IDs to ensure in correct order. getID := func(m *gtsmodel.MediaAttachment) string { return m.ID } - util.OrderBy(media, ids, getID) + xslices.OrderBy(media, ids, getID) return media, nil } diff --git a/internal/db/bundb/mention.go b/internal/db/bundb/mention.go index ba8c0ba11..04aa5d76e 100644 --- a/internal/db/bundb/mention.go +++ b/internal/db/bundb/mention.go @@ -28,7 +28,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -91,7 +91,7 @@ func(uncached []string) ([]*gtsmodel.Mention, error) { // Reorder the mentions by their // IDs to ensure in correct order. getID := func(m *gtsmodel.Mention) string { return m.ID } - util.OrderBy(mentions, ids, getID) + xslices.OrderBy(mentions, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/notification.go b/internal/db/bundb/notification.go index 770e84c5c..ef2527637 100644 --- a/internal/db/bundb/notification.go +++ b/internal/db/bundb/notification.go @@ -29,7 +29,7 @@ "github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -130,7 +130,7 @@ func(uncached []string) ([]*gtsmodel.Notification, error) { // Reorder the notifs by their // IDs to ensure in correct order. getID := func(n *gtsmodel.Notification) string { return n.ID } - util.OrderBy(notifs, ids, getID) + xslices.OrderBy(notifs, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/poll.go b/internal/db/bundb/poll.go index f5c33ce9b..b9384774b 100644 --- a/internal/db/bundb/poll.go +++ b/internal/db/bundb/poll.go @@ -29,7 +29,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -315,7 +315,7 @@ func(uncached []string) ([]*gtsmodel.PollVote, error) { // Reorder the poll votes by their // IDs to ensure in correct order. getID := func(v *gtsmodel.PollVote) string { return v.ID } - util.OrderBy(votes, voteIDs, getID) + xslices.OrderBy(votes, voteIDs, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/relationship_block.go b/internal/db/bundb/relationship_block.go index 9738970e5..9578b0e3e 100644 --- a/internal/db/bundb/relationship_block.go +++ b/internal/db/bundb/relationship_block.go @@ -27,7 +27,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -127,7 +127,7 @@ func(uncached []string) ([]*gtsmodel.Block, error) { // Reorder the blocks by their // IDs to ensure in correct order. getID := func(b *gtsmodel.Block) string { return b.ID } - util.OrderBy(blocks, ids, getID) + xslices.OrderBy(blocks, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/relationship_follow.go b/internal/db/bundb/relationship_follow.go index 042d12f37..aea36f39c 100644 --- a/internal/db/bundb/relationship_follow.go +++ b/internal/db/bundb/relationship_follow.go @@ -28,7 +28,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -103,7 +103,7 @@ func(uncached []string) ([]*gtsmodel.Follow, error) { // Reorder the follows by their // IDs to ensure in correct order. getID := func(f *gtsmodel.Follow) string { return f.ID } - util.OrderBy(follows, ids, getID) + xslices.OrderBy(follows, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. @@ -376,7 +376,7 @@ func (r *relationshipDB) DeleteAccountFollows(ctx context.Context, accountID str } // Gather the follow IDs that were deleted for removing related list entries. - followIDs := util.Gather(nil, deleted, func(follow *gtsmodel.Follow) string { + followIDs := xslices.Gather(nil, deleted, func(follow *gtsmodel.Follow) string { return follow.ID }) diff --git a/internal/db/bundb/relationship_follow_req.go b/internal/db/bundb/relationship_follow_req.go index fc0ca5c0a..030c99c58 100644 --- a/internal/db/bundb/relationship_follow_req.go +++ b/internal/db/bundb/relationship_follow_req.go @@ -28,7 +28,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -103,7 +103,7 @@ func(uncached []string) ([]*gtsmodel.FollowRequest, error) { // Reorder the requests by their // IDs to ensure in correct order. getID := func(f *gtsmodel.FollowRequest) string { return f.ID } - util.OrderBy(follows, ids, getID) + xslices.OrderBy(follows, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/relationship_mute.go b/internal/db/bundb/relationship_mute.go index 37d97a64f..b7b7e109e 100644 --- a/internal/db/bundb/relationship_mute.go +++ b/internal/db/bundb/relationship_mute.go @@ -28,7 +28,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/paging" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect" ) @@ -109,7 +109,7 @@ func(uncached []string) ([]*gtsmodel.UserMute, error) { // Reorder the mutes by their // IDs to ensure in correct order. getID := func(b *gtsmodel.UserMute) string { return b.ID } - util.OrderBy(mutes, ids, getID) + xslices.OrderBy(mutes, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/status.go b/internal/db/bundb/status.go index 5340b63cd..45e9864a3 100644 --- a/internal/db/bundb/status.go +++ b/internal/db/bundb/status.go @@ -29,7 +29,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -76,7 +76,7 @@ func(uncached []string) ([]*gtsmodel.Status, error) { // Reorder the statuses by their // IDs to ensure in correct order. getID := func(s *gtsmodel.Status) string { return s.ID } - util.OrderBy(statuses, ids, getID) + xslices.OrderBy(statuses, ids, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. diff --git a/internal/db/bundb/statusbookmark.go b/internal/db/bundb/statusbookmark.go index 1534050da..6cbd7f583 100644 --- a/internal/db/bundb/statusbookmark.go +++ b/internal/db/bundb/statusbookmark.go @@ -28,7 +28,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -95,7 +95,7 @@ func(uncached []string) ([]*gtsmodel.StatusBookmark, error) { // Reorder the bookmarks by their // IDs to ensure in correct order. getID := func(b *gtsmodel.StatusBookmark) string { return b.ID } - util.OrderBy(bookmarks, ids, getID) + xslices.OrderBy(bookmarks, ids, getID) // Populate all loaded bookmarks, removing those we fail // to populate (removes needing so many later nil checks). diff --git a/internal/db/bundb/statusfave.go b/internal/db/bundb/statusfave.go index cf20fbba3..c1fa375aa 100644 --- a/internal/db/bundb/statusfave.go +++ b/internal/db/bundb/statusfave.go @@ -31,7 +31,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -155,7 +155,7 @@ func(uncached []string) ([]*gtsmodel.StatusFave, error) { // Reorder the statuses by their // IDs to ensure in correct order. getID := func(f *gtsmodel.StatusFave) string { return f.ID } - util.OrderBy(faves, faveIDs, getID) + xslices.OrderBy(faves, faveIDs, getID) if gtscontext.Barebones(ctx) { // no need to fully populate. @@ -339,7 +339,7 @@ func (s *statusFaveDB) DeleteStatusFaves(ctx context.Context, targetAccountID st } // Deduplicate determined status IDs. - statusIDs = util.Deduplicate(statusIDs) + statusIDs = xslices.Deduplicate(statusIDs) // Invalidate any cached status faves for this status ID. s.state.Caches.DB.StatusFave.InvalidateIDs("ID", statusIDs) diff --git a/internal/db/bundb/tag.go b/internal/db/bundb/tag.go index 6c3d870f6..dfb80e829 100644 --- a/internal/db/bundb/tag.go +++ b/internal/db/bundb/tag.go @@ -28,7 +28,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/paging" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/uptrace/bun" ) @@ -102,7 +102,7 @@ func(uncached []string) ([]*gtsmodel.Tag, error) { // Reorder the tags by their // IDs to ensure in correct order. getID := func(t *gtsmodel.Tag) string { return t.ID } - util.OrderBy(tags, ids, getID) + xslices.OrderBy(tags, ids, getID) return tags, nil } @@ -301,5 +301,5 @@ func (t *tagDB) GetAccountIDsFollowingTagIDs(ctx context.Context, tagIDs []strin // Accounts might be following multiple tags in list, // but we only want to return each account once. - return util.Deduplicate(accountIDs), nil + return xslices.Deduplicate(accountIDs), nil } diff --git a/internal/federation/federatingprotocol.go b/internal/federation/federatingprotocol.go index fdd13f7f0..a953701f8 100644 --- a/internal/federation/federatingprotocol.go +++ b/internal/federation/federatingprotocol.go @@ -35,7 +35,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/uris" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) type errOtherIRIBlocked struct { @@ -162,7 +162,7 @@ func (f *Federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Reques // OtherIRIs will likely contain some // duplicate entries now, so remove them. - otherIRIs = util.DeduplicateFunc(otherIRIs, + otherIRIs = xslices.DeduplicateFunc(otherIRIs, (*url.URL).String, // serialized URL is 'key()' ) diff --git a/internal/gtsmodel/conversation.go b/internal/gtsmodel/conversation.go index d17cbe6fe..d3bdcbf1d 100644 --- a/internal/gtsmodel/conversation.go +++ b/internal/gtsmodel/conversation.go @@ -22,7 +22,7 @@ "strings" "time" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) // Conversation represents direct messages between the owner account and a set of other accounts. @@ -62,7 +62,7 @@ type Conversation struct { // ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs. func ConversationOtherAccountsKey(otherAccountIDs []string) string { - otherAccountIDs = util.Deduplicate(otherAccountIDs) + otherAccountIDs = xslices.Deduplicate(otherAccountIDs) slices.Sort(otherAccountIDs) return strings.Join(otherAccountIDs, ",") } diff --git a/internal/log/log.go b/internal/log/log.go index bb2e561b3..52703ef28 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -22,11 +22,11 @@ "fmt" "log/syslog" "os" - "slices" "strings" "time" "codeberg.org/gruf/go-kv" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) var ( @@ -412,7 +412,10 @@ func logf(ctx context.Context, depth int, lvl LEVEL, fields []kv.Field, s string buf.B = append(buf.B, lvlstrs[lvl]...) buf.B = append(buf.B, ' ') - if ctx != nil { + if ctx != nil && len(ctxhooks) > 0 { + // Ensure fields have space for hooks (+1 for below). + fields = xslices.GrowJust(fields, len(ctxhooks)+1) + // Pass context through hooks. for _, hook := range ctxhooks { fields = hook(ctx, fields) @@ -420,9 +423,8 @@ func logf(ctx context.Context, depth int, lvl LEVEL, fields []kv.Field, s string } if s != "" { - // Append message to log fields. - fields = slices.Grow(fields, 1) - fields = append(fields, kv.Field{ + // Append message (if given) as final log field. + fields = xslices.AppendJust(fields, kv.Field{ K: "msg", V: fmt.Sprintf(s, a...), }) } diff --git a/internal/processing/account/alias.go b/internal/processing/account/alias.go index a11be0305..d7d4cf547 100644 --- a/internal/processing/account/alias.go +++ b/internal/processing/account/alias.go @@ -27,7 +27,7 @@ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) func (p *Processor) Alias( @@ -137,8 +137,8 @@ type uri struct { // Dedupe URIs + accounts, in case someone // provided both an account URL and an // account URI above, for the same account. - account.AlsoKnownAsURIs = util.Deduplicate(account.AlsoKnownAsURIs) - account.AlsoKnownAs = util.DeduplicateFunc( + account.AlsoKnownAsURIs = xslices.Deduplicate(account.AlsoKnownAsURIs) + account.AlsoKnownAs = xslices.DeduplicateFunc( account.AlsoKnownAs, func(a *gtsmodel.Account) string { return a.URI diff --git a/internal/processing/status/create.go b/internal/processing/status/create.go index 55a78610f..fbc2dadf7 100644 --- a/internal/processing/status/create.go +++ b/internal/processing/status/create.go @@ -36,6 +36,7 @@ "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/uris" "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) // Create processes the given form to create a new status, returning the api model representation of that status if it's OK. @@ -536,9 +537,9 @@ func (p *Processor) processContent(ctx context.Context, parseMention gtsmodel.Pa } // Gather all the database IDs from each of the gathered status mentions, tags, and emojis. - status.MentionIDs = util.Gather(nil, status.Mentions, func(mention *gtsmodel.Mention) string { return mention.ID }) - status.TagIDs = util.Gather(nil, status.Tags, func(tag *gtsmodel.Tag) string { return tag.ID }) - status.EmojiIDs = util.Gather(nil, status.Emojis, func(emoji *gtsmodel.Emoji) string { return emoji.ID }) + status.MentionIDs = xslices.Gather(nil, status.Mentions, func(mention *gtsmodel.Mention) string { return mention.ID }) + status.TagIDs = xslices.Gather(nil, status.Tags, func(tag *gtsmodel.Tag) string { return tag.ID }) + status.EmojiIDs = xslices.Gather(nil, status.Emojis, func(emoji *gtsmodel.Emoji) string { return emoji.ID }) if status.ContentWarning != "" && len(status.AttachmentIDs) > 0 { // If a content-warning is set, and diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go index cfc790bd4..ed8bc1d8d 100644 --- a/internal/typeutils/internaltoas.go +++ b/internal/typeutils/internaltoas.go @@ -36,7 +36,7 @@ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/uris" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) // AccountToAS converts a gts model account into an activity streams person, suitable for federation @@ -1819,7 +1819,7 @@ func populateValuesForProp[T ap.WithIRI]( // Deduplicate the iri strings to // make sure we're not parsing + adding // the same string multiple times. - iriStrs = util.Deduplicate(iriStrs) + iriStrs = xslices.Deduplicate(iriStrs) // Append them to the property. for _, iriStr := range iriStrs { diff --git a/internal/util/slices.go b/internal/util/xslices/slices.go similarity index 78% rename from internal/util/slices.go rename to internal/util/xslices/slices.go index 955fe8830..1c1c159b2 100644 --- a/internal/util/slices.go +++ b/internal/util/xslices/slices.go @@ -15,12 +15,53 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package util +package xslices import ( "slices" ) +// GrowJust increases slice capacity to guarantee +// extra room 'size', where in the case that it does +// need to allocate more it ONLY allocates 'size' extra. +// This is different to typical slices.Grow behaviour, +// which simply guarantees extra through append() which +// may allocate more than necessary extra size. +func GrowJust[T any](in []T, size int) []T { + + if cap(in)-len(in) < size { + // Reallocate enough for in + size. + in2 := make([]T, len(in), len(in)+size) + _ = copy(in2, in) + in = in2 + } + + return in +} + +// AppendJust appends extra elements to slice, +// ONLY allocating at most len(extra) elements. This +// is different to the typical append behaviour which +// will append extra, in a manner to reduce the need +// for new allocations on every call to append. +func AppendJust[T any](in []T, extra ...T) []T { + l := len(in) + + if cap(in)-l < len(extra) { + // Reallocate enough for + extra. + in2 := make([]T, l+len(extra)) + _ = copy(in2, in) + in = in2 + } else { + // Reslice for + extra. + in = in[:l+len(extra)] + } + + // Copy extra into slice. + _ = copy(in[l:], extra) + return in +} + // Deduplicate deduplicates entries in the given slice. func Deduplicate[T comparable](in []T) []T { var ( diff --git a/internal/util/slices_test.go b/internal/util/xslices/slices_test.go similarity index 52% rename from internal/util/slices_test.go rename to internal/util/xslices/slices_test.go index c93e489f5..7c62ac77f 100644 --- a/internal/util/slices_test.go +++ b/internal/util/xslices/slices_test.go @@ -15,22 +15,90 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package util_test +package xslices_test import ( + "math/rand" "net/url" "slices" "testing" - "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/stretchr/testify/assert" + "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) -var ( - testURLSlice = []*url.URL{} -) +func TestGrowJust(t *testing.T) { + for _, l := range []int{0, 2, 4, 8, 16, 32, 64} { + for _, x := range []int{0, 2, 4, 8, 16, 32, 64} { + s := make([]int, l, l+x) + for _, g := range []int{0, 2, 4, 8, 16, 32, 64} { + s2 := xslices.GrowJust(s, g) + + // Slice length should not be different. + assert.Equal(t, len(s), len(s2)) + + switch { + // If slice already has capacity for + // 'g' then it should not be changed. + case cap(s) >= len(s)+g: + assert.Equal(t, cap(s), cap(s2)) + + // Else, returned slice should only + // have capacity for original length + // plus extra elements, NOTHING MORE. + default: + assert.Equal(t, cap(s2), len(s)+g) + } + } + } + } +} + +func TestAppendJust(t *testing.T) { + for _, l := range []int{0, 2, 4, 8, 16, 32, 64} { + for _, x := range []int{0, 2, 4, 8, 16, 32, 64} { + s := make([]int, l, l+x) + + // Randomize slice. + for i := range s { + s[i] = rand.Int() + } + + for _, a := range []int{0, 2, 4, 8, 16, 32, 64} { + toAppend := make([]int, a) + + // Randomize appended vals. + for i := range toAppend { + toAppend[i] = rand.Int() + } + + s2 := xslices.AppendJust(s, toAppend...) + + // Slice length should be as expected. + assert.Equal(t, len(s)+a, len(s2)) + + // Slice contents should be as expected. + assert.Equal(t, append(s, toAppend...), s2) + + switch { + // If slice already has capacity for + // 'toAppend' then it should not change. + case cap(s) >= len(s)+a: + assert.Equal(t, cap(s), cap(s2)) + + // Else, returned slice should only + // have capacity for original length + // plus extra elements, NOTHING MORE. + default: + assert.Equal(t, len(s)+a, cap(s2)) + } + } + } + } +} func TestGather(t *testing.T) { - out := util.Gather(nil, []*url.URL{ + out := xslices.Gather(nil, []*url.URL{ {Scheme: "https", Host: "google.com", Path: "/some-search"}, {Scheme: "http", Host: "example.com", Path: "/robots.txt"}, }, (*url.URL).String) @@ -41,7 +109,7 @@ func TestGather(t *testing.T) { t.Fatal("unexpected gather output") } - out = util.Gather([]string{ + out = xslices.Gather([]string{ "starting input string", "another starting input", }, []*url.URL{ @@ -59,7 +127,7 @@ func TestGather(t *testing.T) { } func TestGatherIf(t *testing.T) { - out := util.GatherIf(nil, []string{ + out := xslices.GatherIf(nil, []string{ "hello world", "not hello world", "hello world", @@ -73,7 +141,7 @@ func TestGatherIf(t *testing.T) { t.Fatal("unexpected gatherif output") } - out = util.GatherIf([]string{ + out = xslices.GatherIf([]string{ "starting input string", "another starting input", }, []string{