1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Diagnostics; 6 using System.Globalization; 7 using System.Runtime.InteropServices; 8 9 namespace System 10 { 11 // The Parse methods provided by the numeric classes convert a 12 // string to a numeric value. The optional style parameter specifies the 13 // permitted style of the numeric string. It must be a combination of bit flags 14 // from the NumberStyles enumeration. The optional info parameter 15 // specifies the NumberFormatInfo instance to use when parsing the 16 // string. If the info parameter is null or omitted, the numeric 17 // formatting information is obtained from the current culture. 18 // 19 // Numeric strings produced by the Format methods using the Currency, 20 // Decimal, Engineering, Fixed point, General, or Number standard formats 21 // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable 22 // by the Parse methods if the NumberStyles.Any style is 23 // specified. Note, however, that the Parse methods do not accept 24 // NaNs or Infinities. 25 26 internal partial class Number 27 { 28 private const int Int32Precision = 10; 29 private const int UInt32Precision = Int32Precision; 30 private const int Int64Precision = 19; 31 private const int UInt64Precision = 20; 32 HexNumberToInt32(ref NumberBuffer number, ref int value)33 private static bool HexNumberToInt32(ref NumberBuffer number, ref int value) 34 { 35 uint passedValue = 0; 36 bool returnValue = HexNumberToUInt32(ref number, ref passedValue); 37 value = (int)passedValue; 38 return returnValue; 39 } 40 HexNumberToInt64(ref NumberBuffer number, ref long value)41 private static bool HexNumberToInt64(ref NumberBuffer number, ref long value) 42 { 43 ulong passedValue = 0; 44 bool returnValue = HexNumberToUInt64(ref number, ref passedValue); 45 value = (long)passedValue; 46 return returnValue; 47 } 48 HexNumberToUInt32(ref NumberBuffer number, ref uint value)49 private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value) 50 { 51 int i = number.scale; 52 if (i > UInt32Precision || i < number.precision) 53 { 54 return false; 55 } 56 char* p = number.digits; 57 Debug.Assert(p != null); 58 59 uint n = 0; 60 while (--i >= 0) 61 { 62 if (n > ((uint)0xFFFFFFFF / 16)) 63 { 64 return false; 65 } 66 n *= 16; 67 if (*p != '\0') 68 { 69 uint newN = n; 70 if (*p != '\0') 71 { 72 if (*p >= '0' && *p <= '9') 73 { 74 newN += (uint)(*p - '0'); 75 } 76 else 77 { 78 if (*p >= 'A' && *p <= 'F') 79 { 80 newN += (uint)((*p - 'A') + 10); 81 } 82 else 83 { 84 Debug.Assert(*p >= 'a' && *p <= 'f'); 85 newN += (uint)((*p - 'a') + 10); 86 } 87 } 88 p++; 89 } 90 91 // Detect an overflow here... 92 if (newN < n) 93 { 94 return false; 95 } 96 n = newN; 97 } 98 } 99 value = n; 100 return true; 101 } 102 HexNumberToUInt64(ref NumberBuffer number, ref ulong value)103 private static unsafe bool HexNumberToUInt64(ref NumberBuffer number, ref ulong value) 104 { 105 int i = number.scale; 106 if (i > UInt64Precision || i < number.precision) 107 { 108 return false; 109 } 110 char* p = number.digits; 111 Debug.Assert(p != null); 112 113 ulong n = 0; 114 while (--i >= 0) 115 { 116 if (n > (0xFFFFFFFFFFFFFFFF / 16)) 117 { 118 return false; 119 } 120 n *= 16; 121 if (*p != '\0') 122 { 123 ulong newN = n; 124 if (*p != '\0') 125 { 126 if (*p >= '0' && *p <= '9') 127 { 128 newN += (ulong)(*p - '0'); 129 } 130 else 131 { 132 if (*p >= 'A' && *p <= 'F') 133 { 134 newN += (ulong)((*p - 'A') + 10); 135 } 136 else 137 { 138 Debug.Assert(*p >= 'a' && *p <= 'f'); 139 newN += (ulong)((*p - 'a') + 10); 140 } 141 } 142 p++; 143 } 144 145 // Detect an overflow here... 146 if (newN < n) 147 { 148 return false; 149 } 150 n = newN; 151 } 152 } 153 value = n; 154 return true; 155 } 156 NumberToInt32(ref NumberBuffer number, ref int value)157 private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value) 158 { 159 int i = number.scale; 160 if (i > Int32Precision || i < number.precision) 161 { 162 return false; 163 } 164 char* p = number.digits; 165 Debug.Assert(p != null); 166 int n = 0; 167 while (--i >= 0) 168 { 169 if ((uint)n > (0x7FFFFFFF / 10)) 170 { 171 return false; 172 } 173 n *= 10; 174 if (*p != '\0') 175 { 176 n += (int)(*p++ - '0'); 177 } 178 } 179 if (number.sign) 180 { 181 n = -n; 182 if (n > 0) 183 { 184 return false; 185 } 186 } 187 else 188 { 189 if (n < 0) 190 { 191 return false; 192 } 193 } 194 value = n; 195 return true; 196 } 197 NumberToInt64(ref NumberBuffer number, ref long value)198 private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value) 199 { 200 int i = number.scale; 201 if (i > Int64Precision || i < number.precision) 202 { 203 return false; 204 } 205 char* p = number.digits; 206 Debug.Assert(p != null); 207 long n = 0; 208 while (--i >= 0) 209 { 210 if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10)) 211 { 212 return false; 213 } 214 n *= 10; 215 if (*p != '\0') 216 { 217 n += (int)(*p++ - '0'); 218 } 219 } 220 if (number.sign) 221 { 222 n = -n; 223 if (n > 0) 224 { 225 return false; 226 } 227 } 228 else 229 { 230 if (n < 0) 231 { 232 return false; 233 } 234 } 235 value = n; 236 return true; 237 } 238 NumberToUInt32(ref NumberBuffer number, ref uint value)239 private static unsafe bool NumberToUInt32(ref NumberBuffer number, ref uint value) 240 { 241 int i = number.scale; 242 if (i > UInt32Precision || i < number.precision || number.sign) 243 { 244 return false; 245 } 246 char* p = number.digits; 247 Debug.Assert(p != null); 248 uint n = 0; 249 while (--i >= 0) 250 { 251 if (n > (0xFFFFFFFF / 10)) 252 { 253 return false; 254 } 255 n *= 10; 256 if (*p != '\0') 257 { 258 uint newN = n + (uint)(*p++ - '0'); 259 // Detect an overflow here... 260 if (newN < n) 261 { 262 return false; 263 } 264 n = newN; 265 } 266 } 267 value = n; 268 return true; 269 } 270 NumberToUInt64(ref NumberBuffer number, ref ulong value)271 private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value) 272 { 273 int i = number.scale; 274 if (i > UInt64Precision || i < number.precision || number.sign) 275 { 276 return false; 277 } 278 char* p = number.digits; 279 Debug.Assert(p != null); 280 ulong n = 0; 281 while (--i >= 0) 282 { 283 if (n > (0xFFFFFFFFFFFFFFFF / 10)) 284 { 285 return false; 286 } 287 n *= 10; 288 if (*p != '\0') 289 { 290 ulong newN = n + (ulong)(*p++ - '0'); 291 // Detect an overflow here... 292 if (newN < n) 293 { 294 return false; 295 } 296 n = newN; 297 } 298 } 299 value = n; 300 return true; 301 } 302 ParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)303 internal unsafe static int ParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info) 304 { 305 NumberBuffer number = default; 306 int i = 0; 307 308 StringToNumber(s, style, ref number, info, false); 309 310 if ((style & NumberStyles.AllowHexSpecifier) != 0) 311 { 312 if (!HexNumberToInt32(ref number, ref i)) 313 { 314 throw new OverflowException(SR.Overflow_Int32); 315 } 316 } 317 else 318 { 319 if (!NumberToInt32(ref number, ref i)) 320 { 321 throw new OverflowException(SR.Overflow_Int32); 322 } 323 } 324 return i; 325 } 326 ParseInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)327 internal unsafe static long ParseInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt) 328 { 329 NumberBuffer number = default; 330 long i = 0; 331 332 StringToNumber(value, options, ref number, numfmt, false); 333 334 if ((options & NumberStyles.AllowHexSpecifier) != 0) 335 { 336 if (!HexNumberToInt64(ref number, ref i)) 337 { 338 throw new OverflowException(SR.Overflow_Int64); 339 } 340 } 341 else 342 { 343 if (!NumberToInt64(ref number, ref i)) 344 { 345 throw new OverflowException(SR.Overflow_Int64); 346 } 347 } 348 return i; 349 } 350 ParseUInt32(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)351 internal unsafe static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt) 352 { 353 NumberBuffer number = default; 354 uint i = 0; 355 356 StringToNumber(value, options, ref number, numfmt, false); 357 358 if ((options & NumberStyles.AllowHexSpecifier) != 0) 359 { 360 if (!HexNumberToUInt32(ref number, ref i)) 361 { 362 throw new OverflowException(SR.Overflow_UInt32); 363 } 364 } 365 else 366 { 367 if (!NumberToUInt32(ref number, ref i)) 368 { 369 throw new OverflowException(SR.Overflow_UInt32); 370 } 371 } 372 373 return i; 374 } 375 ParseUInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)376 internal unsafe static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt) 377 { 378 NumberBuffer number = default; 379 ulong i = 0; 380 381 StringToNumber(value, options, ref number, numfmt, false); 382 if ((options & NumberStyles.AllowHexSpecifier) != 0) 383 { 384 if (!HexNumberToUInt64(ref number, ref i)) 385 { 386 throw new OverflowException(SR.Overflow_UInt64); 387 } 388 } 389 else 390 { 391 if (!NumberToUInt64(ref number, ref i)) 392 { 393 throw new OverflowException(SR.Overflow_UInt64); 394 } 395 } 396 return i; 397 } 398 ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)399 private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) 400 { 401 const int StateSign = 0x0001; 402 const int StateParens = 0x0002; 403 const int StateDigits = 0x0004; 404 const int StateNonZero = 0x0008; 405 const int StateDecimal = 0x0010; 406 const int StateCurrency = 0x0020; 407 408 number.scale = 0; 409 number.sign = false; 410 string decSep; // decimal separator from NumberFormatInfo. 411 string groupSep; // group separator from NumberFormatInfo. 412 string currSymbol = null; // currency symbol from NumberFormatInfo. 413 414 bool parsingCurrency = false; 415 if ((options & NumberStyles.AllowCurrencySymbol) != 0) 416 { 417 currSymbol = numfmt.CurrencySymbol; 418 419 // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast. 420 // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part). 421 decSep = numfmt.CurrencyDecimalSeparator; 422 groupSep = numfmt.CurrencyGroupSeparator; 423 parsingCurrency = true; 424 } 425 else 426 { 427 decSep = numfmt.NumberDecimalSeparator; 428 groupSep = numfmt.NumberGroupSeparator; 429 } 430 431 int state = 0; 432 char* p = str; 433 char ch = *p; 434 char* next; 435 436 while (true) 437 { 438 // Eat whitespace unless we've found a sign which isn't followed by a currency symbol. 439 // "-Kr 1231.47" is legal but "- 1231.47" is not. 440 if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2))) 441 { 442 if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true)))) 443 { 444 state |= StateSign; 445 p = next - 1; 446 } 447 else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0)) 448 { 449 state |= StateSign | StateParens; 450 number.sign = true; 451 } 452 else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) 453 { 454 state |= StateCurrency; 455 currSymbol = null; 456 // We already found the currency symbol. There should not be more currency symbols. Set 457 // currSymbol to NULL so that we won't search it again in the later code path. 458 p = next - 1; 459 } 460 else 461 { 462 break; 463 } 464 } 465 ch = *++p; 466 } 467 int digCount = 0; 468 int digEnd = 0; 469 while (true) 470 { 471 if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))) 472 { 473 state |= StateDigits; 474 475 if (ch != '0' || (state & StateNonZero) != 0) 476 { 477 if (digCount < NumberMaxDigits) 478 { 479 number.digits[digCount++] = ch; 480 if (ch != '0' || parseDecimal) 481 { 482 digEnd = digCount; 483 } 484 } 485 if ((state & StateDecimal) == 0) 486 { 487 number.scale++; 488 } 489 state |= StateNonZero; 490 } 491 else if ((state & StateDecimal) != 0) 492 { 493 number.scale--; 494 } 495 } 496 else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null)) 497 { 498 state |= StateDecimal; 499 p = next - 1; 500 } 501 else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null)) 502 { 503 p = next - 1; 504 } 505 else 506 { 507 break; 508 } 509 ch = *++p; 510 } 511 512 bool negExp = false; 513 number.precision = digEnd; 514 number.digits[digEnd] = '\0'; 515 if ((state & StateDigits) != 0) 516 { 517 if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0)) 518 { 519 char* temp = p; 520 ch = *++p; 521 if ((next = MatchChars(p, numfmt.positiveSign)) != null) 522 { 523 ch = *(p = next); 524 } 525 else if ((next = MatchChars(p, numfmt.negativeSign)) != null) 526 { 527 ch = *(p = next); 528 negExp = true; 529 } 530 if (ch >= '0' && ch <= '9') 531 { 532 int exp = 0; 533 do 534 { 535 exp = exp * 10 + (ch - '0'); 536 ch = *++p; 537 if (exp > 1000) 538 { 539 exp = 9999; 540 while (ch >= '0' && ch <= '9') 541 { 542 ch = *++p; 543 } 544 } 545 } while (ch >= '0' && ch <= '9'); 546 if (negExp) 547 { 548 exp = -exp; 549 } 550 number.scale += exp; 551 } 552 else 553 { 554 p = temp; 555 ch = *p; 556 } 557 } 558 while (true) 559 { 560 if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0) 561 { 562 if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true)))) 563 { 564 state |= StateSign; 565 p = next - 1; 566 } 567 else if (ch == ')' && ((state & StateParens) != 0)) 568 { 569 state &= ~StateParens; 570 } 571 else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) 572 { 573 currSymbol = null; 574 p = next - 1; 575 } 576 else 577 { 578 break; 579 } 580 } 581 ch = *++p; 582 } 583 if ((state & StateParens) == 0) 584 { 585 if ((state & StateNonZero) == 0) 586 { 587 if (!parseDecimal) 588 { 589 number.scale = 0; 590 } 591 if ((state & StateDecimal) == 0) 592 { 593 number.sign = false; 594 } 595 } 596 str = p; 597 return true; 598 } 599 } 600 str = p; 601 return false; 602 } 603 TryParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out int result)604 internal unsafe static bool TryParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out int result) 605 { 606 NumberBuffer number = default; 607 result = 0; 608 609 if (!TryStringToNumber(s, style, ref number, info, false)) 610 { 611 return false; 612 } 613 614 if ((style & NumberStyles.AllowHexSpecifier) != 0) 615 { 616 if (!HexNumberToInt32(ref number, ref result)) 617 { 618 return false; 619 } 620 } 621 else 622 { 623 if (!NumberToInt32(ref number, ref result)) 624 { 625 return false; 626 } 627 } 628 return true; 629 } 630 TryParseInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out long result)631 internal unsafe static bool TryParseInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out long result) 632 { 633 NumberBuffer number = default; 634 result = 0; 635 636 if (!TryStringToNumber(s, style, ref number, info, false)) 637 { 638 return false; 639 } 640 641 if ((style & NumberStyles.AllowHexSpecifier) != 0) 642 { 643 if (!HexNumberToInt64(ref number, ref result)) 644 { 645 return false; 646 } 647 } 648 else 649 { 650 if (!NumberToInt64(ref number, ref result)) 651 { 652 return false; 653 } 654 } 655 return true; 656 } 657 TryParseUInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out uint result)658 internal unsafe static bool TryParseUInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out uint result) 659 { 660 NumberBuffer number = default; 661 result = 0; 662 663 if (!TryStringToNumber(s, style, ref number, info, false)) 664 { 665 return false; 666 } 667 668 if ((style & NumberStyles.AllowHexSpecifier) != 0) 669 { 670 if (!HexNumberToUInt32(ref number, ref result)) 671 { 672 return false; 673 } 674 } 675 else 676 { 677 if (!NumberToUInt32(ref number, ref result)) 678 { 679 return false; 680 } 681 } 682 return true; 683 } 684 TryParseUInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out ulong result)685 internal unsafe static bool TryParseUInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out ulong result) 686 { 687 NumberBuffer number = default; 688 result = 0; 689 690 if (!TryStringToNumber(s, style, ref number, info, false)) 691 { 692 return false; 693 } 694 695 if ((style & NumberStyles.AllowHexSpecifier) != 0) 696 { 697 if (!HexNumberToUInt64(ref number, ref result)) 698 { 699 return false; 700 } 701 } 702 else 703 { 704 if (!NumberToUInt64(ref number, ref result)) 705 { 706 return false; 707 } 708 } 709 return true; 710 } 711 ParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)712 internal unsafe static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt) 713 { 714 NumberBuffer number = default; 715 decimal result = 0; 716 717 StringToNumber(value, options, ref number, numfmt, true); 718 719 if (!NumberBufferToDecimal(ref number, ref result)) 720 { 721 throw new OverflowException(SR.Overflow_Decimal); 722 } 723 return result; 724 } 725 ParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)726 internal unsafe static double ParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt) 727 { 728 NumberBuffer number = default; 729 double d = 0; 730 731 if (!TryStringToNumber(value, options, ref number, numfmt, false)) 732 { 733 //If we failed TryStringToNumber, it may be from one of our special strings. 734 //Check the three with which we're concerned and rethrow if it's not one of 735 //those strings. 736 ReadOnlySpan<char> sTrim = value.Trim(); 737 if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) 738 { 739 return double.PositiveInfinity; 740 } 741 if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) 742 { 743 return double.NegativeInfinity; 744 } 745 if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) 746 { 747 return double.NaN; 748 } 749 throw new FormatException(SR.Format_InvalidString); 750 } 751 752 if (!NumberBufferToDouble(ref number, ref d)) 753 { 754 throw new OverflowException(SR.Overflow_Double); 755 } 756 757 return d; 758 } 759 ParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)760 internal unsafe static float ParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt) 761 { 762 NumberBuffer number = default; 763 double d = 0; 764 765 if (!TryStringToNumber(value, options, ref number, numfmt, false)) 766 { 767 //If we failed TryStringToNumber, it may be from one of our special strings. 768 //Check the three with which we're concerned and rethrow if it's not one of 769 //those strings. 770 ReadOnlySpan<char> sTrim = value.Trim(); 771 if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) 772 { 773 return float.PositiveInfinity; 774 } 775 if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) 776 { 777 return float.NegativeInfinity; 778 } 779 if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) 780 { 781 return float.NaN; 782 } 783 throw new FormatException(SR.Format_InvalidString); 784 } 785 786 if (!NumberBufferToDouble(ref number, ref d)) 787 { 788 throw new OverflowException(SR.Overflow_Single); 789 } 790 float castSingle = (float)d; 791 if (float.IsInfinity(castSingle)) 792 { 793 throw new OverflowException(SR.Overflow_Single); 794 } 795 return castSingle; 796 } 797 TryParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out decimal result)798 internal unsafe static bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out decimal result) 799 { 800 NumberBuffer number = default; 801 result = 0; 802 803 if (!TryStringToNumber(value, options, ref number, numfmt, true)) 804 { 805 return false; 806 } 807 808 if (!NumberBufferToDecimal(ref number, ref result)) 809 { 810 return false; 811 } 812 return true; 813 } 814 TryParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out double result)815 internal unsafe static bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out double result) 816 { 817 NumberBuffer number = default; 818 result = 0; 819 820 821 if (!TryStringToNumber(value, options, ref number, numfmt, false)) 822 { 823 return false; 824 } 825 if (!NumberBufferToDouble(ref number, ref result)) 826 { 827 return false; 828 } 829 return true; 830 } 831 TryParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out float result)832 internal unsafe static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out float result) 833 { 834 NumberBuffer number = default; 835 result = 0; 836 double d = 0; 837 838 if (!TryStringToNumber(value, options, ref number, numfmt, false)) 839 { 840 return false; 841 } 842 if (!NumberBufferToDouble(ref number, ref d)) 843 { 844 return false; 845 } 846 float castSingle = (float)d; 847 if (float.IsInfinity(castSingle)) 848 { 849 return false; 850 } 851 852 result = castSingle; 853 return true; 854 } 855 StringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)856 private static unsafe void StringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal) 857 { 858 Debug.Assert(info != null); 859 fixed (char* stringPointer = &MemoryMarshal.GetReference(str)) 860 { 861 char* p = stringPointer; 862 if (!ParseNumber(ref p, options, ref number, info, parseDecimal) 863 || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) 864 { 865 throw new FormatException(SR.Format_InvalidString); 866 } 867 } 868 } 869 TryStringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)870 internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) 871 { 872 Debug.Assert(numfmt != null); 873 fixed (char* stringPointer = &MemoryMarshal.GetReference(str)) 874 { 875 char* p = stringPointer; 876 if (!ParseNumber(ref p, options, ref number, numfmt, parseDecimal) 877 || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) 878 { 879 return false; 880 } 881 } 882 883 return true; 884 } 885 TrailingZeros(ReadOnlySpan<char> s, int index)886 private static bool TrailingZeros(ReadOnlySpan<char> s, int index) 887 { 888 // For compatibility, we need to allow trailing zeros at the end of a number string 889 for (int i = index; i < s.Length; i++) 890 { 891 if (s[i] != '\0') 892 { 893 return false; 894 } 895 } 896 897 return true; 898 } 899 MatchChars(char* p, string str)900 private unsafe static char* MatchChars(char* p, string str) 901 { 902 fixed (char* stringPointer = str) 903 { 904 return MatchChars(p, stringPointer); 905 } 906 } 907 MatchChars(char* p, char* str)908 private unsafe static char* MatchChars(char* p, char* str) 909 { 910 Debug.Assert(p != null && str != null); 911 912 if (*str == '\0') 913 { 914 return null; 915 } 916 917 // We only hurt the failure case 918 // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a 919 // space character we use 0x20 space character instead to mean the same. 920 while (*p == *str || (*str == '\u00a0' && *p == '\u0020')) 921 { 922 p++; 923 str++; 924 if (*str == '\0') return p; 925 } 926 927 return null; 928 } 929 IsWhite(char ch)930 private static bool IsWhite(char ch) => ch == 0x20 || (ch >= 0x09 && ch <= 0x0D); 931 NumberBufferToDouble(ref NumberBuffer number, ref double value)932 private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value) 933 { 934 double d = NumberToDouble(ref number); 935 uint e = DoubleHelper.Exponent(d); 936 ulong m = DoubleHelper.Mantissa(d); 937 938 if (e == 0x7FF) 939 { 940 return false; 941 } 942 943 if (e == 0 && m == 0) 944 { 945 d = 0; 946 } 947 948 value = d; 949 return true; 950 } 951 952 private static class DoubleHelper 953 { Exponent(double d)954 public static unsafe uint Exponent(double d) => 955 (*((uint*)&d + 1) >> 20) & 0x000007ff; 956 Mantissa(double d)957 public static unsafe ulong Mantissa(double d) => 958 *((ulong*)&d) & 0x000fffffffffffff; 959 Sign(double d)960 public static unsafe bool Sign(double d) => 961 (*((uint*)&d + 1) >> 31) != 0; 962 } 963 } 964 } 965