github.com/leiless/cuckoohash-go@v0.0.0-20210302141208-144fd4badaaf/map_test.go (about)

     1  package cuckoohash
     2  
     3  import (
     4  	"crypto/md5"
     5  	"crypto/rand"
     6  	"fmt"
     7  	"github.com/OneOfOne/xxhash"
     8  	"github.com/dgryski/go-farm"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"io"
    12  	rand2 "math/rand"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  var (
    18  	h1       = farm.Hash64WithSeed
    19  	h2       = xxhash.Checksum64S
    20  	dummyVal = []byte{0xa, 0xb, 0xc, 0xd, 0xe, 0xf}
    21  )
    22  
    23  func TestMap1(t *testing.T) {
    24  	m, err := newMap(1, 1, 1, h1, h2, true, true)
    25  	assert.Nil(t, err)
    26  	assert.True(t, m.IsEmpty())
    27  	assert.Equal(t, 0.0, m.LoadFactor())
    28  	assert.Nil(t, m.Get(nil))
    29  	assert.False(t, m.ContainsKey(nil))
    30  	assert.False(t, m.ContainsKey(nil))
    31  	assert.Equal(t, m.Get(nil, dummyVal), dummyVal)
    32  	assert.Equal(t, m.Get(nil, dummyVal), []byte{0xa, 0xb, 0xc, 0xd, 0xe, 0xf})
    33  	t.Log(m)
    34  
    35  	for i := 0; i < 256; i++ {
    36  		k := []byte{byte(i)}
    37  		assert.Nil(t, m.Get(k))
    38  		assert.False(t, m.ContainsKey(k))
    39  		assert.False(t, m.ContainsValue(k))
    40  	}
    41  	for i := 0; i < 256; i++ {
    42  		for j := 0; j < 256; j++ {
    43  			k := []byte{byte(i), byte(j)}
    44  			assert.Nil(t, m.Get(k))
    45  			assert.False(t, m.ContainsKey(k))
    46  			assert.False(t, m.ContainsValue(k))
    47  		}
    48  	}
    49  	assert.False(t, m.ContainsKey(nil))
    50  	assert.False(t, m.ContainsValue(nil))
    51  
    52  	m.Clear()
    53  	t.Log(m)
    54  
    55  	for i := 0; i < 256; i++ {
    56  		k := []byte{byte(i)}
    57  		oldVal, err := m.Put(k, k, true)
    58  		assert.Nil(t, err)
    59  		assert.Nil(t, oldVal)
    60  
    61  		v := m.Get(k)
    62  		assert.Equal(t, k, v)
    63  
    64  		assert.True(t, m.ContainsKey(k))
    65  		assert.True(t, m.ContainsValue(k))
    66  	}
    67  
    68  	for i := 0; i < 256; i++ {
    69  		k := []byte{byte(i)}
    70  		oldVal, err := m.Put(k, k, true)
    71  		assert.Nil(t, err)
    72  		assert.Equal(t, k, oldVal)
    73  
    74  		v := m.Get(k)
    75  		assert.Equal(t, k, v)
    76  
    77  		assert.True(t, m.ContainsKey(k))
    78  		assert.True(t, m.ContainsValue(k))
    79  	}
    80  
    81  	for i := 0; i < 256; i++ {
    82  		for j := 0; j < 256; j++ {
    83  			k := []byte{byte(i), byte(j)}
    84  			assert.Nil(t, m.Get(k))
    85  			assert.False(t, m.ContainsKey(k))
    86  			assert.False(t, m.ContainsValue(k))
    87  		}
    88  	}
    89  
    90  	t.Log(m)
    91  }
    92  
    93  func TestMap2(t *testing.T) {
    94  	m, err := newMap(1, 1, 1, h1, h2, true, true)
    95  	assert.Nil(t, err)
    96  
    97  	for i := 0; i < 256; i++ {
    98  		k := []byte{byte(i)}
    99  		oldVal, err := m.Put(k, k, true)
   100  		assert.Nil(t, err)
   101  		assert.Nil(t, oldVal)
   102  
   103  		v := m.Get(k)
   104  		assert.Equal(t, k, v)
   105  	}
   106  
   107  	assert.Equal(t, m.Count(), uint64(256))
   108  	t.Log(m)
   109  
   110  	for i := 0; i < 256; i++ {
   111  		k := []byte{byte(i)}
   112  		oldVal, err := m.Del(k)
   113  		assert.Nil(t, err)
   114  		assert.Equal(t, k, oldVal)
   115  	}
   116  
   117  	assert.True(t, m.IsEmpty())
   118  	t.Log(m)
   119  
   120  	for i := 0; i < 256; i++ {
   121  		k := []byte{byte(i)}
   122  		oldVal, err := m.Del(k)
   123  		assert.ErrorIs(t, err, ErrKeyNotFound)
   124  		assert.Nil(t, oldVal)
   125  
   126  		assert.Nil(t, m.Get(k))
   127  		assert.False(t, m.ContainsKey(k))
   128  		assert.False(t, m.ContainsValue(k))
   129  	}
   130  }
   131  
   132  func genRandomBytes(size int) []byte {
   133  	b := make([]byte, size)
   134  	n, err := rand.Read(b)
   135  	if err != nil {
   136  		panic(err)
   137  	} else if n != size {
   138  		panic(fmt.Errorf("%w: %v vs %v", io.ErrShortWrite, n, size))
   139  	}
   140  	return b
   141  }
   142  
   143  func TestMap3(t *testing.T) {
   144  	m, err := newMap(md5.Size, 1, 1, h1, h2, true, true)
   145  	assert.Nil(t, err)
   146  
   147  	n := 5000
   148  	list := make([][]byte, n)
   149  	for i := 0; i < n; i++ {
   150  		list[i] = genRandomBytes(md5.Size)
   151  		oldVal, err := m.Put(list[i], list[i], true)
   152  		assert.Nil(t, err)
   153  		assert.Nil(t, oldVal)
   154  
   155  		assert.True(t, m.ContainsKey(list[i]))
   156  		assert.True(t, m.ContainsValue(list[i]))
   157  		assert.Equal(t, m.Get(list[i]), list[i])
   158  	}
   159  
   160  	assert.Equal(t, m.Count(), uint64(n))
   161  	t.Log(m)
   162  
   163  	for i := 0; i < n; i++ {
   164  		k := genRandomBytes(md5.Size)
   165  		assert.Nil(t, m.Get(k))
   166  		assert.False(t, m.ContainsKey(k))
   167  		assert.False(t, m.ContainsValue(k))
   168  	}
   169  
   170  	for i := 0; i < n; i += 2 {
   171  		oldVal, err := m.Del(list[i])
   172  		assert.Nil(t, err)
   173  		assert.Equal(t, list[i], oldVal)
   174  	}
   175  
   176  	assert.Equal(t, m.Count(), uint64(n)/2)
   177  
   178  	for i := 1; i < n; i += 2 {
   179  		assert.NotNil(t, m.Get(list[i]))
   180  		assert.True(t, m.ContainsKey(list[i]))
   181  		assert.True(t, m.ContainsValue(list[i]))
   182  	}
   183  
   184  	for i := 1; i < n; i += 2 {
   185  		oldVal, err := m.Del(list[i])
   186  		assert.Nil(t, err)
   187  		assert.Equal(t, list[i], oldVal)
   188  	}
   189  
   190  	assert.True(t, m.IsEmpty())
   191  
   192  	for i := 0; i < n; i++ {
   193  		assert.Nil(t, m.Get(list[i]))
   194  		assert.False(t, m.ContainsKey(list[i]))
   195  		assert.False(t, m.ContainsValue(list[i]))
   196  	}
   197  }
   198  
   199  func TestMap4(t *testing.T) {
   200  	m, err := newMap(md5.Size, 1, 1, h1, h2, true, true)
   201  	assert.Nil(t, err)
   202  	require.Greater(t, md5.Size, 1)
   203  
   204  	n := 5000
   205  	keys := make([][]byte, n)
   206  	vals := make([][]byte, n)
   207  	for i := 0; i < n; i++ {
   208  		keys[i] = genRandomBytes(md5.Size)
   209  		vals[i] = genRandomBytes(md5.Size / 2)
   210  
   211  		oldVal, err := m.Put(keys[i], vals[i], true)
   212  		assert.Nil(t, err)
   213  		assert.Nil(t, oldVal)
   214  
   215  		assert.True(t, m.ContainsKey(keys[i]))
   216  		assert.True(t, m.ContainsValue(vals[i]))
   217  		assert.Equal(t, m.Get(keys[i]), vals[i])
   218  	}
   219  
   220  	for i := 0; i < n; i += 2 {
   221  		oldVal, err := m.Del(keys[i])
   222  		assert.Nil(t, err)
   223  		assert.Equal(t, vals[i], oldVal)
   224  
   225  		assert.Nil(t, m.Get(keys[i]))
   226  	}
   227  
   228  	assert.Equal(t, m.Count(), uint64(n)/2)
   229  
   230  	for i := 1; i < n; i += 2 {
   231  		assert.NotNil(t, m.Get(keys[i]))
   232  		assert.True(t, m.ContainsKey(keys[i]))
   233  		assert.True(t, m.ContainsValue(vals[i]))
   234  	}
   235  
   236  	for i := 1; i < n; i += 2 {
   237  		oldVal, err := m.Del(keys[i])
   238  		assert.Nil(t, err)
   239  		assert.Equal(t, vals[i], oldVal)
   240  	}
   241  
   242  	assert.True(t, m.IsEmpty())
   243  
   244  	for i := 0; i < n; i++ {
   245  		assert.Nil(t, m.Get(keys[i]))
   246  		assert.False(t, m.ContainsKey(keys[i]))
   247  		assert.False(t, m.ContainsValue(vals[i]))
   248  	}
   249  }
   250  
   251  func TestMap5(t *testing.T) {
   252  	m, err := newMap(md5.Size, 16, 1, h1, h2, false, true)
   253  	assert.Nil(t, err)
   254  
   255  	n := 5_000_000
   256  	keys := make([][]byte, n)
   257  	for i := 0; i < n; i++ {
   258  		keys[i] = genRandomBytes(md5.Size)
   259  	}
   260  
   261  	for i := 0; i < n; i++ {
   262  		oldVal, err := m.Put(keys[i], nil, true)
   263  		if err != nil {
   264  			panic(err)
   265  		} else if oldVal != nil {
   266  			panic(fmt.Sprintf("expected nil value, got %v", oldVal))
   267  		}
   268  	}
   269  
   270  	m.debug = true
   271  	m.sanityCheck()
   272  }
   273  
   274  func pickN(big []int, n int) []int {
   275  	if n <= 0 {
   276  		return nil
   277  	}
   278  	if n > len(big) {
   279  		n = len(big)
   280  	}
   281  	cot := make([]int, n)
   282  	arr := make([]int, n)
   283  	k := (len(big) / n) * n
   284  	r := rand2.NewSource(time.Now().UnixNano()).(rand2.Source64)
   285  	for i, e := range big {
   286  		var j int
   287  		if i < k {
   288  			j = i % n
   289  		} else {
   290  			j = int(r.Uint64()&0x7fff_ffff) % n
   291  		}
   292  		cot[j] += 1
   293  		if int(r.Uint64())%cot[j] == 0 {
   294  			arr[j] = e
   295  		}
   296  	}
   297  	return arr
   298  }
   299  
   300  func intArrToMap(arr []int) map[int]struct{} {
   301  	m := make(map[int]struct{})
   302  	for _, e := range arr {
   303  		m[e] = struct{}{}
   304  	}
   305  	return m
   306  }
   307  
   308  // Fuzzing test
   309  func TestMap6(t *testing.T) {
   310  	m, err := newMap(md5.Size, 3, 1, h1, h2, true, true)
   311  	assert.Nil(t, err)
   312  
   313  	n := 10000
   314  	keys := make([][]byte, n)
   315  	vals := make([][]byte, n)
   316  	for i := 0; i < n; i++ {
   317  		keys[i] = genRandomBytes(md5.Size)
   318  		vals[i] = genRandomBytes(md5.Size / 4)
   319  
   320  		oldVal, err := m.Put(keys[i], vals[i], true)
   321  		assert.Nil(t, err)
   322  		assert.Nil(t, oldVal)
   323  	}
   324  	assert.Equal(t, m.Count(), uint64(n))
   325  	t.Log(m)
   326  
   327  	r := rand2.NewSource(time.Now().UnixNano()).(rand2.Source64)
   328  	var p int
   329  	for p == 0 {
   330  		p = int(r.Uint64() % 5000)
   331  	}
   332  	indexes := make([]int, n)
   333  	for i := range indexes {
   334  		indexes[i] = i
   335  	}
   336  	keyIndexesToRemove := pickN(indexes, p)
   337  	for _, idx := range keyIndexesToRemove {
   338  		oldVal, err := m.Del(keys[idx])
   339  		assert.Nil(t, err)
   340  		assert.Equal(t, oldVal, vals[idx])
   341  	}
   342  
   343  	assert.Equal(t, int(m.Count()), len(keys)-len(keyIndexesToRemove))
   344  
   345  	indexSet := intArrToMap(keyIndexesToRemove)
   346  	for i := 0; i < n; i++ {
   347  		if _, ok := indexSet[i]; ok {
   348  			// Key-val removed
   349  			assert.Nil(t, m.Get(keys[i]))
   350  			assert.False(t, m.ContainsKey(keys[i]))
   351  			assert.False(t, m.ContainsValue(vals[i]))
   352  		} else {
   353  			val := m.Get(keys[i])
   354  			assert.Equal(t, val, vals[i])
   355  			assert.True(t, m.ContainsKey(keys[i]))
   356  			assert.True(t, m.ContainsValue(vals[i]))
   357  		}
   358  	}
   359  
   360  	m.sanityCheck()
   361  	t.Log(m)
   362  
   363  	m.Clear()
   364  	m.sanityCheck()
   365  }
   366  
   367  // In-expandable Map tests
   368  func TestMap7(t *testing.T) {
   369  	m, err := newMap(md5.Size, 2, 1, h1, h2, true, false)
   370  	assert.Nil(t, err)
   371  
   372  	b1 := genRandomBytes(md5.Size)
   373  	oldVal, err := m.Put(b1, b1, true)
   374  	assert.Nil(t, err)
   375  	assert.Nil(t, oldVal)
   376  
   377  	oldVal, err = m.Put(b1, nil, true)
   378  	assert.Nil(t, err)
   379  	assert.Equal(t, oldVal, b1)
   380  
   381  	b2 := genRandomBytes(md5.Size)
   382  	oldVal, err = m.Put(b2, b2, true)
   383  	assert.Nil(t, err)
   384  	assert.Nil(t, oldVal)
   385  
   386  	t.Log(m)
   387  	assert.Equal(t, m.LoadFactor(), 1.0)
   388  
   389  	oldVal, err = m.Put(b2, b1, true)
   390  	assert.Nil(t, err)
   391  	assert.Equal(t, oldVal, b2)
   392  
   393  	b3 := genRandomBytes(md5.Size)
   394  	oldVal, err = m.Put(b3, b3, true)
   395  	assert.ErrorIs(t, err, ErrBucketIsFull)
   396  	assert.Nil(t, oldVal)
   397  
   398  	assert.Equal(t, m.Get(b1), b1)
   399  	assert.True(t, m.ContainsKey(b1))
   400  	assert.True(t, m.ContainsValue(b1))
   401  
   402  	assert.Nil(t, m.Get(b3))
   403  	assert.False(t, m.ContainsKey(b3))
   404  	assert.False(t, m.ContainsValue(b3))
   405  
   406  	assert.Nil(t, m.Get(nil))
   407  	assert.False(t, m.ContainsKey(nil))
   408  	assert.False(t, m.ContainsValue(nil))
   409  
   410  	oldVal, err = m.Del(b1)
   411  	assert.Nil(t, err)
   412  	assert.Equal(t, oldVal, b1)
   413  
   414  	assert.Equal(t, m.LoadFactor(), 0.5)
   415  
   416  	oldVal, err = m.Put(b3, b3, true)
   417  	assert.Nil(t, err)
   418  	assert.Nil(t, oldVal)
   419  	assert.Equal(t, m.LoadFactor(), 1.0)
   420  
   421  	assert.Nil(t, m.Get(b1))
   422  	assert.False(t, m.ContainsKey(b1))
   423  	assert.False(t, m.ContainsValue(b1))
   424  
   425  	assert.Equal(t, m.Get(b3), b3)
   426  	assert.True(t, m.ContainsKey(b3))
   427  	assert.True(t, m.ContainsValue(b3))
   428  
   429  	t.Log(m)
   430  }
   431  
   432  func BenchmarkMap1(b *testing.B) {
   433  	m, err := newMap(md5.Size, 16, 1, h1, h2, false, true)
   434  	if err != nil {
   435  		panic(err)
   436  	}
   437  
   438  	n := 5_000_000
   439  	keys := make([][]byte, n)
   440  	for i := 0; i < n; i++ {
   441  		keys[i] = genRandomBytes(md5.Size)
   442  	}
   443  
   444  	b.ResetTimer()
   445  	for i := 0; i < n; i++ {
   446  		oldVal, err := m.Put(keys[i], nil, true)
   447  		if err != nil {
   448  			panic(err)
   449  		} else if oldVal != nil {
   450  			panic(fmt.Sprintf("expected nil value, got %v", oldVal))
   451  		}
   452  	}
   453  }
   454  
   455  func BenchmarkMap2(b *testing.B) {
   456  	// With preset bucket count, no expansion are needed
   457  	m, err := newMap(md5.Size, 16, 524_288, h1, h2, false, true)
   458  	if err != nil {
   459  		panic(err)
   460  	}
   461  
   462  	n := 5_000_000
   463  	keys := make([][]byte, n)
   464  	for i := 0; i < n; i++ {
   465  		keys[i] = genRandomBytes(md5.Size)
   466  	}
   467  
   468  	b.ResetTimer()
   469  	for i := 0; i < n; i++ {
   470  		oldVal, err := m.Put(keys[i], nil, true)
   471  		if err != nil {
   472  			panic(err)
   473  		} else if oldVal != nil {
   474  			panic(fmt.Sprintf("expected nil value, got %v", oldVal))
   475  		}
   476  	}
   477  }