in src/Elastic.Apm/Libraries/Newtonsoft.Json/Utilities/ConvertUtils.cs [1218:1394]
public static ParseResult DecimalTryParse(char[] chars, int start, int length, out decimal value)
{
value = 0M;
const decimal decimalMaxValueHi28 = 7922816251426433759354395033M;
const ulong decimalMaxValueHi19 = 7922816251426433759UL;
const ulong decimalMaxValueLo9 = 354395033UL;
const char decimalMaxValueLo1 = '5';
if (length == 0) return ParseResult.Invalid;
var isNegative = chars[start] == '-';
if (isNegative)
{
// text just a negative sign
if (length == 1) return ParseResult.Invalid;
start++;
length--;
}
var i = start;
var end = start + length;
var numDecimalStart = end;
var numDecimalEnd = end;
var exponent = 0;
var hi19 = 0UL;
var lo10 = 0UL;
var mantissaDigits = 0;
var exponentFromMantissa = 0;
char? digit29 = null;
bool? storeOnly28Digits = null;
for (; i < end; i++)
{
var c = chars[i];
switch (c)
{
case '.':
if (i == start) return ParseResult.Invalid;
if (i + 1 == end) return ParseResult.Invalid;
if (numDecimalStart != end)
{
// multiple decimal points
return ParseResult.Invalid;
}
numDecimalStart = i + 1;
break;
case 'e':
case 'E':
if (i == start) return ParseResult.Invalid;
if (i == numDecimalStart)
{
// E follows decimal point
return ParseResult.Invalid;
}
i++;
if (i == end) return ParseResult.Invalid;
if (numDecimalStart < end) numDecimalEnd = i - 1;
c = chars[i];
var exponentNegative = false;
switch (c)
{
case '-':
exponentNegative = true;
i++;
break;
case '+':
i++;
break;
}
// parse 3 digit
for (; i < end; i++)
{
c = chars[i];
if (c < '0' || c > '9') return ParseResult.Invalid;
var newExponent = 10 * exponent + (c - '0');
// stops updating exponent when overflowing
if (exponent < newExponent) exponent = newExponent;
}
if (exponentNegative) exponent = -exponent;
break;
default:
if (c < '0' || c > '9') return ParseResult.Invalid;
if (i == start && c == '0')
{
i++;
if (i != end)
{
c = chars[i];
if (c == '.') goto case '.';
if (c == 'e' || c == 'E') goto case 'E';
return ParseResult.Invalid;
}
}
if (mantissaDigits < 29 && (mantissaDigits != 28 || !(storeOnly28Digits
?? (storeOnly28Digits = hi19 > decimalMaxValueHi19 || hi19 == decimalMaxValueHi19
&& (lo10 > decimalMaxValueLo9 || lo10 == decimalMaxValueLo9 && c > decimalMaxValueLo1)).GetValueOrDefault())))
{
if (mantissaDigits < 19)
hi19 = hi19 * 10UL + (ulong)(c - '0');
else
lo10 = lo10 * 10UL + (ulong)(c - '0');
++mantissaDigits;
}
else
{
if (!digit29.HasValue) digit29 = c;
++exponentFromMantissa;
}
break;
}
}
exponent += exponentFromMantissa;
// correct the decimal point
exponent -= numDecimalEnd - numDecimalStart;
if (mantissaDigits <= 19)
value = hi19;
else
value = hi19 / new decimal(1, 0, 0, false, (byte)(mantissaDigits - 19)) + lo10;
if (exponent > 0)
{
mantissaDigits += exponent;
if (mantissaDigits > 29) return ParseResult.Overflow;
if (mantissaDigits == 29)
{
if (exponent > 1)
{
value /= new decimal(1, 0, 0, false, (byte)(exponent - 1));
if (value > decimalMaxValueHi28) return ParseResult.Overflow;
}
else if (value == decimalMaxValueHi28 && digit29 > decimalMaxValueLo1) return ParseResult.Overflow;
value *= 10M;
}
else
value /= new decimal(1, 0, 0, false, (byte)exponent);
}
else
{
if (digit29 >= '5' && exponent >= -28) ++value;
if (exponent < 0)
{
if (mantissaDigits + exponent + 28 <= 0)
{
value = isNegative ? -0M : 0M;
return ParseResult.Success;
}
if (exponent >= -28)
value *= new decimal(1, 0, 0, false, (byte)-exponent);
else
{
value /= 1e28M;
value *= new decimal(1, 0, 0, false, (byte)(-exponent - 28));
}
}
}
if (isNegative) value = -value;
return ParseResult.Success;
}