diff --git a/include/pbcopper/json/internal/json.hpp b/include/pbcopper/json/internal/json.hpp index 43646763..b02a1344 100644 --- a/include/pbcopper/json/internal/json.hpp +++ b/include/pbcopper/json/internal/json.hpp @@ -711,73 +711,6 @@ class basic_json private: - /*! - @brief a type to hold JSON type information - - This bitfield type holds information about JSON types. It is internally - used to hold the basic JSON type enumeration, as well as additional - information in the case of values that have been parsed from a string - including whether of not it was created directly or parsed, and in the - case of floating point numbers the number of significant figures in the - original representaiton and if it was in exponential form, if a '+' was - included in the exponent and the capitilization of the exponent marker. - The sole purpose of this information is to permit accurate round trips. - - @since version 2.0.0 - */ - union type_data_t - { - struct - { - /// the type of the value (@ref value_t) - uint16_t type : 4; - /// whether the number was parsed from a string - uint16_t parsed : 1; - /// whether parsed number contained an exponent ('e'/'E') - uint16_t has_exp : 1; - /// whether parsed number contained a plus in the exponent - uint16_t exp_plus : 1; - /// whether parsed number's exponent was capitalized ('E') - uint16_t exp_cap : 1; - /// the number of figures for a parsed number - uint16_t precision : 8; - } bits; - uint16_t data; - - /// return the type as value_t - operator value_t() const - { - return static_cast(bits.type); - } - - /// test type for equality (ignore other fields) - bool operator==(const value_t& rhs) const - { - return static_cast(bits.type) == rhs; - } - - /// assignment - type_data_t& operator=(value_t rhs) - { - bits.type = static_cast(rhs) & 15; // avoid overflow - return *this; - } - - /// construct from value_t - type_data_t(value_t t) noexcept - { - *reinterpret_cast(this) = 0; - bits.type = static_cast(t) & 15; // avoid overflow - } - - /// default constructor - type_data_t() noexcept - { - data = 0; - bits.type = reinterpret_cast(value_t::null); - } - }; - /// helper for exception-safe object creation template static T* create(Args&& ... args) @@ -1461,13 +1394,13 @@ class basic_json @since version 2.0.0 */ - template < typename CompatibleNumberUnsignedType, typename - std::enable_if < - std::is_constructible::value and - std::numeric_limits::is_integer and - !std::numeric_limits::is_signed, - CompatibleNumberUnsignedType >::type - = 0 > + template ::value and + std::numeric_limits::is_integer and + not 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)) @@ -5079,6 +5012,7 @@ class basic_json throw std::domain_error("iterator does not fit current value"); } + // check if range iterators belong to the same JSON object if (first.m_object != last.m_object) { throw std::domain_error("iterators do not fit"); @@ -6182,79 +6116,23 @@ class basic_json case value_t::number_float: { - // check if number was parsed from a string - if (m_type.bits.parsed) + if (m_value.number_float == 0) { - // check if parsed number had an exponent given - if (m_type.bits.has_exp) - { - // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1) - char buf[263]; - int len; - - // handle capitalization of the exponent - if (m_type.bits.exp_cap) - { - len = snprintf(buf, sizeof(buf), "%.*E", - m_type.bits.precision, m_value.number_float) + 1; - } - else - { - len = snprintf(buf, sizeof(buf), "%.*e", - m_type.bits.precision, m_value.number_float) + 1; - } - - // remove '+' sign from the exponent if necessary - if (not m_type.bits.exp_plus) - { - if (len > static_cast(sizeof(buf))) - { - len = sizeof(buf); - } - for (int i = 0; i < len; i++) - { - if (buf[i] == '+') - { - for (; i + 1 < len; i++) - { - buf[i] = buf[i + 1]; - } - } - } - } - - o << buf; - } - else - { - // no exponent - output as a decimal - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems - ss << std::setprecision(m_type.bits.precision) - << std::fixed << m_value.number_float; - o << ss.str(); - } + // special case for zero to get "0.0"/"-0.0" + o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); } else { - if (m_value.number_float == 0) - { - // special case for zero to get "0.0"/"-0.0" - o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); - } - else - { - // Otherwise 6, 15 or 16 digits of precision allows - // round-trip IEEE 754 string->float->string, - // string->double->string or string->long - // double->string; to be safe, we read this value from - // std::numeric_limits::digits10 - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems - ss << std::setprecision(std::numeric_limits::digits10) - << m_value.number_float; - o << ss.str(); - } + // Otherwise 6, 15 or 16 digits of precision allows + // round-trip IEEE 754 string->float->string, + // string->double->string or string->long + // double->string; to be safe, we read this value from + // std::numeric_limits::digits10 + std::stringstream ss; + ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems + ss << std::setprecision(std::numeric_limits::digits10) + << m_value.number_float; + o << ss.str(); } return; } @@ -6279,7 +6157,7 @@ class basic_json ////////////////////// /// the type of the current element - type_data_t m_type = value_t::null; + value_t m_type = value_t::null; /// the value of the current element json_value m_value = {}; @@ -7277,17 +7155,17 @@ class basic_json enum class token_type { uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the "true" literal - literal_false, ///< the "false" literal - literal_null, ///< the "null" literal + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal value_string, ///< a string -- use get_string() for actual value value_number, ///< a number -- use get_number() for actual value - begin_array, ///< the character for array begin "[" - begin_object, ///< the character for object begin "{" - end_array, ///< the character for array end "]" - end_object, ///< the character for object end "}" - name_separator, ///< the name separator ":" - value_separator, ///< the value separator "," + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` parse_error, ///< indicating a parse error end_of_input ///< indicating the end of the input buffer }; @@ -7332,7 +7210,7 @@ class basic_json @return string representation of the code point - @throw std::out_of_range if code point is >0x10ffff; example: `"code + @throw std::out_of_range if code point is > 0x10ffff; example: `"code points above 0x10FFFF are invalid"` @throw std::invalid_argument if the low surrogate is invalid; example: `""missing or wrong low surrogate""` @@ -8456,18 +8334,12 @@ class basic_json number_integer_t or @ref number_unsigned_t then it sets the result parameter accordingly. - The 'floating point representation' includes the number of significant - figures after the radix point, whether the number is in exponential or - decimal form, the capitalization of the exponent marker, and if the - optional '+' is present in the exponent. This information is necessary - to perform accurate round trips of floating point numbers. - If the number is a floating point number the number is then parsed using @a std:strtod (or @a std:strtof or @a std::strtold). @param[out] result @ref basic_json object to receive the number, or - NAN if the conversion read past the current token. The latter case - needs to be treated by the caller function. + NAN if the conversion read past the current token. The latter case + needs to be treated by the caller function. */ void get_number(basic_json& result) const { @@ -8475,15 +8347,6 @@ class basic_json const lexer::lexer_char_t* curptr = m_start; - // remember this number was parsed (for later serialization) - result.m_type.bits.parsed = true; - - // 'found_radix_point' will be set to 0xFF upon finding a radix - // point and later used to mask in/out the precision depending - // whether a radix is found i.e. 'precision &= found_radix_point' - uint8_t found_radix_point = 0; - uint8_t precision = 0; - // accumulate the integer conversion result (unsigned for now) number_unsigned_t value = 0; @@ -8497,13 +8360,13 @@ class basic_json if (*curptr == '-') { type = value_t::number_integer; - max = static_cast(std::numeric_limits::max()) + 1; + max = static_cast((std::numeric_limits::max)()) + 1; curptr++; } else { type = value_t::number_unsigned; - max = static_cast(std::numeric_limits::max()); + max = static_cast((std::numeric_limits::max)()); } // count the significant figures @@ -8516,22 +8379,11 @@ class basic_json { // don't count '.' but change to float type = value_t::number_float; - - // reset precision count - precision = 0; - found_radix_point = 0xFF; continue; } // assume exponent (if not then will fail parse): change to // float, stop counting and record exponent details type = value_t::number_float; - result.m_type.bits.has_exp = true; - - // exponent capitalization - result.m_type.bits.exp_cap = (*curptr == 'E'); - - // exponent '+' sign - result.m_type.bits.exp_plus = (*(++curptr) == '+'); break; } @@ -8553,13 +8405,8 @@ class basic_json value = temp; } } - ++precision; } - // If no radix point was found then precision would now be set to - // the number of digits, which is wrong - clear it. - result.m_type.bits.precision = precision & found_radix_point; - // save the value (if not a float) if (type == value_t::number_unsigned) { @@ -10001,9 +9848,12 @@ class basic_json // in a second pass, traverse the remaining elements // remove my remaining elements + const auto end_index = static_cast(result.size()); while (i < source.size()) { - result.push_back(object( + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( { {"op", "remove"}, {"path", path + "/" + std::to_string(i)}