public static bool TryParseDecimal(ReadOnlySpan <byte> text, out decimal value, out int bytesConsumed, SymbolTable symbolTable = null)
        {
            symbolTable = symbolTable ?? SymbolTable.InvariantUtf8;

            bytesConsumed = 0;
            value         = default;

            if (symbolTable == SymbolTable.InvariantUtf8)
            {
                return(Utf8Parser.TryParse(text, out value, out bytesConsumed));
            }
            else if (symbolTable == SymbolTable.InvariantUtf16)
            {
                ReadOnlySpan <char> textChars = MemoryMarshal.Cast <byte, char>(text);
                bool result = Utf16Parser.TryParseDecimal(textChars, out value, out int charactersConsumed);
                bytesConsumed = charactersConsumed * sizeof(char);
                return(result);
            }

            return(false);
        }
Beispiel #2
0
        public static bool TryParseBoolean(ReadOnlySpan <byte> text, out bool value, out int bytesConsumed, SymbolTable symbolTable = null)
        {
            symbolTable = symbolTable ?? SymbolTable.InvariantUtf8;

            bytesConsumed = 0;
            value         = default;

            if (symbolTable == SymbolTable.InvariantUtf8)
            {
                return(Utf8Parser.TryParseBoolean(text, out value, out bytesConsumed));
            }
            if (symbolTable == SymbolTable.InvariantUtf16)
            {
                ReadOnlySpan <char> textChars = text.NonPortableCast <byte, char>();
                int  charactersConsumed;
                bool result = Utf16Parser.TryParseBoolean(textChars, out value, out charactersConsumed);
                bytesConsumed = charactersConsumed * sizeof(char);
                return(result);
            }

            return(false);
        }
Beispiel #3
0
        public bool TryParse(out ulong value)
        {
            var unread = Unread;

            if (Utf8Parser.TryParse(unread, out value, out int consumed))
            {
                if (unread.Length > consumed)
                {
                    _currentSpanIndex += consumed;
                    return(true);
                }
            }

            Span <byte> tempSpan = stackalloc byte[30];
            var         copied   = CopyTo(this, tempSpan);

            if (Utf8Parser.TryParse(tempSpan.Slice(0, copied), out value, out consumed))
            {
                Advance(consumed);
                return(true);
            }
            return(false);
        }
Beispiel #4
0
        public static bool TryParseInt64(ReadOnlySpan <byte> text, out long value, out int bytesConsumed, StandardFormat format = default, SymbolTable symbolTable = null)
        {
            symbolTable = symbolTable ?? SymbolTable.InvariantUtf8;

            if (!format.IsDefault && format.HasPrecision)
            {
                throw new NotImplementedException("Format with precision not supported.");
            }

            if (symbolTable == SymbolTable.InvariantUtf8)
            {
                if (Parsers.IsHexFormat(format))
                {
                    return(Utf8Parser.TryParse(text, out value, out bytesConsumed, 'X'));
                }
                else
                {
                    return(Utf8Parser.TryParse(text, out value, out bytesConsumed));
                }
            }
            else if (symbolTable == SymbolTable.InvariantUtf16)
            {
                ReadOnlySpan <char> utf16Text = MemoryMarshal.Cast <byte, char>(text);
                int  charactersConsumed;
                bool result;
                if (Parsers.IsHexFormat(format))
                {
                    result = Utf16Parser.Hex.TryParseInt64(utf16Text, out value, out charactersConsumed);
                }
                else
                {
                    result = Utf16Parser.TryParseInt64(utf16Text, out value, out charactersConsumed);
                }
                bytesConsumed = charactersConsumed * sizeof(char);
                return(result);
            }

            if (Parsers.IsHexFormat(format))
            {
                throw new NotImplementedException("The only supported encodings for hexadecimal parsing are InvariantUtf8 and InvariantUtf16.");
            }

            if (!(format.IsDefault || format.Symbol == 'G' || format.Symbol == 'g'))
            {
                throw new NotImplementedException(String.Format("Format '{0}' not supported.", format.Symbol));
            }
            if (!symbolTable.TryParse(text, out SymbolTable.Symbol nextSymbol, out int thisSymbolConsumed))
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            int sign = 1;

            if (nextSymbol == SymbolTable.Symbol.MinusSign)
            {
                sign = -1;
            }

            int signConsumed = 0;

            if (nextSymbol == SymbolTable.Symbol.PlusSign || nextSymbol == SymbolTable.Symbol.MinusSign)
            {
                signConsumed = thisSymbolConsumed;
                if (!symbolTable.TryParse(text.Slice(signConsumed), out nextSymbol, out thisSymbolConsumed))
                {
                    value         = default;
                    bytesConsumed = 0;
                    return(false);
                }
            }

            if (nextSymbol > SymbolTable.Symbol.D9)
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            long parsedValue = (long)nextSymbol;
            int  index       = signConsumed + thisSymbolConsumed;

            while (index < text.Length)
            {
                bool success = symbolTable.TryParse(text.Slice(index), out nextSymbol, out thisSymbolConsumed);
                if (!success || nextSymbol > SymbolTable.Symbol.D9)
                {
                    bytesConsumed = index;
                    value         = (long)(parsedValue * sign);
                    return(true);
                }

                // 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.
                bool positive          = sign > 0;
                bool nextDigitTooLarge = nextSymbol > SymbolTable.Symbol.D8 || (positive && nextSymbol > SymbolTable.Symbol.D7);
                if (parsedValue > long.MaxValue / 10 || (parsedValue == long.MaxValue / 10 && nextDigitTooLarge))
                {
                    bytesConsumed = 0;
                    value         = default;
                    return(false);
                }

                index      += thisSymbolConsumed;
                parsedValue = parsedValue * 10 + (long)nextSymbol;
            }

            bytesConsumed = text.Length;
            value         = (long)(parsedValue * sign);
            return(true);
        }
