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 }