triedb/pathdb: change the bitmap to big endian (#33584)
Some checks are pending
/ Linux Build (push) Waiting to run
/ Linux Build (arm) (push) Waiting to run
/ Keeper Build (push) Waiting to run
/ Windows Build (push) Waiting to run
/ Docker Image (push) Waiting to run

The bitmap is used in compact-encoded trie nodes to indicate which elements 
have been modified. The bitmap format has been updated to use big-endian
encoding. 

Bit positions are numbered from 0 to 15, where position 0 corresponds to
the most significant bit of b[0], and position 15 corresponds to the least
significant bit of b[1].
This commit is contained in:
rjl493456442 2026-01-15 17:28:57 +08:00 committed by GitHub
parent e3e556b266
commit 494908a852
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 70 additions and 10 deletions

View file

@ -19,6 +19,7 @@ package pathdb
import (
"encoding/binary"
"fmt"
"math/bits"
"slices"
)
@ -81,3 +82,25 @@ func isBitSet(b []byte, index int) bool {
func setBit(b []byte, index int) {
b[index/8] |= 1 << (7 - index%8)
}
// bitPosTwoBytes returns the positions of set bits in a 2-byte bitmap.
//
// The bitmap is interpreted as a big-endian uint16. Bit positions are
// numbered from 0 to 15, where position 0 corresponds to the most
// significant bit of b[0], and position 15 corresponds to the least
// significant bit of b[1].
func bitPosTwoBytes(b []byte) []int {
if len(b) != 2 {
panic("expect 2 bytes")
}
var (
pos []int
mask = binary.BigEndian.Uint16(b)
)
for mask != 0 {
p := bits.LeadingZeros16(mask)
pos = append(pos, p)
mask &^= 1 << (15 - p)
}
return pos
}

View file

@ -18,6 +18,7 @@ package pathdb
import (
"bytes"
"reflect"
"testing"
)
@ -79,3 +80,47 @@ func TestBitmapSet(t *testing.T) {
}
}
}
func TestBitPositions(t *testing.T) {
suites := []struct {
input []byte
expect []int
}{
{
[]byte{0b10000000, 0x0}, []int{0},
},
{
[]byte{0b01000000, 0x0}, []int{1},
},
{
[]byte{0b00000001, 0x0}, []int{7},
},
{
[]byte{0b00000000, 0b10000000}, []int{8},
},
{
[]byte{0b00000000, 0b00000001}, []int{15},
},
{
[]byte{0b10000000, 0b00000001}, []int{0, 15},
},
{
[]byte{0b10000001, 0b00000001}, []int{0, 7, 15},
},
{
[]byte{0b10000001, 0b10000001}, []int{0, 7, 8, 15},
},
{
[]byte{0b11111111, 0b11111111}, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
},
{
[]byte{0x0, 0x0}, nil,
},
}
for _, tc := range suites {
got := bitPosTwoBytes(tc.input)
if !reflect.DeepEqual(got, tc.expect) {
t.Fatalf("Unexpected position set, want: %v, got: %v", tc.expect, got)
}
}
}

View file

@ -500,8 +500,7 @@ func encodeNodeCompressed(addExtension bool, elements [][]byte, indices []int) [
flag |= 1 << 4 // Use the reserved flagE
continue
}
bitIndex := uint(pos % 8)
bitmap[pos/8] |= 1 << bitIndex
setBit(bitmap, pos)
}
enc = append(enc, flag)
enc = append(enc, bitmap...)
@ -553,14 +552,7 @@ func decodeNodeCompressed(data []byte) ([][]byte, []int, error) {
return nil, nil, errors.New("invalid data: too short")
}
bitmap := data[1:3]
for index, b := range bitmap {
for bitIdx := 0; bitIdx < 8; bitIdx++ {
if b&(1<<uint(bitIdx)) != 0 {
pos := index*8 + bitIdx
indices = append(indices, pos)
}
}
}
indices = bitPosTwoBytes(bitmap)
if flag&byte(16) != 0 { // flagE
indices = append(indices, 16)
log.Info("Unexpected 16th child encountered in a full node")