-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhashpool.go
More file actions
106 lines (94 loc) · 2.4 KB
/
Copy pathhashpool.go
File metadata and controls
106 lines (94 loc) · 2.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package randomizer
import (
"encoding/binary"
"hash/maphash"
"sync"
"sync/atomic"
)
// hashPool pairs a [sync.Pool] of [maphash.Hash] objects with an atomic SplitMix64
// counter used as the package's primary lock-free PRNG.
type hashPool struct {
pool sync.Pool
state atomic.Uint64
}
const splitMixGamma uint64 = 0x9e3779b97f4a7c15
// splitMix64 returns the SplitMix64 avalanche of x.
func splitMix64(x uint64) uint64 {
z := x
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9
z = (z ^ (z >> 27)) * 0x94d049bb133111eb
return z ^ (z >> 31)
}
// NewHashPool returns a new hashPool seeded by [maphash.MakeSeed].
// It returns nil if size <= 0; [sync.Pool] manages capacity automatically.
func NewHashPool(size int) *hashPool {
if size <= 0 {
return nil
}
p := &hashPool{
pool: sync.Pool{
New: func() any {
h := new(maphash.Hash)
h.SetSeed(maphash.MakeSeed())
return h
},
},
}
seed := maphash.Bytes(maphash.MakeSeed(), nil)
if seed == 0 {
seed = splitMixGamma
}
p.state.Store(seed)
return p
}
// Get retrieves a [maphash.Hash] from the pool. The caller must return it with Put.
func (p *hashPool) Get() *maphash.Hash {
if p == nil {
h := new(maphash.Hash)
h.SetSeed(maphash.MakeSeed())
return h
}
return p.pool.Get().(*maphash.Hash)
}
// Put resets h and returns the [maphash.Hash] to the pool for reuse.
func (p *hashPool) Put(h *maphash.Hash) {
if p == nil || h == nil {
return
}
h.Reset()
p.pool.Put(h)
}
// next64 returns the next random 64-bit value from the pool's SplitMix64 stream.
func (p *hashPool) next64() uint64 {
if p == nil {
return splitMix64(maphash.Bytes(maphash.MakeSeed(), nil) + splitMixGamma)
}
return splitMix64(p.state.Add(splitMixGamma))
}
// Read fills b with random bytes and returns len(b), nil.
func (p *hashPool) Read(b []byte) (n int, err error) {
if p == nil {
var state atomic.Uint64
seed := maphash.Bytes(maphash.MakeSeed(), nil)
if seed == 0 {
seed = splitMixGamma
}
state.Store(seed)
fillAtomicRandomBytes(b, &state)
return len(b), nil
}
fillAtomicRandomBytes(b, &p.state)
return len(b), nil
}
// Sum implements [Provider.Sum].
func (p *hashPool) Sum(b []byte) []byte {
return binary.LittleEndian.AppendUint64(b, p.next64())
}
// Sum32 implements [Provider.Sum32].
func (p *hashPool) Sum32() uint32 {
return uint32(p.next64() >> 32)
}
// Sum64 implements [Provider.Sum64].
func (p *hashPool) Sum64() uint64 {
return p.next64()
}