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