Commit 9c233be5 by Niels

merged #201

parent 04edafbd
...@@ -402,7 +402,7 @@ I deeply appreciate the help of the following people. ...@@ -402,7 +402,7 @@ I deeply appreciate the help of the following people.
- [406345](https://github.com/406345) fixed two small warnings. - [406345](https://github.com/406345) fixed two small warnings.
- [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function. - [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function.
- [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines. - [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines.
- [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers. - [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers.
- [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file. - [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file.
- [msm-](https://github.com/msm-) added support for american fuzzy lop. - [msm-](https://github.com/msm-) added support for american fuzzy lop.
- [Annihil](https://github.com/Annihil) fixed an example in the README file. - [Annihil](https://github.com/Annihil) fixed an example in the README file.
......
...@@ -695,6 +695,74 @@ class basic_json ...@@ -695,6 +695,74 @@ class basic_json
private: private:
/*!
@brief a type to hold JSON type information
This bitfield type holds information about JSON types. It is internally
used to hold the basic JSON type enumeration, as well as additional
information in the case of values that have been parsed from a string
including whether of not it was created directly or parsed, and in the
case of floating point numbers the number of significant figures in the
original representaiton and if it was in exponential form, if a '+' was
included in the exponent and the capitilization of the exponent marker.
The sole purpose of this information is to permit accurate round trips.
@since version 2.0.0
*/
union type_data_t
{
struct
{
/// the type of the value (@ref value_t)
uint16_t type : 4;
/// whether the number was parsed from a string
uint16_t parsed : 1;
/// whether parsed number contained an exponent ('e'/'E')
uint16_t has_exp : 1;
/// whether parsed number contained a plus in the exponent
uint16_t exp_plus : 1;
/// whether parsed number's exponent was capitalized ('E')
uint16_t exp_cap : 1;
/// the number of figures for a parsed number
uint16_t precision : 8;
} bits;
uint16_t data;
/// return the type as value_t
operator value_t() const
{
return static_cast<value_t>(bits.type);
}
/// test type for equality (ignore other fields)
bool operator==(const value_t& rhs) const
{
return static_cast<value_t>(bits.type) == rhs;
}
/// assignment
type_data_t& operator=(value_t rhs)
{
bits.type = static_cast<uint16_t>(rhs);
return *this;
}
/// construct from value_t
type_data_t(value_t t) noexcept
{
*reinterpret_cast<uint16_t*>(this) = 0;
bits.type = static_cast<uint16_t>(t);
}
/// default constructor
type_data_t() noexcept
{
data = 0;
bits.type = reinterpret_cast<uint16_t>(value_t::null);
}
};
/// helper for exception-safe object creation /// helper for exception-safe object creation
template<typename T, typename... Args> template<typename T, typename... Args>
static T* create(Args&& ... args) static T* create(Args&& ... args)
...@@ -6046,23 +6114,78 @@ class basic_json ...@@ -6046,23 +6114,78 @@ class basic_json
case value_t::number_float: case value_t::number_float:
{ {
// If the number is an integer then output as a fixed with with // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
// precision 1 to output "0.0", "1.0" etc as expected for some char buf[263];
// round trip tests otherwise 15 digits of precision allows int len;
// round-trip IEEE 754 string->double->string; to be safe, we
// read this value from // check if number was parsed from a string
// std::numeric_limits<number_float_t>::digits10 if (m_type.bits.parsed)
if (std::fmod(m_value.number_float, 1) == 0) {
// check if parsed number had an exponent given
if (m_type.bits.has_exp)
{
// handle capitalization of the exponent
if (m_type.bits.exp_cap)
{
len = snprintf(buf, sizeof(buf), "%.*E", m_type.bits.precision, m_value.number_float) + 1;
}
else
{
len = snprintf(buf, sizeof(buf), "%.*e", m_type.bits.precision, m_value.number_float) + 1;
}
// remove '+' sign from the exponent if necessary
if (not m_type.bits.exp_plus)
{
if (len > static_cast<int>(sizeof(buf)))
{
len = sizeof(buf);
}
for (int i = 0; i < len; i++)
{
if (buf[i] == '+')
{
for (; i + 1 < len; i++)
{
buf[i] = buf[i + 1];
}
}
}
}
}
else
{
// no exponent - output as a decimal
snprintf(buf, sizeof(buf), "%.*f",
m_type.bits.precision, m_value.number_float);
}
}
else if (m_value.number_float == 0)
{
// special case for zero to get "0.0"/"-0.0"
if (std::signbit(m_value.number_float))
{
o << "-0.0";
}
else
{ {
o << std::fixed << std::setprecision(1); o << "0.0";
}
return;
} }
else else
{ {
// std::defaultfloat not supported in gcc version < 5 // Otherwise 6, 15 or 16 digits of precision allows
o.unsetf(std::ios_base::floatfield); // round-trip IEEE 754 string->float->string,
o << std::setprecision(std::numeric_limits<double>::digits10); // string->double->string or string->long double->string;
// to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
snprintf(buf, sizeof(buf), "%.*g",
std::numeric_limits<double>::digits10,
m_value.number_float);
} }
o << m_value.number_float;
o << buf;
return; return;
} }
...@@ -6086,7 +6209,7 @@ class basic_json ...@@ -6086,7 +6209,7 @@ class basic_json
////////////////////// //////////////////////
/// the type of the current element /// the type of the current element
value_t m_type = value_t::null; type_data_t m_type = value_t::null;
/// the value of the current element /// the value of the current element
json_value m_value = {}; json_value m_value = {};
...@@ -7278,361 +7401,355 @@ class basic_json ...@@ -7278,361 +7401,355 @@ class basic_json
{ {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 32, 32, 0, 0, 32, 0, 0, 0, 32, 32, 0, 0, 32, 0, 0,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
160, 128, 0, 128, 128, 128, 128, 128, 96, 64, 0, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
192, 192, 128, 128, 128, 128, 128, 128, 192, 192, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 0, 128, 128, 128, 64, 64, 64, 64, 0, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 64, 64, 64, 64,
}; };
if ((m_limit - m_cursor) < 5) if ((m_limit - m_cursor) < 5)
{ {
yyfill(); // LCOV_EXCL_LINE; yyfill(); // LCOV_EXCL_LINE;
} }
yych = *m_cursor; yych = *m_cursor;
if (yybm[0 + yych] & 32) if (yych <= ':')
{
goto basic_json_parser_6;
}
if (yych <= '\\')
{ {
if (yych <= '-') if (yych <= ' ')
{ {
if (yych <= '"') if (yych <= '\n')
{ {
if (yych <= 0x00) if (yych <= 0x00)
{ {
goto basic_json_parser_2; goto basic_json_parser_28;
}
if (yych <= 0x08)
{
goto basic_json_parser_30;
} }
if (yych <= '!') if (yych >= '\n')
{ {
goto basic_json_parser_4; goto basic_json_parser_4;
} }
goto basic_json_parser_9;
} }
else else
{ {
if (yych <= '+') if (yych == '\r')
{ {
goto basic_json_parser_4; goto basic_json_parser_2;
} }
if (yych <= ',') if (yych <= 0x1F)
{ {
goto basic_json_parser_10; goto basic_json_parser_30;
} }
goto basic_json_parser_12;
} }
} }
else else
{ {
if (yych <= '9') if (yych <= ',')
{ {
if (yych <= '/') if (yych == '"')
{ {
goto basic_json_parser_4; goto basic_json_parser_27;
} }
if (yych <= '0') if (yych <= '+')
{ {
goto basic_json_parser_13; goto basic_json_parser_30;
} }
goto basic_json_parser_15; goto basic_json_parser_16;
} }
else else
{ {
if (yych <= ':') if (yych <= '/')
{
if (yych <= '-')
{ {
goto basic_json_parser_17; goto basic_json_parser_23;
} }
if (yych == '[') goto basic_json_parser_30;
}
else
{
if (yych <= '0')
{ {
goto basic_json_parser_19; goto basic_json_parser_24;
}
if (yych <= '9')
{
goto basic_json_parser_26;
}
goto basic_json_parser_18;
} }
goto basic_json_parser_4;
} }
} }
} }
else else
{ {
if (yych <= 't') if (yych <= 'n')
{
if (yych <= 'f')
{ {
if (yych <= ']') if (yych <= ']')
{ {
goto basic_json_parser_21; if (yych == '[')
{
goto basic_json_parser_8;
} }
if (yych <= 'e') if (yych <= '\\')
{ {
goto basic_json_parser_4; goto basic_json_parser_30;
} }
goto basic_json_parser_23; goto basic_json_parser_10;
} }
else else
{ {
if (yych == 'n') if (yych == 'f')
{ {
goto basic_json_parser_24; goto basic_json_parser_22;
} }
if (yych <= 's') if (yych <= 'm')
{ {
goto basic_json_parser_4; goto basic_json_parser_30;
} }
goto basic_json_parser_25; goto basic_json_parser_20;
} }
} }
else else
{ {
if (yych <= '|') if (yych <= '{')
{ {
if (yych == '{') if (yych == 't')
{ {
goto basic_json_parser_26; goto basic_json_parser_21;
} }
goto basic_json_parser_4; if (yych <= 'z')
{
goto basic_json_parser_30;
}
goto basic_json_parser_12;
} }
else else
{ {
if (yych <= '}') if (yych <= '}')
{ {
goto basic_json_parser_28; if (yych <= '|')
{
goto basic_json_parser_30;
} }
goto basic_json_parser_14;
}
else
{
if (yych == 0xEF) if (yych == 0xEF)
{ {
goto basic_json_parser_6;
}
goto basic_json_parser_30; goto basic_json_parser_30;
} }
goto basic_json_parser_4;
} }
} }
} }
basic_json_parser_2: basic_json_parser_2:
++m_cursor; ++m_cursor;
yych = *m_cursor;
goto basic_json_parser_5;
basic_json_parser_3:
{ {
return token_type::end_of_input; return scan();
} }
basic_json_parser_4: basic_json_parser_4:
++m_cursor; ++m_cursor;
basic_json_parser_5:
{
return token_type::parse_error;
}
basic_json_parser_6:
++m_cursor;
if (m_limit <= m_cursor) if (m_limit <= m_cursor)
{ {
yyfill(); // LCOV_EXCL_LINE; yyfill(); // LCOV_EXCL_LINE;
} }
yych = *m_cursor; yych = *m_cursor;
basic_json_parser_5:
if (yybm[0 + yych] & 32) if (yybm[0 + yych] & 32)
{ {
goto basic_json_parser_6; goto basic_json_parser_4;
}
{
return scan();
} }
basic_json_parser_9: goto basic_json_parser_3;
basic_json_parser_6:
yyaccept = 0; yyaccept = 0;
yych = *(m_marker = ++m_cursor); yych = *(m_marker = ++m_cursor);
if (yych <= 0x0F) if (yych == 0xBB)
{ {
goto basic_json_parser_5; goto basic_json_parser_64;
}
basic_json_parser_7:
{
return token_type::parse_error;
}
basic_json_parser_8:
++m_cursor;
{
return token_type::begin_array;
} }
goto basic_json_parser_32;
basic_json_parser_10: basic_json_parser_10:
++m_cursor; ++m_cursor;
{ {
return token_type::value_separator; return token_type::end_array;
} }
basic_json_parser_12: basic_json_parser_12:
++m_cursor;
{
return token_type::begin_object;
}
basic_json_parser_14:
++m_cursor;
{
return token_type::end_object;
}
basic_json_parser_16:
++m_cursor;
{
return token_type::value_separator;
}
basic_json_parser_18:
++m_cursor;
{
return token_type::name_separator;
}
basic_json_parser_20:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych == 'u')
{
goto basic_json_parser_60;
}
goto basic_json_parser_7;
basic_json_parser_21:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych == 'r')
{
goto basic_json_parser_56;
}
goto basic_json_parser_7;
basic_json_parser_22:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych == 'a')
{
goto basic_json_parser_51;
}
goto basic_json_parser_7;
basic_json_parser_23:
yych = *++m_cursor; yych = *++m_cursor;
if (yych <= '/') if (yych <= '/')
{ {
goto basic_json_parser_5; goto basic_json_parser_7;
} }
if (yych <= '0') if (yych <= '0')
{ {
goto basic_json_parser_13; goto basic_json_parser_50;
} }
if (yych <= '9') if (yych <= '9')
{ {
goto basic_json_parser_15; goto basic_json_parser_41;
} }
goto basic_json_parser_5; goto basic_json_parser_7;
basic_json_parser_13: basic_json_parser_24:
yyaccept = 1; yyaccept = 1;
yych = *(m_marker = ++m_cursor); yych = *(m_marker = ++m_cursor);
if (yych <= 'D') if (yych <= 'D')
{ {
if (yych == '.') if (yych == '.')
{ {
goto basic_json_parser_37; goto basic_json_parser_43;
} }
} }
else else
{ {
if (yych <= 'E') if (yych <= 'E')
{ {
goto basic_json_parser_38; goto basic_json_parser_44;
} }
if (yych == 'e') if (yych == 'e')
{ {
goto basic_json_parser_38; goto basic_json_parser_44;
} }
} }
basic_json_parser_14: basic_json_parser_25:
{ {
return token_type::value_number; return token_type::value_number;
} }
basic_json_parser_15: basic_json_parser_26:
yyaccept = 1; yyaccept = 1;
m_marker = ++m_cursor; yych = *(m_marker = ++m_cursor);
if ((m_limit - m_cursor) < 3) goto basic_json_parser_42;
basic_json_parser_27:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych <= 0x0F)
{
goto basic_json_parser_7;
}
goto basic_json_parser_32;
basic_json_parser_28:
++m_cursor;
{
return token_type::end_of_input;
}
basic_json_parser_30:
yych = *++m_cursor;
goto basic_json_parser_7;
basic_json_parser_31:
++m_cursor;
if (m_limit <= m_cursor)
{ {
yyfill(); // LCOV_EXCL_LINE; yyfill(); // LCOV_EXCL_LINE;
} }
yych = *m_cursor; yych = *m_cursor;
basic_json_parser_32:
if (yybm[0 + yych] & 64) if (yybm[0 + yych] & 64)
{ {
goto basic_json_parser_15; goto basic_json_parser_31;
} }
if (yych <= 'D') if (yych <= 0x0F)
{
if (yych == '.')
{ {
goto basic_json_parser_37; goto basic_json_parser_33;
}
goto basic_json_parser_14;
} }
else if (yych <= '"')
{ {
if (yych <= 'E') goto basic_json_parser_35;
{
goto basic_json_parser_38;
}
if (yych == 'e')
{
goto basic_json_parser_38;
}
goto basic_json_parser_14;
}
basic_json_parser_17:
++m_cursor;
{
return token_type::name_separator;
}
basic_json_parser_19:
++m_cursor;
{
return token_type::begin_array;
}
basic_json_parser_21:
++m_cursor;
{
return token_type::end_array;
}
basic_json_parser_23:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych == 'a')
{
goto basic_json_parser_39;
}
goto basic_json_parser_5;
basic_json_parser_24:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych == 'u')
{
goto basic_json_parser_40;
}
goto basic_json_parser_5;
basic_json_parser_25:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych == 'r')
{
goto basic_json_parser_41;
}
goto basic_json_parser_5;
basic_json_parser_26:
++m_cursor;
{
return token_type::begin_object;
}
basic_json_parser_28:
++m_cursor;
{
return token_type::end_object;
}
basic_json_parser_30:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
if (yych == 0xBB)
{
goto basic_json_parser_42;
}
goto basic_json_parser_5;
basic_json_parser_31:
++m_cursor;
if (m_limit <= m_cursor)
{
yyfill(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
basic_json_parser_32:
if (yybm[0 + yych] & 128)
{
goto basic_json_parser_31;
} }
if (yych <= 0x0F)
{
goto basic_json_parser_33;
}
if (yych <= '"')
{
goto basic_json_parser_34; goto basic_json_parser_34;
}
goto basic_json_parser_36;
basic_json_parser_33: basic_json_parser_33:
m_cursor = m_marker; m_cursor = m_marker;
if (yyaccept == 0) if (yyaccept == 0)
{ {
goto basic_json_parser_5; goto basic_json_parser_7;
} }
else else
{ {
goto basic_json_parser_14; goto basic_json_parser_25;
} }
basic_json_parser_34: basic_json_parser_34:
++m_cursor; ++m_cursor;
{
return token_type::value_string;
}
basic_json_parser_36:
++m_cursor;
if (m_limit <= m_cursor) if (m_limit <= m_cursor)
{ {
yyfill(); // LCOV_EXCL_LINE; yyfill(); // LCOV_EXCL_LINE;
...@@ -7704,78 +7821,117 @@ basic_json_parser_36: ...@@ -7704,78 +7821,117 @@ basic_json_parser_36:
} }
if (yych <= 'u') if (yych <= 'u')
{ {
goto basic_json_parser_43; goto basic_json_parser_37;
} }
goto basic_json_parser_33; goto basic_json_parser_33;
} }
} }
} }
basic_json_parser_35:
++m_cursor;
{
return token_type::value_string;
}
basic_json_parser_37: basic_json_parser_37:
yych = *++m_cursor; ++m_cursor;
if (m_limit <= m_cursor)
{
yyfill(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= '@')
{
if (yych <= '/') if (yych <= '/')
{ {
goto basic_json_parser_33; goto basic_json_parser_33;
} }
if (yych <= '9') if (yych >= ':')
{ {
goto basic_json_parser_44; goto basic_json_parser_33;
}
} }
else
{
if (yych <= 'F')
{
goto basic_json_parser_38;
}
if (yych <= '`')
{
goto basic_json_parser_33;
}
if (yych >= 'g')
{
goto basic_json_parser_33; goto basic_json_parser_33;
}
}
basic_json_parser_38: basic_json_parser_38:
yych = *++m_cursor; ++m_cursor;
if (yych <= ',') if (m_limit <= m_cursor)
{ {
if (yych == '+') yyfill(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= '@')
{ {
goto basic_json_parser_46; if (yych <= '/')
{
goto basic_json_parser_33;
} }
if (yych >= ':')
{
goto basic_json_parser_33; goto basic_json_parser_33;
} }
}
else else
{ {
if (yych <= '-') if (yych <= 'F')
{ {
goto basic_json_parser_46; goto basic_json_parser_39;
} }
if (yych <= '/') if (yych <= '`')
{ {
goto basic_json_parser_33; goto basic_json_parser_33;
} }
if (yych <= '9') if (yych >= 'g')
{ {
goto basic_json_parser_47;
}
goto basic_json_parser_33; goto basic_json_parser_33;
} }
}
basic_json_parser_39: basic_json_parser_39:
yych = *++m_cursor; ++m_cursor;
if (yych == 'l') if (m_limit <= m_cursor)
{ {
goto basic_json_parser_49; yyfill(); // LCOV_EXCL_LINE;
} }
goto basic_json_parser_33; yych = *m_cursor;
basic_json_parser_40: if (yych <= '@')
yych = *++m_cursor;
if (yych == 'l')
{ {
goto basic_json_parser_50; if (yych <= '/')
{
goto basic_json_parser_33;
} }
if (yych >= ':')
{
goto basic_json_parser_33; goto basic_json_parser_33;
basic_json_parser_41: }
yych = *++m_cursor; }
if (yych == 'u') else
{ {
goto basic_json_parser_51; if (yych <= 'F')
{
goto basic_json_parser_40;
} }
goto basic_json_parser_33; if (yych <= '`')
basic_json_parser_42:
yych = *++m_cursor;
if (yych == 0xBF)
{ {
goto basic_json_parser_52; goto basic_json_parser_33;
} }
if (yych >= 'g')
{
goto basic_json_parser_33; goto basic_json_parser_33;
basic_json_parser_43: }
}
basic_json_parser_40:
++m_cursor; ++m_cursor;
if (m_limit <= m_cursor) if (m_limit <= m_cursor)
{ {
...@@ -7790,7 +7946,7 @@ basic_json_parser_43: ...@@ -7790,7 +7946,7 @@ basic_json_parser_43:
} }
if (yych <= '9') if (yych <= '9')
{ {
goto basic_json_parser_54; goto basic_json_parser_31;
} }
goto basic_json_parser_33; goto basic_json_parser_33;
} }
...@@ -7798,7 +7954,7 @@ basic_json_parser_43: ...@@ -7798,7 +7954,7 @@ basic_json_parser_43:
{ {
if (yych <= 'F') if (yych <= 'F')
{ {
goto basic_json_parser_54; goto basic_json_parser_31;
} }
if (yych <= '`') if (yych <= '`')
{ {
...@@ -7806,11 +7962,11 @@ basic_json_parser_43: ...@@ -7806,11 +7962,11 @@ basic_json_parser_43:
} }
if (yych <= 'f') if (yych <= 'f')
{ {
goto basic_json_parser_54; goto basic_json_parser_31;
} }
goto basic_json_parser_33; goto basic_json_parser_33;
} }
basic_json_parser_44: basic_json_parser_41:
yyaccept = 1; yyaccept = 1;
m_marker = ++m_cursor; m_marker = ++m_cursor;
if ((m_limit - m_cursor) < 3) if ((m_limit - m_cursor) < 3)
...@@ -7818,208 +7974,206 @@ basic_json_parser_44: ...@@ -7818,208 +7974,206 @@ basic_json_parser_44:
yyfill(); // LCOV_EXCL_LINE; yyfill(); // LCOV_EXCL_LINE;
} }
yych = *m_cursor; yych = *m_cursor;
if (yych <= 'D') basic_json_parser_42:
{ if (yybm[0 + yych] & 128)
if (yych <= '/')
{ {
goto basic_json_parser_14; goto basic_json_parser_41;
} }
if (yych <= '9') if (yych <= 'D')
{ {
goto basic_json_parser_44; if (yych != '.')
{
goto basic_json_parser_25;
} }
goto basic_json_parser_14;
} }
else else
{ {
if (yych <= 'E') if (yych <= 'E')
{ {
goto basic_json_parser_38; goto basic_json_parser_44;
} }
if (yych == 'e') if (yych == 'e')
{ {
goto basic_json_parser_38; goto basic_json_parser_44;
} }
goto basic_json_parser_14; goto basic_json_parser_25;
} }
basic_json_parser_46: basic_json_parser_43:
yych = *++m_cursor; yych = *++m_cursor;
if (yych <= '/') if (yych <= '/')
{ {
goto basic_json_parser_33; goto basic_json_parser_33;
} }
if (yych >= ':') if (yych <= '9')
{
goto basic_json_parser_48;
}
goto basic_json_parser_33;
basic_json_parser_44:
yych = *++m_cursor;
if (yych <= ',')
{
if (yych != '+')
{ {
goto basic_json_parser_33; goto basic_json_parser_33;
} }
basic_json_parser_47: }
++m_cursor; else
if (m_limit <= m_cursor)
{ {
yyfill(); // LCOV_EXCL_LINE; if (yych <= '-')
{
goto basic_json_parser_45;
} }
yych = *m_cursor;
if (yych <= '/') if (yych <= '/')
{ {
goto basic_json_parser_14; goto basic_json_parser_33;
} }
if (yych <= '9') if (yych <= '9')
{ {
goto basic_json_parser_47; goto basic_json_parser_46;
}
goto basic_json_parser_14;
basic_json_parser_49:
yych = *++m_cursor;
if (yych == 's')
{
goto basic_json_parser_55;
} }
goto basic_json_parser_33; goto basic_json_parser_33;
basic_json_parser_50:
yych = *++m_cursor;
if (yych == 'l')
{
goto basic_json_parser_56;
} }
goto basic_json_parser_33; basic_json_parser_45:
basic_json_parser_51:
yych = *++m_cursor; yych = *++m_cursor;
if (yych == 'e') if (yych <= '/')
{ {
goto basic_json_parser_58;
}
goto basic_json_parser_33; goto basic_json_parser_33;
basic_json_parser_52: }
++m_cursor; if (yych >= ':')
{ {
return scan(); goto basic_json_parser_33;
} }
basic_json_parser_54: basic_json_parser_46:
++m_cursor; ++m_cursor;
if (m_limit <= m_cursor) if (m_limit <= m_cursor)
{ {
yyfill(); // LCOV_EXCL_LINE; yyfill(); // LCOV_EXCL_LINE;
} }
yych = *m_cursor; yych = *m_cursor;
if (yych <= '@')
{
if (yych <= '/') if (yych <= '/')
{ {
goto basic_json_parser_33; goto basic_json_parser_25;
} }
if (yych <= '9') if (yych <= '9')
{ {
goto basic_json_parser_60; goto basic_json_parser_46;
} }
goto basic_json_parser_33; goto basic_json_parser_25;
basic_json_parser_48:
yyaccept = 1;
m_marker = ++m_cursor;
if ((m_limit - m_cursor) < 3)
{
yyfill(); // LCOV_EXCL_LINE;
} }
else yych = *m_cursor;
if (yych <= 'D')
{ {
if (yych <= 'F') if (yych <= '/')
{ {
goto basic_json_parser_60; goto basic_json_parser_25;
} }
if (yych <= '`') if (yych <= '9')
{ {
goto basic_json_parser_33; goto basic_json_parser_48;
} }
if (yych <= 'f') goto basic_json_parser_25;
{
goto basic_json_parser_60;
} }
goto basic_json_parser_33; else
{
if (yych <= 'E')
{
goto basic_json_parser_44;
} }
basic_json_parser_55:
yych = *++m_cursor;
if (yych == 'e') if (yych == 'e')
{ {
goto basic_json_parser_61; goto basic_json_parser_44;
} }
goto basic_json_parser_33; goto basic_json_parser_25;
basic_json_parser_56:
++m_cursor;
{
return token_type::literal_null;
} }
basic_json_parser_58: basic_json_parser_50:
++m_cursor; yyaccept = 1;
yych = *(m_marker = ++m_cursor);
if (yych <= 'D')
{ {
return token_type::literal_true; if (yych == '.')
}
basic_json_parser_60:
++m_cursor;
if (m_limit <= m_cursor)
{ {
yyfill(); // LCOV_EXCL_LINE; goto basic_json_parser_43;
} }
yych = *m_cursor; goto basic_json_parser_25;
if (yych <= '@') }
else
{ {
if (yych <= '/') if (yych <= 'E')
{ {
goto basic_json_parser_33; goto basic_json_parser_44;
} }
if (yych <= '9') if (yych == 'e')
{ {
goto basic_json_parser_63; goto basic_json_parser_44;
} }
goto basic_json_parser_33; goto basic_json_parser_25;
} }
else basic_json_parser_51:
{ yych = *++m_cursor;
if (yych <= 'F') if (yych != 'l')
{ {
goto basic_json_parser_63; goto basic_json_parser_33;
} }
if (yych <= '`') yych = *++m_cursor;
if (yych != 's')
{ {
goto basic_json_parser_33; goto basic_json_parser_33;
} }
if (yych <= 'f') yych = *++m_cursor;
if (yych != 'e')
{ {
goto basic_json_parser_63;
}
goto basic_json_parser_33; goto basic_json_parser_33;
} }
basic_json_parser_61:
++m_cursor; ++m_cursor;
{ {
return token_type::literal_false; return token_type::literal_false;
} }
basic_json_parser_63: basic_json_parser_56:
++m_cursor; yych = *++m_cursor;
if (m_limit <= m_cursor) if (yych != 'u')
{ {
yyfill(); // LCOV_EXCL_LINE; goto basic_json_parser_33;
} }
yych = *m_cursor; yych = *++m_cursor;
if (yych <= '@') if (yych != 'e')
{
if (yych <= '/')
{ {
goto basic_json_parser_33; goto basic_json_parser_33;
} }
if (yych <= '9') ++m_cursor;
{ {
goto basic_json_parser_31; return token_type::literal_true;
} }
basic_json_parser_60:
yych = *++m_cursor;
if (yych != 'l')
{
goto basic_json_parser_33; goto basic_json_parser_33;
} }
else yych = *++m_cursor;
if (yych != 'l')
{ {
if (yych <= 'F') goto basic_json_parser_33;
}
++m_cursor;
{ {
goto basic_json_parser_31; return token_type::literal_null;
} }
if (yych <= '`') basic_json_parser_64:
yych = *++m_cursor;
if (yych != 0xBF)
{ {
goto basic_json_parser_33; goto basic_json_parser_33;
} }
if (yych <= 'f') ++m_cursor;
{ {
goto basic_json_parser_31; return scan();
}
goto basic_json_parser_33;
} }
} }
...@@ -8249,123 +8403,144 @@ basic_json_parser_63: ...@@ -8249,123 +8403,144 @@ basic_json_parser_63:
} }
/*! /*!
@brief static_cast between two types and indicate if it results in error @brief return number value for number tokens
This function performs a `static_cast` between @a source and @a dest. This function translates the last token into the most appropriate
It then checks if a `static_cast` back to @a dest produces an error. number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
@param[in] source the value to cast from This function parses the integer component up to the radix point or
exponent while collecting information about the 'floating point
representation', which it stores in the result parameter. If there is
no radix point or exponent, and the number can fit into a
@ref number_integer_t or @ref number_unsigned_t then it sets the
result parameter accordingly.
@param[in, out] dest the value to cast to The 'floating point representation' includes the number of significant
figures after the radix point, whether the number is in exponential
or decimal form, the capitalization of the exponent marker, and if the
optional '+' is present in the exponent. This information is necessary
to perform accurate round trips of floating point numbers.
@return true iff the cast was performed without error If the number is a floating point number the number is then parsed
*/ using @a std:strtod (or @a std:strtof or @a std::strtold).
template <typename T_A, typename T_B>
static bool attempt_cast(T_A source, T_B& dest)
{
dest = static_cast<T_B>(source);
return (source == static_cast<T_A>(dest));
}
/*!
@brief return number value for number tokens
This function translates the last token into the most appropriate @param[out] result @ref basic_json object to receive the number, or
number type (either integer, unsigned integer or floating point), which NAN if the conversion read past the current token. The latter case
is passed back to the caller via the result parameter. The pointer @a needs to be treated by the caller function.
m_start points to the beginning of the parsed number. We first examine
the first character to determine the sign of the number and then pass
this pointer to either @a std::strtoull (if positive) or @a
std::strtoll (if negative), both of which set @a endptr to the first
character past the converted number. If this pointer is not the same as
@a m_cursor, then either more or less characters have been used during
the comparison.
This can happen for inputs like "01" which will be treated like number
0 followed by number 1. This will also occur for valid floating point
inputs like "12e3" will be incorrectly read as 12. Numbers that are too
large or too small for a signed/unsigned long long will cause a range
error (@a errno set to ERANGE). The parsed number is cast to a @ref
number_integer_t/@ref number_unsigned_t using the helper function @ref
attempt_cast, which returns @a false if the cast could not be peformed
without error.
In any of these cases (more/less characters read, range error or a cast
error) the pointer is passed to @a std:strtod, which also sets @a
endptr to the first character past the converted number. The resulting
@ref number_float_t is then cast to a @ref number_integer_t/@ref
number_unsigned_t using @ref attempt_cast and if no error occurs is
stored in that form, otherwise it is stored as a @ref number_float_t.
A final comparison is made of @a endptr and if still not the same as
@ref m_cursor a bad input is assumed and @a result parameter is set to
NAN.
@param[out] result @ref basic_json object to receive the number, or NAN
if the conversion read past the current token. The latter case needs to
be treated by the caller function.
*/ */
void get_number(basic_json& result) const void get_number(basic_json& result) const
{ {
typename string_t::value_type* endptr;
assert(m_start != nullptr); assert(m_start != nullptr);
errno = 0;
// attempt to parse it as an integer - first checking for a const lexer::lexer_char_t* curptr = m_start;
// negative number
if (*reinterpret_cast<typename string_t::const_pointer>(m_start) != '-') // remember this number was parsed (for later serialization)
{ result.m_type.bits.parsed = true;
// positive, parse with strtoull and attempt cast to
// number_unsigned_t // 'found_radix_point' will be set to 0xFF upon finding a radix
if (attempt_cast(std::strtoull(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr, // point and later used to mask in/out the precision depending
10), result.m_value.number_unsigned)) // whether a radix is found i.e. 'precision &= found_radix_point'
uint8_t found_radix_point = 0;
uint8_t precision = 0;
// accumulate the integer conversion result (unsigned for now)
number_unsigned_t value = 0;
// maximum absolute value of the relevant integer type
number_unsigned_t max;
// temporarily store the type to avoid unecessary bitfield access
value_t type;
// look for sign
if (*curptr == '-')
{ {
result.m_type = value_t::number_unsigned; type = value_t::number_integer;
max = static_cast<uint64_t>(std::numeric_limits<number_integer_t>::max()) + 1;
curptr++;
} }
else else
{ {
// cast failed due to overflow - store as float type = value_t::number_unsigned;
result.m_type = value_t::number_float; max = static_cast<uint64_t>(std::numeric_limits<number_unsigned_t>::max());
if (*curptr == '+')
{
curptr++;
} }
} }
else
// count the significant figures
for (; curptr < m_cursor; curptr++)
{ {
// Negative, parse with strtoll and attempt cast to // quickly skip tests if a digit
// number_integer_t if (*curptr < '0' || *curptr > '9')
if (attempt_cast(std::strtoll(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr,
10), result.m_value.number_integer))
{ {
result.m_type = value_t::number_integer; if (*curptr == '.')
}
else
{ {
// cast failed due to overflow - store as float // don't count '.' but change to float
result.m_type = value_t::number_float; type = value_t::number_float;
// reset precision count
precision = 0;
found_radix_point = 0xFF;
continue;
} }
// assume exponent (if not then will fail parse): change to
// float, stop counting and record exponent details
type = value_t::number_float;
result.m_type.bits.has_exp = true;
// exponent capitalization
result.m_type.bits.exp_cap = (*curptr == 'E');
// exponent '+' sign
result.m_type.bits.exp_plus = (*(++curptr) == '+');
break;
} }
// check the end of the number was reached and no range error // skip if definitely not an integer
// occurred if (type != value_t::number_float)
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor || errno == ERANGE)
{ {
result.m_type = value_t::number_float; // multiply last value by ten and add the new digit
} auto temp = value * 10 + *curptr - 0x30;
if (result.m_type == value_t::number_float) // test for overflow
if (temp < value || temp > max)
{
// overflow
type = value_t::number_float;
}
else
{ {
// either the number won't fit in an integer (range error from // no overflow - save it
// strtoull/strtoll or overflow on cast) or there was something value = temp;
// else after the number, which could be an exponent }
}
++precision;
}
// parse with strtod // If no radix point was found then precision would now be set to
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), &endptr); // the number of digits, which is wrong - clear it.
result.m_type.bits.precision = precision & found_radix_point;
// anything after the number is an error // save the value (if not a float)
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor) if (type == value_t::number_unsigned)
{ {
throw std::invalid_argument(std::string("parse error - ") + get_token() + " is not a number"); result.m_value.number_unsigned = value;
} }
else if (type == value_t::number_integer)
{
result.m_value.number_integer = -static_cast<number_integer_t>(value);
}
else
{
// parse with strtod
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL);
} }
// save the type
result.m_type = type;
} }
private: private:
......
...@@ -695,6 +695,74 @@ class basic_json ...@@ -695,6 +695,74 @@ class basic_json
private: private:
/*!
@brief a type to hold JSON type information
This bitfield type holds information about JSON types. It is internally
used to hold the basic JSON type enumeration, as well as additional
information in the case of values that have been parsed from a string
including whether of not it was created directly or parsed, and in the
case of floating point numbers the number of significant figures in the
original representaiton and if it was in exponential form, if a '+' was
included in the exponent and the capitilization of the exponent marker.
The sole purpose of this information is to permit accurate round trips.
@since version 2.0.0
*/
union type_data_t
{
struct
{
/// the type of the value (@ref value_t)
uint16_t type : 4;
/// whether the number was parsed from a string
uint16_t parsed : 1;
/// whether parsed number contained an exponent ('e'/'E')
uint16_t has_exp : 1;
/// whether parsed number contained a plus in the exponent
uint16_t exp_plus : 1;
/// whether parsed number's exponent was capitalized ('E')
uint16_t exp_cap : 1;
/// the number of figures for a parsed number
uint16_t precision : 8;
} bits;
uint16_t data;
/// return the type as value_t
operator value_t() const
{
return static_cast<value_t>(bits.type);
}
/// test type for equality (ignore other fields)
bool operator==(const value_t& rhs) const
{
return static_cast<value_t>(bits.type) == rhs;
}
/// assignment
type_data_t& operator=(value_t rhs)
{
bits.type = static_cast<uint16_t>(rhs);
return *this;
}
/// construct from value_t
type_data_t(value_t t) noexcept
{
*reinterpret_cast<uint16_t*>(this) = 0;
bits.type = static_cast<uint16_t>(t);
}
/// default constructor
type_data_t() noexcept
{
data = 0;
bits.type = reinterpret_cast<uint16_t>(value_t::null);
}
};
/// helper for exception-safe object creation /// helper for exception-safe object creation
template<typename T, typename... Args> template<typename T, typename... Args>
static T* create(Args&& ... args) static T* create(Args&& ... args)
...@@ -6046,23 +6114,78 @@ class basic_json ...@@ -6046,23 +6114,78 @@ class basic_json
case value_t::number_float: case value_t::number_float:
{ {
// If the number is an integer then output as a fixed with with // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
// precision 1 to output "0.0", "1.0" etc as expected for some char buf[263];
// round trip tests otherwise 15 digits of precision allows int len;
// round-trip IEEE 754 string->double->string; to be safe, we
// read this value from // check if number was parsed from a string
// std::numeric_limits<number_float_t>::digits10 if (m_type.bits.parsed)
if (std::fmod(m_value.number_float, 1) == 0)
{ {
o << std::fixed << std::setprecision(1); // check if parsed number had an exponent given
if (m_type.bits.has_exp)
{
// handle capitalization of the exponent
if (m_type.bits.exp_cap)
{
len = snprintf(buf, sizeof(buf), "%.*E", m_type.bits.precision, m_value.number_float) + 1;
} }
else else
{ {
// std::defaultfloat not supported in gcc version < 5 len = snprintf(buf, sizeof(buf), "%.*e", m_type.bits.precision, m_value.number_float) + 1;
o.unsetf(std::ios_base::floatfield); }
o << std::setprecision(std::numeric_limits<double>::digits10);
// remove '+' sign from the exponent if necessary
if (not m_type.bits.exp_plus)
{
if (len > static_cast<int>(sizeof(buf)))
{
len = sizeof(buf);
}
for (int i = 0; i < len; i++)
{
if (buf[i] == '+')
{
for (; i + 1 < len; i++)
{
buf[i] = buf[i + 1];
}
}
}
}
}
else
{
// no exponent - output as a decimal
snprintf(buf, sizeof(buf), "%.*f",
m_type.bits.precision, m_value.number_float);
}
}
else if (m_value.number_float == 0)
{
// special case for zero to get "0.0"/"-0.0"
if (std::signbit(m_value.number_float))
{
o << "-0.0";
}
else
{
o << "0.0";
} }
o << m_value.number_float; return;
}
else
{
// Otherwise 6, 15 or 16 digits of precision allows
// round-trip IEEE 754 string->float->string,
// string->double->string or string->long double->string;
// to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
snprintf(buf, sizeof(buf), "%.*g",
std::numeric_limits<double>::digits10,
m_value.number_float);
}
o << buf;
return; return;
} }
...@@ -6086,7 +6209,7 @@ class basic_json ...@@ -6086,7 +6209,7 @@ class basic_json
////////////////////// //////////////////////
/// the type of the current element /// the type of the current element
value_t m_type = value_t::null; type_data_t m_type = value_t::null;
/// the value of the current element /// the value of the current element
json_value m_value = {}; json_value m_value = {};
...@@ -7559,123 +7682,144 @@ class basic_json ...@@ -7559,123 +7682,144 @@ class basic_json
} }
/*! /*!
@brief static_cast between two types and indicate if it results in error @brief return number value for number tokens
This function performs a `static_cast` between @a source and @a dest. This function translates the last token into the most appropriate
It then checks if a `static_cast` back to @a dest produces an error. number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
@param[in] source the value to cast from This function parses the integer component up to the radix point or
exponent while collecting information about the 'floating point
representation', which it stores in the result parameter. If there is
no radix point or exponent, and the number can fit into a
@ref number_integer_t or @ref number_unsigned_t then it sets the
result parameter accordingly.
@param[in, out] dest the value to cast to The 'floating point representation' includes the number of significant
figures after the radix point, whether the number is in exponential
or decimal form, the capitalization of the exponent marker, and if the
optional '+' is present in the exponent. This information is necessary
to perform accurate round trips of floating point numbers.
@return true iff the cast was performed without error If the number is a floating point number the number is then parsed
*/ using @a std:strtod (or @a std:strtof or @a std::strtold).
template <typename T_A, typename T_B>
static bool attempt_cast(T_A source, T_B& dest)
{
dest = static_cast<T_B>(source);
return (source == static_cast<T_A>(dest));
}
/*!
@brief return number value for number tokens
This function translates the last token into the most appropriate @param[out] result @ref basic_json object to receive the number, or
number type (either integer, unsigned integer or floating point), which NAN if the conversion read past the current token. The latter case
is passed back to the caller via the result parameter. The pointer @a needs to be treated by the caller function.
m_start points to the beginning of the parsed number. We first examine
the first character to determine the sign of the number and then pass
this pointer to either @a std::strtoull (if positive) or @a
std::strtoll (if negative), both of which set @a endptr to the first
character past the converted number. If this pointer is not the same as
@a m_cursor, then either more or less characters have been used during
the comparison.
This can happen for inputs like "01" which will be treated like number
0 followed by number 1. This will also occur for valid floating point
inputs like "12e3" will be incorrectly read as 12. Numbers that are too
large or too small for a signed/unsigned long long will cause a range
error (@a errno set to ERANGE). The parsed number is cast to a @ref
number_integer_t/@ref number_unsigned_t using the helper function @ref
attempt_cast, which returns @a false if the cast could not be peformed
without error.
In any of these cases (more/less characters read, range error or a cast
error) the pointer is passed to @a std:strtod, which also sets @a
endptr to the first character past the converted number. The resulting
@ref number_float_t is then cast to a @ref number_integer_t/@ref
number_unsigned_t using @ref attempt_cast and if no error occurs is
stored in that form, otherwise it is stored as a @ref number_float_t.
A final comparison is made of @a endptr and if still not the same as
@ref m_cursor a bad input is assumed and @a result parameter is set to
NAN.
@param[out] result @ref basic_json object to receive the number, or NAN
if the conversion read past the current token. The latter case needs to
be treated by the caller function.
*/ */
void get_number(basic_json& result) const void get_number(basic_json& result) const
{ {
typename string_t::value_type* endptr;
assert(m_start != nullptr); assert(m_start != nullptr);
errno = 0;
// attempt to parse it as an integer - first checking for a const lexer::lexer_char_t* curptr = m_start;
// negative number
if (*reinterpret_cast<typename string_t::const_pointer>(m_start) != '-') // remember this number was parsed (for later serialization)
{ result.m_type.bits.parsed = true;
// positive, parse with strtoull and attempt cast to
// number_unsigned_t // 'found_radix_point' will be set to 0xFF upon finding a radix
if (attempt_cast(std::strtoull(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr, // point and later used to mask in/out the precision depending
10), result.m_value.number_unsigned)) // whether a radix is found i.e. 'precision &= found_radix_point'
uint8_t found_radix_point = 0;
uint8_t precision = 0;
// accumulate the integer conversion result (unsigned for now)
number_unsigned_t value = 0;
// maximum absolute value of the relevant integer type
number_unsigned_t max;
// temporarily store the type to avoid unecessary bitfield access
value_t type;
// look for sign
if (*curptr == '-')
{ {
result.m_type = value_t::number_unsigned; type = value_t::number_integer;
max = static_cast<uint64_t>(std::numeric_limits<number_integer_t>::max()) + 1;
curptr++;
} }
else else
{ {
// cast failed due to overflow - store as float type = value_t::number_unsigned;
result.m_type = value_t::number_float; max = static_cast<uint64_t>(std::numeric_limits<number_unsigned_t>::max());
if (*curptr == '+')
{
curptr++;
} }
} }
else
// count the significant figures
for (; curptr < m_cursor; curptr++)
{ {
// Negative, parse with strtoll and attempt cast to // quickly skip tests if a digit
// number_integer_t if (*curptr < '0' || *curptr > '9')
if (attempt_cast(std::strtoll(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr,
10), result.m_value.number_integer))
{ {
result.m_type = value_t::number_integer; if (*curptr == '.')
}
else
{ {
// cast failed due to overflow - store as float // don't count '.' but change to float
result.m_type = value_t::number_float; type = value_t::number_float;
// reset precision count
precision = 0;
found_radix_point = 0xFF;
continue;
} }
// assume exponent (if not then will fail parse): change to
// float, stop counting and record exponent details
type = value_t::number_float;
result.m_type.bits.has_exp = true;
// exponent capitalization
result.m_type.bits.exp_cap = (*curptr == 'E');
// exponent '+' sign
result.m_type.bits.exp_plus = (*(++curptr) == '+');
break;
} }
// check the end of the number was reached and no range error // skip if definitely not an integer
// occurred if (type != value_t::number_float)
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor || errno == ERANGE)
{ {
result.m_type = value_t::number_float; // multiply last value by ten and add the new digit
} auto temp = value * 10 + *curptr - 0x30;
if (result.m_type == value_t::number_float) // test for overflow
if (temp < value || temp > max)
{
// overflow
type = value_t::number_float;
}
else
{ {
// either the number won't fit in an integer (range error from // no overflow - save it
// strtoull/strtoll or overflow on cast) or there was something value = temp;
// else after the number, which could be an exponent }
}
++precision;
}
// parse with strtod // If no radix point was found then precision would now be set to
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), &endptr); // the number of digits, which is wrong - clear it.
result.m_type.bits.precision = precision & found_radix_point;
// anything after the number is an error // save the value (if not a float)
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor) if (type == value_t::number_unsigned)
{ {
throw std::invalid_argument(std::string("parse error - ") + get_token() + " is not a number"); result.m_value.number_unsigned = value;
} }
else if (type == value_t::number_integer)
{
result.m_value.number_integer = -static_cast<number_integer_t>(value);
}
else
{
// parse with strtod
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL);
} }
// save the type
result.m_type = type;
} }
private: private:
......
[4.940656458412e-324]
\ No newline at end of file
[2.2250738585072e-308]
\ No newline at end of file
[1.2345E-30]
\ No newline at end of file
[1.2345E+30]
\ No newline at end of file
[1.2345e+30]
\ No newline at end of file
...@@ -9776,7 +9776,8 @@ TEST_CASE("parser class") ...@@ -9776,7 +9776,8 @@ TEST_CASE("parser class")
CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("01").parse(), "parse error - 0 is not a number"); CHECK_THROWS_WITH(json::parser("01").parse(),
"parse error - unexpected number literal; expected end of input");
CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("1.").parse(), CHECK_THROWS_WITH(json::parser("1.").parse(),
"parse error - unexpected '.'; expected end of input"); "parse error - unexpected '.'; expected end of input");
...@@ -11823,10 +11824,15 @@ TEST_CASE("compliance tests from nativejson-benchmark") ...@@ -11823,10 +11824,15 @@ TEST_CASE("compliance tests from nativejson-benchmark")
"test/json_roundtrip/roundtrip21.json", "test/json_roundtrip/roundtrip21.json",
"test/json_roundtrip/roundtrip22.json", "test/json_roundtrip/roundtrip22.json",
"test/json_roundtrip/roundtrip23.json", "test/json_roundtrip/roundtrip23.json",
//"test/json_roundtrip/roundtrip24.json", "test/json_roundtrip/roundtrip24.json",
//"test/json_roundtrip/roundtrip25.json", "test/json_roundtrip/roundtrip25.json",
//"test/json_roundtrip/roundtrip26.json", "test/json_roundtrip/roundtrip26.json",
//"test/json_roundtrip/roundtrip27.json" "test/json_roundtrip/roundtrip27.json",
"test/json_roundtrip/roundtrip28.json",
"test/json_roundtrip/roundtrip29.json",
"test/json_roundtrip/roundtrip30.json",
"test/json_roundtrip/roundtrip31.json",
"test/json_roundtrip/roundtrip32.json"
}) })
{ {
CAPTURE(filename); CAPTURE(filename);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment