diff --git a/doc/doxygen_sqlite3.db b/doc/doxygen_sqlite3.db new file mode 100644 index 000000000..b0b9b32d6 Binary files /dev/null and b/doc/doxygen_sqlite3.db differ diff --git a/src/json.hpp b/src/json.hpp index a91edba4f..3563e9ce3 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -121,6 +121,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 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 @@ -182,6 +184,7 @@ template < class StringType = std::string, class BooleanType = bool, class NumberIntegerType = int64_t, + class NumberUnsignedType = uint64_t, class NumberFloatType = double, template class AllocatorType = std::allocator > @@ -194,6 +197,7 @@ class basic_json StringType, BooleanType, NumberIntegerType, + NumberUnsignedType, NumberFloatType, AllocatorType>; @@ -472,9 +476,10 @@ 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 an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + 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 + @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template parameter @a NumberIntegerType which chooses the type to use. @@ -507,7 +512,7 @@ class basic_json that can be stored is `-9223372036854775808` (INT64_MIN). 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_float_t. + automatically be stored as @ref number_unsigned_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 @@ -523,10 +528,84 @@ class basic_json @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_integer_t = NumberIntegerType; + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most programming + > languages. A number is represented in base 10 using decimal digits. It + > contains an integer component that may be prefixed with an optional minus + > sign, which may be followed by a fraction part and/or an exponent part. + > Leading zeros are not allowed. (...) Numeric values that cannot be + > represented in the grammar below (such as Infinity and NaN) are not + > 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 + signed integer, an unsigned integer or a floating-point number. Therefore, + 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 + parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the default + value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > 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. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > 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 + class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_integer_t -- type for number values (integer) + + @since version 1.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + /*! @brief a type for a number (floating-point) @@ -540,9 +619,10 @@ 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 an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + 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 + @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template parameter @a NumberFloatType which chooses the type to use. @@ -588,6 +668,8 @@ class basic_json @sa @ref number_integer_t -- type for number values (integer) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_float_t = NumberFloatType; @@ -618,7 +700,8 @@ class basic_json boolean, ///< boolean value number_integer, ///< number value (integer) number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + discarded, ///< discarded by the the parser callback function + number_unsigned ///< number value (unsigned integer) }; @@ -660,6 +743,8 @@ class basic_json boolean_t boolean; /// number (integer) number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; /// number (floating-point) number_float_t number_float; @@ -669,6 +754,8 @@ class basic_json json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} /// constructor for numbers (floating-point) json_value(number_float_t v) noexcept : number_float(v) {} /// constructor for empty values of a given type @@ -705,6 +792,12 @@ class basic_json number_integer = number_integer_t(0); break; } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } case value_t::number_float: { @@ -861,6 +954,8 @@ class basic_json (floating-point) value @sa @ref basic_json(const number_integer_t) -- create a number (integer) value + @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned) + value @since version 1.0.0 */ @@ -1225,13 +1320,71 @@ class basic_json template::value and - std::numeric_limits::is_integer, CompatibleNumberIntegerType>::type - = 0> + std::numeric_limits::is_integer and + std::numeric_limits::is_signed, + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast(val)) {} + /*! + @brief create an unsigned integer number (explicit) + + Create an unsigned integer number JSON value with a given content. + + @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 + + @complexity Constant. + + @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number + value (unsigned integer) from a compatible number type + + @since version 1.0.0 + */ + template::value) + and std::is_same::value + , int>::type = 0> + 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`. + + @tparam CompatibleNumberUnsignedType an integer type which is compatible to + @ref number_unsigned_t. + + @param[in] val an unsigned integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const number_unsigned_t) -- create a number value + (unsigned) + + @since version 1.0.0 + */ + template::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)) + {} + /*! @brief create a floating-point number (explicit) @@ -1591,6 +1744,7 @@ class basic_json case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) @@ -1614,6 +1768,13 @@ 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); + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } case value_t::number_float: { @@ -1717,6 +1878,12 @@ class basic_json m_value = other.m_value.number_integer; break; } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } case value_t::number_float: { @@ -1992,18 +2159,20 @@ class basic_json /*! @brief return whether value is a number - This function returns true iff the JSON value is a number. This includes + This function returns true if the JSON value is a number. This includes both integer and floating-point values. - @return `true` if type is number (regardless whether integer or - floating-type), `false` otherwise. + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. @complexity Constant. @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 number + @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_float() -- check if value is a floating-point number @since version 1.0.0 @@ -2016,10 +2185,11 @@ class basic_json /*! @brief return whether value is an integer number - This function returns true iff the JSON value is an integer number. This - excludes floating-point values. + This function returns true if the JSON value is an integer or unsigned + integer number. This excludes floating-point values. - @return `true` if type is an integer number, `false` otherwise. + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. @complexity Constant. @@ -2027,20 +2197,43 @@ 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_float() -- check if value is a floating-point number @since version 1.0.0 */ bool is_number_integer() const noexcept { - return m_type == value_t::number_integer; + 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 if 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 + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; } /*! @brief return whether value is a floating-point number - This function returns true iff the JSON value is a floating-point number. - This excludes integer values. + This function returns true if the JSON value is a floating-point number. + This excludes integer and unsigned integer values. @return `true` if type is a floating-point number, `false` otherwise. @@ -2051,6 +2244,7 @@ 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 @since version 1.0.0 */ @@ -2318,6 +2512,11 @@ class basic_json { return static_cast(m_value.number_integer); } + + case value_t::number_unsigned: + { + return static_cast(m_value.number_unsigned); + } case value_t::number_float: { @@ -2403,7 +2602,19 @@ 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 + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + /// get a pointer to the value (unsigned number) + const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept + { + 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 { @@ -2472,8 +2683,8 @@ 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, or @ref - number_float_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 pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -2523,8 +2734,8 @@ 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, or @ref - number_float_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 pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -3286,6 +3497,7 @@ class basic_json case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not pos.m_it.primitive_iterator.is_begin()) @@ -3391,6 +3603,7 @@ class basic_json case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) @@ -4080,6 +4293,12 @@ class basic_json break; } + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + case value_t::number_float: { m_value.number_float = 0.0; @@ -4618,14 +4837,16 @@ class basic_json */ friend bool operator<(const value_t lhs, const value_t rhs) { - static constexpr std::array order = {{ + static constexpr std::array order = {{ 0, // null 3, // object 4, // array 5, // string 1, // boolean 2, // integer - 2 // float + 2, // float + 0, // filler for discarded (preserves existing value_t values) + 2 // unsigned } }; @@ -4701,6 +4922,10 @@ class basic_json { return lhs.m_value.number_integer == rhs.m_value.number_integer; } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } case value_t::number_float: { return approx(lhs.m_value.number_float, rhs.m_value.number_float); @@ -4721,6 +4946,25 @@ class basic_json return approx(lhs.m_value.number_float, static_cast(rhs.m_value.number_integer)); } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return approx(static_cast(lhs.m_value.number_unsigned), + rhs.m_value.number_float); + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return approx(lhs.m_value.number_float, + static_cast(rhs.m_value.number_unsigned)); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + return false; } @@ -4872,6 +5116,10 @@ class basic_json { return lhs.m_value.number_integer < rhs.m_value.number_integer; } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } case value_t::number_float: { return lhs.m_value.number_float < rhs.m_value.number_float; @@ -4892,6 +5140,24 @@ class basic_json return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < + rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < + static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } // We only reach this line if we cannot compare values. In that case, // we compare types. Note we have to call the operator explicitly, @@ -5455,6 +5721,12 @@ class basic_json return; } + case value_t::number_unsigned: + { + o << m_value.number_unsigned; + return; + } + case value_t::number_float: { // 15 digits of precision allows round-trip IEEE 754 @@ -6665,789 +6937,386 @@ 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; + + { + 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; } - 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 <= ',') - { - 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; - } - } - } - } -basic_json_parser_2: - ++m_cursor; - yych = *m_cursor; - goto basic_json_parser_5; -basic_json_parser_3: - { - return scan(); - } -basic_json_parser_4: - ++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; -basic_json_parser_6: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - 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; - } -basic_json_parser_10: - ++m_cursor; - { - return token_type::end_array; - } -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; - 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; - } - } -basic_json_parser_25: - { - return token_type::value_number; - } -basic_json_parser_26: - 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; -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; - } - 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; - } - 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; - } -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; - } - } -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; - } - } -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; - } - } -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; - } -basic_json_parser_41: - 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; - } -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; -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; - } -basic_json_parser_45: - 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; -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; - } -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; - } -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; - } -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; - } -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; - } -basic_json_parser_64: - yych = *++m_cursor; - if (yych != 0xBF) - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return scan(); } } + } 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; +basic_json_parser_3: + { return scan(); } +basic_json_parser_4: + ++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; +basic_json_parser_6: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 0xBB) 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; } +basic_json_parser_10: + ++m_cursor; + { return token_type::end_array; } +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; + 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; + } +basic_json_parser_25: + { return token_type::value_number; } +basic_json_parser_26: + 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; +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; + 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; + 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; } +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; + } +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; + } +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; + } +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; + } +basic_json_parser_41: + 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; + } +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; +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; + } +basic_json_parser_45: + 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; +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; + } +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; + } +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; } +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; } +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; } +basic_json_parser_64: + yych = *++m_cursor; + if (yych != 0xBF) goto basic_json_parser_33; + ++m_cursor; + { return scan(); } + } } @@ -7627,17 +7496,40 @@ basic_json_parser_64: @throw std::range_error if passed value is out of range */ - long double get_number() const + void get_number(basic_json& result) const { - // conversion - typename string_t::value_type* endptr; + typename string_t::value_type* float_endptr, *endptr; assert(m_start != nullptr); - const auto float_val = std::strtold(reinterpret_cast(m_start), - &endptr); + + // Parse it as an integer + if(*reinterpret_cast(m_start) != '-') { + // Unsigned + result.m_value.number_unsigned = strtoull(reinterpret_cast(m_start),&endptr,10); + result.m_type = value_t::number_unsigned; + } + else { + // Signed + result.m_value.number_integer = strtoll(reinterpret_cast(m_start),&endptr,10); + result.m_type = value_t::number_integer; + } - // return float_val if the whole number was translated and NAN - // otherwise - return (reinterpret_cast(endptr) == m_cursor) ? float_val : NAN; + // Parse it as a double + const auto float_val = strtold(reinterpret_cast(m_start),&endptr); + long double int_part; + const auto frac_part = std::modf(float_val, &int_part); + + // Test if the double or integer is a better representation + if(!approx(frac_part, static_cast(0)) || + (result.m_type == value_t::number_unsigned && !approx(int_part, static_cast(result.m_value.number_unsigned))) || + (result.m_type == value_t::number_integer && !approx(int_part, static_cast(result.m_value.number_integer)))) { + result.m_value.number_float = float_val; + result.m_type = value_t::number_float; + } + + if(reinterpret_cast(endptr) != m_cursor) { + result.m_value.number_float = NAN; + result.m_type = value_t::number_float; + } } private: @@ -7867,11 +7759,12 @@ basic_json_parser_64: case lexer::token_type::value_number: { - auto float_val = m_lexer.get_number(); + m_lexer.get_number(result); // NAN is returned if token could not be translated // completely - if (std::isnan(float_val)) + if (result.m_type == value_t::number_float && + std::isnan(result.m_value.number_float)) { throw std::invalid_argument(std::string("parse error - ") + m_lexer.get_token() + " is not a number"); @@ -7879,20 +7772,6 @@ basic_json_parser_64: get_token(); - // check if conversion loses precision - const auto int_val = static_cast(float_val); - if (approx(float_val, static_cast(int_val))) - { - // we would not lose precision -> return int - result.m_type = value_t::number_integer; - result.m_value = int_val; - } - else - { - // we would lose precision -> return float - result.m_type = value_t::number_float; - result.m_value = static_cast(float_val); - } break; } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index a733f0979..8d342e485 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -121,6 +121,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 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 @@ -182,6 +184,7 @@ template < class StringType = std::string, class BooleanType = bool, class NumberIntegerType = int64_t, + class NumberUnsignedType = uint64_t, class NumberFloatType = double, template class AllocatorType = std::allocator > @@ -194,6 +197,7 @@ class basic_json StringType, BooleanType, NumberIntegerType, + NumberUnsignedType, NumberFloatType, AllocatorType>; @@ -472,9 +476,10 @@ 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 an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + 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 + @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template parameter @a NumberIntegerType which chooses the type to use. @@ -507,7 +512,7 @@ class basic_json that can be stored is `-9223372036854775808` (INT64_MIN). 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_float_t. + automatically be stored as @ref number_unsigned_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 @@ -523,10 +528,84 @@ class basic_json @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_integer_t = NumberIntegerType; + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most programming + > languages. A number is represented in base 10 using decimal digits. It + > contains an integer component that may be prefixed with an optional minus + > sign, which may be followed by a fraction part and/or an exponent part. + > Leading zeros are not allowed. (...) Numeric values that cannot be + > represented in the grammar below (such as Infinity and NaN) are not + > 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 + signed integer, an unsigned integer or a floating-point number. Therefore, + 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 + parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the default + value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > 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. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > 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 + class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_integer_t -- type for number values (integer) + + @since version 1.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + /*! @brief a type for a number (floating-point) @@ -540,9 +619,10 @@ 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 an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + 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 + @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template parameter @a NumberFloatType which chooses the type to use. @@ -588,6 +668,8 @@ class basic_json @sa @ref number_integer_t -- type for number values (integer) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_float_t = NumberFloatType; @@ -618,7 +700,8 @@ class basic_json boolean, ///< boolean value number_integer, ///< number value (integer) number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + discarded, ///< discarded by the the parser callback function + number_unsigned ///< number value (unsigned integer) }; @@ -660,6 +743,8 @@ class basic_json boolean_t boolean; /// number (integer) number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; /// number (floating-point) number_float_t number_float; @@ -669,6 +754,8 @@ class basic_json json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} /// constructor for numbers (floating-point) json_value(number_float_t v) noexcept : number_float(v) {} /// constructor for empty values of a given type @@ -705,6 +792,12 @@ class basic_json number_integer = number_integer_t(0); break; } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } case value_t::number_float: { @@ -861,6 +954,8 @@ class basic_json (floating-point) value @sa @ref basic_json(const number_integer_t) -- create a number (integer) value + @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned) + value @since version 1.0.0 */ @@ -1225,13 +1320,71 @@ class basic_json template::value and - std::numeric_limits::is_integer, CompatibleNumberIntegerType>::type - = 0> + std::numeric_limits::is_integer and + std::numeric_limits::is_signed, + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast(val)) {} + /*! + @brief create an unsigned integer number (explicit) + + Create an unsigned integer number JSON value with a given content. + + @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 + + @complexity Constant. + + @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number + value (unsigned integer) from a compatible number type + + @since version 1.0.0 + */ + template::value) + and std::is_same::value + , int>::type = 0> + 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`. + + @tparam CompatibleNumberUnsignedType an integer type which is compatible to + @ref number_unsigned_t. + + @param[in] val an unsigned integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const number_unsigned_t) -- create a number value + (unsigned) + + @since version 1.0.0 + */ + template::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)) + {} + /*! @brief create a floating-point number (explicit) @@ -1591,6 +1744,7 @@ class basic_json case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) @@ -1614,6 +1768,13 @@ 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); + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } case value_t::number_float: { @@ -1717,6 +1878,12 @@ class basic_json m_value = other.m_value.number_integer; break; } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } case value_t::number_float: { @@ -1992,18 +2159,20 @@ class basic_json /*! @brief return whether value is a number - This function returns true iff the JSON value is a number. This includes + This function returns true if the JSON value is a number. This includes both integer and floating-point values. - @return `true` if type is number (regardless whether integer or - floating-type), `false` otherwise. + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. @complexity Constant. @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 number + @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_float() -- check if value is a floating-point number @since version 1.0.0 @@ -2016,10 +2185,11 @@ class basic_json /*! @brief return whether value is an integer number - This function returns true iff the JSON value is an integer number. This - excludes floating-point values. + This function returns true if the JSON value is an integer or unsigned + integer number. This excludes floating-point values. - @return `true` if type is an integer number, `false` otherwise. + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. @complexity Constant. @@ -2027,20 +2197,43 @@ 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_float() -- check if value is a floating-point number @since version 1.0.0 */ bool is_number_integer() const noexcept { - return m_type == value_t::number_integer; + 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 if 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 + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; } /*! @brief return whether value is a floating-point number - This function returns true iff the JSON value is a floating-point number. - This excludes integer values. + This function returns true if the JSON value is a floating-point number. + This excludes integer and unsigned integer values. @return `true` if type is a floating-point number, `false` otherwise. @@ -2051,6 +2244,7 @@ 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 @since version 1.0.0 */ @@ -2318,6 +2512,11 @@ class basic_json { return static_cast(m_value.number_integer); } + + case value_t::number_unsigned: + { + return static_cast(m_value.number_unsigned); + } case value_t::number_float: { @@ -2403,7 +2602,19 @@ 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 + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + /// get a pointer to the value (unsigned number) + const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept + { + 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 { @@ -2472,8 +2683,8 @@ 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, or @ref - number_float_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 pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -2523,8 +2734,8 @@ 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, or @ref - number_float_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 pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -3286,6 +3497,7 @@ class basic_json case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not pos.m_it.primitive_iterator.is_begin()) @@ -3391,6 +3603,7 @@ class basic_json case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) @@ -4080,6 +4293,12 @@ class basic_json break; } + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + case value_t::number_float: { m_value.number_float = 0.0; @@ -4618,14 +4837,16 @@ class basic_json */ friend bool operator<(const value_t lhs, const value_t rhs) { - static constexpr std::array order = {{ + static constexpr std::array order = {{ 0, // null 3, // object 4, // array 5, // string 1, // boolean 2, // integer - 2 // float + 2, // float + 0, // filler for discarded (preserves existing value_t values) + 2 // unsigned } }; @@ -4701,6 +4922,10 @@ class basic_json { return lhs.m_value.number_integer == rhs.m_value.number_integer; } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } case value_t::number_float: { return approx(lhs.m_value.number_float, rhs.m_value.number_float); @@ -4721,6 +4946,25 @@ class basic_json return approx(lhs.m_value.number_float, static_cast(rhs.m_value.number_integer)); } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return approx(static_cast(lhs.m_value.number_unsigned), + rhs.m_value.number_float); + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return approx(lhs.m_value.number_float, + static_cast(rhs.m_value.number_unsigned)); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + return false; } @@ -4872,6 +5116,10 @@ class basic_json { return lhs.m_value.number_integer < rhs.m_value.number_integer; } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } case value_t::number_float: { return lhs.m_value.number_float < rhs.m_value.number_float; @@ -4892,6 +5140,24 @@ class basic_json return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < + rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < + static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } // We only reach this line if we cannot compare values. In that case, // we compare types. Note we have to call the operator explicitly, @@ -5455,6 +5721,12 @@ class basic_json return; } + case value_t::number_unsigned: + { + o << m_value.number_unsigned; + return; + } + case value_t::number_float: { // 15 digits of precision allows round-trip IEEE 754 @@ -6906,17 +7178,40 @@ class basic_json @throw std::range_error if passed value is out of range */ - long double get_number() const + void get_number(basic_json& result) const { - // conversion - typename string_t::value_type* endptr; + typename string_t::value_type* float_endptr, *endptr; assert(m_start != nullptr); - const auto float_val = std::strtold(reinterpret_cast(m_start), - &endptr); + + // Parse it as an integer + if(*reinterpret_cast(m_start) != '-') { + // Unsigned + result.m_value.number_unsigned = strtoull(reinterpret_cast(m_start),&endptr,10); + result.m_type = value_t::number_unsigned; + } + else { + // Signed + result.m_value.number_integer = strtoll(reinterpret_cast(m_start),&endptr,10); + result.m_type = value_t::number_integer; + } - // return float_val if the whole number was translated and NAN - // otherwise - return (reinterpret_cast(endptr) == m_cursor) ? float_val : NAN; + // Parse it as a double + const auto float_val = strtold(reinterpret_cast(m_start),&endptr); + long double int_part; + const auto frac_part = std::modf(float_val, &int_part); + + // Test if the double or integer is a better representation + if(!approx(frac_part, static_cast(0)) || + (result.m_type == value_t::number_unsigned && !approx(int_part, static_cast(result.m_value.number_unsigned))) || + (result.m_type == value_t::number_integer && !approx(int_part, static_cast(result.m_value.number_integer)))) { + result.m_value.number_float = float_val; + result.m_type = value_t::number_float; + } + + if(reinterpret_cast(endptr) != m_cursor) { + result.m_value.number_float = NAN; + result.m_type = value_t::number_float; + } } private: @@ -7146,11 +7441,12 @@ class basic_json case lexer::token_type::value_number: { - auto float_val = m_lexer.get_number(); + m_lexer.get_number(result); // NAN is returned if token could not be translated // completely - if (std::isnan(float_val)) + if (result.m_type == value_t::number_float && + std::isnan(result.m_value.number_float)) { throw std::invalid_argument(std::string("parse error - ") + m_lexer.get_token() + " is not a number"); @@ -7158,20 +7454,6 @@ class basic_json get_token(); - // check if conversion loses precision - const auto int_val = static_cast(float_val); - if (approx(float_val, static_cast(int_val))) - { - // we would not lose precision -> return int - result.m_type = value_t::number_integer; - result.m_value = int_val; - } - else - { - // we would lose precision -> return float - result.m_type = value_t::number_float; - result.m_value = static_cast(float_val); - } break; } diff --git a/test/unit.cpp b/test/unit.cpp index ebf3fb2a3..7ddb95dc8 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -78,6 +78,13 @@ TEST_CASE("constructors") CHECK(j.type() == t); } + SECTION("number_unsigned") + { + auto t = json::value_t::number_unsigned; + json j(t); + CHECK(j.type() == t); + } + SECTION("number_float") { auto t = json::value_t::number_float; @@ -115,7 +122,7 @@ TEST_CASE("constructors") SECTION("filled object") { - json::object_t o {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; + json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); } @@ -124,12 +131,12 @@ TEST_CASE("constructors") SECTION("create an object (implicit)") { // reference object - json::object_t o_reference {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; + json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j_reference(o_reference); SECTION("std::map") { - std::map o {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; + std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); @@ -137,7 +144,7 @@ TEST_CASE("constructors") SECTION("std::map") { - std::map o {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; + std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); @@ -145,7 +152,7 @@ TEST_CASE("constructors") SECTION("std::multimap") { - std::multimap o {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; + std::multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); @@ -153,7 +160,7 @@ TEST_CASE("constructors") SECTION("std::unordered_map") { - std::unordered_map o {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; + std::unordered_map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); @@ -161,7 +168,7 @@ TEST_CASE("constructors") SECTION("std::unordered_multimap") { - std::unordered_multimap o {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; + std::unordered_multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); @@ -169,7 +176,7 @@ TEST_CASE("constructors") SECTION("associative container literal") { - json j({{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}); + json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); } @@ -186,7 +193,7 @@ TEST_CASE("constructors") SECTION("filled array") { - json::array_t a {json(1), json(2.2), json(false), json("string"), json()}; + json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); } @@ -195,12 +202,12 @@ TEST_CASE("constructors") SECTION("create an array (implicit)") { // reference array - json::array_t a_reference {json(1), json(2.2), json(false), json("string"), json()}; + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j_reference(a_reference); SECTION("std::list") { - std::list a {json(1), json(2.2), json(false), json("string"), json()}; + std::list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); @@ -208,7 +215,7 @@ TEST_CASE("constructors") SECTION("std::forward_list") { - std::forward_list a {json(1), json(2.2), json(false), json("string"), json()}; + std::forward_list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); @@ -216,7 +223,7 @@ TEST_CASE("constructors") SECTION("std::array") { - std::array a {{json(1), json(2.2), json(false), json("string"), json()}}; + std::array a {{json(1), json(1u), json(2.2), json(false), json("string"), json()}}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); @@ -224,7 +231,7 @@ TEST_CASE("constructors") SECTION("std::vector") { - std::vector a {json(1), json(2.2), json(false), json("string"), json()}; + std::vector a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); @@ -232,7 +239,7 @@ TEST_CASE("constructors") SECTION("std::deque") { - std::deque a {json(1), json(2.2), json(false), json("string"), json()}; + std::deque a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); @@ -240,7 +247,7 @@ TEST_CASE("constructors") SECTION("std::set") { - std::set a {json(1), json(2.2), json(false), json("string"), json()}; + std::set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); // we cannot really check for equality here @@ -248,7 +255,7 @@ TEST_CASE("constructors") SECTION("std::unordered_set") { - std::unordered_set a {json(1), json(2.2), json(false), json("string"), json()}; + std::unordered_set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); // we cannot really check for equality here @@ -256,7 +263,7 @@ TEST_CASE("constructors") SECTION("sequence container literal") { - json j({json(1), json(2.2), json(false), json("string"), json()}); + json j({json(1), json(1u), json(2.2), json(false), json("string"), json()}); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); } @@ -358,9 +365,11 @@ TEST_CASE("constructors") SECTION("create an integer number (implicit)") { - // reference object + // reference objects json::number_integer_t n_reference = 42; json j_reference(n_reference); + json::number_unsigned_t n_unsigned_reference = 42; + json j_unsigned_reference(n_unsigned_reference); SECTION("short") { @@ -374,8 +383,8 @@ TEST_CASE("constructors") { unsigned short n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("int") @@ -390,8 +399,8 @@ TEST_CASE("constructors") { unsigned int n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("long") @@ -404,10 +413,10 @@ TEST_CASE("constructors") SECTION("unsigned long") { - short n = 42; + unsigned long n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("long long") @@ -422,8 +431,8 @@ TEST_CASE("constructors") { unsigned long long n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("int8_t") @@ -526,96 +535,96 @@ TEST_CASE("constructors") { uint8_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint16_t") { uint16_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint32_t") { uint32_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint64_t") { uint64_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_fast8_t") { uint_fast8_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_fast16_t") { uint_fast16_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_fast32_t") { uint_fast32_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_fast64_t") { uint_fast64_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_least8_t") { uint_least8_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_least16_t") { uint_least16_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_least32_t") { uint_least32_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("uint_least64_t") { uint_least64_t n = 42; json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("integer literal without suffix") @@ -628,8 +637,8 @@ TEST_CASE("constructors") SECTION("integer literal with u suffix") { json j(42u); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("integer literal with l suffix") @@ -642,8 +651,8 @@ TEST_CASE("constructors") SECTION("integer literal with ul suffix") { json j(42ul); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } SECTION("integer literal with ll suffix") @@ -656,8 +665,8 @@ TEST_CASE("constructors") SECTION("integer literal with ull suffix") { json j(42ull); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); } } @@ -830,6 +839,22 @@ TEST_CASE("constructors") } } + SECTION("number (unsigned)") + { + SECTION("explicit") + { + std::initializer_list l = {json(1u)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {1u}; + CHECK(j.type() == json::value_t::array); + } + } + SECTION("number (floating-point)") { SECTION("explicit") @@ -851,14 +876,14 @@ TEST_CASE("constructors") { SECTION("explicit") { - std::initializer_list l = {1, 42.23, true, nullptr, json::object_t(), json::array_t()}; + std::initializer_list l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { - json j {1, 42.23, true, nullptr, json::object_t(), json::array_t()}; + json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; CHECK(j.type() == json::value_t::array); } } @@ -867,13 +892,13 @@ TEST_CASE("constructors") { SECTION("object") { - json j { {"one", 1}, {"two", 2.2}, {"three", false} }; + json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }; CHECK(j.type() == json::value_t::object); } SECTION("array") { - json j { {"one", 1}, {"two", 2.2}, {"three", false}, 13 }; + json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 }; CHECK(j.type() == json::value_t::array); } } @@ -888,14 +913,14 @@ TEST_CASE("constructors") SECTION("object") { - json j = json::object({ {"one", 1}, {"two", 2.2}, {"three", false} }); + json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); CHECK(j.type() == json::value_t::object); } SECTION("object with error") { - CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 2.2}, {"three", false}, 13 }), std::logic_error); - CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 2.2}, {"three", false}, 13 }), + 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"); } @@ -907,7 +932,7 @@ TEST_CASE("constructors") SECTION("array") { - json j = json::array({ {"one", 1}, {"two", 2.2}, {"three", false} }); + json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); CHECK(j.type() == json::value_t::array); } } @@ -915,7 +940,7 @@ TEST_CASE("constructors") SECTION("create an array of n copies of a given value") { - json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2}}}; + json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}}; json arr(3, v); CHECK(arr.size() == 3); for (auto& x : arr) @@ -931,12 +956,12 @@ TEST_CASE("constructors") SECTION("json(begin(), end())") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.begin(), jobject.end()); CHECK(j_new == jobject); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.cbegin(), jobject.cend()); CHECK(j_new == jobject); } @@ -945,12 +970,12 @@ TEST_CASE("constructors") SECTION("json(begin(), begin())") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.begin(), jobject.begin()); CHECK(j_new == json::object()); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.cbegin(), jobject.cbegin()); CHECK(j_new == json::object()); } @@ -958,24 +983,24 @@ TEST_CASE("constructors") SECTION("construct from subrange") { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json j_new(jobject.find("b"), jobject.find("e")); - CHECK(j_new == json({{"b", 1}, {"c", 17}, {"d", false}})); + CHECK(j_new == json({{"b", 1}, {"c", 17u}, {"d", false}})); } SECTION("incompatible iterators") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error); CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error); CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible"); CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible"); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error); CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error); CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible"); @@ -1109,6 +1134,20 @@ TEST_CASE("constructors") } } + SECTION("number (unsigned)") + { + { + json j = 17u; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 17u; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + SECTION("number (floating point)") { { @@ -1180,6 +1219,24 @@ TEST_CASE("constructors") } } + SECTION("number (integer)") + { + { + json j = 17u; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + SECTION("number (floating point)") { { @@ -1248,6 +1305,13 @@ TEST_CASE("other constructors and destructor") CHECK(j == k); } + SECTION("number (unsigned)") + { + json j(42u); + json k(j); + CHECK(j == k); + } + SECTION("number (floating-point)") { json j(42.23); @@ -1258,7 +1322,7 @@ TEST_CASE("other constructors and destructor") SECTION("move constructor") { - json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42.23}, {"b", nullptr}}; + json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; CHECK(j.type() == json::value_t::object); json k(std::move(j)); CHECK(k.type() == json::value_t::object); @@ -1315,6 +1379,14 @@ TEST_CASE("other constructors and destructor") CHECK(j == k); } + SECTION("number (unsigned)") + { + json j(42u); + json k; + k = j; + CHECK(j == k); + } + SECTION("number (floating-point)") { json j(42.23); @@ -1334,7 +1406,7 @@ TEST_CASE("other constructors and destructor") SECTION("array") { - auto j = new json {"foo", 1, false, 23.42}; + auto j = new json {"foo", 1, 1u, false, 23.42}; delete j; } @@ -1357,6 +1429,7 @@ TEST_CASE("object inspection") CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(j.is_object()); CHECK(not j.is_array()); @@ -1368,11 +1441,12 @@ TEST_CASE("object inspection") SECTION("array") { - json j {"foo", 1, 42.23, false}; + json j {"foo", 1, 1u, 42.23, false}; CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(j.is_array()); @@ -1389,6 +1463,7 @@ TEST_CASE("object inspection") CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); @@ -1405,6 +1480,7 @@ TEST_CASE("object inspection") CHECK(j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); @@ -1421,6 +1497,7 @@ TEST_CASE("object inspection") CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); @@ -1437,6 +1514,24 @@ TEST_CASE("object inspection") CHECK(not j.is_boolean()); CHECK(j.is_number()); CHECK(j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("number (unsigned)") + { + json j(42u); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(j.is_number_integer()); + CHECK(j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); @@ -1453,6 +1548,7 @@ TEST_CASE("object inspection") CHECK(not j.is_boolean()); CHECK(j.is_number()); CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); CHECK(j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); @@ -1469,6 +1565,7 @@ TEST_CASE("object inspection") CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); @@ -1567,6 +1664,12 @@ TEST_CASE("object inspection") CHECK(j.type() == json::value_t::number_integer); } + SECTION("number (unsigned)") + { + json j = 23u; + CHECK(j.type() == json::value_t::number_unsigned); + } + SECTION("number (floating-point)") { json j = 42.23; @@ -1618,6 +1721,13 @@ TEST_CASE("object inspection") CHECK(t == j.type()); } + SECTION("number (unsigned)") + { + json j = 23u; + json::value_t t = j; + CHECK(t == j.type()); + } + SECTION("number (floating-point)") { json j = 42.23; @@ -1672,6 +1782,7 @@ TEST_CASE("value conversion") CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), @@ -1684,6 +1795,8 @@ TEST_CASE("value conversion") "type must be object, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be object, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be object, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be object, but is number"); } @@ -1727,7 +1840,7 @@ TEST_CASE("value conversion") SECTION("get an array (explicit)") { - json::array_t a_reference {json(1), json(2.2), json(false), json("string"), json()}; + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a_reference); SECTION("json::array_t") @@ -1767,6 +1880,7 @@ TEST_CASE("value conversion") CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), @@ -1779,6 +1893,8 @@ TEST_CASE("value conversion") "type must be array, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be array, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be array, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be array, but is number"); } @@ -1786,7 +1902,7 @@ TEST_CASE("value conversion") SECTION("get an array (implicit)") { - json::array_t a_reference {json(1), json(2.2), json(false), json("string"), json()}; + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a_reference); SECTION("json::array_t") @@ -1844,6 +1960,7 @@ TEST_CASE("value conversion") CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), @@ -1856,6 +1973,8 @@ TEST_CASE("value conversion") "type must be string, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be string, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be string, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be string, but is number"); } @@ -1903,6 +2022,7 @@ TEST_CASE("value conversion") CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), @@ -1915,6 +2035,8 @@ TEST_CASE("value conversion") "type must be boolean, but is string"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be boolean, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be boolean, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be boolean, but is number"); } @@ -1942,6 +2064,8 @@ TEST_CASE("value conversion") { json::number_integer_t n_reference {42}; json j(n_reference); + json::number_unsigned_t n_unsigned_reference {42u}; + json j_unsigned(n_unsigned_reference); SECTION("number_integer_t") { @@ -1949,6 +2073,12 @@ TEST_CASE("value conversion") CHECK(json(n) == j); } + SECTION("number_unsigned_t") + { + json::number_unsigned_t n = j_unsigned.get(); + CHECK(json(n) == j_unsigned); + } + SECTION("short") { short n = j.get(); @@ -2161,6 +2291,7 @@ TEST_CASE("value conversion") "type must be number, but is boolean"); CHECK_NOTHROW(json(json::value_t::number_float).get()); + CHECK_NOTHROW(json(json::value_t::number_float).get()); } } @@ -2168,12 +2299,20 @@ TEST_CASE("value conversion") { json::number_integer_t n_reference {42}; json j(n_reference); + json::number_unsigned_t n_unsigned_reference {42u}; + json j_unsigned(n_unsigned_reference); SECTION("number_integer_t") { json::number_integer_t n = j.get(); CHECK(json(n) == j); } + + SECTION("number_unsigned_t") + { + json::number_unsigned_t n = j_unsigned.get(); + CHECK(json(n) == j_unsigned); + } SECTION("short") { @@ -2183,8 +2322,8 @@ TEST_CASE("value conversion") SECTION("unsigned short") { - unsigned short n = j; - CHECK(json(n) == j); + unsigned short n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("int") @@ -2195,8 +2334,8 @@ TEST_CASE("value conversion") SECTION("unsigned int") { - unsigned int n = j; - CHECK(json(n) == j); + unsigned int n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("long") @@ -2207,8 +2346,8 @@ TEST_CASE("value conversion") SECTION("unsigned long") { - unsigned long n = j; - CHECK(json(n) == j); + unsigned long n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("long long") @@ -2219,8 +2358,8 @@ TEST_CASE("value conversion") SECTION("unsigned long long") { - unsigned long long n = j; - CHECK(json(n) == j); + unsigned long long n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("int8_t") @@ -2297,74 +2436,74 @@ TEST_CASE("value conversion") SECTION("uint8_t") { - uint8_t n = j; - CHECK(json(n) == j); + uint8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint16_t") { - uint16_t n = j; - CHECK(json(n) == j); + uint16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint32_t") { - uint32_t n = j; - CHECK(json(n) == j); + uint32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint64_t") { - uint64_t n = j; - CHECK(json(n) == j); + uint64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint8_fast_t") { - uint_fast8_t n = j; - CHECK(json(n) == j); + uint_fast8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint16_fast_t") { - uint_fast16_t n = j; - CHECK(json(n) == j); + uint_fast16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint32_fast_t") { - uint_fast32_t n = j; - CHECK(json(n) == j); + uint_fast32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint64_fast_t") { - uint_fast64_t n = j; - CHECK(json(n) == j); + uint_fast64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint8_least_t") { - uint_least8_t n = j; - CHECK(json(n) == j); + uint_least8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint16_least_t") { - uint_least16_t n = j; - CHECK(json(n) == j); + uint_least16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint32_least_t") { - uint_least32_t n = j; - CHECK(json(n) == j); + uint_least32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } SECTION("uint64_least_t") { - uint_least64_t n = j; - CHECK(json(n) == j); + uint_least64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); } } @@ -2411,6 +2550,7 @@ TEST_CASE("value conversion") "type must be number, but is boolean"); CHECK_NOTHROW(json(json::value_t::number_integer).get()); + CHECK_NOTHROW(json(json::value_t::number_unsigned).get()); } } @@ -2443,43 +2583,48 @@ TEST_CASE("value conversion") SECTION("object-like STL containers") { json j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j2 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; - json j3 = {{"one", true}, {"two", false}, {"three", true}}; - json j4 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; + json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; + json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; + json j4 = {{"one", true}, {"two", false}, {"three", true}}; + json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; SECTION("std::map") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - //auto m4 = j4.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); } SECTION("std::unordered_map") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - //auto m4 = j4.get>(); - //CHECK(m4["one"] == "eins"); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); } SECTION("std::multimap") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - //auto m4 = j4.get>(); - //CHECK(m4["one"] == "eins"); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); } SECTION("std::unordered_multimap") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - //auto m4 = j4.get>(); - //CHECK(m4["one"] == "eins"); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); } SECTION("exception in case of a non-object type") @@ -2492,56 +2637,63 @@ TEST_CASE("value conversion") SECTION("array-like STL containers") { json j1 = {1, 2, 3, 4}; - json j2 = {1.2, 2.3, 3.4, 4.5}; - json j3 = {true, false, true}; - json j4 = {"one", "two", "three"}; + json j2 = {1u, 2u, 3u, 4u}; + json j3 = {1.2, 2.3, 3.4, 4.5}; + json j4 = {true, false, true}; + json j5 = {"one", "two", "three"}; SECTION("std::list") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); } //SECTION("std::forward_list") //{ // auto m1 = j1.get>(); - // auto m2 = j2.get>(); - // auto m3 = j3.get>(); - // auto m4 = j4.get>(); + // auto m2 = j2.get>(); + // auto m3 = j3.get>(); + // auto m4 = j4.get>(); + // auto m5 = j5.get>(); //} SECTION("std::vector") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); } SECTION("std::deque") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); + auto m2 = j2.get>(); + auto m3 = j2.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); } SECTION("std::set") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); } SECTION("std::unordered_set") { auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); } SECTION("exception in case of a non-object type") @@ -2569,6 +2721,7 @@ TEST_CASE("pointer access") { "number", { {"integer", 42}, + {"unsigned", 42u}, {"floating-point", 17.23} } }, @@ -2601,6 +2754,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -2628,6 +2782,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -2655,6 +2810,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -2682,6 +2838,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -2709,6 +2866,35 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_unsigned_t") + { + using test_type = json::number_unsigned_t; + json value = 23u; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); } @@ -2736,6 +2922,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); } } @@ -2744,7 +2931,7 @@ TEST_CASE("element access") { SECTION("array") { - json j = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; const json j_const = j; SECTION("access specified element with bounds checking") @@ -2752,29 +2939,31 @@ TEST_CASE("element access") SECTION("access within bounds") { CHECK(j.at(0) == json(1)); - CHECK(j.at(1) == json(true)); - CHECK(j.at(2) == json(nullptr)); - CHECK(j.at(3) == json("string")); - CHECK(j.at(4) == json(42.23)); - CHECK(j.at(5) == json(json::object())); - CHECK(j.at(6) == json({1, 2, 3})); + CHECK(j.at(1) == json(1u)); + CHECK(j.at(2) == json(true)); + CHECK(j.at(3) == json(nullptr)); + CHECK(j.at(4) == json("string")); + CHECK(j.at(5) == json(42.23)); + CHECK(j.at(6) == json(json::object())); + CHECK(j.at(7) == json({1, 2, 3})); CHECK(j_const.at(0) == json(1)); - CHECK(j_const.at(1) == json(true)); - CHECK(j_const.at(2) == json(nullptr)); - CHECK(j_const.at(3) == json("string")); - CHECK(j_const.at(4) == json(42.23)); - CHECK(j_const.at(5) == json(json::object())); - CHECK(j_const.at(6) == json({1, 2, 3})); + CHECK(j_const.at(1) == json(1u)); + CHECK(j_const.at(2) == json(true)); + CHECK(j_const.at(3) == json(nullptr)); + CHECK(j_const.at(4) == json("string")); + CHECK(j_const.at(5) == json(42.23)); + CHECK(j_const.at(6) == json(json::object())); + CHECK(j_const.at(7) == json({1, 2, 3})); } SECTION("access outside bounds") { - CHECK_THROWS_AS(j.at(7), std::out_of_range); - CHECK_THROWS_AS(j_const.at(7), std::out_of_range); + CHECK_THROWS_AS(j.at(8), std::out_of_range); + CHECK_THROWS_AS(j_const.at(8), std::out_of_range); - CHECK_THROWS_WITH(j.at(7), "array index 7 is out of range"); - CHECK_THROWS_WITH(j_const.at(7), "array index 7 is out of range"); + CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); + CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); } SECTION("access on non-array type") @@ -2834,6 +3023,17 @@ TEST_CASE("element access") CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); } + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); @@ -2860,20 +3060,22 @@ TEST_CASE("element access") SECTION("access within bounds") { CHECK(j[0] == json(1)); - CHECK(j[1] == json(true)); - CHECK(j[2] == json(nullptr)); - CHECK(j[3] == json("string")); - CHECK(j[4] == json(42.23)); - CHECK(j[5] == json(json::object())); - CHECK(j[6] == json({1, 2, 3})); + CHECK(j[1] == json(1u)); + CHECK(j[2] == json(true)); + CHECK(j[3] == json(nullptr)); + CHECK(j[4] == json("string")); + CHECK(j[5] == json(42.23)); + CHECK(j[6] == json(json::object())); + CHECK(j[7] == json({1, 2, 3})); CHECK(j_const[0] == json(1)); - CHECK(j_const[1] == json(true)); - CHECK(j_const[2] == json(nullptr)); - CHECK(j_const[3] == json("string")); - CHECK(j_const[4] == json(42.23)); - CHECK(j_const[5] == json(json::object())); - CHECK(j_const[6] == json({1, 2, 3})); + CHECK(j_const[1] == json(1u)); + CHECK(j_const[2] == json(true)); + CHECK(j_const[3] == json(nullptr)); + CHECK(j_const[4] == json("string")); + CHECK(j_const[5] == json(42.23)); + CHECK(j_const[6] == json(json::object())); + CHECK(j_const[7] == json({1, 2, 3})); } SECTION("access on non-array type") @@ -2937,6 +3139,16 @@ TEST_CASE("element access") CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); } + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); @@ -2954,44 +3166,49 @@ TEST_CASE("element access") SECTION("remove element by index") { { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(0); - CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(1); - CHECK(jarray == json({1, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(2); - CHECK(jarray == json({1, true, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(3); - CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(4); - CHECK(jarray == json({1, true, nullptr, "string", json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(5); - CHECK(jarray == json({1, true, nullptr, "string", 42.23, {1, 2, 3}})); + CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(6); - CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object()})); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - CHECK_THROWS_AS(jarray.erase(7), std::out_of_range); - CHECK_THROWS_WITH(jarray.erase(7), "index out of range"); + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(7); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); + CHECK_THROWS_WITH(jarray.erase(8), "index out of range"); } } @@ -3000,29 +3217,29 @@ TEST_CASE("element access") SECTION("erase(begin())") { { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin()); - CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(true)); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin()); - CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(true)); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); } } SECTION("erase(begin(), end())") { { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); CHECK(jarray == json::array()); CHECK(it2 == jarray.end()); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); CHECK(jarray == json::array()); CHECK(it2 == jarray.cend()); @@ -3032,15 +3249,15 @@ TEST_CASE("element access") SECTION("erase(begin(), begin())") { { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); - CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1)); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); - CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1)); } } @@ -3048,17 +3265,17 @@ TEST_CASE("element access") SECTION("erase at offset") { { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it = jarray.begin() + 3; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it = jarray.begin() + 4; json::iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(42.23)); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it = jarray.cbegin() + 3; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it = jarray.cbegin() + 4; json::const_iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(42.23)); } } @@ -3066,15 +3283,15 @@ TEST_CASE("element access") SECTION("erase subrange") { { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin() + 2, jarray.begin() + 5); - CHECK(jarray == json({1, true, json::object(), {1, 2, 3}})); + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); CHECK(*it2 == json::object()); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin() + 2, jarray.cbegin() + 5); - CHECK(jarray == json({1, true, json::object(), {1, 2, 3}})); + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); CHECK(*it2 == json::object()); } } @@ -3082,7 +3299,7 @@ TEST_CASE("element access") SECTION("different arrays") { { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); @@ -3098,7 +3315,7 @@ TEST_CASE("element access") "iterators do not fit current value"); } { - json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); @@ -3153,6 +3370,13 @@ TEST_CASE("element access") CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); } + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); @@ -3165,7 +3389,7 @@ TEST_CASE("element access") SECTION("object") { - json j = {{"integer", 1}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; + json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; const json j_const = j; SECTION("access specified element with bounds checking") @@ -3173,6 +3397,7 @@ TEST_CASE("element access") SECTION("access within bounds") { CHECK(j.at("integer") == json(1)); + CHECK(j.at("unsigned") == json(1u)); CHECK(j.at("boolean") == json(true)); CHECK(j.at("null") == json(nullptr)); CHECK(j.at("string") == json("hello world")); @@ -3181,6 +3406,7 @@ TEST_CASE("element access") CHECK(j.at("array") == json({1, 2, 3})); CHECK(j_const.at("integer") == json(1)); + CHECK(j_const.at("unsigned") == json(1u)); CHECK(j_const.at("boolean") == json(true)); CHECK(j_const.at("null") == json(nullptr)); CHECK(j_const.at("string") == json("hello world")); @@ -3248,7 +3474,17 @@ 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); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + 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); @@ -3267,6 +3503,8 @@ TEST_CASE("element access") { CHECK(j.value("integer", 2) == 1); CHECK(j.value("integer", 1.0) == Approx(1)); + CHECK(j.value("unsigned", 2) == 1u); + CHECK(j.value("unsigned", 1.0) == Approx(1u)); CHECK(j.value("null", json(1)) == json()); CHECK(j.value("boolean", false) == true); CHECK(j.value("string", "bar") == "hello world"); @@ -3278,6 +3516,8 @@ TEST_CASE("element access") CHECK(j_const.value("integer", 2) == 1); CHECK(j_const.value("integer", 1.0) == Approx(1)); + CHECK(j_const.value("unsigned", 2) == 1u); + CHECK(j_const.value("unsigned", 1.0) == Approx(1u)); CHECK(j_const.value("boolean", false) == true); CHECK(j_const.value("string", "bar") == "hello world"); CHECK(j_const.value("string", std::string("bar")) == "hello world"); @@ -3290,6 +3530,7 @@ TEST_CASE("element access") SECTION("access non-existing value") { CHECK(j.value("_", 2) == 2); + CHECK(j.value("_", 2u) == 2u); CHECK(j.value("_", false) == false); CHECK(j.value("_", "bar") == "bar"); CHECK(j.value("_", 12.34) == Approx(12.34)); @@ -3297,6 +3538,7 @@ TEST_CASE("element access") CHECK(j.value("_", json({10, 100})) == json({10, 100})); CHECK(j_const.value("_", 2) == 2); + CHECK(j_const.value("_", 2u) == 2u); CHECK(j_const.value("_", false) == false); CHECK(j_const.value("_", "bar") == "bar"); CHECK(j_const.value("_", 12.34) == Approx(12.34)); @@ -3356,6 +3598,16 @@ TEST_CASE("element access") CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); } + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + } + SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); @@ -3373,9 +3625,9 @@ TEST_CASE("element access") // "array" is the smallest key CHECK(j.front() == json({1, 2, 3})); CHECK(j_const.front() == json({1, 2, 3})); - // "string" is the largest key - CHECK(j.back() == json("hello world")); - CHECK(j_const.back() == json("hello world")); + // "unsigned" is the largest key + CHECK(j.back() == json(1u)); + CHECK(j_const.back() == json(1u)); } SECTION("access specified element") @@ -3385,6 +3637,9 @@ TEST_CASE("element access") CHECK(j["integer"] == json(1)); CHECK(j[json::object_t::key_type("integer")] == j["integer"]); + CHECK(j["unsigned"] == json(1u)); + CHECK(j[json::object_t::key_type("unsigned")] == j["unsigned"]); + CHECK(j["boolean"] == json(true)); CHECK(j[json::object_t::key_type("boolean")] == j["boolean"]); @@ -3504,6 +3759,22 @@ TEST_CASE("element access") "cannot use operator[] with number"); } + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + } + SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); @@ -3531,6 +3802,11 @@ TEST_CASE("element access") CHECK(j.find("integer") == j.end()); CHECK(j.erase("integer") == 0); + CHECK(j.find("unsigned") != j.end()); + CHECK(j.erase("unsigned") == 1); + CHECK(j.find("unsigned") == j.end()); + CHECK(j.erase("unsigned") == 0); + CHECK(j.find("boolean") != j.end()); CHECK(j.erase("boolean") == 1); CHECK(j.find("boolean") == j.end()); @@ -3567,15 +3843,15 @@ TEST_CASE("element access") SECTION("erase(begin())") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it2 = jobject.erase(jobject.begin()); - CHECK(jobject == json({{"b", 1}, {"c", 17}})); + CHECK(jobject == json({{"b", 1}, {"c", 17u}})); CHECK(*it2 == json(1)); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it2 = jobject.erase(jobject.cbegin()); - CHECK(jobject == json({{"b", 1}, {"c", 17}})); + CHECK(jobject == json({{"b", 1}, {"c", 17u}})); CHECK(*it2 == json(1)); } } @@ -3583,13 +3859,13 @@ TEST_CASE("element access") SECTION("erase(begin(), end())") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); CHECK(jobject == json::object()); CHECK(it2 == jobject.end()); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); CHECK(jobject == json::object()); CHECK(it2 == jobject.cend()); @@ -3599,15 +3875,15 @@ TEST_CASE("element access") SECTION("erase(begin(), begin())") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin()); - CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17}})); + CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); CHECK(*it2 == json("a")); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); - CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17}})); + CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); CHECK(*it2 == json("a")); } } @@ -3615,17 +3891,17 @@ TEST_CASE("element access") SECTION("erase at offset") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it = jobject.find("b"); json::iterator it2 = jobject.erase(it); - CHECK(jobject == json({{"a", "a"}, {"c", 17}})); + CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); CHECK(*it2 == json(17)); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it = jobject.find("b"); json::const_iterator it2 = jobject.erase(it); - CHECK(jobject == json({{"a", "a"}, {"c", 17}})); + CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); CHECK(*it2 == json(17)); } } @@ -3633,13 +3909,13 @@ TEST_CASE("element access") SECTION("erase subrange") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); CHECK(jobject == json({{"a", "a"}, {"e", true}})); CHECK(*it2 == json(true)); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); CHECK(jobject == json({{"a", "a"}, {"e", true}})); CHECK(*it2 == json(true)); @@ -3649,8 +3925,8 @@ TEST_CASE("element access") SECTION("different objects") { { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error); @@ -3664,8 +3940,8 @@ TEST_CASE("element access") "iterators do not fit current value"); } { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error); @@ -3732,7 +4008,7 @@ TEST_CASE("element access") SECTION("existing element") { for (auto key : - {"integer", "floating", "null", "string", "boolean", "object", "array" + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" }) { CHECK(j.find(key) != j.end()); @@ -3798,6 +4074,14 @@ TEST_CASE("element access") CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); @@ -3813,7 +4097,7 @@ TEST_CASE("element access") SECTION("existing element") { for (auto key : - {"integer", "floating", "null", "string", "boolean", "object", "array" + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" }) { CHECK(j.count(key) == 1); @@ -3877,6 +4161,14 @@ TEST_CASE("element access") CHECK(j_nonobject_const.count("foo") == 0); } + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); @@ -3952,6 +4244,20 @@ TEST_CASE("element access") } } + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + SECTION("number (floating point)") { { @@ -4031,6 +4337,22 @@ TEST_CASE("element access") } } + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + SECTION("number (floating point)") { { @@ -4092,6 +4414,20 @@ TEST_CASE("element access") } } + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + SECTION("number (floating point)") { { @@ -4171,6 +4507,22 @@ TEST_CASE("element access") } } + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + SECTION("number (floating point)") { { @@ -4244,6 +4596,24 @@ TEST_CASE("element access") } } + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + SECTION("number (floating point)") { { @@ -5260,6 +5630,210 @@ TEST_CASE("iterators") } } + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(23)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(23)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + SECTION("number (float)") { json j = 23.42; @@ -5543,7 +6117,7 @@ TEST_CASE("iterators") SECTION("iterator comparisons") { - json j_values = {nullptr, true, 42, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; for (json& j : j_values) { @@ -5962,7 +6536,7 @@ TEST_CASE("iterators") SECTION("reverse iterator comparisons") { - json j_values = {nullptr, true, 42, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; for (json& j : j_values) { @@ -6516,6 +7090,24 @@ TEST_CASE("capacity") } } + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + SECTION("number (float)") { json j = 23.42; @@ -6701,6 +7293,26 @@ TEST_CASE("capacity") } } + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + SECTION("number (float)") { json j = 23.42; @@ -6834,6 +7446,18 @@ TEST_CASE("capacity") } } + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + SECTION("number (float)") { json j = 23.42; @@ -6930,6 +7554,14 @@ TEST_CASE("modifiers") CHECK(j == json(json::value_t::number_integer)); } + SECTION("number (unsigned)") + { + json j = 23u; + + j.clear(); + CHECK(j == json(json::value_t::number_integer)); + } + SECTION("number (float)") { json j = 23.42; @@ -7470,6 +8102,7 @@ TEST_CASE("lexicographical comparison operators") json::value_t::null, json::value_t::boolean, json::value_t::number_integer, + json::value_t::number_unsigned, json::value_t::number_float, json::value_t::object, json::value_t::array, @@ -7480,13 +8113,14 @@ TEST_CASE("lexicographical comparison operators") { std::vector> expected = { - {false, true, true, true, true, true, true}, - {false, false, true, true, true, true, true}, - {false, false, false, false, true, true, true}, - {false, false, false, false, true, true, true}, - {false, false, false, false, false, true, true}, - {false, false, false, false, false, false, true}, - {false, false, false, false, false, false, false} + {false, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, false, true, true}, + {false, false, false, false, false, false, false, true}, + {false, false, false, false, false, false, false, false} }; for (size_t i = 0; i < j_types.size(); ++i) @@ -7508,6 +8142,7 @@ TEST_CASE("lexicographical comparison operators") { nullptr, nullptr, 17, 42, + 8u, 13u, 3.14159, 23.42, "foo", "bar", true, false, @@ -7519,26 +8154,30 @@ TEST_CASE("lexicographical comparison operators") { std::vector> expected = { - {true, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {true, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, true, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, true, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, true, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, true} + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} }; for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { + CAPTURE(i); + CAPTURE(j); // check precomputed values CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); } @@ -7565,6 +8204,8 @@ TEST_CASE("lexicographical comparison operators") { for (size_t j = 0; j < j_values.size(); ++j) { + CAPTURE(i); + CAPTURE(j); // check definition CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); } @@ -7582,26 +8223,30 @@ TEST_CASE("lexicographical comparison operators") { std::vector> expected = { - {false, false, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, false, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, true, true, false, false, true, true, true, true}, - {false, false, true, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, true, false, false, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, true, true, true, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, false, true, true, true, true}, - {false, false, false, false, false, false, true, true, false, false, false, true, false, false}, - {false, false, false, false, false, false, true, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, true, false, false, true, true, false, false}, - {false, false, false, false, false, false, true, true, false, false, true, true, true, false} + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true}, + {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false} }; for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { + CAPTURE(i); + CAPTURE(j); // check precomputed values CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); } @@ -7611,6 +8256,7 @@ TEST_CASE("lexicographical comparison operators") json j_discarded(json::value_t::discarded); for (size_t i = 0; i < j_values.size(); ++i) { + CAPTURE(i); CHECK( (j_values[i] < j_discarded) == false); CHECK( (j_discarded < j_values[i]) == false); CHECK( (j_discarded < j_discarded) == false); @@ -7623,6 +8269,8 @@ TEST_CASE("lexicographical comparison operators") { for (size_t j = 0; j < j_values.size(); ++j) { + CAPTURE(i); + CAPTURE(j); // check definition CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); } @@ -7635,6 +8283,8 @@ TEST_CASE("lexicographical comparison operators") { for (size_t j = 0; j < j_values.size(); ++j) { + CAPTURE(i); + CAPTURE(j); // check definition CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); } @@ -7647,6 +8297,8 @@ TEST_CASE("lexicographical comparison operators") { for (size_t j = 0; j < j_values.size(); ++j) { + CAPTURE(i); + CAPTURE(j); // check definition CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); } @@ -8818,6 +9470,23 @@ 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 + // for ranges in range of IEEE 754-2008 binary64 (double precision) + // this does not accommodate 64 bit integers without loss of accuracy. + // As 64 bit integers are now widely used in software, it is desirable + // to expand support to to the full 64 bit (signed and unsigned) range + // 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); + // (2**63)-1 + CHECK(json::parser("9223372036854775807").parse().get() == 9223372036854775807); + // (2**64)-1 + CHECK(json::parser("18446744073709551615").parse().get() == 18446744073709551615u); + } } SECTION("floating-point")