conv.go raw

   1  package conv
   2  
   3  import (
   4  	"encoding/binary"
   5  	"fmt"
   6  	"math/big"
   7  	"strings"
   8  
   9  	"golang.org/x/crypto/cryptobyte"
  10  )
  11  
  12  // BytesLe2Hex returns an hexadecimal string of a number stored in a
  13  // little-endian order slice x.
  14  func BytesLe2Hex(x []byte) string {
  15  	b := &strings.Builder{}
  16  	b.Grow(2*len(x) + 2)
  17  	fmt.Fprint(b, "0x")
  18  	if len(x) == 0 {
  19  		fmt.Fprint(b, "00")
  20  	}
  21  	for i := len(x) - 1; i >= 0; i-- {
  22  		fmt.Fprintf(b, "%02x", x[i])
  23  	}
  24  	return b.String()
  25  }
  26  
  27  // BytesLe2BigInt converts a little-endian slice x into a big-endian
  28  // math/big.Int.
  29  func BytesLe2BigInt(x []byte) *big.Int {
  30  	n := len(x)
  31  	b := new(big.Int)
  32  	if len(x) > 0 {
  33  		y := make([]byte, n)
  34  		for i := 0; i < n; i++ {
  35  			y[n-1-i] = x[i]
  36  		}
  37  		b.SetBytes(y)
  38  	}
  39  	return b
  40  }
  41  
  42  // BytesBe2Uint64Le converts a big-endian slice x to a little-endian slice of uint64.
  43  func BytesBe2Uint64Le(x []byte) []uint64 {
  44  	l := len(x)
  45  	z := make([]uint64, (l+7)/8)
  46  	blocks := l / 8
  47  	for i := 0; i < blocks; i++ {
  48  		z[i] = binary.BigEndian.Uint64(x[l-8*(i+1):])
  49  	}
  50  	remBytes := l % 8
  51  	for i := 0; i < remBytes; i++ {
  52  		z[blocks] |= uint64(x[l-1-8*blocks-i]) << uint(8*i)
  53  	}
  54  	return z
  55  }
  56  
  57  // BigInt2BytesLe stores a positive big.Int number x into a little-endian slice z.
  58  // The slice is modified if the bitlength of x <= 8*len(z) (padding with zeros).
  59  // If x does not fit in the slice or is negative, z is not modified.
  60  func BigInt2BytesLe(z []byte, x *big.Int) {
  61  	xLen := (x.BitLen() + 7) >> 3
  62  	zLen := len(z)
  63  	if zLen >= xLen && x.Sign() >= 0 {
  64  		y := x.Bytes()
  65  		for i := 0; i < xLen; i++ {
  66  			z[i] = y[xLen-1-i]
  67  		}
  68  		for i := xLen; i < zLen; i++ {
  69  			z[i] = 0
  70  		}
  71  	}
  72  }
  73  
  74  // Uint64Le2BigInt converts a little-endian slice x into a big number.
  75  func Uint64Le2BigInt(x []uint64) *big.Int {
  76  	n := len(x)
  77  	b := new(big.Int)
  78  	var bi big.Int
  79  	for i := n - 1; i >= 0; i-- {
  80  		bi.SetUint64(x[i])
  81  		b.Lsh(b, 64)
  82  		b.Add(b, &bi)
  83  	}
  84  	return b
  85  }
  86  
  87  // Uint64Le2BytesLe converts a little-endian slice x to a little-endian slice of bytes.
  88  func Uint64Le2BytesLe(x []uint64) []byte {
  89  	b := make([]byte, 8*len(x))
  90  	n := len(x)
  91  	for i := 0; i < n; i++ {
  92  		binary.LittleEndian.PutUint64(b[i*8:], x[i])
  93  	}
  94  	return b
  95  }
  96  
  97  // Uint64Le2BytesBe converts a little-endian slice x to a big-endian slice of bytes.
  98  func Uint64Le2BytesBe(x []uint64) []byte {
  99  	b := make([]byte, 8*len(x))
 100  	n := len(x)
 101  	for i := 0; i < n; i++ {
 102  		binary.BigEndian.PutUint64(b[i*8:], x[n-1-i])
 103  	}
 104  	return b
 105  }
 106  
 107  // Uint64Le2Hex returns an hexadecimal string of a number stored in a
 108  // little-endian order slice x.
 109  func Uint64Le2Hex(x []uint64) string {
 110  	b := new(strings.Builder)
 111  	b.Grow(16*len(x) + 2)
 112  	fmt.Fprint(b, "0x")
 113  	if len(x) == 0 {
 114  		fmt.Fprint(b, "00")
 115  	}
 116  	for i := len(x) - 1; i >= 0; i-- {
 117  		fmt.Fprintf(b, "%016x", x[i])
 118  	}
 119  	return b.String()
 120  }
 121  
 122  // BigInt2Uint64Le stores a positive big.Int number x into a little-endian slice z.
 123  // The slice is modified if the bitlength of x <= 8*len(z) (padding with zeros).
 124  // If x does not fit in the slice or is negative, z is not modified.
 125  func BigInt2Uint64Le(z []uint64, x *big.Int) {
 126  	xLen := (x.BitLen() + 63) >> 6 // number of 64-bit words
 127  	zLen := len(z)
 128  	if zLen >= xLen && x.Sign() > 0 {
 129  		var y, yi big.Int
 130  		y.Set(x)
 131  		two64 := big.NewInt(1)
 132  		two64.Lsh(two64, 64).Sub(two64, big.NewInt(1))
 133  		for i := 0; i < xLen; i++ {
 134  			yi.And(&y, two64)
 135  			z[i] = yi.Uint64()
 136  			y.Rsh(&y, 64)
 137  		}
 138  	}
 139  	for i := xLen; i < zLen; i++ {
 140  		z[i] = 0
 141  	}
 142  }
 143  
 144  // MarshalBinary encodes a value into a byte array in a format readable by UnmarshalBinary.
 145  func MarshalBinary(v cryptobyte.MarshalingValue) ([]byte, error) {
 146  	const DefaultSize = 32
 147  	b := cryptobyte.NewBuilder(make([]byte, 0, DefaultSize))
 148  	b.AddValue(v)
 149  	return b.Bytes()
 150  }
 151  
 152  // MarshalBinaryLen encodes a value into an array of n bytes in a format readable by UnmarshalBinary.
 153  func MarshalBinaryLen(v cryptobyte.MarshalingValue, length uint) ([]byte, error) {
 154  	b := cryptobyte.NewFixedBuilder(make([]byte, 0, length))
 155  	b.AddValue(v)
 156  	return b.Bytes()
 157  }
 158  
 159  // A UnmarshalingValue decodes itself from a cryptobyte.String and advances the pointer.
 160  // It reports whether the read was successful.
 161  type UnmarshalingValue interface {
 162  	Unmarshal(*cryptobyte.String) bool
 163  }
 164  
 165  // UnmarshalBinary recovers a value from a byte array.
 166  // It returns an error if the read was unsuccessful.
 167  func UnmarshalBinary(v UnmarshalingValue, data []byte) (err error) {
 168  	s := cryptobyte.String(data)
 169  	if data == nil || !v.Unmarshal(&s) || !s.Empty() {
 170  		err = fmt.Errorf("cannot read %T from input string", v)
 171  	}
 172  	return
 173  }
 174