// Code extracted from vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go package statusproto import ( "encoding/hex" "encoding/json" "fmt" "reflect" "strconv" ) const ( badNibble = ^uint64(0) uintBits = 32 << (uint64(^uint(0)) >> 63) ) // Errors 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 } 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 } } // UnmarshalText implements encoding.TextUnmarshaler. func (b *HexBytes) UnmarshalText(input []byte) error { raw, err := checkText(input, true) if err != nil { return err } dec := make([]byte, len(raw)/2) if _, err = hex.Decode(dec, raw); err != nil { err = mapError(err) } else { *b = dec } return err } // UnmarshalFixedHexText decodes the input as a string with 0x prefix. The length of out // determines the required input length. This function is commonly used to implement the // UnmarshalText method for fixed-size types. func UnmarshalFixedHexText(typname string, input, out []byte) error { raw, err := checkText(input, true) if err != nil { return err } if len(raw)/2 != len(out) { return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) } // Pre-verify syntax before modifying out. for _, b := range raw { if decodeNibble(b) == badNibble { return ErrSyntax } } _, err = hex.Decode(out, raw) return err } // String returns the hex encoding of b. func (b HexBytes) String() string { return EncodeHex(b) } // EncodeHex encodes b as a hex string with 0x prefix. func EncodeHex(b []byte) string { enc := make([]byte, len(b)*2+2) copy(enc, "0x") hex.Encode(enc[2:], b) return string(enc) } // DecodeHex decodes a hex string with 0x prefix. func DecodeHex(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 { err = mapError(err) } return b, err } // MustDecodeHex decodes a hex string with 0x prefix. It panics for invalid input. func MustDecodeHex(input string) []byte { dec, err := DecodeHex(input) if err != nil { panic(err) } return dec } func isString(input []byte) bool { return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' } func bytesHave0xPrefix(input []byte) bool { return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') } func checkText(input []byte, wantPrefix bool) ([]byte, error) { if len(input) == 0 { return nil, nil // empty strings are allowed } if bytesHave0xPrefix(input) { input = input[2:] } else if wantPrefix { return nil, ErrMissingPrefix } if len(input)%2 != 0 { return nil, ErrOddLength } return input, nil } 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 } func wrapTypeError(err error, typ reflect.Type) error { if _, ok := err.(*decError); ok { return &json.UnmarshalTypeError{Value: err.Error(), Type: typ} } return err } func errNonString(typ reflect.Type) error { return &json.UnmarshalTypeError{Value: "non-string", Type: typ} }