package hexutil import ( "encoding/hex" "fmt" "math/big" "strconv" ) const uintBits = 32 << (uint64(^uint(0)) >> 63) // These errors are from go-ethereum in order to keep compatibility with geth error codes. var ( ErrEmptyString = &decError{"empty hex string"} ErrSyntax = &decError{"invalid hex string"} ErrMissingPrefix = &decError{"hex string without 0x prefix"} ErrOddLength = &decError{"hex string of odd length"} ErrEmptyNumber = &decError{"hex string \"0x\""} ErrLeadingZero = &decError{"hex number with leading zero digits"} ErrUint64Range = &decError{"hex number > 64 bits"} ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)} ErrBig256Range = &decError{"hex number > 256 bits"} ) type decError struct{ msg string } func (err decError) Error() string { return err.msg } // Decode decodes a hex string with 0x prefix. func Decode(input string) ([]byte, error) { if len(input) == 0 { return nil, ErrEmptyString } if !has0xPrefix(input) { return nil, ErrMissingPrefix } b, err := hex.DecodeString(input[2:]) if err != nil { return nil, mapError(err) } return b, nil } // MustDecode decodes a hex string with 0x prefix. It panics for invalid input. func MustDecode(input string) []byte { dec, err := Decode(input) if err != nil { panic(err) } return dec } // DecodeUint64 decodes a hex string with 0x prefix as a quantity. func DecodeUint64(input string) (uint64, error) { raw, err := checkNumber(input) if err != nil { return 0, err } dec, err := strconv.ParseUint(raw, 16, 64) if err != nil { return 0, mapError(err) } return dec, nil } // EncodeUint64 encodes i as a hex string with 0x prefix. func EncodeUint64(i uint64) string { enc := make([]byte, 2, 10) copy(enc, "0x") return string(strconv.AppendUint(enc, i, 16)) } var bigWordNibbles int func init() { // This is a weird way to compute the number of nibbles required for big.Word. // The usual way would be to use constant arithmetic but go vet can't handle that. b, _ := new(big.Int).SetString("FFFFFFFFFF", 16) switch len(b.Bits()) { case 1: bigWordNibbles = 16 case 2: bigWordNibbles = 8 default: panic("weird big.Word size") } } // DecodeBig decodes a hex string with 0x prefix as a quantity. // Numbers larger than 256 bits are not accepted. func DecodeBig(input string) (*big.Int, error) { raw, err := checkNumber(input) if err != nil { return nil, err } if len(raw) > 64 { return nil, ErrBig256Range } words := make([]big.Word, len(raw)/bigWordNibbles+1) end := len(raw) for i := range words { start := end - bigWordNibbles if start < 0 { start = 0 } for ri := start; ri < end; ri++ { nib := decodeNibble(raw[ri]) if nib == badNibble { return nil, ErrSyntax } words[i] *= 16 words[i] += big.Word(nib) } end = start } dec := new(big.Int).SetBits(words) return dec, nil } // MustDecodeBig decodes a hex string with 0x prefix as a quantity. // It panics for invalid input. func MustDecodeBig(input string) *big.Int { dec, err := DecodeBig(input) if err != nil { panic(err) } return dec } // EncodeBig encodes bigint as a hex string with 0x prefix. // The sign of the integer is ignored. func EncodeBig(bigint *big.Int) string { nbits := bigint.BitLen() if nbits == 0 { return "0x0" } return fmt.Sprintf("%#x", bigint) } func has0xPrefix(input string) bool { return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') } func checkNumber(input string) (raw string, err error) { if len(input) == 0 { return "", ErrEmptyString } if !has0xPrefix(input) { return "", ErrMissingPrefix } input = input[2:] if len(input) == 0 { return "", ErrEmptyNumber } if len(input) > 1 && input[0] == '0' { return "", ErrLeadingZero } return input, nil } const badNibble = ^uint64(0) func decodeNibble(in byte) uint64 { switch { case in >= '0' && in <= '9': return uint64(in - '0') case in >= 'A' && in <= 'F': return uint64(in - 'A' + 10) case in >= 'a' && in <= 'f': return uint64(in - 'a' + 10) default: return badNibble } } // ignore these errors to keep compatiblity with go ethereum // nolint:errorlint func mapError(err error) error { if err, ok := err.(*strconv.NumError); ok { switch err.Err { case strconv.ErrRange: return ErrUint64Range case strconv.ErrSyntax: return ErrSyntax } } if _, ok := err.(hex.InvalidByteError); ok { return ErrSyntax } if err == hex.ErrLength { return ErrOddLength } return err } // CompressNibbles - supports only even number of nibbles // // HI_NIBBLE(b) = (b >> 4) & 0x0F // LO_NIBBLE(b) = b & 0x0F func CompressNibbles(nibbles []byte, out *[]byte) { tmp := (*out)[:0] for i := 0; i < len(nibbles); i += 2 { tmp = append(tmp, nibbles[i]<<4|nibbles[i+1]) } *out = tmp } // DecompressNibbles - supports only even number of nibbles // // HI_NIBBLE(b) = (b >> 4) & 0x0F // LO_NIBBLE(b) = b & 0x0F func DecompressNibbles(in []byte, out *[]byte) { tmp := (*out)[:0] for i := 0; i < len(in); i++ { tmp = append(tmp, (in[i]>>4)&0x0F, in[i]&0x0F) } *out = tmp }