mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
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>
This commit is contained in:
@@ -2,7 +2,15 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["bytes.go"],
|
||||
srcs = [
|
||||
"bits.go",
|
||||
"bytes.go",
|
||||
"bytes_go120.go",
|
||||
"bytes_legacy.go",
|
||||
"eth_types.go",
|
||||
"hex.go",
|
||||
"integers.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
@@ -15,7 +23,13 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["bytes_test.go"],
|
||||
srcs = [
|
||||
"bits_test.go",
|
||||
"bytes_test.go",
|
||||
"eth_types_test.go",
|
||||
"hex_test.go",
|
||||
"integers_test.go",
|
||||
],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
|
||||
90
encoding/bytesutil/bits.go
Normal file
90
encoding/bytesutil/bits.go
Normal file
@@ -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
|
||||
}
|
||||
139
encoding/bytesutil/bits_test.go
Normal file
139
encoding/bytesutil/bits_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,131 +2,9 @@
|
||||
package bytesutil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
"regexp"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
)
|
||||
|
||||
var hexRegex = regexp.MustCompile("^0x[0-9a-fA-F]+$")
|
||||
|
||||
// ToBytes returns integer x to bytes in little-endian format at the specified length.
|
||||
// Spec defines similar method uint_to_bytes(n: uint) -> bytes, which is equivalent to ToBytes(n, 8).
|
||||
func ToBytes(x uint64, length int) []byte {
|
||||
if length < 0 {
|
||||
length = 0
|
||||
}
|
||||
makeLength := length
|
||||
if length < 8 {
|
||||
makeLength = 8
|
||||
}
|
||||
bytes := make([]byte, makeLength)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:length]
|
||||
}
|
||||
|
||||
// Bytes1 returns integer x to bytes in little-endian format, x.to_bytes(1, 'little').
|
||||
func Bytes1(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:1]
|
||||
}
|
||||
|
||||
// Bytes2 returns integer x to bytes in little-endian format, x.to_bytes(2, 'little').
|
||||
func Bytes2(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:2]
|
||||
}
|
||||
|
||||
// Bytes3 returns integer x to bytes in little-endian format, x.to_bytes(3, 'little').
|
||||
func Bytes3(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:3]
|
||||
}
|
||||
|
||||
// Bytes4 returns integer x to bytes in little-endian format, x.to_bytes(4, 'little').
|
||||
func Bytes4(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:4]
|
||||
}
|
||||
|
||||
// Bytes8 returns integer x to bytes in little-endian format, x.to_bytes(8, 'little').
|
||||
func Bytes8(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// Bytes32 returns integer x to bytes in little-endian format, x.to_bytes(32, 'little').
|
||||
func Bytes32(x uint64) []byte {
|
||||
bytes := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// FromBytes4 returns an integer which is stored in the little-endian format(4, 'little')
|
||||
// from a byte array.
|
||||
func FromBytes4(x []byte) uint64 {
|
||||
if len(x) < 4 {
|
||||
return 0
|
||||
}
|
||||
empty4bytes := make([]byte, 4)
|
||||
return binary.LittleEndian.Uint64(append(x[:4], empty4bytes...))
|
||||
}
|
||||
|
||||
// FromBytes8 returns an integer which is stored in the little-endian format(8, 'little')
|
||||
// from a byte array.
|
||||
func FromBytes8(x []byte) uint64 {
|
||||
if len(x) < 8 {
|
||||
return 0
|
||||
}
|
||||
return binary.LittleEndian.Uint64(x)
|
||||
}
|
||||
|
||||
// ToBytes4 is a convenience method for converting a byte slice to a fix
|
||||
// sized 4 byte array. This method will truncate the input if it is larger
|
||||
// than 4 bytes.
|
||||
func ToBytes4(x []byte) [4]byte {
|
||||
var y [4]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes20 is a convenience method for converting a byte slice to a fix
|
||||
// sized 20 byte array. This method will truncate the input if it is larger
|
||||
// than 20 bytes.
|
||||
func ToBytes20(x []byte) [20]byte {
|
||||
var y [20]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes32 is a convenience method for converting a byte slice to a fix
|
||||
// sized 32 byte array. This method will truncate the input if it is larger
|
||||
// than 32 bytes.
|
||||
func ToBytes32(x []byte) [32]byte {
|
||||
var y [32]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes48 is a convenience method for converting a byte slice to a fix
|
||||
// sized 48 byte array. This method will truncate the input if it is larger
|
||||
// than 48 bytes.
|
||||
func ToBytes48(x []byte) [48]byte {
|
||||
var y [48]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes48Array is a convenience method for converting an array of
|
||||
// byte slices to an array of fixed-sized byte arrays.
|
||||
func ToBytes48Array(x [][]byte) [][48]byte {
|
||||
@@ -137,39 +15,12 @@ func ToBytes48Array(x [][]byte) [][48]byte {
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes64 is a convenience method for converting a byte slice to a fix
|
||||
// sized 64 byte array. This method will truncate the input if it is larger
|
||||
// than 64 bytes.
|
||||
func ToBytes64(x []byte) [64]byte {
|
||||
var y [64]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes96 is a convenience method for converting a byte slice to a fix
|
||||
// sized 96 byte array. This method will truncate the input if it is larger
|
||||
// than 96 bytes.
|
||||
func ToBytes96(x []byte) [96]byte {
|
||||
var y [96]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBool is a convenience method for converting a byte to a bool.
|
||||
// This method will use the first bit of the 0 byte to generate the returned value.
|
||||
func ToBool(x byte) bool {
|
||||
return x&1 == 1
|
||||
}
|
||||
|
||||
// FromBytes2 returns an integer which is stored in the little-endian format(2, 'little')
|
||||
// from a byte array.
|
||||
func FromBytes2(x []byte) uint16 {
|
||||
if len(x) < 2 {
|
||||
return 0
|
||||
}
|
||||
return binary.LittleEndian.Uint16(x[:2])
|
||||
}
|
||||
|
||||
// FromBool is a convenience method for converting a bool to a byte.
|
||||
// This method will use the first bit to generate the returned value.
|
||||
func FromBool(x bool) byte {
|
||||
@@ -203,16 +54,6 @@ func Trunc(x []byte) []byte {
|
||||
return x
|
||||
}
|
||||
|
||||
// ToLowInt64 returns the lowest 8 bytes interpreted as little endian.
|
||||
func ToLowInt64(x []byte) int64 {
|
||||
if len(x) < 8 {
|
||||
return 0
|
||||
}
|
||||
// Use the first 8 bytes.
|
||||
x = x[:8]
|
||||
return int64(binary.LittleEndian.Uint64(x)) // lint:ignore uintcast -- A negative number might be the expected result.
|
||||
}
|
||||
|
||||
// SafeCopyRootAtIndex takes a copy of an 32-byte slice in a slice of byte slices. Returns error if index out of range.
|
||||
func SafeCopyRootAtIndex(input [][]byte, idx uint64) ([]byte, error) {
|
||||
if input == nil {
|
||||
@@ -227,7 +68,7 @@ func SafeCopyRootAtIndex(input [][]byte, idx uint64) ([]byte, error) {
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// SafeCopyBytes will copy and return a non-nil byte array, otherwise it returns nil.
|
||||
// SafeCopyBytes will copy and return a non-nil byte slice, otherwise it returns nil.
|
||||
func SafeCopyBytes(cp []byte) []byte {
|
||||
if cp != nil {
|
||||
copied := make([]byte, len(cp))
|
||||
@@ -237,7 +78,7 @@ func SafeCopyBytes(cp []byte) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafeCopy2dBytes will copy and return a non-nil 2d byte array, otherwise it returns nil.
|
||||
// SafeCopy2dBytes will copy and return a non-nil 2d byte slice, otherwise it returns nil.
|
||||
func SafeCopy2dBytes(ary [][]byte) [][]byte {
|
||||
if ary != nil {
|
||||
copied := make([][]byte, len(ary))
|
||||
@@ -249,7 +90,7 @@ func SafeCopy2dBytes(ary [][]byte) [][]byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafeCopy2d32Bytes will copy and return a non-nil 2d byte array, otherwise it returns nil.
|
||||
// SafeCopy2d32Bytes will copy and return a non-nil 2d byte slice, otherwise it returns nil.
|
||||
func SafeCopy2d32Bytes(ary [][32]byte) [][32]byte {
|
||||
if ary != nil {
|
||||
copied := make([][32]byte, len(ary))
|
||||
@@ -270,159 +111,12 @@ func ReverseBytes32Slice(arr [][32]byte) [][32]byte {
|
||||
// PadTo pads a byte slice to the given size. If the byte slice is larger than the given size, the
|
||||
// original slice is returned.
|
||||
func PadTo(b []byte, size int) []byte {
|
||||
if len(b) > size {
|
||||
if len(b) >= size {
|
||||
return b
|
||||
}
|
||||
return append(b, make([]byte, size-len(b))...)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Uint32ToBytes4 is a convenience method for converting uint32 to a fix
|
||||
// sized 4 byte array in big endian order. Returns 4 byte array.
|
||||
func Uint32ToBytes4(i uint32) [4]byte {
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, i)
|
||||
return ToBytes4(buf)
|
||||
}
|
||||
|
||||
// Uint64ToBytesLittleEndian conversion.
|
||||
func Uint64ToBytesLittleEndian(i uint64) []byte {
|
||||
buf := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(buf, i)
|
||||
return buf
|
||||
}
|
||||
|
||||
// Uint64ToBytesBigEndian conversion.
|
||||
func Uint64ToBytesBigEndian(i uint64) []byte {
|
||||
buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf, i)
|
||||
return buf
|
||||
}
|
||||
|
||||
// BytesToUint64BigEndian conversion. Returns 0 if empty bytes or byte slice with length less
|
||||
// than 8.
|
||||
func BytesToUint64BigEndian(b []byte) uint64 {
|
||||
if len(b) < 8 { // This will panic otherwise.
|
||||
return 0
|
||||
}
|
||||
return binary.BigEndian.Uint64(b)
|
||||
}
|
||||
|
||||
// EpochToBytesLittleEndian conversion.
|
||||
func EpochToBytesLittleEndian(i types.Epoch) []byte {
|
||||
return Uint64ToBytesLittleEndian(uint64(i))
|
||||
}
|
||||
|
||||
// EpochToBytesBigEndian conversion.
|
||||
func EpochToBytesBigEndian(i types.Epoch) []byte {
|
||||
return Uint64ToBytesBigEndian(uint64(i))
|
||||
}
|
||||
|
||||
// BytesToEpochBigEndian conversion.
|
||||
func BytesToEpochBigEndian(b []byte) types.Epoch {
|
||||
return types.Epoch(BytesToUint64BigEndian(b))
|
||||
}
|
||||
|
||||
// SlotToBytesBigEndian conversion.
|
||||
func SlotToBytesBigEndian(i types.Slot) []byte {
|
||||
return Uint64ToBytesBigEndian(uint64(i))
|
||||
}
|
||||
|
||||
// BytesToSlotBigEndian conversion.
|
||||
func BytesToSlotBigEndian(b []byte) types.Slot {
|
||||
return types.Slot(BytesToUint64BigEndian(b))
|
||||
}
|
||||
|
||||
// IsHex checks whether the byte array is a hex number prefixed with '0x'.
|
||||
func IsHex(b []byte) bool {
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
return hexRegex.Match(b)
|
||||
}
|
||||
|
||||
// ReverseByteOrder Switch the endianness of a byte slice by reversing its order.
|
||||
// this function does not modify the actual input bytes.
|
||||
func ReverseByteOrder(input []byte) []byte {
|
||||
@@ -433,30 +127,3 @@ func ReverseByteOrder(input []byte) []byte {
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// ZeroRoot returns whether or not a root is of proper length and non-zero hash.
|
||||
func ZeroRoot(root []byte) bool {
|
||||
return string(make([]byte, fieldparams.RootLength)) == string(root)
|
||||
}
|
||||
|
||||
// IsRoot checks whether the byte array is a root.
|
||||
func IsRoot(root []byte) bool {
|
||||
return len(root) == fieldparams.RootLength
|
||||
}
|
||||
|
||||
// IsValidRoot checks whether the byte array is a valid root.
|
||||
func IsValidRoot(root []byte) bool {
|
||||
return IsRoot(root) && !ZeroRoot(root)
|
||||
}
|
||||
|
||||
// LittleEndianBytesToBigInt takes bytes of a number stored as little-endian and returns a big integer
|
||||
func LittleEndianBytesToBigInt(bytes []byte) *big.Int {
|
||||
// Integers are stored as little-endian, but big.Int expects big-endian. So we need to reverse the byte order before decoding.
|
||||
return new(big.Int).SetBytes(ReverseByteOrder(bytes))
|
||||
}
|
||||
|
||||
// BigIntToLittleEndianBytes takes a big integer and returns its bytes stored as little-endian
|
||||
func BigIntToLittleEndianBytes(bigInt *big.Int) []byte {
|
||||
// big.Int.Bytes() returns bytes in big-endian order, so we need to reverse the byte order
|
||||
return ReverseByteOrder(bigInt.Bytes())
|
||||
}
|
||||
|
||||
48
encoding/bytesutil/bytes_go120.go
Normal file
48
encoding/bytesutil/bytes_go120.go
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build go1.20
|
||||
// +build go1.20
|
||||
|
||||
package bytesutil
|
||||
|
||||
// These methods use go1.20 syntax to convert a byte slice to a fixed size array.
|
||||
|
||||
// ToBytes4 is a convenience method for converting a byte slice to a fix
|
||||
// sized 4 byte array. This method will truncate the input if it is larger
|
||||
// than 4 bytes.
|
||||
func ToBytes4(x []byte) [4]byte {
|
||||
return [4]byte(PadTo(x, 4))
|
||||
}
|
||||
|
||||
// ToBytes20 is a convenience method for converting a byte slice to a fix
|
||||
// sized 20 byte array. This method will truncate the input if it is larger
|
||||
// than 20 bytes.
|
||||
func ToBytes20(x []byte) [20]byte {
|
||||
return [20]byte(PadTo(x, 20))
|
||||
}
|
||||
|
||||
// ToBytes32 is a convenience method for converting a byte slice to a fix
|
||||
// sized 32 byte array. This method will truncate the input if it is larger
|
||||
// than 32 bytes.
|
||||
func ToBytes32(x []byte) [32]byte {
|
||||
return [32]byte(PadTo(x, 32))
|
||||
}
|
||||
|
||||
// ToBytes48 is a convenience method for converting a byte slice to a fix
|
||||
// sized 48 byte array. This method will truncate the input if it is larger
|
||||
// than 48 bytes.
|
||||
func ToBytes48(x []byte) [48]byte {
|
||||
return [48]byte(PadTo(x, 48))
|
||||
}
|
||||
|
||||
// ToBytes64 is a convenience method for converting a byte slice to a fix
|
||||
// sized 64 byte array. This method will truncate the input if it is larger
|
||||
// than 64 bytes.
|
||||
func ToBytes64(x []byte) [64]byte {
|
||||
return [64]byte(PadTo(x, 64))
|
||||
}
|
||||
|
||||
// ToBytes96 is a convenience method for converting a byte slice to a fix
|
||||
// sized 96 byte array. This method will truncate the input if it is larger
|
||||
// than 96 bytes.
|
||||
func ToBytes96(x []byte) [96]byte {
|
||||
return [96]byte(PadTo(x, 96))
|
||||
}
|
||||
61
encoding/bytesutil/bytes_legacy.go
Normal file
61
encoding/bytesutil/bytes_legacy.go
Normal file
@@ -0,0 +1,61 @@
|
||||
//go:build !go1.20
|
||||
// +build !go1.20
|
||||
|
||||
package bytesutil
|
||||
|
||||
// These methods use copy() to convert a byte slice to a fixed size array.
|
||||
// This approach is used for go1.19 and below.
|
||||
|
||||
// ToBytes4 is a convenience method for converting a byte slice to a fix
|
||||
// sized 4 byte array. This method will truncate the input if it is larger
|
||||
// than 4 bytes.
|
||||
func ToBytes4(x []byte) [4]byte {
|
||||
var y [4]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes20 is a convenience method for converting a byte slice to a fix
|
||||
// sized 20 byte array. This method will truncate the input if it is larger
|
||||
// than 20 bytes.
|
||||
func ToBytes20(x []byte) [20]byte {
|
||||
var y [20]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes32 is a convenience method for converting a byte slice to a fix
|
||||
// sized 32 byte array. This method will truncate the input if it is larger
|
||||
// than 32 bytes.
|
||||
func ToBytes32(x []byte) [32]byte {
|
||||
var y [32]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes48 is a convenience method for converting a byte slice to a fix
|
||||
// sized 48 byte array. This method will truncate the input if it is larger
|
||||
// than 48 bytes.
|
||||
func ToBytes48(x []byte) [48]byte {
|
||||
var y [48]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes64 is a convenience method for converting a byte slice to a fix
|
||||
// sized 64 byte array. This method will truncate the input if it is larger
|
||||
// than 64 bytes.
|
||||
func ToBytes64(x []byte) [64]byte {
|
||||
var y [64]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
|
||||
// ToBytes96 is a convenience method for converting a byte slice to a fix
|
||||
// sized 96 byte array. This method will truncate the input if it is larger
|
||||
// than 96 bytes.
|
||||
func ToBytes96(x []byte) [96]byte {
|
||||
var y [96]byte
|
||||
copy(y[:], x)
|
||||
return y
|
||||
}
|
||||
@@ -2,213 +2,13 @@ package bytesutil_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
)
|
||||
|
||||
func TestToBytes(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0}},
|
||||
{1, []byte{1}},
|
||||
{2, []byte{2}},
|
||||
{253, []byte{253}},
|
||||
{254, []byte{254}},
|
||||
{255, []byte{255}},
|
||||
{0, []byte{0, 0}},
|
||||
{1, []byte{1, 0}},
|
||||
{255, []byte{255, 0}},
|
||||
{256, []byte{0, 1}},
|
||||
{65534, []byte{254, 255}},
|
||||
{65535, []byte{255, 255}},
|
||||
{0, []byte{0, 0, 0}},
|
||||
{255, []byte{255, 0, 0}},
|
||||
{256, []byte{0, 1, 0}},
|
||||
{65535, []byte{255, 255, 0}},
|
||||
{65536, []byte{0, 0, 1}},
|
||||
{16777215, []byte{255, 255, 255}},
|
||||
{0, []byte{0, 0, 0, 0}},
|
||||
{256, []byte{0, 1, 0, 0}},
|
||||
{65536, []byte{0, 0, 1, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1}},
|
||||
{16777217, []byte{1, 0, 0, 1}},
|
||||
{4294967295, []byte{255, 255, 255, 255}},
|
||||
{0, []byte{0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1, 0, 0, 0, 0}},
|
||||
{4294967296, []byte{0, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{4294967297, []byte{1, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{9223372036854775806, []byte{254, 255, 255, 255, 255, 255, 255, 127}},
|
||||
{9223372036854775807, []byte{255, 255, 255, 255, 255, 255, 255, 127}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt.a, len(tt.b))
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes1(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0}},
|
||||
{1, []byte{1}},
|
||||
{2, []byte{2}},
|
||||
{253, []byte{253}},
|
||||
{254, []byte{254}},
|
||||
{255, []byte{255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes1(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes2(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0}},
|
||||
{1, []byte{1, 0}},
|
||||
{255, []byte{255, 0}},
|
||||
{256, []byte{0, 1}},
|
||||
{65534, []byte{254, 255}},
|
||||
{65535, []byte{255, 255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes2(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes3(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0, 0}},
|
||||
{255, []byte{255, 0, 0}},
|
||||
{256, []byte{0, 1, 0}},
|
||||
{65535, []byte{255, 255, 0}},
|
||||
{65536, []byte{0, 0, 1}},
|
||||
{16777215, []byte{255, 255, 255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes3(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes4(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0, 0, 0}},
|
||||
{256, []byte{0, 1, 0, 0}},
|
||||
{65536, []byte{0, 0, 1, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1}},
|
||||
{16777217, []byte{1, 0, 0, 1}},
|
||||
{4294967295, []byte{255, 255, 255, 255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes4(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes8(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1, 0, 0, 0, 0}},
|
||||
{4294967296, []byte{0, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{4294967297, []byte{1, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{9223372036854775806, []byte{254, 255, 255, 255, 255, 255, 255, 127}},
|
||||
{9223372036854775807, []byte{255, 255, 255, 255, 255, 255, 255, 127}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes8(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBool(t *testing.T) {
|
||||
tests := []byte{
|
||||
0,
|
||||
1,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBool(tt)
|
||||
c := bytesutil.FromBool(b)
|
||||
assert.Equal(t, tt, c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes2(t *testing.T) {
|
||||
tests := []uint64{
|
||||
0,
|
||||
1776,
|
||||
96726,
|
||||
(1 << 16) - 1,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt, 2)
|
||||
c := bytesutil.FromBytes2(b)
|
||||
assert.Equal(t, uint16(tt), c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes4(t *testing.T) {
|
||||
tests := []uint64{
|
||||
0,
|
||||
1776,
|
||||
96726,
|
||||
4290997,
|
||||
4294967295, // 2^32 - 1
|
||||
4294967200,
|
||||
3894948296,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt, 4)
|
||||
c := bytesutil.FromBytes4(b)
|
||||
if c != tt {
|
||||
t.Errorf("Wanted %d but got %d", tt, c)
|
||||
}
|
||||
assert.Equal(t, tt, c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes8(t *testing.T) {
|
||||
tests := []uint64{
|
||||
0,
|
||||
1776,
|
||||
96726,
|
||||
4290997,
|
||||
922376854775806,
|
||||
42893720984775807,
|
||||
18446744073709551615,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt, 8)
|
||||
c := bytesutil.FromBytes8(b)
|
||||
assert.Equal(t, tt, c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncate(t *testing.T) {
|
||||
tests := []struct {
|
||||
a []byte
|
||||
@@ -243,166 +43,6 @@ func TestReverse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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 TestMakeEmptyBitfields(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint64ToBytes_RoundTrip(t *testing.T) {
|
||||
for i := uint64(0); i < 10000; i++ {
|
||||
b := bytesutil.Uint64ToBytesBigEndian(i)
|
||||
if got := bytesutil.BytesToUint64BigEndian(b); got != i {
|
||||
t.Error("Round trip did not match original value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsHex(t *testing.T) {
|
||||
tests := []struct {
|
||||
a []byte
|
||||
b bool
|
||||
}{
|
||||
{nil, false},
|
||||
{[]byte(""), false},
|
||||
{[]byte("0x"), false},
|
||||
{[]byte("0x0"), true},
|
||||
{[]byte("foo"), false},
|
||||
{[]byte("1234567890abcDEF"), false},
|
||||
{[]byte("XYZ4567890abcDEF1234567890abcDEF1234567890abcDEF1234567890abcDEF"), false},
|
||||
{[]byte("0x1234567890abcDEF1234567890abcDEF1234567890abcDEF1234567890abcDEF"), true},
|
||||
{[]byte("1234567890abcDEF1234567890abcDEF1234567890abcDEF1234567890abcDEF"), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
isHex := bytesutil.IsHex(tt.a)
|
||||
assert.Equal(t, tt.b, isHex)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeCopyRootAtIndex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -521,94 +161,6 @@ func TestSafeCopy2d32Bytes(t *testing.T) {
|
||||
assert.DeepEqual(t, input, output)
|
||||
}
|
||||
|
||||
func TestZeroRoot(t *testing.T) {
|
||||
input := make([]byte, fieldparams.RootLength)
|
||||
output := bytesutil.ZeroRoot(input)
|
||||
assert.Equal(t, true, output)
|
||||
copy(input[2:], "a")
|
||||
copy(input[3:], "b")
|
||||
output = bytesutil.ZeroRoot(input)
|
||||
assert.Equal(t, false, output)
|
||||
}
|
||||
|
||||
func TestIsRoot(t *testing.T) {
|
||||
input := make([]byte, fieldparams.RootLength)
|
||||
output := bytesutil.IsRoot(input)
|
||||
assert.Equal(t, true, output)
|
||||
}
|
||||
|
||||
func TestIsValidRoot(t *testing.T) {
|
||||
|
||||
zeroRoot := make([]byte, fieldparams.RootLength)
|
||||
|
||||
validRoot := make([]byte, fieldparams.RootLength)
|
||||
validRoot[0] = 'a'
|
||||
|
||||
wrongLengthRoot := make([]byte, fieldparams.RootLength-4)
|
||||
wrongLengthRoot[0] = 'a'
|
||||
|
||||
type args struct {
|
||||
root []byte
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Is ZeroRoot",
|
||||
args: args{
|
||||
root: zeroRoot,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Is ValidRoot",
|
||||
args: args{
|
||||
root: validRoot,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Is NonZeroRoot but not length 32",
|
||||
args: args{
|
||||
root: wrongLengthRoot,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := bytesutil.IsValidRoot(tt.args.root)
|
||||
require.Equal(t, got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint32ToBytes4(t *testing.T) {
|
||||
tests := []struct {
|
||||
value uint32
|
||||
want [4]byte
|
||||
}{
|
||||
{
|
||||
value: 0x01000000,
|
||||
want: [4]byte{1, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
value: 0x00000001,
|
||||
want: [4]byte{0, 0, 0, 1},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("0x%08x", tt.value), func(t *testing.T) {
|
||||
if got := bytesutil.Uint32ToBytes4(tt.value); !bytes.Equal(got[:], tt.want[:]) {
|
||||
t.Errorf("Uint32ToBytes4() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytes48Array(t *testing.T) {
|
||||
tests := []struct {
|
||||
a [][]byte
|
||||
@@ -655,18 +207,9 @@ func TestToBytes20(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLittleEndianBytesToBigInt(t *testing.T) {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, 1234567890)
|
||||
converted := bytesutil.LittleEndianBytesToBigInt(bytes)
|
||||
expected := new(big.Int).SetInt64(1234567890)
|
||||
assert.DeepEqual(t, expected, converted)
|
||||
}
|
||||
|
||||
func TestBigIntToLittleEndianBytes(t *testing.T) {
|
||||
expected := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(expected, 1234567890)
|
||||
bigInt := new(big.Int).SetUint64(1234567890)
|
||||
converted := bytesutil.BigIntToLittleEndianBytes(bigInt)
|
||||
assert.DeepEqual(t, expected, converted)
|
||||
func BenchmarkToBytes32(b *testing.B) {
|
||||
x := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
for i := 0; i < b.N; i++ {
|
||||
bytesutil.ToBytes32(x)
|
||||
}
|
||||
}
|
||||
|
||||
46
encoding/bytesutil/eth_types.go
Normal file
46
encoding/bytesutil/eth_types.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package bytesutil
|
||||
|
||||
import (
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
)
|
||||
|
||||
// EpochToBytesLittleEndian conversion.
|
||||
func EpochToBytesLittleEndian(i types.Epoch) []byte {
|
||||
return Uint64ToBytesLittleEndian(uint64(i))
|
||||
}
|
||||
|
||||
// EpochToBytesBigEndian conversion.
|
||||
func EpochToBytesBigEndian(i types.Epoch) []byte {
|
||||
return Uint64ToBytesBigEndian(uint64(i))
|
||||
}
|
||||
|
||||
// BytesToEpochBigEndian conversion.
|
||||
func BytesToEpochBigEndian(b []byte) types.Epoch {
|
||||
return types.Epoch(BytesToUint64BigEndian(b))
|
||||
}
|
||||
|
||||
// SlotToBytesBigEndian conversion.
|
||||
func SlotToBytesBigEndian(i types.Slot) []byte {
|
||||
return Uint64ToBytesBigEndian(uint64(i))
|
||||
}
|
||||
|
||||
// BytesToSlotBigEndian conversion.
|
||||
func BytesToSlotBigEndian(b []byte) types.Slot {
|
||||
return types.Slot(BytesToUint64BigEndian(b))
|
||||
}
|
||||
|
||||
// ZeroRoot returns whether or not a root is of proper length and non-zero hash.
|
||||
func ZeroRoot(root []byte) bool {
|
||||
return string(make([]byte, fieldparams.RootLength)) == string(root)
|
||||
}
|
||||
|
||||
// IsRoot checks whether the byte array is a root.
|
||||
func IsRoot(root []byte) bool {
|
||||
return len(root) == fieldparams.RootLength
|
||||
}
|
||||
|
||||
// IsValidRoot checks whether the byte array is a valid root.
|
||||
func IsValidRoot(root []byte) bool {
|
||||
return IsRoot(root) && !ZeroRoot(root)
|
||||
}
|
||||
75
encoding/bytesutil/eth_types_test.go
Normal file
75
encoding/bytesutil/eth_types_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package bytesutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
)
|
||||
|
||||
func TestZeroRoot(t *testing.T) {
|
||||
input := make([]byte, fieldparams.RootLength)
|
||||
output := bytesutil.ZeroRoot(input)
|
||||
assert.Equal(t, true, output)
|
||||
copy(input[2:], "a")
|
||||
copy(input[3:], "b")
|
||||
output = bytesutil.ZeroRoot(input)
|
||||
assert.Equal(t, false, output)
|
||||
}
|
||||
|
||||
func TestIsRoot(t *testing.T) {
|
||||
input := make([]byte, fieldparams.RootLength)
|
||||
output := bytesutil.IsRoot(input)
|
||||
assert.Equal(t, true, output)
|
||||
}
|
||||
|
||||
func TestIsValidRoot(t *testing.T) {
|
||||
|
||||
zeroRoot := make([]byte, fieldparams.RootLength)
|
||||
|
||||
validRoot := make([]byte, fieldparams.RootLength)
|
||||
validRoot[0] = 'a'
|
||||
|
||||
wrongLengthRoot := make([]byte, fieldparams.RootLength-4)
|
||||
wrongLengthRoot[0] = 'a'
|
||||
|
||||
type args struct {
|
||||
root []byte
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Is ZeroRoot",
|
||||
args: args{
|
||||
root: zeroRoot,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Is ValidRoot",
|
||||
args: args{
|
||||
root: validRoot,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Is NonZeroRoot but not length 32",
|
||||
args: args{
|
||||
root: wrongLengthRoot,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := bytesutil.IsValidRoot(tt.args.root)
|
||||
require.Equal(t, got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
13
encoding/bytesutil/hex.go
Normal file
13
encoding/bytesutil/hex.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package bytesutil
|
||||
|
||||
import "regexp"
|
||||
|
||||
var hexRegex = regexp.MustCompile("^0x[0-9a-fA-F]+$")
|
||||
|
||||
// IsHex checks whether the byte array is a hex number prefixed with '0x'.
|
||||
func IsHex(b []byte) bool {
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
return hexRegex.Match(b)
|
||||
}
|
||||
29
encoding/bytesutil/hex_test.go
Normal file
29
encoding/bytesutil/hex_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package bytesutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
)
|
||||
|
||||
func TestIsHex(t *testing.T) {
|
||||
tests := []struct {
|
||||
a []byte
|
||||
b bool
|
||||
}{
|
||||
{nil, false},
|
||||
{[]byte(""), false},
|
||||
{[]byte("0x"), false},
|
||||
{[]byte("0x0"), true},
|
||||
{[]byte("foo"), false},
|
||||
{[]byte("1234567890abcDEF"), false},
|
||||
{[]byte("XYZ4567890abcDEF1234567890abcDEF1234567890abcDEF1234567890abcDEF"), false},
|
||||
{[]byte("0x1234567890abcDEF1234567890abcDEF1234567890abcDEF1234567890abcDEF"), true},
|
||||
{[]byte("1234567890abcDEF1234567890abcDEF1234567890abcDEF1234567890abcDEF"), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
isHex := bytesutil.IsHex(tt.a)
|
||||
assert.Equal(t, tt.b, isHex)
|
||||
}
|
||||
}
|
||||
144
encoding/bytesutil/integers.go
Normal file
144
encoding/bytesutil/integers.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package bytesutil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// ToBytes returns integer x to bytes in little-endian format at the specified length.
|
||||
// Spec defines similar method uint_to_bytes(n: uint) -> bytes, which is equivalent to ToBytes(n, 8).
|
||||
func ToBytes(x uint64, length int) []byte {
|
||||
if length < 0 {
|
||||
length = 0
|
||||
}
|
||||
makeLength := length
|
||||
if length < 8 {
|
||||
makeLength = 8
|
||||
}
|
||||
bytes := make([]byte, makeLength)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:length]
|
||||
}
|
||||
|
||||
// Bytes1 returns integer x to bytes in little-endian format, x.to_bytes(1, 'little').
|
||||
func Bytes1(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:1]
|
||||
}
|
||||
|
||||
// Bytes2 returns integer x to bytes in little-endian format, x.to_bytes(2, 'little').
|
||||
func Bytes2(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:2]
|
||||
}
|
||||
|
||||
// Bytes3 returns integer x to bytes in little-endian format, x.to_bytes(3, 'little').
|
||||
func Bytes3(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:3]
|
||||
}
|
||||
|
||||
// Bytes4 returns integer x to bytes in little-endian format, x.to_bytes(4, 'little').
|
||||
func Bytes4(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes[:4]
|
||||
}
|
||||
|
||||
// Bytes8 returns integer x to bytes in little-endian format, x.to_bytes(8, 'little').
|
||||
func Bytes8(x uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// Bytes32 returns integer x to bytes in little-endian format, x.to_bytes(32, 'little').
|
||||
func Bytes32(x uint64) []byte {
|
||||
bytes := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(bytes, x)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// FromBytes2 returns an integer which is stored in the little-endian format(2, 'little')
|
||||
// from a byte array.
|
||||
func FromBytes2(x []byte) uint16 {
|
||||
if len(x) < 2 {
|
||||
return 0
|
||||
}
|
||||
return binary.LittleEndian.Uint16(x[:2])
|
||||
}
|
||||
|
||||
// FromBytes4 returns an integer which is stored in the little-endian format(4, 'little')
|
||||
// from a byte array.
|
||||
func FromBytes4(x []byte) uint64 {
|
||||
if len(x) < 4 {
|
||||
return 0
|
||||
}
|
||||
empty4bytes := make([]byte, 4)
|
||||
return binary.LittleEndian.Uint64(append(x[:4], empty4bytes...))
|
||||
}
|
||||
|
||||
// FromBytes8 returns an integer which is stored in the little-endian format(8, 'little')
|
||||
// from a byte array.
|
||||
func FromBytes8(x []byte) uint64 {
|
||||
if len(x) < 8 {
|
||||
return 0
|
||||
}
|
||||
return binary.LittleEndian.Uint64(x)
|
||||
}
|
||||
|
||||
// ToLowInt64 returns the lowest 8 bytes interpreted as little endian.
|
||||
func ToLowInt64(x []byte) int64 {
|
||||
if len(x) < 8 {
|
||||
return 0
|
||||
}
|
||||
// Use the first 8 bytes.
|
||||
x = x[:8]
|
||||
return int64(binary.LittleEndian.Uint64(x)) // lint:ignore uintcast -- A negative number might be the expected result.
|
||||
}
|
||||
|
||||
// Uint32ToBytes4 is a convenience method for converting uint32 to a fix
|
||||
// sized 4 byte array in big endian order. Returns 4 byte array.
|
||||
func Uint32ToBytes4(i uint32) [4]byte {
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, i)
|
||||
return ToBytes4(buf)
|
||||
}
|
||||
|
||||
// Uint64ToBytesLittleEndian conversion.
|
||||
func Uint64ToBytesLittleEndian(i uint64) []byte {
|
||||
buf := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(buf, i)
|
||||
return buf
|
||||
}
|
||||
|
||||
// Uint64ToBytesBigEndian conversion.
|
||||
func Uint64ToBytesBigEndian(i uint64) []byte {
|
||||
buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf, i)
|
||||
return buf
|
||||
}
|
||||
|
||||
// BytesToUint64BigEndian conversion. Returns 0 if empty bytes or byte slice with length less
|
||||
// than 8.
|
||||
func BytesToUint64BigEndian(b []byte) uint64 {
|
||||
if len(b) < 8 { // This will panic otherwise.
|
||||
return 0
|
||||
}
|
||||
return binary.BigEndian.Uint64(b)
|
||||
}
|
||||
|
||||
// LittleEndianBytesToBigInt takes bytes of a number stored as little-endian and returns a big integer
|
||||
func LittleEndianBytesToBigInt(bytes []byte) *big.Int {
|
||||
// Integers are stored as little-endian, but big.Int expects big-endian. So we need to reverse the byte order before decoding.
|
||||
return new(big.Int).SetBytes(ReverseByteOrder(bytes))
|
||||
}
|
||||
|
||||
// BigIntToLittleEndianBytes takes a big integer and returns its bytes stored as little-endian
|
||||
func BigIntToLittleEndianBytes(bigInt *big.Int) []byte {
|
||||
// big.Int.Bytes() returns bytes in big-endian order, so we need to reverse the byte order
|
||||
return ReverseByteOrder(bigInt.Bytes())
|
||||
}
|
||||
255
encoding/bytesutil/integers_test.go
Normal file
255
encoding/bytesutil/integers_test.go
Normal file
@@ -0,0 +1,255 @@
|
||||
package bytesutil_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
)
|
||||
|
||||
func TestToBytes(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0}},
|
||||
{1, []byte{1}},
|
||||
{2, []byte{2}},
|
||||
{253, []byte{253}},
|
||||
{254, []byte{254}},
|
||||
{255, []byte{255}},
|
||||
{0, []byte{0, 0}},
|
||||
{1, []byte{1, 0}},
|
||||
{255, []byte{255, 0}},
|
||||
{256, []byte{0, 1}},
|
||||
{65534, []byte{254, 255}},
|
||||
{65535, []byte{255, 255}},
|
||||
{0, []byte{0, 0, 0}},
|
||||
{255, []byte{255, 0, 0}},
|
||||
{256, []byte{0, 1, 0}},
|
||||
{65535, []byte{255, 255, 0}},
|
||||
{65536, []byte{0, 0, 1}},
|
||||
{16777215, []byte{255, 255, 255}},
|
||||
{0, []byte{0, 0, 0, 0}},
|
||||
{256, []byte{0, 1, 0, 0}},
|
||||
{65536, []byte{0, 0, 1, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1}},
|
||||
{16777217, []byte{1, 0, 0, 1}},
|
||||
{4294967295, []byte{255, 255, 255, 255}},
|
||||
{0, []byte{0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1, 0, 0, 0, 0}},
|
||||
{4294967296, []byte{0, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{4294967297, []byte{1, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{9223372036854775806, []byte{254, 255, 255, 255, 255, 255, 255, 127}},
|
||||
{9223372036854775807, []byte{255, 255, 255, 255, 255, 255, 255, 127}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt.a, len(tt.b))
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes1(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0}},
|
||||
{1, []byte{1}},
|
||||
{2, []byte{2}},
|
||||
{253, []byte{253}},
|
||||
{254, []byte{254}},
|
||||
{255, []byte{255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes1(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes2(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0}},
|
||||
{1, []byte{1, 0}},
|
||||
{255, []byte{255, 0}},
|
||||
{256, []byte{0, 1}},
|
||||
{65534, []byte{254, 255}},
|
||||
{65535, []byte{255, 255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes2(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes3(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0, 0}},
|
||||
{255, []byte{255, 0, 0}},
|
||||
{256, []byte{0, 1, 0}},
|
||||
{65535, []byte{255, 255, 0}},
|
||||
{65536, []byte{0, 0, 1}},
|
||||
{16777215, []byte{255, 255, 255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes3(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes4(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0, 0, 0}},
|
||||
{256, []byte{0, 1, 0, 0}},
|
||||
{65536, []byte{0, 0, 1, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1}},
|
||||
{16777217, []byte{1, 0, 0, 1}},
|
||||
{4294967295, []byte{255, 255, 255, 255}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes4(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes8(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b []byte
|
||||
}{
|
||||
{0, []byte{0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{16777216, []byte{0, 0, 0, 1, 0, 0, 0, 0}},
|
||||
{4294967296, []byte{0, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{4294967297, []byte{1, 0, 0, 0, 1, 0, 0, 0}},
|
||||
{9223372036854775806, []byte{254, 255, 255, 255, 255, 255, 255, 127}},
|
||||
{9223372036854775807, []byte{255, 255, 255, 255, 255, 255, 255, 127}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.Bytes8(tt.a)
|
||||
assert.DeepEqual(t, tt.b, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBool(t *testing.T) {
|
||||
tests := []byte{
|
||||
0,
|
||||
1,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBool(tt)
|
||||
c := bytesutil.FromBool(b)
|
||||
assert.Equal(t, tt, c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes2(t *testing.T) {
|
||||
tests := []uint64{
|
||||
0,
|
||||
1776,
|
||||
96726,
|
||||
(1 << 16) - 1,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt, 2)
|
||||
c := bytesutil.FromBytes2(b)
|
||||
assert.Equal(t, uint16(tt), c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes4(t *testing.T) {
|
||||
tests := []uint64{
|
||||
0,
|
||||
1776,
|
||||
96726,
|
||||
4290997,
|
||||
4294967295, // 2^32 - 1
|
||||
4294967200,
|
||||
3894948296,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt, 4)
|
||||
c := bytesutil.FromBytes4(b)
|
||||
if c != tt {
|
||||
t.Errorf("Wanted %d but got %d", tt, c)
|
||||
}
|
||||
assert.Equal(t, tt, c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes8(t *testing.T) {
|
||||
tests := []uint64{
|
||||
0,
|
||||
1776,
|
||||
96726,
|
||||
4290997,
|
||||
922376854775806,
|
||||
42893720984775807,
|
||||
18446744073709551615,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := bytesutil.ToBytes(tt, 8)
|
||||
c := bytesutil.FromBytes8(b)
|
||||
assert.Equal(t, tt, c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint32ToBytes4(t *testing.T) {
|
||||
tests := []struct {
|
||||
value uint32
|
||||
want [4]byte
|
||||
}{
|
||||
{
|
||||
value: 0x01000000,
|
||||
want: [4]byte{1, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
value: 0x00000001,
|
||||
want: [4]byte{0, 0, 0, 1},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("0x%08x", tt.value), func(t *testing.T) {
|
||||
if got := bytesutil.Uint32ToBytes4(tt.value); !bytes.Equal(got[:], tt.want[:]) {
|
||||
t.Errorf("Uint32ToBytes4() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint64ToBytes_RoundTrip(t *testing.T) {
|
||||
for i := uint64(0); i < 10000; i++ {
|
||||
b := bytesutil.Uint64ToBytesBigEndian(i)
|
||||
if got := bytesutil.BytesToUint64BigEndian(b); got != i {
|
||||
t.Error("Round trip did not match original value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLittleEndianBytesToBigInt(t *testing.T) {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, 1234567890)
|
||||
converted := bytesutil.LittleEndianBytesToBigInt(bytes)
|
||||
expected := new(big.Int).SetInt64(1234567890)
|
||||
assert.DeepEqual(t, expected, converted)
|
||||
}
|
||||
|
||||
func TestBigIntToLittleEndianBytes(t *testing.T) {
|
||||
expected := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(expected, 1234567890)
|
||||
bigInt := new(big.Int).SetUint64(1234567890)
|
||||
converted := bytesutil.BigIntToLittleEndianBytes(bigInt)
|
||||
assert.DeepEqual(t, expected, converted)
|
||||
}
|
||||
Reference in New Issue
Block a user