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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }