github.com/prysmaticlabs/prysm@v1.4.4/shared/bytesutil/bytes.go (about)

     1  // Package bytesutil defines helper methods for converting integers to byte slices.
     2  package bytesutil
     3  
     4  import (
     5  	"encoding/binary"
     6  	"errors"
     7  	"math/bits"
     8  	"regexp"
     9  
    10  	types "github.com/prysmaticlabs/eth2-types"
    11  )
    12  
    13  // ToBytes returns integer x to bytes in little-endian format at the specified length.
    14  // Spec defines similar method uint_to_bytes(n: uint) -> bytes, which is equivalent to ToBytes(n, 8).
    15  func ToBytes(x uint64, length int) []byte {
    16  	makeLength := length
    17  	if length < 8 {
    18  		makeLength = 8
    19  	}
    20  	bytes := make([]byte, makeLength)
    21  	binary.LittleEndian.PutUint64(bytes, x)
    22  	return bytes[:length]
    23  }
    24  
    25  // Bytes1 returns integer x to bytes in little-endian format, x.to_bytes(1, 'little').
    26  func Bytes1(x uint64) []byte {
    27  	bytes := make([]byte, 8)
    28  	binary.LittleEndian.PutUint64(bytes, x)
    29  	return bytes[:1]
    30  }
    31  
    32  // Bytes2 returns integer x to bytes in little-endian format, x.to_bytes(2, 'little').
    33  func Bytes2(x uint64) []byte {
    34  	bytes := make([]byte, 8)
    35  	binary.LittleEndian.PutUint64(bytes, x)
    36  	return bytes[:2]
    37  }
    38  
    39  // Bytes3 returns integer x to bytes in little-endian format, x.to_bytes(3, 'little').
    40  func Bytes3(x uint64) []byte {
    41  	bytes := make([]byte, 8)
    42  	binary.LittleEndian.PutUint64(bytes, x)
    43  	return bytes[:3]
    44  }
    45  
    46  // Bytes4 returns integer x to bytes in little-endian format, x.to_bytes(4, 'little').
    47  func Bytes4(x uint64) []byte {
    48  	bytes := make([]byte, 8)
    49  	binary.LittleEndian.PutUint64(bytes, x)
    50  	return bytes[:4]
    51  }
    52  
    53  // Bytes8 returns integer x to bytes in little-endian format, x.to_bytes(8, 'little').
    54  func Bytes8(x uint64) []byte {
    55  	bytes := make([]byte, 8)
    56  	binary.LittleEndian.PutUint64(bytes, x)
    57  	return bytes
    58  }
    59  
    60  // Bytes32 returns integer x to bytes in little-endian format, x.to_bytes(32, 'little').
    61  func Bytes32(x uint64) []byte {
    62  	bytes := make([]byte, 32)
    63  	binary.LittleEndian.PutUint64(bytes, x)
    64  	return bytes
    65  }
    66  
    67  // FromBytes4 returns an integer which is stored in the little-endian format(4, 'little')
    68  // from a byte array.
    69  func FromBytes4(x []byte) uint64 {
    70  	empty4bytes := make([]byte, 4)
    71  	return binary.LittleEndian.Uint64(append(x[:4], empty4bytes...))
    72  }
    73  
    74  // FromBytes8 returns an integer which is stored in the little-endian format(8, 'little')
    75  // from a byte array.
    76  func FromBytes8(x []byte) uint64 {
    77  	return binary.LittleEndian.Uint64(x)
    78  }
    79  
    80  // ToBytes4 is a convenience method for converting a byte slice to a fix
    81  // sized 4 byte array. This method will truncate the input if it is larger
    82  // than 4 bytes.
    83  func ToBytes4(x []byte) [4]byte {
    84  	var y [4]byte
    85  	copy(y[:], x)
    86  	return y
    87  }
    88  
    89  // ToBytes32 is a convenience method for converting a byte slice to a fix
    90  // sized 32 byte array. This method will truncate the input if it is larger
    91  // than 32 bytes.
    92  func ToBytes32(x []byte) [32]byte {
    93  	var y [32]byte
    94  	copy(y[:], x)
    95  	return y
    96  }
    97  
    98  // ToBytes48 is a convenience method for converting a byte slice to a fix
    99  // sized 48 byte array. This method will truncate the input if it is larger
   100  // than 48 bytes.
   101  func ToBytes48(x []byte) [48]byte {
   102  	var y [48]byte
   103  	copy(y[:], x)
   104  	return y
   105  }
   106  
   107  // ToBytes64 is a convenience method for converting a byte slice to a fix
   108  // sized 64 byte array. This method will truncate the input if it is larger
   109  // than 64 bytes.
   110  func ToBytes64(x []byte) [64]byte {
   111  	var y [64]byte
   112  	copy(y[:], x)
   113  	return y
   114  }
   115  
   116  // ToBool is a convenience method for converting a byte to a bool.
   117  // This method will use the first bit of the 0 byte to generate the returned value.
   118  func ToBool(x byte) bool {
   119  	return x&1 == 1
   120  }
   121  
   122  // FromBytes2 returns an integer which is stored in the little-endian format(2, 'little')
   123  // from a byte array.
   124  func FromBytes2(x []byte) uint16 {
   125  	return binary.LittleEndian.Uint16(x[:2])
   126  }
   127  
   128  // FromBool is a convenience method for converting a bool to a byte.
   129  // This method will use the first bit to generate the returned value.
   130  func FromBool(x bool) byte {
   131  	if x {
   132  		return 1
   133  	}
   134  	return 0
   135  }
   136  
   137  // FromBytes48 is a convenience method for converting a fixed-size byte array
   138  // to a byte slice.
   139  func FromBytes48(x [48]byte) []byte {
   140  	return x[:]
   141  }
   142  
   143  // FromBytes48Array is a convenience method for converting an array of
   144  // fixed-size byte arrays to an array of byte slices.
   145  func FromBytes48Array(x [][48]byte) [][]byte {
   146  	y := make([][]byte, len(x))
   147  	for i := range x {
   148  		y[i] = x[i][:]
   149  	}
   150  	return y
   151  }
   152  
   153  // Trunc truncates the byte slices to 6 bytes.
   154  func Trunc(x []byte) []byte {
   155  	if len(x) > 6 {
   156  		return x[:6]
   157  	}
   158  	return x
   159  }
   160  
   161  // ToLowInt64 returns the lowest 8 bytes interpreted as little endian.
   162  func ToLowInt64(x []byte) int64 {
   163  	if len(x) > 8 {
   164  		x = x[:8]
   165  	}
   166  	return int64(binary.LittleEndian.Uint64(x))
   167  }
   168  
   169  // SafeCopyBytes will copy and return a non-nil byte array, otherwise it returns nil.
   170  func SafeCopyBytes(cp []byte) []byte {
   171  	if cp != nil {
   172  		copied := make([]byte, len(cp))
   173  		copy(copied, cp)
   174  		return copied
   175  	}
   176  	return nil
   177  }
   178  
   179  // Copy2dBytes will copy and return a non-nil 2d byte array, otherwise it returns nil.
   180  func Copy2dBytes(ary [][]byte) [][]byte {
   181  	if ary != nil {
   182  		copied := make([][]byte, len(ary))
   183  		for i, a := range ary {
   184  			copied[i] = SafeCopyBytes(a)
   185  		}
   186  		return copied
   187  	}
   188  	return nil
   189  }
   190  
   191  // ReverseBytes32Slice will reverse the provided slice's order.
   192  func ReverseBytes32Slice(arr [][32]byte) [][32]byte {
   193  	for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
   194  		arr[i], arr[j] = arr[j], arr[i]
   195  	}
   196  	return arr
   197  }
   198  
   199  // PadTo pads a byte slice to the given size. If the byte slice is larger than the given size, the
   200  // original slice is returned.
   201  func PadTo(b []byte, size int) []byte {
   202  	if len(b) > size {
   203  		return b
   204  	}
   205  	return append(b, make([]byte, size-len(b))...)
   206  }
   207  
   208  // SetBit sets the index `i` of bitlist `b` to 1.
   209  // It grows and returns a longer bitlist with 1 set
   210  // if index `i` is out of range.
   211  func SetBit(b []byte, i int) []byte {
   212  	if i >= len(b)*8 {
   213  		h := (i + (8 - i%8)) / 8
   214  		b = append(b, make([]byte, h-len(b))...)
   215  	}
   216  
   217  	bit := uint8(1 << (i % 8))
   218  	b[i/8] |= bit
   219  	return b
   220  }
   221  
   222  // ClearBit clears the index `i` of bitlist `b`.
   223  // Returns the original bitlist if the index `i`
   224  // is out of range.
   225  func ClearBit(b []byte, i int) []byte {
   226  	if i >= len(b)*8 {
   227  		return b
   228  	}
   229  
   230  	bit := uint8(1 << (i % 8))
   231  	b[i/8] &^= bit
   232  	return b
   233  }
   234  
   235  // MakeEmptyBitlists returns an empty bitlist with
   236  // input size `i`.
   237  func MakeEmptyBitlists(i int) []byte {
   238  	return make([]byte, (i+(8-i%8))/8)
   239  }
   240  
   241  // HighestBitIndex returns the index of the highest
   242  // bit set from bitlist `b`.
   243  func HighestBitIndex(b []byte) (int, error) {
   244  	if len(b) == 0 {
   245  		return 0, errors.New("input list can't be empty or nil")
   246  	}
   247  
   248  	for i := len(b) - 1; i >= 0; i-- {
   249  		if b[i] == 0 {
   250  			continue
   251  		}
   252  		return bits.Len8(b[i]) + (i * 8), nil
   253  	}
   254  
   255  	return 0, nil
   256  }
   257  
   258  // HighestBitIndexAt returns the index of the highest
   259  // bit set from bitlist `b` that is at `index` (inclusive).
   260  func HighestBitIndexAt(b []byte, index int) (int, error) {
   261  	bLength := len(b)
   262  	if b == nil || bLength == 0 {
   263  		return 0, errors.New("input list can't be empty or nil")
   264  	}
   265  
   266  	start := index / 8
   267  	if start >= bLength {
   268  		start = bLength - 1
   269  	}
   270  
   271  	mask := byte(1<<(index%8) - 1)
   272  	for i := start; i >= 0; i-- {
   273  		if index/8 > i {
   274  			mask = 0xff
   275  		}
   276  		masked := b[i] & mask
   277  		minBitsMasked := bits.Len8(masked)
   278  		if b[i] == 0 || (minBitsMasked == 0 && index/8 <= i) {
   279  			continue
   280  		}
   281  
   282  		return minBitsMasked + (i * 8), nil
   283  	}
   284  
   285  	return 0, nil
   286  }
   287  
   288  // Uint64ToBytesLittleEndian conversion.
   289  func Uint64ToBytesLittleEndian(i uint64) []byte {
   290  	buf := make([]byte, 8)
   291  	binary.LittleEndian.PutUint64(buf, i)
   292  	return buf
   293  }
   294  
   295  // Uint64ToBytesBigEndian conversion.
   296  func Uint64ToBytesBigEndian(i uint64) []byte {
   297  	buf := make([]byte, 8)
   298  	binary.BigEndian.PutUint64(buf, i)
   299  	return buf
   300  }
   301  
   302  // BytesToUint64BigEndian conversion. Returns 0 if empty bytes or byte slice with length less
   303  // than 8.
   304  func BytesToUint64BigEndian(b []byte) uint64 {
   305  	if len(b) < 8 { // This will panic otherwise.
   306  		return 0
   307  	}
   308  	return binary.BigEndian.Uint64(b)
   309  }
   310  
   311  // EpochToBytesLittleEndian conversion.
   312  func EpochToBytesLittleEndian(i types.Epoch) []byte {
   313  	return Uint64ToBytesLittleEndian(uint64(i))
   314  }
   315  
   316  // EpochToBytesBigEndian conversion.
   317  func EpochToBytesBigEndian(i types.Epoch) []byte {
   318  	return Uint64ToBytesBigEndian(uint64(i))
   319  }
   320  
   321  // BytesToEpochBigEndian conversion.
   322  func BytesToEpochBigEndian(b []byte) types.Epoch {
   323  	return types.Epoch(BytesToUint64BigEndian(b))
   324  }
   325  
   326  // SlotToBytesBigEndian conversion.
   327  func SlotToBytesBigEndian(i types.Slot) []byte {
   328  	return Uint64ToBytesBigEndian(uint64(i))
   329  }
   330  
   331  // BytesToSlotBigEndian conversion.
   332  func BytesToSlotBigEndian(b []byte) types.Slot {
   333  	return types.Slot(BytesToUint64BigEndian(b))
   334  }
   335  
   336  // IsHex checks whether the byte array is a hex number prefixed with '0x'.
   337  func IsHex(b []byte) (bool, error) {
   338  	if b == nil {
   339  		return false, nil
   340  	}
   341  	return regexp.Match("^(0x)[0-9a-fA-F]+$", b)
   342  }