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