Exemplo n.º 1
0
        private static bool TryParseSByteN(ReadOnlySpan <byte> source, out sbyte value, out int bytesConsumed)
        {
            if (source.Length < 1)
            {
                goto FalseExit;
            }

            int sign  = 1;
            int index = 0;
            int c     = source[index];

            if (c == '-')
            {
                sign = -1;
                index++;
                if ((uint)index >= (uint)source.Length)
                {
                    goto FalseExit;
                }
                c = source[index];
            }
            else if (c == '+')
            {
                index++;
                if ((uint)index >= (uint)source.Length)
                {
                    goto FalseExit;
                }
                c = source[index];
            }

            int answer;

            // Handle the first digit (or period) as a special case. This ensures some compatible edge-case behavior with the classic parse routines
            // (at least one digit must precede any commas, and a string without any digits prior to the decimal point must have at least
            // one digit after the decimal point.)
            if (c == Utf8Constants.Period)
            {
                goto FractionalPartWithoutLeadingDigits;
            }
            if (!ParserHelpers.IsDigit(c))
            {
                goto FalseExit;
            }
            answer = c - '0';

            for (; ;)
            {
                index++;
                if ((uint)index >= (uint)source.Length)
                {
                    goto Done;
                }

                c = source[index];
                if (c == Utf8Constants.Comma)
                {
                    continue;
                }

                if (c == Utf8Constants.Period)
                {
                    goto FractionalDigits;
                }

                if (!ParserHelpers.IsDigit(c))
                {
                    goto Done;
                }

                answer = answer * 10 + c - '0';

                // if sign < 0, (-1 * sign + 1) / 2 = 1
                // else, (-1 * sign + 1) / 2 = 0
                if (answer > sbyte.MaxValue + (-1 * sign + 1) / 2)
                {
                    goto FalseExit; // Overflow
                }
            }

FractionalPartWithoutLeadingDigits: // If we got here, we found a decimal point before we found any digits. This is legal as long as there's at least one zero after the decimal point.
            answer = 0;
            index++;
            if ((uint)index >= (uint)source.Length)
            {
                goto FalseExit;
            }
            if (source[index] != '0')
            {
                goto FalseExit;
            }

FractionalDigits: // "N" format allows a fractional portion despite being an integer format but only if the post-fraction digits are all 0.
            do
            {
                index++;
                if ((uint)index >= (uint)source.Length)
                {
                    goto Done;
                }
                c = source[index];
            }while (c == '0');

            if (ParserHelpers.IsDigit(c))
            {
                goto FalseExit; // The fractional portion contained a non-zero digit. Treat this as an error, not an early termination.
            }
            goto Done;

FalseExit:
            bytesConsumed = default;
            value         = default;
            return(false);

Done:
            bytesConsumed = index;
            value         = (sbyte)(answer * sign);
            return(true);
        }
Exemplo n.º 2
0
        private static bool TryParseUInt16D(ReadOnlySpan <byte> source, out ushort value, out int bytesConsumed)
        {
            if (source.Length < 1)
            {
                goto FalseExit;
            }

            int index  = 0;
            int num    = source[index];
            int answer = 0;

            if (ParserHelpers.IsDigit(num))
            {
                if (num == '0')
                {
                    do
                    {
                        index++;
                        if ((uint)index >= (uint)source.Length)
                        {
                            goto Done;
                        }
                        num = source[index];
                    } while (num == '0');
                    if (!ParserHelpers.IsDigit(num))
                    {
                        goto Done;
                    }
                }

                answer = num - '0';
                index++;

                if ((uint)index >= (uint)source.Length)
                {
                    goto Done;
                }
                num = source[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)source.Length)
                {
                    goto Done;
                }
                num = source[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)source.Length)
                {
                    goto Done;
                }
                num = source[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                // Potential overflow
                if ((uint)index >= (uint)source.Length)
                {
                    goto Done;
                }
                num = source[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = answer * 10 + num - '0';
                if ((uint)answer > ushort.MaxValue)
                {
                    goto FalseExit; // Overflow
                }
                if ((uint)index >= (uint)source.Length)
                {
                    goto Done;
                }
                if (!ParserHelpers.IsDigit(source[index]))
                {
                    goto Done;
                }

                // Guaranteed overflow
                goto FalseExit;
            }

FalseExit:
            bytesConsumed = default;
            value         = default;
            return(false);

Done:
            bytesConsumed = index;
            value         = (ushort)answer;
            return(true);
        }
        private static bool TryParseSByteD(ReadOnlySpan <byte> text, out sbyte value, out int bytesConsumed)
        {
            if (text.Length < 1)
            {
                goto FalseExit;
            }

            int sign  = 1;
            int index = 0;
            int num   = text[index];

            if (num == '-')
            {
                sign = -1;
                index++;
                if ((uint)index >= (uint)text.Length)
                {
                    goto FalseExit;
                }
                num = text[index];
            }
            else if (num == '+')
            {
                index++;
                if ((uint)index >= (uint)text.Length)
                {
                    goto FalseExit;
                }
                num = text[index];
            }

            int answer = 0;

            if (ParserHelpers.IsDigit(num))
            {
                if (num == '0')
                {
                    do
                    {
                        index++;
                        if ((uint)index >= (uint)text.Length)
                        {
                            goto Done;
                        }
                        num = text[index];
                    } while (num == '0');
                    if (!ParserHelpers.IsDigit(num))
                    {
                        goto Done;
                    }
                }

                answer = num - '0';
                index++;

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                // Potential overflow
                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = answer * 10 + num - '0';
                // if sign < 0, (-1 * sign + 1) / 2 = 1
                // else, (-1 * sign + 1) / 2 = 0
                if ((uint)answer > (uint)sbyte.MaxValue + (-1 * sign + 1) / 2)
                {
                    goto FalseExit; // Overflow
                }
                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                if (!ParserHelpers.IsDigit(text[index]))
                {
                    goto Done;
                }

                // Guaranteed overflow
                goto FalseExit;
            }

FalseExit:
            bytesConsumed = default;
            value         = default;
            return(false);

Done:
            bytesConsumed = index;
            value         = (sbyte)(answer * sign);
            return(true);
        }
        private static bool TryParseUInt32D(ReadOnlySpan <byte> text, out uint value, out int bytesConsumed)
        {
            if (text.Length < 1)
            {
                goto FalseExit;
            }

            int index  = 0;
            int num    = text[index];
            int answer = 0;

            if (ParserHelpers.IsDigit(num))
            {
                if (num == '0')
                {
                    do
                    {
                        index++;
                        if ((uint)index >= (uint)text.Length)
                        {
                            goto Done;
                        }
                        num = text[index];
                    } while (num == '0');
                    if (!ParserHelpers.IsDigit(num))
                    {
                        goto Done;
                    }
                }

                answer = num - '0';
                index++;

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                answer = 10 * answer + num - '0';

                // Potential overflow
                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                num = text[index];
                if (!ParserHelpers.IsDigit(num))
                {
                    goto Done;
                }
                index++;
                if (((uint)answer) > uint.MaxValue / 10 || (((uint)answer) == uint.MaxValue / 10 && num > '5'))
                {
                    goto FalseExit; // Overflow
                }
                answer = answer * 10 + num - '0';

                if ((uint)index >= (uint)text.Length)
                {
                    goto Done;
                }
                if (!ParserHelpers.IsDigit(text[index]))
                {
                    goto Done;
                }

                // Guaranteed overflow
                goto FalseExit;
            }

FalseExit:
            bytesConsumed = default;
            value         = default;
            return(false);

Done:
            bytesConsumed = index;
            value         = (uint)answer;
            return(true);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Tries to convert a memory span that contains the bytes of a UTF-8 string representation of a sequence of digits with an optional decimal point and sign character ('-', '0'–'9' and '.') to its <see cref="Number"/> equivalent and returns a value that indicates whether the conversion succeeded.
        /// </summary>
        /// <param name="value">The memory span that contains the bytes of the UTF-8 string value to parse.</param>
        /// <param name="number">Contains the <see cref="Number"/> value equivalent to the value contained in <paramref name="value"/>, if the conversion succeeded, or default if the conversion failed.</param>
        /// <returns>true if the <paramref name="value"/> parameter was converted successfully; otherwise, false.</returns>
        public static bool TryParse(ReadOnlySpan <byte> value, out Number number)
        {
            if (value.Length < 1)
            {
                number = default;
                return(false);
            }

            // Consume the leading sign if any.
            int  index      = 0;
            bool isNegative = false;
            byte c          = value[index];

            switch (c)
            {
            case Minus:
                isNegative = true;
                goto case Plus;

            case Plus:
                index++;
                // Sign must be followed by a digit.
                if (index == value.Length || !ParserHelpers.IsDigit(value[index] - 48))
                {
                    number = default;
                    return(false);
                }
                break;

            case LowercaseN:
            case UppercaseN:
                // 'NULL'
                if (value.Length != 4 ||
                    (value[1] != LowercaseU && value[1] != UppercaseU) ||
                    (value[2] != LowercaseL && value[2] != UppercaseL) ||
                    (value[3] != LowercaseL && value[3] != UppercaseL))
                {
                    number = default;
                    return(false);
                }
                number = Null;
                return(true);

            default:
                break;
            }

            // Throw away any leading zeroes.
            while (index != value.Length)
            {
                c = value[index];
                if (c != '0')
                {
                    break;
                }
                index++;
            }

            long parsedValue = 0;
            int  length      = Math.Min(value.Length, 18 + index); // 18 digits always fit into an Int64 without overflowing.

            for (; index < length; index++)
            {
                int nextDigit = value[index] - 48;
                if (!ParserHelpers.IsDigit(nextDigit))
                {
                    break;
                }
                parsedValue = parsedValue * 10 + nextDigit;
            }

            if (index == value.Length)
            {
                number = new Number(parsedValue, 0, isNegative);
                return(true);
            }

            c = value[index];
            if (c == Period)
            {
                // Parse the digits after the decimal point.
                index++;
                int startIndexDigitsAfterDecimal = index;
                for (; index < length; index++)
                {
                    c = value[index];
                    int nextDigit = c - 48;
                    if (!ParserHelpers.IsDigit(nextDigit))
                    {
                        number = default;
                        return(false);
                    }
                    parsedValue = parsedValue * 10 + nextDigit;
                }

                if (index == value.Length)
                {
                    if (index == 1)
                    {
                        // "." is not a valid value.
                        number = default;
                        return(false);
                    }

                    // Throw away any trailing zeroes after the decimal point.
                    int decimals = index - startIndexDigitsAfterDecimal;
                    while (index > startIndexDigitsAfterDecimal && value[index - 1] == '0')
                    {
                        decimals--;
                        index--;
                        parsedValue /= 10;
                    }

                    number = new Number(parsedValue, decimals, isNegative);
                    return(true);
                }

                // Try to squeeze in another digit into the Int64.
                int digit = value[index] - 48;
                if (!ParserHelpers.IsDigit(digit))
                {
                    number = default;
                    return(false);
                }

                // If parsedValue > (long.MaxValue / 10), any more appended digits will cause overflow.
                // if parsedValue == (long.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow.
                if (parsedValue < long.MaxValue / 10 || (parsedValue == long.MaxValue / 10 && (isNegative ? digit < 9 : digit < 8)))
                {
                    parsedValue = parsedValue * 10 + digit;
                    index++;
                }

                // Parsed value is too big to fit into an Int64.
                BigInteger bigParsedValue = new BigInteger(parsedValue);
                for (; index < value.Length; index++)
                {
                    int nextDigit = value[index] - 48;
                    if (!ParserHelpers.IsDigit(nextDigit))
                    {
                        number = default;
                        return(false);
                    }
                    bigParsedValue = bigParsedValue * NumberFormatter.s_ten + nextDigit;
                }

                // Throw away any trailing zeroes after the decimal point.
                int scale = index - startIndexDigitsAfterDecimal;
                while (index > startIndexDigitsAfterDecimal && value[index - 1] == '0')
                {
                    scale--;
                    index--;
                    bigParsedValue /= NumberFormatter.s_ten;
                }

                number = new Number(bigParsedValue, scale, isNegative);
                return(true);
            }
            else
            {
                // The parsed value may be too big to fit into an Int64.
                int nextDigit = c - 48;
                if (!ParserHelpers.IsDigit(nextDigit))
                {
                    number = default;
                    return(false);
                }

                if (++index == value.Length)
                {
                    // If parsedValue > (long.MaxValue / 10), any more appended digits will cause overflow.
                    // if parsedValue == (long.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow.
                    if (parsedValue < long.MaxValue / 10 || (parsedValue == long.MaxValue / 10 && (isNegative ? nextDigit < 9 : nextDigit < 8)))
                    {
                        number = new Number(parsedValue * 10 + nextDigit, 0, isNegative);
                    }
                    else
                    {
                        number = new Number(new BigInteger(parsedValue) * NumberFormatter.s_ten + nextDigit, 0, isNegative);
                    }
                    return(true);
                }

                // The parsed value is indeed to big to fit into an Int64.
                BigInteger bigParsedValue = new BigInteger(parsedValue) * NumberFormatter.s_ten + nextDigit;
                for (; index < value.Length; index++)
                {
                    c         = value[index];
                    nextDigit = c - 48;
                    if (!ParserHelpers.IsDigit(nextDigit))
                    {
                        if (c != Period)
                        {
                            number = default;
                            return(false);
                        }
                        break;
                    }
                    bigParsedValue = bigParsedValue * NumberFormatter.s_ten + nextDigit;
                }

                if (index == value.Length)
                {
                    number = new Number(bigParsedValue, 0, isNegative);
                    return(true);
                }

                // Parse the digits after the decimal point.
                index++;
                int startIndexDigitsAfterDecimal = index;
                for (; index < value.Length; index++)
                {
                    nextDigit = value[index] - 48;
                    if (!ParserHelpers.IsDigit(nextDigit))
                    {
                        number = default;
                        return(false);
                    }
                    bigParsedValue = bigParsedValue * NumberFormatter.s_ten + nextDigit;
                }

                // Throw away any trailing zeroes after the decimal point.
                int scale = index - startIndexDigitsAfterDecimal;
                while (index > startIndexDigitsAfterDecimal && value[index - 1] == '0')
                {
                    scale--;
                    index--;
                    bigParsedValue /= NumberFormatter.s_ten;
                }

                number = new Number(bigParsedValue, scale, isNegative);
                return(true);
            }
        }