summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-07-30 09:29:32 +0000
committerLibravatar GitHub <noreply@github.com>2024-07-30 11:29:32 +0200
commit47c26818d6cd1545540e44d3475278a6a4296a77 (patch)
treeb1a2c6046b84d9849af29bf7720c00243005cb89
parent[feature] Implement following hashtags (#3141) (diff)
downloadgotosocial-47c26818d6cd1545540e44d3475278a6a4296a77.tar.xz
[chore] add some more slice related utility functions + remove duplicated functions (#3149)
-rw-r--r--internal/federation/federatingprotocol.go4
-rw-r--r--internal/gtsmodel/conversation.go2
-rw-r--r--internal/media/processingmedia.go9
-rw-r--r--internal/typeutils/internaltofrontend.go1
-rw-r--r--internal/util/slices.go66
-rw-r--r--internal/util/slices_test.go94
-rw-r--r--internal/util/unique.go79
7 files changed, 214 insertions, 41 deletions
diff --git a/internal/federation/federatingprotocol.go b/internal/federation/federatingprotocol.go
index 1a655994c..fdd13f7f0 100644
--- a/internal/federation/federatingprotocol.go
+++ b/internal/federation/federatingprotocol.go
@@ -162,7 +162,9 @@ func (f *Federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Reques
// OtherIRIs will likely contain some
// 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
// so they can be checked for blocks later.
diff --git a/internal/gtsmodel/conversation.go b/internal/gtsmodel/conversation.go
index f03f27458..d17cbe6fe 100644
--- a/internal/gtsmodel/conversation.go
+++ b/internal/gtsmodel/conversation.go
@@ -62,7 +62,7 @@ type Conversation struct {
// ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs.
func ConversationOtherAccountsKey(otherAccountIDs []string) string {
- otherAccountIDs = util.UniqueStrings(otherAccountIDs)
+ otherAccountIDs = util.Deduplicate(otherAccountIDs)
slices.Sort(otherAccountIDs)
return strings.Join(otherAccountIDs, ",")
}
diff --git a/internal/media/processingmedia.go b/internal/media/processingmedia.go
index b68d8d680..3da036ac7 100644
--- a/internal/media/processingmedia.go
+++ b/internal/media/processingmedia.go
@@ -218,8 +218,13 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
}
if width > 0 && height > 0 {
- // Determine thumbnail dimensions to use.
- thumbWidth, thumbHeight := thumbSize(width, height, aspect, result.rotation)
+ // Determine thumbnail dimens to use.
+ thumbWidth, thumbHeight := thumbSize(
+ width,
+ height,
+ aspect,
+ result.rotation,
+ )
p.media.FileMeta.Small.Width = thumbWidth
p.media.FileMeta.Small.Height = thumbHeight
p.media.FileMeta.Small.Size = (thumbWidth * thumbHeight)
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index c555e1d04..70230cd89 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -856,6 +856,7 @@ func (c *Converter) statusToAPIFilterResults(
for _, mention := range s.Mentions {
otherAccounts = append(otherAccounts, mention.TargetAccount)
}
+
// If there are no other accounts, skip this check.
if len(otherAccounts) > 0 {
// Start by assuming that they're all invisible or muted.
diff --git a/internal/util/slices.go b/internal/util/slices.go
index 0505229e5..955fe8830 100644
--- a/internal/util/slices.go
+++ b/internal/util/slices.go
@@ -17,7 +17,9 @@
package util
-import "slices"
+import (
+ "slices"
+)
// Deduplicate deduplicates entries in the given slice.
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
}
+// 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,
// 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 {
if get == nil {
panic("nil func")
diff --git a/internal/util/slices_test.go b/internal/util/slices_test.go
new file mode 100644
index 000000000..c93e489f5
--- /dev/null
+++ b/internal/util/slices_test.go
@@ -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")
+ }
+}
diff --git a/internal/util/unique.go b/internal/util/unique.go
index f0ded1446..bad553d3f 100644
--- a/internal/util/unique.go
+++ b/internal/util/unique.go
@@ -17,48 +17,57 @@
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
-// slice of strings, without changing the order of the entries.
-func UniqueStrings(strings []string) []string {
- var (
- l = len(strings)
- keys = make(map[string]any, l) // Use map to dedupe items.
- 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)
- }
+// ToSet creates a Set[T] from given values,
+// noting that this does not maintain any order.
+func ToSet[T comparable](in []T) Set[T] {
+ set := make(Set[T], len(in))
+ for _, v := range in {
+ set[v] = struct{}{}
}
-
- return unique
+ return set
}
-// UniqueURIs returns a deduplicated version of the given
-// slice of URIs, without changing the order of the entries.
-func UniqueURIs(uris []*url.URL) []*url.URL {
- var (
- l = len(uris)
- keys = make(map[string]any, l) // Use map to dedupe items.
- unique = make([]*url.URL, 0, l) // Return slice.
- )
+// FromSet extracts the values from set to slice,
+// noting that this does not maintain any order.
+func FromSet[T comparable](in Set[T]) []T {
+ out := make([]T, len(in))
+ var i int
+ for v := range in {
+ out[i] = v
+ i++
+ }
+ return out
+}
- for _, uri := range uris {
- uriStr := uri.String()
+// In returns input slice filtered to
+// only contain those in receiving set.
+func (s Set[T]) In(vs []T) []T {
+ out := make([]T, 0, len(vs))
+ for _, v := range vs {
+ if _, ok := s[v]; ok {
+ out = append(out, v)
+ }
+ }
+ return out
+}
- // Check if already set as a key in the map;
- // if not, add to return slice + mark key as set.
- if _, set := keys[uriStr]; !set {
- keys[uriStr] = nil // Value doesn't matter.
- unique = append(unique, uri)
+// 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
+}
- return unique
+// Has returns if value is in Set.
+func (s Set[T]) Has(v T) bool {
+ _, ok := s[v]
+ return ok
}