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 }