1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package runtime_test 6 7import ( 8 "encoding/binary" 9 "fmt" 10 "internal/race" 11 "internal/testenv" 12 "math" 13 "math/rand" 14 "os" 15 . "runtime" 16 "slices" 17 "strings" 18 "testing" 19 "unsafe" 20) 21 22func TestMemHash32Equality(t *testing.T) { 23 if *UseAeshash { 24 t.Skip("skipping since AES hash implementation is used") 25 } 26 var b [4]byte 27 r := rand.New(rand.NewSource(1234)) 28 seed := uintptr(r.Uint64()) 29 for i := 0; i < 100; i++ { 30 randBytes(r, b[:]) 31 got := MemHash32(unsafe.Pointer(&b), seed) 32 want := MemHash(unsafe.Pointer(&b), seed, 4) 33 if got != want { 34 t.Errorf("MemHash32(%x, %v) = %v; want %v", b, seed, got, want) 35 } 36 } 37} 38 39func TestMemHash64Equality(t *testing.T) { 40 if *UseAeshash { 41 t.Skip("skipping since AES hash implementation is used") 42 } 43 var b [8]byte 44 r := rand.New(rand.NewSource(1234)) 45 seed := uintptr(r.Uint64()) 46 for i := 0; i < 100; i++ { 47 randBytes(r, b[:]) 48 got := MemHash64(unsafe.Pointer(&b), seed) 49 want := MemHash(unsafe.Pointer(&b), seed, 8) 50 if got != want { 51 t.Errorf("MemHash64(%x, %v) = %v; want %v", b, seed, got, want) 52 } 53 } 54} 55 56// Smhasher is a torture test for hash functions. 57// https://code.google.com/p/smhasher/ 58// This code is a port of some of the Smhasher tests to Go. 59// 60// The current AES hash function passes Smhasher. Our fallback 61// hash functions don't, so we only enable the difficult tests when 62// we know the AES implementation is available. 63 64// Sanity checks. 65// hash should not depend on values outside key. 66// hash should not depend on alignment. 67func TestSmhasherSanity(t *testing.T) { 68 r := rand.New(rand.NewSource(1234)) 69 const REP = 10 70 const KEYMAX = 128 71 const PAD = 16 72 const OFFMAX = 16 73 for k := 0; k < REP; k++ { 74 for n := 0; n < KEYMAX; n++ { 75 for i := 0; i < OFFMAX; i++ { 76 var b [KEYMAX + OFFMAX + 2*PAD]byte 77 var c [KEYMAX + OFFMAX + 2*PAD]byte 78 randBytes(r, b[:]) 79 randBytes(r, c[:]) 80 copy(c[PAD+i:PAD+i+n], b[PAD:PAD+n]) 81 if BytesHash(b[PAD:PAD+n], 0) != BytesHash(c[PAD+i:PAD+i+n], 0) { 82 t.Errorf("hash depends on bytes outside key") 83 } 84 } 85 } 86 } 87} 88 89type HashSet struct { 90 list []uintptr // list of hashes added 91} 92 93func newHashSet() *HashSet { 94 return &HashSet{list: make([]uintptr, 0, 1024)} 95} 96func (s *HashSet) add(h uintptr) { 97 s.list = append(s.list, h) 98} 99func (s *HashSet) addS(x string) { 100 s.add(StringHash(x, 0)) 101} 102func (s *HashSet) addB(x []byte) { 103 s.add(BytesHash(x, 0)) 104} 105func (s *HashSet) addS_seed(x string, seed uintptr) { 106 s.add(StringHash(x, seed)) 107} 108func (s *HashSet) check(t *testing.T) { 109 list := s.list 110 slices.Sort(list) 111 112 collisions := 0 113 for i := 1; i < len(list); i++ { 114 if list[i] == list[i-1] { 115 collisions++ 116 } 117 } 118 n := len(list) 119 120 const SLOP = 50.0 121 pairs := int64(n) * int64(n-1) / 2 122 expected := float64(pairs) / math.Pow(2.0, float64(hashSize)) 123 stddev := math.Sqrt(expected) 124 if float64(collisions) > expected+SLOP*(3*stddev+1) { 125 t.Errorf("unexpected number of collisions: got=%d mean=%f stddev=%f threshold=%f", collisions, expected, stddev, expected+SLOP*(3*stddev+1)) 126 } 127 // Reset for reuse 128 s.list = s.list[:0] 129} 130 131// a string plus adding zeros must make distinct hashes 132func TestSmhasherAppendedZeros(t *testing.T) { 133 s := "hello" + strings.Repeat("\x00", 256) 134 h := newHashSet() 135 for i := 0; i <= len(s); i++ { 136 h.addS(s[:i]) 137 } 138 h.check(t) 139} 140 141// All 0-3 byte strings have distinct hashes. 142func TestSmhasherSmallKeys(t *testing.T) { 143 if race.Enabled { 144 t.Skip("Too long for race mode") 145 } 146 testenv.ParallelOn64Bit(t) 147 h := newHashSet() 148 var b [3]byte 149 for i := 0; i < 256; i++ { 150 b[0] = byte(i) 151 h.addB(b[:1]) 152 for j := 0; j < 256; j++ { 153 b[1] = byte(j) 154 h.addB(b[:2]) 155 if !testing.Short() { 156 for k := 0; k < 256; k++ { 157 b[2] = byte(k) 158 h.addB(b[:3]) 159 } 160 } 161 } 162 } 163 h.check(t) 164} 165 166// Different length strings of all zeros have distinct hashes. 167func TestSmhasherZeros(t *testing.T) { 168 t.Parallel() 169 N := 256 * 1024 170 if testing.Short() { 171 N = 1024 172 } 173 h := newHashSet() 174 b := make([]byte, N) 175 for i := 0; i <= N; i++ { 176 h.addB(b[:i]) 177 } 178 h.check(t) 179} 180 181// Strings with up to two nonzero bytes all have distinct hashes. 182func TestSmhasherTwoNonzero(t *testing.T) { 183 if GOARCH == "wasm" { 184 t.Skip("Too slow on wasm") 185 } 186 if testing.Short() { 187 t.Skip("Skipping in short mode") 188 } 189 if race.Enabled { 190 t.Skip("Too long for race mode") 191 } 192 testenv.ParallelOn64Bit(t) 193 h := newHashSet() 194 for n := 2; n <= 16; n++ { 195 twoNonZero(h, n) 196 } 197 h.check(t) 198} 199func twoNonZero(h *HashSet, n int) { 200 b := make([]byte, n) 201 202 // all zero 203 h.addB(b) 204 205 // one non-zero byte 206 for i := 0; i < n; i++ { 207 for x := 1; x < 256; x++ { 208 b[i] = byte(x) 209 h.addB(b) 210 b[i] = 0 211 } 212 } 213 214 // two non-zero bytes 215 for i := 0; i < n; i++ { 216 for x := 1; x < 256; x++ { 217 b[i] = byte(x) 218 for j := i + 1; j < n; j++ { 219 for y := 1; y < 256; y++ { 220 b[j] = byte(y) 221 h.addB(b) 222 b[j] = 0 223 } 224 } 225 b[i] = 0 226 } 227 } 228} 229 230// Test strings with repeats, like "abcdabcdabcdabcd..." 231func TestSmhasherCyclic(t *testing.T) { 232 if testing.Short() { 233 t.Skip("Skipping in short mode") 234 } 235 if race.Enabled { 236 t.Skip("Too long for race mode") 237 } 238 t.Parallel() 239 r := rand.New(rand.NewSource(1234)) 240 const REPEAT = 8 241 const N = 1000000 242 h := newHashSet() 243 for n := 4; n <= 12; n++ { 244 b := make([]byte, REPEAT*n) 245 for i := 0; i < N; i++ { 246 b[0] = byte(i * 79 % 97) 247 b[1] = byte(i * 43 % 137) 248 b[2] = byte(i * 151 % 197) 249 b[3] = byte(i * 199 % 251) 250 randBytes(r, b[4:n]) 251 for j := n; j < n*REPEAT; j++ { 252 b[j] = b[j-n] 253 } 254 h.addB(b) 255 } 256 h.check(t) 257 } 258} 259 260// Test strings with only a few bits set 261func TestSmhasherSparse(t *testing.T) { 262 if GOARCH == "wasm" { 263 t.Skip("Too slow on wasm") 264 } 265 if testing.Short() { 266 t.Skip("Skipping in short mode") 267 } 268 t.Parallel() 269 h := newHashSet() 270 sparse(t, h, 32, 6) 271 sparse(t, h, 40, 6) 272 sparse(t, h, 48, 5) 273 sparse(t, h, 56, 5) 274 sparse(t, h, 64, 5) 275 sparse(t, h, 96, 4) 276 sparse(t, h, 256, 3) 277 sparse(t, h, 2048, 2) 278} 279func sparse(t *testing.T, h *HashSet, n int, k int) { 280 b := make([]byte, n/8) 281 setbits(h, b, 0, k) 282 h.check(t) 283} 284 285// set up to k bits at index i and greater 286func setbits(h *HashSet, b []byte, i int, k int) { 287 h.addB(b) 288 if k == 0 { 289 return 290 } 291 for j := i; j < len(b)*8; j++ { 292 b[j/8] |= byte(1 << uint(j&7)) 293 setbits(h, b, j+1, k-1) 294 b[j/8] &= byte(^(1 << uint(j&7))) 295 } 296} 297 298// Test all possible combinations of n blocks from the set s. 299// "permutation" is a bad name here, but it is what Smhasher uses. 300func TestSmhasherPermutation(t *testing.T) { 301 if GOARCH == "wasm" { 302 t.Skip("Too slow on wasm") 303 } 304 if testing.Short() { 305 t.Skip("Skipping in short mode") 306 } 307 if race.Enabled { 308 t.Skip("Too long for race mode") 309 } 310 testenv.ParallelOn64Bit(t) 311 h := newHashSet() 312 permutation(t, h, []uint32{0, 1, 2, 3, 4, 5, 6, 7}, 8) 313 permutation(t, h, []uint32{0, 1 << 29, 2 << 29, 3 << 29, 4 << 29, 5 << 29, 6 << 29, 7 << 29}, 8) 314 permutation(t, h, []uint32{0, 1}, 20) 315 permutation(t, h, []uint32{0, 1 << 31}, 20) 316 permutation(t, h, []uint32{0, 1, 2, 3, 4, 5, 6, 7, 1 << 29, 2 << 29, 3 << 29, 4 << 29, 5 << 29, 6 << 29, 7 << 29}, 6) 317} 318func permutation(t *testing.T, h *HashSet, s []uint32, n int) { 319 b := make([]byte, n*4) 320 genPerm(h, b, s, 0) 321 h.check(t) 322} 323func genPerm(h *HashSet, b []byte, s []uint32, n int) { 324 h.addB(b[:n]) 325 if n == len(b) { 326 return 327 } 328 for _, v := range s { 329 b[n] = byte(v) 330 b[n+1] = byte(v >> 8) 331 b[n+2] = byte(v >> 16) 332 b[n+3] = byte(v >> 24) 333 genPerm(h, b, s, n+4) 334 } 335} 336 337type Key interface { 338 clear() // set bits all to 0 339 random(r *rand.Rand) // set key to something random 340 bits() int // how many bits key has 341 flipBit(i int) // flip bit i of the key 342 hash() uintptr // hash the key 343 name() string // for error reporting 344} 345 346type BytesKey struct { 347 b []byte 348} 349 350func (k *BytesKey) clear() { 351 clear(k.b) 352} 353func (k *BytesKey) random(r *rand.Rand) { 354 randBytes(r, k.b) 355} 356func (k *BytesKey) bits() int { 357 return len(k.b) * 8 358} 359func (k *BytesKey) flipBit(i int) { 360 k.b[i>>3] ^= byte(1 << uint(i&7)) 361} 362func (k *BytesKey) hash() uintptr { 363 return BytesHash(k.b, 0) 364} 365func (k *BytesKey) name() string { 366 return fmt.Sprintf("bytes%d", len(k.b)) 367} 368 369type Int32Key struct { 370 i uint32 371} 372 373func (k *Int32Key) clear() { 374 k.i = 0 375} 376func (k *Int32Key) random(r *rand.Rand) { 377 k.i = r.Uint32() 378} 379func (k *Int32Key) bits() int { 380 return 32 381} 382func (k *Int32Key) flipBit(i int) { 383 k.i ^= 1 << uint(i) 384} 385func (k *Int32Key) hash() uintptr { 386 return Int32Hash(k.i, 0) 387} 388func (k *Int32Key) name() string { 389 return "int32" 390} 391 392type Int64Key struct { 393 i uint64 394} 395 396func (k *Int64Key) clear() { 397 k.i = 0 398} 399func (k *Int64Key) random(r *rand.Rand) { 400 k.i = uint64(r.Uint32()) + uint64(r.Uint32())<<32 401} 402func (k *Int64Key) bits() int { 403 return 64 404} 405func (k *Int64Key) flipBit(i int) { 406 k.i ^= 1 << uint(i) 407} 408func (k *Int64Key) hash() uintptr { 409 return Int64Hash(k.i, 0) 410} 411func (k *Int64Key) name() string { 412 return "int64" 413} 414 415type EfaceKey struct { 416 i any 417} 418 419func (k *EfaceKey) clear() { 420 k.i = nil 421} 422func (k *EfaceKey) random(r *rand.Rand) { 423 k.i = uint64(r.Int63()) 424} 425func (k *EfaceKey) bits() int { 426 // use 64 bits. This tests inlined interfaces 427 // on 64-bit targets and indirect interfaces on 428 // 32-bit targets. 429 return 64 430} 431func (k *EfaceKey) flipBit(i int) { 432 k.i = k.i.(uint64) ^ uint64(1)<<uint(i) 433} 434func (k *EfaceKey) hash() uintptr { 435 return EfaceHash(k.i, 0) 436} 437func (k *EfaceKey) name() string { 438 return "Eface" 439} 440 441type IfaceKey struct { 442 i interface { 443 F() 444 } 445} 446type fInter uint64 447 448func (x fInter) F() { 449} 450 451func (k *IfaceKey) clear() { 452 k.i = nil 453} 454func (k *IfaceKey) random(r *rand.Rand) { 455 k.i = fInter(r.Int63()) 456} 457func (k *IfaceKey) bits() int { 458 // use 64 bits. This tests inlined interfaces 459 // on 64-bit targets and indirect interfaces on 460 // 32-bit targets. 461 return 64 462} 463func (k *IfaceKey) flipBit(i int) { 464 k.i = k.i.(fInter) ^ fInter(1)<<uint(i) 465} 466func (k *IfaceKey) hash() uintptr { 467 return IfaceHash(k.i, 0) 468} 469func (k *IfaceKey) name() string { 470 return "Iface" 471} 472 473// Flipping a single bit of a key should flip each output bit with 50% probability. 474func TestSmhasherAvalanche(t *testing.T) { 475 if GOARCH == "wasm" { 476 t.Skip("Too slow on wasm") 477 } 478 if testing.Short() { 479 t.Skip("Skipping in short mode") 480 } 481 if race.Enabled { 482 t.Skip("Too long for race mode") 483 } 484 t.Parallel() 485 avalancheTest1(t, &BytesKey{make([]byte, 2)}) 486 avalancheTest1(t, &BytesKey{make([]byte, 4)}) 487 avalancheTest1(t, &BytesKey{make([]byte, 8)}) 488 avalancheTest1(t, &BytesKey{make([]byte, 16)}) 489 avalancheTest1(t, &BytesKey{make([]byte, 32)}) 490 avalancheTest1(t, &BytesKey{make([]byte, 200)}) 491 avalancheTest1(t, &Int32Key{}) 492 avalancheTest1(t, &Int64Key{}) 493 avalancheTest1(t, &EfaceKey{}) 494 avalancheTest1(t, &IfaceKey{}) 495} 496func avalancheTest1(t *testing.T, k Key) { 497 const REP = 100000 498 r := rand.New(rand.NewSource(1234)) 499 n := k.bits() 500 501 // grid[i][j] is a count of whether flipping 502 // input bit i affects output bit j. 503 grid := make([][hashSize]int, n) 504 505 for z := 0; z < REP; z++ { 506 // pick a random key, hash it 507 k.random(r) 508 h := k.hash() 509 510 // flip each bit, hash & compare the results 511 for i := 0; i < n; i++ { 512 k.flipBit(i) 513 d := h ^ k.hash() 514 k.flipBit(i) 515 516 // record the effects of that bit flip 517 g := &grid[i] 518 for j := 0; j < hashSize; j++ { 519 g[j] += int(d & 1) 520 d >>= 1 521 } 522 } 523 } 524 525 // Each entry in the grid should be about REP/2. 526 // More precisely, we did N = k.bits() * hashSize experiments where 527 // each is the sum of REP coin flips. We want to find bounds on the 528 // sum of coin flips such that a truly random experiment would have 529 // all sums inside those bounds with 99% probability. 530 N := n * hashSize 531 var c float64 532 // find c such that Prob(mean-c*stddev < x < mean+c*stddev)^N > .9999 533 for c = 0.0; math.Pow(math.Erf(c/math.Sqrt(2)), float64(N)) < .9999; c += .1 { 534 } 535 c *= 11.0 // allowed slack: 40% to 60% - we don't need to be perfectly random 536 mean := .5 * REP 537 stddev := .5 * math.Sqrt(REP) 538 low := int(mean - c*stddev) 539 high := int(mean + c*stddev) 540 for i := 0; i < n; i++ { 541 for j := 0; j < hashSize; j++ { 542 x := grid[i][j] 543 if x < low || x > high { 544 t.Errorf("bad bias for %s bit %d -> bit %d: %d/%d\n", k.name(), i, j, x, REP) 545 } 546 } 547 } 548} 549 550// All bit rotations of a set of distinct keys 551func TestSmhasherWindowed(t *testing.T) { 552 if race.Enabled { 553 t.Skip("Too long for race mode") 554 } 555 t.Parallel() 556 h := newHashSet() 557 t.Logf("32 bit keys") 558 windowed(t, h, &Int32Key{}) 559 t.Logf("64 bit keys") 560 windowed(t, h, &Int64Key{}) 561 t.Logf("string keys") 562 windowed(t, h, &BytesKey{make([]byte, 128)}) 563} 564func windowed(t *testing.T, h *HashSet, k Key) { 565 if GOARCH == "wasm" { 566 t.Skip("Too slow on wasm") 567 } 568 if PtrSize == 4 { 569 // This test tends to be flaky on 32-bit systems. 570 // There's not enough bits in the hash output, so we 571 // expect a nontrivial number of collisions, and it is 572 // often quite a bit higher than expected. See issue 43130. 573 t.Skip("Flaky on 32-bit systems") 574 } 575 if testing.Short() { 576 t.Skip("Skipping in short mode") 577 } 578 const BITS = 16 579 580 for r := 0; r < k.bits(); r++ { 581 for i := 0; i < 1<<BITS; i++ { 582 k.clear() 583 for j := 0; j < BITS; j++ { 584 if i>>uint(j)&1 != 0 { 585 k.flipBit((j + r) % k.bits()) 586 } 587 } 588 h.add(k.hash()) 589 } 590 h.check(t) 591 } 592} 593 594// All keys of the form prefix + [A-Za-z0-9]*N + suffix. 595func TestSmhasherText(t *testing.T) { 596 if testing.Short() { 597 t.Skip("Skipping in short mode") 598 } 599 t.Parallel() 600 h := newHashSet() 601 text(t, h, "Foo", "Bar") 602 text(t, h, "FooBar", "") 603 text(t, h, "", "FooBar") 604} 605func text(t *testing.T, h *HashSet, prefix, suffix string) { 606 const N = 4 607 const S = "ABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrst0123456789" 608 const L = len(S) 609 b := make([]byte, len(prefix)+N+len(suffix)) 610 copy(b, prefix) 611 copy(b[len(prefix)+N:], suffix) 612 c := b[len(prefix):] 613 for i := 0; i < L; i++ { 614 c[0] = S[i] 615 for j := 0; j < L; j++ { 616 c[1] = S[j] 617 for k := 0; k < L; k++ { 618 c[2] = S[k] 619 for x := 0; x < L; x++ { 620 c[3] = S[x] 621 h.addB(b) 622 } 623 } 624 } 625 } 626 h.check(t) 627} 628 629// Make sure different seed values generate different hashes. 630func TestSmhasherSeed(t *testing.T) { 631 h := newHashSet() 632 const N = 100000 633 s := "hello" 634 for i := 0; i < N; i++ { 635 h.addS_seed(s, uintptr(i)) 636 } 637 h.check(t) 638} 639 640func TestIssue66841(t *testing.T) { 641 testenv.MustHaveExec(t) 642 if *UseAeshash && os.Getenv("TEST_ISSUE_66841") == "" { 643 // We want to test the backup hash, so if we're running on a machine 644 // that uses aeshash, exec ourselves while turning aes off. 645 cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestIssue66841$")) 646 cmd.Env = append(cmd.Env, "GODEBUG=cpu.aes=off", "TEST_ISSUE_66841=1") 647 out, err := cmd.CombinedOutput() 648 if err != nil { 649 t.Errorf("%s", string(out)) 650 } 651 // Fall through. Might as well run this test when aeshash is on also. 652 } 653 h := newHashSet() 654 var b [16]byte 655 binary.LittleEndian.PutUint64(b[:8], 0xe7037ed1a0b428db) // runtime.m2 656 for i := 0; i < 1000; i++ { 657 binary.LittleEndian.PutUint64(b[8:], uint64(i)) 658 h.addB(b[:]) 659 } 660 h.check(t) 661} 662 663// size of the hash output (32 or 64 bits) 664const hashSize = 32 + int(^uintptr(0)>>63<<5) 665 666func randBytes(r *rand.Rand, b []byte) { 667 for i := range b { 668 b[i] = byte(r.Uint32()) 669 } 670} 671 672func benchmarkHash(b *testing.B, n int) { 673 s := strings.Repeat("A", n) 674 675 for i := 0; i < b.N; i++ { 676 StringHash(s, 0) 677 } 678 b.SetBytes(int64(n)) 679} 680 681func BenchmarkHash5(b *testing.B) { benchmarkHash(b, 5) } 682func BenchmarkHash16(b *testing.B) { benchmarkHash(b, 16) } 683func BenchmarkHash64(b *testing.B) { benchmarkHash(b, 64) } 684func BenchmarkHash1024(b *testing.B) { benchmarkHash(b, 1024) } 685func BenchmarkHash65536(b *testing.B) { benchmarkHash(b, 65536) } 686 687func TestArrayHash(t *testing.T) { 688 // Make sure that "" in arrays hash correctly. The hash 689 // should at least scramble the input seed so that, e.g., 690 // {"","foo"} and {"foo",""} have different hashes. 691 692 // If the hash is bad, then all (8 choose 4) = 70 keys 693 // have the same hash. If so, we allocate 70/8 = 8 694 // overflow buckets. If the hash is good we don't 695 // normally allocate any overflow buckets, and the 696 // probability of even one or two overflows goes down rapidly. 697 // (There is always 1 allocation of the bucket array. The map 698 // header is allocated on the stack.) 699 f := func() { 700 // Make the key type at most 128 bytes. Otherwise, 701 // we get an allocation per key. 702 type key [8]string 703 m := make(map[key]bool, 70) 704 705 // fill m with keys that have 4 "foo"s and 4 ""s. 706 for i := 0; i < 256; i++ { 707 var k key 708 cnt := 0 709 for j := uint(0); j < 8; j++ { 710 if i>>j&1 != 0 { 711 k[j] = "foo" 712 cnt++ 713 } 714 } 715 if cnt == 4 { 716 m[k] = true 717 } 718 } 719 if len(m) != 70 { 720 t.Errorf("bad test: (8 choose 4) should be 70, not %d", len(m)) 721 } 722 } 723 if n := testing.AllocsPerRun(10, f); n > 6 { 724 t.Errorf("too many allocs %f - hash not balanced", n) 725 } 726} 727func TestStructHash(t *testing.T) { 728 // See the comment in TestArrayHash. 729 f := func() { 730 type key struct { 731 a, b, c, d, e, f, g, h string 732 } 733 m := make(map[key]bool, 70) 734 735 // fill m with keys that have 4 "foo"s and 4 ""s. 736 for i := 0; i < 256; i++ { 737 var k key 738 cnt := 0 739 if i&1 != 0 { 740 k.a = "foo" 741 cnt++ 742 } 743 if i&2 != 0 { 744 k.b = "foo" 745 cnt++ 746 } 747 if i&4 != 0 { 748 k.c = "foo" 749 cnt++ 750 } 751 if i&8 != 0 { 752 k.d = "foo" 753 cnt++ 754 } 755 if i&16 != 0 { 756 k.e = "foo" 757 cnt++ 758 } 759 if i&32 != 0 { 760 k.f = "foo" 761 cnt++ 762 } 763 if i&64 != 0 { 764 k.g = "foo" 765 cnt++ 766 } 767 if i&128 != 0 { 768 k.h = "foo" 769 cnt++ 770 } 771 if cnt == 4 { 772 m[k] = true 773 } 774 } 775 if len(m) != 70 { 776 t.Errorf("bad test: (8 choose 4) should be 70, not %d", len(m)) 777 } 778 } 779 if n := testing.AllocsPerRun(10, f); n > 6 { 780 t.Errorf("too many allocs %f - hash not balanced", n) 781 } 782} 783 784var sink uint64 785 786func BenchmarkAlignedLoad(b *testing.B) { 787 var buf [16]byte 788 p := unsafe.Pointer(&buf[0]) 789 var s uint64 790 for i := 0; i < b.N; i++ { 791 s += ReadUnaligned64(p) 792 } 793 sink = s 794} 795 796func BenchmarkUnalignedLoad(b *testing.B) { 797 var buf [16]byte 798 p := unsafe.Pointer(&buf[1]) 799 var s uint64 800 for i := 0; i < b.N; i++ { 801 s += ReadUnaligned64(p) 802 } 803 sink = s 804} 805 806func TestCollisions(t *testing.T) { 807 if testing.Short() { 808 t.Skip("Skipping in short mode") 809 } 810 t.Parallel() 811 for i := 0; i < 16; i++ { 812 for j := 0; j < 16; j++ { 813 if j == i { 814 continue 815 } 816 var a [16]byte 817 m := make(map[uint16]struct{}, 1<<16) 818 for n := 0; n < 1<<16; n++ { 819 a[i] = byte(n) 820 a[j] = byte(n >> 8) 821 m[uint16(BytesHash(a[:], 0))] = struct{}{} 822 } 823 // N balls in N bins, for N=65536 824 avg := 41427 825 stdDev := 123 826 if len(m) < avg-40*stdDev || len(m) > avg+40*stdDev { 827 t.Errorf("bad number of collisions i=%d j=%d outputs=%d out of 65536\n", i, j, len(m)) 828 } 829 } 830 } 831} 832