fe83189fe195c441667534827b17ecb89b34f95c
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / shared / System / Number.Parsing.cs
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 using Internal.Runtime.CompilerServices;
9
10 namespace System
11 {
12     // The Parse methods provided by the numeric classes convert a
13     // string to a numeric value. The optional style parameter specifies the
14     // permitted style of the numeric string. It must be a combination of bit flags
15     // from the NumberStyles enumeration. The optional info parameter
16     // specifies the NumberFormatInfo instance to use when parsing the
17     // string. If the info parameter is null or omitted, the numeric
18     // formatting information is obtained from the current culture.
19     //
20     // Numeric strings produced by the Format methods using the Currency,
21     // Decimal, Engineering, Fixed point, General, or Number standard formats
22     // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable
23     // by the Parse methods if the NumberStyles.Any style is
24     // specified. Note, however, that the Parse methods do not accept
25     // NaNs or Infinities.
26
27     internal partial class Number
28     {
29         private const int Int32Precision = 10;
30         private const int UInt32Precision = Int32Precision;
31         private const int Int64Precision = 19;
32         private const int UInt64Precision = 20;
33
34         /// <summary>256-element map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
35         internal static readonly int[] s_charToHexLookup =
36         {
37             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
38             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
39             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
40             0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,  0x8,  0x9,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
41             0xFF, 0xA,  0xB,  0xC,  0xD,  0xE,  0xF,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
42             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
43             0xFF, 0xa,  0xb,  0xc,  0xd,  0xe,  0xf,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
44             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
45             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
46             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
47             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
48             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
49             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
50             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
51             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
52             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 255
53         };
54
55         private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
56         {
57             number.CheckConsistency();
58
59             int i = number.Scale;
60             if (i > Int32Precision || i < number.DigitsCount)
61             {
62                 return false;
63             }
64             byte* p = number.GetDigitsPointer();
65             Debug.Assert(p != null);
66             int n = 0;
67             while (--i >= 0)
68             {
69                 if ((uint)n > (0x7FFFFFFF / 10))
70                 {
71                     return false;
72                 }
73                 n *= 10;
74                 if (*p != '\0')
75                 {
76                     n += (*p++ - '0');
77                 }
78             }
79             if (number.IsNegative)
80             {
81                 n = -n;
82                 if (n > 0)
83                 {
84                     return false;
85                 }
86             }
87             else
88             {
89                 if (n < 0)
90                 {
91                     return false;
92                 }
93             }
94             value = n;
95             return true;
96         }
97
98         private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value)
99         {
100             number.CheckConsistency();
101
102             int i = number.Scale;
103             if (i > Int64Precision || i < number.DigitsCount)
104             {
105                 return false;
106             }
107             byte* p = number.GetDigitsPointer();
108             Debug.Assert(p != null);
109             long n = 0;
110             while (--i >= 0)
111             {
112                 if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10))
113                 {
114                     return false;
115                 }
116                 n *= 10;
117                 if (*p != '\0')
118                 {
119                     n += (*p++ - '0');
120                 }
121             }
122             if (number.IsNegative)
123             {
124                 n = -n;
125                 if (n > 0)
126                 {
127                     return false;
128                 }
129             }
130             else
131             {
132                 if (n < 0)
133                 {
134                     return false;
135                 }
136             }
137             value = n;
138             return true;
139         }
140
141         private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
142         {
143             number.CheckConsistency();
144
145             int i = number.Scale;
146             if (i > UInt32Precision || i < number.DigitsCount || number.IsNegative)
147             {
148                 return false;
149             }
150             byte* p = number.GetDigitsPointer();
151             Debug.Assert(p != null);
152             uint n = 0;
153             while (--i >= 0)
154             {
155                 if (n > (0xFFFFFFFF / 10))
156                 {
157                     return false;
158                 }
159                 n *= 10;
160                 if (*p != '\0')
161                 {
162                     uint newN = n + (uint)(*p++ - '0');
163                     // Detect an overflow here...
164                     if (newN < n)
165                     {
166                         return false;
167                     }
168                     n = newN;
169                 }
170             }
171             value = n;
172             return true;
173         }
174
175         private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value)
176         {
177             number.CheckConsistency();
178
179             int i = number.Scale;
180             if (i > UInt64Precision || i < number.DigitsCount || number.IsNegative)
181             {
182                 return false;
183             }
184             byte* p = number.GetDigitsPointer();
185             Debug.Assert(p != null);
186             ulong n = 0;
187             while (--i >= 0)
188             {
189                 if (n > (0xFFFFFFFFFFFFFFFF / 10))
190                 {
191                     return false;
192                 }
193                 n *= 10;
194                 if (*p != '\0')
195                 {
196                     ulong newN = n + (ulong)(*p++ - '0');
197                     // Detect an overflow here...
198                     if (newN < n)
199                     {
200                         return false;
201                     }
202                     n = newN;
203                 }
204             }
205             value = n;
206             return true;
207         }
208
209         internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
210         {
211             if (!TryParseInt32(value, styles, info, out int result, out bool failureIsOverflow))
212             {
213                 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int32));
214             }
215
216             return result;
217         }
218
219         internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
220         {
221             if (!TryParseInt64(value, styles, info, out long result, out bool failureIsOverflow))
222             {
223                 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int64));
224             }
225
226             return result;
227         }
228
229         internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
230         {
231             if (!TryParseUInt32(value, styles, info, out uint result, out bool failureIsOverflow))
232             {
233                 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt32));
234             }
235
236             return result;
237         }
238
239         internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
240         {
241             if (!TryParseUInt64(value, styles, info, out ulong result, out bool failureIsOverflow))
242             {
243                 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt64));
244             }
245
246             return result;
247         }
248
249         private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
250         {
251             Debug.Assert(str != null);
252             Debug.Assert(strEnd != null);
253             Debug.Assert(str <= strEnd);
254             Debug.Assert((styles & NumberStyles.AllowHexSpecifier) == 0);
255
256             const int StateSign = 0x0001;
257             const int StateParens = 0x0002;
258             const int StateDigits = 0x0004;
259             const int StateNonZero = 0x0008;
260             const int StateDecimal = 0x0010;
261             const int StateCurrency = 0x0020;
262
263             Debug.Assert(number.DigitsCount == 0);
264             Debug.Assert(number.Scale == 0);
265             Debug.Assert(number.IsNegative == false);
266             Debug.Assert(number.HasNonZeroTail == false);
267
268             number.CheckConsistency();
269
270             string decSep;                  // decimal separator from NumberFormatInfo.
271             string groupSep;                // group separator from NumberFormatInfo.
272             string currSymbol = null;       // currency symbol from NumberFormatInfo.
273
274             bool parsingCurrency = false;
275             if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
276             {
277                 currSymbol = info.CurrencySymbol;
278
279                 // 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.
280                 // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
281                 decSep = info.CurrencyDecimalSeparator;
282                 groupSep = info.CurrencyGroupSeparator;
283                 parsingCurrency = true;
284             }
285             else
286             {
287                 decSep = info.NumberDecimalSeparator;
288                 groupSep = info.NumberGroupSeparator;
289             }
290
291             int state = 0;
292             char* p = str;
293             char ch = p < strEnd ? *p : '\0';
294             char* next;
295
296             while (true)
297             {
298                 // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
299                 // "-Kr 1231.47" is legal but "- 1231.47" is not.
300                 if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
301                 {
302                     if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.IsNegative = true))))
303                     {
304                         state |= StateSign;
305                         p = next - 1;
306                     }
307                     else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
308                     {
309                         state |= StateSign | StateParens;
310                         number.IsNegative = true;
311                     }
312                     else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
313                     {
314                         state |= StateCurrency;
315                         currSymbol = null;
316                         // We already found the currency symbol. There should not be more currency symbols. Set
317                         // currSymbol to NULL so that we won't search it again in the later code path.
318                         p = next - 1;
319                     }
320                     else
321                     {
322                         break;
323                     }
324                 }
325                 ch = ++p < strEnd ? *p : '\0';
326             }
327
328             int digCount = 0;
329             int digEnd = 0;
330             int maxDigCount = number.Digits.Length - 1;
331
332             while (true)
333             {
334                 if (IsDigit(ch))
335                 {
336                     state |= StateDigits;
337
338                     if (ch != '0' || (state & StateNonZero) != 0)
339                     {
340                         if (digCount < maxDigCount)
341                         {
342                             number.Digits[digCount++] = (byte)(ch);
343                             if ((ch != '0') || (number.Kind != NumberBufferKind.Integer))
344                             {
345                                 digEnd = digCount;
346                             }
347                         }
348                         else if (ch != '0')
349                         {
350                             // For decimal and binary floating-point numbers, we only
351                             // need to store digits up to maxDigCount. However, we still
352                             // need to keep track of whether any additional digits past
353                             // maxDigCount were non-zero, as that can impact rounding
354                             // for an input that falls evenly between two representable
355                             // results.
356
357                             number.HasNonZeroTail = true;
358                         }
359
360                         if ((state & StateDecimal) == 0)
361                         {
362                             number.Scale++;
363                         }
364                         state |= StateNonZero;
365                     }
366                     else if ((state & StateDecimal) != 0)
367                     {
368                         number.Scale--;
369                     }
370                 }
371                 else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
372                 {
373                     state |= StateDecimal;
374                     p = next - 1;
375                 }
376                 else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null))
377                 {
378                     p = next - 1;
379                 }
380                 else
381                 {
382                     break;
383                 }
384                 ch = ++p < strEnd ? *p : '\0';
385             }
386
387             bool negExp = false;
388             number.DigitsCount = digEnd;
389             number.Digits[digEnd] = (byte)('\0');
390             if ((state & StateDigits) != 0)
391             {
392                 if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
393                 {
394                     char* temp = p;
395                     ch = ++p < strEnd ? *p : '\0';
396                     if ((next = MatchChars(p, strEnd, info.positiveSign)) != null)
397                     {
398                         ch = (p = next) < strEnd ? *p : '\0';
399                     }
400                     else if ((next = MatchChars(p, strEnd, info.negativeSign)) != null)
401                     {
402                         ch = (p = next) < strEnd ? *p : '\0';
403                         negExp = true;
404                     }
405                     if (IsDigit(ch))
406                     {
407                         int exp = 0;
408                         do
409                         {
410                             exp = exp * 10 + (ch - '0');
411                             ch = ++p < strEnd ? *p : '\0';
412                             if (exp > 1000)
413                             {
414                                 exp = 9999;
415                                 while (IsDigit(ch))
416                                 {
417                                     ch = ++p < strEnd ? *p : '\0';
418                                 }
419                             }
420                         } while (IsDigit(ch));
421                         if (negExp)
422                         {
423                             exp = -exp;
424                         }
425                         number.Scale += exp;
426                     }
427                     else
428                     {
429                         p = temp;
430                         ch = p < strEnd ? *p : '\0';
431                     }
432                 }
433                 while (true)
434                 {
435                     if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
436                     {
437                         if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.IsNegative = true))))
438                         {
439                             state |= StateSign;
440                             p = next - 1;
441                         }
442                         else if (ch == ')' && ((state & StateParens) != 0))
443                         {
444                             state &= ~StateParens;
445                         }
446                         else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
447                         {
448                             currSymbol = null;
449                             p = next - 1;
450                         }
451                         else
452                         {
453                             break;
454                         }
455                     }
456                     ch = ++p < strEnd ? *p : '\0';
457                 }
458                 if ((state & StateParens) == 0)
459                 {
460                     if ((state & StateNonZero) == 0)
461                     {
462                         if (number.Kind != NumberBufferKind.Decimal)
463                         {
464                             number.Scale = 0;
465                         }
466                         if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0)
467                         {
468                             number.IsNegative = false;
469                         }
470                     }
471                     str = p;
472                     return true;
473                 }
474             }
475             str = p;
476             return false;
477         }
478
479         internal static unsafe bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, out bool failureIsOverflow)
480         {
481             result = 0;
482             failureIsOverflow = false;
483
484             if ((styles & ~NumberStyles.Integer) == 0)
485             {
486                 // Optimized path for the common case of anything that's allowed for integer style.
487                 return TryParseInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
488             }
489
490             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
491             {
492                 return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref failureIsOverflow);
493             }
494
495             byte* pDigits = stackalloc byte[Int32NumberBufferLength];
496             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
497
498             if (!TryStringToNumber(value, styles, ref number, info))
499             {
500                 return false;
501             }
502
503             if (!TryNumberToInt32(ref number, ref result))
504             {
505                 failureIsOverflow = true;
506                 return false;
507             }
508
509             return true;
510         }
511
512         /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
513         private static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
514         {
515             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
516             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
517
518             if ((uint)value.Length < 1)
519                 goto FalseExit;
520
521             bool overflow = false;
522             int sign = 1;
523             int index = 0;
524             int num = value[0];
525
526             // Skip past any whitespace at the beginning.  
527             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
528             {
529                 do
530                 {
531                     index++;
532                     if ((uint)index >= (uint)value.Length)
533                         goto FalseExit;
534                     num = value[index];
535                 }
536                 while (IsWhite(num));
537             }
538
539             // Parse leading sign.
540             if ((styles & NumberStyles.AllowLeadingSign) != 0)
541             {
542                 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
543
544                 if (positiveSign == "+" && negativeSign == "-")
545                 {
546                     if (num == '-')
547                     {
548                         sign = -1;
549                         index++;
550                         if ((uint)index >= (uint)value.Length)
551                             goto FalseExit;
552                         num = value[index];
553                     }
554                     else if (num == '+')
555                     {
556                         index++;
557                         if ((uint)index >= (uint)value.Length)
558                             goto FalseExit;
559                         num = value[index];
560                     }
561                 }
562                 else
563                 {
564                     value = value.Slice(index);
565                     index = 0;
566                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
567                     {
568                         index += positiveSign.Length;
569                         if ((uint)index >= (uint)value.Length)
570                             goto FalseExit;
571                         num = value[index];
572                     }
573                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
574                     {
575                         sign = -1;
576                         index += negativeSign.Length;
577                         if ((uint)index >= (uint)value.Length)
578                             goto FalseExit;
579                         num = value[index];
580                     }
581                 }
582             }
583
584             int answer = 0;
585
586             if (IsDigit(num))
587             {
588                 // Skip past leading zeros.
589                 if (num == '0')
590                 {
591                     do
592                     {
593                         index++;
594                         if ((uint)index >= (uint)value.Length)
595                             goto DoneAtEndButPotentialOverflow;
596                         num = value[index];
597                     } while (num == '0');
598                     if (!IsDigit(num))
599                         goto HasTrailingChars;
600                 }
601
602                 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
603                 answer = num - '0'; // first digit
604                 index++;
605                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
606                 {
607                     if ((uint)index >= (uint)value.Length)
608                         goto DoneAtEndButPotentialOverflow;
609                     num = value[index];
610                     if (!IsDigit(num))
611                         goto HasTrailingChars;
612                     index++;
613                     answer = 10 * answer + num - '0';
614                 }
615
616                 // Potential overflow now processing the 10th digit.
617                 if ((uint)index >= (uint)value.Length)
618                     goto DoneAtEndButPotentialOverflow;
619                 num = value[index];
620                 if (!IsDigit(num))
621                     goto HasTrailingChars;
622                 index++;
623                 if (answer > int.MaxValue / 10)
624                 {
625                     overflow = true;
626                 }
627                 answer = answer * 10 + num - '0';
628                 if ((uint)answer > (uint)int.MaxValue + (-1 * sign + 1) / 2)
629                 {
630                     overflow = true;
631                 }
632                 if ((uint)index >= (uint)value.Length)
633                     goto DoneAtEndButPotentialOverflow;
634
635                 // At this point, we're either overflowing or hitting a formatting error.
636                 // Format errors take precedence for compatibility.
637                 num = value[index];
638                 while (IsDigit(num))
639                 {
640                     overflow = true;
641                     index++;
642                     if ((uint)index >= (uint)value.Length)
643                         goto DoneAtEndButPotentialOverflow;
644                     num = value[index];
645                 }
646                 goto HasTrailingChars;
647             }
648
649         FalseExit: // parsing failed
650             result = 0;
651             return false;
652
653         DoneAtEndButPotentialOverflow:
654             if (overflow)
655             {
656                 failureIsOverflow = true;
657                 goto FalseExit;
658             }
659             result = answer * sign;
660             return true;
661
662         HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
663             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
664             if (IsWhite(num))
665             {
666                 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
667                     goto FalseExit;
668                 for (index++; index < value.Length; index++)
669                 {
670                     if (!IsWhite(value[index]))
671                         break;
672                 }
673                 if ((uint)index >= (uint)value.Length)
674                     goto DoneAtEndButPotentialOverflow;
675             }
676
677             if (!TrailingZeros(value, index))
678                 goto FalseExit;
679
680             goto DoneAtEndButPotentialOverflow;
681         }
682
683         /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
684         private static bool TryParseInt64IntegerStyle(
685             ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
686         {
687             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
688             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
689
690             if ((uint)value.Length < 1)
691                 goto FalseExit;
692
693             bool overflow = false;
694             int sign = 1;
695             int index = 0;
696             int num = value[0];
697
698             // Skip past any whitespace at the beginning.  
699             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
700             {
701                 do
702                 {
703                     index++;
704                     if ((uint)index >= (uint)value.Length)
705                         goto FalseExit;
706                     num = value[index];
707                 }
708                 while (IsWhite(num));
709             }
710
711             // Parse leading sign.
712             if ((styles & NumberStyles.AllowLeadingSign) != 0)
713             {
714                 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
715
716                 if (positiveSign == "+" && negativeSign == "-")
717                 {
718                     if (num == '-')
719                     {
720                         sign = -1;
721                         index++;
722                         if ((uint)index >= (uint)value.Length)
723                             goto FalseExit;
724                         num = value[index];
725                     }
726                     else if (num == '+')
727                     {
728                         index++;
729                         if ((uint)index >= (uint)value.Length)
730                             goto FalseExit;
731                         num = value[index];
732                     }
733                 }
734                 else
735                 {
736                     value = value.Slice(index);
737                     index = 0;
738                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
739                     {
740                         index += positiveSign.Length;
741                         if ((uint)index >= (uint)value.Length)
742                             goto FalseExit;
743                         num = value[index];
744                     }
745                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
746                     {
747                         sign = -1;
748                         index += negativeSign.Length;
749                         if ((uint)index >= (uint)value.Length)
750                             goto FalseExit;
751                         num = value[index];
752                     }
753                 }
754             }
755
756             long answer = 0;
757
758             if (IsDigit(num))
759             {
760                 // Skip past leading zeros.
761                 if (num == '0')
762                 {
763                     do
764                     {
765                         index++;
766                         if ((uint)index >= (uint)value.Length)
767                             goto DoneAtEndButPotentialOverflow;
768                         num = value[index];
769                     } while (num == '0');
770                     if (!IsDigit(num))
771                         goto HasTrailingChars;
772                 }
773
774                 // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
775                 answer = num - '0'; // first digit
776                 index++;
777                 for (int i = 0; i < 17; i++) // next 17 digits can't overflow
778                 {
779                     if ((uint)index >= (uint)value.Length)
780                         goto DoneAtEndButPotentialOverflow;
781                     num = value[index];
782                     if (!IsDigit(num))
783                         goto HasTrailingChars;
784                     index++;
785                     answer = 10 * answer + num - '0';
786                 }
787
788                 // Potential overflow now processing the 19th digit.
789                 if ((uint)index >= (uint)value.Length)
790                     goto DoneAtEndButPotentialOverflow;
791                 num = value[index];
792                 if (!IsDigit(num))
793                     goto HasTrailingChars;
794                 index++;
795                 if (answer > long.MaxValue / 10)
796                 {
797                     overflow = true;
798                 }
799                 answer = answer * 10 + num - '0';
800                 if ((ulong)answer > (ulong)long.MaxValue + (ulong)((-1 * sign + 1) / 2)) // + sign => 0, - sign => 1
801                 {
802                     overflow = true;
803                 }
804                 if ((uint)index >= (uint)value.Length)
805                     goto DoneAtEndButPotentialOverflow;
806
807                 // At this point, we're either overflowing or hitting a formatting error.
808                 // Format errors take precedence for compatibility.
809                 num = value[index];
810                 while (IsDigit(num))
811                 {
812                     overflow = true;
813                     index++;
814                     if ((uint)index >= (uint)value.Length)
815                         goto DoneAtEndButPotentialOverflow;
816                     num = value[index];
817                 }
818                 goto HasTrailingChars;
819             }
820
821         FalseExit: // parsing failed
822             result = 0;
823             return false;
824
825         DoneAtEndButPotentialOverflow:
826             if (overflow)
827             {
828                 failureIsOverflow = true;
829                 goto FalseExit;
830             }
831             result = answer * sign;
832             return true;
833
834         HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
835             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
836             if (IsWhite(num))
837             {
838                 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
839                     goto FalseExit;
840                 for (index++; index < value.Length; index++)
841                 {
842                     if (!IsWhite(value[index]))
843                         break;
844                 }
845                 if ((uint)index >= (uint)value.Length)
846                     goto DoneAtEndButPotentialOverflow;
847             }
848
849             if (!TrailingZeros(value, index))
850                 goto FalseExit;
851
852             goto DoneAtEndButPotentialOverflow;
853         }
854
855         internal static unsafe bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, out bool failureIsOverflow)
856         {
857             result = 0;
858             failureIsOverflow = false;
859
860             if ((styles & ~NumberStyles.Integer) == 0)
861             {
862                 // Optimized path for the common case of anything that's allowed for integer style.
863                 return TryParseInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
864             }
865
866             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
867             {
868                 return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref failureIsOverflow);
869             }
870
871             byte* pDigits = stackalloc byte[Int64NumberBufferLength];
872             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
873
874             if (!TryStringToNumber(value, styles, ref number, info))
875             {
876                 return false;
877             }
878
879             if (!TryNumberToInt64(ref number, ref result))
880             {
881                 failureIsOverflow = true;
882                 return false;
883             }
884
885             return true;
886         }
887
888         internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, out bool failureIsOverflow)
889         {
890             result = 0;
891             failureIsOverflow = false;
892
893             if ((styles & ~NumberStyles.Integer) == 0)
894             {
895                 // Optimized path for the common case of anything that's allowed for integer style.
896                 return TryParseUInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
897             }
898
899             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
900             {
901                 return TryParseUInt32HexNumberStyle(value, styles, out result, ref failureIsOverflow);
902             }
903
904             byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
905             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
906
907             if (!TryStringToNumber(value, styles, ref number, info))
908             {
909                 return false;
910             }
911
912             if (!TryNumberToUInt32(ref number, ref result))
913             {
914                 failureIsOverflow = true;
915                 return false;
916             }
917
918             return true;
919         }
920
921         /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
922         private static bool TryParseUInt32IntegerStyle(
923             ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
924         {
925             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
926             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
927
928             if ((uint)value.Length < 1)
929                 goto FalseExit;
930
931             bool overflow = false;
932             bool hasNegativeSign = false;
933             int index = 0;
934             int num = value[0];
935
936             // Skip past any whitespace at the beginning.  
937             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
938             {
939                 do
940                 {
941                     index++;
942                     if ((uint)index >= (uint)value.Length)
943                         goto FalseExit;
944                     num = value[index];
945                 }
946                 while (IsWhite(num));
947             }
948
949             // Parse leading sign.
950             if ((styles & NumberStyles.AllowLeadingSign) != 0)
951             {
952                 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
953
954                 if (positiveSign == "+" && negativeSign == "-")
955                 {
956                     if (num == '+')
957                     {
958                         index++;
959                         if ((uint)index >= (uint)value.Length)
960                             goto FalseExit;
961                         num = value[index];
962                     }
963                     else if (num == '-')
964                     {
965                         hasNegativeSign = true;
966                         index++;
967                         if ((uint)index >= (uint)value.Length)
968                             goto FalseExit;
969                         num = value[index];
970                     }
971                 }
972                 else
973                 {
974                     value = value.Slice(index);
975                     index = 0;
976                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
977                     {
978                         index += positiveSign.Length;
979                         if ((uint)index >= (uint)value.Length)
980                             goto FalseExit;
981                         num = value[index];
982                     }
983                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
984                     {
985                         hasNegativeSign = true;
986                         index += negativeSign.Length;
987                         if ((uint)index >= (uint)value.Length)
988                             goto FalseExit;
989                         num = value[index];
990                     }
991                 }
992             }
993
994             int answer = 0;
995
996             if (IsDigit(num))
997             {
998                 // Skip past leading zeros.
999                 if (num == '0')
1000                 {
1001                     do
1002                     {
1003                         index++;
1004                         if ((uint)index >= (uint)value.Length)
1005                             goto DoneAtEndButPotentialOverflow;
1006                         num = value[index];
1007                     } while (num == '0');
1008                     if (!IsDigit(num))
1009                         goto HasTrailingChars;
1010                 }
1011
1012                 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
1013                 answer = num - '0'; // first digit
1014                 index++;
1015                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
1016                 {
1017                     if ((uint)index >= (uint)value.Length)
1018                         goto DoneAtEndButPotentialOverflow;
1019                     num = value[index];
1020                     if (!IsDigit(num))
1021                         goto HasTrailingChars;
1022                     index++;
1023                     answer = 10 * answer + num - '0';
1024                 }
1025
1026                 // Potential overflow now processing the 10th digit.
1027                 if ((uint)index >= (uint)value.Length)
1028                     goto DoneAtEndButPotentialOverflow;
1029                 num = value[index];
1030                 if (!IsDigit(num))
1031                     goto HasTrailingChars;
1032                 index++;
1033                 if ((uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5'))
1034                 {
1035                     overflow = true;
1036                 }
1037                 answer = answer * 10 + num - '0';
1038                 if ((uint)index >= (uint)value.Length)
1039                     goto DoneAtEndButPotentialOverflow;
1040
1041                 // At this point, we're either overflowing or hitting a formatting error.
1042                 // Format errors take precedence for compatibility.
1043                 num = value[index];
1044                 while (IsDigit(num))
1045                 {
1046                     overflow = true;
1047                     index++;
1048                     if ((uint)index >= (uint)value.Length)
1049                         goto DoneAtEndButPotentialOverflow;
1050                     num = value[index];
1051                 }
1052                 goto HasTrailingChars;
1053             }
1054
1055         FalseExit: // parsing failed
1056             result = 0;
1057             return false;
1058
1059         DoneAtEndButPotentialOverflow:
1060             if (overflow || (hasNegativeSign && answer != 0))
1061             {
1062                 failureIsOverflow = true;
1063                 goto FalseExit;
1064             }
1065             result = (uint)answer;
1066             return true;
1067
1068         HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1069             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1070             if (IsWhite(num))
1071             {
1072                 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1073                     goto FalseExit;
1074                 for (index++; index < value.Length; index++)
1075                 {
1076                     if (!IsWhite(value[index]))
1077                         break;
1078                 }
1079                 if ((uint)index >= (uint)value.Length)
1080                     goto DoneAtEndButPotentialOverflow;
1081             }
1082
1083             if (!TrailingZeros(value, index))
1084                 goto FalseExit;
1085
1086             goto DoneAtEndButPotentialOverflow;
1087         }
1088
1089         /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
1090         private static bool TryParseUInt32HexNumberStyle(
1091             ReadOnlySpan<char> value, NumberStyles styles, out uint result, ref bool failureIsOverflow)
1092         {
1093             Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
1094             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
1095
1096             if ((uint)value.Length < 1)
1097                 goto FalseExit;
1098
1099             bool overflow = false;
1100             int index = 0;
1101             int num = value[0];
1102             int numValue = 0;
1103
1104             // Skip past any whitespace at the beginning.  
1105             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
1106             {
1107                 do
1108                 {
1109                     index++;
1110                     if ((uint)index >= (uint)value.Length)
1111                         goto FalseExit;
1112                     num = value[index];
1113                 }
1114                 while (IsWhite(num));
1115             }
1116
1117             int answer = 0;
1118             int[] charToHexLookup = s_charToHexLookup;
1119
1120             if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1121             {
1122                 // Skip past leading zeros.
1123                 if (num == '0')
1124                 {
1125                     do
1126                     {
1127                         index++;
1128                         if ((uint)index >= (uint)value.Length)
1129                             goto DoneAtEnd;
1130                         num = value[index];
1131                     } while (num == '0');
1132                     if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
1133                         goto HasTrailingChars;
1134                 }
1135
1136                 // Parse up through 8 digits, as no overflow is possible
1137                 answer = charToHexLookup[num]; // first digit
1138                 index++;
1139                 for (int i = 0; i < 7; i++) // next 7 digits can't overflow
1140                 {
1141                     if ((uint)index >= (uint)value.Length)
1142                         goto DoneAtEnd;
1143                     num = value[index];
1144                     if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1145                         goto HasTrailingChars;
1146                     index++;
1147                     answer = 16 * answer + numValue;
1148                 }
1149
1150                 // If there's another digit, it's an overflow.
1151                 if ((uint)index >= (uint)value.Length)
1152                     goto DoneAtEnd;
1153                 num = value[index];
1154                 if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1155                     goto HasTrailingChars;
1156                 index++;
1157                 overflow = true;
1158                 if ((uint)index >= (uint)value.Length)
1159                     goto DoneAtEndButPotentialOverflow;
1160
1161                 // At this point, we're either overflowing or hitting a formatting error.
1162                 // Format errors take precedence for compatibility. Read through any remaining digits.
1163                 num = value[index];
1164                 while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1165                 {
1166                     index++;
1167                     if ((uint)index >= (uint)value.Length)
1168                         goto DoneAtEndButPotentialOverflow;
1169                     num = value[index];
1170                 }
1171                 goto HasTrailingChars;
1172             }
1173
1174         FalseExit: // parsing failed
1175             result = 0;
1176             return false;
1177
1178         DoneAtEndButPotentialOverflow:
1179             if (overflow)
1180             {
1181                 failureIsOverflow = true;
1182                 goto FalseExit;
1183             }
1184         DoneAtEnd:
1185             result = (uint)answer;
1186             return true;
1187
1188         HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1189             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1190             if (IsWhite(num))
1191             {
1192                 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1193                     goto FalseExit;
1194                 for (index++; index < value.Length; index++)
1195                 {
1196                     if (!IsWhite(value[index]))
1197                         break;
1198                 }
1199                 if ((uint)index >= (uint)value.Length)
1200                     goto DoneAtEndButPotentialOverflow;
1201             }
1202
1203             if (!TrailingZeros(value, index))
1204                 goto FalseExit;
1205
1206             goto DoneAtEndButPotentialOverflow;
1207         }
1208
1209         internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, out bool failureIsOverflow)
1210         {
1211             result = 0;
1212             failureIsOverflow = false;
1213
1214             if ((styles & ~NumberStyles.Integer) == 0)
1215             {
1216                 // Optimized path for the common case of anything that's allowed for integer style.
1217                 return TryParseUInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
1218             }
1219
1220             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
1221             {
1222                 return TryParseUInt64HexNumberStyle(value, styles, out result, ref failureIsOverflow);
1223             }
1224
1225             byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
1226             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
1227
1228             if (!TryStringToNumber(value, styles, ref number, info))
1229             {
1230                 return false;
1231             }
1232
1233             if (!TryNumberToUInt64(ref number, ref result))
1234             {
1235                 failureIsOverflow = true;
1236                 return false;
1237             }
1238
1239             return true;
1240         }
1241
1242         /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
1243         private static bool TryParseUInt64IntegerStyle(
1244             ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
1245         {
1246             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
1247             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
1248
1249             if ((uint)value.Length < 1)
1250                 goto FalseExit;
1251
1252             bool overflow = false;
1253             bool hasNegativeSign = false;
1254             int index = 0;
1255             int num = value[0];
1256
1257             // Skip past any whitespace at the beginning.  
1258             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
1259             {
1260                 do
1261                 {
1262                     index++;
1263                     if ((uint)index >= (uint)value.Length)
1264                         goto FalseExit;
1265                     num = value[index];
1266                 }
1267                 while (IsWhite(num));
1268             }
1269
1270             // Parse leading sign.
1271             if ((styles & NumberStyles.AllowLeadingSign) != 0)
1272             {
1273                 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
1274
1275                 if (positiveSign == "+" && negativeSign == "-")
1276                 {
1277                     if (num == '+')
1278                     {
1279                         index++;
1280                         if ((uint)index >= (uint)value.Length)
1281                             goto FalseExit;
1282                         num = value[index];
1283                     }
1284                     else if (num == '-')
1285                     {
1286                         hasNegativeSign = true;
1287                         index++;
1288                         if ((uint)index >= (uint)value.Length)
1289                             goto FalseExit;
1290                         num = value[index];
1291                     }
1292                 }
1293                 else
1294                 {
1295                     value = value.Slice(index);
1296                     index = 0;
1297                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
1298                     {
1299                         index += positiveSign.Length;
1300                         if ((uint)index >= (uint)value.Length)
1301                             goto FalseExit;
1302                         num = value[index];
1303                     }
1304                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
1305                     {
1306                         hasNegativeSign = true;
1307                         index += negativeSign.Length;
1308                         if ((uint)index >= (uint)value.Length)
1309                             goto FalseExit;
1310                         num = value[index];
1311                     }
1312                 }
1313             }
1314
1315             long answer = 0;
1316
1317             if (IsDigit(num))
1318             {
1319                 // Skip past leading zeros.
1320                 if (num == '0')
1321                 {
1322                     do
1323                     {
1324                         index++;
1325                         if ((uint)index >= (uint)value.Length)
1326                             goto DoneAtEndButPotentialOverflow;
1327                         num = value[index];
1328                     } while (num == '0');
1329                     if (!IsDigit(num))
1330                         goto HasTrailingChars;
1331                 }
1332
1333                 // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
1334                 answer = num - '0'; // first digit
1335                 index++;
1336                 for (int i = 0; i < 18; i++) // next 18 digits can't overflow
1337                 {
1338                     if ((uint)index >= (uint)value.Length)
1339                         goto DoneAtEndButPotentialOverflow;
1340                     num = value[index];
1341                     if (!IsDigit(num))
1342                         goto HasTrailingChars;
1343                     index++;
1344                     answer = 10 * answer + num - '0';
1345                 }
1346
1347                 // Potential overflow now processing the 20th digit.
1348                 if ((uint)index >= (uint)value.Length)
1349                     goto DoneAtEndButPotentialOverflow;
1350                 num = value[index];
1351                 if (!IsDigit(num))
1352                     goto HasTrailingChars;
1353                 index++;
1354                 if ((ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5'))
1355                 {
1356                     overflow = true;
1357                 }
1358                 answer = answer * 10 + num - '0';
1359                 if ((uint)index >= (uint)value.Length)
1360                     goto DoneAtEndButPotentialOverflow;
1361
1362                 // At this point, we're either overflowing or hitting a formatting error.
1363                 // Format errors take precedence for compatibility.
1364                 num = value[index];
1365                 while (IsDigit(num))
1366                 {
1367                     overflow = true;
1368                     index++;
1369                     if ((uint)index >= (uint)value.Length)
1370                         goto DoneAtEndButPotentialOverflow;
1371                     num = value[index];
1372                 }
1373                 goto HasTrailingChars;
1374             }
1375
1376         FalseExit: // parsing failed
1377             result = 0;
1378             return false;
1379
1380         DoneAtEndButPotentialOverflow:
1381             if (overflow || (hasNegativeSign && answer != 0))
1382             {
1383                 failureIsOverflow = true;
1384                 goto FalseExit;
1385             }
1386             result = (ulong)answer;
1387             return true;
1388
1389         HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1390             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1391             if (IsWhite(num))
1392             {
1393                 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1394                     goto FalseExit;
1395                 for (index++; index < value.Length; index++)
1396                 {
1397                     if (!IsWhite(value[index]))
1398                         break;
1399                 }
1400                 if ((uint)index >= (uint)value.Length)
1401                     goto DoneAtEndButPotentialOverflow;
1402             }
1403
1404             if (!TrailingZeros(value, index))
1405                 goto FalseExit;
1406
1407             goto DoneAtEndButPotentialOverflow;
1408         }
1409
1410         /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
1411         private static bool TryParseUInt64HexNumberStyle(
1412             ReadOnlySpan<char> value, NumberStyles styles, out ulong result, ref bool failureIsOverflow)
1413         {
1414             Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
1415             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
1416
1417             if ((uint)value.Length < 1)
1418                 goto FalseExit;
1419
1420             bool overflow = false;
1421             int index = 0;
1422             int num = value[0];
1423             int numValue = 0;
1424
1425             // Skip past any whitespace at the beginning.  
1426             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
1427             {
1428                 do
1429                 {
1430                     index++;
1431                     if ((uint)index >= (uint)value.Length)
1432                         goto FalseExit;
1433                     num = value[index];
1434                 }
1435                 while (IsWhite(num));
1436             }
1437
1438             long answer = 0;
1439             int[] charToHexLookup = s_charToHexLookup;
1440
1441             if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1442             {
1443                 // Skip past leading zeros.
1444                 if (num == '0')
1445                 {
1446                     do
1447                     {
1448                         index++;
1449                         if ((uint)index >= (uint)value.Length)
1450                             goto DoneAtEnd;
1451                         num = value[index];
1452                     } while (num == '0');
1453                     if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
1454                         goto HasTrailingChars;
1455                 }
1456
1457                 // Parse up through 16 digits, as no overflow is possible
1458                 answer = charToHexLookup[num]; // first digit
1459                 index++;
1460                 for (int i = 0; i < 15; i++) // next 15 digits can't overflow
1461                 {
1462                     if ((uint)index >= (uint)value.Length)
1463                         goto DoneAtEnd;
1464                     num = value[index];
1465                     if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1466                         goto HasTrailingChars;
1467                     index++;
1468                     answer = 16 * answer + numValue;
1469                 }
1470
1471                 // If there's another digit, it's an overflow.
1472                 if ((uint)index >= (uint)value.Length)
1473                     goto DoneAtEnd;
1474                 num = value[index];
1475                 if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1476                     goto HasTrailingChars;
1477                 index++;
1478                 overflow = true;
1479                 if ((uint)index >= (uint)value.Length)
1480                     goto DoneAtEndButPotentialOverflow;
1481
1482                 // At this point, we're either overflowing or hitting a formatting error.
1483                 // Format errors take precedence for compatibility. Read through any remaining digits.
1484                 num = value[index];
1485                 while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1486                 {
1487                     index++;
1488                     if ((uint)index >= (uint)value.Length)
1489                         goto DoneAtEndButPotentialOverflow;
1490                     num = value[index];
1491                 }
1492                 goto HasTrailingChars;
1493             }
1494
1495         FalseExit: // parsing failed
1496             result = 0;
1497             return false;
1498
1499         DoneAtEndButPotentialOverflow:
1500             if (overflow)
1501             {
1502                 failureIsOverflow = true;
1503                 goto FalseExit;
1504             }
1505         DoneAtEnd:
1506             result = (ulong)answer;
1507             return true;
1508
1509         HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1510             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1511             if (IsWhite(num))
1512             {
1513                 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1514                     goto FalseExit;
1515                 for (index++; index < value.Length; index++)
1516                 {
1517                     if (!IsWhite(value[index]))
1518                         break;
1519                 }
1520                 if ((uint)index >= (uint)value.Length)
1521                     goto DoneAtEndButPotentialOverflow;
1522             }
1523
1524             if (!TrailingZeros(value, index))
1525                 goto FalseExit;
1526
1527             goto DoneAtEndButPotentialOverflow;
1528         }
1529
1530         internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
1531         {
1532             if (!TryParseDecimal(value, styles, info, out decimal result, out bool failureIsOverflow))
1533             {
1534                 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Decimal));
1535             }
1536
1537             return result;
1538         }
1539
1540         internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
1541         {
1542             number.CheckConsistency();
1543
1544             byte* p = number.GetDigitsPointer();
1545             int e = number.Scale;
1546             bool sign = number.IsNegative;
1547             uint c = *p;
1548             if (c == 0)
1549             {
1550                 // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force
1551                 // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.)
1552                 value = new decimal(0, 0, 0, sign, (byte)Math.Clamp(-e, 0, 28));
1553                 return true;
1554             }
1555
1556             if (e > DecimalPrecision)
1557                 return false;
1558
1559             ulong low64 = 0;
1560             while (e > -28)
1561             {
1562                 e--;
1563                 low64 *= 10;
1564                 low64 += c - '0';
1565                 c = *++p;
1566                 if (low64 >= ulong.MaxValue / 10)
1567                     break;
1568                 if (c == 0)
1569                 {
1570                     while (e > 0)
1571                     {
1572                         e--;
1573                         low64 *= 10;
1574                         if (low64 >= ulong.MaxValue / 10)
1575                             break;
1576                     }
1577                     break;
1578                 }
1579             }
1580
1581             uint high = 0;
1582             while ((e > 0 || (c != 0 && e > -28)) &&
1583               (high < uint.MaxValue / 10 || (high == uint.MaxValue / 10 && (low64 < 0x99999999_99999999 || (low64 == 0x99999999_99999999 && c <= '5')))))
1584             {
1585                 // multiply by 10
1586                 ulong tmpLow = (uint)low64 * 10UL;
1587                 ulong tmp64 = (uint)(low64 >> 32) * 10UL + (tmpLow >> 32);
1588                 low64 = (uint)tmpLow + (tmp64 << 32);
1589                 high = (uint)(tmp64 >> 32) + high * 10;
1590
1591                 if (c != 0)
1592                 {
1593                     c -= '0';
1594                     low64 += c;
1595                     if (low64 < c)
1596                         high++;
1597                     c = *++p;
1598                 }
1599                 e--;
1600             }
1601
1602             if (c >= '5')
1603             {
1604                 if ((c == '5') && ((low64 & 1) == 0))
1605                 {
1606                     c = *++p;
1607
1608                     // At this point we should either be at the end of the buffer, or just
1609                     // have a single rounding digit left, and the next should be the end
1610                     Debug.Assert((c == 0) || (p[1] == 0));
1611
1612                     if (((c == 0) || c == '0') && !number.HasNonZeroTail)
1613                     {
1614                         // When the next digit is 5, the number is even, and all following digits are zero
1615                         // we don't need to round.
1616                         goto NoRounding;
1617                     }
1618                 }
1619
1620                 if (++low64 == 0 && ++high == 0)
1621                 {
1622                     low64 = 0x99999999_9999999A;
1623                     high = uint.MaxValue / 10;
1624                     e++;
1625                 }
1626             }
1627         NoRounding:
1628
1629             if (e > 0)
1630                 return false;
1631
1632             if (e <= -DecimalPrecision)
1633             {
1634                 // Parsing a large scale zero can give you more precision than fits in the decimal.
1635                 // This should only happen for actual zeros or very small numbers that round to zero.
1636                 value = new decimal(0, 0, 0, sign, DecimalPrecision - 1);
1637             }
1638             else
1639             {
1640                 value = new decimal((int)low64, (int)(low64 >> 32), (int)high, sign, (byte)-e);
1641             }
1642             return true;
1643         }
1644
1645         internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
1646         {
1647             if (!TryParseDouble(value, styles, info, out double result))
1648             {
1649                 ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
1650             }
1651
1652             return result;
1653         }
1654
1655         internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
1656         {
1657             if (!TryParseSingle(value, styles, info, out float result))
1658             {
1659                 ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
1660             }
1661
1662             return result;
1663         }
1664
1665         internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result, out bool failureIsOverflow)
1666         {
1667             byte* pDigits = stackalloc byte[DecimalNumberBufferLength];
1668             NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
1669
1670             result = 0;
1671             failureIsOverflow = false;
1672
1673             if (!TryStringToNumber(value, styles, ref number, info))
1674             {
1675                 return false;
1676             }
1677
1678             if (!TryNumberToDecimal(ref number, ref result))
1679             {
1680                 failureIsOverflow = true;
1681                 return false;
1682             }
1683
1684             return true;
1685         }
1686
1687         internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
1688         {
1689             byte* pDigits = stackalloc byte[DoubleNumberBufferLength];
1690             NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, DoubleNumberBufferLength);
1691
1692             if (!TryStringToNumber(value, styles, ref number, info))
1693             {
1694                 ReadOnlySpan<char> valueTrim = value.Trim();
1695
1696                 // This code would be simpler if we only had the concept of `InfinitySymbol`, but
1697                 // we don't so we'll check the existing cases first and then handle `PositiveSign` +
1698                 // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
1699
1700                 if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1701                 {
1702                     result = double.PositiveInfinity;
1703                 }
1704                 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
1705                 {
1706                     result = double.NegativeInfinity;
1707                 }
1708                 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1709                 {
1710                     result = double.NaN;
1711                 }
1712                 else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
1713                 {
1714                     valueTrim = valueTrim.Slice(info.PositiveSign.Length);
1715
1716                     if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1717                     {
1718                         result = double.PositiveInfinity;
1719                     }
1720                     else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1721                     {
1722                         result = double.NaN;
1723                     }
1724                     else
1725                     {
1726                         result = 0;
1727                         return false;
1728                     }
1729                 }
1730                 else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
1731                         valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
1732                 {
1733                     result = double.NaN;
1734                 }
1735                 else
1736                 {
1737                     result = 0;
1738                     return false; // We really failed
1739                 }
1740             }
1741             else
1742             {
1743                 result = NumberToDouble(ref number);
1744             }
1745
1746             return true;
1747         }
1748
1749         internal static unsafe bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
1750         {
1751             byte* pDigits = stackalloc byte[SingleNumberBufferLength];
1752             NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, SingleNumberBufferLength);
1753
1754             if (!TryStringToNumber(value, styles, ref number, info))
1755             {
1756                 ReadOnlySpan<char> valueTrim = value.Trim();
1757
1758                 // This code would be simpler if we only had the concept of `InfinitySymbol`, but
1759                 // we don't so we'll check the existing cases first and then handle `PositiveSign` +
1760                 // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
1761                 //
1762                 // Additionally, since some cultures ("wo") actually define `PositiveInfinitySymbol`
1763                 // to include `PositiveSign`, we need to check whether `PositiveInfinitySymbol` fits
1764                 // that case so that we don't start parsing things like `++infini`.
1765
1766                 if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1767                 {
1768                     result = float.PositiveInfinity;
1769                 }
1770                 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
1771                 {
1772                     result = float.NegativeInfinity;
1773                 }
1774                 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1775                 {
1776                     result = float.NaN;
1777                 }
1778                 else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
1779                 {
1780                     valueTrim = valueTrim.Slice(info.PositiveSign.Length);
1781
1782                     if (!info.PositiveInfinitySymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1783                     {
1784                         result = float.PositiveInfinity;
1785                     }
1786                     else if (!info.NaNSymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1787                     {
1788                         result = float.NaN;
1789                     }
1790                     else
1791                     {
1792                         result = 0;
1793                         return false;
1794                     }
1795                 }
1796                 else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
1797                          !info.NaNSymbol.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
1798                          valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
1799                 {
1800                     result = float.NaN;
1801                 }
1802                 else
1803                 {
1804                     result = 0;
1805                     return false; // We really failed
1806                 }
1807             }
1808             else
1809             {
1810                 result = NumberToSingle(ref number);
1811             }
1812
1813             return true;
1814         }
1815
1816         private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
1817         {
1818             if (!TryStringToNumber(value, styles, ref number, info))
1819             {
1820                 ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
1821             }
1822         }
1823
1824         internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
1825         {
1826             Debug.Assert(info != null);
1827             fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
1828             {
1829                 char* p = stringPointer;
1830                 if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
1831                     || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
1832                 {
1833                     number.CheckConsistency();
1834                     return false;
1835                 }
1836             }
1837
1838             number.CheckConsistency();
1839             return true;
1840         }
1841
1842         private static bool TrailingZeros(ReadOnlySpan<char> value, int index)
1843         {
1844             // For compatibility, we need to allow trailing zeros at the end of a number string
1845             for (int i = index; i < value.Length; i++)
1846             {
1847                 if (value[i] != '\0')
1848                 {
1849                     return false;
1850                 }
1851             }
1852
1853             return true;
1854         }
1855
1856         private static unsafe char* MatchChars(char* p, char* pEnd, string value)
1857         {
1858             Debug.Assert(p != null && pEnd != null && p <= pEnd && value != null);
1859             fixed (char* stringPointer = value)
1860             {
1861                 char* str = stringPointer;
1862                 if (*str != '\0')
1863                 {
1864                     // We only hurt the failure case
1865                     // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
1866                     // space character we use 0x20 space character instead to mean the same.
1867                     while (true)
1868                     {
1869                         char cp = p < pEnd ? *p : '\0';
1870                         if (cp != *str && !(*str == '\u00a0' && cp == '\u0020'))
1871                         {
1872                             break;
1873                         }
1874                         p++;
1875                         str++;
1876                         if (*str == '\0')
1877                             return p;
1878                     }
1879                 }
1880             }
1881
1882             return null;
1883         }
1884
1885         private static bool IsWhite(int ch) => ch == 0x20 || ((uint)(ch - 0x09) <= (0x0D - 0x09));
1886
1887         private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
1888
1889         private static void ThrowOverflowOrFormatException(bool overflow, string overflowResourceKey)
1890         {
1891             throw overflow ?
1892                new OverflowException(SR.GetResourceString(overflowResourceKey)) :
1893                (Exception)new FormatException(SR.Format_InvalidString);
1894         }
1895
1896         internal static double NumberToDouble(ref NumberBuffer number)
1897         {
1898             number.CheckConsistency();
1899
1900             ulong bits = NumberToFloatingPointBits(ref number, in FloatingPointInfo.Double);
1901             double result = BitConverter.Int64BitsToDouble((long)(bits));
1902             return number.IsNegative ? -result : result;
1903         }
1904
1905         internal static float NumberToSingle(ref NumberBuffer number)
1906         {
1907             number.CheckConsistency();
1908
1909             uint bits = (uint)(NumberToFloatingPointBits(ref number, in FloatingPointInfo.Single));
1910             float result = BitConverter.Int32BitsToSingle((int)(bits));
1911             return number.IsNegative ? -result : result;
1912         }
1913     }
1914 }