瀏覽代碼

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.
Marcel Meyer 4 月之前
父節點
當前提交
598915193a

+ 4 - 2
cmd/syncthing/crash_reporting.go

@@ -14,7 +14,7 @@ import (
 	"net/http"
 	"net/http"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
-	"sort"
+	"slices"
 	"strings"
 	"strings"
 	"time"
 	"time"
 )
 )
@@ -37,7 +37,9 @@ func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
 		return
 		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 {
 	for _, file := range files {
 		if strings.Contains(file, ".reported.") {
 		if strings.Contains(file, ".reported.") {
 			// We've already sent this file. It'll be cleaned out at some
 			// We've already sent this file. It'll be cleaned out at some

+ 2 - 2
lib/api/tokenmanager.go

@@ -117,8 +117,8 @@ func (m *tokenManager) saveLocked() {
 		for token, expiry := range m.tokens.Tokens {
 		for token, expiry := range m.tokens.Tokens {
 			tokens = append(tokens, tokenExpiry{token, expiry})
 			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.
 		// Remove the oldest tokens.
 		for _, token := range tokens[:len(tokens)-m.maxItems] {
 		for _, token := range tokens[:len(tokens)-m.maxItems] {

+ 2 - 3
lib/config/config.go

@@ -18,7 +18,6 @@ import (
 	"os"
 	"os"
 	"reflect"
 	"reflect"
 	"slices"
 	"slices"
-	"sort"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
@@ -338,8 +337,8 @@ func (cfg *Configuration) prepareDeviceList() map[protocol.DeviceID]*DeviceConfi
 	// - sorted by ID
 	// - sorted by ID
 	// Happen before preparting folders as that needs a correct device list.
 	// Happen before preparting folders as that needs a correct device list.
 	cfg.Devices = ensureNoDuplicateOrEmptyIDDevices(cfg.Devices)
 	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
 	// Build a list of available devices

+ 3 - 3
lib/config/deviceconfiguration.go

@@ -8,7 +8,7 @@ package config
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"sort"
+	"slices"
 
 
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 )
 )
@@ -100,8 +100,8 @@ func sortedObservedFolderSlice(input map[string]ObservedFolder) []ObservedFolder
 	for _, folder := range input {
 	for _, folder := range input {
 		output = append(output, folder)
 		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
 	return output
 }
 }

+ 3 - 3
lib/config/folderconfiguration.go

@@ -15,7 +15,7 @@ import (
 	"fmt"
 	"fmt"
 	"path"
 	"path"
 	"path/filepath"
 	"path/filepath"
-	"sort"
+	"slices"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -291,8 +291,8 @@ func (f *FolderConfiguration) prepare(myID protocol.DeviceID, existingDevices ma
 	f.Devices = ensureDevicePresent(f.Devices, myID)
 	f.Devices = ensureDevicePresent(f.Devices, myID)
 	f.Devices = ensureNoUntrustedTrustingSharing(f, f.Devices, existingDevices)
 	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 {
 	if f.RescanIntervalS > MaxRescanIntervalS {

+ 3 - 3
lib/db/meta_test.go

@@ -8,7 +8,7 @@ package db
 
 
 import (
 import (
 	"math/bits"
 	"math/bits"
-	"sort"
+	"slices"
 	"testing"
 	"testing"
 
 
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/events"
@@ -71,8 +71,8 @@ func TestMetaDevices(t *testing.T) {
 	}
 	}
 
 
 	// Check that we got the two devices we expect
 	// 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 {
 	if devs[0] != d1 {
 		t.Error("first device should be d1")
 		t.Error("first device should be d1")

+ 19 - 26
lib/db/set_test.go

@@ -11,7 +11,8 @@ import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
-	"sort"
+	"slices"
+	"strings"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
@@ -102,16 +103,8 @@ func needList(t testing.TB, s *db.FileSet, n protocol.DeviceID) []protocol.FileI
 
 
 type fileList []protocol.FileInfo
 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 {
 func (l fileList) String() string {
@@ -218,7 +211,7 @@ func TestGlobalSet(t *testing.T) {
 		t.Helper()
 		t.Helper()
 
 
 		g := fileList(globalList(t, m))
 		g := fileList(globalList(t, m))
-		sort.Sort(g)
+		slices.SortFunc(g, compareByName)
 
 
 		if fmt.Sprint(g) != fmt.Sprint(expectedGlobal) {
 		if fmt.Sprint(g) != fmt.Sprint(expectedGlobal) {
 			t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, 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))
 		h := fileList(haveList(t, m, protocol.LocalDeviceID))
-		sort.Sort(h)
+		slices.SortFunc(h, compareByName)
 
 
 		if fmt.Sprint(h) != fmt.Sprint(localTot) {
 		if fmt.Sprint(h) != fmt.Sprint(localTot) {
 			t.Errorf("Have incorrect (local);\n A: %v !=\n E: %v", h, 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))
 		h = fileList(haveList(t, m, remoteDevice0))
-		sort.Sort(h)
+		slices.SortFunc(h, compareByName)
 
 
 		if fmt.Sprint(h) != fmt.Sprint(remoteTot) {
 		if fmt.Sprint(h) != fmt.Sprint(remoteTot) {
 			t.Errorf("Have incorrect (remote);\n A: %v !=\n E: %v", h, remoteTot)
 			t.Errorf("Have incorrect (remote);\n A: %v !=\n E: %v", h, remoteTot)
 		}
 		}
 
 
 		n := fileList(needList(t, m, protocol.LocalDeviceID))
 		n := fileList(needList(t, m, protocol.LocalDeviceID))
-		sort.Sort(n)
+		slices.SortFunc(n, compareByName)
 
 
 		if fmt.Sprint(n) != fmt.Sprint(expectedLocalNeed) {
 		if fmt.Sprint(n) != fmt.Sprint(expectedLocalNeed) {
 			t.Errorf("Need incorrect (local);\n A: %v !=\n E: %v", n, 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)
 		checkNeed(t, m, protocol.LocalDeviceID, expectedLocalNeed)
 
 
 		n = fileList(needList(t, m, remoteDevice0))
 		n = fileList(needList(t, m, remoteDevice0))
-		sort.Sort(n)
+		slices.SortFunc(n, compareByName)
 
 
 		if fmt.Sprint(n) != fmt.Sprint(expectedRemoteNeed) {
 		if fmt.Sprint(n) != fmt.Sprint(expectedRemoteNeed) {
 			t.Errorf("Need incorrect (remote);\n A: %v !=\n E: %v", n, expectedRemoteNeed)
 			t.Errorf("Need incorrect (remote);\n A: %v !=\n E: %v", n, expectedRemoteNeed)
@@ -428,14 +421,14 @@ func TestGlobalSet(t *testing.T) {
 	check()
 	check()
 
 
 	h := fileList(haveList(t, m, remoteDevice1))
 	h := fileList(haveList(t, m, remoteDevice1))
-	sort.Sort(h)
+	slices.SortFunc(h, compareByName)
 
 
 	if fmt.Sprint(h) != fmt.Sprint(secRemote) {
 	if fmt.Sprint(h) != fmt.Sprint(secRemote) {
 		t.Errorf("Have incorrect (secRemote);\n A: %v !=\n E: %v", h, secRemote)
 		t.Errorf("Have incorrect (secRemote);\n A: %v !=\n E: %v", h, secRemote)
 	}
 	}
 
 
 	n := fileList(needList(t, m, remoteDevice1))
 	n := fileList(needList(t, m, remoteDevice1))
-	sort.Sort(n)
+	slices.SortFunc(n, compareByName)
 
 
 	if fmt.Sprint(n) != fmt.Sprint(expectedSecRemoteNeed) {
 	if fmt.Sprint(n) != fmt.Sprint(expectedSecRemoteNeed) {
 		t.Errorf("Need incorrect (secRemote);\n A: %v !=\n E: %v", n, 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)
 	replace(s, remoteDevice1, remote1Have)
 
 
 	need := fileList(needList(t, s, protocol.LocalDeviceID))
 	need := fileList(needList(t, s, protocol.LocalDeviceID))
-	sort.Sort(need)
+	slices.SortFunc(need, compareByName)
 
 
 	if fmt.Sprint(need) != fmt.Sprint(expectedNeed) {
 	if fmt.Sprint(need) != fmt.Sprint(expectedNeed) {
 		t.Errorf("Need incorrect;\n A: %v !=\n E: %v", need, 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)
 	replace(s, protocol.LocalDeviceID, localHave)
 
 
 	have := fileList(haveList(t, s, protocol.LocalDeviceID))
 	have := fileList(haveList(t, s, protocol.LocalDeviceID))
-	sort.Sort(have)
+	slices.SortFunc(have, compareByName)
 
 
 	if fmt.Sprint(have) != fmt.Sprint(localHave) {
 	if fmt.Sprint(have) != fmt.Sprint(localHave) {
 		t.Errorf("Have incorrect before invalidation;\n A: %v !=\n E: %v", have, 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]))
 	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) {
 	if fmt.Sprint(have) != fmt.Sprint(localHave) {
 		t.Errorf("Have incorrect after invalidation;\n A: %v !=\n E: %v", have, 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)
 	replace(m, protocol.LocalDeviceID, local)
 	g := globalList(t, m)
 	g := globalList(t, m)
-	sort.Sort(fileList(g))
+	slices.SortFunc(g, compareByName)
 
 
 	if diff, equal := messagediff.PrettyDiff(local, g); !equal {
 	if diff, equal := messagediff.PrettyDiff(local, g); !equal {
 		t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff)
 		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)
 	replace(m, remoteDevice0, nil)
 
 
 	g = globalList(t, m)
 	g = globalList(t, m)
-	sort.Sort(fileList(g))
+	slices.SortFunc(g, compareByName)
 
 
 	if diff, equal := messagediff.PrettyDiff(local, g); !equal {
 	if diff, equal := messagediff.PrettyDiff(local, g); !equal {
 		t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff)
 		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)
 	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) {
 	if fmt.Sprint(need) != fmt.Sprint(shouldNeed) {
 		t.Errorf("Need incorrect;\n%v !=\n%v", need, shouldNeed)
 		t.Errorf("Need incorrect;\n%v !=\n%v", need, shouldNeed)

+ 3 - 3
lib/model/blockpullreorderer.go

@@ -7,7 +7,7 @@
 package model
 package model
 
 
 import (
 import (
-	"sort"
+	"slices"
 
 
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
@@ -52,8 +52,8 @@ type standardBlockPullReorderer struct {
 
 
 func newStandardBlockPullReorderer(id protocol.DeviceID, otherDevices []protocol.DeviceID) *standardBlockPullReorderer {
 func newStandardBlockPullReorderer(id protocol.DeviceID, otherDevices []protocol.DeviceID) *standardBlockPullReorderer {
 	allDevices := append(otherDevices, id)
 	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
 	// Find our index
 	myIndex := -1
 	myIndex := -1

+ 3 - 3
lib/model/blockpullreorderer_test.go

@@ -8,7 +8,7 @@ package model
 
 
 import (
 import (
 	"reflect"
 	"reflect"
-	"sort"
+	"slices"
 	"testing"
 	"testing"
 
 
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
@@ -65,8 +65,8 @@ func Test_inOrderBlockPullReorderer_Reorder(t *testing.T) {
 func Test_standardBlockPullReorderer_Reorder(t *testing.T) {
 func Test_standardBlockPullReorderer_Reorder(t *testing.T) {
 	// Order the devices, so we know their ordering ahead of time.
 	// Order the devices, so we know their ordering ahead of time.
 	devices := []protocol.DeviceID{myID, device1, device2}
 	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 {
 	blocks := func(i ...int) []protocol.BlockInfo {

+ 4 - 2
lib/model/folder.go

@@ -13,7 +13,7 @@ import (
 	"math/rand"
 	"math/rand"
 	"path/filepath"
 	"path/filepath"
 	"slices"
 	"slices"
-	"sort"
+	"strings"
 	"time"
 	"time"
 
 
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/config"
@@ -1201,7 +1201,9 @@ func (f *folder) Errors() []FileError {
 	errors := make([]FileError, scanLen+len(f.pullErrors))
 	errors := make([]FileError, scanLen+len(f.pullErrors))
 	copy(errors[:scanLen], f.scanErrors)
 	copy(errors[:scanLen], f.scanErrors)
 	copy(errors[scanLen:], f.pullErrors)
 	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
 	return errors
 }
 }
 
 

+ 5 - 2
lib/model/folder_recvenc.go

@@ -8,7 +8,8 @@ package model
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"sort"
+	"slices"
+	"strings"
 
 
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/db"
@@ -112,7 +113,9 @@ func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string, snap *db.Snapsh
 	go f.pullScannerRoutine(scanChan)
 	go f.pullScannerRoutine(scanChan)
 	defer close(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 {
 	for _, dir := range dirs {
 		if err := f.deleteDirOnDisk(dir, snap, scanChan); err != nil {
 		if err := f.deleteDirOnDisk(dir, snap, scanChan); err != nil {
 			f.newScanError(dir, fmt.Errorf("deleting unexpected dir: %w", err))
 			f.newScanError(dir, fmt.Errorf("deleting unexpected dir: %w", err))

+ 5 - 2
lib/model/folder_recvonly.go

@@ -7,7 +7,8 @@
 package model
 package model
 
 
 import (
 import (
-	"sort"
+	"slices"
+	"strings"
 	"time"
 	"time"
 
 
 	"github.com/syncthing/syncthing/lib/config"
 	"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) {
 func (q *deleteQueue) flush(snap *db.Snapshot) ([]string, error) {
 	// Process directories from the leaves inward.
 	// 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 firstError error
 	var deleted []string
 	var deleted []string

+ 4 - 16
lib/model/folder_sendrecv.go

@@ -14,7 +14,7 @@ import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"path/filepath"
 	"path/filepath"
-	"sort"
+	"slices"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
@@ -1867,7 +1867,9 @@ func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan cha
 	if f.MaxConflicts > -1 {
 	if f.MaxConflicts > -1 {
 		matches := existingConflicts(name, f.mtimefs)
 		matches := existingConflicts(name, f.mtimefs)
 		if len(matches) > f.MaxConflicts {
 		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:] {
 			for _, match := range matches[f.MaxConflicts:] {
 				if gerr := f.mtimefs.Remove(match); gerr != nil {
 				if gerr := f.mtimefs.Remove(match); gerr != nil {
 					l.Debugln(f, "removing extra conflict", gerr)
 					l.Debugln(f, "removing extra conflict", gerr)
@@ -2206,20 +2208,6 @@ type FileError struct {
 	Err  string `json:"error"`
 	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 {
 func conflictName(name, lastModBy string) string {
 	ext := filepath.Ext(name)
 	ext := filepath.Ext(name)
 	return name[:len(name)-len(ext)] + time.Now().Format(".sync-conflict-20060102-150405-") + lastModBy + ext
 	return name[:len(name)-len(ext)] + time.Now().Format(".sync-conflict-20060102-150405-") + lastModBy + ext

+ 14 - 19
lib/model/queue.go

@@ -7,7 +7,8 @@
 package model
 package model
 
 
 import (
 import (
-	"sort"
+	"cmp"
+	"slices"
 	"time"
 	"time"
 
 
 	"github.com/syncthing/syncthing/lib/rand"
 	"github.com/syncthing/syncthing/lib/rand"
@@ -157,40 +158,34 @@ func (q *jobQueue) SortSmallestFirst() {
 	q.mut.Lock()
 	q.mut.Lock()
 	defer q.mut.Unlock()
 	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() {
 func (q *jobQueue) SortLargestFirst() {
 	q.mut.Lock()
 	q.mut.Lock()
 	defer q.mut.Unlock()
 	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() {
 func (q *jobQueue) SortOldestFirst() {
 	q.mut.Lock()
 	q.mut.Lock()
 	defer q.mut.Unlock()
 	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() {
 func (q *jobQueue) SortNewestFirst() {
 	q.mut.Lock()
 	q.mut.Lock()
 	defer q.mut.Unlock()
 	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
lib/scanner/walk_test.go

@@ -16,7 +16,8 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	rdebug "runtime/debug"
 	rdebug "runtime/debug"
-	"sort"
+	"slices"
+	"strings"
 	"sync"
 	"sync"
 	"testing"
 	"testing"
 
 
@@ -145,7 +146,7 @@ func TestWalk(t *testing.T) {
 		}
 		}
 		tmp = append(tmp, f.File)
 		tmp = append(tmp, f.File)
 	}
 	}
-	sort.Sort(fileList(tmp))
+	slices.SortFunc(fileList(tmp), compareByName)
 	files := fileList(tmp).testfiles()
 	files := fileList(tmp).testfiles()
 
 
 	if diff, equal := messagediff.PrettyDiff(testdata, files); !equal {
 	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)
 			tmp = append(tmp, f.File)
 		}
 		}
 	}
 	}
-	sort.Sort(fileList(tmp))
+	slices.SortFunc(fileList(tmp), compareByName)
 
 
 	return tmp
 	return tmp
 }
 }
 
 
 type fileList []protocol.FileInfo
 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 {
 func (l fileList) testfiles() testfileList {
@@ -825,7 +818,7 @@ func TestIssue4841(t *testing.T) {
 		}
 		}
 		files = append(files, f.File)
 		files = append(files, f.File)
 	}
 	}
-	sort.Sort(fileList(files))
+	slices.SortFunc(fileList(files), compareByName)
 
 
 	if len(files) != 1 {
 	if len(files) != 1 {
 		t.Fatalf("Expected 1 file, got %d: %v", len(files), files)
 		t.Fatalf("Expected 1 file, got %d: %v", len(files), files)

+ 5 - 2
lib/versioner/empty_dir_tracker.go

@@ -8,7 +8,8 @@ package versioner
 
 
 import (
 import (
 	"path/filepath"
 	"path/filepath"
-	"sort"
+	"slices"
+	"strings"
 
 
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/fs"
 )
 )
@@ -37,7 +38,9 @@ func (t emptyDirTracker) emptyDirs() []string {
 	for dir := range t {
 	for dir := range t {
 		empty = append(empty, dir)
 		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
 	return empty
 }
 }
 
 

+ 17 - 31
script/authors.go

@@ -14,6 +14,7 @@ package main
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"cmp"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"log"
 	"log"
@@ -21,7 +22,7 @@ import (
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"regexp"
 	"regexp"
-	"sort"
+	"slices"
 	"strings"
 	"strings"
 )
 )
 
 
@@ -102,7 +103,17 @@ func main() {
 	// Write author names in GUI about modal
 	// Write author names in GUI about modal
 
 
 	getContributions(authors)
 	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
 	var lines []string
 	for _, author := range authors {
 	for _, author := range authors {
@@ -125,7 +136,10 @@ func main() {
 
 
 	// Write AUTHORS file
 	// 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")
 	out, err := os.Create("AUTHORS")
 	if err != nil {
 	if err != nil {
@@ -298,34 +312,6 @@ func allAuthors() map[string]string {
 	return names
 	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
 // A simple string set type
 
 
 type stringSet map[string]struct{}
 type stringSet map[string]struct{}

+ 4 - 16
test/util.go

@@ -20,7 +20,7 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"runtime"
 	"runtime"
-	"sort"
+	"slices"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 	"time"
 	"time"
@@ -375,7 +375,9 @@ func mergeDirectoryContents(c ...[]fileInfo) []fileInfo {
 		i++
 		i++
 	}
 	}
 
 
-	sort.Sort(fileInfoList(res))
+	slices.SortFunc(res, func(a, b fileInfo) int {
+		return strings.Compare(a.name, b.name)
+	})
 	return res
 	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)
 	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 {
 func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan error {
 	walker := func(path string, info os.FileInfo, err error) error {
 	walker := func(path string, info os.FileInfo, err error) error {
 		if err != nil {
 		if err != nil {