[chore] add some more slice related utility functions + remove duplicated functions (#3149)

This commit is contained in:
kim 2024-07-30 09:29:32 +00:00 committed by GitHub
parent a237e2b295
commit 47c26818d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 216 additions and 43 deletions

View file

@ -162,7 +162,9 @@ func (f *Federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Reques
// OtherIRIs will likely contain some // OtherIRIs will likely contain some
// duplicate entries now, so remove them. // duplicate entries now, so remove them.
otherIRIs = util.UniqueURIs(otherIRIs) otherIRIs = util.DeduplicateFunc(otherIRIs,
(*url.URL).String, // serialized URL is 'key()'
)
// Finished, set other IRIs on the context // Finished, set other IRIs on the context
// so they can be checked for blocks later. // so they can be checked for blocks later.

View file

@ -62,7 +62,7 @@ type Conversation struct {
// ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs. // ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs.
func ConversationOtherAccountsKey(otherAccountIDs []string) string { func ConversationOtherAccountsKey(otherAccountIDs []string) string {
otherAccountIDs = util.UniqueStrings(otherAccountIDs) otherAccountIDs = util.Deduplicate(otherAccountIDs)
slices.Sort(otherAccountIDs) slices.Sort(otherAccountIDs)
return strings.Join(otherAccountIDs, ",") return strings.Join(otherAccountIDs, ",")
} }

View file

@ -218,8 +218,13 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
} }
if width > 0 && height > 0 { if width > 0 && height > 0 {
// Determine thumbnail dimensions to use. // Determine thumbnail dimens to use.
thumbWidth, thumbHeight := thumbSize(width, height, aspect, result.rotation) thumbWidth, thumbHeight := thumbSize(
width,
height,
aspect,
result.rotation,
)
p.media.FileMeta.Small.Width = thumbWidth p.media.FileMeta.Small.Width = thumbWidth
p.media.FileMeta.Small.Height = thumbHeight p.media.FileMeta.Small.Height = thumbHeight
p.media.FileMeta.Small.Size = (thumbWidth * thumbHeight) p.media.FileMeta.Small.Size = (thumbWidth * thumbHeight)

View file

@ -856,6 +856,7 @@ func (c *Converter) statusToAPIFilterResults(
for _, mention := range s.Mentions { for _, mention := range s.Mentions {
otherAccounts = append(otherAccounts, mention.TargetAccount) otherAccounts = append(otherAccounts, mention.TargetAccount)
} }
// If there are no other accounts, skip this check. // If there are no other accounts, skip this check.
if len(otherAccounts) > 0 { if len(otherAccounts) > 0 {
// Start by assuming that they're all invisible or muted. // Start by assuming that they're all invisible or muted.

View file

@ -17,7 +17,9 @@
package util package util
import "slices" import (
"slices"
)
// Deduplicate deduplicates entries in the given slice. // Deduplicate deduplicates entries in the given slice.
func Deduplicate[T comparable](in []T) []T { func Deduplicate[T comparable](in []T) []T {
@ -68,9 +70,69 @@ func DeduplicateFunc[T any, C comparable](in []T, key func(v T) C) []T {
return deduped return deduped
} }
// Gather will collect the values of type V from input type []T,
// passing each item to 'get' and appending V to the return slice.
func Gather[T, V any](out []V, in []T, get func(T) V) []V {
if get == nil {
panic("nil func")
}
// Starting write index
// in the resliced / re
// alloc'd output slice.
start := len(out)
// Total required slice len.
total := start + len(in)
if total > cap(out) {
// Reallocate output with
// capacity for total len.
out2 := make([]V, len(out), total)
copy(out2, out)
out = out2
}
// Reslice with capacity
// up to total required.
out = out[:total]
// Gather vs from 'in'.
for i, v := range in {
j := start + i
out[j] = get(v)
}
return out
}
// GatherIf is functionally similar to Gather(), but only when return bool is true.
// If you don't need to check the boolean, Gather() will be very slightly faster.
func GatherIf[T, V any](out []V, in []T, get func(T) (V, bool)) []V {
if get == nil {
panic("nil func")
}
if cap(out)-len(out) < len(in) {
// Reallocate output with capacity for 'in'.
out2 := make([]V, len(out), cap(out)+len(in))
copy(out2, out)
out = out2
}
// Gather vs from 'in'.
for _, v := range in {
if v, ok := get(v); ok {
out = append(out, v)
}
}
return out
}
// Collate will collect the values of type K from input type []T, // Collate will collect the values of type K from input type []T,
// passing each item to 'get' and deduplicating the end result. // passing each item to 'get' and deduplicating the end result.
// Compared to Deduplicate() this returns []K, NOT input type []T. // This is equivalent to calling Gather() followed by Deduplicate().
func Collate[T any, K comparable](in []T, get func(T) K) []K { func Collate[T any, K comparable](in []T, get func(T) K) []K {
if get == nil { if get == nil {
panic("nil func") panic("nil func")

View file

@ -0,0 +1,94 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package util_test
import (
"net/url"
"slices"
"testing"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
var (
testURLSlice = []*url.URL{}
)
func TestGather(t *testing.T) {
out := util.Gather(nil, []*url.URL{
{Scheme: "https", Host: "google.com", Path: "/some-search"},
{Scheme: "http", Host: "example.com", Path: "/robots.txt"},
}, (*url.URL).String)
if !slices.Equal(out, []string{
"https://google.com/some-search",
"http://example.com/robots.txt",
}) {
t.Fatal("unexpected gather output")
}
out = util.Gather([]string{
"starting input string",
"another starting input",
}, []*url.URL{
{Scheme: "https", Host: "google.com", Path: "/some-search"},
{Scheme: "http", Host: "example.com", Path: "/robots.txt"},
}, (*url.URL).String)
if !slices.Equal(out, []string{
"starting input string",
"another starting input",
"https://google.com/some-search",
"http://example.com/robots.txt",
}) {
t.Fatal("unexpected gather output")
}
}
func TestGatherIf(t *testing.T) {
out := util.GatherIf(nil, []string{
"hello world",
"not hello world",
"hello world",
}, func(s string) (string, bool) {
return s, s == "hello world"
})
if !slices.Equal(out, []string{
"hello world",
"hello world",
}) {
t.Fatal("unexpected gatherif output")
}
out = util.GatherIf([]string{
"starting input string",
"another starting input",
}, []string{
"hello world",
"not hello world",
"hello world",
}, func(s string) (string, bool) {
return s, s == "hello world"
})
if !slices.Equal(out, []string{
"starting input string",
"another starting input",
"hello world",
"hello world",
}) {
t.Fatal("unexpected gatherif output")
}
}

View file

@ -17,48 +17,57 @@
package util package util
import "net/url" // Set represents a hashmap of only keys,
// useful for deduplication / key checking.
type Set[T comparable] map[T]struct{}
// UniqueStrings returns a deduplicated version of the given // ToSet creates a Set[T] from given values,
// slice of strings, without changing the order of the entries. // noting that this does not maintain any order.
func UniqueStrings(strings []string) []string { func ToSet[T comparable](in []T) Set[T] {
var ( set := make(Set[T], len(in))
l = len(strings) for _, v := range in {
keys = make(map[string]any, l) // Use map to dedupe items. set[v] = struct{}{}
unique = make([]string, 0, l) // Return slice.
)
for _, str := range strings {
// Check if already set as a key in the map;
// if not, add to return slice + mark key as set.
if _, set := keys[str]; !set {
keys[str] = nil // Value doesn't matter.
unique = append(unique, str)
}
} }
return set
return unique
} }
// UniqueURIs returns a deduplicated version of the given // FromSet extracts the values from set to slice,
// slice of URIs, without changing the order of the entries. // noting that this does not maintain any order.
func UniqueURIs(uris []*url.URL) []*url.URL { func FromSet[T comparable](in Set[T]) []T {
var ( out := make([]T, len(in))
l = len(uris) var i int
keys = make(map[string]any, l) // Use map to dedupe items. for v := range in {
unique = make([]*url.URL, 0, l) // Return slice. out[i] = v
) i++
}
return out
}
for _, uri := range uris { // In returns input slice filtered to
uriStr := uri.String() // only contain those in receiving set.
func (s Set[T]) In(vs []T) []T {
// Check if already set as a key in the map; out := make([]T, 0, len(vs))
// if not, add to return slice + mark key as set. for _, v := range vs {
if _, set := keys[uriStr]; !set { if _, ok := s[v]; ok {
keys[uriStr] = nil // Value doesn't matter. out = append(out, v)
unique = append(unique, uri)
} }
} }
return out
return unique }
// NotIn is the functional inverse of In().
func (s Set[T]) NotIn(vs []T) []T {
out := make([]T, 0, len(vs))
for _, v := range vs {
if _, ok := s[v]; !ok {
out = append(out, v)
}
}
return out
}
// Has returns if value is in Set.
func (s Set[T]) Has(v T) bool {
_, ok := s[v]
return ok
} }