Skip to content

Commit 632259d

Browse files
authored
Merge pull request #3 from f1monkey/feature/bitmap-32
Add 32-bit bitmap version
2 parents bd818e3 + 3a85aef commit 632259d

5 files changed

Lines changed: 348 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,9 @@ func main() {
3939
b3.Set(100)
4040
b3.String() // "2|68719476736"
4141
b4, err := bitmap.FromString("2|68719476736")
42+
43+
// Bitmap32 is backed by []uint32 slice
44+
// Everything else is all the same
45+
var b32 bitmap.Bitmap32
4246
}
4347
```

bitmap_32.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package bitmap
2+
3+
import (
4+
"math/bits"
5+
"strconv"
6+
"strings"
7+
)
8+
9+
type Bitmap32 []uint32
10+
11+
// Set set n-th bit to 1
12+
func (b *Bitmap32) Set(n uint32) {
13+
block, bit := n>>5, n%32
14+
b.grow(block)
15+
(*b)[block] |= (1 << bit)
16+
}
17+
18+
// Remove set n-th bit to 0
19+
func (b *Bitmap32) Remove(n uint32) {
20+
block, bit := n>>5, n%32
21+
if uint32(len(*b)) <= block {
22+
return
23+
}
24+
(*b)[block] &= (0 << bit)
25+
}
26+
27+
// Xor invert n-th bit
28+
func (b *Bitmap32) Xor(n uint32) {
29+
block, val := n>>5, n%32
30+
b.grow(block)
31+
(*b)[block] ^= (1 << val)
32+
}
33+
34+
// IsEmpty check if the bitmap has any bit set to 1
35+
func (b *Bitmap32) IsEmpty() bool {
36+
for i := range *b {
37+
if (*b)[i] > 0 {
38+
return false
39+
}
40+
}
41+
42+
return true
43+
}
44+
45+
// Has check if n-th bit is set to 1
46+
func (b *Bitmap32) Has(n uint32) bool {
47+
block, val := n>>5, n%32
48+
if uint32(len(*b)) <= block {
49+
return false
50+
}
51+
52+
return (*b)[block]&(1<<val) > 0
53+
}
54+
55+
// CountDiff count different bits in two bitmaps
56+
func (b *Bitmap32) CountDiff(b2 Bitmap32) int {
57+
diff := 0
58+
max := len(*b)
59+
if len(b2) > max {
60+
max = len(b2)
61+
}
62+
63+
for i := 0; i < max; i++ {
64+
if len(b2) <= i {
65+
diff += bits.OnesCount32((*b)[i])
66+
continue
67+
}
68+
if len(*b) <= i {
69+
diff += bits.OnesCount32((b2)[i])
70+
continue
71+
}
72+
73+
diff += bits.OnesCount32((*b)[i] ^ b2[i])
74+
}
75+
76+
return diff
77+
}
78+
79+
// Clone create a copy of the bitmap
80+
func (b *Bitmap32) Clone() Bitmap32 {
81+
clone := make(Bitmap32, len(*b))
82+
copy(clone, *b)
83+
84+
return clone
85+
}
86+
87+
// Range call the passed callback with all bits set to 1.
88+
// If the callback returns false, the method exits
89+
func (b *Bitmap32) Range(f func(n uint32) bool) {
90+
for i, block := range *b {
91+
for block != 0 {
92+
tz := bits.TrailingZeros32(block)
93+
bitIndex := uint32(i*64 + tz)
94+
95+
if !f(bitIndex) {
96+
return
97+
}
98+
99+
block &= block - 1
100+
}
101+
}
102+
}
103+
104+
func (b *Bitmap32) String() string {
105+
var sb strings.Builder
106+
107+
for i := range *b {
108+
sb.WriteString(strconv.FormatUint(uint64((*b)[i]), 10))
109+
if i != len(*b)-1 {
110+
sb.WriteString("|")
111+
}
112+
}
113+
114+
return sb.String()
115+
}
116+
117+
func FromString32(str string) (Bitmap32, error) {
118+
if str == "" {
119+
return Bitmap32{}, nil
120+
}
121+
122+
nums := strings.Split(str, "|")
123+
result := make(Bitmap32, 0, len(nums))
124+
for _, num := range nums {
125+
v, err := strconv.ParseUint(num, 10, 32)
126+
if err != nil {
127+
return nil, err
128+
}
129+
result = append(result, uint32(v))
130+
}
131+
return result, nil
132+
}
133+
134+
func (b *Bitmap32) grow(length uint32) {
135+
if length+1 > uint32(len(*b)) {
136+
*b = append(*b, make(Bitmap32, length+1-uint32(len(*b)))...)
137+
}
138+
}

bitmap_32_test.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package bitmap
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func Benchmark_Bitmap32_Set(b *testing.B) {
10+
var bm Bitmap32
11+
for i := 0; i < b.N; i++ {
12+
bm.Set(1)
13+
}
14+
}
15+
16+
func Test_Bitmap32_Set(t *testing.T) {
17+
t.Run("must set the specified bit", func(t *testing.T) {
18+
t.Run("0", func(t *testing.T) {
19+
var b Bitmap32
20+
b.Set(0)
21+
assert.Equal(t, Bitmap32{1}, b)
22+
})
23+
t.Run("2", func(t *testing.T) {
24+
var b Bitmap32
25+
b.Set(2)
26+
assert.Equal(t, Bitmap32{4}, b)
27+
})
28+
t.Run("0,2", func(t *testing.T) {
29+
var b Bitmap32
30+
b.Set(0)
31+
b.Set(2)
32+
assert.Equal(t, Bitmap32{5}, b)
33+
})
34+
35+
t.Run("31", func(t *testing.T) {
36+
var b Bitmap32
37+
b.Set(31)
38+
assert.Equal(t, Bitmap32{2147483648}, b)
39+
})
40+
41+
t.Run("32", func(t *testing.T) {
42+
var b Bitmap32
43+
b.Set(32)
44+
assert.Equal(t, Bitmap32{0, 1}, b)
45+
})
46+
})
47+
t.Run("must do nothing if the specified bit is already == 1", func(t *testing.T) {
48+
var b Bitmap32
49+
b.Set(0)
50+
b.Set(0)
51+
assert.Equal(t, Bitmap32{1}, b)
52+
})
53+
}
54+
55+
func Test_Bitmap32_Remove(t *testing.T) {
56+
var b Bitmap32
57+
58+
b.Remove(0)
59+
assert.Nil(t, b)
60+
61+
b.Set(0)
62+
b.Set(100)
63+
b.Remove(100)
64+
assert.Equal(t, Bitmap32{1, 0, 0, 0}, b)
65+
}
66+
67+
func Benchmark_Bitmap32_Xor(b *testing.B) {
68+
var bm Bitmap32
69+
for i := 0; i < b.N; i++ {
70+
bm.Xor(1)
71+
}
72+
}
73+
74+
func Test_Bitmap32_Xor(t *testing.T) {
75+
t.Run("must invert the specified bit", func(t *testing.T) {
76+
var b Bitmap32
77+
b.Xor(0)
78+
assert.Equal(t, Bitmap32{1}, b)
79+
b.Xor(0)
80+
assert.Equal(t, Bitmap32{0}, b)
81+
})
82+
}
83+
84+
func Test_Bitmap32_IsEmpty(t *testing.T) {
85+
t.Run("must return true if it's empty", func(t *testing.T) {
86+
var b Bitmap32
87+
assert.True(t, b.IsEmpty())
88+
b.Xor(1)
89+
b.Xor(1)
90+
assert.True(t, b.IsEmpty())
91+
})
92+
t.Run("must return false if it is not empty", func(t *testing.T) {
93+
var b Bitmap32
94+
b.Set(1)
95+
assert.False(t, b.IsEmpty())
96+
})
97+
}
98+
99+
func Test_Bitmap32_Has(t *testing.T) {
100+
var b Bitmap32
101+
assert.False(t, b.Has(0))
102+
b.Xor(0)
103+
assert.True(t, b.Has(0))
104+
b.Xor(0)
105+
assert.False(t, b.Has(0))
106+
}
107+
108+
func Benchmark_Bitmap32_CountDiff(b *testing.B) {
109+
var b1, b2 Bitmap32
110+
b1.Set(1)
111+
b1.Set(2)
112+
b2.Set(31)
113+
for i := 0; i < b.N; i++ {
114+
b1.CountDiff(b2)
115+
}
116+
}
117+
118+
func Test_Bitmap32_CountDiff(t *testing.T) {
119+
t.Run("must return 0 if bitmaps are equal", func(t *testing.T) {
120+
var b1, b2 Bitmap32
121+
b1.Set(1)
122+
b2.Set(1)
123+
assert.Equal(t, 0, b1.CountDiff(b2))
124+
})
125+
126+
t.Run("must return correct count of different bits", func(t *testing.T) {
127+
var b1, b2 Bitmap32
128+
b1.Set(1)
129+
b1.Set(2)
130+
b1.Set(64)
131+
132+
b2.Set(31)
133+
b2.Set(64)
134+
b2.Set(650)
135+
136+
assert.Equal(t, 4, b1.CountDiff(b2))
137+
})
138+
}
139+
140+
func Test_Bitmap32_Clone(t *testing.T) {
141+
var b1 Bitmap32
142+
b1.Set(0)
143+
b2 := b1.Clone()
144+
145+
assert.Equal(t, b1, b2)
146+
147+
b2.Set(2)
148+
assert.Equal(t, Bitmap32{5}, b2)
149+
assert.Equal(t, Bitmap32{1}, b1)
150+
}
151+
152+
func Test_Bitmap32_Range(t *testing.T) {
153+
var b1 Bitmap32
154+
b1.Set(0)
155+
b1.Set(1)
156+
b1.Set(2)
157+
b1.Set(1000)
158+
b1.Set(10000)
159+
160+
var items []uint32
161+
b1.Range(func(n uint32) bool {
162+
items = append(items, n)
163+
if n == 1000 {
164+
return false
165+
}
166+
167+
return true
168+
})
169+
170+
assert.Equal(t, []uint32{0, 1, 2, 1992, 19984}, items)
171+
}
172+
173+
func Benchmark_Bitmap32_String(b *testing.B) {
174+
bm := Bitmap32{0, 5, 1000}
175+
for i := 0; i < b.N; i++ {
176+
_ = bm.String()
177+
}
178+
}
179+
180+
func Test_Bitmap32_String(t *testing.T) {
181+
b := Bitmap32{}
182+
assert.Equal(t, "", b.String())
183+
184+
b = Bitmap32{0, 5, 100}
185+
assert.Equal(t, "0|5|100", b.String())
186+
}
187+
188+
func Test_FromString32(t *testing.T) {
189+
t.Run("must return error if unable to parse the string", func(t *testing.T) {
190+
_, err := FromString32("qwe")
191+
assert.Error(t, err)
192+
})
193+
t.Run("must parse the string correctly", func(t *testing.T) {
194+
v, err := FromString32("")
195+
assert.Nil(t, err)
196+
assert.Equal(t, Bitmap32{}, v)
197+
198+
v, err = FromString32("0")
199+
assert.Nil(t, err)
200+
assert.Equal(t, Bitmap32{0}, v)
201+
202+
v, err = FromString32("0|5")
203+
assert.Nil(t, err)
204+
assert.Equal(t, Bitmap32{0, 5}, v)
205+
})
206+
}
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)