public static bool TryParseDecimal(ReadOnlySpan <char> value, bool allowThousands, out decimal result) { result = 0; if (value.Length == 0) { return(false); } ulong preResult = 0; bool isLargeNumber = false; int i = 0; int end = value.Length; var state = ParseState.LeadingWhite; bool negative = false; bool noIntegerPart = false; sbyte scale = 0; while (i < end) { var c = value[i++]; switch (state) { case ParseState.LeadingWhite: if (JsonUtils.IsWhiteSpace(c)) { break; } if (c == '-') { negative = true; state = ParseState.Sign; } else if (c == '.') { noIntegerPart = true; state = ParseState.FractionNumber; if (i == end) { return(false); } } else if (c == '0') { state = ParseState.DecimalPoint; } else if (c > '0' && c <= '9') { preResult = (ulong)(c - '0'); state = ParseState.Number; } else { return(false); } break; case ParseState.Sign: if (c == '.') { noIntegerPart = true; state = ParseState.FractionNumber; if (i == end) { return(false); } } else if (c == '0') { state = ParseState.DecimalPoint; } else if (c > '0' && c <= '9') { preResult = (ulong)(c - '0'); state = ParseState.Number; } else { return(false); } break; case ParseState.Number: if (c == '.') { state = ParseState.FractionNumber; } else if (c >= '0' && c <= '9') { if (isLargeNumber) { checked { result = 10 * result + (c - '0'); } } else { preResult = 10 * preResult + (ulong)(c - '0'); if (preResult > ulong.MaxValue / 10 - 10) { isLargeNumber = true; result = preResult; } } } else if (JsonUtils.IsWhiteSpace(c)) { state = ParseState.TrailingWhite; } else if (allowThousands && c == ',') { } else { return(false); } break; case ParseState.DecimalPoint: if (c == '.') { state = ParseState.FractionNumber; } else { return(false); } break; case ParseState.FractionNumber: if (JsonUtils.IsWhiteSpace(c)) { if (noIntegerPart) { return(false); } state = ParseState.TrailingWhite; } else if (c == 'e' || c == 'E') { if (noIntegerPart && scale == 0) { return(false); } state = ParseState.Exponent; } else if (c >= '0' && c <= '9') { if (isLargeNumber) { checked { result = 10 * result + (c - '0'); } } else { preResult = 10 * preResult + (ulong)(c - '0'); if (preResult > ulong.MaxValue / 10 - 10) { isLargeNumber = true; result = preResult; } } scale++; } else { return(false); } break; case ParseState.Exponent: bool expNegative = false; if (c == '-') { if (i == end) { return(false); } expNegative = true; c = value[i++]; } else if (c == '+') { if (i == end) { return(false); } c = value[i++]; } //skip leading zeroes while (c == '0' && i < end) { c = value[i++]; } if (c > '0' && c <= '9') { var exp = SignedInteger <long> .ParseInt64(value.Slice(i - 1, end - i + 1)); if (exp < sbyte.MinValue || exp > sbyte.MaxValue) { return(false); } if (!expNegative) { exp = (sbyte)-exp; } if (exp >= 0 || scale > -exp) { scale += (sbyte)exp; } else { for (int j = 0; j < -exp - scale; j++) { if (isLargeNumber) { checked { result = 10 * result; } } else { preResult = 10 * preResult; if (preResult > ulong.MaxValue / 10) { isLargeNumber = true; result = preResult; } } } scale = 0; } //set i to end of string, because ParseInt16 eats number and all trailing whites i = end; } else { return(false); } break; case ParseState.TrailingWhite: if (!JsonUtils.IsWhiteSpace(c)) { return(false); } break; } } if (!isLargeNumber) { var mid = (int)(preResult >> 32); var lo = (int)(preResult & 0xffffffff); result = new decimal(lo, mid, 0, negative, (byte)scale); } else { var bits = decimal.GetBits(result); result = new decimal(bits[0], bits[1], bits[2], negative, (byte)scale); } return(true); }
public override long ParseInt64(ReadOnlySpan <char> value) => SignedInteger <int> .ParseInt64(value);