Beispiel #5
0
        public static bool TryParseInt32(ReadOnlySpan <byte> text, out int value, out int bytesConsumed, StandardFormat format = default, SymbolTable symbolTable = null)
        {
            bool isDefault = format.IsDefault;

            symbolTable = symbolTable ?? SymbolTable.InvariantUtf8;

            if (!isDefault && format.HasPrecision)
            {
                throw new NotImplementedException("Format with precision not supported.");
            }

            bool isHex = Parsers.IsHexFormat(format);

            if (symbolTable == SymbolTable.InvariantUtf8)
            {
                return(isHex ? Utf8Parser.TryParse(text, out value, out bytesConsumed, 'X') :
                       Utf8Parser.TryParse(text, out value, out bytesConsumed));
            }
            else if (symbolTable == SymbolTable.InvariantUtf16)
            {
                /*return isHex ? InvariantUtf16.Hex.TryParseInt32(text, out value, out bytesConsumed) :
                 *  InvariantUtf16.TryParseInt32(text, out value, out bytesConsumed);*/
                ReadOnlySpan <char> utf16Text = MemoryMarshal.Cast <byte, char>(text);
                bool result = isHex ? Utf16Parser.Hex.TryParseInt32(utf16Text, out value, out int charactersConsumed) :
                              Utf16Parser.TryParseInt32(utf16Text, out value, out charactersConsumed);
                bytesConsumed = charactersConsumed * sizeof(char);
                return(result);
            }

            if (isHex)
            {
                throw new NotImplementedException("The only supported encodings for hexadecimal parsing are InvariantUtf8 and InvariantUtf16.");
            }

            if (!(isDefault || format.Symbol == 'G' || format.Symbol == 'g'))
            {
                throw new NotImplementedException(String.Format("Format '{0}' not supported.", format.Symbol));
            }

            int textLength = text.Length;

            if (textLength < 1)
            {
                goto FalseExit;
            }

            if (!symbolTable.TryParse(text, out SymbolTable.Symbol symbol, out int consumed))
            {
                goto FalseExit;
            }

            sbyte sign  = 1;
            int   index = 0;

            if (symbol == SymbolTable.Symbol.MinusSign)
            {
                sign   = -1;
                index += consumed;
                if (index >= textLength)
                {
                    goto FalseExit;
                }
                if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed))
                {
                    goto FalseExit;
                }
            }
            else if (symbol == SymbolTable.Symbol.PlusSign)
            {
                index += consumed;
                if (index >= textLength)
                {
                    goto FalseExit;
                }
                if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed))
                {
                    goto FalseExit;
                }
            }

            int answer = 0;

            if (Parsers.IsValid(symbol))
            {
                int numBytes = consumed;
                if (symbol == SymbolTable.Symbol.D0)
                {
                    do
                    {
                        index += consumed;
                        if (index >= textLength)
                        {
                            goto Done;
                        }
                        if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed))
                        {
                            goto Done;
                        }
                    } while (symbol == SymbolTable.Symbol.D0);
                    if (!Parsers.IsValid(symbol))
                    {
                        goto Done;
                    }
                }

                int firstNonZeroDigitIndex = index;
                if (textLength - firstNonZeroDigitIndex < Parsers.Int32OverflowLength * numBytes)
                {
                    do
                    {
                        answer = answer * 10 + (int)symbol;
                        index += consumed;
                        if (index >= textLength)
                        {
                            goto Done;
                        }
                        if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed))
                        {
                            goto Done;
                        }
                    } while (Parsers.IsValid(symbol));
                }
                else
                {
                    do
                    {
                        answer = answer * 10 + (int)symbol;
                        index += consumed;
                        if (index - firstNonZeroDigitIndex == (Parsers.Int32OverflowLength - 1) * numBytes)
                        {
                            if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed))
                            {
                                goto Done;
                            }
                            if (Parsers.IsValid(symbol))
                            {
                                if (Parsers.WillOverFlow(answer, (int)symbol, sign))
                                {
                                    goto FalseExit;
                                }
                                answer = answer * 10 + (int)symbol;
                                index += consumed;
                            }
                            goto Done;
                        }
                        if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed))
                        {
                            goto Done;
                        }
                    } while (Parsers.IsValid(symbol));
                }
                goto Done;
            }

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

