Source file src/runtime/hash_test.go

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

View as plain text