Skip to content

Commit 04cb62c

Browse files
committed
Extend functionality of FilterBitArray
Allow batch operations on bit array, helps to initialize and manipulate continuos subsets of bits. Change-Id: Ic600b341513300b43073271902612c3b0f3e9565 Signed-off-by: Artem Barger <[email protected]>
1 parent b576c78 commit 04cb62c

File tree

2 files changed

+118
-9
lines changed

2 files changed

+118
-9
lines changed

core/ledger/util/filterbitarray.go

+80-9
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,99 @@ package util
2121
// current capacity. The bit index starts at 0.
2222
type FilterBitArray []byte
2323

24+
const (
25+
byteMask byte = 0xFF
26+
byteSize = 8
27+
)
28+
2429
// NewFilterBitArray creates an array with the specified bit-size. This is an
2530
// optimization to make array once for the expected capacity rather than
2631
// using Set function to auto-increase the array.
2732
func NewFilterBitArray(size uint) FilterBitArray {
28-
ba := make(FilterBitArray, (size-1)/8+1)
33+
ba := make(FilterBitArray, (size-1)/byteSize+1)
2934
return ba
3035
}
3136

37+
// NewFilterBitArrayFromBytes reconstructs an array from given byte array.
38+
func NewFilterBitArrayFromBytes(bytes []byte) FilterBitArray {
39+
bitArray := FilterBitArray{}
40+
bitArray.FromBytes(bytes)
41+
return bitArray
42+
}
43+
3244
// Capacity returns the number of bits in the FilterBitArray.
3345
func (ba *FilterBitArray) Capacity() uint {
34-
return uint(len(*ba) * 8)
46+
return uint(len(*ba) * byteSize)
3547
}
3648

3749
// Set assigns 1 to the specified bit-index, which is starting from 0.
3850
// Set automatically increases the array to accommodate the bit-index.
3951
func (ba *FilterBitArray) Set(i uint) {
40-
// Location of i in the array index is floor(i/8) + 1. If it exceeds the
52+
// Location of i in the array index is floor(i/byte_size) + 1. If it exceeds the
4153
// current byte array, we'll make a new one large enough to include the
4254
// specified bit-index
4355
if i >= ba.Capacity() {
44-
array := make([]byte, i/8+1)
45-
copy(array, *ba)
46-
*ba = array
56+
ba.expand(i/byteSize + 1)
57+
}
58+
(*ba)[i/byteSize] |= 1 << (i % byteSize)
59+
}
60+
61+
// SetRange assigns 1 to the bit-indexes specified by range [begin, end]
62+
// Set automatically increases the array to accommodate the bit-index.
63+
func (ba *FilterBitArray) SetRange(begin uint, end uint) {
64+
// Location of i in the array index is floor(i/byte_size) + 1. If it exceeds the
65+
// current byte array, we'll make a new one large enough to include the
66+
// specified bit-index
67+
startByteIndex := ba.byteIndex(begin)
68+
endByteIndex := ba.byteIndex(end)
69+
70+
if end >= ba.Capacity() {
71+
ba.expand(endByteIndex + 1)
72+
}
73+
74+
firstByteMask := byteMask << (begin % byteSize)
75+
lastByteMask := byteMask >> ((byteSize - end - 1) % byteSize)
76+
77+
if startByteIndex == endByteIndex {
78+
(*ba)[startByteIndex] |= (firstByteMask & lastByteMask)
79+
} else {
80+
(*ba)[startByteIndex] |= firstByteMask
81+
for i := startByteIndex + 1; i < endByteIndex; i++ {
82+
(*ba)[i] = byteMask
83+
}
84+
(*ba)[endByteIndex] |= lastByteMask
4785
}
48-
(*ba)[i/8] |= 1 << (i % 8)
4986
}
5087

