github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/common/hexutil/hexutil.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 /* 13 Package hexutil implements hex encoding with 0x prefix. 14 This encoding is used by the Sberex RPC API to transport binary data in JSON payloads. 15 16 Encoding Rules 17 18 All hex data must have prefix "0x". 19 20 For byte slices, the hex data must be of even length. An empty byte slice 21 encodes as "0x". 22 23 Integers are encoded using the least amount of digits (no leading zero digits). Their 24 encoding may be of uneven length. The number zero encodes as "0x0". 25 */ 26 package hexutil 27 28 import ( 29 "encoding/hex" 30 "fmt" 31 "math/big" 32 "strconv" 33 ) 34 35 const uintBits = 32 << (uint64(^uint(0)) >> 63) 36 37 var ( 38 ErrEmptyString = &decError{"empty hex string"} 39 ErrSyntax = &decError{"invalid hex string"} 40 ErrMissingPrefix = &decError{"hex string without 0x prefix"} 41 ErrOddLength = &decError{"hex string of odd length"} 42 ErrEmptyNumber = &decError{"hex string \"0x\""} 43 ErrLeadingZero = &decError{"hex number with leading zero digits"} 44 ErrUint64Range = &decError{"hex number > 64 bits"} 45 ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)} 46 ErrBig256Range = &decError{"hex number > 256 bits"} 47 ) 48 49 type decError struct{ msg string } 50 51 func (err decError) Error() string { return err.msg } 52 53 // Decode decodes a hex string with 0x prefix. 54 func Decode(input string) ([]byte, error) { 55 if len(input) == 0 { 56 return nil, ErrEmptyString 57 } 58 if !has0xPrefix(input) { 59 return nil, ErrMissingPrefix 60 } 61 b, err := hex.DecodeString(input[2:]) 62 if err != nil { 63 err = mapError(err) 64 } 65 return b, err 66 } 67 68 // MustDecode decodes a hex string with 0x prefix. It panics for invalid input. 69 func MustDecode(input string) []byte { 70 dec, err := Decode(input) 71 if err != nil { 72 panic(err) 73 } 74 return dec 75 } 76 77 // Encode encodes b as a hex string with 0x prefix. 78 func Encode(b []byte) string { 79 enc := make([]byte, len(b)*2+2) 80 copy(enc, "0x") 81 hex.Encode(enc[2:], b) 82 return string(enc) 83 } 84 85 // DecodeUint64 decodes a hex string with 0x prefix as a quantity. 86 func DecodeUint64(input string) (uint64, error) { 87 raw, err := checkNumber(input) 88 if err != nil { 89 return 0, err 90 } 91 dec, err := strconv.ParseUint(raw, 16, 64) 92 if err != nil { 93 err = mapError(err) 94 } 95 return dec, err 96 } 97 98 // MustDecodeUint64 decodes a hex string with 0x prefix as a quantity. 99 // It panics for invalid input. 100 func MustDecodeUint64(input string) uint64 { 101 dec, err := DecodeUint64(input) 102 if err != nil { 103 panic(err) 104 } 105 return dec 106 } 107 108 // EncodeUint64 encodes i as a hex string with 0x prefix. 109 func EncodeUint64(i uint64) string { 110 enc := make([]byte, 2, 10) 111 copy(enc, "0x") 112 return string(strconv.AppendUint(enc, i, 16)) 113 } 114 115 var bigWordNibbles int 116 117 func init() { 118 // This is a weird way to compute the number of nibbles required for big.Word. 119 // The usual way would be to use constant arithmetic but go vet can't handle that. 120 b, _ := new(big.Int).SetString("FFFFFFFFFF", 16) 121 switch len(b.Bits()) { 122 case 1: 123 bigWordNibbles = 16 124 case 2: 125 bigWordNibbles = 8 126 default: 127 panic("weird big.Word size") 128 } 129 } 130 131 // DecodeBig decodes a hex string with 0x prefix as a quantity. 132 // Numbers larger than 256 bits are not accepted. 133 func DecodeBig(input string) (*big.Int, error) { 134 raw, err := checkNumber(input) 135 if err != nil { 136 return nil, err 137 } 138 if len(raw) > 64 { 139 return nil, ErrBig256Range 140 } 141 words := make([]big.Word, len(raw)/bigWordNibbles+1) 142 end := len(raw) 143 for i := range words { 144 start := end - bigWordNibbles 145 if start < 0 { 146 start = 0 147 } 148 for ri := start; ri < end; ri++ { 149 nib := decodeNibble(raw[ri]) 150 if nib == badNibble { 151 return nil, ErrSyntax 152 } 153 words[i] *= 16 154 words[i] += big.Word(nib) 155 } 156 end = start 157 } 158 dec := new(big.Int).SetBits(words) 159 return dec, nil 160 } 161 162 // MustDecodeBig decodes a hex string with 0x prefix as a quantity. 163 // It panics for invalid input. 164 func MustDecodeBig(input string) *big.Int { 165 dec, err := DecodeBig(input) 166 if err != nil { 167 panic(err) 168 } 169 return dec 170 } 171 172 // EncodeBig encodes bigint as a hex string with 0x prefix. 173 // The sign of the integer is ignored. 174 func EncodeBig(bigint *big.Int) string { 175 nbits := bigint.BitLen() 176 if nbits == 0 { 177 return "0x0" 178 } 179 return fmt.Sprintf("%#x", bigint) 180 } 181 182 func has0xPrefix(input string) bool { 183 return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') 184 } 185 186 func checkNumber(input string) (raw string, err error) { 187 if len(input) == 0 { 188 return "", ErrEmptyString 189 } 190 if !has0xPrefix(input) { 191 return "", ErrMissingPrefix 192 } 193 input = input[2:] 194 if len(input) == 0 { 195 return "", ErrEmptyNumber 196 } 197 if len(input) > 1 && input[0] == '0' { 198 return "", ErrLeadingZero 199 } 200 return input, nil 201 } 202 203 const badNibble = ^uint64(0) 204 205 func decodeNibble(in byte) uint64 { 206 switch { 207 case in >= '0' && in <= '9': 208 return uint64(in - '0') 209 case in >= 'A' && in <= 'F': 210 return uint64(in - 'A' + 10) 211 case in >= 'a' && in <= 'f': 212 return uint64(in - 'a' + 10) 213 default: 214 return badNibble 215 } 216 } 217 218 func mapError(err error) error { 219 if err, ok := err.(*strconv.NumError); ok { 220 switch err.Err { 221 case strconv.ErrRange: 222 return ErrUint64Range 223 case strconv.ErrSyntax: 224 return ErrSyntax 225 } 226 } 227 if _, ok := err.(hex.InvalidByteError); ok { 228 return ErrSyntax 229 } 230 if err == hex.ErrLength { 231 return ErrOddLength 232 } 233 return err 234 }