diff --git a/docs/mkdocs/docs/api/basic_json/insert.md b/docs/mkdocs/docs/api/basic_json/insert.md index fcb1e6e44..ff9082b56 100644 --- a/docs/mkdocs/docs/api/basic_json/insert.md +++ b/docs/mkdocs/docs/api/basic_json/insert.md @@ -195,5 +195,7 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Added in version 1.0.0. 2. Added in version 1.0.0. 3. Added in version 1.0.0. -4. Added in version 1.0.0. +4. Added in version 1.0.0. Fixed in version 3.13.0 to copy the values before inserting; before, an `ilist` that + referred to elements of the array being inserted into could insert wrong values, because the range insert could + move from or shift an element before it was copied. 5. Added in version 3.0.0. diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 500fcddf2..a3f13d29e 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -4152,8 +4152,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } + // copy the values first: ilist may refer to elements of this array + array_t values; + values.reserve(ilist.size()); + for (const auto& element : ilist) + { + values.push_back(element.moved_or_copied()); + } + // insert to array and return iterator - return insert_iterator(pos, ilist.begin(), ilist.end()); + return insert_iterator(pos, std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())); } /// @brief inserts range of elements into object diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 576498738..639f53c1a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -30233,8 +30233,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } + // copy the values first: ilist may refer to elements of this array + array_t values; + values.reserve(ilist.size()); + for (const auto& element : ilist) + { + values.push_back(element.moved_or_copied()); + } + // insert to array and return iterator - return insert_iterator(pos, ilist.begin(), ilist.end()); + return insert_iterator(pos, std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())); } /// @brief inserts range of elements into object diff --git a/tests/src/unit-modifiers.cpp b/tests/src/unit-modifiers.cpp index c878ec15c..794cc241b 100644 --- a/tests/src/unit-modifiers.cpp +++ b/tests/src/unit-modifiers.cpp @@ -761,6 +761,34 @@ TEST_CASE("modifiers") } } + SECTION("initializer list referring to the array's own elements (#5656)") + { + SECTION("sufficient capacity (no reallocation)") + { + json j_own = json::array(); + j_own.get_ref().reserve(8); + j_own.push_back("a"); + j_own.push_back("b"); + j_own.push_back("c"); + + const json& j_own_cref = j_own; + auto it = j_own.insert(j_own.begin(), {j_own_cref[0], j_own_cref[1]}); + CHECK(*it == json("a")); + CHECK(j_own == json({"a", "b", "a", "b", "c"})); + } + + SECTION("insufficient capacity (reallocation)") + { + json j_own = {"a", "b", "c"}; + j_own.get_ref().shrink_to_fit(); + + const json& j_own_cref = j_own; + auto it = j_own.insert(j_own.begin(), {j_own_cref[2]}); + CHECK(*it == json("c")); + CHECK(j_own == json({"c", "a", "b", "c"})); + } + } + SECTION("invalid iterator") { // pass iterator to a different array