mirror of https://github.com/tailscale/tailscale/
util/checkchange: stop using deephash everywhere
Saves 45 KB from the min build, no longer pulling in deephash or util/hashx, both with unsafe code. It can actually be more efficient to not use deephash, as you don't have to walk all bytes of all fields recursively to answer that two things are not equal. Instead, you can just return false at the first difference you see. And then with views (as we use ~everywhere nowadays), the cloning the old value isn't expensive, as it's just a pointer under the hood. Updates #12614 Change-Id: I7b08616b8a09b3ade454bb5e0ac5672086fe8aec Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/17475/head
parent
28b1b4c3c1
commit
316afe7d02
@ -0,0 +1,25 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package checkchange defines a utility for determining whether a value
|
||||
// has changed since the last time it was checked.
|
||||
package checkchange
|
||||
|
||||
// EqualCloner is an interface for types that can be compared for equality
|
||||
// and can be cloned.
|
||||
type EqualCloner[T any] interface {
|
||||
Equal(T) bool
|
||||
Clone() T
|
||||
}
|
||||
|
||||
// Update sets *old to a clone of new if they are not equal, returning whether
|
||||
// they were different.
|
||||
//
|
||||
// It only modifies *old if they are different. old must be non-nil.
|
||||
func Update[T EqualCloner[T]](old *T, new T) (changed bool) {
|
||||
if new.Equal(*old) {
|
||||
return false
|
||||
}
|
||||
*old = new.Clone()
|
||||
return true
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package wgcfg
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests that [Config.Equal] tests all fields of [Config], even ones
|
||||
// that might get added in the future.
|
||||
func TestConfigEqual(t *testing.T) {
|
||||
rt := reflect.TypeFor[Config]()
|
||||
for i := range rt.NumField() {
|
||||
sf := rt.Field(i)
|
||||
switch sf.Name {
|
||||
case "Name", "NodeID", "PrivateKey", "MTU", "Addresses", "DNS", "Peers",
|
||||
"NetworkLogging":
|
||||
// These are compared in [Config.Equal].
|
||||
default:
|
||||
t.Errorf("Have you added field %q to Config.Equal? Do so if not, and then update TestConfigEqual", sf.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that [Peer.Equal] tests all fields of [Peer], even ones
|
||||
// that might get added in the future.
|
||||
func TestPeerEqual(t *testing.T) {
|
||||
rt := reflect.TypeFor[Peer]()
|
||||
for i := range rt.NumField() {
|
||||
sf := rt.Field(i)
|
||||
switch sf.Name {
|
||||
case "PublicKey", "DiscoKey", "AllowedIPs", "IsJailed",
|
||||
"PersistentKeepalive", "V4MasqAddr", "V6MasqAddr", "WGEndpoint":
|
||||
// These are compared in [Peer.Equal].
|
||||
default:
|
||||
t.Errorf("Have you added field %q to Peer.Equal? Do so if not, and then update TestPeerEqual", sf.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue