this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

internal/anyunique: new package

In order to avoid O(N^2) behaviour in encoding/jsonschema.Generate,
we want some way to efficiently compare generated JSON Schema
fragments for structural equality.

This new package makes that easier to accomplish by storing items
uniquely by structural equality. The Hash interface used is the same
as that proposed for the standard library [1].

Note that having a separate type for the canonical unique values
is very helpful so the compiler can help to tell the caller
when it's OK to compare using `==`.

[1]: https://go-review.googlesource.com/c/go/+/657296/11/src/hash/maphash/hasher.go

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: Iddab07c0c2207040700e66fafd00a7c4f503cbae
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1224573
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+656
+122
internal/anyunique/unique.go
··· 1 + // Package anyunique provides canonicalization of values under a 2 + // caller-defined equivalence relation. 3 + // 4 + // A [Store] holds a set of unique values of a specific type T. Calling 5 + // [Store.Make] with two values that are equivalent according to the provided 6 + // [Hasher] returns [Handle] values that are identical. [Handle] is a lightweight 7 + // wrapper around the canonical value; use [U.Get] to obtain the 8 + // underlying T. 9 + // 10 + // The zero [Handle] represents the zero value of T. Make returns the zero 11 + // [Handle] when called with the zero value of T: it will never try to hash 12 + // the zero value. 13 + // 14 + // [Store.WriteHash] writes a short representation of a canonicalized 15 + // value to a [maphash.Hash]. It is useful when hashing structures that 16 + // themselves contain canonicalized values, avoiding re-hashing the full 17 + // value graph. 18 + // 19 + // NOTE this package assumes that T values are treated as immutable. 20 + // That is, after calling [Store.Make] a value must not change. 21 + package anyunique 22 + 23 + import "hash/maphash" 24 + 25 + // A Hasher defines a hash function and an equivalence relation over 26 + // values of type T. 27 + // 28 + // Hash must write a hash of its argument to the provided *maphash.Hash, 29 + // and Equal must report whether two values are equivalent. Hash and 30 + // Equal must be consistent: if Equal(x, y) is true then Hash must 31 + // produce the same output for x and y. 32 + // 33 + // Note: this is an exact copy of the proposed new Hasher interface 34 + // for the Go API. 35 + // See https://go-review.googlesource.com/c/go/+/657296/11/src/hash/maphash/hasher.go 36 + // 37 + // TODO alias this to maphash.Hasher when the above CL lands. 38 + type Hasher[T any] interface { 39 + Hash(*maphash.Hash, T) 40 + Equal(x, y T) bool 41 + } 42 + 43 + // New returns a new store holding a set of unique values 44 + // of type T, using h to determine whether values are the 45 + // same. 46 + // 47 + // The equivalence relation and hash are supplied by the given [Hasher]. 48 + func New[T comparable, H Hasher[T]](h H) *Store[T, H] { 49 + s := &Store[T, H]{ 50 + h: h, 51 + seed: maphash.MakeSeed(), 52 + hashes: make(map[T]uint64), 53 + entries: make(map[uint64][]T), 54 + } 55 + return s 56 + } 57 + 58 + // Store holds a set of unique values of type T. 59 + type Store[T comparable, H Hasher[T]] struct { 60 + h H 61 + seed maphash.Seed 62 + entries map[uint64][]T 63 + hashes map[T]uint64 64 + } 65 + 66 + // Handle represents a unique value of type T. If two values of type Handle[T] 67 + // originating from the same [Store] compare equal, they are guaranteed 68 + // to be equal according to the equality criteria that the store was 69 + // created with. 70 + type Handle[T comparable] struct { 71 + x T 72 + } 73 + 74 + // Value returns the actual value held in u. 75 + func (u Handle[T]) Value() T { 76 + return u.x 77 + } 78 + 79 + // WriteHash writes a short representation of x to h. 80 + // This allows callers to avoid hashing an tree of values 81 + // when hashing a value that itself contains other Handle[T] items. 82 + func (s *Store[T, H]) WriteHash(h *maphash.Hash, x Handle[T]) { 83 + z := isZero(x) 84 + maphash.WriteComparable(h, z) 85 + if !z { 86 + // TODO we _could_ write two independent hashes here 87 + // if we were concerned about collisions. 88 + maphash.WriteComparable(h, s.hashes[x.Value()]) 89 + } 90 + } 91 + 92 + // Make returns a unique value u such that u.Get() is equal to x 93 + // according to the equality criteria defined by the store. 94 + // 95 + // It is assumed that values will not change after passing to Make: the 96 + // caller must take care to preserve immutability. 97 + func (s *Store[T, H]) Make(x T) Handle[T] { 98 + if isZero(x) { 99 + return Handle[T]{} 100 + } 101 + 102 + if _, ok := s.hashes[x]; ok { 103 + return Handle[T]{x} 104 + } 105 + var hasher maphash.Hash 106 + hasher.SetSeed(s.seed) 107 + s.h.Hash(&hasher, x) 108 + h := hasher.Sum64() 109 + entries := s.entries[h] 110 + for _, e := range entries { 111 + if s.h.Equal(x, e) { 112 + return Handle[T]{e} 113 + } 114 + } 115 + s.entries[h] = append(entries, x) 116 + s.hashes[x] = h 117 + return Handle[T]{x} 118 + } 119 + 120 + func isZero[T comparable](x T) bool { 121 + return x == *new(T) 122 + }
+534
internal/anyunique/unique_test.go
··· 1 + // Copyright 2025 CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + package anyunique_test 16 + 17 + import ( 18 + "hash/maphash" 19 + "slices" 20 + "testing" 21 + 22 + "cuelang.org/go/internal/anyunique" 23 + "github.com/go-quicktest/qt" 24 + ) 25 + 26 + // stringHasher is a test Hasher implementation for string values 27 + // using standard equality. 28 + type stringHasher struct{} 29 + 30 + func (stringHasher) Equal(a, b string) bool { 31 + return a == b 32 + } 33 + 34 + func (stringHasher) Hash(h *maphash.Hash, s string) { 35 + h.WriteString(s) 36 + } 37 + 38 + // simpleIntHasher is a test Hasher implementation for int values. 39 + type simpleIntHasher struct{} 40 + 41 + func (simpleIntHasher) Equal(a, b int) bool { 42 + return a == b 43 + } 44 + 45 + func (simpleIntHasher) Hash(h *maphash.Hash, v int) { 46 + maphash.WriteComparable(h, v) 47 + } 48 + 49 + // node represents a struct pointer containing a slice - a common pattern 50 + // that needs custom equality/hashing since the pointer itself isn't enough. 51 + type node struct { 52 + values []int 53 + } 54 + 55 + // nodeHasher implements deep equality for node pointers. 56 + type nodeHasher struct{} 57 + 58 + func (nodeHasher) Equal(a, b *node) bool { 59 + if a == b { 60 + return true 61 + } 62 + if a == nil || b == nil { 63 + return false 64 + } 65 + return slices.Equal(a.values, b.values) 66 + } 67 + 68 + func (nodeHasher) Hash(h *maphash.Hash, n *node) { 69 + if n == nil { 70 + return 71 + } 72 + for _, v := range n.values { 73 + maphash.WriteComparable(h, v) 74 + } 75 + } 76 + 77 + func TestNew(t *testing.T) { 78 + s := anyunique.New[string](stringHasher{}) 79 + qt.Assert(t, qt.Not(qt.IsNil(s))) 80 + } 81 + 82 + func TestStore_Make_BasicEquality(t *testing.T) { 83 + s := anyunique.New[string](stringHasher{}) 84 + 85 + // First call creates a new unique value 86 + u1 := s.Make("hello") 87 + qt.Assert(t, qt.Equals(u1.Value(), "hello")) 88 + 89 + // Second call with same value should return equal U[T] 90 + u2 := s.Make("hello") 91 + qt.Assert(t, qt.Equals(u2.Value(), "hello")) 92 + qt.Assert(t, qt.Equals(u1, u2)) 93 + 94 + // Different value should not be equal 95 + u3 := s.Make("world") 96 + qt.Assert(t, qt.Equals(u3.Value(), "world")) 97 + qt.Assert(t, qt.Not(qt.Equals(u1, u3))) 98 + } 99 + 100 + func TestStore_Make_ZeroValue(t *testing.T) { 101 + s := anyunique.New[string](stringHasher{}) 102 + 103 + // Zero value should be handled specially 104 + u1 := s.Make("") 105 + u2 := s.Make("") 106 + qt.Assert(t, qt.Equals(u1.Value(), "")) 107 + qt.Assert(t, qt.Equals(u2.Value(), "")) 108 + qt.Assert(t, qt.Equals(u1, u2)) 109 + 110 + // Zero value should not equal non-zero 111 + u3 := s.Make("something") 112 + qt.Assert(t, qt.Not(qt.Equals(u1, u3))) 113 + } 114 + 115 + func TestStore_Make_StructPointers(t *testing.T) { 116 + s := anyunique.New[*node](nodeHasher{}) 117 + 118 + node1 := &node{values: []int{1, 2, 3}} 119 + node2 := &node{values: []int{1, 2, 3}} // same content, different pointer 120 + node3 := &node{values: []int{1, 2, 4}} // different content 121 + 122 + u1 := s.Make(node1) 123 + u2 := s.Make(node2) 124 + u3 := s.Make(node3) 125 + 126 + // u1 and u2 should be equal (same content, even though different pointers) 127 + qt.Assert(t, qt.Equals(u1, u2)) 128 + qt.Assert(t, qt.DeepEquals(u1.Value().values, []int{1, 2, 3})) 129 + qt.Assert(t, qt.DeepEquals(u2.Value().values, []int{1, 2, 3})) 130 + 131 + // The stored pointer should be the first one encountered 132 + qt.Assert(t, qt.Equals(u1.Value(), node1)) 133 + qt.Assert(t, qt.Equals(u2.Value(), node1)) // canonicalized to node1 134 + 135 + // u3 should be different 136 + qt.Assert(t, qt.Not(qt.Equals(u1, u3))) 137 + qt.Assert(t, qt.DeepEquals(u3.Value().values, []int{1, 2, 4})) 138 + qt.Assert(t, qt.Equals(u3.Value(), node3)) 139 + } 140 + 141 + func TestStore_Make_IntValues(t *testing.T) { 142 + s := anyunique.New[int](simpleIntHasher{}) 143 + 144 + u1 := s.Make(42) 145 + u2 := s.Make(42) 146 + u3 := s.Make(100) 147 + 148 + qt.Assert(t, qt.Equals(u1, u2)) 149 + qt.Assert(t, qt.Not(qt.Equals(u1, u3))) 150 + qt.Assert(t, qt.Equals(u1.Value(), 42)) 151 + qt.Assert(t, qt.Equals(u3.Value(), 100)) 152 + } 153 + 154 + func TestStore_Make_MultipleValues(t *testing.T) { 155 + s := anyunique.New[string](stringHasher{}) 156 + 157 + values := []string{"apple", "banana", "cherry", "date", "elderberry"} 158 + uniqueValues := make(map[anyunique.Handle[string]]bool) 159 + 160 + // Create unique values for all strings 161 + for _, v := range values { 162 + u := s.Make(v) 163 + uniqueValues[u] = true 164 + qt.Assert(t, qt.Equals(u.Value(), v)) 165 + } 166 + 167 + // Should have 5 distinct unique values 168 + qt.Assert(t, qt.Equals(len(uniqueValues), 5)) 169 + 170 + // Re-making the same values should return equal U[T]s 171 + for _, v := range values { 172 + u := s.Make(v) 173 + qt.Assert(t, qt.Equals(uniqueValues[u], true)) 174 + } 175 + } 176 + 177 + // badHasher creates intentional hash collisions for testing. 178 + type badHasher struct{} 179 + 180 + func (badHasher) Equal(a, b string) bool { 181 + return a == b 182 + } 183 + 184 + func (badHasher) Hash(*maphash.Hash, string) { 185 + // Don't write anything, so we always get the same hash 186 + } 187 + 188 + func TestStore_Make_HashCollisions(t *testing.T) { 189 + s := anyunique.New[string](badHasher{}) 190 + 191 + // All these will hash to the same bucket 192 + u1 := s.Make("key1") 193 + u2 := s.Make("key2") 194 + u3 := s.Make("key3") 195 + 196 + // They should all be different despite hash collision 197 + qt.Assert(t, qt.Not(qt.Equals(u1, u2))) 198 + qt.Assert(t, qt.Not(qt.Equals(u2, u3))) 199 + qt.Assert(t, qt.Not(qt.Equals(u1, u3))) 200 + 201 + // But re-making should return equal values 202 + u1b := s.Make("key1") 203 + u2b := s.Make("key2") 204 + u3b := s.Make("key3") 205 + 206 + qt.Assert(t, qt.Equals(u1, u1b)) 207 + qt.Assert(t, qt.Equals(u2, u2b)) 208 + qt.Assert(t, qt.Equals(u3, u3b)) 209 + } 210 + 211 + func TestStore_WriteHash(t *testing.T) { 212 + s := anyunique.New[string](stringHasher{}) 213 + 214 + u1 := s.Make("hello") 215 + u2 := s.Make("hello") 216 + u3 := s.Make("world") 217 + 218 + // Write hashes for equal values 219 + var h1, h2 maphash.Hash 220 + h1.SetSeed(maphash.MakeSeed()) 221 + h2.SetSeed(h1.Seed()) 222 + 223 + s.WriteHash(&h1, u1) 224 + s.WriteHash(&h2, u2) 225 + 226 + // Hashes of equal values should be the same 227 + qt.Assert(t, qt.Equals(h1.Sum64(), h2.Sum64())) 228 + 229 + // Hash of different value should be different 230 + var h3 maphash.Hash 231 + h3.SetSeed(h1.Seed()) 232 + s.WriteHash(&h3, u3) 233 + 234 + qt.Assert(t, qt.Not(qt.Equals(h1.Sum64(), h3.Sum64()))) 235 + } 236 + 237 + func TestStore_WriteHash_ZeroValue(t *testing.T) { 238 + s := anyunique.New[string](stringHasher{}) 239 + 240 + u1 := s.Make("") 241 + u2 := s.Make("") 242 + 243 + var h1, h2 maphash.Hash 244 + seed := maphash.MakeSeed() 245 + h1.SetSeed(seed) 246 + h2.SetSeed(seed) 247 + 248 + s.WriteHash(&h1, u1) 249 + s.WriteHash(&h2, u2) 250 + 251 + // Zero values should hash the same 252 + qt.Assert(t, qt.Equals(h1.Sum64(), h2.Sum64())) 253 + } 254 + 255 + func TestU_Get(t *testing.T) { 256 + s := anyunique.New[string](stringHasher{}) 257 + 258 + u := s.Make("test") 259 + qt.Assert(t, qt.Equals(u.Value(), "test")) 260 + 261 + // Zero value 262 + var zeroU anyunique.Handle[string] 263 + qt.Assert(t, qt.Equals(zeroU.Value(), "")) 264 + } 265 + 266 + func TestStore_Make_NilPointers(t *testing.T) { 267 + s := anyunique.New[*node](nodeHasher{}) 268 + 269 + u1 := s.Make(nil) 270 + u2 := s.Make(nil) 271 + 272 + // Nil pointers should be treated as zero values 273 + qt.Assert(t, qt.Equals(u1, u2)) 274 + qt.Assert(t, qt.IsNil(u1.Value())) 275 + qt.Assert(t, qt.IsNil(u2.Value())) 276 + 277 + // Non-nil should be different from nil 278 + u3 := s.Make(&node{values: []int{1}}) 279 + qt.Assert(t, qt.Not(qt.Equals(u1, u3))) 280 + } 281 + 282 + func TestStore_Make_RepeatedCalls(t *testing.T) { 283 + s := anyunique.New[string](stringHasher{}) 284 + 285 + // Make the same value many times 286 + var values []anyunique.Handle[string] 287 + for i := 0; i < 100; i++ { 288 + values = append(values, s.Make("repeated")) 289 + } 290 + 291 + // All should be equal 292 + first := values[0] 293 + for _, v := range values[1:] { 294 + qt.Assert(t, qt.Equals(v, first)) 295 + } 296 + } 297 + 298 + func TestStore_Make_DifferentStores(t *testing.T) { 299 + s1 := anyunique.New[string](stringHasher{}) 300 + s2 := anyunique.New[string](stringHasher{}) 301 + 302 + u1 := s1.Make("hello") 303 + u2 := s2.Make("hello") 304 + 305 + // Values from different stores should not be comparable in practice, 306 + // but technically they might compare equal if the underlying strings 307 + // are the same. The important thing is they're independent stores. 308 + qt.Assert(t, qt.Equals(u1.Value(), "hello")) 309 + qt.Assert(t, qt.Equals(u2.Value(), "hello")) 310 + 311 + // Hashes from different stores will be different due to different seeds 312 + var h1, h2 maphash.Hash 313 + h1.SetSeed(maphash.MakeSeed()) 314 + h2.SetSeed(maphash.MakeSeed()) 315 + 316 + s1.WriteHash(&h1, u1) 317 + s2.WriteHash(&h2, u2) 318 + 319 + // Very unlikely to be equal with different seeds 320 + // (This test might occasionally fail due to hash collision, but extremely rare) 321 + } 322 + 323 + func TestStore_WriteHash_ConsistentWithMake(t *testing.T) { 324 + s := anyunique.New[string](stringHasher{}) 325 + 326 + // Create several unique values 327 + values := []string{"alpha", "beta", "gamma"} 328 + uniques := make([]anyunique.Handle[string], len(values)) 329 + for i, v := range values { 330 + uniques[i] = s.Make(v) 331 + } 332 + 333 + // The hash of the unique value should be consistent 334 + // i.e., calling WriteHash multiple times should give the same result 335 + for _, u := range uniques { 336 + var h1, h2 maphash.Hash 337 + seed := maphash.MakeSeed() 338 + h1.SetSeed(seed) 339 + h2.SetSeed(seed) 340 + 341 + s.WriteHash(&h1, u) 342 + s.WriteHash(&h2, u) 343 + 344 + qt.Assert(t, qt.Equals(h1.Sum64(), h2.Sum64())) 345 + } 346 + } 347 + 348 + func TestStore_Make_StressTest(t *testing.T) { 349 + s := anyunique.New[int](simpleIntHasher{}) 350 + 351 + // Create many unique values 352 + n := 10000 353 + uniqueMap := make(map[int]anyunique.Handle[int]) 354 + 355 + for i := 0; i < n; i++ { 356 + u := s.Make(i) 357 + uniqueMap[i] = u 358 + qt.Assert(t, qt.Equals(u.Value(), i)) 359 + } 360 + 361 + // Verify all values are stored correctly and re-making gives equal results 362 + for i := 0; i < n; i++ { 363 + u := s.Make(i) 364 + qt.Assert(t, qt.Equals(u, uniqueMap[i])) 365 + } 366 + } 367 + 368 + // treeNode represents a tree structure with unique child pointers 369 + type treeNode struct { 370 + value int 371 + children []*treeNode 372 + } 373 + 374 + // treeHasher implements deep equality for tree nodes 375 + type treeHasher struct { 376 + childStore *anyunique.Store[*treeNode, treeHasher] 377 + } 378 + 379 + func (h treeHasher) Equal(a, b *treeNode) bool { 380 + if a == b { 381 + return true 382 + } 383 + if a == nil || b == nil { 384 + return false 385 + } 386 + if a.value != b.value || len(a.children) != len(b.children) { 387 + return false 388 + } 389 + for i := range a.children { 390 + if !h.Equal(a.children[i], b.children[i]) { 391 + return false 392 + } 393 + } 394 + return true 395 + } 396 + 397 + func (h treeHasher) Hash(hash *maphash.Hash, n *treeNode) { 398 + if n == nil { 399 + return 400 + } 401 + maphash.WriteComparable(hash, n.value) 402 + maphash.WriteComparable(hash, len(n.children)) 403 + for _, child := range n.children { 404 + h.Hash(hash, child) 405 + } 406 + } 407 + 408 + func TestStore_Make_NestedStructures(t *testing.T) { 409 + s := anyunique.New[*treeNode](treeHasher{}) 410 + 411 + // Create identical tree structures with different pointers 412 + tree1 := &treeNode{ 413 + value: 1, 414 + children: []*treeNode{ 415 + {value: 2}, 416 + {value: 3}, 417 + }, 418 + } 419 + 420 + tree2 := &treeNode{ 421 + value: 1, 422 + children: []*treeNode{ 423 + {value: 2}, 424 + {value: 3}, 425 + }, 426 + } 427 + 428 + tree3 := &treeNode{ 429 + value: 1, 430 + children: []*treeNode{ 431 + {value: 2}, 432 + {value: 4}, // different 433 + }, 434 + } 435 + 436 + u1 := s.Make(tree1) 437 + u2 := s.Make(tree2) 438 + u3 := s.Make(tree3) 439 + 440 + // tree1 and tree2 have same structure, should be equal 441 + qt.Assert(t, qt.Equals(u1, u2)) 442 + qt.Assert(t, qt.Equals(u1.Value(), tree1)) // canonicalized to tree1 443 + 444 + // tree3 is different 445 + qt.Assert(t, qt.Not(qt.Equals(u1, u3))) 446 + } 447 + 448 + func TestStore_Make_ZeroValueInt(t *testing.T) { 449 + s := anyunique.New[int](simpleIntHasher{}) 450 + 451 + u1 := s.Make(0) 452 + u2 := s.Make(0) 453 + 454 + // Zero value for int is 0 455 + qt.Assert(t, qt.Equals(u1, u2)) 456 + qt.Assert(t, qt.Equals(u1.Value(), 0)) 457 + } 458 + 459 + func TestStore_WriteHash_DifferentSeeds(t *testing.T) { 460 + s := anyunique.New[string](stringHasher{}) 461 + 462 + u := s.Make("test") 463 + 464 + // Different seeds should produce different hashes 465 + var h1, h2 maphash.Hash 466 + h1.SetSeed(maphash.MakeSeed()) 467 + h2.SetSeed(maphash.MakeSeed()) 468 + 469 + s.WriteHash(&h1, u) 470 + s.WriteHash(&h2, u) 471 + 472 + // With extremely high probability, different seeds give different hashes 473 + // Note: This could theoretically fail, but it's astronomically unlikely 474 + } 475 + 476 + func TestStore_Make_LargeStructPointers(t *testing.T) { 477 + s := anyunique.New[*node](nodeHasher{}) 478 + 479 + // Create nodes with large slices 480 + largeSlice := make([]int, 1000) 481 + for i := range largeSlice { 482 + largeSlice[i] = i 483 + } 484 + 485 + node1 := &node{values: largeSlice} 486 + node2 := &node{values: append([]int{}, largeSlice...)} // copy 487 + 488 + u1 := s.Make(node1) 489 + u2 := s.Make(node2) 490 + 491 + // Should be equal despite different underlying pointers 492 + qt.Assert(t, qt.Equals(u1, u2)) 493 + qt.Assert(t, qt.Equals(u1.Value(), node1)) 494 + } 495 + 496 + func TestStore_Make_AlternatingPatterns(t *testing.T) { 497 + s := anyunique.New[string](stringHasher{}) 498 + 499 + // Alternate between two values many times 500 + for i := 0; i < 100; i++ { 501 + u1 := s.Make("a") 502 + u2 := s.Make("b") 503 + u3 := s.Make("a") 504 + 505 + qt.Assert(t, qt.Equals(u1, u3)) 506 + qt.Assert(t, qt.Not(qt.Equals(u1, u2))) 507 + } 508 + } 509 + 510 + func TestStore_WriteHash_MultipleValues(t *testing.T) { 511 + s := anyunique.New[string](stringHasher{}) 512 + 513 + values := []string{"apple", "banana", "cherry", "date"} 514 + uniques := make([]anyunique.Handle[string], len(values)) 515 + 516 + for i, v := range values { 517 + uniques[i] = s.Make(v) 518 + } 519 + 520 + // All unique values should have different hashes 521 + hashes := make(map[uint64]bool) 522 + seed := maphash.MakeSeed() 523 + 524 + for _, u := range uniques { 525 + var h maphash.Hash 526 + h.SetSeed(seed) 527 + s.WriteHash(&h, u) 528 + hash := h.Sum64() 529 + 530 + // Each should be unique (no collisions expected for these values) 531 + qt.Assert(t, qt.Equals(hashes[hash], false)) 532 + hashes[hash] = true 533 + } 534 + }