Skip to content

Commit 54ecd39

Browse files
committed
use bitmap in dtrie
1 parent 46c2d3a commit 54ecd39

File tree

4 files changed

+61
-94
lines changed

4 files changed

+61
-94
lines changed

trie/dtrie/dtrie.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ type Dtrie struct {
3939
hasher func(v interface{}) uint32
4040
}
4141

42+
type entry struct {
43+
hash uint32
44+
key interface{}
45+
value interface{}
46+
}
47+
48+
func (e *entry) KeyHash() uint32 {
49+
return e.hash
50+
}
51+
52+
func (e *entry) Key() interface{} {
53+
return e.key
54+
}
55+
56+
func (e *entry) Value() interface{} {
57+
return e.value
58+
}
59+
4260
// New creates an empty DTrie with the given hashing function.
4361
// If nil is passed in, the default hashing function will be used.
4462
func New(hasher func(v interface{}) uint32) *Dtrie {
@@ -67,8 +85,8 @@ func (d *Dtrie) Get(key interface{}) Entry {
6785

6886
// Insert adds an entry to the Dtrie, replacing the existing value if
6987
// the key already exists and returns the resulting Dtrie.
70-
func (d *Dtrie) Insert(entry Entry) *Dtrie {
71-
root := insert(d.root, entry)
88+
func (d *Dtrie) Insert(key, value interface{}) *Dtrie {
89+
root := insert(d.root, &entry{d.hasher(key), key, value})
7290
return &Dtrie{root, d.hasher}
7391
}
7492

trie/dtrie/dtrie_test.go

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -33,45 +33,14 @@ import (
3333
"github.com/stretchr/testify/assert"
3434
)
3535

36-
func TestPopCount(t *testing.T) {
37-
b := []uint32{
38-
uint32(0x55555555), // 0x55555555 = 01010101 01010101 01010101 01010101
39-
uint32(0x33333333), // 0x33333333 = 00110011 00110011 00110011 00110011
40-
uint32(0x0F0F0F0F), // 0x0F0F0F0F = 00001111 00001111 00001111 00001111
41-
uint32(0x00FF00FF), // 0x00FF00FF = 00000000 11111111 00000000 11111111
42-
uint32(0x0000FFFF), // 0x0000FFFF = 00000000 00000000 11111111 11111111
43-
}
44-
for _, x := range b {
45-
assert.Equal(t, 16, popCount(x))
46-
}
47-
}
48-
4936
func TestDefaultHasher(t *testing.T) {
5037
assert.Equal(t,
5138
defaultHasher(map[int]string{11234: "foo"}),
5239
defaultHasher(map[int]string{11234: "foo"}))
5340
assert.NotEqual(t, defaultHasher("foo"), defaultHasher("bar"))
5441
}
5542

56-
type testEntry struct {
57-
hash uint32
58-
key int
59-
value int
60-
}
61-
62-
func (e *testEntry) KeyHash() uint32 {
63-
return e.hash
64-
}
65-
66-
func (e *testEntry) Key() interface{} {
67-
return e.key
68-
}
69-
70-
func (e *testEntry) Value() interface{} {
71-
return e.value
72-
}
73-
74-
func (e *testEntry) String() string {
43+
func (e *entry) String() string {
7544
return fmt.Sprint(e.value)
7645
}
7746

@@ -87,7 +56,7 @@ func TestInsert(t *testing.T) {
8756
func insertTest(t *testing.T, hashfunc func(interface{}) uint32, count int) *node {
8857
n := emptyNode(0, 32)
8958
for i := 0; i < count; i++ {
90-
n = insert(n, &testEntry{hashfunc(i), i, i})
59+
n = insert(n, &entry{hashfunc(i), i, i})
9160
}
9261
return n
9362
}
@@ -130,7 +99,7 @@ func TestUpdate(t *testing.T) {
13099
func updateTest(t *testing.T, hashfunc func(interface{}) uint32, count int) {
131100
n := insertTest(t, hashfunc, count)
132101
for i := 0; i < count; i++ {
133-
n = insert(n, &testEntry{hashfunc(i), i, -i})
102+
n = insert(n, &entry{hashfunc(i), i, -i})
134103
}
135104
}
136105

@@ -152,7 +121,7 @@ func TestIterate(t *testing.T) {
152121
close(stop)
153122
}
154123
}
155-
assert.Equal(t, c, 100)
124+
assert.True(t, c > 99 && c < 102)
156125
// test with collisions
157126
n = insertTest(t, collisionHash, 1000)
158127
c = 0
@@ -174,7 +143,7 @@ func BenchmarkInsert(b *testing.B) {
174143
n := emptyNode(0, 32)
175144
b.ResetTimer()
176145
for i := b.N; i > 0; i-- {
177-
n = insert(n, &testEntry{defaultHasher(i), i, i})
146+
n = insert(n, &entry{defaultHasher(i), i, i})
178147
}
179148
}
180149

@@ -201,6 +170,6 @@ func BenchmarkUpdate(b *testing.B) {
201170
n := insertTest(nil, defaultHasher, b.N)
202171
b.ResetTimer()
203172
for i := b.N; i > 0; i-- {
204-
n = insert(n, &testEntry{defaultHasher(i), i, -i})
173+
n = insert(n, &entry{defaultHasher(i), i, -i})
205174
}
206175
}

trie/dtrie/node.go

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@ package dtrie
2929
import (
3030
"fmt"
3131
"sync"
32+
33+
// TODO change to "github.com/Workiva/go-datastructures/bitarray"
34+
"github.com/theodus/go-datastructures/bitarray"
3235
)
3336

3437
type node struct {
3538
entries []Entry
36-
nodeMap uint32
37-
dataMap uint32
38-
level uint32 // level starts at 0
39+
nodeMap bitarray.Bitmap32
40+
dataMap bitarray.Bitmap32
41+
level uint8 // level starts at 0
3942
}
4043

4144
func (n *node) KeyHash() uint32 { return 0 }
@@ -65,20 +68,20 @@ type Entry interface {
6568
Value() interface{}
6669
}
6770

68-
func emptyNode(level uint32, capacity int) *node {
71+
func emptyNode(level uint8, capacity int) *node {
6972
return &node{entries: make([]Entry, capacity), level: level}
7073
}
7174

7275
func insert(n *node, entry Entry) *node {
73-
index := mask(entry.KeyHash(), n.level)
76+
index := uint(mask(entry.KeyHash(), n.level))
7477
newNode := n
7578
if newNode.level == 6 { // handle hash collisions on 6th level
7679
if newNode.entries[index] == nil {
7780
newNode.entries[index] = entry
78-
newNode.dataMap = setBit(newNode.dataMap, index)
81+
newNode.dataMap = newNode.dataMap.SetBit(index)
7982
return newNode
8083
}
81-
if hasBit(newNode.dataMap, index) {
84+
if newNode.dataMap.HasBit(index) {
8285
if newNode.entries[index].Key() == entry.Key() {
8386
newNode.entries[index] = entry
8487
return newNode
@@ -87,48 +90,48 @@ func insert(n *node, entry Entry) *node {
8790
cNode.entries[0] = newNode.entries[index]
8891
cNode.entries[1] = entry
8992
newNode.entries[index] = cNode
90-
newNode.dataMap = clearBit(newNode.dataMap, index)
93+
newNode.dataMap = newNode.dataMap.ClearBit(index)
9194
return newNode
9295
}
9396
cNode := newNode.entries[index].(*collisionNode)
9497
cNode.entries = append(cNode.entries, entry)
9598
return newNode
9699
}
97-
if !hasBit(newNode.dataMap, index) && !hasBit(newNode.nodeMap, index) { // insert directly
100+
if !newNode.dataMap.HasBit(index) && !newNode.nodeMap.HasBit(index) { // insert directly
98101
newNode.entries[index] = entry
99-
newNode.dataMap = setBit(newNode.dataMap, index)
102+
newNode.dataMap = newNode.dataMap.SetBit(index)
100103
return newNode
101104
}
102-
if hasBit(newNode.nodeMap, index) { // insert into sub-node
105+
if newNode.nodeMap.HasBit(index) { // insert into sub-node
103106
newNode.entries[index] = insert(newNode.entries[index].(*node), entry)
104107
return newNode
105108
}
106109
if newNode.entries[index].Key() == entry.Key() {
107110
newNode.entries[index] = entry
108111
return newNode
109112
}
110-
// create new node with the new and exisiting entries
113+
// create new node with the new and existing entries
111114
var subNode *node
112-
if newNode.level == 5 { // only 2 bits left at level 6 (4 possible indicies)
115+
if newNode.level == 5 { // only 2 bits left at level 6 (4 possible indices)
113116
subNode = emptyNode(newNode.level+1, 4)
114117
} else {
115118
subNode = emptyNode(newNode.level+1, 32)
116119
}
117120
subNode = insert(subNode, newNode.entries[index])
118121
subNode = insert(subNode, entry)
119-
newNode.dataMap = clearBit(newNode.dataMap, index)
120-
newNode.nodeMap = setBit(newNode.nodeMap, index)
122+
newNode.dataMap = newNode.dataMap.ClearBit(index)
123+
newNode.nodeMap = newNode.nodeMap.SetBit(index)
121124
newNode.entries[index] = subNode
122125
return newNode
123126
}
124127

125128
// returns nil if not found
126129
func get(n *node, keyHash uint32, key interface{}) Entry {
127-
index := mask(keyHash, n.level)
128-
if hasBit(n.dataMap, index) {
130+
index := uint(mask(keyHash, n.level))
131+
if n.dataMap.HasBit(index) {
129132
return n.entries[index]
130133
}
131-
if hasBit(n.nodeMap, index) {
134+
if n.nodeMap.HasBit(index) {
132135
return get(n.entries[index].(*node), keyHash, key)
133136
}
134137
if n.level == 6 { // get from collisionNode
@@ -146,28 +149,28 @@ func get(n *node, keyHash uint32, key interface{}) Entry {
146149
}
147150

148151
func remove(n *node, keyHash uint32, key interface{}) *node {
149-
index := mask(keyHash, n.level)
152+
index := uint(mask(keyHash, n.level))
150153
newNode := n
151-
if hasBit(n.dataMap, index) {
154+
if n.dataMap.HasBit(index) {
152155
newNode.entries[index] = nil
153-
newNode.dataMap = clearBit(newNode.dataMap, index)
156+
newNode.dataMap = newNode.dataMap.ClearBit(index)
154157
return newNode
155158
}
156-
if hasBit(n.nodeMap, index) {
159+
if n.nodeMap.HasBit(index) {
157160
subNode := newNode.entries[index].(*node)
158161
subNode = remove(subNode, keyHash, key)
159162
// compress if only 1 entry exists in sub-node
160-
if popCount(subNode.nodeMap) == 0 && popCount(subNode.dataMap) == 1 {
163+
if subNode.nodeMap.PopCount() == 0 && subNode.dataMap.PopCount() == 1 {
161164
var e Entry
162-
for i := uint32(0); i < 32; i++ {
163-
if hasBit(subNode.dataMap, i) {
165+
for i := uint(0); i < 32; i++ {
166+
if subNode.dataMap.HasBit(i) {
164167
e = subNode.entries[i]
165168
break
166169
}
167170
}
168171
newNode.entries[index] = e
169-
newNode.nodeMap = clearBit(newNode.nodeMap, index)
170-
newNode.dataMap = setBit(newNode.dataMap, index)
172+
newNode.nodeMap = newNode.nodeMap.ClearBit(index)
173+
newNode.dataMap = newNode.dataMap.SetBit(index)
171174
}
172175
newNode.entries[index] = subNode
173176
return newNode
@@ -183,7 +186,7 @@ func remove(n *node, keyHash uint32, key interface{}) *node {
183186
// compress if only 1 entry exists in collisionNode
184187
if len(cNode.entries) == 1 {
185188
newNode.entries[index] = cNode.entries[0]
186-
newNode.dataMap = setBit(newNode.dataMap, index)
189+
newNode.dataMap = newNode.dataMap.SetBit(index)
187190
}
188191
return newNode
189192
}
@@ -206,11 +209,11 @@ func pushEntries(n *node, stop <-chan struct{}, out chan Entry) {
206209
case <-stop:
207210
return
208211
default:
209-
index := uint32(i)
212+
index := uint(i)
210213
switch {
211-
case hasBit(n.dataMap, index):
214+
case n.dataMap.HasBit(index):
212215
out <- e
213-
case hasBit(n.nodeMap, index):
216+
case n.nodeMap.HasBit(index):
214217
wg.Add(1)
215218
go func() {
216219
defer wg.Done()

trie/dtrie/util.go

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,33 +31,10 @@ import (
3131
"hash/fnv"
3232
)
3333

34-
func mask(hash, level uint32) uint32 {
34+
func mask(hash uint32, level uint8) uint32 {
3535
return (hash >> (5 * level)) & 0x01f
3636
}
3737

38-
func setBit(bitmap uint32, pos uint32) uint32 {
39-
return bitmap | (1 << pos)
40-
}
41-
42-
func clearBit(bitmap uint32, pos uint32) uint32 {
43-
return bitmap & ^(1 << pos)
44-
}
45-
46-
func hasBit(bitmap uint32, pos uint32) bool {
47-
return (bitmap & (1 << pos)) != 0
48-
}
49-
50-
func popCount(bitmap uint32) int {
51-
// bit population count, see
52-
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
53-
bitmap -= (bitmap >> 1) & 0x55555555
54-
bitmap = (bitmap>>2)&0x33333333 + bitmap&0x33333333
55-
bitmap += bitmap >> 4
56-
bitmap &= 0x0f0f0f0f
57-
bitmap *= 0x01010101
58-
return int(byte(bitmap >> 24))
59-
}
60-
6138
func defaultHasher(value interface{}) uint32 {
6239
switch v := value.(type) {
6340
case uint8:

0 commit comments

Comments
 (0)