Done:
            bytesConsumed = index;
            value         = answer * sign;
            return(true);
        }
Beispiel #6
0
        private static bool TryParseNumber(ReadOnlySpan <byte> text, ref NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation)
        {
            Debug.Assert(number.Digits[0] == 0 && number.Scale == 0 && !number.IsNegative, "Number not initialized to default(NumberBuffer)");

            textUsedExponentNotation = false;

            if (text.Length == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            Span <byte> digits = number.Digits;

            int srcIndex = 0;
            int dstIndex = 0;

            // Consume the leading sign if any.
            byte c = text[srcIndex];

            switch (c)
            {
            case Utf8Constants.Minus:
                number.IsNegative = true;
                goto case Utf8Constants.Plus;

            case Utf8Constants.Plus:
                srcIndex++;
                if (srcIndex == text.Length)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                c = text[srcIndex];
                break;

            default:
                break;
            }

            int startIndexDigitsBeforeDecimal = srcIndex;

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

            if (srcIndex == text.Length)
            {
                digits[0]     = 0;
                number.Scale  = 0;
                bytesConsumed = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            int startIndexNonLeadingDigitsBeforeDecimal = srcIndex;

            while (srcIndex != text.Length)
            {
                c = text[srcIndex];
                if ((c - 48u) > 9)
                {
                    break;
                }
                srcIndex++;
            }

            int numDigitsBeforeDecimal           = srcIndex - startIndexDigitsBeforeDecimal;
            int numNonLeadingDigitsBeforeDecimal = srcIndex - startIndexNonLeadingDigitsBeforeDecimal;

            Debug.Assert(dstIndex == 0);
            int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, NumberBuffer.BufferSize - 1);

            text.Slice(startIndexNonLeadingDigitsBeforeDecimal, numNonLeadingDigitsBeforeDecimalToCopy).CopyTo(digits);
            dstIndex     = numNonLeadingDigitsBeforeDecimalToCopy;
            number.Scale = numNonLeadingDigitsBeforeDecimal;

            if (srcIndex == text.Length)
            {
                bytesConsumed = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            int numDigitsAfterDecimal = 0;

            if (c == Utf8Constants.Period)
            {
                //
                // Parse the digits after the decimal point.
                //

                srcIndex++;
                int startIndexDigitsAfterDecimal = srcIndex;
                while (srcIndex != text.Length)
                {
                    c = text[srcIndex];
                    if ((c - 48u) > 9)
                    {
                        break;
                    }
                    srcIndex++;
                }
                numDigitsAfterDecimal = srcIndex - startIndexDigitsAfterDecimal;

                int startIndexOfDigitsAfterDecimalToCopy = startIndexDigitsAfterDecimal;
                if (dstIndex == 0)
                {
                    // Not copied any digits to the Number struct yet. This means we must continue discarding leading zeroes even though they appeared after the decimal point.
                    while (startIndexOfDigitsAfterDecimalToCopy < srcIndex && text[startIndexOfDigitsAfterDecimalToCopy] == '0')
                    {
                        number.Scale--;
                        startIndexOfDigitsAfterDecimalToCopy++;
                    }
                }

                int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, NumberBuffer.BufferSize - dstIndex - 1);
                text.Slice(startIndexOfDigitsAfterDecimalToCopy, numDigitsAfterDecimalToCopy).CopyTo(digits.Slice(dstIndex));
                dstIndex += numDigitsAfterDecimalToCopy;
                // We "should" really NUL terminate, but there are multiple places we'd have to do this and it is a precondition that the caller pass in a fully zero=initialized Number.

                if (srcIndex == text.Length)
                {
                    if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0)
                    {
                        // For compatibility. You can say "5." and ".5" but you can't say "."
                        bytesConsumed = 0;
                        return(false);
                    }

                    bytesConsumed = srcIndex;
                    number.CheckConsistency();
                    return(true);
                }
            }

            if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            if ((c & ~0x20u) != 'E')
            {
                bytesConsumed = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            //
            // Parse the exponent after the "E"
            //
            textUsedExponentNotation = true;
            srcIndex++;

            if ((options & ParseNumberOptions.AllowExponent) == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            if (srcIndex == text.Length)
            {
                bytesConsumed = 0;
                return(false);
            }

            bool exponentIsNegative = false;

            c = text[srcIndex];
            switch (c)
            {
            case Utf8Constants.Minus:
                exponentIsNegative = true;
                goto case Utf8Constants.Plus;

            case Utf8Constants.Plus:
                srcIndex++;
                if (srcIndex == text.Length)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                c = text[srcIndex];
                break;

            default:
                break;
            }

            if (!Utf8Parser.TryParseUInt32D(text.Slice(srcIndex), out uint absoluteExponent, out int bytesConsumedByExponent))
            {
                bytesConsumed = 0;
                return(false);
            }

            srcIndex += bytesConsumedByExponent;

            if (exponentIsNegative)
            {
                if (number.Scale < int.MinValue + (long)absoluteExponent)
                {
                    // A scale underflow means all non-zero digits are all so far to the right of the decimal point, no
                    // number format we have will be able to see them. Just pin the scale at the absolute minimum
                    // and let the converter produce a 0 with the max precision available for that type.
                    number.Scale = int.MinValue;
                }
                else
                {
                    number.Scale -= (int)absoluteExponent;
                }
            }
            else
            {
                if (number.Scale > int.MaxValue - (long)absoluteExponent)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                number.Scale += (int)absoluteExponent;
            }

            bytesConsumed = srcIndex;
            number.CheckConsistency();
            return(true);
        }
Beispiel #7
0
        private static bool TryParseNumber(ReadOnlySpan <byte> source, ref Number.NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation)
        {
            Debug.Assert(number.DigitsCount == 0);
            Debug.Assert(number.Scale == 0);
            Debug.Assert(number.IsNegative == false);
            Debug.Assert(number.HasNonZeroTail == false);

            number.CheckConsistency();
            textUsedExponentNotation = false;

            if (source.Length == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            Span <byte> digits = number.Digits;

            int srcIndex = 0;
            int dstIndex = 0;

            // Consume the leading sign if any.
            byte c = source[srcIndex];

            switch (c)
            {
            case Utf8Constants.Minus:
                number.IsNegative = true;
                goto case Utf8Constants.Plus;

            case Utf8Constants.Plus:
                srcIndex++;
                if (srcIndex == source.Length)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                c = source[srcIndex];
                break;

            default:
                break;
            }

            int startIndexDigitsBeforeDecimal = srcIndex;
            int digitCount    = 0;
            int maxDigitCount = digits.Length - 1;

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

            if (srcIndex == source.Length)
            {
                number.IsNegative = false;
                bytesConsumed     = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            int startIndexNonLeadingDigitsBeforeDecimal = srcIndex;

            int hasNonZeroTail = 0;

            while (srcIndex != source.Length)
            {
                c = source[srcIndex];
                int value = (byte)(c - (byte)('0'));

                if (value > 9)
                {
                    break;
                }

                srcIndex++;
                digitCount++;

                if (digitCount >= maxDigitCount)
                {
                    // For decimal and binary floating-point numbers, we only
                    // need to store digits up to maxDigCount. However, we still
                    // need to keep track of whether any additional digits past
                    // maxDigCount were non-zero, as that can impact rounding
                    // for an input that falls evenly between two representable
                    // results.

                    hasNonZeroTail |= value;
                }
            }
            number.HasNonZeroTail = (hasNonZeroTail != 0);

            int numDigitsBeforeDecimal           = srcIndex - startIndexDigitsBeforeDecimal;
            int numNonLeadingDigitsBeforeDecimal = srcIndex - startIndexNonLeadingDigitsBeforeDecimal;

            Debug.Assert(dstIndex == 0);
            int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, maxDigitCount);

            source.Slice(startIndexNonLeadingDigitsBeforeDecimal, numNonLeadingDigitsBeforeDecimalToCopy).CopyTo(digits);
            dstIndex     = numNonLeadingDigitsBeforeDecimalToCopy;
            number.Scale = numNonLeadingDigitsBeforeDecimal;

            if (srcIndex == source.Length)
            {
                digits[dstIndex]   = 0;
                number.DigitsCount = dstIndex;
                bytesConsumed      = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            int numDigitsAfterDecimal = 0;

            if (c == Utf8Constants.Period)
            {
                //
                // Parse the digits after the decimal point.
                //

                srcIndex++;
                int startIndexDigitsAfterDecimal = srcIndex;

                while (srcIndex != source.Length)
                {
                    c = source[srcIndex];
                    int value = (byte)(c - (byte)('0'));

                    if (value > 9)
                    {
                        break;
                    }

                    srcIndex++;
                    digitCount++;

                    if (digitCount >= maxDigitCount)
                    {
                        // For decimal and binary floating-point numbers, we only
                        // need to store digits up to maxDigCount. However, we still
                        // need to keep track of whether any additional digits past
                        // maxDigCount were non-zero, as that can impact rounding
                        // for an input that falls evenly between two representable
                        // results.

                        hasNonZeroTail |= value;
                    }
                }
                number.HasNonZeroTail = (hasNonZeroTail != 0);

                numDigitsAfterDecimal = srcIndex - startIndexDigitsAfterDecimal;

                int startIndexOfDigitsAfterDecimalToCopy = startIndexDigitsAfterDecimal;
                if (dstIndex == 0)
                {
                    // Not copied any digits to the Number struct yet. This means we must continue discarding leading zeroes even though they appeared after the decimal point.
                    while (startIndexOfDigitsAfterDecimalToCopy < srcIndex && source[startIndexOfDigitsAfterDecimalToCopy] == '0')
                    {
                        number.Scale--;
                        startIndexOfDigitsAfterDecimalToCopy++;
                    }
                }

                int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, maxDigitCount - dstIndex);
                source.Slice(startIndexOfDigitsAfterDecimalToCopy, numDigitsAfterDecimalToCopy).CopyTo(digits.Slice(dstIndex));
                dstIndex += numDigitsAfterDecimalToCopy;
                // We "should" really NUL terminate, but there are multiple places we'd have to do this and it is a precondition that the caller pass in a fully zero=initialized Number.

                if (srcIndex == source.Length)
                {
                    if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0)
                    {
                        // For compatibility. You can say "5." and ".5" but you can't say "."
                        bytesConsumed = 0;
                        return(false);
                    }

                    digits[dstIndex]   = 0;
                    number.DigitsCount = dstIndex;
                    bytesConsumed      = srcIndex;
                    number.CheckConsistency();
                    return(true);
                }
            }

            if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            if ((c & ~0x20u) != 'E')
            {
                if ((digits[0] == 0) && (numDigitsAfterDecimal == 0))
                {
                    number.IsNegative = false;
                }

                digits[dstIndex]   = 0;
                number.DigitsCount = dstIndex;
                bytesConsumed      = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            //
            // Parse the exponent after the "E"
            //
            textUsedExponentNotation = true;
            srcIndex++;

            if ((options & ParseNumberOptions.AllowExponent) == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            if (srcIndex == source.Length)
            {
                bytesConsumed = 0;
                return(false);
            }

            bool exponentIsNegative = false;

            c = source[srcIndex];
            switch (c)
            {
            case Utf8Constants.Minus:
                exponentIsNegative = true;
                goto case Utf8Constants.Plus;

            case Utf8Constants.Plus:
                srcIndex++;
                if (srcIndex == source.Length)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                c = source[srcIndex];
                break;

            default:
                break;
            }

            if (!Utf8Parser.TryParseUInt32D(source.Slice(srcIndex), out uint absoluteExponent, out int bytesConsumedByExponent))
            {
                bytesConsumed = 0;
                return(false);
            }

            srcIndex += bytesConsumedByExponent;

            if (exponentIsNegative)
            {
                if (number.Scale < int.MinValue + (long)absoluteExponent)
                {
                    // A scale underflow means all non-zero digits are all so far to the right of the decimal point, no
                    // number format we have will be able to see them. Just pin the scale at the absolute minimum
                    // and let the converter produce a 0 with the max precision available for that type.
                    number.Scale = int.MinValue;
                }
                else
                {
                    number.Scale -= (int)absoluteExponent;
                }
            }
            else
            {
                if (number.Scale > int.MaxValue - (long)absoluteExponent)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                number.Scale += (int)absoluteExponent;
            }

            digits[dstIndex]   = 0;
            number.DigitsCount = dstIndex;
            bytesConsumed      = srcIndex;
            number.CheckConsistency();
            return(true);
        }
Beispiel #8
0
        public static bool TryParseByte(ReadOnlySpan <byte> text, out byte value, out int bytesConsumed, ParsedFormat format = default, SymbolTable symbolTable = null)
        {
            symbolTable = symbolTable ?? SymbolTable.InvariantUtf8;

            if (!format.IsDefault && format.HasPrecision)
            {
                throw new NotImplementedException("Format with precision not supported.");
            }

            if (symbolTable == SymbolTable.InvariantUtf8)
            {
                if (Parsers.IsHexFormat(format))
                {
                    return(Utf8Parser.Hex.TryParseByte(text, out value, out bytesConsumed));
                }
                else
                {
                    return(Utf8Parser.TryParseByte(text, out value, out bytesConsumed));
                }
            }
            else if (symbolTable == SymbolTable.InvariantUtf16)
            {
                ReadOnlySpan <char> utf16Text = text.NonPortableCast <byte, char>();
                int  charactersConsumed;
                bool result;
                if (Parsers.IsHexFormat(format))
                {
                    result = Utf16Parser.Hex.TryParseByte(utf16Text, out value, out charactersConsumed);
                }
                else
                {
                    result = Utf16Parser.TryParseByte(utf16Text, out value, out charactersConsumed);
                }
                bytesConsumed = charactersConsumed * sizeof(char);
                return(result);
            }

            if (Parsers.IsHexFormat(format))
            {
                throw new NotImplementedException("The only supported encodings for hexadecimal parsing are InvariantUtf8 and InvariantUtf16.");
            }

            if (!(format.IsDefault || format.Symbol == 'G' || format.Symbol == 'g'))
            {
                throw new NotImplementedException(String.Format("Format '{0}' not supported.", format.Symbol));
            }

            SymbolTable.Symbol nextSymbol;
            int thisSymbolConsumed;

            if (!symbolTable.TryParse(text, out nextSymbol, out thisSymbolConsumed))
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            if (nextSymbol > SymbolTable.Symbol.D9)
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            uint parsedValue = (uint)nextSymbol;
            int  index       = thisSymbolConsumed;

            while (index < text.Length)
            {
                bool success = symbolTable.TryParse(text.Slice(index), out nextSymbol, out thisSymbolConsumed);
                if (!success || nextSymbol > SymbolTable.Symbol.D9)
                {
                    bytesConsumed = index;
                    value         = (byte)parsedValue;
                    return(true);
                }

                // If parsedValue > (byte.MaxValue / 10), any more appended digits will cause overflow.
                // if parsedValue == (byte.MaxValue / 10), any nextDigit greater than 5 implies overflow.
                if (parsedValue > byte.MaxValue / 10 || (parsedValue == byte.MaxValue / 10 && nextSymbol > SymbolTable.Symbol.D5))
                {
                    bytesConsumed = 0;
                    value         = default;
                    return(false);
                }

                index      += thisSymbolConsumed;
                parsedValue = parsedValue * 10 + (uint)nextSymbol;
            }

            bytesConsumed = text.Length;
            value         = (byte)parsedValue;
            return(true);
        }