From 707732a53ebf5efc8555413180896e13fdd5ec04 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 26 Jan 2016 19:50:49 +0100 Subject: [PATCH] clean up after #193 --- README.md | 4 +- src/json.hpp | 1287 ++++++++++++++++++++++++++++++--------------- src/json.hpp.re2c | 224 ++++---- test/unit.cpp | 42 +- 4 files changed, 1012 insertions(+), 545 deletions(-) diff --git a/README.md b/README.md index acb2e9e2..8d180c28 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,7 @@ I deeply appreciate the help of the following people. - [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. - [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. +- [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. - [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file. Thanks a lot for helping out! @@ -409,7 +409,7 @@ $ make $ ./json_unit "*" =============================================================================== -All tests passed (3343329 assertions in 29 test cases) +All tests passed (3344278 assertions in 29 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/src/json.hpp b/src/json.hpp index 3bba54a6..8b8ed337 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -124,8 +124,8 @@ default; will be used in @ref string_t) in @ref boolean_t) @tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c `uint64_t` by -default; will be used in @ref number_unsigned_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) @tparam NumberFloatType type for JSON floating-point numbers (@c `double` by default; will be used in @ref number_float_t) @tparam AllocatorType type of the allocator to use (@c `std::allocator` by @@ -485,9 +485,9 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is a + C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, - three different types, @ref number_integer_t, @ref number_unsigned_t and + three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template @@ -556,9 +556,9 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is a + C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, - three different types, @ref number_integer_t, @ref number_unsigned_t and + three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store unsigned integer numbers in C++, a type is defined by the template @@ -588,11 +588,11 @@ class basic_json > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer number - that can be stored is `0`. Integer numbers - that are out of range will yield over/underflow when used in a constructor. - During deserialization, too large or small integer numbers will be - automatically be stored as @ref number_integer_t or @ref number_float_t. + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. [RFC 7159](http://rfc7159.net/rfc7159) further states: > Note that when such software is used, numbers that are integers and are @@ -600,7 +600,7 @@ class basic_json > that implementations will agree exactly on their numeric values. As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], this + number_integer_t type) of the exactly supported range [0, UINT64_MAX], this class's integer type is interoperable. #### Storage @@ -614,7 +614,7 @@ class basic_json @since version 2.0.0 */ using number_unsigned_t = NumberUnsignedType; - + /*! @brief a type for a number (floating-point) @@ -628,9 +628,9 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is a + C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, - three different types, @ref number_integer_t, @ref number_unsigned_t and + three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template @@ -801,7 +801,7 @@ class basic_json number_integer = number_integer_t(0); break; } - + case value_t::number_unsigned: { number_unsigned = number_unsigned_t(0); @@ -1330,8 +1330,8 @@ class basic_json template::value and - std::numeric_limits::is_integer and - std::numeric_limits::is_signed, + std::numeric_limits::is_integer and + std::numeric_limits::is_signed, CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept @@ -1344,7 +1344,7 @@ class basic_json Create an unsigned integer number JSON value with a given content. - @tparam T helper type to compare number_unsigned_t and unsigned int + @tparam T helper type to compare number_unsigned_t and unsigned int (not visible in) the interface. @param[in] val an integer to create a JSON number from @@ -1365,14 +1365,14 @@ class basic_json basic_json(const number_unsigned_t val) : m_type(value_t::number_unsigned), m_value(val) {} - + /*! @brief create an unsigned number (implicit) Create an unsigned number JSON value with a given content. This constructor allows any type that can be used to construct values of type @ref - number_unsigned_t. Examples may include the types `unsigned int`, `uint32_t`, - or `unsigned short`. + number_unsigned_t. Examples may include the types `unsigned int`, + `uint32_t`, or `unsigned short`. @tparam CompatibleNumberUnsignedType an integer type which is compatible to @ref number_unsigned_t. @@ -1386,13 +1386,13 @@ class basic_json @since version 2.0.0 */ - template::value and - std::numeric_limits::is_integer and - !std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template < typename CompatibleNumberUnsignedType, typename + std::enable_if < + std::is_constructible::value and + std::numeric_limits::is_integer and + !std::numeric_limits::is_signed, + CompatibleNumberUnsignedType >::type + = 0 > basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1781,7 +1781,7 @@ class basic_json m_value.number_integer = first.m_object->m_value.number_integer; break; } - + case value_t::number_unsigned: { assert(first.m_object != nullptr); @@ -1891,7 +1891,7 @@ class basic_json m_value = other.m_value.number_integer; break; } - + case value_t::number_unsigned: { m_value = other.m_value.number_unsigned; @@ -2183,9 +2183,10 @@ class basic_json @liveexample{The following code exemplifies @ref is_number for all JSON types.,is_number} - @sa @ref is_number_integer() -- check if value is an integer or unsigned + @sa @ref is_number_integer() -- check if value is an integer or unsigned integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 @@ -2198,10 +2199,10 @@ class basic_json /*! @brief return whether value is an integer number - This function returns true iff the JSON value is an integer or unsigned + This function returns true iff the JSON value is an integer or unsigned integer number. This excludes floating-point values. - @return `true` if type is an integer or unsigned integer number, `false` + @return `true` if type is an integer or unsigned integer number, `false` otherwise. @complexity Constant. @@ -2210,7 +2211,8 @@ class basic_json JSON types.,is_number_integer} @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 @@ -2219,19 +2221,19 @@ class basic_json { return m_type == value_t::number_integer or m_type == value_t::number_unsigned; } - + /*! @brief return whether value is an unsigned integer number - This function returns true iff the JSON value is an unsigned integer number. - This excludes floating-point and (signed) integer values. + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. @return `true` if type is an unsigned integer number, `false` otherwise. @complexity Constant. @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned + @sa @ref is_number_integer() -- check if value is an integer or unsigned integer number @sa @ref is_number_float() -- check if value is a floating-point number @@ -2257,7 +2259,8 @@ class basic_json @sa @ref is_number() -- check if value is number @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @since version 1.0.0 */ @@ -2525,7 +2528,7 @@ class basic_json { return static_cast(m_value.number_integer); } - + case value_t::number_unsigned: { return static_cast(m_value.number_unsigned); @@ -2615,7 +2618,7 @@ class basic_json { return is_number_integer() ? &m_value.number_integer : nullptr; } - + /// get a pointer to the value (unsigned number) number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept { @@ -2627,7 +2630,7 @@ class basic_json { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } - + /// get a pointer to the value (floating-point number) number_float_t* get_impl_ptr(number_float_t*) noexcept { @@ -2725,7 +2728,7 @@ class basic_json @warning The pointer becomes invalid if the underlying JSON object changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested @@ -2776,7 +2779,7 @@ class basic_json state. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested @@ -2895,14 +2898,14 @@ class basic_json @since version 1.0.0 */ - template::value - and not std::is_same::value + template < typename ValueType, typename + std::enable_if < + not std::is_pointer::value + and not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 - and not std::is_same>::value + and not std::is_same>::value #endif - , int>::type = 0> + , int >::type = 0 > operator ValueType() const { // delegate the call to get<>() const @@ -7100,386 +7103,789 @@ class basic_json m_start = m_cursor; assert(m_start != nullptr); - - { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 96, 64, 0, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 0, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - }; - if ((m_limit - m_cursor) < 5) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; - if (yych <= ':') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych <= 0x00) goto basic_json_parser_28; - if (yych <= 0x08) goto basic_json_parser_30; - if (yych >= '\n') goto basic_json_parser_4; - } else { - if (yych == '\r') goto basic_json_parser_2; - if (yych <= 0x1F) goto basic_json_parser_30; + + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 96, 64, 0, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 0, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }; + if ((m_limit - m_cursor) < 5) + { + yyfill(); // LCOV_EXCL_LINE; } - } else { - if (yych <= ',') { - if (yych == '"') goto basic_json_parser_27; - if (yych <= '+') goto basic_json_parser_30; - goto basic_json_parser_16; - } else { - if (yych <= '/') { - if (yych <= '-') goto basic_json_parser_23; - goto basic_json_parser_30; - } else { - if (yych <= '0') goto basic_json_parser_24; - if (yych <= '9') goto basic_json_parser_26; - goto basic_json_parser_18; + yych = *m_cursor; + if (yych <= ':') + { + if (yych <= ' ') + { + if (yych <= '\n') + { + if (yych <= 0x00) + { + goto basic_json_parser_28; + } + if (yych <= 0x08) + { + goto basic_json_parser_30; + } + if (yych >= '\n') + { + goto basic_json_parser_4; + } + } + else + { + if (yych == '\r') + { + goto basic_json_parser_2; + } + if (yych <= 0x1F) + { + goto basic_json_parser_30; + } + } + } + else + { + if (yych <= ',') + { + if (yych == '"') + { + goto basic_json_parser_27; + } + if (yych <= '+') + { + goto basic_json_parser_30; + } + goto basic_json_parser_16; + } + else + { + if (yych <= '/') + { + if (yych <= '-') + { + goto basic_json_parser_23; + } + goto basic_json_parser_30; + } + else + { + if (yych <= '0') + { + goto basic_json_parser_24; + } + if (yych <= '9') + { + goto basic_json_parser_26; + } + goto basic_json_parser_18; + } + } } } - } - } else { - if (yych <= 'n') { - if (yych <= ']') { - if (yych == '[') goto basic_json_parser_8; - if (yych <= '\\') goto basic_json_parser_30; - goto basic_json_parser_10; - } else { - if (yych == 'f') goto basic_json_parser_22; - if (yych <= 'm') goto basic_json_parser_30; - goto basic_json_parser_20; - } - } else { - if (yych <= '{') { - if (yych == 't') goto basic_json_parser_21; - if (yych <= 'z') goto basic_json_parser_30; - goto basic_json_parser_12; - } else { - if (yych <= '}') { - if (yych <= '|') goto basic_json_parser_30; - goto basic_json_parser_14; - } else { - if (yych == 0xEF) goto basic_json_parser_6; - goto basic_json_parser_30; + else + { + if (yych <= 'n') + { + if (yych <= ']') + { + if (yych == '[') + { + goto basic_json_parser_8; + } + if (yych <= '\\') + { + goto basic_json_parser_30; + } + goto basic_json_parser_10; + } + else + { + if (yych == 'f') + { + goto basic_json_parser_22; + } + if (yych <= 'm') + { + goto basic_json_parser_30; + } + goto basic_json_parser_20; + } + } + else + { + if (yych <= '{') + { + if (yych == 't') + { + goto basic_json_parser_21; + } + if (yych <= 'z') + { + goto basic_json_parser_30; + } + goto basic_json_parser_12; + } + else + { + if (yych <= '}') + { + if (yych <= '|') + { + goto basic_json_parser_30; + } + goto basic_json_parser_14; + } + else + { + if (yych == 0xEF) + { + goto basic_json_parser_6; + } + goto basic_json_parser_30; + } + } } } - } - } basic_json_parser_2: - ++m_cursor; - yych = *m_cursor; - goto basic_json_parser_5; + ++m_cursor; + yych = *m_cursor; + goto basic_json_parser_5; basic_json_parser_3: - { return scan(); } + { + return scan(); + } basic_json_parser_4: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; basic_json_parser_5: - if (yybm[0+yych] & 32) { - goto basic_json_parser_4; - } - goto basic_json_parser_3; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_4; + } + goto basic_json_parser_3; basic_json_parser_6: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) goto basic_json_parser_64; + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 0xBB) + { + goto basic_json_parser_64; + } basic_json_parser_7: - { return token_type::parse_error; } + { + return token_type::parse_error; + } basic_json_parser_8: - ++m_cursor; - { return token_type::begin_array; } + ++m_cursor; + { + return token_type::begin_array; + } basic_json_parser_10: - ++m_cursor; - { return token_type::end_array; } + ++m_cursor; + { + return token_type::end_array; + } basic_json_parser_12: - ++m_cursor; - { return token_type::begin_object; } + ++m_cursor; + { + return token_type::begin_object; + } basic_json_parser_14: - ++m_cursor; - { return token_type::end_object; } + ++m_cursor; + { + return token_type::end_object; + } basic_json_parser_16: - ++m_cursor; - { return token_type::value_separator; } + ++m_cursor; + { + return token_type::value_separator; + } basic_json_parser_18: - ++m_cursor; - { return token_type::name_separator; } + ++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; + 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; + 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; + 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; - if (yych <= '/') goto basic_json_parser_7; - if (yych <= '0') goto basic_json_parser_50; - if (yych <= '9') goto basic_json_parser_41; - goto basic_json_parser_7; + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_7; + } + if (yych <= '0') + { + goto basic_json_parser_50; + } + if (yych <= '9') + { + goto basic_json_parser_41; + } + goto basic_json_parser_7; basic_json_parser_24: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') { - if (yych == '.') goto basic_json_parser_43; - } else { - if (yych <= 'E') goto basic_json_parser_44; - if (yych == 'e') goto basic_json_parser_44; - } + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + } basic_json_parser_25: - { return token_type::value_number; } + { + return token_type::value_number; + } basic_json_parser_26: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - goto basic_json_parser_42; + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + 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; + 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; } + ++m_cursor; + { + return token_type::end_of_input; + } basic_json_parser_30: - yych = *++m_cursor; - goto basic_json_parser_7; + yych = *++m_cursor; + goto basic_json_parser_7; basic_json_parser_31: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; basic_json_parser_32: - if (yybm[0+yych] & 64) { - goto basic_json_parser_31; - } - if (yych <= 0x0F) goto basic_json_parser_33; - if (yych <= '"') goto basic_json_parser_35; - goto basic_json_parser_34; -basic_json_parser_33: - m_cursor = m_marker; - if (yyaccept == 0) { - goto basic_json_parser_7; - } else { - goto basic_json_parser_25; - } -basic_json_parser_34: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; - if (yych <= 'e') { - if (yych <= '/') { - if (yych == '"') goto basic_json_parser_31; - if (yych <= '.') goto basic_json_parser_33; - goto basic_json_parser_31; - } else { - if (yych <= '\\') { - if (yych <= '[') goto basic_json_parser_33; + if (yybm[0 + yych] & 64) + { goto basic_json_parser_31; - } else { - if (yych == 'b') goto basic_json_parser_31; + } + if (yych <= 0x0F) + { goto basic_json_parser_33; } - } - } else { - if (yych <= 'q') { - if (yych <= 'f') goto basic_json_parser_31; - if (yych == 'n') goto basic_json_parser_31; - goto basic_json_parser_33; - } else { - if (yych <= 's') { - if (yych <= 'r') goto basic_json_parser_31; - goto basic_json_parser_33; - } else { - if (yych <= 't') goto basic_json_parser_31; - if (yych <= 'u') goto basic_json_parser_37; - goto basic_json_parser_33; + if (yych <= '"') + { + goto basic_json_parser_35; + } + goto basic_json_parser_34; +basic_json_parser_33: + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_7; + } + else + { + goto basic_json_parser_25; + } +basic_json_parser_34: + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= 'e') + { + if (yych <= '/') + { + if (yych == '"') + { + goto basic_json_parser_31; + } + if (yych <= '.') + { + goto basic_json_parser_33; + } + goto basic_json_parser_31; + } + else + { + if (yych <= '\\') + { + if (yych <= '[') + { + goto basic_json_parser_33; + } + goto basic_json_parser_31; + } + else + { + if (yych == 'b') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + } + } + else + { + if (yych <= 'q') + { + if (yych <= 'f') + { + goto basic_json_parser_31; + } + if (yych == 'n') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + else + { + if (yych <= 's') + { + if (yych <= 'r') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + else + { + if (yych <= 't') + { + goto basic_json_parser_31; + } + if (yych <= 'u') + { + goto basic_json_parser_37; + } + goto basic_json_parser_33; + } + } } - } - } basic_json_parser_35: - ++m_cursor; - { return token_type::value_string; } + ++m_cursor; + { + return token_type::value_string; + } basic_json_parser_37: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_33; - if (yych >= ':') 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; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych >= ':') + { + 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; + } + } basic_json_parser_38: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_33; - if (yych >= ':') goto basic_json_parser_33; - } else { - if (yych <= 'F') goto basic_json_parser_39; - if (yych <= '`') goto basic_json_parser_33; - if (yych >= 'g') goto basic_json_parser_33; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych >= ':') + { + goto basic_json_parser_33; + } + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_39; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych >= 'g') + { + goto basic_json_parser_33; + } + } basic_json_parser_39: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_33; - if (yych >= ':') goto basic_json_parser_33; - } else { - if (yych <= 'F') goto basic_json_parser_40; - if (yych <= '`') goto basic_json_parser_33; - if (yych >= 'g') goto basic_json_parser_33; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych >= ':') + { + goto basic_json_parser_33; + } + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_40; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych >= 'g') + { + goto basic_json_parser_33; + } + } basic_json_parser_40: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_33; - if (yych <= '9') goto basic_json_parser_31; - goto basic_json_parser_33; - } else { - if (yych <= 'F') goto basic_json_parser_31; - if (yych <= '`') goto basic_json_parser_33; - if (yych <= 'f') goto basic_json_parser_31; - goto basic_json_parser_33; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_31; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } basic_json_parser_41: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; basic_json_parser_42: - if (yybm[0+yych] & 128) { - goto basic_json_parser_41; - } - if (yych <= 'D') { - if (yych != '.') goto basic_json_parser_25; - } else { - if (yych <= 'E') goto basic_json_parser_44; - if (yych == 'e') goto basic_json_parser_44; - goto basic_json_parser_25; - } + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_41; + } + if (yych <= 'D') + { + if (yych != '.') + { + goto basic_json_parser_25; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_25; + } basic_json_parser_43: - yych = *++m_cursor; - if (yych <= '/') goto basic_json_parser_33; - if (yych <= '9') goto basic_json_parser_48; - goto basic_json_parser_33; + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_33; + } + 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; - } else { - if (yych <= '-') goto basic_json_parser_45; - if (yych <= '/') goto basic_json_parser_33; - if (yych <= '9') goto basic_json_parser_46; - goto basic_json_parser_33; - } + yych = *++m_cursor; + if (yych <= ',') + { + if (yych != '+') + { + goto basic_json_parser_33; + } + } + else + { + if (yych <= '-') + { + goto basic_json_parser_45; + } + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_46; + } + goto basic_json_parser_33; + } basic_json_parser_45: - yych = *++m_cursor; - if (yych <= '/') goto basic_json_parser_33; - if (yych >= ':') goto basic_json_parser_33; + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych >= ':') + { + goto basic_json_parser_33; + } basic_json_parser_46: - ++m_cursor; - if (m_limit <= m_cursor) yyfill(); // LCOV_EXCL_LINE; - yych = *m_cursor; - if (yych <= '/') goto basic_json_parser_25; - if (yych <= '9') goto basic_json_parser_46; - goto basic_json_parser_25; + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_25; + } + if (yych <= '9') + { + goto basic_json_parser_46; + } + 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; - yych = *m_cursor; - if (yych <= 'D') { - if (yych <= '/') goto basic_json_parser_25; - if (yych <= '9') goto basic_json_parser_48; - goto basic_json_parser_25; - } else { - if (yych <= 'E') goto basic_json_parser_44; - if (yych == 'e') goto basic_json_parser_44; - goto basic_json_parser_25; - } + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') + { + goto basic_json_parser_25; + } + if (yych <= '9') + { + goto basic_json_parser_48; + } + goto basic_json_parser_25; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_25; + } basic_json_parser_50: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') { - if (yych == '.') goto basic_json_parser_43; - goto basic_json_parser_25; - } else { - if (yych <= 'E') goto basic_json_parser_44; - if (yych == 'e') goto basic_json_parser_44; - goto basic_json_parser_25; - } + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + goto basic_json_parser_25; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_25; + } basic_json_parser_51: - yych = *++m_cursor; - if (yych != 'l') goto basic_json_parser_33; - yych = *++m_cursor; - if (yych != 's') goto basic_json_parser_33; - yych = *++m_cursor; - if (yych != 'e') goto basic_json_parser_33; - ++m_cursor; - { return token_type::literal_false; } + yych = *++m_cursor; + if (yych != 'l') + { + goto basic_json_parser_33; + } + yych = *++m_cursor; + if (yych != 's') + { + goto basic_json_parser_33; + } + yych = *++m_cursor; + if (yych != 'e') + { + goto basic_json_parser_33; + } + ++m_cursor; + { + return token_type::literal_false; + } basic_json_parser_56: - yych = *++m_cursor; - if (yych != 'u') goto basic_json_parser_33; - yych = *++m_cursor; - if (yych != 'e') goto basic_json_parser_33; - ++m_cursor; - { return token_type::literal_true; } + yych = *++m_cursor; + if (yych != 'u') + { + goto basic_json_parser_33; + } + yych = *++m_cursor; + if (yych != 'e') + { + goto basic_json_parser_33; + } + ++m_cursor; + { + return token_type::literal_true; + } basic_json_parser_60: - yych = *++m_cursor; - if (yych != 'l') goto basic_json_parser_33; - yych = *++m_cursor; - if (yych != 'l') goto basic_json_parser_33; - ++m_cursor; - { return token_type::literal_null; } + yych = *++m_cursor; + if (yych != 'l') + { + goto basic_json_parser_33; + } + yych = *++m_cursor; + if (yych != 'l') + { + goto basic_json_parser_33; + } + ++m_cursor; + { + return token_type::literal_null; + } basic_json_parser_64: - yych = *++m_cursor; - if (yych != 0xBF) goto basic_json_parser_33; - ++m_cursor; - { return scan(); } - } + yych = *++m_cursor; + if (yych != 0xBF) + { + goto basic_json_parser_33; + } + ++m_cursor; + { + return scan(); + } + } } @@ -7708,17 +8114,17 @@ basic_json_parser_64: /*! @brief static_cast between two types and indicate if it results in error - This function performs a static_cast between @a source and @a dest. It + This function performs a static_cast between @a source and @a dest. It then checks if a static_cast back to @a dest produces an error. - @param[in] source the value to cast from + @param[in] source the value to cast from @param[out] dest the value to cast to @return @a true if the cast was performed without error, @a false otherwise */ template - bool attempt_cast(T_A source, T_B & dest) const + bool attempt_cast(T_A source, T_B& dest) const { dest = static_cast(source); return (source == static_cast(dest)); @@ -7727,74 +8133,101 @@ basic_json_parser_64: /*! @brief return number value for number tokens - This function translates the last token into the most appropriate - number type (either integer, unsigned integer or floating point), - which is passed back to the caller via the result parameter. The pointer - @a m_start points to the beginning of the parsed number. We first examine + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), which + is passed back to the caller via the result parameter. The pointer @a + 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 + 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. + 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. + 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. - @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. + 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 { typename string_t::value_type* endptr; assert(m_start != nullptr); errno = 0; - - // Attempt to parse it as an integer - first checking for a negative number + + // attempt to parse it as an integer - first checking for a + // negative number if (*reinterpret_cast(m_start) != '-') { - // Positive, parse with strtoull and attempt cast to number_unsigned_t - if (attempt_cast(std::strtoull(reinterpret_cast(m_start), &endptr, 10), result.m_value.number_unsigned)) + // positive, parse with strtoull and attempt cast to + // number_unsigned_t + if (attempt_cast(std::strtoull(reinterpret_cast(m_start), &endptr, + 10), result.m_value.number_unsigned)) + { result.m_type = value_t::number_unsigned; - else result.m_type = value_t::number_float; // Cast failed due to overflow - store as float + } + else + { + // cast failed due to overflow - store as float + result.m_type = value_t::number_float; + } } else { - // Negative, parse with strtoll and attempt cast to number_integer_t - if (attempt_cast(std::strtoll(reinterpret_cast(m_start), &endptr, 10), result.m_value.number_unsigned)) + // Negative, parse with strtoll and attempt cast to + // number_integer_t + if (attempt_cast(std::strtoll(reinterpret_cast(m_start), &endptr, + 10), result.m_value.number_unsigned)) + { result.m_type = value_t::number_integer; - else result.m_type = value_t::number_float; // Cast failed due to overflow - store as float + } + else + { + // cast failed due to overflow - store as float + result.m_type = value_t::number_float; + } } - // Check the end of the number was reached and no range error occurred - if (reinterpret_cast(endptr) != m_cursor || errno == ERANGE) result.m_type = value_t::number_float; + // check the end of the number was reached and no range error + // occurred + if (reinterpret_cast(endptr) != m_cursor || errno == ERANGE) + { + result.m_type = value_t::number_float; + } if (result.m_type == value_t::number_float) { - // Either the number won't fit in an integer (range error from strtoull/strtoll or overflow on cast) or there was - // something else after the number, which could be an exponent - - // Parse with strtod + // either the number won't fit in an integer (range error from + // strtoull/strtoll or overflow on cast) or there was something + // else after the number, which could be an exponent + + // parse with strtod result.m_value.number_float = str_to_float_t(static_cast(nullptr), &endptr); - // Anything after the number is an error - if(reinterpret_cast(endptr) != m_cursor) + // anything after the number is an error + if (reinterpret_cast(endptr) != m_cursor) + { throw std::invalid_argument(std::string("parse error - ") + get_token() + " is not a number"); + } } } @@ -8166,5 +8599,3 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t) #endif #endif - - diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index d76f8ca2..52bdc161 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -124,8 +124,8 @@ default; will be used in @ref string_t) in @ref boolean_t) @tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c `uint64_t` by -default; will be used in @ref number_unsigned_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) @tparam NumberFloatType type for JSON floating-point numbers (@c `double` by default; will be used in @ref number_float_t) @tparam AllocatorType type of the allocator to use (@c `std::allocator` by @@ -485,9 +485,9 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is a + C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, - three different types, @ref number_integer_t, @ref number_unsigned_t and + three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template @@ -556,9 +556,9 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is a + C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, - three different types, @ref number_integer_t, @ref number_unsigned_t and + three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store unsigned integer numbers in C++, a type is defined by the template @@ -588,11 +588,11 @@ class basic_json > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer number - that can be stored is `0`. Integer numbers - that are out of range will yield over/underflow when used in a constructor. - During deserialization, too large or small integer numbers will be - automatically be stored as @ref number_integer_t or @ref number_float_t. + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. [RFC 7159](http://rfc7159.net/rfc7159) further states: > Note that when such software is used, numbers that are integers and are @@ -600,7 +600,7 @@ class basic_json > that implementations will agree exactly on their numeric values. As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], this + number_integer_t type) of the exactly supported range [0, UINT64_MAX], this class's integer type is interoperable. #### Storage @@ -614,7 +614,7 @@ class basic_json @since version 2.0.0 */ using number_unsigned_t = NumberUnsignedType; - + /*! @brief a type for a number (floating-point) @@ -628,9 +628,9 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is a + C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, - three different types, @ref number_integer_t, @ref number_unsigned_t and + three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template @@ -801,7 +801,7 @@ class basic_json number_integer = number_integer_t(0); break; } - + case value_t::number_unsigned: { number_unsigned = number_unsigned_t(0); @@ -1330,8 +1330,8 @@ class basic_json template::value and - std::numeric_limits::is_integer and - std::numeric_limits::is_signed, + std::numeric_limits::is_integer and + std::numeric_limits::is_signed, CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept @@ -1344,7 +1344,7 @@ class basic_json Create an unsigned integer number JSON value with a given content. - @tparam T helper type to compare number_unsigned_t and unsigned int + @tparam T helper type to compare number_unsigned_t and unsigned int (not visible in) the interface. @param[in] val an integer to create a JSON number from @@ -1365,14 +1365,14 @@ class basic_json basic_json(const number_unsigned_t val) : m_type(value_t::number_unsigned), m_value(val) {} - + /*! @brief create an unsigned number (implicit) Create an unsigned number JSON value with a given content. This constructor allows any type that can be used to construct values of type @ref - number_unsigned_t. Examples may include the types `unsigned int`, `uint32_t`, - or `unsigned short`. + number_unsigned_t. Examples may include the types `unsigned int`, + `uint32_t`, or `unsigned short`. @tparam CompatibleNumberUnsignedType an integer type which is compatible to @ref number_unsigned_t. @@ -1386,13 +1386,13 @@ class basic_json @since version 2.0.0 */ - template::value and - std::numeric_limits::is_integer and - !std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template < typename CompatibleNumberUnsignedType, typename + std::enable_if < + std::is_constructible::value and + std::numeric_limits::is_integer and + !std::numeric_limits::is_signed, + CompatibleNumberUnsignedType >::type + = 0 > basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1781,7 +1781,7 @@ class basic_json m_value.number_integer = first.m_object->m_value.number_integer; break; } - + case value_t::number_unsigned: { assert(first.m_object != nullptr); @@ -1891,7 +1891,7 @@ class basic_json m_value = other.m_value.number_integer; break; } - + case value_t::number_unsigned: { m_value = other.m_value.number_unsigned; @@ -2183,9 +2183,10 @@ class basic_json @liveexample{The following code exemplifies @ref is_number for all JSON types.,is_number} - @sa @ref is_number_integer() -- check if value is an integer or unsigned + @sa @ref is_number_integer() -- check if value is an integer or unsigned integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 @@ -2198,10 +2199,10 @@ class basic_json /*! @brief return whether value is an integer number - This function returns true iff the JSON value is an integer or unsigned + This function returns true iff the JSON value is an integer or unsigned integer number. This excludes floating-point values. - @return `true` if type is an integer or unsigned integer number, `false` + @return `true` if type is an integer or unsigned integer number, `false` otherwise. @complexity Constant. @@ -2210,7 +2211,8 @@ class basic_json JSON types.,is_number_integer} @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 @@ -2219,19 +2221,19 @@ class basic_json { return m_type == value_t::number_integer or m_type == value_t::number_unsigned; } - + /*! @brief return whether value is an unsigned integer number - This function returns true iff the JSON value is an unsigned integer number. - This excludes floating-point and (signed) integer values. + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. @return `true` if type is an unsigned integer number, `false` otherwise. @complexity Constant. @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned + @sa @ref is_number_integer() -- check if value is an integer or unsigned integer number @sa @ref is_number_float() -- check if value is a floating-point number @@ -2257,7 +2259,8 @@ class basic_json @sa @ref is_number() -- check if value is number @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @since version 1.0.0 */ @@ -2525,7 +2528,7 @@ class basic_json { return static_cast(m_value.number_integer); } - + case value_t::number_unsigned: { return static_cast(m_value.number_unsigned); @@ -2615,7 +2618,7 @@ class basic_json { return is_number_integer() ? &m_value.number_integer : nullptr; } - + /// get a pointer to the value (unsigned number) number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept { @@ -2627,7 +2630,7 @@ class basic_json { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } - + /// get a pointer to the value (floating-point number) number_float_t* get_impl_ptr(number_float_t*) noexcept { @@ -2725,7 +2728,7 @@ class basic_json @warning The pointer becomes invalid if the underlying JSON object changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested @@ -2776,7 +2779,7 @@ class basic_json state. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested @@ -2895,14 +2898,14 @@ class basic_json @since version 1.0.0 */ - template::value - and not std::is_same::value + template < typename ValueType, typename + std::enable_if < + not std::is_pointer::value + and not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 - and not std::is_same>::value + and not std::is_same>::value #endif - , int>::type = 0> + , int >::type = 0 > operator ValueType() const { // delegate the call to get<>() const @@ -7390,17 +7393,17 @@ class basic_json /*! @brief static_cast between two types and indicate if it results in error - This function performs a static_cast between @a source and @a dest. It + This function performs a static_cast between @a source and @a dest. It then checks if a static_cast back to @a dest produces an error. - @param[in] source the value to cast from + @param[in] source the value to cast from @param[out] dest the value to cast to @return @a true if the cast was performed without error, @a false otherwise */ template - bool attempt_cast(T_A source, T_B & dest) const + bool attempt_cast(T_A source, T_B& dest) const { dest = static_cast(source); return (source == static_cast(dest)); @@ -7409,74 +7412,101 @@ class basic_json /*! @brief return number value for number tokens - This function translates the last token into the most appropriate - number type (either integer, unsigned integer or floating point), - which is passed back to the caller via the result parameter. The pointer - @a m_start points to the beginning of the parsed number. We first examine + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), which + is passed back to the caller via the result parameter. The pointer @a + 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 + 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. + 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. + 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. - @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. + 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 { typename string_t::value_type* endptr; assert(m_start != nullptr); errno = 0; - - // Attempt to parse it as an integer - first checking for a negative number + + // attempt to parse it as an integer - first checking for a + // negative number if (*reinterpret_cast(m_start) != '-') { - // Positive, parse with strtoull and attempt cast to number_unsigned_t - if (attempt_cast(std::strtoull(reinterpret_cast(m_start), &endptr, 10), result.m_value.number_unsigned)) + // positive, parse with strtoull and attempt cast to + // number_unsigned_t + if (attempt_cast(std::strtoull(reinterpret_cast(m_start), &endptr, + 10), result.m_value.number_unsigned)) + { result.m_type = value_t::number_unsigned; - else result.m_type = value_t::number_float; // Cast failed due to overflow - store as float + } + else + { + // cast failed due to overflow - store as float + result.m_type = value_t::number_float; + } } else { - // Negative, parse with strtoll and attempt cast to number_integer_t - if (attempt_cast(std::strtoll(reinterpret_cast(m_start), &endptr, 10), result.m_value.number_unsigned)) + // Negative, parse with strtoll and attempt cast to + // number_integer_t + if (attempt_cast(std::strtoll(reinterpret_cast(m_start), &endptr, + 10), result.m_value.number_unsigned)) + { result.m_type = value_t::number_integer; - else result.m_type = value_t::number_float; // Cast failed due to overflow - store as float + } + else + { + // cast failed due to overflow - store as float + result.m_type = value_t::number_float; + } } - // Check the end of the number was reached and no range error occurred - if (reinterpret_cast(endptr) != m_cursor || errno == ERANGE) result.m_type = value_t::number_float; + // check the end of the number was reached and no range error + // occurred + if (reinterpret_cast(endptr) != m_cursor || errno == ERANGE) + { + result.m_type = value_t::number_float; + } if (result.m_type == value_t::number_float) { - // Either the number won't fit in an integer (range error from strtoull/strtoll or overflow on cast) or there was - // something else after the number, which could be an exponent - - // Parse with strtod + // either the number won't fit in an integer (range error from + // strtoull/strtoll or overflow on cast) or there was something + // else after the number, which could be an exponent + + // parse with strtod result.m_value.number_float = str_to_float_t(static_cast(nullptr), &endptr); - // Anything after the number is an error - if(reinterpret_cast(endptr) != m_cursor) + // anything after the number is an error + if (reinterpret_cast(endptr) != m_cursor) + { throw std::invalid_argument(std::string("parse error - ") + get_token() + " is not a number"); + } } } @@ -7848,5 +7878,3 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t) #endif #endif - - diff --git a/test/unit.cpp b/test/unit.cpp index 519f760b..507c55cc 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -924,7 +924,8 @@ TEST_CASE("constructors") SECTION("object with error") { - CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), std::logic_error); + CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), + std::logic_error); CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), "cannot create object from initializer list"); } @@ -2312,7 +2313,7 @@ TEST_CASE("value conversion") json::number_integer_t n = j.get(); CHECK(json(n) == j); } - + SECTION("number_unsigned_t") { json::number_unsigned_t n = j_unsigned.get(); @@ -3665,7 +3666,7 @@ TEST_CASE("element access") CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); } - + SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); @@ -3675,7 +3676,7 @@ TEST_CASE("element access") CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); } - + SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); @@ -5435,7 +5436,7 @@ TEST_CASE("iterators") SECTION("object") { - json j = {{"A", 1},{"B", 2},{"C", 3}}; + json j = {{"A", 1}, {"B", 2}, {"C", 3}}; json j_const(j); SECTION("json + begin/end") @@ -9661,7 +9662,7 @@ TEST_CASE("parser class") // (2**53)-1 CHECK(json::parser("9007199254740991").parse().get() == 9007199254740991); } - + SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) { // While RFC7159, Section 6 specifies a preference for support @@ -9672,7 +9673,7 @@ TEST_CASE("parser class") // i.e. -(2**63) -> (2**64)-1. // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) - CHECK(json::parser("-9223372036854775808").parse().get() == -9223372036854775807-1); + CHECK(json::parser("-9223372036854775808").parse().get() == -9223372036854775807 - 1); // (2**63)-1 CHECK(json::parser("9223372036854775807").parse().get() == 9223372036854775807); // (2**64)-1 @@ -9720,12 +9721,14 @@ TEST_CASE("parser class") CHECK_THROWS_WITH(json::parser("01").parse(), "parse error - 0 is not a number"); CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("1.").parse(), "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1.").parse(), + "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E-").parse(), "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1.E1").parse(), "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1.E1").parse(), + "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("-1E").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0E#").parse(), @@ -9767,7 +9770,8 @@ TEST_CASE("parser class") CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("0.").parse(), "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("0.").parse(), + "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("--").parse(), "parse error - unexpected '-'"); @@ -12076,14 +12080,15 @@ TEST_CASE("regression tests") SECTION("issue #89 - nonstandard integer type") { // create JSON class with nonstandard integer number type - using custom_json = nlohmann::basic_json; + using custom_json = + nlohmann::basic_json; custom_json j; j["int_1"] = 1; // we need to cast to int to compile with Catch - the value is int32_t CHECK(static_cast(j["int_1"]) == 1); // tests for correct handling of non-standard integers that overflow the type selected by the user - + // unsigned integer object creation - expected to wrap and still be stored as an integer j = 4294967296U; // 2^32 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_unsigned)); @@ -12201,11 +12206,11 @@ TEST_CASE("regression tests") { CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); } - + SECTION("issue #171 - Cannot index by key of type static constexpr const char*") { json j; - + // Non-const access with key as "char []" char array_key[] = "Key1"; CHECK_NOTHROW(j[array_key] = 1); @@ -12281,15 +12286,18 @@ TEST_CASE("regression tests") // create JSON class with nonstandard float number type // float - nlohmann::basic_json j_float = 1.23e25f; + nlohmann::basic_json j_float = + 1.23e25f; CHECK(j_float.get() == 1.23e25f); // double - nlohmann::basic_json j_double = 1.23e35f; + nlohmann::basic_json j_double = + 1.23e35f; CHECK(j_double.get() == 1.23e35f); // long double - nlohmann::basic_json j_long_double = 1.23e45L; + nlohmann::basic_json + j_long_double = 1.23e45L; CHECK(j_long_double.get() == 1.23e45L); } }