Skip to content

Commit d17aa97

Browse files
committed
use OneCount to decide uint size, add regression tests
Signed-off-by: jsign <[email protected]>
1 parent a63b80c commit d17aa97

File tree

2 files changed

+63
-22
lines changed

2 files changed

+63
-22
lines changed

uhamt.go

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,20 @@ import (
88
// indexForBitPos returns the index within the collapsed array corresponding to
99
// the given bit in the bitset. The collapsed array contains only one entry
1010
// per bit set in the bitfield, and this function is used to map the indices.
11-
func (n *Node) indexForBitPosOld(bp int) int {
12-
// TODO: an optimization could reuse the same 'mask' here and change the size
13-
// as needed. This isnt yet done as the bitset package doesnt make it easy
14-
// to do.
15-
16-
// make a bitmask (all bits set) 'bp' bits long
17-
mask := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bp)), nil), big.NewInt(1))
18-
mask.And(mask, n.Bitfield)
19-
20-
return popCount(mask)
21-
}
22-
23-
func popCount(i *big.Int) int {
24-
var n int
25-
for _, v := range i.Bits() {
26-
n += bits.OnesCount64(uint64(v))
27-
}
28-
return n
11+
func (n *Node) indexForBitPos(bp int) int {
12+
return indexForBitPos(bp, n.Bitfield)
2913
}
3014

31-
func (n *Node) indexForBitPos(bp int) int {
15+
func indexForBitPos(bp int, bitfield *big.Int) int {
3216
var x uint
3317
var count, i int
34-
w := n.Bitfield.Bits()
18+
w := bitfield.Bits()
3519
for x = uint(bp); x > bits.UintSize && i < len(w); x -= bits.UintSize {
36-
count += bits.OnesCount64(uint64(w[i]))
20+
count += bits.OnesCount(uint(w[i]))
3721
i++
3822
}
3923
if i == len(w) {
4024
return count
4125
}
42-
return count + bits.OnesCount64(uint64(w[i])&((1<<x)-1))
26+
return count + bits.OnesCount(uint(w[i])&((1<<x)-1))
4327
}

uhamt_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package hamt
2+
3+
import (
4+
"math/big"
5+
"math/bits"
6+
"math/rand"
7+
"testing"
8+
)
9+
10+
func TestIndexForBitRandom(t *testing.T) {
11+
t.Parallel()
12+
r := rand.New(rand.NewSource(int64(42)))
13+
14+
count := 100000
15+
slot := make([]byte, 32)
16+
for i := 0; i < count; i++ {
17+
_, err := r.Read(slot)
18+
if err != nil {
19+
t.Fatal("couldn't create random bitfield")
20+
}
21+
bi := big.NewInt(0).SetBytes(slot)
22+
for k := 0; k < 256; k++ {
23+
if indexForBitPos(k, bi) != indexForBitPosOriginal(k, bi) {
24+
t.Fatalf("indexForBit doesn't match with original")
25+
}
26+
}
27+
}
28+
}
29+
30+
func TestIndexForBitLinear(t *testing.T) {
31+
t.Parallel()
32+
var i int64
33+
for i = 0; i < 1<<16-1; i++ {
34+
bi := big.NewInt(i)
35+
for k := 0; k < 16; k++ {
36+
if indexForBitPos(k, bi) != indexForBitPosOriginal(k, bi) {
37+
t.Fatalf("indexForBit doesn't match with original")
38+
}
39+
}
40+
}
41+
}
42+
43+
// Original implementation of indexForBit, before #39.
44+
func indexForBitPosOriginal(bp int, bitfield *big.Int) int {
45+
mask := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bp)), nil), big.NewInt(1))
46+
mask.And(mask, bitfield)
47+
48+
return popCount(mask)
49+
}
50+
51+
func popCount(i *big.Int) int {
52+
var n int
53+
for _, v := range i.Bits() {
54+
n += bits.OnesCount64(uint64(v))
55+
}
56+
return n
57+
}

0 commit comments

Comments
 (0)