Skip to content

Commit 3e88fd3

Browse files
committed
FilterBitArray for ledger transaction filters.
I decided to implement our own FilterBitArray rather than using the Golang one because it is bloated and lacking of function to restore the Bit Array from a byte array; ie ToBytes and FromBytes functions. Change-Id: I5235928db9ed65ddab90c507ba3beea46f414d8b Signed-off-by: Binh Q. Nguyen <[email protected]>
1 parent 69a3aa6 commit 3e88fd3

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed

core/ledger/util/filterbitarray.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package util
18+
19+
// FilterBitArray is an array of bits based on byte unit, so 8 bits at each
20+
// index. The array automatically increases if the set index is larger than the
21+
// current capacity. The bit index starts at 0.
22+
type FilterBitArray []byte
23+
24+
// NewFilterBitArray creates an array with the specified bit-size. This is an
25+
// optimization to make array once for the expected capacity rather than
26+
// using Set function to auto-increase the array.
27+
func NewFilterBitArray(size uint) FilterBitArray {
28+
ba := make(FilterBitArray, (size-1)/8+1)
29+
return ba
30+
}
31+
32+
// Capacity returns the number of bits in the FilterBitArray.
33+
func (ba *FilterBitArray) Capacity() uint {
34+
return uint(len(*ba) * 8)
35+
}
36+
37+
// Set assigns 1 to the specified bit-index, which is starting from 0.
38+
// Set automatically increases the array to accommodate the bit-index.
39+
func (ba *FilterBitArray) Set(i uint) {
40+
// Location of i in the array index is floor(i/8) + 1. If it exceeds the
41+
// current byte array, we'll make a new one large enough to include the
42+
// specified bit-index
43+
if i >= ba.Capacity() {
44+
array := make([]byte, i/8+1)
45+
copy(array, *ba)
46+
*ba = array
47+
}
48+
(*ba)[i/8] |= 1 << (i % 8)
49+
}
50+
51+
// Unset assigns 0 the specified bit-index. If bit-index is larger than capacity,
52+
// do nothing.
53+
func (ba *FilterBitArray) Unset(i uint) {
54+
if i < ba.Capacity() {
55+
(*ba)[i/8] &^= 1 << (i % 8)
56+
}
57+
}
58+
59+
// ValueAt returns the value at the specified bit-index. If bit-index is out
60+
// of range, return 0. Note that the returned value is in byte, so it may be
61+
// a power of 2 if not 0.
62+
func (ba *FilterBitArray) ValueAt(i uint) byte {
63+
if i < ba.Capacity() {
64+
return (*ba)[i/8] & (1 << (i % 8))
65+
}
66+
return 0
67+
}
68+
69+
// IsSet returns true if the specified bit-index is 1; false otherwise.
70+
func (ba *FilterBitArray) IsSet(i uint) bool {
71+
return (ba.ValueAt(i) != 0)
72+
}
73+
74+
// ToBytes returns the byte array for storage.
75+
func (ba *FilterBitArray) ToBytes() []byte {
76+
return *ba
77+
}
78+
79+
// FromBytes accepts a byte array.
80+
func (ba *FilterBitArray) FromBytes(bytes []byte) {
81+
*ba = bytes
82+
}
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// To run this test by itself, enter `go test -run FilterBitArray` from this folder
18+
package util
19+
20+
import (
21+
"bytes"
22+
"encoding/binary"
23+
"testing"
24+
)
25+
26+
func TestFilterBitArrayFixed(t *testing.T) {
27+
ba := NewFilterBitArray(25)
28+
// Note that capacity may be greater than 25
29+
t.Logf("FilterBitArray capacity: %d\n", ba.Capacity())
30+
var i uint
31+
for i = 0; i < ba.Capacity(); i++ {
32+
ba.Set(i)
33+
}
34+
// All bits must be set
35+
for i = 0; i < ba.Capacity(); i++ {
36+
if !ba.IsSet(i) {
37+
t.FailNow()
38+
}
39+
}
40+
for i = 0; i < ba.Capacity(); i++ {
41+
ba.Unset(i)
42+
}
43+
// All bits must be unset
44+
for i = 0; i < ba.Capacity(); i++ {
45+
if ba.IsSet(i) {
46+
t.FailNow()
47+
}
48+
}
49+
}
50+
51+
func TestFilterBitArraySparse(t *testing.T) {
52+
ba := new(FilterBitArray)
53+
// test byte boundary
54+
ba.Unset(0)
55+
ba.Set(8)
56+
ba.Unset(9)
57+
ba.Set(116)
58+
if ba.IsSet(0) {
59+
t.FailNow()
60+
}
61+
if !ba.IsSet(8) {
62+
t.FailNow()
63+
}
64+
if ba.IsSet(9) {
65+
t.FailNow()
66+
}
67+
if !ba.IsSet(116) {
68+
t.FailNow()
69+
}
70+
}
71+
72+
func TestFilterBitArrayIO(t *testing.T) {
73+
ba := NewFilterBitArray(20)
74+
var i uint
75+
for i = 0; i < 20; i++ {
76+
if i%2 == 0 {
77+
ba.Set(i)
78+
} else {
79+
ba.Unset(i)
80+
}
81+
}
82+
b := ba.ToBytes()
83+
buf := new(bytes.Buffer)
84+
if err := binary.Write(buf, binary.LittleEndian, b); err != nil {
85+
t.Fatalf("binary.Write failed: %s", err)
86+
}
87+
if err := binary.Read(buf, binary.LittleEndian, b); err != nil {
88+
t.Fatalf("binary.Read failed: %s", err)
89+
}
90+
ba.FromBytes(b)
91+
for i = 0; i < 20; i++ {
92+
if i%2 == 0 {
93+
if !ba.IsSet(i) {
94+
t.FailNow()
95+
}
96+
} else {
97+
if ba.IsSet(i) {
98+
t.FailNow()
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)