github.com/puzpuzpuz/xsync/v2@v2.5.2-0.20231021165734-92b8269e19a9/mapof_test.go (about)

     1  //go:build go1.18
     2  // +build go1.18
     3  
     4  package xsync_test
     5  
     6  import (
     7  	"encoding/binary"
     8  	"hash/maphash"
     9  	"math"
    10  	"math/rand"
    11  	"strconv"
    12  	"sync"
    13  	"sync/atomic"
    14  	"testing"
    15  	"time"
    16  	"unsafe"
    17  
    18  	. "github.com/puzpuzpuz/xsync/v2"
    19  )
    20  
    21  type point struct {
    22  	x int32
    23  	y int32
    24  }
    25  
    26  func pointHash(seed maphash.Seed, p point) uint64 {
    27  	var h maphash.Hash
    28  	h.SetSeed(seed)
    29  	binary.Write(&h, binary.LittleEndian, p.x)
    30  	hash := h.Sum64()
    31  	h.Reset()
    32  	binary.Write(&h, binary.LittleEndian, p.y)
    33  	return 31*hash + h.Sum64()
    34  }
    35  
    36  func TestMap_BucketOfStructSize(t *testing.T) {
    37  	size := unsafe.Sizeof(BucketOfPadded{})
    38  	if size != 64 {
    39  		t.Fatalf("size of 64B (one cache line) is expected, got: %d", size)
    40  	}
    41  }
    42  
    43  func TestMapOf_MissingEntry(t *testing.T) {
    44  	m := NewMapOf[string]()
    45  	v, ok := m.Load("foo")
    46  	if ok {
    47  		t.Fatalf("value was not expected: %v", v)
    48  	}
    49  	if deleted, loaded := m.LoadAndDelete("foo"); loaded {
    50  		t.Fatalf("value was not expected %v", deleted)
    51  	}
    52  	if actual, loaded := m.LoadOrStore("foo", "bar"); loaded {
    53  		t.Fatalf("value was not expected %v", actual)
    54  	}
    55  }
    56  
    57  func TestMapOf_EmptyStringKey(t *testing.T) {
    58  	m := NewMapOf[string]()
    59  	m.Store("", "foobar")
    60  	v, ok := m.Load("")
    61  	if !ok {
    62  		t.Fatal("value was expected")
    63  	}
    64  	if v != "foobar" {
    65  		t.Fatalf("value does not match: %v", v)
    66  	}
    67  }
    68  
    69  func TestMapOfStore_NilValue(t *testing.T) {
    70  	m := NewMapOf[*struct{}]()
    71  	m.Store("foo", nil)
    72  	v, ok := m.Load("foo")
    73  	if !ok {
    74  		t.Fatal("nil value was expected")
    75  	}
    76  	if v != nil {
    77  		t.Fatalf("value was not nil: %v", v)
    78  	}
    79  }
    80  
    81  func TestMapOfLoadOrStore_NilValue(t *testing.T) {
    82  	m := NewMapOf[*struct{}]()
    83  	m.LoadOrStore("foo", nil)
    84  	v, loaded := m.LoadOrStore("foo", nil)
    85  	if !loaded {
    86  		t.Fatal("nil value was expected")
    87  	}
    88  	if v != nil {
    89  		t.Fatalf("value was not nil: %v", v)
    90  	}
    91  }
    92  
    93  func TestMapOfLoadOrStore_NonNilValue(t *testing.T) {
    94  	type foo struct{}
    95  	m := NewMapOf[*foo]()
    96  	newv := &foo{}
    97  	v, loaded := m.LoadOrStore("foo", newv)
    98  	if loaded {
    99  		t.Fatal("no value was expected")
   100  	}
   101  	if v != newv {
   102  		t.Fatalf("value does not match: %v", v)
   103  	}
   104  	newv2 := &foo{}
   105  	v, loaded = m.LoadOrStore("foo", newv2)
   106  	if !loaded {
   107  		t.Fatal("value was expected")
   108  	}
   109  	if v != newv {
   110  		t.Fatalf("value does not match: %v", v)
   111  	}
   112  }
   113  
   114  func TestMapOfLoadAndStore_NilValue(t *testing.T) {
   115  	m := NewMapOf[*struct{}]()
   116  	m.LoadAndStore("foo", nil)
   117  	v, loaded := m.LoadAndStore("foo", nil)
   118  	if !loaded {
   119  		t.Fatal("nil value was expected")
   120  	}
   121  	if v != nil {
   122  		t.Fatalf("value was not nil: %v", v)
   123  	}
   124  	v, loaded = m.Load("foo")
   125  	if !loaded {
   126  		t.Fatal("nil value was expected")
   127  	}
   128  	if v != nil {
   129  		t.Fatalf("value was not nil: %v", v)
   130  	}
   131  }
   132  
   133  func TestMapOfLoadAndStore_NonNilValue(t *testing.T) {
   134  	m := NewMapOf[int]()
   135  	v1 := 1
   136  	v, loaded := m.LoadAndStore("foo", v1)
   137  	if loaded {
   138  		t.Fatal("no value was expected")
   139  	}
   140  	if v != v1 {
   141  		t.Fatalf("value does not match: %v", v)
   142  	}
   143  	v2 := 2
   144  	v, loaded = m.LoadAndStore("foo", v2)
   145  	if !loaded {
   146  		t.Fatal("value was expected")
   147  	}
   148  	if v != v1 {
   149  		t.Fatalf("value does not match: %v", v)
   150  	}
   151  	v, loaded = m.Load("foo")
   152  	if !loaded {
   153  		t.Fatal("value was expected")
   154  	}
   155  	if v != v2 {
   156  		t.Fatalf("value does not match: %v", v)
   157  	}
   158  }
   159  
   160  func TestMapOfRange(t *testing.T) {
   161  	const numEntries = 1000
   162  	m := NewMapOf[int]()
   163  	for i := 0; i < numEntries; i++ {
   164  		m.Store(strconv.Itoa(i), i)
   165  	}
   166  	iters := 0
   167  	met := make(map[string]int)
   168  	m.Range(func(key string, value int) bool {
   169  		if key != strconv.Itoa(value) {
   170  			t.Fatalf("got unexpected key/value for iteration %d: %v/%v", iters, key, value)
   171  			return false
   172  		}
   173  		met[key] += 1
   174  		iters++
   175  		return true
   176  	})
   177  	if iters != numEntries {
   178  		t.Fatalf("got unexpected number of iterations: %d", iters)
   179  	}
   180  	for i := 0; i < numEntries; i++ {
   181  		if c := met[strconv.Itoa(i)]; c != 1 {
   182  			t.Fatalf("range did not iterate correctly over %d: %d", i, c)
   183  		}
   184  	}
   185  }
   186  
   187  func TestMapOfRange_FalseReturned(t *testing.T) {
   188  	m := NewMapOf[int]()
   189  	for i := 0; i < 100; i++ {
   190  		m.Store(strconv.Itoa(i), i)
   191  	}
   192  	iters := 0
   193  	m.Range(func(key string, value int) bool {
   194  		iters++
   195  		return iters != 13
   196  	})
   197  	if iters != 13 {
   198  		t.Fatalf("got unexpected number of iterations: %d", iters)
   199  	}
   200  }
   201  
   202  func TestMapOfRange_NestedDelete(t *testing.T) {
   203  	const numEntries = 256
   204  	m := NewMapOf[int]()
   205  	for i := 0; i < numEntries; i++ {
   206  		m.Store(strconv.Itoa(i), i)
   207  	}
   208  	m.Range(func(key string, value int) bool {
   209  		m.Delete(key)
   210  		return true
   211  	})
   212  	for i := 0; i < numEntries; i++ {
   213  		if _, ok := m.Load(strconv.Itoa(i)); ok {
   214  			t.Fatalf("value found for %d", i)
   215  		}
   216  	}
   217  }
   218  
   219  func TestMapOfStore(t *testing.T) {
   220  	const numEntries = 128
   221  	m := NewMapOf[int]()
   222  	for i := 0; i < numEntries; i++ {
   223  		m.Store(strconv.Itoa(i), i)
   224  	}
   225  	for i := 0; i < numEntries; i++ {
   226  		v, ok := m.Load(strconv.Itoa(i))
   227  		if !ok {
   228  			t.Fatalf("value not found for %d", i)
   229  		}
   230  		if v != i {
   231  			t.Fatalf("values do not match for %d: %v", i, v)
   232  		}
   233  	}
   234  }
   235  
   236  func TestIntegerMapOfStore(t *testing.T) {
   237  	const numEntries = 128
   238  	m := NewIntegerMapOf[int, int]()
   239  	for i := 0; i < numEntries; i++ {
   240  		m.Store(i, i)
   241  	}
   242  	for i := 0; i < numEntries; i++ {
   243  		v, ok := m.Load(i)
   244  		if !ok {
   245  			t.Fatalf("value not found for %d", i)
   246  		}
   247  		if v != i {
   248  			t.Fatalf("values do not match for %d: %v", i, v)
   249  		}
   250  	}
   251  }
   252  
   253  func TestTypedMapOfStore_StructKeys_IntValues(t *testing.T) {
   254  	const numEntries = 128
   255  	m := NewTypedMapOf[point, int](pointHash)
   256  	for i := 0; i < numEntries; i++ {
   257  		m.Store(point{int32(i), -int32(i)}, i)
   258  	}
   259  	for i := 0; i < numEntries; i++ {
   260  		v, ok := m.Load(point{int32(i), -int32(i)})
   261  		if !ok {
   262  			t.Fatalf("value not found for %d", i)
   263  		}
   264  		if v != i {
   265  			t.Fatalf("values do not match for %d: %v", i, v)
   266  		}
   267  	}
   268  }
   269  
   270  func TestTypedMapOfStore_StructKeys_StructValues(t *testing.T) {
   271  	const numEntries = 128
   272  	m := NewTypedMapOf[point, point](pointHash)
   273  	for i := 0; i < numEntries; i++ {
   274  		m.Store(point{int32(i), -int32(i)}, point{-int32(i), int32(i)})
   275  	}
   276  	for i := 0; i < numEntries; i++ {
   277  		v, ok := m.Load(point{int32(i), -int32(i)})
   278  		if !ok {
   279  			t.Fatalf("value not found for %d", i)
   280  		}
   281  		if v.x != -int32(i) {
   282  			t.Fatalf("x value does not match for %d: %v", i, v)
   283  		}
   284  		if v.y != int32(i) {
   285  			t.Fatalf("y value does not match for %d: %v", i, v)
   286  		}
   287  	}
   288  }
   289  
   290  func TestTypedMapOfStore_HashCodeCollisions(t *testing.T) {
   291  	const numEntries = 1000
   292  	m := NewTypedMapOf[int, int](func(_ maphash.Seed, i int) uint64 {
   293  		// We intentionally use an awful hash function here to make sure
   294  		// that the map copes with key collisions.
   295  		return 42
   296  	})
   297  	for i := 0; i < numEntries; i++ {
   298  		m.Store(i, i)
   299  	}
   300  	for i := 0; i < numEntries; i++ {
   301  		v, ok := m.Load(i)
   302  		if !ok {
   303  			t.Fatalf("value not found for %d", i)
   304  		}
   305  		if v != i {
   306  			t.Fatalf("values do not match for %d: %v", i, v)
   307  		}
   308  	}
   309  }
   310  
   311  func TestMapOfLoadOrStore(t *testing.T) {
   312  	const numEntries = 1000
   313  	m := NewMapOf[int]()
   314  	for i := 0; i < numEntries; i++ {
   315  		m.Store(strconv.Itoa(i), i)
   316  	}
   317  	for i := 0; i < numEntries; i++ {
   318  		if _, loaded := m.LoadOrStore(strconv.Itoa(i), i); !loaded {
   319  			t.Fatalf("value not found for %d", i)
   320  		}
   321  	}
   322  }
   323  
   324  func TestMapOfLoadOrCompute(t *testing.T) {
   325  	const numEntries = 1000
   326  	m := NewMapOf[int]()
   327  	for i := 0; i < numEntries; i++ {
   328  		v, loaded := m.LoadOrCompute(strconv.Itoa(i), func() int {
   329  			return i
   330  		})
   331  		if loaded {
   332  			t.Fatalf("value not computed for %d", i)
   333  		}
   334  		if v != i {
   335  			t.Fatalf("values do not match for %d: %v", i, v)
   336  		}
   337  	}
   338  	for i := 0; i < numEntries; i++ {
   339  		v, loaded := m.LoadOrCompute(strconv.Itoa(i), func() int {
   340  			return i
   341  		})
   342  		if !loaded {
   343  			t.Fatalf("value not loaded for %d", i)
   344  		}
   345  		if v != i {
   346  			t.Fatalf("values do not match for %d: %v", i, v)
   347  		}
   348  	}
   349  }
   350  
   351  func TestMapOfLoadOrCompute_FunctionCalledOnce(t *testing.T) {
   352  	m := NewIntegerMapOf[int, int]()
   353  	for i := 0; i < 100; {
   354  		m.LoadOrCompute(i, func() (v int) {
   355  			v, i = i, i+1
   356  			return v
   357  		})
   358  	}
   359  	m.Range(func(k, v int) bool {
   360  		if k != v {
   361  			t.Fatalf("%dth key is not equal to value %d", k, v)
   362  		}
   363  		return true
   364  	})
   365  }
   366  
   367  func TestMapOfCompute(t *testing.T) {
   368  	m := NewMapOf[int]()
   369  	// Store a new value.
   370  	v, ok := m.Compute("foobar", func(oldValue int, loaded bool) (newValue int, delete bool) {
   371  		if oldValue != 0 {
   372  			t.Fatalf("oldValue should be 0 when computing a new value: %d", oldValue)
   373  		}
   374  		if loaded {
   375  			t.Fatal("loaded should be false when computing a new value")
   376  		}
   377  		newValue = 42
   378  		delete = false
   379  		return
   380  	})
   381  	if v != 42 {
   382  		t.Fatalf("v should be 42 when computing a new value: %d", v)
   383  	}
   384  	if !ok {
   385  		t.Fatal("ok should be true when computing a new value")
   386  	}
   387  	// Update an existing value.
   388  	v, ok = m.Compute("foobar", func(oldValue int, loaded bool) (newValue int, delete bool) {
   389  		if oldValue != 42 {
   390  			t.Fatalf("oldValue should be 42 when updating the value: %d", oldValue)
   391  		}
   392  		if !loaded {
   393  			t.Fatal("loaded should be true when updating the value")
   394  		}
   395  		newValue = oldValue + 42
   396  		delete = false
   397  		return
   398  	})
   399  	if v != 84 {
   400  		t.Fatalf("v should be 84 when updating the value: %d", v)
   401  	}
   402  	if !ok {
   403  		t.Fatal("ok should be true when updating the value")
   404  	}
   405  	// Delete an existing value.
   406  	v, ok = m.Compute("foobar", func(oldValue int, loaded bool) (newValue int, delete bool) {
   407  		if oldValue != 84 {
   408  			t.Fatalf("oldValue should be 84 when deleting the value: %d", oldValue)
   409  		}
   410  		if !loaded {
   411  			t.Fatal("loaded should be true when deleting the value")
   412  		}
   413  		delete = true
   414  		return
   415  	})
   416  	if v != 84 {
   417  		t.Fatalf("v should be 84 when deleting the value: %d", v)
   418  	}
   419  	if ok {
   420  		t.Fatal("ok should be false when deleting the value")
   421  	}
   422  	// Try to delete a non-existing value. Notice different key.
   423  	v, ok = m.Compute("barbaz", func(oldValue int, loaded bool) (newValue int, delete bool) {
   424  		if oldValue != 0 {
   425  			t.Fatalf("oldValue should be 0 when trying to delete a non-existing value: %d", oldValue)
   426  		}
   427  		if loaded {
   428  			t.Fatal("loaded should be false when trying to delete a non-existing value")
   429  		}
   430  		// We're returning a non-zero value, but the map should ignore it.
   431  		newValue = 42
   432  		delete = true
   433  		return
   434  	})
   435  	if v != 0 {
   436  		t.Fatalf("v should be 0 when trying to delete a non-existing value: %d", v)
   437  	}
   438  	if ok {
   439  		t.Fatal("ok should be false when trying to delete a non-existing value")
   440  	}
   441  }
   442  
   443  func TestMapOfStoreThenDelete(t *testing.T) {
   444  	const numEntries = 1000
   445  	m := NewMapOf[int]()
   446  	for i := 0; i < numEntries; i++ {
   447  		m.Store(strconv.Itoa(i), i)
   448  	}
   449  	for i := 0; i < numEntries; i++ {
   450  		m.Delete(strconv.Itoa(i))
   451  		if _, ok := m.Load(strconv.Itoa(i)); ok {
   452  			t.Fatalf("value was not expected for %d", i)
   453  		}
   454  	}
   455  }
   456  
   457  func TestIntegerMapOfStoreThenDelete(t *testing.T) {
   458  	const numEntries = 1000
   459  	m := NewIntegerMapOf[int32, int32]()
   460  	for i := 0; i < numEntries; i++ {
   461  		m.Store(int32(i), int32(i))
   462  	}
   463  	for i := 0; i < numEntries; i++ {
   464  		m.Delete(int32(i))
   465  		if _, ok := m.Load(int32(i)); ok {
   466  			t.Fatalf("value was not expected for %d", i)
   467  		}
   468  	}
   469  }
   470  
   471  func TestTypedMapOfStoreThenDelete(t *testing.T) {
   472  	const numEntries = 1000
   473  	m := NewTypedMapOf[point, string](pointHash)
   474  	for i := 0; i < numEntries; i++ {
   475  		m.Store(point{int32(i), 42}, strconv.Itoa(i))
   476  	}
   477  	for i := 0; i < numEntries; i++ {
   478  		m.Delete(point{int32(i), 42})
   479  		if _, ok := m.Load(point{int32(i), 42}); ok {
   480  			t.Fatalf("value was not expected for %d", i)
   481  		}
   482  	}
   483  }
   484  
   485  func TestMapOfStoreThenLoadAndDelete(t *testing.T) {
   486  	const numEntries = 1000
   487  	m := NewMapOf[int]()
   488  	for i := 0; i < numEntries; i++ {
   489  		m.Store(strconv.Itoa(i), i)
   490  	}
   491  	for i := 0; i < numEntries; i++ {
   492  		if v, loaded := m.LoadAndDelete(strconv.Itoa(i)); !loaded || v != i {
   493  			t.Fatalf("value was not found or different for %d: %v", i, v)
   494  		}
   495  		if _, ok := m.Load(strconv.Itoa(i)); ok {
   496  			t.Fatalf("value was not expected for %d", i)
   497  		}
   498  	}
   499  }
   500  
   501  func TestIntegerMapOfStoreThenLoadAndDelete(t *testing.T) {
   502  	const numEntries = 1000
   503  	m := NewIntegerMapOf[int, int]()
   504  	for i := 0; i < numEntries; i++ {
   505  		m.Store(i, i)
   506  	}
   507  	for i := 0; i < numEntries; i++ {
   508  		if _, loaded := m.LoadAndDelete(i); !loaded {
   509  			t.Fatalf("value was not found for %d", i)
   510  		}
   511  		if _, ok := m.Load(i); ok {
   512  			t.Fatalf("value was not expected for %d", i)
   513  		}
   514  	}
   515  }
   516  
   517  func TestTypedMapOfStoreThenLoadAndDelete(t *testing.T) {
   518  	const numEntries = 1000
   519  	m := NewTypedMapOf[point, int](pointHash)
   520  	for i := 0; i < numEntries; i++ {
   521  		m.Store(point{42, int32(i)}, i)
   522  	}
   523  	for i := 0; i < numEntries; i++ {
   524  		if _, loaded := m.LoadAndDelete(point{42, int32(i)}); !loaded {
   525  			t.Fatalf("value was not found for %d", i)
   526  		}
   527  		if _, ok := m.Load(point{42, int32(i)}); ok {
   528  			t.Fatalf("value was not expected for %d", i)
   529  		}
   530  	}
   531  }
   532  
   533  func sizeBasedOnTypedRange(m *MapOf[string, int]) int {
   534  	size := 0
   535  	m.Range(func(key string, value int) bool {
   536  		size++
   537  		return true
   538  	})
   539  	return size
   540  }
   541  
   542  func TestMapOfSize(t *testing.T) {
   543  	const numEntries = 1000
   544  	m := NewMapOf[int]()
   545  	size := m.Size()
   546  	if size != 0 {
   547  		t.Fatalf("zero size expected: %d", size)
   548  	}
   549  	expectedSize := 0
   550  	for i := 0; i < numEntries; i++ {
   551  		m.Store(strconv.Itoa(i), i)
   552  		expectedSize++
   553  		size := m.Size()
   554  		if size != expectedSize {
   555  			t.Fatalf("size of %d was expected, got: %d", expectedSize, size)
   556  		}
   557  		rsize := sizeBasedOnTypedRange(m)
   558  		if size != rsize {
   559  			t.Fatalf("size does not match number of entries in Range: %v, %v", size, rsize)
   560  		}
   561  	}
   562  	for i := 0; i < numEntries; i++ {
   563  		m.Delete(strconv.Itoa(i))
   564  		expectedSize--
   565  		size := m.Size()
   566  		if size != expectedSize {
   567  			t.Fatalf("size of %d was expected, got: %d", expectedSize, size)
   568  		}
   569  		rsize := sizeBasedOnTypedRange(m)
   570  		if size != rsize {
   571  			t.Fatalf("size does not match number of entries in Range: %v, %v", size, rsize)
   572  		}
   573  	}
   574  }
   575  
   576  func TestMapOfClear(t *testing.T) {
   577  	const numEntries = 1000
   578  	m := NewMapOf[int]()
   579  	for i := 0; i < numEntries; i++ {
   580  		m.Store(strconv.Itoa(i), i)
   581  	}
   582  	size := m.Size()
   583  	if size != numEntries {
   584  		t.Fatalf("size of %d was expected, got: %d", numEntries, size)
   585  	}
   586  	m.Clear()
   587  	size = m.Size()
   588  	if size != 0 {
   589  		t.Fatalf("zero size was expected, got: %d", size)
   590  	}
   591  	rsize := sizeBasedOnTypedRange(m)
   592  	if rsize != 0 {
   593  		t.Fatalf("zero number of entries in Range was expected, got: %d", rsize)
   594  	}
   595  }
   596  
   597  func assertMapOfCapacity[K comparable, V any](t *testing.T, m *MapOf[K, V], expectedCap int) {
   598  	stats := CollectMapOfStats(m)
   599  	if stats.Capacity != expectedCap {
   600  		t.Fatalf("capacity was different from %d: %d", expectedCap, stats.Capacity)
   601  	}
   602  }
   603  
   604  func TestNewMapOfPresized(t *testing.T) {
   605  	assertMapOfCapacity(t, NewMapOf[string](), MinMapTableCap)
   606  	assertMapOfCapacity(t, NewMapOfPresized[string](0), MinMapTableCap)
   607  	assertMapOfCapacity(t, NewMapOfPresized[string](-100), MinMapTableCap)
   608  	assertMapOfCapacity(t, NewMapOfPresized[string](500), 768)
   609  
   610  	assertMapOfCapacity(t, NewIntegerMapOf[int, int](), MinMapTableCap)
   611  	assertMapOfCapacity(t, NewIntegerMapOfPresized[int, int](0), MinMapTableCap)
   612  	assertMapOfCapacity(t, NewIntegerMapOfPresized[int, int](-1), MinMapTableCap)
   613  	assertMapOfCapacity(t, NewIntegerMapOfPresized[int, int](1_000_000), 1_572_864)
   614  
   615  	assertMapOfCapacity(t, NewTypedMapOf[point, point](pointHash), MinMapTableCap)
   616  	assertMapOfCapacity(t, NewTypedMapOfPresized[point, point](pointHash, 0), MinMapTableCap)
   617  	assertMapOfCapacity(t, NewTypedMapOfPresized[point, point](pointHash, -42), MinMapTableCap)
   618  	assertMapOfCapacity(t, NewTypedMapOfPresized[point, point](pointHash, 100), 192)
   619  }
   620  
   621  func TestMapOfResize(t *testing.T) {
   622  	const numEntries = 100_000
   623  	m := NewMapOf[int]()
   624  
   625  	for i := 0; i < numEntries; i++ {
   626  		m.Store(strconv.Itoa(i), i)
   627  	}
   628  	stats := CollectMapOfStats(m)
   629  	if stats.Size != numEntries {
   630  		t.Fatalf("size was too small: %d", stats.Size)
   631  	}
   632  	expectedCapacity := int(math.RoundToEven(MapLoadFactor+1)) * stats.RootBuckets * EntriesPerMapBucket
   633  	if stats.Capacity > expectedCapacity {
   634  		t.Fatalf("capacity was too large: %d, expected: %d", stats.Capacity, expectedCapacity)
   635  	}
   636  	if stats.RootBuckets <= MinMapTableLen {
   637  		t.Fatalf("table was too small: %d", stats.RootBuckets)
   638  	}
   639  	if stats.TotalGrowths == 0 {
   640  		t.Fatalf("non-zero total growths expected: %d", stats.TotalGrowths)
   641  	}
   642  	if stats.TotalShrinks > 0 {
   643  		t.Fatalf("zero total shrinks expected: %d", stats.TotalShrinks)
   644  	}
   645  	// This is useful when debugging table resize and occupancy.
   646  	// Use -v flag to see the output.
   647  	t.Log(stats.ToString())
   648  
   649  	for i := 0; i < numEntries; i++ {
   650  		m.Delete(strconv.Itoa(i))
   651  	}
   652  	stats = CollectMapOfStats(m)
   653  	if stats.Size > 0 {
   654  		t.Fatalf("zero size was expected: %d", stats.Size)
   655  	}
   656  	expectedCapacity = stats.RootBuckets * EntriesPerMapBucket
   657  	if stats.Capacity != expectedCapacity {
   658  		t.Fatalf("capacity was too large: %d, expected: %d", stats.Capacity, expectedCapacity)
   659  	}
   660  	if stats.RootBuckets != MinMapTableLen {
   661  		t.Fatalf("table was too large: %d", stats.RootBuckets)
   662  	}
   663  	if stats.TotalShrinks == 0 {
   664  		t.Fatalf("non-zero total shrinks expected: %d", stats.TotalShrinks)
   665  	}
   666  	t.Log(stats.ToString())
   667  }
   668  
   669  func TestMapOfResize_CounterLenLimit(t *testing.T) {
   670  	const numEntries = 1_000_000
   671  	m := NewMapOf[string]()
   672  
   673  	for i := 0; i < numEntries; i++ {
   674  		m.Store("foo"+strconv.Itoa(i), "bar"+strconv.Itoa(i))
   675  	}
   676  	stats := CollectMapOfStats(m)
   677  	if stats.Size != numEntries {
   678  		t.Fatalf("size was too small: %d", stats.Size)
   679  	}
   680  	if stats.CounterLen != MaxMapCounterLen {
   681  		t.Fatalf("number of counter stripes was too large: %d, expected: %d",
   682  			stats.CounterLen, MaxMapCounterLen)
   683  	}
   684  }
   685  
   686  func parallelSeqTypedResizer(t *testing.T, m *MapOf[int, int], numEntries int, positive bool, cdone chan bool) {
   687  	for i := 0; i < numEntries; i++ {
   688  		if positive {
   689  			m.Store(i, i)
   690  		} else {
   691  			m.Store(-i, -i)
   692  		}
   693  	}
   694  	cdone <- true
   695  }
   696  
   697  func TestMapOfParallelResize_GrowOnly(t *testing.T) {
   698  	const numEntries = 100_000
   699  	m := NewIntegerMapOf[int, int]()
   700  	cdone := make(chan bool)
   701  	go parallelSeqTypedResizer(t, m, numEntries, true, cdone)
   702  	go parallelSeqTypedResizer(t, m, numEntries, false, cdone)
   703  	// Wait for the goroutines to finish.
   704  	<-cdone
   705  	<-cdone
   706  	// Verify map contents.
   707  	for i := -numEntries + 1; i < numEntries; i++ {
   708  		v, ok := m.Load(i)
   709  		if !ok {
   710  			t.Fatalf("value not found for %d", i)
   711  		}
   712  		if v != i {
   713  			t.Fatalf("values do not match for %d: %v", i, v)
   714  		}
   715  	}
   716  	if s := m.Size(); s != 2*numEntries-1 {
   717  		t.Fatalf("unexpected size: %v", s)
   718  	}
   719  }
   720  
   721  func parallelRandTypedResizer(t *testing.T, m *MapOf[string, int], numIters, numEntries int, cdone chan bool) {
   722  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   723  	for i := 0; i < numIters; i++ {
   724  		coin := r.Int63n(2)
   725  		for j := 0; j < numEntries; j++ {
   726  			if coin == 1 {
   727  				m.Store(strconv.Itoa(j), j)
   728  			} else {
   729  				m.Delete(strconv.Itoa(j))
   730  			}
   731  		}
   732  	}
   733  	cdone <- true
   734  }
   735  
   736  func TestMapOfParallelResize(t *testing.T) {
   737  	const numIters = 1_000
   738  	const numEntries = 2 * EntriesPerMapBucket * MinMapTableLen
   739  	m := NewMapOf[int]()
   740  	cdone := make(chan bool)
   741  	go parallelRandTypedResizer(t, m, numIters, numEntries, cdone)
   742  	go parallelRandTypedResizer(t, m, numIters, numEntries, cdone)
   743  	// Wait for the goroutines to finish.
   744  	<-cdone
   745  	<-cdone
   746  	// Verify map contents.
   747  	for i := 0; i < numEntries; i++ {
   748  		v, ok := m.Load(strconv.Itoa(i))
   749  		if !ok {
   750  			// The entry may be deleted and that's ok.
   751  			continue
   752  		}
   753  		if v != i {
   754  			t.Fatalf("values do not match for %d: %v", i, v)
   755  		}
   756  	}
   757  	s := m.Size()
   758  	if s > numEntries {
   759  		t.Fatalf("unexpected size: %v", s)
   760  	}
   761  	rs := sizeBasedOnTypedRange(m)
   762  	if s != rs {
   763  		t.Fatalf("size does not match number of entries in Range: %v, %v", s, rs)
   764  	}
   765  }
   766  
   767  func parallelRandTypedClearer(t *testing.T, m *MapOf[string, int], numIters, numEntries int, cdone chan bool) {
   768  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   769  	for i := 0; i < numIters; i++ {
   770  		coin := r.Int63n(2)
   771  		for j := 0; j < numEntries; j++ {
   772  			if coin == 1 {
   773  				m.Store(strconv.Itoa(j), j)
   774  			} else {
   775  				m.Clear()
   776  			}
   777  		}
   778  	}
   779  	cdone <- true
   780  }
   781  
   782  func TestMapOfParallelClear(t *testing.T) {
   783  	const numIters = 100
   784  	const numEntries = 1_000
   785  	m := NewMapOf[int]()
   786  	cdone := make(chan bool)
   787  	go parallelRandTypedClearer(t, m, numIters, numEntries, cdone)
   788  	go parallelRandTypedClearer(t, m, numIters, numEntries, cdone)
   789  	// Wait for the goroutines to finish.
   790  	<-cdone
   791  	<-cdone
   792  	// Verify map size.
   793  	s := m.Size()
   794  	if s > numEntries {
   795  		t.Fatalf("unexpected size: %v", s)
   796  	}
   797  	rs := sizeBasedOnTypedRange(m)
   798  	if s != rs {
   799  		t.Fatalf("size does not match number of entries in Range: %v, %v", s, rs)
   800  	}
   801  }
   802  
   803  func parallelSeqTypedStorer(t *testing.T, m *MapOf[string, int], storeEach, numIters, numEntries int, cdone chan bool) {
   804  	for i := 0; i < numIters; i++ {
   805  		for j := 0; j < numEntries; j++ {
   806  			if storeEach == 0 || j%storeEach == 0 {
   807  				m.Store(strconv.Itoa(j), j)
   808  				// Due to atomic snapshots we must see a "<j>"/j pair.
   809  				v, ok := m.Load(strconv.Itoa(j))
   810  				if !ok {
   811  					t.Errorf("value was not found for %d", j)
   812  					break
   813  				}
   814  				if v != j {
   815  					t.Errorf("value was not expected for %d: %d", j, v)
   816  					break
   817  				}
   818  			}
   819  		}
   820  	}
   821  	cdone <- true
   822  }
   823  
   824  func TestMapOfParallelStores(t *testing.T) {
   825  	const numStorers = 4
   826  	const numIters = 10_000
   827  	const numEntries = 100
   828  	m := NewMapOf[int]()
   829  	cdone := make(chan bool)
   830  	for i := 0; i < numStorers; i++ {
   831  		go parallelSeqTypedStorer(t, m, i, numIters, numEntries, cdone)
   832  	}
   833  	// Wait for the goroutines to finish.
   834  	for i := 0; i < numStorers; i++ {
   835  		<-cdone
   836  	}
   837  	// Verify map contents.
   838  	for i := 0; i < numEntries; i++ {
   839  		v, ok := m.Load(strconv.Itoa(i))
   840  		if !ok {
   841  			t.Fatalf("value not found for %d", i)
   842  		}
   843  		if v != i {
   844  			t.Fatalf("values do not match for %d: %v", i, v)
   845  		}
   846  	}
   847  }
   848  
   849  func parallelRandTypedStorer(t *testing.T, m *MapOf[string, int], numIters, numEntries int, cdone chan bool) {
   850  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   851  	for i := 0; i < numIters; i++ {
   852  		j := r.Intn(numEntries)
   853  		if v, loaded := m.LoadOrStore(strconv.Itoa(j), j); loaded {
   854  			if v != j {
   855  				t.Errorf("value was not expected for %d: %d", j, v)
   856  			}
   857  		}
   858  	}
   859  	cdone <- true
   860  }
   861  
   862  func parallelRandTypedDeleter(t *testing.T, m *MapOf[string, int], numIters, numEntries int, cdone chan bool) {
   863  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   864  	for i := 0; i < numIters; i++ {
   865  		j := r.Intn(numEntries)
   866  		if v, loaded := m.LoadAndDelete(strconv.Itoa(j)); loaded {
   867  			if v != j {
   868  				t.Errorf("value was not expected for %d: %d", j, v)
   869  			}
   870  		}
   871  	}
   872  	cdone <- true
   873  }
   874  
   875  func parallelTypedLoader(t *testing.T, m *MapOf[string, int], numIters, numEntries int, cdone chan bool) {
   876  	for i := 0; i < numIters; i++ {
   877  		for j := 0; j < numEntries; j++ {
   878  			// Due to atomic snapshots we must either see no entry, or a "<j>"/j pair.
   879  			if v, ok := m.Load(strconv.Itoa(j)); ok {
   880  				if v != j {
   881  					t.Errorf("value was not expected for %d: %d", j, v)
   882  				}
   883  			}
   884  		}
   885  	}
   886  	cdone <- true
   887  }
   888  
   889  func TestMapOfAtomicSnapshot(t *testing.T) {
   890  	const numIters = 100_000
   891  	const numEntries = 100
   892  	m := NewMapOf[int]()
   893  	cdone := make(chan bool)
   894  	// Update or delete random entry in parallel with loads.
   895  	go parallelRandTypedStorer(t, m, numIters, numEntries, cdone)
   896  	go parallelRandTypedDeleter(t, m, numIters, numEntries, cdone)
   897  	go parallelTypedLoader(t, m, numIters, numEntries, cdone)
   898  	// Wait for the goroutines to finish.
   899  	for i := 0; i < 3; i++ {
   900  		<-cdone
   901  	}
   902  }
   903  
   904  func TestMapOfParallelStoresAndDeletes(t *testing.T) {
   905  	const numWorkers = 2
   906  	const numIters = 100_000
   907  	const numEntries = 1000
   908  	m := NewMapOf[int]()
   909  	cdone := make(chan bool)
   910  	// Update random entry in parallel with deletes.
   911  	for i := 0; i < numWorkers; i++ {
   912  		go parallelRandTypedStorer(t, m, numIters, numEntries, cdone)
   913  		go parallelRandTypedDeleter(t, m, numIters, numEntries, cdone)
   914  	}
   915  	// Wait for the goroutines to finish.
   916  	for i := 0; i < 2*numWorkers; i++ {
   917  		<-cdone
   918  	}
   919  }
   920  
   921  func parallelTypedComputer(t *testing.T, m *MapOf[uint64, uint64], numIters, numEntries int, cdone chan bool) {
   922  	for i := 0; i < numIters; i++ {
   923  		for j := 0; j < numEntries; j++ {
   924  			m.Compute(uint64(j), func(oldValue uint64, loaded bool) (newValue uint64, delete bool) {
   925  				return oldValue + 1, false
   926  			})
   927  		}
   928  	}
   929  	cdone <- true
   930  }
   931  
   932  func TestMapOfParallelComputes(t *testing.T) {
   933  	const numWorkers = 4 // Also stands for numEntries.
   934  	const numIters = 10_000
   935  	m := NewIntegerMapOf[uint64, uint64]()
   936  	cdone := make(chan bool)
   937  	for i := 0; i < numWorkers; i++ {
   938  		go parallelTypedComputer(t, m, numIters, numWorkers, cdone)
   939  	}
   940  	// Wait for the goroutines to finish.
   941  	for i := 0; i < numWorkers; i++ {
   942  		<-cdone
   943  	}
   944  	// Verify map contents.
   945  	for i := 0; i < numWorkers; i++ {
   946  		v, ok := m.Load(uint64(i))
   947  		if !ok {
   948  			t.Fatalf("value not found for %d", i)
   949  		}
   950  		if v != numWorkers*numIters {
   951  			t.Fatalf("values do not match for %d: %v", i, v)
   952  		}
   953  	}
   954  }
   955  
   956  func parallelTypedRangeStorer(t *testing.T, m *MapOf[int, int], numEntries int, stopFlag *int64, cdone chan bool) {
   957  	for {
   958  		for i := 0; i < numEntries; i++ {
   959  			m.Store(i, i)
   960  		}
   961  		if atomic.LoadInt64(stopFlag) != 0 {
   962  			break
   963  		}
   964  	}
   965  	cdone <- true
   966  }
   967  
   968  func parallelTypedRangeDeleter(t *testing.T, m *MapOf[int, int], numEntries int, stopFlag *int64, cdone chan bool) {
   969  	for {
   970  		for i := 0; i < numEntries; i++ {
   971  			m.Delete(i)
   972  		}
   973  		if atomic.LoadInt64(stopFlag) != 0 {
   974  			break
   975  		}
   976  	}
   977  	cdone <- true
   978  }
   979  
   980  func TestMapOfParallelRange(t *testing.T) {
   981  	const numEntries = 10_000
   982  	m := NewIntegerMapOfPresized[int, int](numEntries)
   983  	for i := 0; i < numEntries; i++ {
   984  		m.Store(i, i)
   985  	}
   986  	// Start goroutines that would be storing and deleting items in parallel.
   987  	cdone := make(chan bool)
   988  	stopFlag := int64(0)
   989  	go parallelTypedRangeStorer(t, m, numEntries, &stopFlag, cdone)
   990  	go parallelTypedRangeDeleter(t, m, numEntries, &stopFlag, cdone)
   991  	// Iterate the map and verify that no duplicate keys were met.
   992  	met := make(map[int]int)
   993  	m.Range(func(key int, value int) bool {
   994  		if key != value {
   995  			t.Fatalf("got unexpected value for key %d: %d", key, value)
   996  			return false
   997  		}
   998  		met[key] += 1
   999  		return true
  1000  	})
  1001  	if len(met) == 0 {
  1002  		t.Fatal("no entries were met when iterating")
  1003  	}
  1004  	for k, c := range met {
  1005  		if c != 1 {
  1006  			t.Fatalf("met key %d multiple times: %d", k, c)
  1007  		}
  1008  	}
  1009  	// Make sure that both goroutines finish.
  1010  	atomic.StoreInt64(&stopFlag, 1)
  1011  	<-cdone
  1012  	<-cdone
  1013  }
  1014  
  1015  func BenchmarkMapOf_NoWarmUp(b *testing.B) {
  1016  	for _, bc := range benchmarkCases {
  1017  		if bc.readPercentage == 100 {
  1018  			// This benchmark doesn't make sense without a warm-up.
  1019  			continue
  1020  		}
  1021  		b.Run(bc.name, func(b *testing.B) {
  1022  			m := NewMapOf[int]()
  1023  			benchmarkMapOfStringKeys(b, func(k string) (int, bool) {
  1024  				return m.Load(k)
  1025  			}, func(k string, v int) {
  1026  				m.Store(k, v)
  1027  			}, func(k string) {
  1028  				m.Delete(k)
  1029  			}, bc.readPercentage)
  1030  		})
  1031  	}
  1032  }
  1033  
  1034  func BenchmarkMapOf_WarmUp(b *testing.B) {
  1035  	for _, bc := range benchmarkCases {
  1036  		b.Run(bc.name, func(b *testing.B) {
  1037  			m := NewMapOfPresized[int](benchmarkNumEntries)
  1038  			for i := 0; i < benchmarkNumEntries; i++ {
  1039  				m.Store(benchmarkKeyPrefix+strconv.Itoa(i), i)
  1040  			}
  1041  			b.ResetTimer()
  1042  			benchmarkMapOfStringKeys(b, func(k string) (int, bool) {
  1043  				return m.Load(k)
  1044  			}, func(k string, v int) {
  1045  				m.Store(k, v)
  1046  			}, func(k string) {
  1047  				m.Delete(k)
  1048  			}, bc.readPercentage)
  1049  		})
  1050  	}
  1051  }
  1052  
  1053  func benchmarkMapOfStringKeys(
  1054  	b *testing.B,
  1055  	loadFn func(k string) (int, bool),
  1056  	storeFn func(k string, v int),
  1057  	deleteFn func(k string),
  1058  	readPercentage int,
  1059  ) {
  1060  	runParallel(b, func(pb *testing.PB) {
  1061  		// convert percent to permille to support 99% case
  1062  		storeThreshold := 10 * readPercentage
  1063  		deleteThreshold := 10*readPercentage + ((1000 - 10*readPercentage) / 2)
  1064  		for pb.Next() {
  1065  			op := int(Fastrand() % 1000)
  1066  			i := int(Fastrand() % benchmarkNumEntries)
  1067  			if op >= deleteThreshold {
  1068  				deleteFn(benchmarkKeys[i])
  1069  			} else if op >= storeThreshold {
  1070  				storeFn(benchmarkKeys[i], i)
  1071  			} else {
  1072  				loadFn(benchmarkKeys[i])
  1073  			}
  1074  		}
  1075  	})
  1076  }
  1077  
  1078  func BenchmarkIntegerMapOf_NoWarmUp(b *testing.B) {
  1079  	for _, bc := range benchmarkCases {
  1080  		if bc.readPercentage == 100 {
  1081  			// This benchmark doesn't make sense without a warm-up.
  1082  			continue
  1083  		}
  1084  		b.Run(bc.name, func(b *testing.B) {
  1085  			m := NewIntegerMapOf[int, int]()
  1086  			benchmarkMapOfIntegerKeys(b, func(k int) (int, bool) {
  1087  				return m.Load(k)
  1088  			}, func(k int, v int) {
  1089  				m.Store(k, v)
  1090  			}, func(k int) {
  1091  				m.Delete(k)
  1092  			}, bc.readPercentage)
  1093  		})
  1094  	}
  1095  }
  1096  
  1097  func BenchmarkIntegerMapOf_WarmUp(b *testing.B) {
  1098  	for _, bc := range benchmarkCases {
  1099  		b.Run(bc.name, func(b *testing.B) {
  1100  			m := NewIntegerMapOfPresized[int, int](benchmarkNumEntries)
  1101  			for i := 0; i < benchmarkNumEntries; i++ {
  1102  				m.Store(i, i)
  1103  			}
  1104  			b.ResetTimer()
  1105  			benchmarkMapOfIntegerKeys(b, func(k int) (int, bool) {
  1106  				return m.Load(k)
  1107  			}, func(k int, v int) {
  1108  				m.Store(k, v)
  1109  			}, func(k int) {
  1110  				m.Delete(k)
  1111  			}, bc.readPercentage)
  1112  		})
  1113  	}
  1114  }
  1115  
  1116  func BenchmarkIntegerMapStandard_NoWarmUp(b *testing.B) {
  1117  	for _, bc := range benchmarkCases {
  1118  		if bc.readPercentage == 100 {
  1119  			// This benchmark doesn't make sense without a warm-up.
  1120  			continue
  1121  		}
  1122  		b.Run(bc.name, func(b *testing.B) {
  1123  			var m sync.Map
  1124  			benchmarkMapOfIntegerKeys(b, func(k int) (value int, ok bool) {
  1125  				v, ok := m.Load(k)
  1126  				if ok {
  1127  					return v.(int), ok
  1128  				} else {
  1129  					return 0, false
  1130  				}
  1131  			}, func(k int, v int) {
  1132  				m.Store(k, v)
  1133  			}, func(k int) {
  1134  				m.Delete(k)
  1135  			}, bc.readPercentage)
  1136  		})
  1137  	}
  1138  }
  1139  
  1140  // This is a nice scenario for sync.Map since a lot of updates
  1141  // will hit the readOnly part of the map.
  1142  func BenchmarkIntegerMapStandard_WarmUp(b *testing.B) {
  1143  	for _, bc := range benchmarkCases {
  1144  		b.Run(bc.name, func(b *testing.B) {
  1145  			var m sync.Map
  1146  			for i := 0; i < benchmarkNumEntries; i++ {
  1147  				m.Store(i, i)
  1148  			}
  1149  			b.ResetTimer()
  1150  			benchmarkMapOfIntegerKeys(b, func(k int) (value int, ok bool) {
  1151  				v, ok := m.Load(k)
  1152  				if ok {
  1153  					return v.(int), ok
  1154  				} else {
  1155  					return 0, false
  1156  				}
  1157  			}, func(k int, v int) {
  1158  				m.Store(k, v)
  1159  			}, func(k int) {
  1160  				m.Delete(k)
  1161  			}, bc.readPercentage)
  1162  		})
  1163  	}
  1164  }
  1165  
  1166  func benchmarkMapOfIntegerKeys(
  1167  	b *testing.B,
  1168  	loadFn func(k int) (int, bool),
  1169  	storeFn func(k int, v int),
  1170  	deleteFn func(k int),
  1171  	readPercentage int,
  1172  ) {
  1173  	runParallel(b, func(pb *testing.PB) {
  1174  		// convert percent to permille to support 99% case
  1175  		storeThreshold := 10 * readPercentage
  1176  		deleteThreshold := 10*readPercentage + ((1000 - 10*readPercentage) / 2)
  1177  		for pb.Next() {
  1178  			op := int(Fastrand() % 1000)
  1179  			i := int(Fastrand() % benchmarkNumEntries)
  1180  			if op >= deleteThreshold {
  1181  				deleteFn(i)
  1182  			} else if op >= storeThreshold {
  1183  				storeFn(i, i)
  1184  			} else {
  1185  				loadFn(i)
  1186  			}
  1187  		}
  1188  	})
  1189  }
  1190  
  1191  func BenchmarkMapOfRange(b *testing.B) {
  1192  	m := NewMapOfPresized[int](benchmarkNumEntries)
  1193  	for i := 0; i < benchmarkNumEntries; i++ {
  1194  		m.Store(benchmarkKeys[i], i)
  1195  	}
  1196  	b.ResetTimer()
  1197  	runParallel(b, func(pb *testing.PB) {
  1198  		foo := 0
  1199  		for pb.Next() {
  1200  			m.Range(func(key string, value int) bool {
  1201  				foo++
  1202  				return true
  1203  			})
  1204  			_ = foo
  1205  		}
  1206  	})
  1207  }