github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/hex/big.go (about)

     1  package hex
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"math/big"
     7  	"strconv"
     8  )
     9  
    10  const uintBits = 32 << (uint64(^uint(0)) >> 63)
    11  
    12  // Errors
    13  var (
    14  	ErrEmptyString   = &decError{"empty hex string"}
    15  	ErrSyntax        = &decError{"invalid hex string"}
    16  	ErrMissingPrefix = &decError{"hex string without 0x prefix"}
    17  	ErrOddLength     = &decError{"hex string of odd length"}
    18  	ErrEmptyNumber   = &decError{"hex string \"0x\""}
    19  	ErrLeadingZero   = &decError{"hex number with leading zero digits"}
    20  	ErrUint64Range   = &decError{"hex number > 64 bits"}
    21  	ErrUintRange     = &decError{fmt.Sprintf("hex number > %d bits", uintBits)}
    22  	ErrBig256Range   = &decError{"hex number > 256 bits"}
    23  )
    24  
    25  type decError struct{ msg string }
    26  
    27  func (err decError) Error() string { return err.msg }
    28  
    29  // Decode decodes a hex string with 0x prefix.
    30  func Decode(input string) ([]byte, error) {
    31  	if len(input) == 0 {
    32  		return nil, ErrEmptyString
    33  	}
    34  	if !has0xPrefix(input) {
    35  		return nil, ErrMissingPrefix
    36  	}
    37  	b, err := hex.DecodeString(input[2:])
    38  	if err != nil {
    39  		err = mapError(err)
    40  	}
    41  	return b, err
    42  }
    43  
    44  // MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
    45  func MustDecode(input string) []byte {
    46  	dec, err := Decode(input)
    47  	if err != nil {
    48  		panic(err)
    49  	}
    50  	return dec
    51  }
    52  
    53  // Encode encodes b as a hex string with 0x prefix.
    54  func Encode(b []byte) string {
    55  	enc := make([]byte, len(b)*2+2)
    56  	copy(enc, "0x")
    57  	hex.Encode(enc[2:], b)
    58  	return string(enc)
    59  }
    60  
    61  // DecodeUint64 decodes a hex string with 0x prefix as a quantity.
    62  func DecodeUint64(input string) (uint64, error) {
    63  	raw, err := checkNumber(input)
    64  	if err != nil {
    65  		return 0, err
    66  	}
    67  	dec, err := strconv.ParseUint(raw, 16, 64)
    68  	if err != nil {
    69  		err = mapError(err)
    70  	}
    71  	return dec, err
    72  }
    73  
    74  // MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
    75  // It panics for invalid input.
    76  func MustDecodeUint64(input string) uint64 {
    77  	dec, err := DecodeUint64(input)
    78  	if err != nil {
    79  		panic(err)
    80  	}
    81  	return dec
    82  }
    83  
    84  // EncodeUint64 encodes i as a hex string with 0x prefix.
    85  func EncodeUint64(i uint64) string {
    86  	enc := make([]byte, 2, 10)
    87  	copy(enc, "0x")
    88  	return string(strconv.AppendUint(enc, i, 16))
    89  }
    90  
    91  var bigWordNibbles int
    92  
    93  func init() {
    94  	// This is a weird way to compute the number of nibbles required for big.Word.
    95  	// The usual way would be to use constant arithmetic but go vet can't handle that.
    96  	b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
    97  	switch len(b.Bits()) {
    98  	case 1:
    99  		bigWordNibbles = 16
   100  	case 2:
   101  		bigWordNibbles = 8
   102  	default:
   103  		panic("weird big.Word size")
   104  	}
   105  }
   106  
   107  // DecodeBig decodes a hex string with 0x prefix as a quantity.
   108  // Numbers larger than 256 bits are not accepted.
   109  func DecodeBig(input string) (*big.Int, error) {
   110  	raw, err := checkNumber(input)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	if len(raw) > 64 {
   115  		return nil, ErrBig256Range
   116  	}
   117  	words := make([]big.Word, len(raw)/bigWordNibbles+1)
   118  	end := len(raw)
   119  	for i := range words {
   120  		start := end - bigWordNibbles
   121  		if start < 0 {
   122  			start = 0
   123  		}
   124  		for ri := start; ri < end; ri++ {
   125  			nib := decodeNibble(raw[ri])
   126  			if nib == badNibble {
   127  				return nil, ErrSyntax
   128  			}
   129  			words[i] *= 16
   130  			words[i] += big.Word(nib)
   131  		}
   132  		end = start
   133  	}
   134  	dec := new(big.Int).SetBits(words)
   135  	return dec, nil
   136  }
   137  
   138  // MustDecodeBig decodes a hex string with 0x prefix as a quantity.
   139  // It panics for invalid input.
   140  func MustDecodeBig(input string) *big.Int {
   141  	dec, err := DecodeBig(input)
   142  	if err != nil {
   143  		panic(err)
   144  	}
   145  	return dec
   146  }
   147  
   148  // EncodeBig encodes bigint as a hex string with 0x prefix.
   149  // The sign of the integer is ignored.
   150  func EncodeBig(bigint *big.Int) string {
   151  	nbits := bigint.BitLen()
   152  	if nbits == 0 {
   153  		return "0x0"
   154  	}
   155  	return fmt.Sprintf("%#x", bigint)
   156  }
   157  
   158  func has0xPrefix(input string) bool {
   159  	return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
   160  }
   161  
   162  func checkNumber(input string) (raw string, err error) {
   163  	if len(input) == 0 {
   164  		return "", ErrEmptyString
   165  	}
   166  	if !has0xPrefix(input) {
   167  		return "", ErrMissingPrefix
   168  	}
   169  	input = input[2:]
   170  	if len(input) == 0 {
   171  		return "", ErrEmptyNumber
   172  	}
   173  	if len(input) > 1 && input[0] == '0' {
   174  		return "", ErrLeadingZero
   175  	}
   176  	return input, nil
   177  }
   178  
   179  const badNibble = ^uint64(0)
   180  
   181  func decodeNibble(in byte) uint64 {
   182  	switch {
   183  	case in >= '0' && in <= '9':
   184  		return uint64(in - '0')
   185  	case in >= 'A' && in <= 'F':
   186  		return uint64(in - 'A' + 10)
   187  	case in >= 'a' && in <= 'f':
   188  		return uint64(in - 'a' + 10)
   189  	default:
   190  		return badNibble
   191  	}
   192  }
   193  
   194  func mapError(err error) error {
   195  	if err, ok := err.(*strconv.NumError); ok {
   196  		switch err.Err {
   197  		case strconv.ErrRange:
   198  			return ErrUint64Range
   199  		case strconv.ErrSyntax:
   200  			return ErrSyntax
   201  		}
   202  	}
   203  	if _, ok := err.(hex.InvalidByteError); ok {
   204  		return ErrSyntax
   205  	}
   206  	if err == hex.ErrLength {
   207  		return ErrOddLength
   208  	}
   209  	return err
   210  }