deephash_test.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package deephash
  4. import (
  5. "archive/tar"
  6. "crypto/sha256"
  7. "encoding/binary"
  8. "fmt"
  9. "hash"
  10. "math"
  11. "math/bits"
  12. "math/rand"
  13. "net/netip"
  14. "reflect"
  15. "runtime"
  16. "testing"
  17. "testing/quick"
  18. "time"
  19. qt "github.com/frankban/quicktest"
  20. "go4.org/mem"
  21. "go4.org/netipx"
  22. "tailscale.com/tailcfg"
  23. "tailscale.com/types/key"
  24. "tailscale.com/types/ptr"
  25. "tailscale.com/util/deephash/testtype"
  26. "tailscale.com/util/hashx"
  27. "tailscale.com/version"
  28. )
  29. type appendBytes []byte
  30. func (p appendBytes) AppendTo(b []byte) []byte {
  31. return append(b, p...)
  32. }
  33. type selfHasherValueRecv struct {
  34. emit uint64
  35. }
  36. func (s selfHasherValueRecv) Hash(h *hashx.Block512) {
  37. h.HashUint64(s.emit)
  38. }
  39. type selfHasherPointerRecv struct {
  40. emit uint64
  41. }
  42. func (s *selfHasherPointerRecv) Hash(h *hashx.Block512) {
  43. h.HashUint64(s.emit)
  44. }
  45. func TestHash(t *testing.T) {
  46. type tuple [2]any
  47. type iface struct{ X any }
  48. type scalars struct {
  49. I8 int8
  50. I16 int16
  51. I32 int32
  52. I64 int64
  53. I int
  54. U8 uint8
  55. U16 uint16
  56. U32 uint32
  57. U64 uint64
  58. U uint
  59. UP uintptr
  60. F32 float32
  61. F64 float64
  62. C64 complex64
  63. C128 complex128
  64. }
  65. type MyBool bool
  66. type MyHeader tar.Header
  67. var zeroFloat64 float64
  68. tests := []struct {
  69. in tuple
  70. wantEq bool
  71. }{
  72. {in: tuple{false, true}, wantEq: false},
  73. {in: tuple{true, true}, wantEq: true},
  74. {in: tuple{false, false}, wantEq: true},
  75. {
  76. in: tuple{
  77. scalars{-8, -16, -32, -64, -1234, 8, 16, 32, 64, 1234, 5678, 32.32, 64.64, 32 + 32i, 64 + 64i},
  78. scalars{-8, -16, -32, -64, -1234, 8, 16, 32, 64, 1234, 5678, 32.32, 64.64, 32 + 32i, 64 + 64i},
  79. },
  80. wantEq: true,
  81. },
  82. {in: tuple{scalars{I8: math.MinInt8}, scalars{I8: math.MinInt8 / 2}}, wantEq: false},
  83. {in: tuple{scalars{I16: math.MinInt16}, scalars{I16: math.MinInt16 / 2}}, wantEq: false},
  84. {in: tuple{scalars{I32: math.MinInt32}, scalars{I32: math.MinInt32 / 2}}, wantEq: false},
  85. {in: tuple{scalars{I64: math.MinInt64}, scalars{I64: math.MinInt64 / 2}}, wantEq: false},
  86. {in: tuple{scalars{I: -1234}, scalars{I: -1234 / 2}}, wantEq: false},
  87. {in: tuple{scalars{U8: math.MaxUint8}, scalars{U8: math.MaxUint8 / 2}}, wantEq: false},
  88. {in: tuple{scalars{U16: math.MaxUint16}, scalars{U16: math.MaxUint16 / 2}}, wantEq: false},
  89. {in: tuple{scalars{U32: math.MaxUint32}, scalars{U32: math.MaxUint32 / 2}}, wantEq: false},
  90. {in: tuple{scalars{U64: math.MaxUint64}, scalars{U64: math.MaxUint64 / 2}}, wantEq: false},
  91. {in: tuple{scalars{U: 1234}, scalars{U: 1234 / 2}}, wantEq: false},
  92. {in: tuple{scalars{UP: 5678}, scalars{UP: 5678 / 2}}, wantEq: false},
  93. {in: tuple{scalars{F32: 32.32}, scalars{F32: math.Nextafter32(32.32, 0)}}, wantEq: false},
  94. {in: tuple{scalars{F64: 64.64}, scalars{F64: math.Nextafter(64.64, 0)}}, wantEq: false},
  95. {in: tuple{scalars{F32: float32(math.NaN())}, scalars{F32: float32(math.NaN())}}, wantEq: true},
  96. {in: tuple{scalars{F64: float64(math.NaN())}, scalars{F64: float64(math.NaN())}}, wantEq: true},
  97. {in: tuple{scalars{C64: 32 + 32i}, scalars{C64: complex(math.Nextafter32(32, 0), 32)}}, wantEq: false},
  98. {in: tuple{scalars{C128: 64 + 64i}, scalars{C128: complex(math.Nextafter(64, 0), 64)}}, wantEq: false},
  99. {in: tuple{[]int(nil), []int(nil)}, wantEq: true},
  100. {in: tuple{[]int{}, []int(nil)}, wantEq: false},
  101. {in: tuple{[]int{}, []int{}}, wantEq: true},
  102. {in: tuple{[]string(nil), []string(nil)}, wantEq: true},
  103. {in: tuple{[]string{}, []string(nil)}, wantEq: false},
  104. {in: tuple{[]string{}, []string{}}, wantEq: true},
  105. {in: tuple{[]appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}, []appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}}, wantEq: true},
  106. {in: tuple{[]appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}, []appendBytes{{0, 0, 0, 0, 0, 0, 0, 1}, {}}}, wantEq: false},
  107. {in: tuple{iface{MyBool(true)}, iface{MyBool(true)}}, wantEq: true},
  108. {in: tuple{iface{true}, iface{MyBool(true)}}, wantEq: false},
  109. {in: tuple{iface{MyHeader{}}, iface{MyHeader{}}}, wantEq: true},
  110. {in: tuple{iface{MyHeader{}}, iface{tar.Header{}}}, wantEq: false},
  111. {in: tuple{iface{&MyHeader{}}, iface{&MyHeader{}}}, wantEq: true},
  112. {in: tuple{iface{&MyHeader{}}, iface{&tar.Header{}}}, wantEq: false},
  113. {in: tuple{iface{[]map[string]MyBool{}}, iface{[]map[string]MyBool{}}}, wantEq: true},
  114. {in: tuple{iface{[]map[string]bool{}}, iface{[]map[string]MyBool{}}}, wantEq: false},
  115. {in: tuple{zeroFloat64, -zeroFloat64}, wantEq: false}, // Issue 4883 (false alarm)
  116. {in: tuple{[]any(nil), 0.0}, wantEq: false}, // Issue 4883
  117. {in: tuple{[]any(nil), uint8(0)}, wantEq: false}, // Issue 4883
  118. {in: tuple{nil, nil}, wantEq: true}, // Issue 4883
  119. {
  120. in: func() tuple {
  121. i1 := 1
  122. i2 := 2
  123. v1 := [3]*int{&i1, &i2, &i1}
  124. v2 := [3]*int{&i1, &i2, &i2}
  125. return tuple{v1, v2}
  126. }(),
  127. wantEq: false,
  128. },
  129. {in: tuple{netip.Addr{}, netip.Addr{}}, wantEq: true},
  130. {in: tuple{netip.Addr{}, netip.AddrFrom4([4]byte{})}, wantEq: false},
  131. {in: tuple{netip.AddrFrom4([4]byte{}), netip.AddrFrom4([4]byte{})}, wantEq: true},
  132. {in: tuple{netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 1})}, wantEq: true},
  133. {in: tuple{netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 2})}, wantEq: false},
  134. {in: tuple{netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{})}, wantEq: false},
  135. {in: tuple{netip.AddrFrom16([16]byte{}), netip.AddrFrom16([16]byte{})}, wantEq: true},
  136. {in: tuple{netip.AddrPort{}, netip.AddrPort{}}, wantEq: true},
  137. {in: tuple{netip.AddrPort{}, netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 0)}, wantEq: false},
  138. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 0), netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 0)}, wantEq: true},
  139. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234), netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234)}, wantEq: true},
  140. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234), netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1235)}, wantEq: false},
  141. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234), netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 2}), 1234)}, wantEq: false},
  142. {in: tuple{netip.Prefix{}, netip.Prefix{}}, wantEq: true},
  143. {in: tuple{netip.Prefix{}, netip.PrefixFrom(netip.Addr{}, 1)}, wantEq: true},
  144. {in: tuple{netip.Prefix{}, netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 0)}, wantEq: false},
  145. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 1)}, wantEq: true},
  146. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1)}, wantEq: true},
  147. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 0)}, wantEq: false},
  148. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 2}), 1)}, wantEq: false},
  149. {in: tuple{netipx.IPRange{}, netipx.IPRange{}}, wantEq: true},
  150. {in: tuple{netipx.IPRange{}, netipx.IPRangeFrom(netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{}))}, wantEq: false},
  151. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{}))}, wantEq: true},
  152. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100}))}, wantEq: true},
  153. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 101}))}, wantEq: false},
  154. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 2}), netip.AddrFrom4([4]byte{192, 168, 0, 100}))}, wantEq: false},
  155. {in: tuple{key.DiscoPublic{}, key.DiscoPublic{}}, wantEq: true},
  156. {in: tuple{key.DiscoPublic{}, key.DiscoPublicFromRaw32(mem.B(func() []byte {
  157. b := make([]byte, 32)
  158. b[0] = 1
  159. return b
  160. }()))}, wantEq: false},
  161. {in: tuple{key.NodePublic{}, key.NodePublic{}}, wantEq: true},
  162. {in: tuple{key.NodePublic{}, key.NodePublicFromRaw32(mem.B(func() []byte {
  163. b := make([]byte, 32)
  164. b[0] = 1
  165. return b
  166. }()))}, wantEq: false},
  167. {in: tuple{&selfHasherPointerRecv{}, &selfHasherPointerRecv{}}, wantEq: true},
  168. {in: tuple{(*selfHasherPointerRecv)(nil), (*selfHasherPointerRecv)(nil)}, wantEq: true},
  169. {in: tuple{(*selfHasherPointerRecv)(nil), &selfHasherPointerRecv{}}, wantEq: false},
  170. {in: tuple{&selfHasherPointerRecv{emit: 1}, &selfHasherPointerRecv{emit: 2}}, wantEq: false},
  171. {in: tuple{selfHasherValueRecv{emit: 1}, selfHasherValueRecv{emit: 2}}, wantEq: false},
  172. {in: tuple{selfHasherValueRecv{emit: 2}, selfHasherValueRecv{emit: 2}}, wantEq: true},
  173. }
  174. for _, tt := range tests {
  175. gotEq := Hash(&tt.in[0]) == Hash(&tt.in[1])
  176. if gotEq != tt.wantEq {
  177. t.Errorf("(Hash(%T %v) == Hash(%T %v)) = %v, want %v", tt.in[0], tt.in[0], tt.in[1], tt.in[1], gotEq, tt.wantEq)
  178. }
  179. }
  180. }
  181. // Tests that we actually hash map elements. Whoops.
  182. func TestIssue4868(t *testing.T) {
  183. m1 := map[int]string{1: "foo"}
  184. m2 := map[int]string{1: "bar"}
  185. if Hash(&m1) == Hash(&m2) {
  186. t.Error("bogus")
  187. }
  188. }
  189. func TestIssue4871(t *testing.T) {
  190. m1 := map[string]string{"": "", "x": "foo"}
  191. m2 := map[string]string{}
  192. if h1, h2 := Hash(&m1), Hash(&m2); h1 == h2 {
  193. t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
  194. }
  195. }
  196. func TestNilVsEmptymap(t *testing.T) {
  197. m1 := map[string]string(nil)
  198. m2 := map[string]string{}
  199. if h1, h2 := Hash(&m1), Hash(&m2); h1 == h2 {
  200. t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
  201. }
  202. }
  203. func TestMapFraming(t *testing.T) {
  204. m1 := map[string]string{"foo": "", "fo": "o"}
  205. m2 := map[string]string{}
  206. if h1, h2 := Hash(&m1), Hash(&m2); h1 == h2 {
  207. t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
  208. }
  209. }
  210. func TestQuick(t *testing.T) {
  211. initSeed()
  212. err := quick.Check(func(v, w map[string]string) bool {
  213. return (Hash(&v) == Hash(&w)) == reflect.DeepEqual(v, w)
  214. }, &quick.Config{MaxCount: 1000, Rand: rand.New(rand.NewSource(int64(seed)))})
  215. if err != nil {
  216. t.Fatalf("seed=%v, err=%v", seed, err)
  217. }
  218. }
  219. type IntThenByte struct {
  220. _ int
  221. _ byte
  222. }
  223. type TwoInts struct{ _, _ int }
  224. type IntIntByteInt struct {
  225. i1, i2 int32
  226. b byte // padding after
  227. i3 int32
  228. }
  229. func u8(n uint8) string { return string([]byte{n}) }
  230. func u32(n uint32) string { return string(binary.LittleEndian.AppendUint32(nil, n)) }
  231. func u64(n uint64) string { return string(binary.LittleEndian.AppendUint64(nil, n)) }
  232. func ux(n uint) string {
  233. if bits.UintSize == 32 {
  234. return u32(uint32(n))
  235. } else {
  236. return u64(uint64(n))
  237. }
  238. }
  239. func TestGetTypeHasher(t *testing.T) {
  240. switch runtime.GOARCH {
  241. case "amd64", "arm64", "arm", "386", "riscv64":
  242. default:
  243. // Test outputs below are specifically for little-endian machines.
  244. // Just skip everything else for now. Feel free to add more above if
  245. // you have the hardware to test and it's little-endian.
  246. t.Skipf("skipping on %v", runtime.GOARCH)
  247. }
  248. type typedString string
  249. var (
  250. someInt = int('A')
  251. someComplex128 = complex128(1 + 2i)
  252. someIP = netip.MustParseAddr("1.2.3.4")
  253. )
  254. tests := []struct {
  255. name string
  256. val any
  257. out string
  258. out32 string // overwrites out if 32-bit
  259. }{
  260. {
  261. name: "int",
  262. val: int(1),
  263. out: ux(1),
  264. },
  265. {
  266. name: "int_negative",
  267. val: int(-1),
  268. out: ux(math.MaxUint),
  269. },
  270. {
  271. name: "int8",
  272. val: int8(1),
  273. out: "\x01",
  274. },
  275. {
  276. name: "float64",
  277. val: float64(1.0),
  278. out: "\x00\x00\x00\x00\x00\x00\xf0?",
  279. },
  280. {
  281. name: "float32",
  282. val: float32(1.0),
  283. out: "\x00\x00\x80?",
  284. },
  285. {
  286. name: "string",
  287. val: "foo",
  288. out: "\x03\x00\x00\x00\x00\x00\x00\x00foo",
  289. },
  290. {
  291. name: "typedString",
  292. val: typedString("foo"),
  293. out: "\x03\x00\x00\x00\x00\x00\x00\x00foo",
  294. },
  295. {
  296. name: "string_slice",
  297. val: []string{"foo", "bar"},
  298. out: "\x01\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x03\x00\x00\x00\x00\x00\x00\x00bar",
  299. },
  300. {
  301. name: "int_slice",
  302. val: []int{1, 0, -1},
  303. out: "\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff",
  304. out32: "\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff",
  305. },
  306. {
  307. name: "struct",
  308. val: struct {
  309. a, b int
  310. c uint16
  311. }{1, -1, 2},
  312. out: "\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x02\x00",
  313. out32: "\x01\x00\x00\x00\xff\xff\xff\xff\x02\x00",
  314. },
  315. {
  316. name: "nil_int_ptr",
  317. val: (*int)(nil),
  318. out: "\x00",
  319. },
  320. {
  321. name: "int_ptr",
  322. val: &someInt,
  323. out: "\x01A\x00\x00\x00\x00\x00\x00\x00",
  324. out32: "\x01A\x00\x00\x00",
  325. },
  326. {
  327. name: "nil_uint32_ptr",
  328. val: (*uint32)(nil),
  329. out: "\x00",
  330. },
  331. {
  332. name: "complex128_ptr",
  333. val: &someComplex128,
  334. out: "\x01\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@",
  335. },
  336. {
  337. name: "packet_filter",
  338. val: filterRules,
  339. out: "\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04!\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00",
  340. out32: "\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x01\x00\x02\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04!\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00",
  341. },
  342. {
  343. name: "netip.Addr",
  344. val: netip.MustParseAddr("fe80::123%foo"),
  345. out: u64(16+3) + u64(0x80fe) + u64(0x2301<<48) + "foo",
  346. },
  347. {
  348. name: "ptr-netip.Addr",
  349. val: &someIP,
  350. out: u8(1) + u64(4) + u32(0x04030201),
  351. },
  352. {
  353. name: "ptr-nil-netip.Addr",
  354. val: (*netip.Addr)(nil),
  355. out: "\x00",
  356. },
  357. {
  358. name: "time",
  359. val: time.Unix(1234, 5678).In(time.UTC),
  360. out: u64(1234) + u32(5678) + u32(0),
  361. },
  362. {
  363. name: "time_ptr", // addressable, as opposed to "time" test above
  364. val: ptr.To(time.Unix(1234, 5678).In(time.UTC)),
  365. out: u8(1) + u64(1234) + u32(5678) + u32(0),
  366. },
  367. {
  368. name: "time_ptr_via_unexported",
  369. val: testtype.NewUnexportedAddressableTime(time.Unix(1234, 5678).In(time.UTC)),
  370. out: u8(1) + u64(1234) + u32(5678) + u32(0),
  371. },
  372. {
  373. name: "time_ptr_via_unexported_value",
  374. val: *testtype.NewUnexportedAddressableTime(time.Unix(1234, 5678).In(time.UTC)),
  375. out: u64(1234) + u32(5678) + u32(0),
  376. },
  377. {
  378. name: "time_custom_zone",
  379. val: time.Unix(1655311822, 0).In(time.FixedZone("FOO", -60*60)),
  380. out: u64(1655311822) + u32(0) + u32(math.MaxUint32-60*60+1),
  381. },
  382. {
  383. name: "time_nil",
  384. val: (*time.Time)(nil),
  385. out: "\x00",
  386. },
  387. {
  388. name: "array_memhash",
  389. val: [4]byte{1, 2, 3, 4},
  390. out: "\x01\x02\x03\x04",
  391. },
  392. {
  393. name: "array_ptr_memhash",
  394. val: ptr.To([4]byte{1, 2, 3, 4}),
  395. out: "\x01\x01\x02\x03\x04",
  396. },
  397. {
  398. name: "ptr_to_struct_partially_memhashable",
  399. val: &struct {
  400. A int16
  401. B int16
  402. C *int
  403. }{5, 6, nil},
  404. out: "\x01\x05\x00\x06\x00\x00",
  405. },
  406. {
  407. name: "struct_partially_memhashable_but_cant_addr",
  408. val: struct {
  409. A int16
  410. B int16
  411. C *int
  412. }{5, 6, nil},
  413. out: "\x05\x00\x06\x00\x00",
  414. },
  415. {
  416. name: "array_elements",
  417. val: [4]byte{1, 2, 3, 4},
  418. out: "\x01\x02\x03\x04",
  419. },
  420. {
  421. name: "bool",
  422. val: true,
  423. out: "\x01",
  424. },
  425. {
  426. name: "IntIntByteInt",
  427. val: IntIntByteInt{1, 2, 3, 4},
  428. out: "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00",
  429. },
  430. {
  431. name: "IntIntByteInt-canaddr",
  432. val: &IntIntByteInt{1, 2, 3, 4},
  433. out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00",
  434. },
  435. {
  436. name: "array-IntIntByteInt",
  437. val: [2]IntIntByteInt{
  438. {1, 2, 3, 4},
  439. {5, 6, 7, 8},
  440. },
  441. out: "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00",
  442. },
  443. {
  444. name: "array-IntIntByteInt-canaddr",
  445. val: &[2]IntIntByteInt{
  446. {1, 2, 3, 4},
  447. {5, 6, 7, 8},
  448. },
  449. out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00",
  450. },
  451. {
  452. name: "tailcfg.Node",
  453. val: &tailcfg.Node{},
  454. out: "ANY", // magic value; just check it doesn't fail to hash
  455. out32: "ANY",
  456. },
  457. }
  458. for _, tt := range tests {
  459. t.Run(tt.name, func(t *testing.T) {
  460. rv := reflect.ValueOf(tt.val)
  461. va := reflect.New(rv.Type()).Elem()
  462. va.Set(rv)
  463. fn := lookupTypeHasher(va.Type())
  464. hb := &hashBuffer{Hash: sha256.New()}
  465. h := new(hasher)
  466. h.Block512.Hash = hb
  467. fn(h, pointerOf(va.Addr()))
  468. const ptrSize = 32 << uintptr(^uintptr(0)>>63)
  469. if tt.out32 != "" && ptrSize == 32 {
  470. tt.out = tt.out32
  471. }
  472. h.sum()
  473. if got := string(hb.B); got != tt.out && tt.out != "ANY" {
  474. t.Fatalf("got %q; want %q", got, tt.out)
  475. }
  476. })
  477. }
  478. }
  479. func TestSliceCycle(t *testing.T) {
  480. type S []S
  481. c := qt.New(t)
  482. a := make(S, 1) // cyclic graph of 1 node
  483. a[0] = a
  484. b := make(S, 1) // cyclic graph of 1 node
  485. b[0] = b
  486. ha := Hash(&a)
  487. hb := Hash(&b)
  488. c.Assert(ha, qt.Equals, hb)
  489. c1 := make(S, 1) // cyclic graph of 2 nodes
  490. c2 := make(S, 1) // cyclic graph of 2 nodes
  491. c1[0] = c2
  492. c2[0] = c1
  493. hc1 := Hash(&c1)
  494. hc2 := Hash(&c2)
  495. c.Assert(hc1, qt.Equals, hc2)
  496. c.Assert(ha, qt.Not(qt.Equals), hc1)
  497. c.Assert(hb, qt.Not(qt.Equals), hc2)
  498. c3 := make(S, 1) // graph of 1 node pointing to cyclic graph of 2 nodes
  499. c3[0] = c1
  500. hc3 := Hash(&c3)
  501. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  502. c4 := make(S, 2) // cyclic graph of 3 nodes
  503. c5 := make(S, 2) // cyclic graph of 3 nodes
  504. c4[0] = nil
  505. c4[1] = c4
  506. c5[0] = c5
  507. c5[1] = nil
  508. hc4 := Hash(&c4)
  509. hc5 := Hash(&c5)
  510. c.Assert(hc4, qt.Not(qt.Equals), hc5) // cycle occurs through different indexes
  511. }
  512. func TestMapCycle(t *testing.T) {
  513. type M map[string]M
  514. c := qt.New(t)
  515. a := make(M) // cyclic graph of 1 node
  516. a["self"] = a
  517. b := make(M) // cyclic graph of 1 node
  518. b["self"] = b
  519. ha := Hash(&a)
  520. hb := Hash(&b)
  521. c.Assert(ha, qt.Equals, hb)
  522. c1 := make(M) // cyclic graph of 2 nodes
  523. c2 := make(M) // cyclic graph of 2 nodes
  524. c1["peer"] = c2
  525. c2["peer"] = c1
  526. hc1 := Hash(&c1)
  527. hc2 := Hash(&c2)
  528. c.Assert(hc1, qt.Equals, hc2)
  529. c.Assert(ha, qt.Not(qt.Equals), hc1)
  530. c.Assert(hb, qt.Not(qt.Equals), hc2)
  531. c3 := make(M) // graph of 1 node pointing to cyclic graph of 2 nodes
  532. c3["child"] = c1
  533. hc3 := Hash(&c3)
  534. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  535. c4 := make(M) // cyclic graph of 3 nodes
  536. c5 := make(M) // cyclic graph of 3 nodes
  537. c4["0"] = nil
  538. c4["1"] = c4
  539. c5["0"] = c5
  540. c5["1"] = nil
  541. hc4 := Hash(&c4)
  542. hc5 := Hash(&c5)
  543. c.Assert(hc4, qt.Not(qt.Equals), hc5) // cycle occurs through different keys
  544. }
  545. func TestPointerCycle(t *testing.T) {
  546. type P *P
  547. c := qt.New(t)
  548. a := new(P) // cyclic graph of 1 node
  549. *a = a
  550. b := new(P) // cyclic graph of 1 node
  551. *b = b
  552. ha := Hash(&a)
  553. hb := Hash(&b)
  554. c.Assert(ha, qt.Equals, hb)
  555. c1 := new(P) // cyclic graph of 2 nodes
  556. c2 := new(P) // cyclic graph of 2 nodes
  557. *c1 = c2
  558. *c2 = c1
  559. hc1 := Hash(&c1)
  560. hc2 := Hash(&c2)
  561. c.Assert(hc1, qt.Equals, hc2)
  562. c.Assert(ha, qt.Not(qt.Equals), hc1)
  563. c.Assert(hb, qt.Not(qt.Equals), hc2)
  564. c3 := new(P) // graph of 1 node pointing to cyclic graph of 2 nodes
  565. *c3 = c1
  566. hc3 := Hash(&c3)
  567. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  568. }
  569. func TestInterfaceCycle(t *testing.T) {
  570. type I struct{ v any }
  571. c := qt.New(t)
  572. a := new(I) // cyclic graph of 1 node
  573. a.v = a
  574. b := new(I) // cyclic graph of 1 node
  575. b.v = b
  576. ha := Hash(&a)
  577. hb := Hash(&b)
  578. c.Assert(ha, qt.Equals, hb)
  579. c1 := new(I) // cyclic graph of 2 nodes
  580. c2 := new(I) // cyclic graph of 2 nodes
  581. c1.v = c2
  582. c2.v = c1
  583. hc1 := Hash(&c1)
  584. hc2 := Hash(&c2)
  585. c.Assert(hc1, qt.Equals, hc2)
  586. c.Assert(ha, qt.Not(qt.Equals), hc1)
  587. c.Assert(hb, qt.Not(qt.Equals), hc2)
  588. c3 := new(I) // graph of 1 node pointing to cyclic graph of 2 nodes
  589. c3.v = c1
  590. hc3 := Hash(&c3)
  591. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  592. }
  593. var sink Sum
  594. // filterRules is a packet filter that has both everything populated (in its
  595. // first element) and also a few entries that are the typical shape for regular
  596. // packet filters as sent to clients.
  597. var filterRules = []tailcfg.FilterRule{
  598. {
  599. SrcIPs: []string{"*", "10.1.3.4/32", "10.0.0.0/24"},
  600. DstPorts: []tailcfg.NetPortRange{{
  601. IP: "1.2.3.4/32",
  602. Bits: ptr.To(32),
  603. Ports: tailcfg.PortRange{First: 1, Last: 2},
  604. }},
  605. IPProto: []int{1, 2, 3, 4},
  606. CapGrant: []tailcfg.CapGrant{{
  607. Dsts: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")},
  608. Caps: []tailcfg.PeerCapability{"foo"},
  609. }},
  610. },
  611. {
  612. SrcIPs: []string{"foooooooooo"},
  613. DstPorts: []tailcfg.NetPortRange{{
  614. IP: "baaaaaarrrrr",
  615. Ports: tailcfg.PortRange{First: 1, Last: 2},
  616. }},
  617. },
  618. {
  619. SrcIPs: []string{"foooooooooo"},
  620. DstPorts: []tailcfg.NetPortRange{{
  621. IP: "baaaaaarrrrr",
  622. Ports: tailcfg.PortRange{First: 1, Last: 2},
  623. }},
  624. },
  625. {
  626. SrcIPs: []string{"foooooooooo"},
  627. DstPorts: []tailcfg.NetPortRange{{
  628. IP: "baaaaaarrrrr",
  629. Ports: tailcfg.PortRange{First: 1, Last: 2},
  630. }},
  631. },
  632. }
  633. func BenchmarkHashPacketFilter(b *testing.B) {
  634. b.ReportAllocs()
  635. for range b.N {
  636. sink = Hash(&filterRules)
  637. }
  638. }
  639. func TestHashMapAcyclic(t *testing.T) {
  640. m := map[int]string{}
  641. for i := range 100 {
  642. m[i] = fmt.Sprint(i)
  643. }
  644. got := map[string]bool{}
  645. hb := &hashBuffer{Hash: sha256.New()}
  646. hash := lookupTypeHasher(reflect.TypeFor[map[int]string]())
  647. for range 20 {
  648. va := reflect.ValueOf(&m).Elem()
  649. hb.Reset()
  650. h := new(hasher)
  651. h.Block512.Hash = hb
  652. hash(h, pointerOf(va.Addr()))
  653. h.sum()
  654. if got[string(hb.B)] {
  655. continue
  656. }
  657. got[string(hb.B)] = true
  658. }
  659. if len(got) != 1 {
  660. t.Errorf("got %d results; want 1", len(got))
  661. }
  662. }
  663. func TestPrintArray(t *testing.T) {
  664. type T struct {
  665. X [32]byte
  666. }
  667. x := T{X: [32]byte{1: 1, 31: 31}}
  668. hb := &hashBuffer{Hash: sha256.New()}
  669. h := new(hasher)
  670. h.Block512.Hash = hb
  671. va := reflect.ValueOf(&x).Elem()
  672. hash := lookupTypeHasher(va.Type())
  673. hash(h, pointerOf(va.Addr()))
  674. h.sum()
  675. const want = "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f"
  676. if got := hb.B; string(got) != want {
  677. t.Errorf("wrong:\n got: %q\nwant: %q\n", got, want)
  678. }
  679. }
  680. func BenchmarkHashMapAcyclic(b *testing.B) {
  681. b.ReportAllocs()
  682. m := map[int]string{}
  683. for i := range 100 {
  684. m[i] = fmt.Sprint(i)
  685. }
  686. hb := &hashBuffer{Hash: sha256.New()}
  687. va := reflect.ValueOf(&m).Elem()
  688. hash := lookupTypeHasher(va.Type())
  689. h := new(hasher)
  690. h.Block512.Hash = hb
  691. for range b.N {
  692. h.Reset()
  693. hash(h, pointerOf(va.Addr()))
  694. }
  695. }
  696. func BenchmarkTailcfgNode(b *testing.B) {
  697. b.ReportAllocs()
  698. node := new(tailcfg.Node)
  699. for range b.N {
  700. sink = Hash(node)
  701. }
  702. }
  703. func TestExhaustive(t *testing.T) {
  704. seen := make(map[Sum]bool)
  705. for i := range 100000 {
  706. s := Hash(&i)
  707. if seen[s] {
  708. t.Fatalf("hash collision %v", i)
  709. }
  710. seen[s] = true
  711. }
  712. }
  713. // verify this doesn't loop forever, as it used to (Issue 2340)
  714. func TestMapCyclicFallback(t *testing.T) {
  715. type T struct {
  716. M map[string]any
  717. }
  718. v := &T{
  719. M: map[string]any{},
  720. }
  721. v.M["m"] = v.M
  722. Hash(v)
  723. }
  724. func TestArrayAllocs(t *testing.T) {
  725. if version.IsRace() {
  726. t.Skip("skipping test under race detector")
  727. }
  728. // In theory, there should be no allocations. However, escape analysis on
  729. // certain architectures fails to detect that certain cases do not escape.
  730. // This discrepancy currently affects sha256.digest.Sum.
  731. // Measure the number of allocations in sha256 to ensure that Hash does
  732. // not allocate on top of its usage of sha256.
  733. // See https://golang.org/issue/48055.
  734. var b []byte
  735. h := sha256.New()
  736. want := int(testing.AllocsPerRun(1000, func() {
  737. b = h.Sum(b[:0])
  738. }))
  739. switch runtime.GOARCH {
  740. case "amd64", "arm64":
  741. want = 0 // ensure no allocations on popular architectures
  742. }
  743. type T struct {
  744. X [32]byte
  745. }
  746. x := &T{X: [32]byte{1: 1, 2: 2, 3: 3, 4: 4}}
  747. got := int(testing.AllocsPerRun(1000, func() {
  748. sink = Hash(x)
  749. }))
  750. if got > want {
  751. t.Errorf("allocs = %v; want %v", got, want)
  752. }
  753. }
  754. // Test for http://go/corp/6311 issue.
  755. func TestHashThroughView(t *testing.T) {
  756. type sshPolicyOut struct {
  757. Rules []tailcfg.SSHRuleView
  758. }
  759. type mapResponseOut struct {
  760. SSHPolicy *sshPolicyOut
  761. }
  762. // Just test we don't panic:
  763. _ = Hash(&mapResponseOut{
  764. SSHPolicy: &sshPolicyOut{
  765. Rules: []tailcfg.SSHRuleView{
  766. (&tailcfg.SSHRule{
  767. RuleExpires: ptr.To(time.Unix(123, 0)),
  768. }).View(),
  769. },
  770. },
  771. })
  772. }
  773. func BenchmarkHashArray(b *testing.B) {
  774. b.ReportAllocs()
  775. type T struct {
  776. X [32]byte
  777. }
  778. x := &T{X: [32]byte{1: 1, 2: 2, 3: 3, 4: 4}}
  779. for range b.N {
  780. sink = Hash(x)
  781. }
  782. }
  783. // hashBuffer is a hash.Hash that buffers all written data.
  784. type hashBuffer struct {
  785. hash.Hash
  786. B []byte
  787. }
  788. func (h *hashBuffer) Write(b []byte) (int, error) {
  789. n, err := h.Hash.Write(b)
  790. h.B = append(h.B, b[:n]...)
  791. return n, err
  792. }
  793. func (h *hashBuffer) Reset() {
  794. h.Hash.Reset()
  795. h.B = h.B[:0]
  796. }
  797. func FuzzTime(f *testing.F) {
  798. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), false, "", 0)
  799. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "", 0)
  800. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "hello", 0)
  801. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "", 1234)
  802. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "hello", 1234)
  803. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), false, "", 0)
  804. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "", 0)
  805. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "hello", 0)
  806. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "", 1234)
  807. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "hello", 1234)
  808. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0)
  809. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "", 0)
  810. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "hello", 0)
  811. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "", 1234)
  812. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "hello", 1234)
  813. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), false, "", 0)
  814. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "", 0)
  815. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "hello", 0)
  816. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "", 1234)
  817. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "hello", 1234)
  818. f.Fuzz(func(t *testing.T,
  819. s1, ns1 int64, loc1 bool, name1 string, off1 int,
  820. s2, ns2 int64, loc2 bool, name2 string, off2 int,
  821. ) {
  822. t1 := time.Unix(s1, ns1)
  823. if loc1 {
  824. _ = t1.In(time.FixedZone(name1, off1))
  825. }
  826. t2 := time.Unix(s2, ns2)
  827. if loc2 {
  828. _ = t2.In(time.FixedZone(name2, off2))
  829. }
  830. got := Hash(&t1) == Hash(&t2)
  831. want := t1.Format(time.RFC3339Nano) == t2.Format(time.RFC3339Nano)
  832. if got != want {
  833. t.Errorf("time.Time(%s) == time.Time(%s) mismatches hash equivalent", t1.Format(time.RFC3339Nano), t2.Format(time.RFC3339Nano))
  834. }
  835. })
  836. }
  837. func FuzzAddr(f *testing.F) {
  838. f.Fuzz(func(t *testing.T,
  839. u1a, u1b uint64, zone1 string,
  840. u2a, u2b uint64, zone2 string,
  841. ) {
  842. var b1, b2 [16]byte
  843. binary.LittleEndian.PutUint64(b1[:8], u1a)
  844. binary.LittleEndian.PutUint64(b1[8:], u1b)
  845. binary.LittleEndian.PutUint64(b2[:8], u2a)
  846. binary.LittleEndian.PutUint64(b2[8:], u2b)
  847. var ips [4]netip.Addr
  848. ips[0] = netip.AddrFrom4(*(*[4]byte)(b1[:]))
  849. ips[1] = netip.AddrFrom4(*(*[4]byte)(b2[:]))
  850. ips[2] = netip.AddrFrom16(b1)
  851. if zone1 != "" {
  852. ips[2] = ips[2].WithZone(zone1)
  853. }
  854. ips[3] = netip.AddrFrom16(b2)
  855. if zone2 != "" {
  856. ips[3] = ips[2].WithZone(zone2)
  857. }
  858. for _, ip1 := range ips[:] {
  859. for _, ip2 := range ips[:] {
  860. got := Hash(&ip1) == Hash(&ip2)
  861. want := ip1 == ip2
  862. if got != want {
  863. t.Errorf("netip.Addr(%s) == netip.Addr(%s) mismatches hash equivalent", ip1.String(), ip2.String())
  864. }
  865. }
  866. }
  867. })
  868. }
  869. func TestFilterFields(t *testing.T) {
  870. type T struct {
  871. A int
  872. B int
  873. C int
  874. }
  875. hashers := map[string]func(*T) Sum{
  876. "all": HasherForType[T](),
  877. "ac": HasherForType[T](IncludeFields[T]("A", "C")),
  878. "b": HasherForType[T](ExcludeFields[T]("A", "C")),
  879. }
  880. tests := []struct {
  881. hasher string
  882. a, b T
  883. wantEq bool
  884. }{
  885. {"all", T{1, 2, 3}, T{1, 2, 3}, true},
  886. {"all", T{1, 2, 3}, T{0, 2, 3}, false},
  887. {"all", T{1, 2, 3}, T{1, 0, 3}, false},
  888. {"all", T{1, 2, 3}, T{1, 2, 0}, false},
  889. {"ac", T{0, 0, 0}, T{0, 0, 0}, true},
  890. {"ac", T{1, 0, 1}, T{1, 1, 1}, true},
  891. {"ac", T{1, 1, 1}, T{1, 1, 0}, false},
  892. {"b", T{0, 0, 0}, T{0, 0, 0}, true},
  893. {"b", T{1, 0, 1}, T{1, 1, 1}, false},
  894. {"b", T{1, 1, 1}, T{0, 1, 0}, true},
  895. }
  896. for _, tt := range tests {
  897. f, ok := hashers[tt.hasher]
  898. if !ok {
  899. t.Fatalf("bad test: unknown hasher %q", tt.hasher)
  900. }
  901. sum1 := f(&tt.a)
  902. sum2 := f(&tt.b)
  903. got := sum1 == sum2
  904. if got != tt.wantEq {
  905. t.Errorf("hasher %q, for %+v and %v, got equal = %v; want %v", tt.hasher, tt.a, tt.b, got, tt.wantEq)
  906. }
  907. }
  908. }