From 834918018eedf1b3737d87a9dfb7462db13c1a23 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Nov 2021 13:46:58 +0000 Subject: [PATCH 1/2] Fix spelling (#3125) --- .../nlohmann/detail/input/binary_reader.hpp | 8 ++++---- .../nlohmann/detail/input/input_adapters.hpp | 2 +- .../nlohmann/detail/output/binary_writer.hpp | 6 +++--- include/nlohmann/json.hpp | 4 ++-- single_include/nlohmann/json.hpp | 20 +++++++++---------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index f1b65ba54..3add2c115 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -42,7 +42,7 @@ enum class cbor_tag_handler_t @note from https://stackoverflow.com/a/1001328/266378 */ -static inline bool little_endianess(int num = 1) noexcept +static inline bool little_endianness(int num = 1) noexcept { return *reinterpret_cast(&num) == 1; } @@ -2341,7 +2341,7 @@ class binary_reader @return whether conversion completed - @note This function needs to respect the system's endianess, because + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -2514,8 +2514,8 @@ class binary_reader /// the number of characters read std::size_t chars_read = 0; - /// whether we can assume little endianess - const bool is_little_endian = little_endianess(); + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); /// the SAX parser json_sax_t* sax = nullptr; diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 6df58a1cd..0577bfe4d 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -450,7 +450,7 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) / } // This class only handles inputs of input_buffer_adapter type. -// It's required so that expressions like {ptr, len} can be implicitely casted +// It's required so that expressions like {ptr, len} can be implicitly casted // to the correct adapter. class span_input_adapter { diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 42fd50e32..23b9ff16e 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -1535,7 +1535,7 @@ class binary_writer @tparam OutputIsLittleEndian Set to true if output data is required to be little endian - @note This function needs to respect the system's endianess, because bytes + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -1625,8 +1625,8 @@ class binary_writer } private: - /// whether we can assume little endianess - const bool is_little_endian = little_endianess(); + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); /// the output output_adapter_t oa = nullptr; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 9a00b3226..7ea86c38c 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -908,7 +908,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. - The subtype is then added as singed 8-bit integer. + The subtype is then added as signed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - BSON - If a subtype is given, it is used and added as unsigned 8-bit integer. @@ -5021,7 +5021,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec `key()` returns an empty string. @warning Using `items()` on temporary objects is dangerous. Make sure the - object's lifetime exeeds the iteration. See + object's lifetime exceeds the iteration. See for more information. diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4ca17ca4a..5e25c98e3 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5875,7 +5875,7 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) / } // This class only handles inputs of input_buffer_adapter type. -// It's required so that expressions like {ptr, len} can be implicitely casted +// It's required so that expressions like {ptr, len} can be implicitly casted // to the correct adapter. class span_input_adapter { @@ -8430,7 +8430,7 @@ enum class cbor_tag_handler_t @note from https://stackoverflow.com/a/1001328/266378 */ -static inline bool little_endianess(int num = 1) noexcept +static inline bool little_endianness(int num = 1) noexcept { return *reinterpret_cast(&num) == 1; } @@ -10729,7 +10729,7 @@ class binary_reader @return whether conversion completed - @note This function needs to respect the system's endianess, because + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -10902,8 +10902,8 @@ class binary_reader /// the number of characters read std::size_t chars_read = 0; - /// whether we can assume little endianess - const bool is_little_endian = little_endianess(); + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); /// the SAX parser json_sax_t* sax = nullptr; @@ -15202,7 +15202,7 @@ class binary_writer @tparam OutputIsLittleEndian Set to true if output data is required to be little endian - @note This function needs to respect the system's endianess, because bytes + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -15292,8 +15292,8 @@ class binary_writer } private: - /// whether we can assume little endianess - const bool is_little_endian = little_endianess(); + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); /// the output output_adapter_t oa = nullptr; @@ -18409,7 +18409,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. - The subtype is then added as singed 8-bit integer. + The subtype is then added as signed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - BSON - If a subtype is given, it is used and added as unsigned 8-bit integer. @@ -22522,7 +22522,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec `key()` returns an empty string. @warning Using `items()` on temporary objects is dangerous. Make sure the - object's lifetime exeeds the iteration. See + object's lifetime exceeds the iteration. See for more information. From e9f88c2fad4b910b90262019df72181c14a0370f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 9 Nov 2021 22:24:58 +0100 Subject: [PATCH 2/2] Add missing erase(first, last) function to ordered_map (#3109) --- include/nlohmann/ordered_map.hpp | 53 +++++++++++++++++++++++++++----- single_include/nlohmann/json.hpp | 53 +++++++++++++++++++++++++++----- test/src/unit-ordered_map.cpp | 39 +++++++++++++++++++++++ test/src/unit-regression2.cpp | 17 ++++++++++ 4 files changed, 148 insertions(+), 14 deletions(-) diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index cf5f133e7..51b954524 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -107,16 +107,55 @@ template , iterator erase(iterator pos) { - auto it = pos; + return erase(pos, std::next(pos)); + } - // Since we cannot move const Keys, re-construct them in place - for (auto next = it; ++next != this->end(); ++it) + iterator erase(iterator first, iterator last) + { + const auto elements_affected = std::distance(first, last); + const auto offset = std::distance(Container::begin(), first); + + // This is the start situation. We need to delete elements_affected + // elements (3 in this example: e, f, g), and need to return an + // iterator past the last deleted element (h in this example). + // Note that offset is the distance from the start of the vector + // to first. We will need this later. + + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // first last + + // Since we cannot move const Keys, we re-construct them in place. + // We start at first and re-construct (viz. copy) the elements from + // the back of the vector. Example for first iteration: + + // ,--------. + // v | destroy e and re-construct with h + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // it it + elements_affected + + for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) { - it->~value_type(); // Destroy but keep allocation - new (&*it) value_type{std::move(*next)}; + it->~value_type(); // destroy but keep allocation + new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it } - Container::pop_back(); - return pos; + + // [ a, b, c, d, h, i, j, h, i, j ] + // ^ ^ + // first last + + // remove the unneeded elements at the end of the vector + Container::resize(this->size() - static_cast(elements_affected)); + + // [ a, b, c, d, h, i, j ] + // ^ ^ + // first last + + // first is now pointing past the last deleted element, but we cannot + // use this iterator, because it may have been invalidated by the + // resize call. Instead, we can return begin() + offset. + return Container::begin() + offset; } size_type count(const Key& key) const diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 5e25c98e3..580097630 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17494,16 +17494,55 @@ template , iterator erase(iterator pos) { - auto it = pos; + return erase(pos, std::next(pos)); + } - // Since we cannot move const Keys, re-construct them in place - for (auto next = it; ++next != this->end(); ++it) + iterator erase(iterator first, iterator last) + { + const auto elements_affected = std::distance(first, last); + const auto offset = std::distance(Container::begin(), first); + + // This is the start situation. We need to delete elements_affected + // elements (3 in this example: e, f, g), and need to return an + // iterator past the last deleted element (h in this example). + // Note that offset is the distance from the start of the vector + // to first. We will need this later. + + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // first last + + // Since we cannot move const Keys, we re-construct them in place. + // We start at first and re-construct (viz. copy) the elements from + // the back of the vector. Example for first iteration: + + // ,--------. + // v | destroy e and re-construct with h + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // it it + elements_affected + + for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) { - it->~value_type(); // Destroy but keep allocation - new (&*it) value_type{std::move(*next)}; + it->~value_type(); // destroy but keep allocation + new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it } - Container::pop_back(); - return pos; + + // [ a, b, c, d, h, i, j, h, i, j ] + // ^ ^ + // first last + + // remove the unneeded elements at the end of the vector + Container::resize(this->size() - static_cast(elements_affected)); + + // [ a, b, c, d, h, i, j ] + // ^ ^ + // first last + + // first is now pointing past the last deleted element, but we cannot + // use this iterator, because it may have been invalidated by the + // resize call. Instead, we can return begin() + offset. + return Container::begin() + offset; } size_type count(const Key& key) const diff --git a/test/src/unit-ordered_map.cpp b/test/src/unit-ordered_map.cpp index 7a23e36fb..a8f2850c8 100644 --- a/test/src/unit-ordered_map.cpp +++ b/test/src/unit-ordered_map.cpp @@ -210,6 +210,45 @@ TEST_CASE("ordered_map") ++it2; CHECK(it2 == om.end()); } + + SECTION("with iterator pair") + { + SECTION("range in the middle") + { + // need more elements + om["vier"] = "four"; + om["fünf"] = "five"; + + // delete "zwei" and "drei" + auto it = om.erase(om.begin() + 1, om.begin() + 3); + CHECK(it->first == "vier"); + CHECK(om.size() == 3); + } + + SECTION("range at the beginning") + { + // need more elements + om["vier"] = "four"; + om["fünf"] = "five"; + + // delete "eins" and "zwei" + auto it = om.erase(om.begin(), om.begin() + 2); + CHECK(it->first == "drei"); + CHECK(om.size() == 3); + } + + SECTION("range at the end") + { + // need more elements + om["vier"] = "four"; + om["fünf"] = "five"; + + // delete "vier" and "fünf" + auto it = om.erase(om.begin() + 3, om.end()); + CHECK(it == om.end()); + CHECK(om.size() == 3); + } + } } SECTION("count") diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index f0c4ef451..b2f2a7dc3 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -746,6 +746,23 @@ TEST_CASE("regression tests 2") std::vector foo; j.get_to(foo); } + + SECTION("issue #3108 - ordered_json doesn't support range based erase") + { + ordered_json j = {1, 2, 2, 4}; + + auto last = std::unique(j.begin(), j.end()); + j.erase(last, j.end()); + + CHECK(j.dump() == "[1,2,4]"); + + j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val) + { + return val == 2; + }), j.end()); + + CHECK(j.dump() == "[1,4]"); + } } DOCTEST_CLANG_SUPPRESS_WARNING_POP