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