-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <binhn@us.ibm.com>
- Loading branch information
Showing
2 changed files
with
184 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
Copyright IBM Corp. 2016 All Rights Reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package util | ||
|
||
// FilterBitArray is an array of bits based on byte unit, so 8 bits at each | ||
// index. The array automatically increases if the set index is larger than the | ||
// current capacity. The bit index starts at 0. | ||
type FilterBitArray []byte | ||
|
||
// NewFilterBitArray creates an array with the specified bit-size. This is an | ||
// optimization to make array once for the expected capacity rather than | ||
// using Set function to auto-increase the array. | ||
func NewFilterBitArray(size uint) FilterBitArray { | ||
ba := make(FilterBitArray, (size-1)/8+1) | ||
return ba | ||
} | ||
|
||
// Capacity returns the number of bits in the FilterBitArray. | ||
func (ba *FilterBitArray) Capacity() uint { | ||
return uint(len(*ba) * 8) | ||
} | ||
|
||
// Set assigns 1 to the specified bit-index, which is starting from 0. | ||
// Set automatically increases the array to accommodate the bit-index. | ||
func (ba *FilterBitArray) Set(i uint) { | ||
// Location of i in the array index is floor(i/8) + 1. If it exceeds the | ||
// current byte array, we'll make a new one large enough to include the | ||
// specified bit-index | ||
if i >= ba.Capacity() { | ||
array := make([]byte, i/8+1) | ||
copy(array, *ba) | ||
*ba = array | ||
} | ||
(*ba)[i/8] |= 1 << (i % 8) | ||
} | ||
|
||
// Unset assigns 0 the specified bit-index. If bit-index is larger than capacity, | ||
// do nothing. | ||
func (ba *FilterBitArray) Unset(i uint) { | ||
if i < ba.Capacity() { | ||
(*ba)[i/8] &^= 1 << (i % 8) | ||
} | ||
} | ||
|
||
// ValueAt returns the value at the specified bit-index. If bit-index is out | ||
// of range, return 0. Note that the returned value is in byte, so it may be | ||
// a power of 2 if not 0. | ||
func (ba *FilterBitArray) ValueAt(i uint) byte { | ||
if i < ba.Capacity() { | ||
return (*ba)[i/8] & (1 << (i % 8)) | ||
} | ||
return 0 | ||
} | ||
|
||
// IsSet returns true if the specified bit-index is 1; false otherwise. | ||
func (ba *FilterBitArray) IsSet(i uint) bool { | ||
return (ba.ValueAt(i) != 0) | ||
} | ||
|
||
// ToBytes returns the byte array for storage. | ||
func (ba *FilterBitArray) ToBytes() []byte { | ||
return *ba | ||
} | ||
|
||
// FromBytes accepts a byte array. | ||
func (ba *FilterBitArray) FromBytes(bytes []byte) { | ||
*ba = bytes | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
Copyright IBM Corp. 2016 All Rights Reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
// To run this test by itself, enter `go test -run FilterBitArray` from this folder | ||
package util | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"testing" | ||
) | ||
|
||
func TestFilterBitArrayFixed(t *testing.T) { | ||
ba := NewFilterBitArray(25) | ||
// Note that capacity may be greater than 25 | ||
t.Logf("FilterBitArray capacity: %d\n", ba.Capacity()) | ||
var i uint | ||
for i = 0; i < ba.Capacity(); i++ { | ||
ba.Set(i) | ||
} | ||
// All bits must be set | ||
for i = 0; i < ba.Capacity(); i++ { | ||
if !ba.IsSet(i) { | ||
t.FailNow() | ||
} | ||
} | ||
for i = 0; i < ba.Capacity(); i++ { | ||
ba.Unset(i) | ||
} | ||
// All bits must be unset | ||
for i = 0; i < ba.Capacity(); i++ { | ||
if ba.IsSet(i) { | ||
t.FailNow() | ||
} | ||
} | ||
} | ||
|
||
func TestFilterBitArraySparse(t *testing.T) { | ||
ba := new(FilterBitArray) | ||
// test byte boundary | ||
ba.Unset(0) | ||
ba.Set(8) | ||
ba.Unset(9) | ||
ba.Set(116) | ||
if ba.IsSet(0) { | ||
t.FailNow() | ||
} | ||
if !ba.IsSet(8) { | ||
t.FailNow() | ||
} | ||
if ba.IsSet(9) { | ||
t.FailNow() | ||
} | ||
if !ba.IsSet(116) { | ||
t.FailNow() | ||
} | ||
} | ||
|
||
func TestFilterBitArrayIO(t *testing.T) { | ||
ba := NewFilterBitArray(20) | ||
var i uint | ||
for i = 0; i < 20; i++ { | ||
if i%2 == 0 { | ||
ba.Set(i) | ||
} else { | ||
ba.Unset(i) | ||
} | ||
} | ||
b := ba.ToBytes() | ||
buf := new(bytes.Buffer) | ||
if err := binary.Write(buf, binary.LittleEndian, b); err != nil { | ||
t.Fatalf("binary.Write failed: %s", err) | ||
} | ||
if err := binary.Read(buf, binary.LittleEndian, b); err != nil { | ||
t.Fatalf("binary.Read failed: %s", err) | ||
} | ||
ba.FromBytes(b) | ||
for i = 0; i < 20; i++ { | ||
if i%2 == 0 { | ||
if !ba.IsSet(i) { | ||
t.FailNow() | ||
} | ||
} else { | ||
if ba.IsSet(i) { | ||
t.FailNow() | ||
} | ||
} | ||
} | ||
} |