all: update exp/slices and fix call sites

slices.SortFunc suffered a late-in-cycle API breakage.

Updates #cleanup

Signed-off-by: David Anderson <danderson@tailscale.com>
pull/8741/head
David Anderson 1 year ago committed by Dave Anderson
parent 90a7d3066c
commit 52212f4323

@ -47,7 +47,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
L github.com/vishvananda/netns from github.com/tailscale/netlink+ L github.com/vishvananda/netns from github.com/tailscale/netlink+
github.com/x448/float16 from github.com/fxamacker/cbor/v2 github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/client/tailscale+ 💣 go4.org/mem from tailscale.com/client/tailscale+
go4.org/netipx from tailscale.com/wgengine/filter go4.org/netipx from tailscale.com/wgengine/filter+
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+ W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
google.golang.org/protobuf/encoding/prototext from github.com/golang/protobuf/proto+ google.golang.org/protobuf/encoding/prototext from github.com/golang/protobuf/proto+
google.golang.org/protobuf/encoding/protowire from github.com/golang/protobuf/proto+ google.golang.org/protobuf/encoding/protowire from github.com/golang/protobuf/proto+

@ -45,6 +45,7 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"tailscale.com/types/logid" "tailscale.com/types/logid"
"tailscale.com/types/netlogtype" "tailscale.com/types/netlogtype"
"tailscale.com/util/cmpx"
"tailscale.com/util/must" "tailscale.com/util/must"
) )
@ -151,10 +152,10 @@ func printMessage(msg message) {
if len(traffic) == 0 { if len(traffic) == 0 {
return return
} }
slices.SortFunc(traffic, func(x, y netlogtype.ConnectionCounts) bool { slices.SortFunc(traffic, func(x, y netlogtype.ConnectionCounts) int {
nx := x.TxPackets + x.TxBytes + x.RxPackets + x.RxBytes nx := x.TxPackets + x.TxBytes + x.RxPackets + x.RxBytes
ny := y.TxPackets + y.TxBytes + y.RxPackets + y.RxBytes ny := y.TxPackets + y.TxBytes + y.RxPackets + y.RxBytes
return nx > ny return cmpx.Compare(ny, nx)
}) })
var sum netlogtype.Counts var sum netlogtype.Counts
for _, cc := range traffic { for _, cc := range traffic {

@ -18,6 +18,7 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/util/cmpx"
) )
var exitNodeCmd = &ffcli.Command{ var exitNodeCmd = &ffcli.Command{
@ -227,19 +228,21 @@ func filterFormatAndSortExitNodes(peers []*ipnstate.PeerStatus, filterBy string)
// sortPeersByPriority sorts a slice of PeerStatus // sortPeersByPriority sorts a slice of PeerStatus
// by location.Priority, in order of highest priority. // by location.Priority, in order of highest priority.
func sortPeersByPriority(peers []*ipnstate.PeerStatus) { func sortPeersByPriority(peers []*ipnstate.PeerStatus) {
slices.SortFunc(peers, func(a, b *ipnstate.PeerStatus) bool { return a.Location.Priority > b.Location.Priority }) slices.SortStableFunc(peers, func(a, b *ipnstate.PeerStatus) int {
return cmpx.Compare(b.Location.Priority, a.Location.Priority)
})
} }
// sortByCityName sorts a slice of filteredCity alphabetically // sortByCityName sorts a slice of filteredCity alphabetically
// by name. The '-' used to indicate no location data will always // by name. The '-' used to indicate no location data will always
// be sorted to the front of the slice. // be sorted to the front of the slice.
func sortByCityName(cities []*filteredCity) { func sortByCityName(cities []*filteredCity) {
slices.SortFunc(cities, func(a, b *filteredCity) bool { return a.Name < b.Name }) slices.SortStableFunc(cities, func(a, b *filteredCity) int { return strings.Compare(a.Name, b.Name) })
} }
// sortByCountryName sorts a slice of filteredCountry alphabetically // sortByCountryName sorts a slice of filteredCountry alphabetically
// by name. The '-' used to indicate no location data will always // by name. The '-' used to indicate no location data will always
// be sorted to the front of the slice. // be sorted to the front of the slice.
func sortByCountryName(countries []*filteredCountry) { func sortByCountryName(countries []*filteredCountry) {
slices.SortFunc(countries, func(a, b *filteredCountry) bool { return a.Name < b.Name }) slices.SortStableFunc(countries, func(a, b *filteredCountry) int { return strings.Compare(a.Name, b.Name) })
} }

@ -52,7 +52,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
L github.com/vishvananda/netns from github.com/tailscale/netlink+ L github.com/vishvananda/netns from github.com/tailscale/netlink+
github.com/x448/float16 from github.com/fxamacker/cbor/v2 github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/derp+ 💣 go4.org/mem from tailscale.com/derp+
go4.org/netipx from tailscale.com/wgengine/filter go4.org/netipx from tailscale.com/wgengine/filter+
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+ W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
gopkg.in/yaml.v2 from sigs.k8s.io/yaml gopkg.in/yaml.v2 from sigs.k8s.io/yaml
k8s.io/client-go/util/homedir from tailscale.com/cmd/tailscale/cli k8s.io/client-go/util/homedir from tailscale.com/cmd/tailscale/cli

@ -73,10 +73,10 @@ require (
github.com/vishvananda/netns v0.0.4 github.com/vishvananda/netns v0.0.4
go.uber.org/zap v1.24.0 go.uber.org/zap v1.24.0
go4.org/mem v0.0.0-20220726221520-4f986261bf13 go4.org/mem v0.0.0-20220726221520-4f986261bf13
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516
golang.org/x/crypto v0.11.0 golang.org/x/crypto v0.11.0
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
golang.org/x/mod v0.10.0 golang.org/x/mod v0.11.0
golang.org/x/net v0.10.0 golang.org/x/net v0.10.0
golang.org/x/oauth2 v0.7.0 golang.org/x/oauth2 v0.7.0
golang.org/x/sync v0.2.0 golang.org/x/sync v0.2.0

@ -1183,8 +1183,8 @@ go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 h1:nJAwRlGWZZDOD+6wni9KVUNHMpHko/OnRwsrCYeAzPo= go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 h1:X66ZEoMN2SuaoI/dfZVYobB6E5zjZyyHUMWlCA7MgGE=
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y= go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -1222,8 +1222,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20230425010034-47ecfdc1ba53 h1:w/MOPdQ1IoYoDou3L55ZbTx2Nhn7JAhX1BBZor8qChU= golang.org/x/exp/typeparams v0.0.0-20230425010034-47ecfdc1ba53 h1:w/MOPdQ1IoYoDou3L55ZbTx2Nhn7JAhX1BBZor8qChU=
@ -1261,8 +1261,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

@ -902,8 +902,8 @@ func (h *peerAPIHandler) handleServeSockStats(w http.ResponseWriter, r *http.Req
for label := range stats.Stats { for label := range stats.Stats {
labels = append(labels, label) labels = append(labels, label)
} }
slices.SortFunc(labels, func(a, b sockstats.Label) bool { slices.SortFunc(labels, func(a, b sockstats.Label) int {
return a.String() < b.String() return strings.Compare(a.String(), b.String())
}) })
txTotal := uint64(0) txTotal := uint64(0)

@ -298,8 +298,8 @@ func (pm *profileManager) writePrefsToStore(key ipn.StateKey, prefs ipn.PrefsVie
// Profiles returns the list of known profiles. // Profiles returns the list of known profiles.
func (pm *profileManager) Profiles() []ipn.LoginProfile { func (pm *profileManager) Profiles() []ipn.LoginProfile {
profiles := pm.matchingProfiles(func(*ipn.LoginProfile) bool { return true }) profiles := pm.matchingProfiles(func(*ipn.LoginProfile) bool { return true })
slices.SortFunc(profiles, func(a, b *ipn.LoginProfile) bool { slices.SortFunc(profiles, func(a, b *ipn.LoginProfile) int {
return a.Name < b.Name return strings.Compare(a.Name, b.Name)
}) })
out := make([]ipn.LoginProfile, 0, len(profiles)) out := make([]ipn.LoginProfile, 0, len(profiles))
for _, p := range profiles { for _, p := range profiles {

@ -12,6 +12,7 @@ import (
"net" "net"
"net/netip" "net/netip"
"runtime" "runtime"
"strings"
"sync/atomic" "sync/atomic"
"time" "time"
@ -139,14 +140,15 @@ func compileHostEntries(cfg Config) (hosts []*HostEntry) {
} }
} }
} }
slices.SortFunc(hosts, func(a, b *HostEntry) bool { slices.SortFunc(hosts, func(a, b *HostEntry) int {
if len(a.Hosts) == 0 { if len(a.Hosts) == 0 && len(b.Hosts) == 0 {
return false return 0
} else if len(a.Hosts) == 0 {
return -1
} else if len(b.Hosts) == 0 {
return 1
} }
if len(b.Hosts) == 0 { return strings.Compare(a.Hosts[0], b.Hosts[0])
return true
}
return a.Hosts[0] < b.Hosts[0]
}) })
return hosts return hosts
} }

@ -366,8 +366,8 @@ func TestBasicRecursion(t *testing.T) {
netip.MustParseAddr("2600:9000:a602:b1e6:86d:8165:5e8c:295b"), netip.MustParseAddr("2600:9000:a602:b1e6:86d:8165:5e8c:295b"),
netip.MustParseAddr("2600:9000:a51d:27c1:1530:b9ef:2a6:b9e5"), netip.MustParseAddr("2600:9000:a51d:27c1:1530:b9ef:2a6:b9e5"),
} }
slices.SortFunc(addrs, func(x, y netip.Addr) bool { return x.String() < y.String() }) slices.SortFunc(addrs, func(x, y netip.Addr) int { return strings.Compare(x.String(), y.String()) })
slices.SortFunc(wantAddrs, func(x, y netip.Addr) bool { return x.String() < y.String() }) slices.SortFunc(wantAddrs, func(x, y netip.Addr) int { return strings.Compare(x.String(), y.String()) })
if !reflect.DeepEqual(addrs, wantAddrs) { if !reflect.DeepEqual(addrs, wantAddrs) {
t.Errorf("got addrs=%+v; want %+v", addrs, wantAddrs) t.Errorf("got addrs=%+v; want %+v", addrs, wantAddrs)
@ -485,8 +485,8 @@ func TestRecursionCNAME(t *testing.T) {
netip.MustParseAddr("13.248.141.131"), netip.MustParseAddr("13.248.141.131"),
netip.MustParseAddr("2600:9000:a602:b1e6:86d:8165:5e8c:295b"), netip.MustParseAddr("2600:9000:a602:b1e6:86d:8165:5e8c:295b"),
} }
slices.SortFunc(addrs, func(x, y netip.Addr) bool { return x.String() < y.String() }) slices.SortFunc(addrs, func(x, y netip.Addr) int { return strings.Compare(x.String(), y.String()) })
slices.SortFunc(wantAddrs, func(x, y netip.Addr) bool { return x.String() < y.String() }) slices.SortFunc(wantAddrs, func(x, y netip.Addr) int { return strings.Compare(x.String(), y.String()) })
if !reflect.DeepEqual(addrs, wantAddrs) { if !reflect.DeepEqual(addrs, wantAddrs) {
t.Errorf("got addrs=%+v; want %+v", addrs, wantAddrs) t.Errorf("got addrs=%+v; want %+v", addrs, wantAddrs)
@ -590,8 +590,8 @@ func TestRecursionNoGlue(t *testing.T) {
netip.MustParseAddr("13.248.141.131"), netip.MustParseAddr("13.248.141.131"),
netip.MustParseAddr("2600:9000:a602:b1e6:86d:8165:5e8c:295b"), netip.MustParseAddr("2600:9000:a602:b1e6:86d:8165:5e8c:295b"),
} }
slices.SortFunc(addrs, func(x, y netip.Addr) bool { return x.String() < y.String() }) slices.SortFunc(addrs, func(x, y netip.Addr) int { return strings.Compare(x.String(), y.String()) })
slices.SortFunc(wantAddrs, func(x, y netip.Addr) bool { return x.String() < y.String() }) slices.SortFunc(wantAddrs, func(x, y netip.Addr) int { return strings.Compare(x.String(), y.String()) })
if !reflect.DeepEqual(addrs, wantAddrs) { if !reflect.DeepEqual(addrs, wantAddrs) {
t.Errorf("got addrs=%+v; want %+v", addrs, wantAddrs) t.Errorf("got addrs=%+v; want %+v", addrs, wantAddrs)

@ -22,6 +22,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"go4.org/netipx"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"tailscale.com/atomicfile" "tailscale.com/atomicfile"
"tailscale.com/envknob" "tailscale.com/envknob"
@ -76,11 +77,11 @@ func MakeLookupFunc(logf logger.Logf, netMon *netmon.Monitor) func(ctx context.C
metricRecursiveErrors.Add(1) metricRecursiveErrors.Add(1)
return return
} }
slices.SortFunc(addrs, func(a, b netip.Addr) bool { return a.Less(b) }) slices.SortFunc(addrs, netipx.CompareAddr)
// Wait for a response from the main function // Wait for a response from the main function
oldAddrs := <-addrsCh oldAddrs := <-addrsCh
slices.SortFunc(oldAddrs, func(a, b netip.Addr) bool { return a.Less(b) }) slices.SortFunc(oldAddrs, netipx.CompareAddr)
matches := slices.Equal(addrs, oldAddrs) matches := slices.Equal(addrs, oldAddrs)

@ -10,6 +10,7 @@ import (
"net/netip" "net/netip"
"sync" "sync"
"go4.org/netipx"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"tailscale.com/net/netaddr" "tailscale.com/net/netaddr"
) )
@ -252,12 +253,7 @@ func ExitRoutes() []netip.Prefix { return []netip.Prefix{allIPv4, allIPv6} }
// SortPrefixes sorts the prefixes in place. // SortPrefixes sorts the prefixes in place.
func SortPrefixes(p []netip.Prefix) { func SortPrefixes(p []netip.Prefix) {
slices.SortFunc(p, func(ri, rj netip.Prefix) bool { slices.SortFunc(p, netipx.ComparePrefix)
if ri.Addr() == rj.Addr() {
return ri.Bits() < rj.Bits()
}
return ri.Addr().Less(rj.Addr())
})
} }
// FilterPrefixes returns a new slice, not aliasing in, containing elements of // FilterPrefixes returns a new slice, not aliasing in, containing elements of

@ -20,3 +20,40 @@ func Or[T comparable](list ...T) T {
} }
return zero return zero
} }
// Ordered is cmp.Ordered from Go 1.21.
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}
// Compare returns
//
// -1 if x is less than y,
// 0 if x equals y,
// +1 if x is greater than y.
//
// For floating-point types, a NaN is considered less than any non-NaN,
// a NaN is considered equal to a NaN, and -0.0 is equal to 0.0.
func Compare[T Ordered](x, y T) int {
xNaN := isNaN(x)
yNaN := isNaN(y)
if xNaN && yNaN {
return 0
}
if xNaN || x < y {
return -1
}
if yNaN || x > y {
return +1
}
return 0
}
// isNaN reports whether x is a NaN without requiring the math package.
// This will always return false if T is not floating-point.
func isNaN[T Ordered](x T) bool {
return x != x
}

@ -2438,11 +2438,11 @@ func TestEndpointTracker(t *testing.T) {
got := et.update(tt.now, tt.eps) got := et.update(tt.now, tt.eps)
// Sort both arrays for comparison // Sort both arrays for comparison
slices.SortFunc(got, func(a, b tailcfg.Endpoint) bool { slices.SortFunc(got, func(a, b tailcfg.Endpoint) int {
return a.Addr.String() < b.Addr.String() return strings.Compare(a.Addr.String(), b.Addr.String())
}) })
slices.SortFunc(tt.want, func(a, b tailcfg.Endpoint) bool { slices.SortFunc(tt.want, func(a, b tailcfg.Endpoint) int {
return a.Addr.String() < b.Addr.String() return strings.Compare(a.Addr.String(), b.Addr.String())
}) })
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {

@ -396,7 +396,7 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) (retErr error) {
return fmt.Errorf("syncAddresses: %w", err) return fmt.Errorf("syncAddresses: %w", err)
} }
slices.SortFunc(routes, routeDataLess) slices.SortFunc(routes, routeDataCompare)
deduplicatedRoutes := []*winipcfg.RouteData{} deduplicatedRoutes := []*winipcfg.RouteData{}
for i := 0; i < len(routes); i++ { for i := 0; i < len(routes); i++ {
@ -652,8 +652,8 @@ func routeDataCompare(a, b *winipcfg.RouteData) int {
func deltaRouteData(a, b []*winipcfg.RouteData) (add, del []*winipcfg.RouteData) { func deltaRouteData(a, b []*winipcfg.RouteData) (add, del []*winipcfg.RouteData) {
add = make([]*winipcfg.RouteData, 0, len(b)) add = make([]*winipcfg.RouteData, 0, len(b))
del = make([]*winipcfg.RouteData, 0, len(a)) del = make([]*winipcfg.RouteData, 0, len(a))
slices.SortFunc(a, routeDataLess) slices.SortFunc(a, routeDataCompare)
slices.SortFunc(b, routeDataLess) slices.SortFunc(b, routeDataCompare)
i := 0 i := 0
j := 0 j := 0

@ -20,6 +20,7 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"go4.org/netipx"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
@ -1022,8 +1023,8 @@ func TestCIDRDiff(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
slices.SortFunc(added, func(a, b netip.Prefix) bool { return a.Addr().Less(b.Addr()) }) slices.SortFunc(added, netipx.ComparePrefix)
slices.SortFunc(deleted, func(a, b netip.Prefix) bool { return a.Addr().Less(b.Addr()) }) slices.SortFunc(deleted, netipx.ComparePrefix)
if !reflect.DeepEqual(added, tc.wantAdd) { if !reflect.DeepEqual(added, tc.wantAdd) {
t.Errorf("added = %v, want %v", added, tc.wantAdd) t.Errorf("added = %v, want %v", added, tc.wantAdd)
} }

Loading…
Cancel
Save