@@ -13,12 +13,13 @@ import (
13
13
// The underlying hashmap structure for Go.
14
14
type hashmap struct {
15
15
buckets unsafe.Pointer // pointer to array of buckets
16
+ seed uintptr
16
17
count uintptr
17
18
keySize uint8 // maybe this can store the key type as well? E.g. keysize == 5 means string?
18
19
valueSize uint8
19
20
bucketBits uint8
20
21
keyEqual func (x , y unsafe.Pointer , n uintptr ) bool
21
- keyHash func (key unsafe.Pointer , size uintptr ) uint32
22
+ keyHash func (key unsafe.Pointer , size , seed uintptr ) uint32
22
23
}
23
24
24
25
type hashmapAlgorithm uint8
@@ -74,6 +75,7 @@ func hashmapMake(keySize, valueSize uint8, sizeHint uintptr, alg uint8) *hashmap
74
75
75
76
return & hashmap {
76
77
buckets : buckets ,
78
+ seed : uintptr (fastrand ()),
77
79
keySize : keySize ,
78
80
valueSize : valueSize ,
79
81
bucketBits : bucketBits ,
@@ -96,7 +98,7 @@ func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintpt
96
98
}
97
99
}
98
100
99
- func hashmapKeyHashAlg (alg hashmapAlgorithm ) func (key unsafe.Pointer , n uintptr ) uint32 {
101
+ func hashmapKeyHashAlg (alg hashmapAlgorithm ) func (key unsafe.Pointer , n , seed uintptr ) uint32 {
100
102
switch alg {
101
103
case hashmapAlgorithmBinary :
102
104
return hash32
@@ -148,12 +150,14 @@ func hashmapLenUnsafePointer(p unsafe.Pointer) int {
148
150
// Set a specified key to a given value. Grow the map if necessary.
149
151
//go:nobounds
150
152
func hashmapSet (m * hashmap , key unsafe.Pointer , value unsafe.Pointer , hash uint32 ) {
151
- tophash := hashmapTopHash (hash )
152
-
153
153
if hashmapShouldGrow (m ) {
154
154
hashmapGrow (m )
155
+ // seed changed when we grew; rehash key with new seed
156
+ hash = m .keyHash (key , uintptr (m .keySize ), m .seed )
155
157
}
156
158
159
+ tophash := hashmapTopHash (hash )
160
+
157
161
numBuckets := uintptr (1 ) << m .bucketBits
158
162
bucketNumber := (uintptr (hash ) & (numBuckets - 1 ))
159
163
bucketSize := unsafe .Sizeof (hashmapBucket {}) + uintptr (m .keySize )* 8 + uintptr (m .valueSize )* 8
@@ -221,10 +225,10 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u
221
225
}
222
226
223
227
func hashmapGrow (m * hashmap ) {
224
-
225
228
// clone map as empty
226
229
n := * m
227
230
n .count = 0
231
+ n .seed = uintptr (fastrand ())
228
232
229
233
// allocate our new buckets twice as big
230
234
n .bucketBits = m .bucketBits + 1
@@ -239,7 +243,7 @@ func hashmapGrow(m *hashmap) {
239
243
var value = alloc (uintptr (m .valueSize ), nil )
240
244
241
245
for hashmapNext (m , & it , key , value ) {
242
- h := m .keyHash (key , uintptr (m .keySize ))
246
+ h := n .keyHash (key , uintptr (n .keySize ), n . seed )
243
247
hashmapSet (& n , key , value , h )
244
248
}
245
249
@@ -386,7 +390,7 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
386
390
387
391
// Our view of the buckets doesn't match the parent map.
388
392
// Look up the key in the new buckets and return that value if it exists
389
- hash := m .keyHash (key , uintptr (m .keySize ))
393
+ hash := m .keyHash (key , uintptr (m .keySize ), m . seed )
390
394
ok := hashmapGet (m , key , value , uintptr (m .valueSize ), hash )
391
395
if ! ok {
392
396
// doesn't exist in parent map; try next key
@@ -401,10 +405,9 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
401
405
}
402
406
403
407
// Hashmap with plain binary data keys (not containing strings etc.).
404
-
405
408
func hashmapBinarySet (m * hashmap , key , value unsafe.Pointer ) {
406
409
// TODO: detect nil map here and throw a better panic message?
407
- hash := hash32 (key , uintptr (m .keySize ))
410
+ hash := hash32 (key , uintptr (m .keySize ), m . seed )
408
411
hashmapSet (m , key , value , hash )
409
412
}
410
413
@@ -413,15 +416,15 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr)
413
416
memzero (value , uintptr (valueSize ))
414
417
return false
415
418
}
416
- hash := hash32 (key , uintptr (m .keySize ))
419
+ hash := hash32 (key , uintptr (m .keySize ), m . seed )
417
420
return hashmapGet (m , key , value , valueSize , hash )
418
421
}
419
422
420
423
func hashmapBinaryDelete (m * hashmap , key unsafe.Pointer ) {
421
424
if m == nil {
422
425
return
423
426
}
424
- hash := hash32 (key , uintptr (m .keySize ))
427
+ hash := hash32 (key , uintptr (m .keySize ), m . seed )
425
428
hashmapDelete (m , key , hash )
426
429
}
427
430
@@ -431,28 +434,35 @@ func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
431
434
return * (* string )(x ) == * (* string )(y )
432
435
}
433
436
434
- func hashmapStringHash (s string ) uint32 {
437
+ func hashmapStringHash (s string , seed uintptr ) uint32 {
435
438
_s := (* _string )(unsafe .Pointer (& s ))
436
- return hash32 (unsafe .Pointer (_s .ptr ), uintptr (_s .length ))
439
+ return hash32 (unsafe .Pointer (_s .ptr ), uintptr (_s .length ), seed )
437
440
}
438
441
439
- func hashmapStringPtrHash (sptr unsafe.Pointer , size uintptr ) uint32 {
442
+ func hashmapStringPtrHash (sptr unsafe.Pointer , size uintptr , seed uintptr ) uint32 {
440
443
_s := * (* _string )(sptr )
441
- return hash32 (unsafe .Pointer (_s .ptr ), uintptr (_s .length ))
444
+ return hash32 (unsafe .Pointer (_s .ptr ), uintptr (_s .length ), seed )
442
445
}
443
446
444
447
func hashmapStringSet (m * hashmap , key string , value unsafe.Pointer ) {
445
- hash := hashmapStringHash (key )
448
+ hash := hashmapStringHash (key , m . seed )
446
449
hashmapSet (m , unsafe .Pointer (& key ), value , hash )
447
450
}
448
451
449
452
func hashmapStringGet (m * hashmap , key string , value unsafe.Pointer , valueSize uintptr ) bool {
450
- hash := hashmapStringHash (key )
453
+ if m == nil {
454
+ memzero (value , uintptr (valueSize ))
455
+ return false
456
+ }
457
+ hash := hashmapStringHash (key , m .seed )
451
458
return hashmapGet (m , unsafe .Pointer (& key ), value , valueSize , hash )
452
459
}
453
460
454
461
func hashmapStringDelete (m * hashmap , key string ) {
455
- hash := hashmapStringHash (key )
462
+ if m == nil {
463
+ return
464
+ }
465
+ hash := hashmapStringHash (key , m .seed )
456
466
hashmapDelete (m , unsafe .Pointer (& key ), hash )
457
467
}
458
468
@@ -465,25 +475,25 @@ func hashmapStringDelete(m *hashmap, key string) {
465
475
//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
466
476
func valueInterfaceUnsafe (v reflect.Value ) interface {}
467
477
468
- func hashmapFloat32Hash (ptr unsafe.Pointer ) uint32 {
478
+ func hashmapFloat32Hash (ptr unsafe.Pointer , seed uintptr ) uint32 {
469
479
f := * (* uint32 )(ptr )
470
480
if f == 0x80000000 {
471
481
// convert -0 to 0 for hashing
472
482
f = 0
473
483
}
474
- return hash32 (unsafe .Pointer (& f ), 4 )
484
+ return hash32 (unsafe .Pointer (& f ), 4 , seed )
475
485
}
476
486
477
- func hashmapFloat64Hash (ptr unsafe.Pointer ) uint32 {
487
+ func hashmapFloat64Hash (ptr unsafe.Pointer , seed uintptr ) uint32 {
478
488
f := * (* uint64 )(ptr )
479
489
if f == 0x8000000000000000 {
480
490
// convert -0 to 0 for hashing
481
491
f = 0
482
492
}
483
- return hash32 (unsafe .Pointer (& f ), 8 )
493
+ return hash32 (unsafe .Pointer (& f ), 8 , seed )
484
494
}
485
495
486
- func hashmapInterfaceHash (itf interface {}) uint32 {
496
+ func hashmapInterfaceHash (itf interface {}, seed uintptr ) uint32 {
487
497
x := reflect .ValueOf (itf )
488
498
if x .RawType () == 0 {
489
499
return 0 // nil interface
@@ -498,41 +508,41 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
498
508
499
509
switch x .RawType ().Kind () {
500
510
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
501
- return hash32 (ptr , x .RawType ().Size ())
511
+ return hash32 (ptr , x .RawType ().Size (), seed )
502
512
case reflect .Bool , reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
503
- return hash32 (ptr , x .RawType ().Size ())
513
+ return hash32 (ptr , x .RawType ().Size (), seed )
504
514
case reflect .Float32 :
505
515
// It should be possible to just has the contents. However, NaN != NaN
506
516
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
507
517
// time may become exponential. To fix that, it would be better to
508
518
// return a random number instead:
509
519
// https://research.swtch.com/randhash
510
- return hashmapFloat32Hash (ptr )
520
+ return hashmapFloat32Hash (ptr , seed )
511
521
case reflect .Float64 :
512
- return hashmapFloat64Hash (ptr )
522
+ return hashmapFloat64Hash (ptr , seed )
513
523
case reflect .Complex64 :
514
524
rptr , iptr := ptr , unsafe .Pointer (uintptr (ptr )+ 4 )
515
- return hashmapFloat32Hash (rptr ) ^ hashmapFloat32Hash (iptr )
525
+ return hashmapFloat32Hash (rptr , seed ) ^ hashmapFloat32Hash (iptr , seed )
516
526
case reflect .Complex128 :
517
527
rptr , iptr := ptr , unsafe .Pointer (uintptr (ptr )+ 8 )
518
- return hashmapFloat64Hash (rptr ) ^ hashmapFloat64Hash (iptr )
528
+ return hashmapFloat64Hash (rptr , seed ) ^ hashmapFloat64Hash (iptr , seed )
519
529
case reflect .String :
520
- return hashmapStringHash (x .String ())
530
+ return hashmapStringHash (x .String (), seed )
521
531
case reflect .Chan , reflect .Ptr , reflect .UnsafePointer :
522
532
// It might seem better to just return the pointer, but that won't
523
533
// result in an evenly distributed hashmap. Instead, hash the pointer
524
534
// like most other types.
525
- return hash32 (ptr , x .RawType ().Size ())
535
+ return hash32 (ptr , x .RawType ().Size (), seed )
526
536
case reflect .Array :
527
537
var hash uint32
528
538
for i := 0 ; i < x .Len (); i ++ {
529
- hash ^= hashmapInterfaceHash (valueInterfaceUnsafe (x .Index (i )))
539
+ hash ^= hashmapInterfaceHash (valueInterfaceUnsafe (x .Index (i )), seed )
530
540
}
531
541
return hash
532
542
case reflect .Struct :
533
543
var hash uint32
534
544
for i := 0 ; i < x .NumField (); i ++ {
535
- hash ^= hashmapInterfaceHash (valueInterfaceUnsafe (x .Field (i )))
545
+ hash ^= hashmapInterfaceHash (valueInterfaceUnsafe (x .Field (i )), seed )
536
546
}
537
547
return hash
538
548
default :
@@ -541,26 +551,33 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
541
551
}
542
552
}
543
553
544
- func hashmapInterfacePtrHash (iptr unsafe.Pointer , size uintptr ) uint32 {
554
+ func hashmapInterfacePtrHash (iptr unsafe.Pointer , size uintptr , seed uintptr ) uint32 {
545
555
_i := * (* _interface )(iptr )
546
- return hashmapInterfaceHash (_i )
556
+ return hashmapInterfaceHash (_i , seed )
547
557
}
548
558
549
559
func hashmapInterfaceEqual (x , y unsafe.Pointer , n uintptr ) bool {
550
560
return * (* interface {})(x ) == * (* interface {})(y )
551
561
}
552
562
553
563
func hashmapInterfaceSet (m * hashmap , key interface {}, value unsafe.Pointer ) {
554
- hash := hashmapInterfaceHash (key )
564
+ hash := hashmapInterfaceHash (key , m . seed )
555
565
hashmapSet (m , unsafe .Pointer (& key ), value , hash )
556
566
}
557
567
558
568
func hashmapInterfaceGet (m * hashmap , key interface {}, value unsafe.Pointer , valueSize uintptr ) bool {
559
- hash := hashmapInterfaceHash (key )
569
+ if m == nil {
570
+ memzero (value , uintptr (valueSize ))
571
+ return false
572
+ }
573
+ hash := hashmapInterfaceHash (key , m .seed )
560
574
return hashmapGet (m , unsafe .Pointer (& key ), value , valueSize , hash )
561
575
}
562
576
563
577
func hashmapInterfaceDelete (m * hashmap , key interface {}) {
564
- hash := hashmapInterfaceHash (key )
578
+ if m == nil {
579
+ return
580
+ }
581
+ hash := hashmapInterfaceHash (key , m .seed )
565
582
hashmapDelete (m , unsafe .Pointer (& key ), hash )
566
583
}
0 commit comments