refactor: use slices package for sorting (#10136)

Few more complicated usages of the sort packages are left.

### Purpose

Make progress towards replacing the sort package with slices package.
This commit is contained in:
Marcel Meyer
2025-05-26 20:37:49 +02:00
committed by GitHub
parent 905e5ec07f
commit 598915193a
18 changed files with 107 additions and 152 deletions
+4 -2
View File
@@ -14,7 +14,7 @@ import (
"net/http"
"os"
"path/filepath"
"sort"
"slices"
"strings"
"time"
)
@@ -37,7 +37,9 @@ func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
return
}
sort.Sort(sort.Reverse(sort.StringSlice(files)))
slices.SortFunc(files, func(a, b string) int {
return strings.Compare(b, a)
})
for _, file := range files {
if strings.Contains(file, ".reported.") {
// We've already sent this file. It'll be cleaned out at some
+2 -2
View File
@@ -117,8 +117,8 @@ func (m *tokenManager) saveLocked() {
for token, expiry := range m.tokens.Tokens {
tokens = append(tokens, tokenExpiry{token, expiry})
}
slices.SortFunc(tokens, func(i, j tokenExpiry) int {
return int(i.expiry - j.expiry)
slices.SortFunc(tokens, func(a, b tokenExpiry) int {
return int(a.expiry - b.expiry)
})
// Remove the oldest tokens.
for _, token := range tokens[:len(tokens)-m.maxItems] {
+2 -3
View File
@@ -18,7 +18,6 @@ import (
"os"
"reflect"
"slices"
"sort"
"strconv"
"strings"
@@ -338,8 +337,8 @@ func (cfg *Configuration) prepareDeviceList() map[protocol.DeviceID]*DeviceConfi
// - sorted by ID
// Happen before preparting folders as that needs a correct device list.
cfg.Devices = ensureNoDuplicateOrEmptyIDDevices(cfg.Devices)
sort.Slice(cfg.Devices, func(a, b int) bool {
return cfg.Devices[a].DeviceID.Compare(cfg.Devices[b].DeviceID) == -1
slices.SortFunc(cfg.Devices, func(a, b DeviceConfiguration) int {
return a.DeviceID.Compare(b.DeviceID)
})
// Build a list of available devices
+3 -3
View File
@@ -8,7 +8,7 @@ package config
import (
"fmt"
"sort"
"slices"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -100,8 +100,8 @@ func sortedObservedFolderSlice(input map[string]ObservedFolder) []ObservedFolder
for _, folder := range input {
output = append(output, folder)
}
sort.Slice(output, func(i, j int) bool {
return output[i].Time.Before(output[j].Time)
slices.SortFunc(output, func(a, b ObservedFolder) int {
return a.Time.Compare(b.Time)
})
return output
}
+3 -3
View File
@@ -15,7 +15,7 @@ import (
"fmt"
"path"
"path/filepath"
"sort"
"slices"
"strings"
"time"
@@ -291,8 +291,8 @@ func (f *FolderConfiguration) prepare(myID protocol.DeviceID, existingDevices ma
f.Devices = ensureDevicePresent(f.Devices, myID)
f.Devices = ensureNoUntrustedTrustingSharing(f, f.Devices, existingDevices)
sort.Slice(f.Devices, func(a, b int) bool {
return f.Devices[a].DeviceID.Compare(f.Devices[b].DeviceID) == -1
slices.SortFunc(f.Devices, func(a, b FolderDeviceConfiguration) int {
return a.DeviceID.Compare(b.DeviceID)
})
if f.RescanIntervalS > MaxRescanIntervalS {
+3 -3
View File
@@ -8,7 +8,7 @@ package db
import (
"math/bits"
"sort"
"slices"
"testing"
"github.com/syncthing/syncthing/lib/events"
@@ -71,8 +71,8 @@ func TestMetaDevices(t *testing.T) {
}
// Check that we got the two devices we expect
sort.Slice(devs, func(a, b int) bool {
return devs[a].Compare(devs[b]) == -1
slices.SortFunc(devs, func(a, b protocol.DeviceID) int {
return a.Compare(b)
})
if devs[0] != d1 {
t.Error("first device should be d1")
+19 -26
View File
@@ -11,7 +11,8 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"slices"
"strings"
"testing"
"time"
@@ -102,16 +103,8 @@ func needList(t testing.TB, s *db.FileSet, n protocol.DeviceID) []protocol.FileI
type fileList []protocol.FileInfo
func (l fileList) Len() int {
return len(l)
}
func (l fileList) Less(a, b int) bool {
return l[a].Name < l[b].Name
}
func (l fileList) Swap(a, b int) {
l[a], l[b] = l[b], l[a]
func compareByName(a, b protocol.FileInfo) int {
return strings.Compare(a.Name, b.Name)
}
func (l fileList) String() string {
@@ -218,7 +211,7 @@ func TestGlobalSet(t *testing.T) {
t.Helper()
g := fileList(globalList(t, m))
sort.Sort(g)
slices.SortFunc(g, compareByName)
if fmt.Sprint(g) != fmt.Sprint(expectedGlobal) {
t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal)
@@ -255,7 +248,7 @@ func TestGlobalSet(t *testing.T) {
}
h := fileList(haveList(t, m, protocol.LocalDeviceID))
sort.Sort(h)
slices.SortFunc(h, compareByName)
if fmt.Sprint(h) != fmt.Sprint(localTot) {
t.Errorf("Have incorrect (local);\n A: %v !=\n E: %v", h, localTot)
@@ -292,14 +285,14 @@ func TestGlobalSet(t *testing.T) {
}
h = fileList(haveList(t, m, remoteDevice0))
sort.Sort(h)
slices.SortFunc(h, compareByName)
if fmt.Sprint(h) != fmt.Sprint(remoteTot) {
t.Errorf("Have incorrect (remote);\n A: %v !=\n E: %v", h, remoteTot)
}
n := fileList(needList(t, m, protocol.LocalDeviceID))
sort.Sort(n)
slices.SortFunc(n, compareByName)
if fmt.Sprint(n) != fmt.Sprint(expectedLocalNeed) {
t.Errorf("Need incorrect (local);\n A: %v !=\n E: %v", n, expectedLocalNeed)
@@ -308,7 +301,7 @@ func TestGlobalSet(t *testing.T) {
checkNeed(t, m, protocol.LocalDeviceID, expectedLocalNeed)
n = fileList(needList(t, m, remoteDevice0))
sort.Sort(n)
slices.SortFunc(n, compareByName)
if fmt.Sprint(n) != fmt.Sprint(expectedRemoteNeed) {
t.Errorf("Need incorrect (remote);\n A: %v !=\n E: %v", n, expectedRemoteNeed)
@@ -428,14 +421,14 @@ func TestGlobalSet(t *testing.T) {
check()
h := fileList(haveList(t, m, remoteDevice1))
sort.Sort(h)
slices.SortFunc(h, compareByName)
if fmt.Sprint(h) != fmt.Sprint(secRemote) {
t.Errorf("Have incorrect (secRemote);\n A: %v !=\n E: %v", h, secRemote)
}
n := fileList(needList(t, m, remoteDevice1))
sort.Sort(n)
slices.SortFunc(n, compareByName)
if fmt.Sprint(n) != fmt.Sprint(expectedSecRemoteNeed) {
t.Errorf("Need incorrect (secRemote);\n A: %v !=\n E: %v", n, expectedSecRemoteNeed)
@@ -475,7 +468,7 @@ func TestNeedWithInvalid(t *testing.T) {
replace(s, remoteDevice1, remote1Have)
need := fileList(needList(t, s, protocol.LocalDeviceID))
sort.Sort(need)
slices.SortFunc(need, compareByName)
if fmt.Sprint(need) != fmt.Sprint(expectedNeed) {
t.Errorf("Need incorrect;\n A: %v !=\n E: %v", need, expectedNeed)
@@ -503,7 +496,7 @@ func TestUpdateToInvalid(t *testing.T) {
replace(s, protocol.LocalDeviceID, localHave)
have := fileList(haveList(t, s, protocol.LocalDeviceID))
sort.Sort(have)
slices.SortFunc(have, compareByName)
if fmt.Sprint(have) != fmt.Sprint(localHave) {
t.Errorf("Have incorrect before invalidation;\n A: %v !=\n E: %v", have, localHave)
@@ -519,8 +512,8 @@ func TestUpdateToInvalid(t *testing.T) {
s.Update(protocol.LocalDeviceID, append(fileList{}, localHave[1], localHave[4]))
have = fileList(haveList(t, s, protocol.LocalDeviceID))
sort.Sort(have)
have = haveList(t, s, protocol.LocalDeviceID)
slices.SortFunc(have, compareByName)
if fmt.Sprint(have) != fmt.Sprint(localHave) {
t.Errorf("Have incorrect after invalidation;\n A: %v !=\n E: %v", have, localHave)
@@ -605,7 +598,7 @@ func TestGlobalReset(t *testing.T) {
replace(m, protocol.LocalDeviceID, local)
g := globalList(t, m)
sort.Sort(fileList(g))
slices.SortFunc(g, compareByName)
if diff, equal := messagediff.PrettyDiff(local, g); !equal {
t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff)
@@ -615,7 +608,7 @@ func TestGlobalReset(t *testing.T) {
replace(m, remoteDevice0, nil)
g = globalList(t, m)
sort.Sort(fileList(g))
slices.SortFunc(g, compareByName)
if diff, equal := messagediff.PrettyDiff(local, g); !equal {
t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff)
@@ -653,8 +646,8 @@ func TestNeed(t *testing.T) {
need := needList(t, m, protocol.LocalDeviceID)
sort.Sort(fileList(need))
sort.Sort(fileList(shouldNeed))
slices.SortFunc(need, compareByName)
slices.SortFunc(shouldNeed, compareByName)
if fmt.Sprint(need) != fmt.Sprint(shouldNeed) {
t.Errorf("Need incorrect;\n%v !=\n%v", need, shouldNeed)
+3 -3
View File
@@ -7,7 +7,7 @@
package model
import (
"sort"
"slices"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
@@ -52,8 +52,8 @@ type standardBlockPullReorderer struct {
func newStandardBlockPullReorderer(id protocol.DeviceID, otherDevices []protocol.DeviceID) *standardBlockPullReorderer {
allDevices := append(otherDevices, id)
sort.Slice(allDevices, func(i, j int) bool {
return allDevices[i].Compare(allDevices[j]) == -1
slices.SortFunc(allDevices, func(a, b protocol.DeviceID) int {
return a.Compare(b)
})
// Find our index
myIndex := -1
+3 -3
View File
@@ -8,7 +8,7 @@ package model
import (
"reflect"
"sort"
"slices"
"testing"
"github.com/syncthing/syncthing/lib/protocol"
@@ -65,8 +65,8 @@ func Test_inOrderBlockPullReorderer_Reorder(t *testing.T) {
func Test_standardBlockPullReorderer_Reorder(t *testing.T) {
// Order the devices, so we know their ordering ahead of time.
devices := []protocol.DeviceID{myID, device1, device2}
sort.Slice(devices, func(i, j int) bool {
return devices[i].Compare(devices[j]) == -1
slices.SortFunc(devices, func(a, b protocol.DeviceID) int {
return a.Compare(b)
})
blocks := func(i ...int) []protocol.BlockInfo {
+4 -2
View File
@@ -13,7 +13,7 @@ import (
"math/rand"
"path/filepath"
"slices"
"sort"
"strings"
"time"
"github.com/syncthing/syncthing/lib/config"
@@ -1201,7 +1201,9 @@ func (f *folder) Errors() []FileError {
errors := make([]FileError, scanLen+len(f.pullErrors))
copy(errors[:scanLen], f.scanErrors)
copy(errors[scanLen:], f.pullErrors)
sort.Sort(fileErrorList(errors))
slices.SortFunc(errors, func(a, b FileError) int {
return strings.Compare(a.Path, b.Path)
})
return errors
}
+5 -2
View File
@@ -8,7 +8,8 @@ package model
import (
"fmt"
"sort"
"slices"
"strings"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
@@ -112,7 +113,9 @@ func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string, snap *db.Snapsh
go f.pullScannerRoutine(scanChan)
defer close(scanChan)
sort.Sort(sort.Reverse(sort.StringSlice(dirs)))
slices.SortFunc(dirs, func(a, b string) int {
return strings.Compare(b, a)
})
for _, dir := range dirs {
if err := f.deleteDirOnDisk(dir, snap, scanChan); err != nil {
f.newScanError(dir, fmt.Errorf("deleting unexpected dir: %w", err))
+5 -2
View File
@@ -7,7 +7,8 @@
package model
import (
"sort"
"slices"
"strings"
"time"
"github.com/syncthing/syncthing/lib/config"
@@ -207,7 +208,9 @@ func (q *deleteQueue) handle(fi protocol.FileInfo, snap *db.Snapshot) (bool, err
func (q *deleteQueue) flush(snap *db.Snapshot) ([]string, error) {
// Process directories from the leaves inward.
sort.Sort(sort.Reverse(sort.StringSlice(q.dirs)))
slices.SortFunc(q.dirs, func(a, b string) int {
return strings.Compare(b, a)
})
var firstError error
var deleted []string
+4 -16
View File
@@ -14,7 +14,7 @@ import (
"fmt"
"io"
"path/filepath"
"sort"
"slices"
"strconv"
"strings"
"time"
@@ -1867,7 +1867,9 @@ func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan cha
if f.MaxConflicts > -1 {
matches := existingConflicts(name, f.mtimefs)
if len(matches) > f.MaxConflicts {
sort.Sort(sort.Reverse(sort.StringSlice(matches)))
slices.SortFunc(matches, func(a, b string) int {
return strings.Compare(b, a)
})
for _, match := range matches[f.MaxConflicts:] {
if gerr := f.mtimefs.Remove(match); gerr != nil {
l.Debugln(f, "removing extra conflict", gerr)
@@ -2206,20 +2208,6 @@ type FileError struct {
Err string `json:"error"`
}
type fileErrorList []FileError
func (l fileErrorList) Len() int {
return len(l)
}
func (l fileErrorList) Less(a, b int) bool {
return l[a].Path < l[b].Path
}
func (l fileErrorList) Swap(a, b int) {
l[a], l[b] = l[b], l[a]
}
func conflictName(name, lastModBy string) string {
ext := filepath.Ext(name)
return name[:len(name)-len(ext)] + time.Now().Format(".sync-conflict-20060102-150405-") + lastModBy + ext
+14 -19
View File
@@ -7,7 +7,8 @@
package model
import (
"sort"
"cmp"
"slices"
"time"
"github.com/syncthing/syncthing/lib/rand"
@@ -157,40 +158,34 @@ func (q *jobQueue) SortSmallestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(smallestFirst(q.queued))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(a.size, b.size)
})
}
func (q *jobQueue) SortLargestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(sort.Reverse(smallestFirst(q.queued)))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(b.size, a.size)
})
}
func (q *jobQueue) SortOldestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(oldestFirst(q.queued))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(a.modified, b.modified)
})
}
func (q *jobQueue) SortNewestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(sort.Reverse(oldestFirst(q.queued)))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(b.modified, a.modified)
})
}
// The usual sort.Interface boilerplate
type smallestFirst []jobQueueEntry
func (q smallestFirst) Len() int { return len(q) }
func (q smallestFirst) Less(a, b int) bool { return q[a].size < q[b].size }
func (q smallestFirst) Swap(a, b int) { q[a], q[b] = q[b], q[a] }
type oldestFirst []jobQueueEntry
func (q oldestFirst) Len() int { return len(q) }
func (q oldestFirst) Less(a, b int) bool { return q[a].modified < q[b].modified }
func (q oldestFirst) Swap(a, b int) { q[a], q[b] = q[b], q[a] }
+7 -14
View File
@@ -16,7 +16,8 @@ import (
"os"
"path/filepath"
rdebug "runtime/debug"
"sort"
"slices"
"strings"
"sync"
"testing"
@@ -145,7 +146,7 @@ func TestWalk(t *testing.T) {
}
tmp = append(tmp, f.File)
}
sort.Sort(fileList(tmp))
slices.SortFunc(fileList(tmp), compareByName)
files := fileList(tmp).testfiles()
if diff, equal := messagediff.PrettyDiff(testdata, files); !equal {
@@ -584,23 +585,15 @@ func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler, matcher *ignore.
tmp = append(tmp, f.File)
}
}
sort.Sort(fileList(tmp))
slices.SortFunc(fileList(tmp), compareByName)
return tmp
}
type fileList []protocol.FileInfo
func (l fileList) Len() int {
return len(l)
}
func (l fileList) Less(a, b int) bool {
return l[a].Name < l[b].Name
}
func (l fileList) Swap(a, b int) {
l[a], l[b] = l[b], l[a]
func compareByName(a, b protocol.FileInfo) int {
return strings.Compare(a.Name, b.Name)
}
func (l fileList) testfiles() testfileList {
@@ -825,7 +818,7 @@ func TestIssue4841(t *testing.T) {
}
files = append(files, f.File)
}
sort.Sort(fileList(files))
slices.SortFunc(fileList(files), compareByName)
if len(files) != 1 {
t.Fatalf("Expected 1 file, got %d: %v", len(files), files)
+5 -2
View File
@@ -8,7 +8,8 @@ package versioner
import (
"path/filepath"
"sort"
"slices"
"strings"
"github.com/syncthing/syncthing/lib/fs"
)
@@ -37,7 +38,9 @@ func (t emptyDirTracker) emptyDirs() []string {
for dir := range t {
empty = append(empty, dir)
}
sort.Sort(sort.Reverse(sort.StringSlice(empty)))
slices.SortFunc(empty, func(a, b string) int {
return strings.Compare(b, a)
})
return empty
}
+17 -31
View File
@@ -14,6 +14,7 @@ package main
import (
"bytes"
"cmp"
"fmt"
"io"
"log"
@@ -21,7 +22,7 @@ import (
"os"
"os/exec"
"regexp"
"sort"
"slices"
"strings"
)
@@ -102,7 +103,17 @@ func main() {
// Write author names in GUI about modal
getContributions(authors)
sort.Sort(byContributions(authors))
// Sort by contributions
slices.SortFunc(authors, func(a, b author) int {
// Sort first by log10(commits), then by name. This means that we first get
// an alphabetic list of people with >= 1000 commits, then a list of people
// with >= 100 commits, and so on.
if a.log10commits != b.log10commits {
return cmp.Compare(b.log10commits, a.log10commits)
}
return strings.Compare(a.name, b.name)
})
var lines []string
for _, author := range authors {
@@ -125,7 +136,10 @@ func main() {
// Write AUTHORS file
sort.Sort(byName(authors))
// Sort by author name
slices.SortFunc(authors, func(a, b author) int {
return strings.Compare(strings.ToLower(a.name), strings.ToLower(b.name))
})
out, err := os.Create("AUTHORS")
if err != nil {
@@ -298,34 +312,6 @@ func allAuthors() map[string]string {
return names
}
type byContributions []author
func (l byContributions) Len() int { return len(l) }
// Sort first by log10(commits), then by name. This means that we first get
// an alphabetic list of people with >= 1000 commits, then a list of people
// with >= 100 commits, and so on.
func (l byContributions) Less(a, b int) bool {
if l[a].log10commits != l[b].log10commits {
return l[a].log10commits > l[b].log10commits
}
return l[a].name < l[b].name
}
func (l byContributions) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
type byName []author
func (l byName) Len() int { return len(l) }
func (l byName) Less(a, b int) bool {
aname := strings.ToLower(l[a].name)
bname := strings.ToLower(l[b].name)
return aname < bname
}
func (l byName) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
// A simple string set type
type stringSet map[string]struct{}
+4 -16
View File
@@ -20,7 +20,7 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"slices"
"strings"
"testing"
"time"
@@ -375,7 +375,9 @@ func mergeDirectoryContents(c ...[]fileInfo) []fileInfo {
i++
}
sort.Sort(fileInfoList(res))
slices.SortFunc(res, func(a, b fileInfo) int {
return strings.Compare(a.name, b.name)
})
return res
}
@@ -404,20 +406,6 @@ func (f fileInfo) String() string {
return fmt.Sprintf("%s %04o %d %x", f.name, f.mode, f.mod, f.hash)
}
type fileInfoList []fileInfo
func (l fileInfoList) Len() int {
return len(l)
}
func (l fileInfoList) Less(a, b int) bool {
return l[a].name < l[b].name
}
func (l fileInfoList) Swap(a, b int) {
l[a], l[b] = l[b], l[a]
}
func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan error {
walker := func(path string, info os.FileInfo, err error) error {
if err != nil {