github.com/andeya/ameda@v1.5.3/atoi62.go (about) 1 package ameda 2 3 import ( 4 "errors" 5 "math" 6 "strconv" 7 ) 8 9 // ParseUint is like ParseInt but for unsigned numbers. 10 // NOTE: 11 // 12 // Compatible with standard package strconv. 13 func ParseUint(s string, base int, bitSize int) (uint64, error) { 14 // Ignore letter case 15 if base <= 36 { 16 return strconv.ParseUint(s, base, bitSize) 17 } 18 19 const fnParseUint = "ParseUint" 20 21 if base > 62 { 22 return 0, baseError(fnParseUint, s, base) 23 } 24 25 if s == "" || !underscoreOK(s) { 26 return 0, syntaxError(fnParseUint, s) 27 } 28 29 if bitSize == 0 { 30 bitSize = int(strconv.IntSize) 31 } else if bitSize < 0 || bitSize > 64 { 32 return 0, bitSizeError(fnParseUint, s, bitSize) 33 } 34 35 // Cutoff is the smallest number such that cutoff*base > maxUint64. 36 // Use compile-time constants for common cases. 37 cutoff := math.MaxUint64/uint64(base) + 1 38 39 maxVal := uint64(1)<<uint(bitSize) - 1 40 41 var n uint64 42 for _, c := range []byte(s) { 43 var d byte 44 switch { 45 case '0' <= c && c <= '9': 46 d = c - '0' 47 case 'a' <= c && c <= 'z': 48 d = c - 'a' + 10 49 case 'A' <= c && c <= 'Z': 50 d = c - 'A' + 10 + 26 51 case c == '_': 52 continue 53 default: 54 return 0, syntaxError(fnParseUint, s) 55 } 56 57 if d >= byte(base) { 58 return 0, syntaxError(fnParseUint, s) 59 } 60 61 if n >= cutoff { 62 // n*base overflows 63 return maxVal, rangeError(fnParseUint, s) 64 } 65 n *= uint64(base) 66 67 n1 := n + uint64(d) 68 if n1 < n || n1 > maxVal { 69 // n+v overflows 70 return maxVal, rangeError(fnParseUint, s) 71 } 72 n = n1 73 } 74 75 return n, nil 76 } 77 78 // ParseInt interprets a string s in the given base (0, 2 to 62) and 79 // bit size (0 to 64) and returns the corresponding value i. 80 // 81 // If base == 0, the base is implied by the string's prefix: 82 // base 2 for "0b", base 8 for "0" or "0o", base 16 for "0x", 83 // and base 10 otherwise. Also, for base == 0 only, underscore 84 // characters are permitted per the Go integer literal syntax. 85 // If base is below 0, is 1, or is above 62, an error is returned. 86 // 87 // The bitSize argument specifies the integer type 88 // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 89 // correspond to int, int8, int16, int32, and int64. 90 // If bitSize is below 0 or above 64, an error is returned. 91 // 92 // The errors that ParseInt returns have concrete type *NumError 93 // and include err.Num = s. If s is empty or contains invalid 94 // digits, err.Err = ErrSyntax and the returned value is 0; 95 // if the value corresponding to s cannot be represented by a 96 // signed integer of the given size, err.Err = ErrRange and the 97 // returned value is the maximum magnitude integer of the 98 // appropriate bitSize and sign. 99 // NOTE: 100 // 101 // Compatible with standard package strconv. 102 func ParseInt(s string, base int, bitSize int) (i int64, err error) { 103 // Ignore letter case 104 if base <= 36 { 105 return strconv.ParseInt(s, base, bitSize) 106 } 107 108 const fnParseInt = "ParseInt" 109 110 if s == "" { 111 return 0, syntaxError(fnParseInt, s) 112 } 113 114 // Pick off leading sign. 115 s0 := s 116 neg := false 117 if s[0] == '+' { 118 s = s[1:] 119 } else if s[0] == '-' { 120 neg = true 121 s = s[1:] 122 } 123 124 // Convert unsigned and check range. 125 var un uint64 126 un, err = ParseUint(s, base, bitSize) 127 if err != nil && err.(*strconv.NumError).Err != strconv.ErrRange { 128 err.(*strconv.NumError).Func = fnParseInt 129 err.(*strconv.NumError).Num = s0 130 return 0, err 131 } 132 133 if bitSize == 0 { 134 bitSize = int(strconv.IntSize) 135 } 136 137 cutoff := uint64(1 << uint(bitSize-1)) 138 if !neg && un >= cutoff { 139 return int64(cutoff - 1), rangeError(fnParseInt, s0) 140 } 141 if neg && un > cutoff { 142 return -int64(cutoff), rangeError(fnParseInt, s0) 143 } 144 n := int64(un) 145 if neg { 146 n = -n 147 } 148 return n, nil 149 } 150 151 // underscoreOK reports whether the underscores in s are allowed. 152 // Checking them in this one function lets all the parsers skip over them simply. 153 // Underscore must appear only between digits or between a base prefix and a digit. 154 func underscoreOK(s string) bool { 155 // saw tracks the last character (class) we saw: 156 // ^ for beginning of number, 157 // 0 for a digit or base prefix, 158 // _ for an underscore, 159 // ! for none of the above. 160 saw := '^' 161 i := 0 162 163 // Optional sign. 164 if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { 165 s = s[1:] 166 } 167 168 // Optional base prefix. 169 if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { 170 i = 2 171 saw = '0' // base prefix counts as a digit for "underscore as digit separator" 172 } 173 174 // Number proper. 175 for ; i < len(s); i++ { 176 // Digits are always okay. 177 if '0' <= s[i] && s[i] <= '9' || 'a' <= lower(s[i]) && lower(s[i]) <= 'z' { 178 saw = '0' 179 continue 180 } 181 // Underscore must follow digit. 182 if s[i] == '_' { 183 if saw != '0' { 184 return false 185 } 186 saw = '_' 187 continue 188 } 189 // Saw non-digit, non-underscore. 190 return false 191 } 192 return true 193 } 194 195 // Atoi is equivalent to ParseInt(s, 10, 0), converted to type int. 196 func Atoi(s string) (int, error) { 197 const fnAtoi = "Atoi" 198 199 sLen := len(s) 200 if strconv.IntSize == 32 && (0 < sLen && sLen < 10) || 201 strconv.IntSize == 64 && (0 < sLen && sLen < 19) { 202 // Fast path for small integers that fit int type. 203 s0 := s 204 if s[0] == '-' || s[0] == '+' { 205 s = s[1:] 206 if len(s) < 1 { 207 return 0, &strconv.NumError{fnAtoi, s0, strconv.ErrSyntax} 208 } 209 } 210 211 n := 0 212 for _, ch := range []byte(s) { 213 ch -= '0' 214 if ch > 9 { 215 return 0, &strconv.NumError{fnAtoi, s0, strconv.ErrSyntax} 216 } 217 n = n*10 + int(ch) 218 } 219 if s0[0] == '-' { 220 n = -n 221 } 222 return n, nil 223 } 224 225 // Slow path for invalid, big, or underscored integers. 226 i64, err := ParseInt(s, 10, 0) 227 if nerr, ok := err.(*strconv.NumError); ok { 228 nerr.Func = fnAtoi 229 } 230 return int(i64), err 231 } 232 233 // lower(c) is a lower-case letter if and only if 234 // c is either that lower-case letter or the equivalent upper-case letter. 235 // Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. 236 // Note that lower of non-letters can produce other non-letters. 237 func lower(c byte) byte { 238 return c | ('x' - 'X') 239 } 240 func syntaxError(fn, str string) *strconv.NumError { 241 return &strconv.NumError{fn, str, strconv.ErrSyntax} 242 } 243 func baseError(fn, str string, base int) *strconv.NumError { 244 return &strconv.NumError{fn, str, errors.New("invalid base " + strconv.Itoa(base))} 245 } 246 func rangeError(fn, str string) *strconv.NumError { 247 return &strconv.NumError{fn, str, strconv.ErrRange} 248 } 249 func bitSizeError(fn, str string, bitSize int) *strconv.NumError { 250 return &strconv.NumError{fn, str, errors.New("invalid bit size " + strconv.Itoa(bitSize))} 251 }