5188
// Unset assigns 0 the specified bit-index. If bit-index is larger than capacity,
5289
// do nothing.
5390
func (ba *FilterBitArray) Unset(i uint) {
5491
if i < ba.Capacity() {
55-
(*ba)[i/8] &^= 1 << (i % 8)
92+
(*ba)[i/byteSize] &^= 1 << (i % byteSize)
93+
}
94+
}
95+
96+
// UnsetRange assigns 0 to all bits in range [begin, end]. If bit-index is larger than capacity,
97+
// do nothing.
98+
func (ba *FilterBitArray) UnsetRange(begin uint, end uint) {
99+
if begin > ba.Capacity() || begin == end {
100+
return
101+
}
102+
103+
startByteIndex := ba.byteIndex(begin)
104+
endByteIndex := ba.byteIndex(end)
105+
106+
firstByteMask := byteMask << (begin % byteSize)
107+
lastByteMask := byteMask >> ((byteSize - end - 1) % byteSize)
108+
109+
if startByteIndex == endByteIndex {
110+
(*ba)[startByteIndex] &= ^(firstByteMask & lastByteMask)
111+
} else {
112+
(*ba)[startByteIndex] &= ^firstByteMask
113+
for i := startByteIndex + 1; i < endByteIndex; i++ {
114+
(*ba)[i] = 0
115+
}
116+
(*ba)[endByteIndex] &= ^lastByteMask
56117
}
57118
}
58119

@@ -61,7 +122,7 @@ func (ba *FilterBitArray) Unset(i uint) {
61122
// a power of 2 if not 0.
62123
func (ba *FilterBitArray) ValueAt(i uint) byte {
63124
if i < ba.Capacity() {
64-
return (*ba)[i/8] & (1 << (i % 8))
125+
return (*ba)[i/byteSize] & (1 << (i % byteSize))
65126
}
66127
return 0
67128
}
@@ -80,3 +141,13 @@ func (ba *FilterBitArray) ToBytes() []byte {
80141
func (ba *FilterBitArray) FromBytes(bytes []byte) {
81142
*ba = bytes
82143
}
144+
145+
func (ba *FilterBitArray) expand(newSize uint) {
146+
array := make([]byte, newSize)
147+
copy(array, *ba)
148+
*ba = array
149+
}
150+
151+
func (ba *FilterBitArray) byteIndex(i uint) uint {
152+
return i / byteSize
153+
}

core/ledger/util/filterbitarray_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"bytes"
2222
"encoding/binary"
2323
"testing"
24+
25+
"github.com/stretchr/testify/assert"
2426
)
2527

2628
func TestFilterBitArrayFixed(t *testing.T) {
@@ -100,3 +102,39 @@ func TestFilterBitArrayIO(t *testing.T) {
100102
}
101103
}
102104
}
105+
106+
func TestFilterBitArrayRangeFuncs(t *testing.T) {
107+
ba := NewFilterBitArray(12)
108+
109+
// 1111 1110 0111 1111 ==> { 254, 127 }
110+
ba.SetRange(1, 14)
111+
assert.True(t, bytes.Equal(ba.ToBytes(), []byte{254, 127}))
112+
113+
// 0111 1110 0111 1110 ==> { 126, 126 }
114+
ba.UnsetRange(7, 8)
115+
assert.True(t, bytes.Equal(ba.ToBytes(), []byte{126, 126}))
116+
117+
if !ba.IsSet(11) {
118+
t.FailNow()
119+
}
120+
121+
if !ba.IsSet(1) {
122+
t.FailNow()
123+
}
124+
125+
// 1100 0000 0111 1110 0111 1110 ==> { 126, 126, 192 }
126+
ba.SetRange(22, 23)
127+
assert.Equal(t, ba.ToBytes(), []byte{126, 126, 192})
128+
129+
if ba.IsSet(15) {
130+
t.FailNow()
131+
}
132+
133+
if ba.IsSet(20) {
134+
t.FailNow()
135+
}
136+
137+
// 1100 0000 0111 1110 0000 1110 ==> { 198, 127, 192 }
138+
ba.UnsetRange(4, 6)
139+
assert.Equal(t, ba.ToBytes(), []byte{14, 126, 192})
140+
}

0 commit comments

Comments
 (0)