@@ -29,11 +29,11 @@ import (
2929 "bytes"
3030 "hash"
3131 "hash/fnv"
32- "sync"
3332 "sync/atomic"
3433 "unsafe"
3534
3635 "github.com/Workiva/go-datastructures/list"
36+ "github.com/Workiva/go-datastructures/queue"
3737)
3838
3939const (
@@ -42,14 +42,23 @@ const (
4242
4343 // exp2 is 2^w, which is the hashcode space.
4444 exp2 = 32
45+
46+ // hasherPoolSize is the number of hashers to buffer.
47+ hasherPoolSize = 16
4548)
4649
50+ // HashFactory returns a new Hash32 used to hash keys.
51+ type HashFactory func () hash.Hash32
52+
53+ func defaultHashFactory () hash.Hash32 {
54+ return fnv .New32a ()
55+ }
56+
4757// Ctrie is a concurrent, lock-free hash trie. By default, keys are hashed
48- // using FNV-1a, but the hashing function used can be set with SetHash .
58+ // using FNV-1a unless a HashFactory is provided to New .
4959type Ctrie struct {
50- root * iNode
51- h hash.Hash32
52- hMu sync.Mutex
60+ root * iNode
61+ hasherPool * queue.RingBuffer
5362}
5463
5564// iNode is an indirection node. I-nodes remain present in the Ctrie even as
@@ -215,20 +224,19 @@ type sNode struct {
215224 * entry
216225}
217226
218- // New creates an empty Ctrie, defaulting to FNV-1a for key hashing. Use
219- // SetHash to change the hash function.
220- func New () * Ctrie {
227+ // New creates an empty Ctrie which uses the provided HashFactory for key
228+ // hashing. If nil is passed in, it will default to FNV-1a hashing.
229+ func New (hashFactory HashFactory ) * Ctrie {
230+ if hashFactory == nil {
231+ hashFactory = defaultHashFactory
232+ }
221233 root := & iNode {main : & mainNode {cNode : & cNode {}}}
222- return & Ctrie {root : root , h : fnv .New32a ()}
223- }
234+ hasherPool := queue .NewRingBuffer (hasherPoolSize )
235+ for i := 0 ; i < hasherPoolSize ; i ++ {
236+ hasherPool .Put (hashFactory ())
237+ }
224238
225- // SetHash sets the hash function used by the Ctrie. Existing entries are not
226- // rehashed when this is set, so this should be called on a newly created
227- // Ctrie.
228- func (c * Ctrie ) SetHash (hash hash.Hash32 ) {
229- c .hMu .Lock ()
230- c .h = hash
231- c .hMu .Unlock ()
239+ return & Ctrie {root : root , hasherPool : hasherPool }
232240}
233241
234242// Insert adds the key-value pair to the Ctrie, replacing the existing value if
@@ -282,11 +290,12 @@ func (c *Ctrie) remove(entry *entry) (interface{}, bool) {
282290}
283291
284292func (c * Ctrie ) hash (k []byte ) uint32 {
285- c .hMu .Lock ()
286- c .h .Write (k )
287- hash := c .h .Sum32 ()
288- c .h .Reset ()
289- c .hMu .Unlock ()
293+ hasher , _ := c .hasherPool .Get ()
294+ h := hasher .(hash.Hash32 )
295+ h .Write (k )
296+ hash := h .Sum32 ()
297+ h .Reset ()
298+ c .hasherPool .Put (h )
290299 return hash
291300}
292301
0 commit comments