-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor bytesutil, add support for go1.20 slice to array conversions (…
…#11838) * Refactor bytes.go and bytes_test.go to smaller files, introduce go1.17 and go1.20 style of array copy * rename bytes_go17.go to reflect that it works on any version 1.19 and below * fix PadTo when len is exactly the size * Add go1.20 style conversions * Forgot another int method Co-authored-by: Radosław Kapka <rkapka@wp.pl> Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
- Loading branch information
1 parent
116f3ac
commit 1e3a55c
Showing
13 changed files
with
925 additions
and
801 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
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,90 @@ | ||
package bytesutil | ||
|
||
import ( | ||
"math/bits" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// SetBit sets the index `i` of bitlist `b` to 1. | ||
// It grows and returns a longer bitlist with 1 set | ||
// if index `i` is out of range. | ||
func SetBit(b []byte, i int) []byte { | ||
if i >= len(b)*8 { | ||
h := (i + (8 - i%8)) / 8 | ||
b = append(b, make([]byte, h-len(b))...) | ||
} | ||
|
||
bit := uint8(1 << (i % 8)) | ||
b[i/8] |= bit | ||
return b | ||
} | ||
|
||
// ClearBit clears the index `i` of bitlist `b`. | ||
// Returns the original bitlist if the index `i` | ||
// is out of range. | ||
func ClearBit(b []byte, i int) []byte { | ||
if i >= len(b)*8 || i < 0 { | ||
return b | ||
} | ||
|
||
bit := uint8(1 << (i % 8)) | ||
b[i/8] &^= bit | ||
return b | ||
} | ||
|
||
// MakeEmptyBitlists returns an empty bitlist with | ||
// input size `i`. | ||
func MakeEmptyBitlists(i int) []byte { | ||
return make([]byte, (i+(8-i%8))/8) | ||
} | ||
|
||
// HighestBitIndex returns the index of the highest | ||
// bit set from bitlist `b`. | ||
func HighestBitIndex(b []byte) (int, error) { | ||
if len(b) == 0 { | ||
return 0, errors.New("input list can't be empty or nil") | ||
} | ||
|
||
for i := len(b) - 1; i >= 0; i-- { | ||
if b[i] == 0 { | ||
continue | ||
} | ||
return bits.Len8(b[i]) + (i * 8), nil | ||
} | ||
|
||
return 0, nil | ||
} | ||
|
||
// HighestBitIndexAt returns the index of the highest | ||
// bit set from bitlist `b` that is at `index` (inclusive). | ||
func HighestBitIndexAt(b []byte, index int) (int, error) { | ||
bLength := len(b) | ||
if b == nil || bLength == 0 { | ||
return 0, errors.New("input list can't be empty or nil") | ||
} | ||
if index < 0 { | ||
return 0, errors.Errorf("index is negative: %d", index) | ||
} | ||
|
||
start := index / 8 | ||
if start >= bLength { | ||
start = bLength - 1 | ||
} | ||
|
||
mask := byte(1<<(index%8) - 1) | ||
for i := start; i >= 0; i-- { | ||
if index/8 > i { | ||
mask = 0xff | ||
} | ||
masked := b[i] & mask | ||
minBitsMasked := bits.Len8(masked) | ||
if b[i] == 0 || (minBitsMasked == 0 && index/8 <= i) { | ||
continue | ||
} | ||
|
||
return minBitsMasked + (i * 8), nil | ||
} | ||
|
||
return 0, nil | ||
} |
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,139 @@ | ||
package bytesutil_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" | ||
"github.com/prysmaticlabs/prysm/v3/testing/assert" | ||
"github.com/prysmaticlabs/prysm/v3/testing/require" | ||
) | ||
|
||
func TestSetBit(t *testing.T) { | ||
tests := []struct { | ||
a []byte | ||
b int | ||
c []byte | ||
}{ | ||
{[]byte{0b00000000}, 1, []byte{0b00000010}}, | ||
{[]byte{0b00000010}, 7, []byte{0b10000010}}, | ||
{[]byte{0b10000010}, 9, []byte{0b10000010, 0b00000010}}, | ||
{[]byte{0b10000010}, 27, []byte{0b10000010, 0b00000000, 0b00000000, 0b00001000}}, | ||
{[]byte{0b10000010, 0b00000000}, 8, []byte{0b10000010, 0b00000001}}, | ||
{[]byte{0b10000010, 0b00000000}, 31, []byte{0b10000010, 0b00000000, 0b00000000, 0b10000000}}, | ||
} | ||
for _, tt := range tests { | ||
assert.DeepEqual(t, tt.c, bytesutil.SetBit(tt.a, tt.b)) | ||
} | ||
} | ||
|
||
func TestClearBit(t *testing.T) { | ||
tests := []struct { | ||
a []byte | ||
b int | ||
c []byte | ||
}{ | ||
{[]byte{0b00000000}, 1, []byte{0b00000000}}, | ||
{[]byte{0b00000010}, 1, []byte{0b00000000}}, | ||
{[]byte{0b10000010}, 1, []byte{0b10000000}}, | ||
{[]byte{0b10000010}, 8, []byte{0b10000010}}, | ||
{[]byte{0b10000010, 0b00001111}, 7, []byte{0b00000010, 0b00001111}}, | ||
{[]byte{0b10000010, 0b00001111}, 10, []byte{0b10000010, 0b00001011}}, | ||
} | ||
for _, tt := range tests { | ||
assert.DeepEqual(t, tt.c, bytesutil.ClearBit(tt.a, tt.b)) | ||
} | ||
} | ||
|
||
func TestMakeEmptyBitlists(t *testing.T) { | ||
tests := []struct { | ||
a int | ||
b int | ||
}{ | ||
{0, 1}, | ||
{1, 1}, | ||
{2, 1}, | ||
{7, 1}, | ||
{8, 2}, | ||
{15, 2}, | ||
{16, 3}, | ||
{100, 13}, | ||
{104, 14}, | ||
} | ||
for _, tt := range tests { | ||
assert.DeepEqual(t, tt.b, len(bytesutil.MakeEmptyBitlists(tt.a))) | ||
} | ||
} | ||
|
||
func TestHighestBitIndex(t *testing.T) { | ||
tests := []struct { | ||
a []byte | ||
b int | ||
error bool | ||
}{ | ||
{nil, 0, true}, | ||
{[]byte{}, 0, true}, | ||
{[]byte{0b00000001}, 1, false}, | ||
{[]byte{0b10100101}, 8, false}, | ||
{[]byte{0x00, 0x00}, 0, false}, | ||
{[]byte{0xff, 0xa0}, 16, false}, | ||
{[]byte{12, 34, 56, 78}, 31, false}, | ||
{[]byte{255, 255, 255, 255}, 32, false}, | ||
} | ||
for _, tt := range tests { | ||
i, err := bytesutil.HighestBitIndex(tt.a) | ||
if !tt.error { | ||
require.NoError(t, err) | ||
assert.DeepEqual(t, tt.b, i) | ||
} else { | ||
assert.ErrorContains(t, "input list can't be empty or nil", err) | ||
} | ||
} | ||
} | ||
|
||
func TestHighestBitIndexBelow(t *testing.T) { | ||
tests := []struct { | ||
a []byte | ||
b int | ||
c int | ||
error bool | ||
}{ | ||
{nil, 0, 0, true}, | ||
{[]byte{}, 0, 0, true}, | ||
{[]byte{0b00010001}, 0, 0, false}, | ||
{[]byte{0b00010001}, 1, 1, false}, | ||
{[]byte{0b00010001}, 2, 1, false}, | ||
{[]byte{0b00010001}, 4, 1, false}, | ||
{[]byte{0b00010001}, 5, 5, false}, | ||
{[]byte{0b00010001}, 8, 5, false}, | ||
{[]byte{0b00010001, 0b00000000}, 0, 0, false}, | ||
{[]byte{0b00010001, 0b00000000}, 1, 1, false}, | ||
{[]byte{0b00010001, 0b00000000}, 2, 1, false}, | ||
{[]byte{0b00010001, 0b00000000}, 4, 1, false}, | ||
{[]byte{0b00010001, 0b00000000}, 5, 5, false}, | ||
{[]byte{0b00010001, 0b00000000}, 8, 5, false}, | ||
{[]byte{0b00010001, 0b00000000}, 15, 5, false}, | ||
{[]byte{0b00010001, 0b00000000}, 16, 5, false}, | ||
{[]byte{0b00010001, 0b00100010}, 8, 5, false}, | ||
{[]byte{0b00010001, 0b00100010}, 9, 5, false}, | ||
{[]byte{0b00010001, 0b00100010}, 10, 10, false}, | ||
{[]byte{0b00010001, 0b00100010}, 11, 10, false}, | ||
{[]byte{0b00010001, 0b00100010}, 14, 14, false}, | ||
{[]byte{0b00010001, 0b00100010}, 15, 14, false}, | ||
{[]byte{0b00010001, 0b00100010}, 24, 14, false}, | ||
{[]byte{0b00010001, 0b00100010, 0b10000000}, 23, 14, false}, | ||
{[]byte{0b00010001, 0b00100010, 0b10000000}, 24, 24, false}, | ||
{[]byte{0b00000000, 0b00000001, 0b00000011}, 17, 17, false}, | ||
{[]byte{0b00000000, 0b00000001, 0b00000011}, 18, 18, false}, | ||
{[]byte{12, 34, 56, 78}, 1000, 31, false}, | ||
{[]byte{255, 255, 255, 255}, 1000, 32, false}, | ||
} | ||
for _, tt := range tests { | ||
i, err := bytesutil.HighestBitIndexAt(tt.a, tt.b) | ||
if !tt.error { | ||
require.NoError(t, err) | ||
assert.DeepEqual(t, tt.c, i) | ||
} else { | ||
assert.ErrorContains(t, "input list can't be empty or nil", err) | ||
} | ||
} | ||
} |
Oops, something went wrong.