diff --git a/README.md b/README.md index 54c2e71b..071ca408 100644 --- a/README.md +++ b/README.md @@ -400,7 +400,7 @@ $ make $ ./json_unit "*" =============================================================================== -All tests passed (3341006 assertions in 22 test cases) +All tests passed (3341759 assertions in 27 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/doc/examples/insert.cpp b/doc/examples/insert.cpp new file mode 100644 index 00000000..c7adfc5a --- /dev/null +++ b/doc/examples/insert.cpp @@ -0,0 +1,16 @@ +#include + +using namespace nlohmann; + +int main() +{ + // create a JSON array + json v = {1, 2, 3, 4}; + + // insert number 10 before number 3 + auto new_pos = v.insert(v.begin() + 2, 10); + + // output new array and result of insert call + std::cout << *new_pos << '\n'; + std::cout << v << '\n'; +} diff --git a/doc/examples/insert.link b/doc/examples/insert.link new file mode 100644 index 00000000..80ec801c --- /dev/null +++ b/doc/examples/insert.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/insert.output b/doc/examples/insert.output new file mode 100644 index 00000000..ed5cab1d --- /dev/null +++ b/doc/examples/insert.output @@ -0,0 +1,2 @@ +10 +[1,2,10,3,4] diff --git a/doc/examples/insert__count.cpp b/doc/examples/insert__count.cpp new file mode 100644 index 00000000..cfdb39b6 --- /dev/null +++ b/doc/examples/insert__count.cpp @@ -0,0 +1,16 @@ +#include + +using namespace nlohmann; + +int main() +{ + // create a JSON array + json v = {1, 2, 3, 4}; + + // insert number 7 copies of number 7 before number 3 + auto new_pos = v.insert(v.begin() + 2, 7, 7); + + // output new array and result of insert call + std::cout << *new_pos << '\n'; + std::cout << v << '\n'; +} diff --git a/doc/examples/insert__count.link b/doc/examples/insert__count.link new file mode 100644 index 00000000..3c0e781c --- /dev/null +++ b/doc/examples/insert__count.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/insert__count.output b/doc/examples/insert__count.output new file mode 100644 index 00000000..294685ac --- /dev/null +++ b/doc/examples/insert__count.output @@ -0,0 +1,2 @@ +7 +[1,2,7,7,7,7,7,7,7,3,4] diff --git a/doc/examples/insert__range.cpp b/doc/examples/insert__range.cpp new file mode 100644 index 00000000..03ea0bc3 --- /dev/null +++ b/doc/examples/insert__range.cpp @@ -0,0 +1,19 @@ +#include + +using namespace nlohmann; + +int main() +{ + // create a JSON array + json v = {1, 2, 3, 4}; + + // create a JSON array to copy values from + json v2 = {"one", "two", "three", "four"}; + + // insert range from v2 before the end of array v + auto new_pos = v.insert(v.end(), v2.begin(), v2.end()); + + // output new array and result of insert call + std::cout << *new_pos << '\n'; + std::cout << v << '\n'; +} diff --git a/doc/examples/insert__range.link b/doc/examples/insert__range.link new file mode 100644 index 00000000..03c5d012 --- /dev/null +++ b/doc/examples/insert__range.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/insert__range.output b/doc/examples/insert__range.output new file mode 100644 index 00000000..d50e9f63 --- /dev/null +++ b/doc/examples/insert__range.output @@ -0,0 +1,2 @@ +"one" +[1,2,3,4,"one","two","three","four"] diff --git a/src/json.hpp b/src/json.hpp index cc3617c5..344d5f7b 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -3694,7 +3694,7 @@ class basic_json called on a JSON null value, an empty object is created before inserting @a value. - @param value the value to add to the JSON object + @param[in] value the value to add to the JSON object @throw std::domain_error when called on a type other than JSON object or null @@ -3734,6 +3734,148 @@ class basic_json return operator[](value.first); } + /*! + @brief inserts element + + Inserts element @a value before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] value element to insert + @return iterator pointing to the inserted @a value. + + @throw std::domain_error if called on JSON values other than arrays + @throw std::domain_error if @a pos is not an iterator of *this + + @complexity Constant plus linear in the distance between pos and end of the + container. + + @liveexample{The example shows how insert is used.,insert} + */ + iterator insert(const_iterator pos, const basic_json& value) + { + // insert only works for arrays + if (m_type != value_t::array) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, value); + return result; + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& value) + { + return insert(pos, value); + } + + /*! + @brief inserts elements + + Inserts @a count copies of @a value before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] count number of copies of @a value to insert + @param[in] value element to insert + @return iterator pointing to the first element inserted, or @a pos if + `count==0` + + @throw std::domain_error if called on JSON values other than arrays + @throw std::domain_error if @a pos is not an iterator of *this + + @complexity Linear in @a count plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how insert is used.,insert__count} + */ + iterator insert(const_iterator pos, size_type count, const basic_json& value) + { + // insert only works for arrays + if (m_type != value_t::array) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, count, value); + return result; + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw std::domain_error if called on JSON values other than arrays + @throw std::domain_error if @a pos is not an iterator of *this + @throw std::domain_error if @a first and @a last do not belong to the same + JSON value + @throw std::domain_error if @a first or @a last are iterators into + container for which insert is called + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how insert is used.,insert__range} + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (m_type != value_t::array) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + if (first.m_object != last.m_object) + { + throw std::domain_error("iterators does not fit"); + } + + if (first.m_object == this or last.m_object == this) + { + throw std::domain_error("passed iterators may not belong to container"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, + first.m_it.array_iterator, last.m_it.array_iterator); + return result; + } + /*! @brief exchanges the values diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 58666091..e7624ea6 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3694,7 +3694,7 @@ class basic_json called on a JSON null value, an empty object is created before inserting @a value. - @param value the value to add to the JSON object + @param[in] value the value to add to the JSON object @throw std::domain_error when called on a type other than JSON object or null @@ -3734,6 +3734,148 @@ class basic_json return operator[](value.first); } + /*! + @brief inserts element + + Inserts element @a value before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] value element to insert + @return iterator pointing to the inserted @a value. + + @throw std::domain_error if called on JSON values other than arrays + @throw std::domain_error if @a pos is not an iterator of *this + + @complexity Constant plus linear in the distance between pos and end of the + container. + + @liveexample{The example shows how insert is used.,insert} + */ + iterator insert(const_iterator pos, const basic_json& value) + { + // insert only works for arrays + if (m_type != value_t::array) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, value); + return result; + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& value) + { + return insert(pos, value); + } + + /*! + @brief inserts elements + + Inserts @a count copies of @a value before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] count number of copies of @a value to insert + @param[in] value element to insert + @return iterator pointing to the first element inserted, or @a pos if + `count==0` + + @throw std::domain_error if called on JSON values other than arrays + @throw std::domain_error if @a pos is not an iterator of *this + + @complexity Linear in @a count plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how insert is used.,insert__count} + */ + iterator insert(const_iterator pos, size_type count, const basic_json& value) + { + // insert only works for arrays + if (m_type != value_t::array) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, count, value); + return result; + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw std::domain_error if called on JSON values other than arrays + @throw std::domain_error if @a pos is not an iterator of *this + @throw std::domain_error if @a first and @a last do not belong to the same + JSON value + @throw std::domain_error if @a first or @a last are iterators into + container for which insert is called + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how insert is used.,insert__range} + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (m_type != value_t::array) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + if (first.m_object != last.m_object) + { + throw std::domain_error("iterators does not fit"); + } + + if (first.m_object == this or last.m_object == this) + { + throw std::domain_error("passed iterators may not belong to container"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, + first.m_it.array_iterator, last.m_it.array_iterator); + return result; + } + /*! @brief exchanges the values diff --git a/test/unit.cpp b/test/unit.cpp index 6f5f18f9..3e721627 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -6608,6 +6608,162 @@ TEST_CASE("modifiers") } } + SECTION("insert") + { + json j_array = {1, 2, 3, 4}; + json j_value = 5; + + SECTION("value at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 1); + CHECK(j_array == json({1, 2, 3, 4, 5})); + } + } + + SECTION("rvalue at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 1); + CHECK(j_array == json({1, 2, 3, 4, 5})); + } + } + + SECTION("copies at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 3); + CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5})); + } + + SECTION("insert nothing (count = 0)") + { + auto pos = j_array.end(); + auto it = j_array.insert(j_array.end(), 0, 5); + CHECK(j_array.size() == 4); + CHECK(it == pos); + CHECK(j_array == json({1, 2, 3, 4})); + } + } + + SECTION("range") + { + json j_other_array = {"first", "second"}; + + SECTION("proper usage") + { + auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end()); + CHECK(j_array.size() == 6); + CHECK(*it == *j_other_array.begin()); + CHECK((j_array.end() - it) == 2); + CHECK(j_array == json({1, 2, 3, 4, "first", "second"})); + } + + SECTION("empty range") + { + auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin()); + CHECK(j_array.size() == 4); + CHECK(it == j_array.end()); + CHECK(j_array == json({1, 2, 3, 4})); + } + + SECTION("invalid iterators") + { + CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error); + } + } + + SECTION("invalid iterator") + { + // pass iterator to a different array + json j_another_array = {1, 2}; + json j_yet_another_array = {"first", "second"}; + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), std::domain_error); + } + + SECTION("non-array type") + { + // call insert on a non-array type + json j_nonarray = 3; + json j_yet_another_array = {"first", "second"}; + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), std::domain_error); + } + } + SECTION("swap()") { SECTION("json")