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