The Labs \ Source Viewer \ SSCLI \ System \ NumberBuffer

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace System
  16. {
  17.    
  18.     using System;
  19.     using System.Globalization;
  20.     using System.Runtime.CompilerServices;
  21.     // The Number class implements methods for formatting and parsing
  22.     // numeric values. To format and parse numeric values, applications should
  23.     // use the Format and Parse methods provided by the numeric
  24.     // classes (Byte, Int16, Int32, Int64,
  25.     // Single, Double, Currency, and Decimal). Those
  26.     // Format and Parse methods share a common implementation
  27.     // provided by this class, and are thus documented in detail here.
  28.     //
  29.     // Formatting
  30.     //
  31.     // The Format methods provided by the numeric classes are all of the
  32.     // form
  33.     //
  34.     // public static String Format(XXX value, String format);
  35.     // public static String Format(XXX value, String format, NumberFormatInfo info);
  36.     //
  37.     // where XXX is the name of the particular numeric class. The methods convert
  38.     // the numeric value to a string using the format string given by the
  39.     // format parameter. If the format parameter is null or
  40.     // an empty string, the number is formatted as if the string "G" (general
  41.     // format) was specified. The info parameter specifies the
  42.     // NumberFormatInfo instance to use when formatting the number. If the
  43.     // info parameter is null or omitted, the numeric formatting information
  44.     // is obtained from the current culture. The NumberFormatInfo supplies
  45.     // such information as the characters to use for decimal and thousand
  46.     // separators, and the spelling and placement of currency symbols in monetary
  47.     // values.
  48.     //
  49.     // Format strings fall into two categories: Standard format strings and
  50.     // user-defined format strings. A format string consisting of a single
  51.     // alphabetic character (A-Z or a-z), optionally followed by a sequence of
  52.     // digits (0-9), is a standard format string. All other format strings are
  53.     // used-defined format strings.
  54.     //
  55.     // A standard format string takes the form Axx, where A is an
  56.     // alphabetic character called the format specifier and xx is a
  57.     // sequence of digits called the precision specifier. The format
  58.     // specifier controls the type of formatting applied to the number and the
  59.     // precision specifier controls the number of significant digits or decimal
  60.     // places of the formatting operation. The following table describes the
  61.     // supported standard formats.
  62.     //
  63.     // C c - Currency format. The number is
  64.     // converted to a string that represents a currency amount. The conversion is
  65.     // controlled by the currency format information of the NumberFormatInfo
  66.     // used to format the number. The precision specifier indicates the desired
  67.     // number of decimal places. If the precision specifier is omitted, the default
  68.     // currency precision given by the NumberFormatInfo is used.
  69.     //
  70.     // D d - Decimal format. This format is
  71.     // supported for integral types only. The number is converted to a string of
  72.     // decimal digits, prefixed by a minus sign if the number is negative. The
  73.     // precision specifier indicates the minimum number of digits desired in the
  74.     // resulting string. If required, the number will be left-padded with zeros to
  75.     // produce the number of digits given by the precision specifier.
  76.     //
  77.     // E e Engineering (scientific) format.
  78.     // The number is converted to a string of the form
  79.     // "-d.ddd...E+ddd" or "-d.ddd...e+ddd", where each
  80.     // 'd' indicates a digit (0-9). The string starts with a minus sign if the
  81.     // number is negative, and one digit always precedes the decimal point. The
  82.     // precision specifier indicates the desired number of digits after the decimal
  83.     // point. If the precision specifier is omitted, a default of 6 digits after
  84.     // the decimal point is used. The format specifier indicates whether to prefix
  85.     // the exponent with an 'E' or an 'e'. The exponent is always consists of a
  86.     // plus or minus sign and three digits.
  87.     //
  88.     // F f Fixed point format. The number is
  89.     // converted to a string of the form "-ddd.ddd....", where each
  90.     // 'd' indicates a digit (0-9). The string starts with a minus sign if the
  91.     // number is negative. The precision specifier indicates the desired number of
  92.     // decimal places. If the precision specifier is omitted, the default numeric
  93.     // precision given by the NumberFormatInfo is used.
  94.     //
  95.     // G g - General format. The number is
  96.     // converted to the shortest possible decimal representation using fixed point
  97.     // or scientific format. The precision specifier determines the number of
  98.     // significant digits in the resulting string. If the precision specifier is
  99.     // omitted, the number of significant digits is determined by the type of the
  100.     // number being converted (10 for int, 19 for long, 7 for
  101.     // float, 15 for double, 19 for Currency, and 29 for
  102.     // Decimal). Trailing zeros after the decimal point are removed, and the
  103.     // resulting string contains a decimal point only if required. The resulting
  104.     // string uses fixed point format if the exponent of the number is less than
  105.     // the number of significant digits and greater than or equal to -4. Otherwise,
  106.     // the resulting string uses scientific format, and the case of the format
  107.     // specifier controls whether the exponent is prefixed with an 'E' or an
  108.     // 'e'.
  109.     //
  110.     // N n Number format. The number is
  111.     // converted to a string of the form "-d,ddd,ddd.ddd....", where
  112.     // each 'd' indicates a digit (0-9). The string starts with a minus sign if the
  113.     // number is negative. Thousand separators are inserted between each group of
  114.     // three digits to the left of the decimal point. The precision specifier
  115.     // indicates the desired number of decimal places. If the precision specifier
  116.     // is omitted, the default numeric precision given by the
  117.     // NumberFormatInfo is used.
  118.     //
  119.     // X x - Hexadecimal format. This format is
  120.     // supported for integral types only. The number is converted to a string of
  121.     // hexadecimal digits. The format specifier indicates whether to use upper or
  122.     // lower case characters for the hexadecimal digits above 9 ('X' for 'ABCDEF',
  123.     // and 'x' for 'abcdef'). The precision specifier indicates the minimum number
  124.     // of digits desired in the resulting string. If required, the number will be
  125.     // left-padded with zeros to produce the number of digits given by the
  126.     // precision specifier.
  127.     //
  128.     // Some examples of standard format strings and their results are shown in the
  129.     // table below. (The examples all assume a default NumberFormatInfo.)
  130.     //
  131.     // Value Format Result
  132.     // 12345.6789 C $12,345.68
  133.     // -12345.6789 C ($12,345.68)
  134.     // 12345 D 12345
  135.     // 12345 D8 00012345
  136.     // 12345.6789 E 1.234568E+004
  137.     // 12345.6789 E10 1.2345678900E+004
  138.     // 12345.6789 e4 1.2346e+004
  139.     // 12345.6789 F 12345.68
  140.     // 12345.6789 F0 12346
  141.     // 12345.6789 F6 12345.678900
  142.     // 12345.6789 G 12345.6789
  143.     // 12345.6789 G7 12345.68
  144.     // 123456789 G7 1.234568E8
  145.     // 12345.6789 N 12,345.68
  146.     // 123456789 N4 123,456,789.0000
  147.     // 0x2c45e x 2c45e
  148.     // 0x2c45e X 2C45E
  149.     // 0x2c45e X8 0002C45E
  150.     //
  151.     // Format strings that do not start with an alphabetic character, or that start
  152.     // with an alphabetic character followed by a non-digit, are called
  153.     // user-defined format strings. The following table describes the formatting
  154.     // characters that are supported in user defined format strings.
  155.     //
  156.     //
  157.     // 0 - Digit placeholder. If the value being
  158.     // formatted has a digit in the position where the '0' appears in the format
  159.     // string, then that digit is copied to the output string. Otherwise, a '0' is
  160.     // stored in that position in the output string. The position of the leftmost
  161.     // '0' before the decimal point and the rightmost '0' after the decimal point
  162.     // determines the range of digits that are always present in the output
  163.     // string.
  164.     //
  165.     // # - Digit placeholder. If the value being
  166.     // formatted has a digit in the position where the '#' appears in the format
  167.     // string, then that digit is copied to the output string. Otherwise, nothing
  168.     // is stored in that position in the output string.
  169.     //
  170.     // . - Decimal point. The first '.' character
  171.     // in the format string determines the location of the decimal separator in the
  172.     // formatted value; any additional '.' characters are ignored. The actual
  173.     // character used as a the decimal separator in the output string is given by
  174.     // the NumberFormatInfo used to format the number.
  175.     //
  176.     // , - Thousand separator and number scaling.
  177.     // The ',' character serves two purposes. First, if the format string contains
  178.     // a ',' character between two digit placeholders (0 or #) and to the left of
  179.     // the decimal point if one is present, then the output will have thousand
  180.     // separators inserted between each group of three digits to the left of the
  181.     // decimal separator. The actual character used as a the decimal separator in
  182.     // the output string is given by the NumberFormatInfo used to format the
  183.     // number. Second, if the format string contains one or more ',' characters
  184.     // immediately to the left of the decimal point, or after the last digit
  185.     // placeholder if there is no decimal point, then the number will be divided by
  186.     // 1000 times the number of ',' characters before it is formatted. For example,
  187.     // the format string '0,,' will represent 100 million as just 100. Use of the
  188.     // ',' character to indicate scaling does not also cause the formatted number
  189.     // to have thousand separators. Thus, to scale a number by 1 million and insert
  190.     // thousand separators you would use the format string '#,##0,,'.
  191.     //
  192.     // % - Percentage placeholder. The presence of
  193.     // a '%' character in the format string causes the number to be multiplied by
  194.     // 100 before it is formatted. The '%' character itself is inserted in the
  195.     // output string where it appears in the format string.
  196.     //
  197.     // E+ E- e+ e- - Scientific notation.
  198.     // If any of the strings 'E+', 'E-', 'e+', or 'e-' are present in the format
  199.     // string and are immediately followed by at least one '0' character, then the
  200.     // number is formatted using scientific notation with an 'E' or 'e' inserted
  201.     // between the number and the exponent. The number of '0' characters following
  202.     // the scientific notation indicator determines the minimum number of digits to
  203.     // output for the exponent. The 'E+' and 'e+' formats indicate that a sign
  204.     // character (plus or minus) should always precede the exponent. The 'E-' and
  205.     // 'e-' formats indicate that a sign character should only precede negative
  206.     // exponents.
  207.     //
  208.     // \ - Literal character. A backslash character
  209.     // causes the next character in the format string to be copied to the output
  210.     // string as-is. The backslash itself isn't copied, so to place a backslash
  211.     // character in the output string, use two backslashes (\\) in the format
  212.     // string.
  213.     //
  214.     // 'ABC' "ABC" - Literal string. Characters
  215.     // enclosed in single or double quotation marks are copied to the output string
  216.     // as-is and do not affect formatting.
  217.     //
  218.     // ; - Section separator. The ';' character is
  219.     // used to separate sections for positive, negative, and zero numbers in the
  220.     // format string.
  221.     //
  222.     // Other - All other characters are copied to
  223.     // the output string in the position they appear.
  224.     //
  225.     // For fixed point formats (formats not containing an 'E+', 'E-', 'e+', or
  226.     // 'e-'), the number is rounded to as many decimal places as there are digit
  227.     // placeholders to the right of the decimal point. If the format string does
  228.     // not contain a decimal point, the number is rounded to the nearest
  229.     // integer. If the number has more digits than there are digit placeholders to
  230.     // the left of the decimal point, the extra digits are copied to the output
  231.     // string immediately before the first digit placeholder.
  232.     //
  233.     // For scientific formats, the number is rounded to as many significant digits
  234.     // as there are digit placeholders in the format string.
  235.     //
  236.     // To allow for different formatting of positive, negative, and zero values, a
  237.     // user-defined format string may contain up to three sections separated by
  238.     // semicolons. The results of having one, two, or three sections in the format
  239.     // string are described in the table below.
  240.     //
  241.     // Sections:
  242.     //
  243.     // One - The format string applies to all values.
  244.     //
  245.     // Two - The first section applies to positive values
  246.     // and zeros, and the second section applies to negative values. If the number
  247.     // to be formatted is negative, but becomes zero after rounding according to
  248.     // the format in the second section, then the resulting zero is formatted
  249.     // according to the first section.
  250.     //
  251.     // Three - The first section applies to positive
  252.     // values, the second section applies to negative values, and the third section
  253.     // applies to zeros. The second section may be left empty (by having no
  254.     // characters between the semicolons), in which case the first section applies
  255.     // to all non-zero values. If the number to be formatted is non-zero, but
  256.     // becomes zero after rounding according to the format in the first or second
  257.     // section, then the resulting zero is formatted according to the third
  258.     // section.
  259.     //
  260.     // For both standard and user-defined formatting operations on values of type
  261.     // float and double, if the value being formatted is a NaN (Not
  262.     // a Number) or a positive or negative infinity, then regardless of the format
  263.     // string, the resulting string is given by the NaNSymbol,
  264.     // PositiveInfinitySymbol, or NegativeInfinitySymbol property of
  265.     // the NumberFormatInfo used to format the number.
  266.     //
  267.     // Parsing
  268.     //
  269.     // The Parse methods provided by the numeric classes are all of the form
  270.     //
  271.     // public static XXX Parse(String s);
  272.     // public static XXX Parse(String s, int style);
  273.     // public static XXX Parse(String s, int style, NumberFormatInfo info);
  274.     //
  275.     // where XXX is the name of the particular numeric class. The methods convert a
  276.     // string to a numeric value. The optional style parameter specifies the
  277.     // permitted style of the numeric string. It must be a combination of bit flags
  278.     // from the NumberStyles enumeration. The optional info parameter
  279.     // specifies the NumberFormatInfo instance to use when parsing the
  280.     // string. If the info parameter is null or omitted, the numeric
  281.     // formatting information is obtained from the current culture.
  282.     //
  283.     // Numeric strings produced by the Format methods using the Currency,
  284.     // Decimal, Engineering, Fixed point, General, or Number standard formats
  285.     // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable
  286.     // by the Parse methods if the NumberStyles.Any style is
  287.     // specified. Note, however, that the Parse methods do not accept
  288.     // NaNs or Infinities.
  289.     //
  290.     //This class contains only static members and does not need to be serializable
  291.     internal class Number
  292.     {
  293.         private Number()
  294.         {
  295.         }
  296.        
  297.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  298.         public static extern string FormatDecimal(decimal value, string format, NumberFormatInfo info);
  299.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  300.         public static extern string FormatDouble(double value, string format, NumberFormatInfo info);
  301.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  302.         public static extern string FormatInt32(int value, string format, NumberFormatInfo info);
  303.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  304.         public static extern string FormatUInt32(uint value, string format, NumberFormatInfo info);
  305.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  306.         public static extern string FormatInt64(long value, string format, NumberFormatInfo info);
  307.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  308.         public static extern string FormatUInt64(ulong value, string format, NumberFormatInfo info);
  309.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  310.         public static extern string FormatSingle(float value, string format, NumberFormatInfo info);
  311.        
  312.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  313.         unsafe public static extern bool NumberBufferToDecimal(byte* number, ref decimal value);
  314.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  315.         unsafe public static extern bool NumberBufferToDouble(byte* number, ref double value);
  316.        
  317.         // Constants used by number parsing
  318.         private const Int32 NumberMaxDigits = 50;
  319.        
  320.         private const Int32 Int32Precision = 10;
  321.         private const Int32 UInt32Precision = Int32Precision;
  322.         private const Int32 Int64Precision = 19;
  323.         private const Int32 UInt64Precision = 20;
  324.        
  325.         // NumberBuffer is a partial wrapper around a stack pointer that maps on to
  326.         // the native NUMBER struct so that it can be passed to native directly. It
  327.         // must be initialized with a stack Byte * of size NumberBufferBytes.
  328.         // For performance, this structure should attempt to be completely inlined.
  329.         //
  330.         // It should always be initialized like so:
  331.         //
  332.         // Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
  333.         // NumberBuffer number = new NumberBuffer(numberBufferBytes);
  334.         //
  335.         // For performance, when working on the buffer in managed we use the values in this
  336.         // structure, except for the digits, and pack those values into the byte buffer
  337.         // if called out to managed.
  338.         unsafe struct NumberBuffer
  339.         {
  340.            
  341.             // Enough space for NumberMaxDigit characters plus null and 3 32 bit integers
  342.             public const Int32 NumberBufferBytes = 12 + ((NumberMaxDigits + 1) * 2);
  343.             private byte* baseAddress;
  344.             public char* digits;
  345.             public Int32 precision;
  346.             public Int32 scale;
  347.             public bool sign;
  348.            
  349.             public NumberBuffer(byte* stackBuffer)
  350.             {
  351.                 this.baseAddress = stackBuffer;
  352.                 this.digits = (((char*)stackBuffer) + 6);
  353.                 this.precision = 0;
  354.                 this.scale = 0;
  355.                 this.sign = false;
  356.             }
  357.            
  358.             public byte* PackForNative()
  359.             {
  360.                 Int32* baseInteger = (Int32*)baseAddress;
  361.                 baseInteger[0] = precision;
  362.                 baseInteger[1] = scale;
  363.                 baseInteger[2] = sign ? 1 : 0;
  364.                 return baseAddress;
  365.             }
  366.         }
  367.        
  368.         private static bool HexNumberToInt32(ref NumberBuffer number, ref Int32 value)
  369.         {
  370.             UInt32 passedValue = 0;
  371.             bool returnValue = HexNumberToUInt32(ref number, ref passedValue);
  372.             value = (Int32)passedValue;
  373.             return returnValue;
  374.         }
  375.        
  376.         private static bool HexNumberToInt64(ref NumberBuffer number, ref Int64 value)
  377.         {
  378.             UInt64 passedValue = 0;
  379.             bool returnValue = HexNumberToUInt64(ref number, ref passedValue);
  380.             value = (Int64)passedValue;
  381.             return returnValue;
  382.         }
  383.        
  384.         unsafe private static bool HexNumberToUInt32(ref NumberBuffer number, ref UInt32 value)
  385.         {
  386.            
  387.             Int32 i = number.scale;
  388.             if (i > UInt32Precision || i < number.precision) {
  389.                 return false;
  390.             }
  391.             char* p = number.digits;
  392.             BCLDebug.Assert(p != null, "");
  393.            
  394.             UInt32 n = 0;
  395.             while (--i >= 0) {
  396.                 if (n > ((UInt32)4294967295u / 16)) {
  397.                     return false;
  398.                 }
  399.                 n *= 16;
  400.                 if (*p != '\0') {
  401.                     UInt32 newN = n;
  402.                     if (*p != '\0') {
  403.                         if (*p >= '0' && *p <= '9') {
  404.                             newN += (UInt32)(*p - '0');
  405.                         }
  406.                         else {
  407.                             if (*p >= 'A' && *p <= 'F') {
  408.                                 newN += (UInt32)((*p - 'A') + 10);
  409.                             }
  410.                             else {
  411.                                 BCLDebug.Assert(*p >= 'a' && *p <= 'f', "");
  412.                                 newN += (UInt32)((*p - 'a') + 10);
  413.                             }
  414.                         }
  415.                         p++;
  416.                     }
  417.                    
  418.                     // Detect an overflow here...
  419.                     if (newN < n) {
  420.                         return false;
  421.                     }
  422.                     n = newN;
  423.                 }
  424.             }
  425.             value = n;
  426.             return true;
  427.         }
  428.        
  429.         unsafe private static bool HexNumberToUInt64(ref NumberBuffer number, ref UInt64 value)
  430.         {
  431.            
  432.             Int32 i = number.scale;
  433.             if (i > UInt64Precision || i < number.precision) {
  434.                 return false;
  435.             }
  436.             char* p = number.digits;
  437.             BCLDebug.Assert(p != null, "");
  438.            
  439.             UInt64 n = 0;
  440.             while (--i >= 0) {
  441.                 if (n > (18446744073709551615ul / 16)) {
  442.                     return false;
  443.                 }
  444.                 n *= 16;
  445.                 if (*p != '\0') {
  446.                     UInt64 newN = n;
  447.                     if (*p != '\0') {
  448.                         if (*p >= '0' && *p <= '9') {
  449.                             newN += (UInt64)(*p - '0');
  450.                         }
  451.                         else {
  452.                             if (*p >= 'A' && *p <= 'F') {
  453.                                 newN += (UInt64)((*p - 'A') + 10);
  454.                             }
  455.                             else {
  456.                                 BCLDebug.Assert(*p >= 'a' && *p <= 'f', "");
  457.                                 newN += (UInt64)((*p - 'a') + 10);
  458.                             }
  459.                         }
  460.                         p++;
  461.                     }
  462.                    
  463.                     // Detect an overflow here...
  464.                     if (newN < n) {
  465.                         return false;
  466.                     }
  467.                     n = newN;
  468.                 }
  469.             }
  470.             value = n;
  471.             return true;
  472.         }
  473.        
  474.         private static bool IsWhite(char ch)
  475.         {
  476.             return (((ch) == 32) || ((ch) >= 9 && (ch) <= 13));
  477.         }
  478.        
  479.         unsafe private static bool NumberToInt32(ref NumberBuffer number, ref Int32 value)
  480.         {
  481.            
  482.             Int32 i = number.scale;
  483.             if (i > Int32Precision || i < number.precision) {
  484.                 return false;
  485.             }
  486.             char* p = number.digits;
  487.             BCLDebug.Assert(p != null, "");
  488.             Int32 n = 0;
  489.             while (--i >= 0) {
  490.                 if ((UInt32)n > (2147483647 / 10)) {
  491.                     return false;
  492.                 }
  493.                 n *= 10;
  494.                 if (*p != '\0') {
  495.                     n += (Int32)(*p++ - '0');
  496.                 }
  497.             }
  498.             if (number.sign) {
  499.                 n = -n;
  500.                 if (n > 0) {
  501.                     return false;
  502.                 }
  503.             }
  504.             else {
  505.                 if (n < 0) {
  506.                     return false;
  507.                 }
  508.             }
  509.             value = n;
  510.             return true;
  511.         }
  512.        
  513.         unsafe private static bool NumberToInt64(ref NumberBuffer number, ref Int64 value)
  514.         {
  515.            
  516.             Int32 i = number.scale;
  517.             if (i > Int64Precision || i < number.precision) {
  518.                 return false;
  519.             }
  520.             char* p = number.digits;
  521.             BCLDebug.Assert(p != null, "");
  522.             Int64 n = 0;
  523.             while (--i >= 0) {
  524.                 if ((UInt64)n > (9223372036854775807l / 10)) {
  525.                     return false;
  526.                 }
  527.                 n *= 10;
  528.                 if (*p != '\0') {
  529.                     n += (Int32)(*p++ - '0');
  530.                 }
  531.             }
  532.             if (number.sign) {
  533.                 n = -n;
  534.                 if (n > 0) {
  535.                     return false;
  536.                 }
  537.             }
  538.             else {
  539.                 if (n < 0) {
  540.                     return false;
  541.                 }
  542.             }
  543.             value = n;
  544.             return true;
  545.         }
  546.        
  547.         unsafe private static bool NumberToUInt32(ref NumberBuffer number, ref UInt32 value)
  548.         {
  549.            
  550.             Int32 i = number.scale;
  551.             if (i > UInt32Precision || i < number.precision || number.sign) {
  552.                 return false;
  553.             }
  554.             char* p = number.digits;
  555.             BCLDebug.Assert(p != null, "");
  556.             UInt32 n = 0;
  557.             while (--i >= 0) {
  558.                 if (n > (4294967295u / 10)) {
  559.                     return false;
  560.                 }
  561.                 n *= 10;
  562.                 if (*p != '\0') {
  563.                     UInt32 newN = n + (UInt32)(*p++ - '0');
  564.                     // Detect an overflow here...
  565.                     if (newN < n) {
  566.                         return false;
  567.                     }
  568.                     n = newN;
  569.                 }
  570.             }
  571.             value = n;
  572.             return true;
  573.         }
  574.        
  575.         unsafe private static bool NumberToUInt64(ref NumberBuffer number, ref UInt64 value)
  576.         {
  577.            
  578.             Int32 i = number.scale;
  579.             if (i > UInt64Precision || i < number.precision || number.sign) {
  580.                 return false;
  581.             }
  582.             char* p = number.digits;
  583.             BCLDebug.Assert(p != null, "");
  584.             UInt64 n = 0;
  585.             while (--i >= 0) {
  586.                 if (n > (18446744073709551615ul / 10)) {
  587.                     return false;
  588.                 }
  589.                 n *= 10;
  590.                 if (*p != '\0') {
  591.                     UInt64 newN = n + (UInt64)(*p++ - '0');
  592.                     // Detect an overflow here...
  593.                     if (newN < n) {
  594.                         return false;
  595.                     }
  596.                     n = newN;
  597.                 }
  598.             }
  599.             value = n;
  600.             return true;
  601.         }
  602.        
  603.         unsafe private static char* MatchChars(char* p, string str)
  604.         {
  605.             fixed (char* stringPointer = str) {
  606.                 return MatchChars(p, stringPointer);
  607.             }
  608.         }
  609.         unsafe private static char* MatchChars(char* p, char* str)
  610.         {
  611.             BCLDebug.Assert(p != null && str != null, "");
  612.            
  613.             if (*str == '\0') {
  614.                 return null;
  615.             }
  616.             for (; (*str != '\0'); p++,str++) {
  617.                 if (*p != *str) {
  618.                     //We only hurt the failure case
  619.                     if ((*str == ' ') && (*p == ' ')) {
  620.                         // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
  621.                         // space character we use 0x20 space character instead to mean the same.
  622.                         continue;
  623.                     }
  624.                     return null;
  625.                 }
  626.             }
  627.             return p;
  628.         }
  629.        
  630.         unsafe static internal decimal ParseDecimal(string value, NumberStyles options, NumberFormatInfo numfmt)
  631.         {
  632.            
  633.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  634.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  635.             decimal result = 0;
  636.            
  637.             StringToNumber(value, options, ref number, numfmt, true);
  638.            
  639.             if (!NumberBufferToDecimal(number.PackForNative(), ref result)) {
  640.                 throw new OverflowException(Environment.GetResourceString("Overflow_Decimal"));
  641.             }
  642.             return result;
  643.         }
  644.        
  645.         unsafe static internal double ParseDouble(string value, NumberStyles options, NumberFormatInfo numfmt)
  646.         {
  647.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  648.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  649.             double d = 0;
  650.            
  651.             StringToNumber(value, options, ref number, numfmt, false);
  652.            
  653.             if (!NumberBufferToDouble(number.PackForNative(), ref d)) {
  654.                 throw new OverflowException(Environment.GetResourceString("Overflow_Double"));
  655.             }
  656.            
  657.             return d;
  658.         }
  659.        
  660.         unsafe static internal Int32 ParseInt32(string s, NumberStyles style, NumberFormatInfo info)
  661.         {
  662.            
  663.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  664.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  665.             Int32 i = 0;
  666.            
  667.             StringToNumber(s, style, ref number, info, false);
  668.            
  669.             if ((style & NumberStyles.AllowHexSpecifier) != 0) {
  670.                 if (!HexNumberToInt32(ref number, ref i)) {
  671.                     throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
  672.                 }
  673.             }
  674.             else {
  675.                 if (!NumberToInt32(ref number, ref i)) {
  676.                     throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
  677.                 }
  678.             }
  679.             return i;
  680.         }
  681.        
  682.         unsafe static internal Int64 ParseInt64(string value, NumberStyles options, NumberFormatInfo numfmt)
  683.         {
  684.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  685.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  686.             Int64 i = 0;
  687.            
  688.             StringToNumber(value, options, ref number, numfmt, false);
  689.            
  690.             if ((options & NumberStyles.AllowHexSpecifier) != 0) {
  691.                 if (!HexNumberToInt64(ref number, ref i)) {
  692.                     throw new OverflowException(Environment.GetResourceString("Overflow_Int64"));
  693.                 }
  694.             }
  695.             else {
  696.                 if (!NumberToInt64(ref number, ref i)) {
  697.                     throw new OverflowException(Environment.GetResourceString("Overflow_Int64"));
  698.                 }
  699.             }
  700.             return i;
  701.         }
  702.        
  703.         unsafe private static bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
  704.         {
  705.            
  706.             const Int32 StateSign = 1;
  707.             const Int32 StateParens = 2;
  708.             const Int32 StateDigits = 4;
  709.             const Int32 StateNonZero = 8;
  710.             const Int32 StateDecimal = 16;
  711.             const Int32 StateCurrency = 32;
  712.            
  713.             number.scale = 0;
  714.             number.sign = false;
  715.             string decSep;
  716.             // decimal separator from NumberFormatInfo.
  717.             string groupSep;
  718.             // group separator from NumberFormatInfo.
  719.             string currSymbol = null;
  720.             // currency symbol from NumberFormatInfo.
  721.             // The alternative currency symbol used in Win9x ANSI codepage, that can not roundtrip between ANSI and Unicode.
  722.             // Currently, only ja-JP and ko-KR has non-null values (which is U+005c, backslash)
  723.             string ansicurrSymbol = null;
  724.             // currency symbol from NumberFormatInfo.
  725.             string altdecSep = null;
  726.             // decimal separator from NumberFormatInfo as a decimal
  727.             string altgroupSep = null;
  728.             // group separator from NumberFormatInfo as a decimal
  729.             bool parsingCurrency = false;
  730.             if ((options & NumberStyles.AllowCurrencySymbol) != 0) {
  731.                 currSymbol = numfmt.CurrencySymbol;
  732.                 if (numfmt.ansiCurrencySymbol != null) {
  733.                     ansicurrSymbol = numfmt.ansiCurrencySymbol;
  734.                 }
  735.                 // The idea here to match the curreny separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
  736.                 // The values of decSep are setup to use the correct relevant seperator (currency in the if part and decimal in the else part).
  737.                 altdecSep = numfmt.NumberDecimalSeparator;
  738.                 altgroupSep = numfmt.NumberGroupSeparator;
  739.                 decSep = numfmt.CurrencyDecimalSeparator;
  740.                 groupSep = numfmt.CurrencyGroupSeparator;
  741.                 parsingCurrency = true;
  742.             }
  743.             else {
  744.                 decSep = numfmt.NumberDecimalSeparator;
  745.                 groupSep = numfmt.NumberGroupSeparator;
  746.             }
  747.            
  748.             Int32 state = 0;
  749.             bool signflag = false;
  750.             // Cache the results of "options & PARSE_LEADINGSIGN && !(state & STATE_SIGN)" to avoid doing this twice
  751.             char* p = str;
  752.             char ch = *p;
  753.             char* next;
  754.            
  755.             while (true) {
  756.                 //Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
  757.                 //"-Kr 1231.47" is legal but "- 1231.47" is not.
  758.                 if (IsWhite(ch) && ((options & NumberStyles.AllowLeadingWhite) != 0) && (((state & StateSign) == 0) || (((state & StateSign) != 0) && (((state & StateCurrency) != 0) || numfmt.numberNegativePattern == 2)))) {
  759.                     // Do nothing here. We will increase p at the end of the loop.
  760.                 }
  761.                 else if ((signflag = (((options & NumberStyles.AllowLeadingSign) != 0) && ((state & StateSign) == 0))) && ((next = MatchChars(p, numfmt.positiveSign)) != null)) {
  762.                     state |= StateSign;
  763.                     p = next - 1;
  764.                 }
  765.                 else if (signflag && (next = MatchChars(p, numfmt.negativeSign)) != null) {
  766.                     state |= StateSign;
  767.                     number.sign = true;
  768.                     p = next - 1;
  769.                 }
  770.                 else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0)) {
  771.                     state |= StateSign | StateParens;
  772.                     number.sign = true;
  773.                 }
  774.                 else if ((currSymbol != null && (next = MatchChars(p, currSymbol)) != null) || (ansicurrSymbol != null && (next = MatchChars(p, ansicurrSymbol)) != null)) {
  775.                     state |= StateCurrency;
  776.                     currSymbol = null;
  777.                     ansicurrSymbol = null;
  778.                     // We already found the currency symbol. There should not be more currency symbols. Set
  779.                     // currSymbol to NULL so that we won't search it again in the later code path.
  780.                     p = next - 1;
  781.                 }
  782.                 else {
  783.                     break;
  784.                 }
  785.                 ch = *++p;
  786.             }
  787.             Int32 digCount = 0;
  788.             Int32 digEnd = 0;
  789.             while (true) {
  790.                 if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))) {
  791.                     state |= StateDigits;
  792.                     if (ch != '0' || (state & StateNonZero) != 0) {
  793.                         if (digCount < NumberMaxDigits) {
  794.                             number.digits[digCount++] = ch;
  795.                             if (ch != '0' || parseDecimal) {
  796.                                 digEnd = digCount;
  797.                             }
  798.                         }
  799.                         if ((state & StateDecimal) == 0) {
  800.                             number.scale++;
  801.                         }
  802.                         state |= StateNonZero;
  803.                     }
  804.                     else if ((state & StateDecimal) != 0) {
  805.                         number.scale--;
  806.                     }
  807.                 }
  808.                 else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, altdecSep)) != null)) {
  809.                     state |= StateDecimal;
  810.                     p = next - 1;
  811.                 }
  812.                 else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, altgroupSep)) != null)) {
  813.                     p = next - 1;
  814.                 }
  815.                 else {
  816.                     break;
  817.                 }
  818.                 ch = *++p;
  819.             }
  820.            
  821.             bool negExp = false;
  822.             number.precision = digEnd;
  823.             number.digits[digEnd] = '\0';
  824.             if ((state & StateDigits) != 0) {
  825.                 if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0)) {
  826.                     char* temp = p;
  827.                     ch = *++p;
  828.                     if ((next = MatchChars(p, numfmt.positiveSign)) != null) {
  829.                         ch = *(p = next);
  830.                     }
  831.                     else if ((next = MatchChars(p, numfmt.negativeSign)) != null) {
  832.                         ch = *(p = next);
  833.                         negExp = true;
  834.                     }
  835.                     if (ch >= '0' && ch <= '9') {
  836.                         Int32 exp = 0;
  837.                         do {
  838.                             exp = exp * 10 + (ch - '0');
  839.                             ch = *++p;
  840.                             if (exp > 1000) {
  841.                                 exp = 9999;
  842.                                 while (ch >= '0' && ch <= '9') {
  843.                                     ch = *++p;
  844.                                 }
  845.                             }
  846.                         }
  847.                         while (ch >= '0' && ch <= '9');
  848.                         if (negExp) {
  849.                             exp = -exp;
  850.                         }
  851.                         number.scale += exp;
  852.                     }
  853.                     else {
  854.                         p = temp;
  855.                         ch = *p;
  856.                     }
  857.                 }
  858.                 while (true) {
  859.                     if (IsWhite(ch) && ((options & NumberStyles.AllowTrailingWhite) != 0)) {
  860.                     }
  861.                     else if ((signflag = (((options & NumberStyles.AllowTrailingSign) != 0) && ((state & StateSign) == 0))) && (next = MatchChars(p, numfmt.positiveSign)) != null) {
  862.                         state |= StateSign;
  863.                         p = next - 1;
  864.                     }
  865.                     else if (signflag && (next = MatchChars(p, numfmt.negativeSign)) != null) {
  866.                         state |= StateSign;
  867.                         number.sign = true;
  868.                         p = next - 1;
  869.                     }
  870.                     else if (ch == ')' && ((state & StateParens) != 0)) {
  871.                         state &= ~StateParens;
  872.                     }
  873.                     else if ((currSymbol != null && (next = MatchChars(p, currSymbol)) != null) || (ansicurrSymbol != null && (next = MatchChars(p, ansicurrSymbol)) != null)) {
  874.                         currSymbol = null;
  875.                         ansicurrSymbol = null;
  876.                         p = next - 1;
  877.                     }
  878.                     else {
  879.                         break;
  880.                     }
  881.                     ch = *++p;
  882.                 }
  883.                 if ((state & StateParens) == 0) {
  884.                     if ((state & StateNonZero) == 0) {
  885.                         if (!parseDecimal) {
  886.                             number.scale = 0;
  887.                         }
  888.                         if ((state & StateDecimal) == 0) {
  889.                             number.sign = false;
  890.                         }
  891.                     }
  892.                     str = p;
  893.                     return true;
  894.                 }
  895.             }
  896.             str = p;
  897.             return false;
  898.         }
  899.        
  900.         unsafe static internal float ParseSingle(string value, NumberStyles options, NumberFormatInfo numfmt)
  901.         {
  902.            
  903.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  904.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  905.             double d = 0;
  906.            
  907.             StringToNumber(value, options, ref number, numfmt, false);
  908.            
  909.             if (!NumberBufferToDouble(number.PackForNative(), ref d)) {
  910.                 throw new OverflowException(Environment.GetResourceString("Overflow_Single"));
  911.             }
  912.             float castSingle = (float)d;
  913.             if (Single.IsInfinity(castSingle)) {
  914.                 throw new OverflowException(Environment.GetResourceString("Overflow_Single"));
  915.             }
  916.             return castSingle;
  917.         }
  918.        
  919.         unsafe static internal UInt32 ParseUInt32(string value, NumberStyles options, NumberFormatInfo numfmt)
  920.         {
  921.            
  922.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  923.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  924.             UInt32 i = 0;
  925.            
  926.             StringToNumber(value, options, ref number, numfmt, false);
  927.            
  928.             if ((options & NumberStyles.AllowHexSpecifier) != 0) {
  929.                 if (!HexNumberToUInt32(ref number, ref i)) {
  930.                     throw new OverflowException(Environment.GetResourceString("Overflow_UInt32"));
  931.                 }
  932.             }
  933.             else {
  934.                 if (!NumberToUInt32(ref number, ref i)) {
  935.                     throw new OverflowException(Environment.GetResourceString("Overflow_UInt32"));
  936.                 }
  937.             }
  938.            
  939.             return i;
  940.         }
  941.        
  942.         unsafe static internal UInt64 ParseUInt64(string value, NumberStyles options, NumberFormatInfo numfmt)
  943.         {
  944.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  945.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  946.             UInt64 i = 0;
  947.            
  948.             StringToNumber(value, options, ref number, numfmt, false);
  949.             if ((options & NumberStyles.AllowHexSpecifier) != 0) {
  950.                 if (!HexNumberToUInt64(ref number, ref i)) {
  951.                     throw new OverflowException(Environment.GetResourceString("Overflow_UInt64"));
  952.                 }
  953.             }
  954.             else {
  955.                 if (!NumberToUInt64(ref number, ref i)) {
  956.                     throw new OverflowException(Environment.GetResourceString("Overflow_UInt64"));
  957.                 }
  958.             }
  959.             return i;
  960.         }
  961.        
  962.         unsafe private static void StringToNumber(string str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
  963.         {
  964.            
  965.             if (str == null) {
  966.                 throw new ArgumentNullException("String");
  967.             }
  968.             BCLDebug.Assert(info != null, "");
  969.             fixed (char* stringPointer = str) {
  970.                 char* p = stringPointer;
  971.                 if (!ParseNumber(ref p, options, ref number, info, parseDecimal) || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) {
  972.                     throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
  973.                 }
  974.             }
  975.         }
  976.        
  977.         private static bool TrailingZeros(string s, Int32 index)
  978.         {
  979.             // For compatability, we need to allow trailing zeros at the end of a number string
  980.             for (int i = index; i < s.Length; i++) {
  981.                 if (s[i] != '\0') {
  982.                     return false;
  983.                 }
  984.             }
  985.             return true;
  986.         }
  987.        
  988.         unsafe static internal bool TryParseDecimal(string value, NumberStyles options, NumberFormatInfo numfmt, out decimal result)
  989.         {
  990.            
  991.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  992.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  993.             result = 0;
  994.            
  995.             if (!TryStringToNumber(value, options, ref number, numfmt, true)) {
  996.                 return false;
  997.             }
  998.            
  999.             if (!NumberBufferToDecimal(number.PackForNative(), ref result)) {
  1000.                 return false;
  1001.             }
  1002.             return true;
  1003.         }
  1004.        
  1005.         unsafe static internal bool TryParseDouble(string value, NumberStyles options, NumberFormatInfo numfmt, out double result)
  1006.         {
  1007.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  1008.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  1009.             result = 0;
  1010.            
  1011.            
  1012.             if (!TryStringToNumber(value, options, ref number, numfmt, false)) {
  1013.                 return false;
  1014.             }
  1015.             if (!NumberBufferToDouble(number.PackForNative(), ref result)) {
  1016.                 return false;
  1017.             }
  1018.             return true;
  1019.         }
  1020.        
  1021.         unsafe static internal bool TryParseInt32(string s, NumberStyles style, NumberFormatInfo info, out Int32 result)
  1022.         {
  1023.            
  1024.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  1025.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  1026.             result = 0;
  1027.            
  1028.             if (!TryStringToNumber(s, style, ref number, info, false)) {
  1029.                 return false;
  1030.             }
  1031.            
  1032.             if ((style & NumberStyles.AllowHexSpecifier) != 0) {
  1033.                 if (!HexNumberToInt32(ref number, ref result)) {
  1034.                     return false;
  1035.                 }
  1036.             }
  1037.             else {
  1038.                 if (!NumberToInt32(ref number, ref result)) {
  1039.                     return false;
  1040.                 }
  1041.             }
  1042.             return true;
  1043.         }
  1044.        
  1045.         unsafe static internal bool TryParseInt64(string s, NumberStyles style, NumberFormatInfo info, out Int64 result)
  1046.         {
  1047.            
  1048.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  1049.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  1050.             result = 0;
  1051.            
  1052.             if (!TryStringToNumber(s, style, ref number, info, false)) {
  1053.                 return false;
  1054.             }
  1055.            
  1056.             if ((style & NumberStyles.AllowHexSpecifier) != 0) {
  1057.                 if (!HexNumberToInt64(ref number, ref result)) {
  1058.                     return false;
  1059.                 }
  1060.             }
  1061.             else {
  1062.                 if (!NumberToInt64(ref number, ref result)) {
  1063.                     return false;
  1064.                 }
  1065.             }
  1066.             return true;
  1067.         }
  1068.        
  1069.         unsafe static internal bool TryParseSingle(string value, NumberStyles options, NumberFormatInfo numfmt, out float result)
  1070.         {
  1071.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  1072.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  1073.             result = 0;
  1074.             double d = 0;
  1075.            
  1076.             if (!TryStringToNumber(value, options, ref number, numfmt, false)) {
  1077.                 return false;
  1078.             }
  1079.             if (!NumberBufferToDouble(number.PackForNative(), ref d)) {
  1080.                 return false;
  1081.             }
  1082.             float castSingle = (float)d;
  1083.             if (Single.IsInfinity(castSingle)) {
  1084.                 return false;
  1085.             }
  1086.            
  1087.             result = castSingle;
  1088.             return true;
  1089.         }
  1090.        
  1091.         unsafe static internal bool TryParseUInt32(string s, NumberStyles style, NumberFormatInfo info, out UInt32 result)
  1092.         {
  1093.            
  1094.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  1095.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  1096.             result = 0;
  1097.            
  1098.             if (!TryStringToNumber(s, style, ref number, info, false)) {
  1099.                 return false;
  1100.             }
  1101.            
  1102.             if ((style & NumberStyles.AllowHexSpecifier) != 0) {
  1103.                 if (!HexNumberToUInt32(ref number, ref result)) {
  1104.                     return false;
  1105.                 }
  1106.             }
  1107.             else {
  1108.                 if (!NumberToUInt32(ref number, ref result)) {
  1109.                     return false;
  1110.                 }
  1111.             }
  1112.             return true;
  1113.         }
  1114.        
  1115.         unsafe static internal bool TryParseUInt64(string s, NumberStyles style, NumberFormatInfo info, out UInt64 result)
  1116.         {
  1117.            
  1118.             byte* numberBufferBytes = stackalloc byte[NumberBuffer.NumberBufferBytes];
  1119.             NumberBuffer number = new NumberBuffer(numberBufferBytes);
  1120.             result = 0;
  1121.            
  1122.             if (!TryStringToNumber(s, style, ref number, info, false)) {
  1123.                 return false;
  1124.             }
  1125.            
  1126.             if ((style & NumberStyles.AllowHexSpecifier) != 0) {
  1127.                 if (!HexNumberToUInt64(ref number, ref result)) {
  1128.                     return false;
  1129.                 }
  1130.             }
  1131.             else {
  1132.                 if (!NumberToUInt64(ref number, ref result)) {
  1133.                     return false;
  1134.                 }
  1135.             }
  1136.             return true;
  1137.         }
  1138.        
  1139.         unsafe private static bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
  1140.         {
  1141.            
  1142.             if (str == null) {
  1143.                 return false;
  1144.             }
  1145.             BCLDebug.Assert(numfmt != null, "");
  1146.            
  1147.             fixed (char* stringPointer = str) {
  1148.                 char* p = stringPointer;
  1149.                 if (!ParseNumber(ref p, options, ref number, numfmt, parseDecimal) || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) {
  1150.                     return false;
  1151.                 }
  1152.             }
  1153.            
  1154.             return true;
  1155.         }
  1156.     }
  1157. }

Developer Fusion