172 lines
4.0 KiB
Go
172 lines
4.0 KiB
Go
// 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}
|
|
}
|