gitee.com/quant1x/pkg@v0.2.8/fastjson/fastfloat/parse.go (about) 1 package fastfloat 2 3 import ( 4 "fmt" 5 "math" 6 "strconv" 7 "strings" 8 ) 9 10 // ParseUint64BestEffort parses uint64 number s. 11 // 12 // It is equivalent to strconv.ParseUint(s, 10, 64), but is faster. 13 // 14 // 0 is returned if the number cannot be parsed. 15 // See also ParseUint64, which returns parse error if the number cannot be parsed. 16 func ParseUint64BestEffort(s string) uint64 { 17 if len(s) == 0 { 18 return 0 19 } 20 i := uint(0) 21 d := uint64(0) 22 j := i 23 for i < uint(len(s)) { 24 if s[i] >= '0' && s[i] <= '9' { 25 d = d*10 + uint64(s[i]-'0') 26 i++ 27 if i > 18 { 28 // The integer part may be out of range for uint64. 29 // Fall back to slow parsing. 30 dd, err := strconv.ParseUint(s, 10, 64) 31 if err != nil { 32 return 0 33 } 34 return dd 35 } 36 continue 37 } 38 break 39 } 40 if i <= j { 41 return 0 42 } 43 if i < uint(len(s)) { 44 // Unparsed tail left. 45 return 0 46 } 47 return d 48 } 49 50 // ParseUint64 parses uint64 from s. 51 // 52 // It is equivalent to strconv.ParseUint(s, 10, 64), but is faster. 53 // 54 // See also ParseUint64BestEffort. 55 func ParseUint64(s string) (uint64, error) { 56 if len(s) == 0 { 57 return 0, fmt.Errorf("cannot parse uint64 from empty string") 58 } 59 i := uint(0) 60 d := uint64(0) 61 j := i 62 for i < uint(len(s)) { 63 if s[i] >= '0' && s[i] <= '9' { 64 d = d*10 + uint64(s[i]-'0') 65 i++ 66 if i > 18 { 67 // The integer part may be out of range for uint64. 68 // Fall back to slow parsing. 69 dd, err := strconv.ParseUint(s, 10, 64) 70 if err != nil { 71 return 0, err 72 } 73 return dd, nil 74 } 75 continue 76 } 77 break 78 } 79 if i <= j { 80 return 0, fmt.Errorf("cannot parse uint64 from %q", s) 81 } 82 if i < uint(len(s)) { 83 // Unparsed tail left. 84 return 0, fmt.Errorf("unparsed tail left after parsing uint64 from %q: %q", s, s[i:]) 85 } 86 return d, nil 87 } 88 89 // ParseInt64BestEffort parses int64 number s. 90 // 91 // It is equivalent to strconv.ParseInt(s, 10, 64), but is faster. 92 // 93 // 0 is returned if the number cannot be parsed. 94 // See also ParseInt64, which returns parse error if the number cannot be parsed. 95 func ParseInt64BestEffort(s string) int64 { 96 if len(s) == 0 { 97 return 0 98 } 99 i := uint(0) 100 minus := s[0] == '-' 101 if minus { 102 i++ 103 if i >= uint(len(s)) { 104 return 0 105 } 106 } 107 108 d := int64(0) 109 j := i 110 for i < uint(len(s)) { 111 if s[i] >= '0' && s[i] <= '9' { 112 d = d*10 + int64(s[i]-'0') 113 i++ 114 if i > 18 { 115 // The integer part may be out of range for int64. 116 // Fall back to slow parsing. 117 dd, err := strconv.ParseInt(s, 10, 64) 118 if err != nil { 119 return 0 120 } 121 return dd 122 } 123 continue 124 } 125 break 126 } 127 if i <= j { 128 return 0 129 } 130 if i < uint(len(s)) { 131 // Unparsed tail left. 132 return 0 133 } 134 if minus { 135 d = -d 136 } 137 return d 138 } 139 140 // ParseInt64 parses int64 number s. 141 // 142 // It is equivalent to strconv.ParseInt(s, 10, 64), but is faster. 143 // 144 // See also ParseInt64BestEffort. 145 func ParseInt64(s string) (int64, error) { 146 if len(s) == 0 { 147 return 0, fmt.Errorf("cannot parse int64 from empty string") 148 } 149 i := uint(0) 150 minus := s[0] == '-' 151 if minus { 152 i++ 153 if i >= uint(len(s)) { 154 return 0, fmt.Errorf("cannot parse int64 from %q", s) 155 } 156 } 157 158 d := int64(0) 159 j := i 160 for i < uint(len(s)) { 161 if s[i] >= '0' && s[i] <= '9' { 162 d = d*10 + int64(s[i]-'0') 163 i++ 164 if i > 18 { 165 // The integer part may be out of range for int64. 166 // Fall back to slow parsing. 167 dd, err := strconv.ParseInt(s, 10, 64) 168 if err != nil { 169 return 0, err 170 } 171 return dd, nil 172 } 173 continue 174 } 175 break 176 } 177 if i <= j { 178 return 0, fmt.Errorf("cannot parse int64 from %q", s) 179 } 180 if i < uint(len(s)) { 181 // Unparsed tail left. 182 return 0, fmt.Errorf("unparsed tail left after parsing int64 form %q: %q", s, s[i:]) 183 } 184 if minus { 185 d = -d 186 } 187 return d, nil 188 } 189 190 // Exact powers of 10. 191 // 192 // This works faster than math.Pow10, since it avoids additional multiplication. 193 var float64pow10 = [...]float64{ 194 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 195 } 196 197 // ParseBestEffort parses floating-point number s. 198 // 199 // It is equivalent to strconv.ParseFloat(s, 64), but is faster. 200 // 201 // 0 is returned if the number cannot be parsed. 202 // See also Parse, which returns parse error if the number cannot be parsed. 203 func ParseBestEffort(s string) float64 { 204 if len(s) == 0 { 205 return 0 206 } 207 i := uint(0) 208 minus := s[0] == '-' 209 if minus { 210 i++ 211 if i >= uint(len(s)) { 212 return 0 213 } 214 } 215 216 // the integer part might be elided to remain compliant 217 // with https://go.dev/ref/spec#Floating-point_literals 218 if s[i] == '.' && (i+1 >= uint(len(s)) || s[i+1] < '0' || s[i+1] > '9') { 219 return 0 220 } 221 222 d := uint64(0) 223 j := i 224 for i < uint(len(s)) { 225 if s[i] >= '0' && s[i] <= '9' { 226 d = d*10 + uint64(s[i]-'0') 227 i++ 228 if i > 18 { 229 // The integer part may be out of range for uint64. 230 // Fall back to slow parsing. 231 f, err := strconv.ParseFloat(s, 64) 232 if err != nil && !math.IsInf(f, 0) { 233 return 0 234 } 235 return f 236 } 237 continue 238 } 239 break 240 } 241 if i <= j && s[i] != '.' { 242 s = s[i:] 243 if strings.HasPrefix(s, "+") { 244 s = s[1:] 245 } 246 // "infinity" is needed for OpenMetrics support. 247 // See https://github.com/OpenObservability/OpenMetrics/blob/master/OpenMetrics.md 248 if strings.EqualFold(s, "inf") || strings.EqualFold(s, "infinity") { 249 if minus { 250 return -inf 251 } 252 return inf 253 } 254 if strings.EqualFold(s, "nan") { 255 return nan 256 } 257 return 0 258 } 259 f := float64(d) 260 if i >= uint(len(s)) { 261 // Fast path - just integer. 262 if minus { 263 f = -f 264 } 265 return f 266 } 267 268 if s[i] == '.' { 269 // Parse fractional part. 270 i++ 271 if i >= uint(len(s)) { 272 // the fractional part may be elided to remain compliant 273 // with https://go.dev/ref/spec#Floating-point_literals 274 return f 275 } 276 k := i 277 for i < uint(len(s)) { 278 if s[i] >= '0' && s[i] <= '9' { 279 d = d*10 + uint64(s[i]-'0') 280 i++ 281 if i-j >= uint(len(float64pow10)) { 282 // The mantissa is out of range. Fall back to standard parsing. 283 f, err := strconv.ParseFloat(s, 64) 284 if err != nil && !math.IsInf(f, 0) { 285 return 0 286 } 287 return f 288 } 289 continue 290 } 291 break 292 } 293 if i < k { 294 return 0 295 } 296 // Convert the entire mantissa to a float at once to avoid rounding errors. 297 f = float64(d) / float64pow10[i-k] 298 if i >= uint(len(s)) { 299 // Fast path - parsed fractional number. 300 if minus { 301 f = -f 302 } 303 return f 304 } 305 } 306 if s[i] == 'e' || s[i] == 'E' { 307 // Parse exponent part. 308 i++ 309 if i >= uint(len(s)) { 310 return 0 311 } 312 expMinus := false 313 if s[i] == '+' || s[i] == '-' { 314 expMinus = s[i] == '-' 315 i++ 316 if i >= uint(len(s)) { 317 return 0 318 } 319 } 320 exp := int16(0) 321 j := i 322 for i < uint(len(s)) { 323 if s[i] >= '0' && s[i] <= '9' { 324 exp = exp*10 + int16(s[i]-'0') 325 i++ 326 if exp > 300 { 327 // The exponent may be too big for float64. 328 // Fall back to standard parsing. 329 f, err := strconv.ParseFloat(s, 64) 330 if err != nil && !math.IsInf(f, 0) { 331 return 0 332 } 333 return f 334 } 335 continue 336 } 337 break 338 } 339 if i <= j { 340 return 0 341 } 342 if expMinus { 343 exp = -exp 344 } 345 f *= math.Pow10(int(exp)) 346 if i >= uint(len(s)) { 347 if minus { 348 f = -f 349 } 350 return f 351 } 352 } 353 return 0 354 } 355 356 // Parse parses floating-point number s. 357 // 358 // It is equivalent to strconv.ParseFloat(s, 64), but is faster. 359 // 360 // See also ParseBestEffort. 361 func Parse(s string) (float64, error) { 362 if len(s) == 0 { 363 return 0, fmt.Errorf("cannot parse float64 from empty string") 364 } 365 i := uint(0) 366 minus := s[0] == '-' 367 if minus { 368 i++ 369 if i >= uint(len(s)) { 370 return 0, fmt.Errorf("cannot parse float64 from %q", s) 371 } 372 } 373 374 // the integer part might be elided to remain compliant 375 // with https://go.dev/ref/spec#Floating-point_literals 376 if s[i] == '.' && (i+1 >= uint(len(s)) || s[i+1] < '0' || s[i+1] > '9') { 377 return 0, fmt.Errorf("missing integer and fractional part in %q", s) 378 } 379 380 d := uint64(0) 381 j := i 382 for i < uint(len(s)) { 383 if s[i] >= '0' && s[i] <= '9' { 384 d = d*10 + uint64(s[i]-'0') 385 i++ 386 if i > 18 { 387 // The integer part may be out of range for uint64. 388 // Fall back to slow parsing. 389 f, err := strconv.ParseFloat(s, 64) 390 if err != nil && !math.IsInf(f, 0) { 391 return 0, err 392 } 393 return f, nil 394 } 395 continue 396 } 397 break 398 } 399 if i <= j && s[i] != '.' { 400 ss := s[i:] 401 if strings.HasPrefix(ss, "+") { 402 ss = ss[1:] 403 } 404 // "infinity" is needed for OpenMetrics support. 405 // See https://github.com/OpenObservability/OpenMetrics/blob/master/OpenMetrics.md 406 if strings.EqualFold(ss, "inf") || strings.EqualFold(ss, "infinity") { 407 if minus { 408 return -inf, nil 409 } 410 return inf, nil 411 } 412 if strings.EqualFold(ss, "nan") { 413 return nan, nil 414 } 415 return 0, fmt.Errorf("unparsed tail left after parsing float64 from %q: %q", s, ss) 416 } 417 f := float64(d) 418 if i >= uint(len(s)) { 419 // Fast path - just integer. 420 if minus { 421 f = -f 422 } 423 return f, nil 424 } 425 426 if s[i] == '.' { 427 // Parse fractional part. 428 i++ 429 if i >= uint(len(s)) { 430 // the fractional part might be elided to remain compliant 431 // with https://go.dev/ref/spec#Floating-point_literals 432 return f, nil 433 } 434 k := i 435 for i < uint(len(s)) { 436 if s[i] >= '0' && s[i] <= '9' { 437 d = d*10 + uint64(s[i]-'0') 438 i++ 439 if i-j >= uint(len(float64pow10)) { 440 // The mantissa is out of range. Fall back to standard parsing. 441 f, err := strconv.ParseFloat(s, 64) 442 if err != nil && !math.IsInf(f, 0) { 443 return 0, fmt.Errorf("cannot parse mantissa in %q: %s", s, err) 444 } 445 return f, nil 446 } 447 continue 448 } 449 break 450 } 451 if i < k { 452 return 0, fmt.Errorf("cannot find mantissa in %q", s) 453 } 454 // Convert the entire mantissa to a float at once to avoid rounding errors. 455 f = float64(d) / float64pow10[i-k] 456 if i >= uint(len(s)) { 457 // Fast path - parsed fractional number. 458 if minus { 459 f = -f 460 } 461 return f, nil 462 } 463 } 464 if s[i] == 'e' || s[i] == 'E' { 465 // Parse exponent part. 466 i++ 467 if i >= uint(len(s)) { 468 return 0, fmt.Errorf("cannot parse exponent in %q", s) 469 } 470 expMinus := false 471 if s[i] == '+' || s[i] == '-' { 472 expMinus = s[i] == '-' 473 i++ 474 if i >= uint(len(s)) { 475 return 0, fmt.Errorf("cannot parse exponent in %q", s) 476 } 477 } 478 exp := int16(0) 479 j := i 480 for i < uint(len(s)) { 481 if s[i] >= '0' && s[i] <= '9' { 482 exp = exp*10 + int16(s[i]-'0') 483 i++ 484 if exp > 300 { 485 // The exponent may be too big for float64. 486 // Fall back to standard parsing. 487 f, err := strconv.ParseFloat(s, 64) 488 if err != nil && !math.IsInf(f, 0) { 489 return 0, fmt.Errorf("cannot parse exponent in %q: %s", s, err) 490 } 491 return f, nil 492 } 493 continue 494 } 495 break 496 } 497 if i <= j { 498 return 0, fmt.Errorf("cannot parse exponent in %q", s) 499 } 500 if expMinus { 501 exp = -exp 502 } 503 f *= math.Pow10(int(exp)) 504 if i >= uint(len(s)) { 505 if minus { 506 f = -f 507 } 508 return f, nil 509 } 510 } 511 return 0, fmt.Errorf("cannot parse float64 from %q", s) 512 } 513 514 var inf = math.Inf(1) 515 var nan = math.NaN()