Skip to content

Commit

Permalink
Remove leading zero bytes from bit array
Browse files Browse the repository at this point in the history
  • Loading branch information
weiihann committed Feb 6, 2025
1 parent 3dfc5af commit 1219c57
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
33 changes: 26 additions & 7 deletions core/trie2/trieutils/bitarray.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
const (
maxUint64 = uint64(math.MaxUint64) // 0xFFFFFFFFFFFFFFFF
maxUint8 = uint8(math.MaxUint8)
bytes32 = 32
MaxBitArraySize = 33 // (1 + 4 * 8) bytes
)

Expand Down Expand Up @@ -456,7 +457,7 @@ func (b *BitArray) IsEmpty() bool {

// Serialises the BitArray into a bytes buffer in the following format:
// - First byte: length of the bit array (0-255)
// - Remaining bytes: the necessary bytes included in big endian order
// - Remaining bytes: the necessary bytes included in big endian order, without leading zeros
// Example:
//
// BitArray{len: 10, words: [4]uint64{0x03FF}} -> [0x0A, 0x03, 0xFF]
Expand All @@ -481,16 +482,17 @@ func (b *BitArray) UnmarshalBinary(data []byte) error {
}

Check warning on line 482 in core/trie2/trieutils/bitarray.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/trieutils/bitarray.go#L481-L482

Added lines #L481 - L482 were not covered by tests

length := data[0]
byteCount := (uint(length) + 7) / 8 // Round up to nearest byte
byteCount := (int(length) + 7) / 8 // Get the total number of bytes needed to represent the bit array

if len(data) < int(byteCount)+1 {
return fmt.Errorf("invalid data length: got %d bytes, expected %d", len(data), byteCount+1)
if len(data) > byteCount+1 {
return fmt.Errorf("invalid data length: got %d bytes, expected <= %d", len(data), byteCount+1)
}

Check warning on line 489 in core/trie2/trieutils/bitarray.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/trieutils/bitarray.go#L488-L489

Added lines #L488 - L489 were not covered by tests

b.len = length

var bs [32]byte
copy(bs[32-byteCount:], data[1:])
bitArrBytes := data[1:]
copy(bs[32-len(bitArrBytes):], bitArrBytes) // Fill up the non-zero bytes at the end of the byte array
b.setBytes32(bs[:])

return nil
Expand Down Expand Up @@ -715,14 +717,31 @@ func (b *BitArray) byteCount() uint {
}

// Returns a slice containing only the bytes that are actually used by the bit array,
// as specified by the length. The returned slice is in big-endian order.
// as specified by the length. Leading zero bytes will be removed.
// The returned slice is in big-endian order.
//
// Example:
//
// len = 10, words = [0x3FF, 0, 0, 0] -> [0x03, 0xFF]
func (b *BitArray) activeBytes() []byte {
if b.len == 0 {
return nil
}

wordsBytes := b.Bytes()
return wordsBytes[32-b.byteCount():]
end := uint(bytes32)
start := end - b.byteCount()

// Find first non-zero byte
for start < end-1 && wordsBytes[start] == 0 {
start++
}

if start == end {
return nil
}

Check warning on line 742 in core/trie2/trieutils/bitarray.go

View check run for this annotation

Codecov / codecov/patch

core/trie2/trieutils/bitarray.go#L741-L742

Added lines #L741 - L742 were not covered by tests

return wordsBytes[start:end]
}

func (b *BitArray) rsh64(x *BitArray) {
Expand Down
35 changes: 35 additions & 0 deletions core/trie2/trieutils/bitarray_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,41 @@ func TestWriteAndUnmarshalBinary(t *testing.T) {
},
want: []byte{16, 0xAA, 0xAA}, // length byte + 2 data bytes
},
{
name: "leading zeros in first byte",
ba: BitArray{
len: 8,
words: [4]uint64{0x0F, 0, 0, 0}, // 00001111
},
want: []byte{8, 0x0F}, // length byte + 1 data byte
},
{
name: "all leading zeros in first byte",
ba: BitArray{
len: 8,
words: [4]uint64{0x00, 0, 0, 0}, // 00000000
},
want: []byte{8, 0x00}, // length byte + 1 data byte
},
{
name: "leading zeros across multiple bytes",
ba: BitArray{
len: 24,
words: [4]uint64{0x0000FF, 0, 0, 0}, // 000000000000000011111111
},
want: []byte{24, 0xFF}, // length byte + 1 data byte
},
{
name: "leading zeros in large number",
ba: BitArray{
len: 255,
words: [4]uint64{maxUint64, maxUint64, 0, 0}, // All 1s in lower bits, zeros in upper bits
},
want: append(
[]byte{255}, // length byte
bytes.Repeat([]byte{0xFF}, 16)..., // 16 bytes of all 1s
),
},
}

for _, tt := range tests {
Expand Down

0 comments on commit 1219c57

Please sign in to comment.