tailscale/util
Joe Tsai 539c5e44c5
util/deephash: always keep values addressable (#5328)
The logic of deephash is both simpler and easier to reason about
if values are always addressable.

In Go, the composite kinds are slices, arrays, maps, structs,
interfaces, pointers, channels, and functions,
where we define "composite" as a Go value that encapsulates
some other Go value (e.g., a map is a collection of key-value entries).

In the cases of pointers and slices, the sub-values are always addressable.

In the cases of arrays and structs, the sub-values are always addressable
if and only if the parent value is addressable.

In the case of maps and interfaces, the sub-values are never addressable.
To make them addressable, we need to copy them onto the heap.

For the purposes of deephash, we do not care about channels and functions.

For all non-composite kinds (e.g., strings and ints), they are only addressable
if obtained from one of the composite kinds that produce addressable values
(i.e., pointers, slices, addressable arrays, and addressable structs).
A non-addressible, non-composite kind can be made addressable by
allocating it on the heap, obtaining a pointer to it, and dereferencing it.

Thus, if we can ensure that values are addressable at the entry points,
and shallow copy sub-values whenever we encounter an interface or map,
then we can ensure that all values are always addressable and
assume such property throughout all the logic.

Performance:

	name                 old time/op    new time/op    delta
	Hash-24                21.5µs ± 1%    19.7µs ± 1%  -8.29%  (p=0.000 n=9+9)
	HashPacketFilter-24    2.61µs ± 1%    2.62µs ± 0%  +0.29%  (p=0.037 n=10+9)
	HashMapAcyclic-24      30.8µs ± 1%    30.9µs ± 1%    ~     (p=0.400 n=9+10)
	TailcfgNode-24         1.84µs ± 1%    1.84µs ± 2%    ~     (p=0.928 n=10+10)
	HashArray-24            324ns ± 2%     332ns ± 2%  +2.45%  (p=0.000 n=10+10)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-09 22:00:02 -07:00
..
cibuild all: use cibuild.On 2022-03-18 15:19:26 -07:00
clientmetric all: gofmt for Go 1.19 2022-08-02 10:08:05 -07:00
cloudenv all: use syncs.AtomicValue 2022-08-04 11:52:16 -07:00
cmpver util/cmpver: move into OSS from corp repo. 2021-04-23 20:55:45 -07:00
codegen all: convert more code to use net/netip directly 2022-07-25 21:53:49 -07:00
deephash util/deephash: always keep values addressable (#5328) 2022-08-09 22:00:02 -07:00
dnsname all: use Go 1.18's strings.Cut 2022-03-16 14:53:59 -07:00
endian all: gofmt with Go 1.17 2021-08-05 15:54:00 -07:00
groupmember util/groupmember: remove redundant code (#4298) 2022-03-26 21:45:06 -07:00
jsonutil all: use any instead of interface{} 2022-03-17 11:35:09 -07:00
lineread util/lineread: add docs to Reader 2020-11-19 12:14:58 -08:00
mak util/mak: move tailssh's mapSet into a new package for reuse elsewhere 2022-04-21 21:20:10 -07:00
multierr all: gofmt for Go 1.19 2022-08-02 10:08:05 -07:00
must util/must: rename Do->Get, add Do 2022-08-06 09:30:10 -07:00
osshare all: gofmt with Go 1.17 2021-08-05 15:54:00 -07:00
pidowner all: gofmt with Go 1.17 2021-08-05 15:54:00 -07:00
precompress cmd/tsconnect,util/precompress: move precompression to its own package 2022-08-03 17:44:57 -07:00
racebuild all: gofmt with Go 1.17 2021-08-05 15:54:00 -07:00
singleflight all: gofmt for Go 1.19 2022-08-02 10:08:05 -07:00
strs util/strs: add new package for string utility funcs 2022-08-05 15:06:08 -07:00
systemd all: gofmt for Go 1.19 2022-08-02 10:08:05 -07:00
uniq all: use any instead of interface{} 2022-03-17 11:35:09 -07:00
winutil net/dns, paths, util/winutil: change net/dns/windowsManager NRPT management to support more than 50 domains. 2022-05-27 14:56:09 -06:00