Merge pull request #2327 from nlohmann/issue2326
Fix name of Homebrew formula in documentation
diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp
index 2a72fef..5767909 100644
--- a/include/nlohmann/ordered_map.hpp
+++ b/include/nlohmann/ordered_map.hpp
@@ -18,6 +18,7 @@
using mapped_type = T;
using Container = std::vector<std::pair<const Key, T>, Allocator>;
using typename Container::iterator;
+ using typename Container::const_iterator;
using typename Container::size_type;
using typename Container::value_type;
@@ -30,7 +31,7 @@
ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() )
: Container{init, alloc} {}
- std::pair<iterator, bool> emplace(key_type&& key, T&& t)
+ std::pair<iterator, bool> emplace(const key_type& key, T&& t)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
@@ -43,9 +44,40 @@
return {--this->end(), true};
}
- T& operator[](Key&& key)
+ T& operator[](const Key& key)
{
- return emplace(std::move(key), T{}).first->second;
+ return emplace(key, T{}).first->second;
+ }
+
+ const T& operator[](const Key& key) const
+ {
+ return at(key);
+ }
+
+ T& at(const Key& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it->second;
+ }
+ }
+
+ throw std::out_of_range("key not found");
+ }
+
+ const T& at(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it->second;
+ }
+ }
+
+ throw std::out_of_range("key not found");
}
size_type erase(const Key& key)
@@ -66,6 +98,74 @@
}
return 0;
}
+
+ iterator erase(iterator pos)
+ {
+ auto it = pos;
+
+ // Since we cannot move const Keys, re-construct them in place
+ for (auto next = it; ++next != this->end(); ++it)
+ {
+ it->~value_type(); // Destroy but keep allocation
+ new (&*it) value_type{std::move(*next)};
+ }
+ Container::pop_back();
+ return pos;
+ }
+
+ size_type count(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ iterator find(const Key& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ const_iterator find(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ std::pair<iterator, bool> insert( value_type&& value )
+ {
+ return emplace(value.first, std::move(value.second));
+ }
+
+ std::pair<iterator, bool> insert( const value_type& value )
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == value.first)
+ {
+ return {it, false};
+ }
+ }
+ Container::push_back(value);
+ return {--this->end(), true};
+ }
};
} // namespace nlohmann
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 6a0f47a..9c66a84 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -16408,6 +16408,7 @@
using mapped_type = T;
using Container = std::vector<std::pair<const Key, T>, Allocator>;
using typename Container::iterator;
+ using typename Container::const_iterator;
using typename Container::size_type;
using typename Container::value_type;
@@ -16420,7 +16421,7 @@
ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() )
: Container{init, alloc} {}
- std::pair<iterator, bool> emplace(key_type&& key, T&& t)
+ std::pair<iterator, bool> emplace(const key_type& key, T&& t)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
@@ -16433,9 +16434,40 @@
return {--this->end(), true};
}
- T& operator[](Key&& key)
+ T& operator[](const Key& key)
{
- return emplace(std::move(key), T{}).first->second;
+ return emplace(key, T{}).first->second;
+ }
+
+ const T& operator[](const Key& key) const
+ {
+ return at(key);
+ }
+
+ T& at(const Key& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it->second;
+ }
+ }
+
+ throw std::out_of_range("key not found");
+ }
+
+ const T& at(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it->second;
+ }
+ }
+
+ throw std::out_of_range("key not found");
}
size_type erase(const Key& key)
@@ -16456,6 +16488,74 @@
}
return 0;
}
+
+ iterator erase(iterator pos)
+ {
+ auto it = pos;
+
+ // Since we cannot move const Keys, re-construct them in place
+ for (auto next = it; ++next != this->end(); ++it)
+ {
+ it->~value_type(); // Destroy but keep allocation
+ new (&*it) value_type{std::move(*next)};
+ }
+ Container::pop_back();
+ return pos;
+ }
+
+ size_type count(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ iterator find(const Key& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ const_iterator find(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ std::pair<iterator, bool> insert( value_type&& value )
+ {
+ return emplace(value.first, std::move(value.second));
+ }
+
+ std::pair<iterator, bool> insert( const value_type& value )
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == value.first)
+ {
+ return {it, false};
+ }
+ }
+ Container::push_back(value);
+ return {--this->end(), true};
+ }
};
} // namespace nlohmann
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 76ba31d..3d8bceb 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -126,10 +126,12 @@
src/unit-msgpack.cpp
src/unit-noexcept.cpp
src/unit-ordered_json.cpp
+ src/unit-ordered_map.cpp
src/unit-pointer_access.cpp
src/unit-readme.cpp
src/unit-reference_access.cpp
- src/unit-regression.cpp
+ src/unit-regression1.cpp
+ src/unit-regression2.cpp
src/unit-serialization.cpp
src/unit-testsuites.cpp
src/unit-to_chars.cpp
diff --git a/test/src/unit-ordered_map.cpp b/test/src/unit-ordered_map.cpp
new file mode 100644
index 0000000..49937aa
--- /dev/null
+++ b/test/src/unit-ordered_map.cpp
@@ -0,0 +1,292 @@
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 3.9.0
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+SPDX-License-Identifier: MIT
+Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "doctest_compatibility.h"
+
+#include <nlohmann/json.hpp>
+using nlohmann::ordered_map;
+
+
+TEST_CASE("ordered_map")
+{
+ SECTION("constructor")
+ {
+ SECTION("constructor from iterator range")
+ {
+ std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
+ ordered_map<std::string, std::string> om(m.begin(), m.end());
+ CHECK(om.size() == 3);
+ }
+
+ SECTION("copy assignment")
+ {
+ std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
+ ordered_map<std::string, std::string> om(m.begin(), m.end());
+ const auto com = om;
+ CHECK(com.size() == 3);
+ }
+ }
+
+ SECTION("at")
+ {
+ std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
+ ordered_map<std::string, std::string> om(m.begin(), m.end());
+ const auto com = om;
+
+ SECTION("with Key&&")
+ {
+ CHECK(om.at(std::string("eins")) == std::string("one"));
+ CHECK(com.at(std::string("eins")) == std::string("one"));
+ CHECK_THROWS_AS(om.at(std::string("vier")), std::out_of_range);
+ CHECK_THROWS_AS(com.at(std::string("vier")), std::out_of_range);
+ }
+
+ SECTION("with const Key&&")
+ {
+ const std::string eins = "eins";
+ const std::string vier = "vier";
+ CHECK(om.at(eins) == std::string("one"));
+ CHECK(com.at(eins) == std::string("one"));
+ CHECK_THROWS_AS(om.at(vier), std::out_of_range);
+ CHECK_THROWS_AS(com.at(vier), std::out_of_range);
+ }
+
+ SECTION("with string literal")
+ {
+ CHECK(om.at("eins") == std::string("one"));
+ CHECK(com.at("eins") == std::string("one"));
+ CHECK_THROWS_AS(om.at("vier"), std::out_of_range);
+ CHECK_THROWS_AS(com.at("vier"), std::out_of_range);
+ }
+ }
+
+ SECTION("operator[]")
+ {
+ std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
+ ordered_map<std::string, std::string> om(m.begin(), m.end());
+ const auto com = om;
+
+ SECTION("with Key&&")
+ {
+ CHECK(om[std::string("eins")] == std::string("one"));
+ CHECK(com[std::string("eins")] == std::string("one"));
+
+ CHECK(om[std::string("vier")] == std::string(""));
+ CHECK(om.size() == 4);
+ }
+
+ SECTION("with const Key&&")
+ {
+ const std::string eins = "eins";
+ const std::string vier = "vier";
+
+ CHECK(om[eins] == std::string("one"));
+ CHECK(com[eins] == std::string("one"));
+
+ CHECK(om[vier] == std::string(""));
+ CHECK(om.size() == 4);
+ }
+
+ SECTION("with string literal")
+ {
+ CHECK(om["eins"] == std::string("one"));
+ CHECK(com["eins"] == std::string("one"));
+
+ CHECK(om["vier"] == std::string(""));
+ CHECK(om.size() == 4);
+ }
+ }
+
+ SECTION("erase")
+ {
+ ordered_map<std::string, std::string> om;
+ om["eins"] = "one";
+ om["zwei"] = "two";
+ om["drei"] = "three";
+
+ {
+ auto it = om.begin();
+ CHECK(it->first == "eins");
+ ++it;
+ CHECK(it->first == "zwei");
+ ++it;
+ CHECK(it->first == "drei");
+ ++it;
+ CHECK(it == om.end());
+ }
+
+ SECTION("with Key&&")
+ {
+ CHECK(om.size() == 3);
+ CHECK(om.erase(std::string("eins")) == 1);
+ CHECK(om.size() == 2);
+ CHECK(om.erase(std::string("vier")) == 0);
+ CHECK(om.size() == 2);
+
+ auto it = om.begin();
+ CHECK(it->first == "zwei");
+ ++it;
+ CHECK(it->first == "drei");
+ ++it;
+ CHECK(it == om.end());
+ }
+
+ SECTION("with const Key&&")
+ {
+ const std::string eins = "eins";
+ const std::string vier = "vier";
+ CHECK(om.size() == 3);
+ CHECK(om.erase(eins) == 1);
+ CHECK(om.size() == 2);
+ CHECK(om.erase(vier) == 0);
+ CHECK(om.size() == 2);
+
+ auto it = om.begin();
+ CHECK(it->first == "zwei");
+ ++it;
+ CHECK(it->first == "drei");
+ ++it;
+ CHECK(it == om.end());
+ }
+
+ SECTION("with string literal")
+ {
+ CHECK(om.size() == 3);
+ CHECK(om.erase("eins") == 1);
+ CHECK(om.size() == 2);
+ CHECK(om.erase("vier") == 0);
+ CHECK(om.size() == 2);
+
+ auto it = om.begin();
+ CHECK(it->first == "zwei");
+ ++it;
+ CHECK(it->first == "drei");
+ ++it;
+ CHECK(it == om.end());
+ }
+
+ SECTION("with iterator")
+ {
+ CHECK(om.size() == 3);
+ CHECK(om.begin()->first == "eins");
+ CHECK(std::next(om.begin(), 1)->first == "zwei");
+ CHECK(std::next(om.begin(), 2)->first == "drei");
+
+ auto it = om.erase(om.begin());
+ CHECK(it->first == "zwei");
+ CHECK(om.size() == 2);
+
+ auto it2 = om.begin();
+ CHECK(it2->first == "zwei");
+ ++it2;
+ CHECK(it2->first == "drei");
+ ++it2;
+ CHECK(it2 == om.end());
+ }
+ }
+
+ SECTION("count")
+ {
+ ordered_map<std::string, std::string> om;
+ om["eins"] = "one";
+ om["zwei"] = "two";
+ om["drei"] = "three";
+
+ const std::string eins("eins");
+ const std::string vier("vier");
+ CHECK(om.count("eins") == 1);
+ CHECK(om.count(std::string("eins")) == 1);
+ CHECK(om.count(eins) == 1);
+ CHECK(om.count("vier") == 0);
+ CHECK(om.count(std::string("vier")) == 0);
+ CHECK(om.count(vier) == 0);
+ }
+
+ SECTION("find")
+ {
+ ordered_map<std::string, std::string> om;
+ om["eins"] = "one";
+ om["zwei"] = "two";
+ om["drei"] = "three";
+ const auto com = om;
+
+ const std::string eins("eins");
+ const std::string vier("vier");
+ CHECK(om.find("eins") == om.begin());
+ CHECK(om.find(std::string("eins")) == om.begin());
+ CHECK(om.find(eins) == om.begin());
+ CHECK(om.find("vier") == om.end());
+ CHECK(om.find(std::string("vier")) == om.end());
+ CHECK(om.find(vier) == om.end());
+
+ CHECK(com.find("eins") == com.begin());
+ CHECK(com.find(std::string("eins")) == com.begin());
+ CHECK(com.find(eins) == com.begin());
+ CHECK(com.find("vier") == com.end());
+ CHECK(com.find(std::string("vier")) == com.end());
+ CHECK(com.find(vier) == com.end());
+ }
+
+ SECTION("insert")
+ {
+ ordered_map<std::string, std::string> om;
+ om["eins"] = "one";
+ om["zwei"] = "two";
+ om["drei"] = "three";
+
+ SECTION("const value_type&")
+ {
+ ordered_map<std::string, std::string>::value_type vt1 {"eins", "1"};
+ ordered_map<std::string, std::string>::value_type vt4 {"vier", "four"};
+
+ auto res1 = om.insert(vt1);
+ CHECK(res1.first == om.begin());
+ CHECK(res1.second == false);
+ CHECK(om.size() == 3);
+
+ auto res4 = om.insert(vt4);
+ CHECK(res4.first == om.begin() + 3);
+ CHECK(res4.second == true);
+ CHECK(om.size() == 4);
+ }
+
+ SECTION("value_type&&")
+ {
+ auto res1 = om.insert({"eins", "1"});
+ CHECK(res1.first == om.begin());
+ CHECK(res1.second == false);
+ CHECK(om.size() == 3);
+
+ auto res4 = om.insert({"vier", "four"});
+ CHECK(res4.first == om.begin() + 3);
+ CHECK(res4.second == true);
+ CHECK(om.size() == 4);
+ }
+ }
+}
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression1.cpp
similarity index 81%
rename from test/src/unit-regression.cpp
rename to test/src/unit-regression1.cpp
index 223466a..97f64e7 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression1.cpp
@@ -128,83 +128,9 @@
j = {{"val", n.val}};
}
};
-
-struct Data
-{
- Data() = default;
- Data(const std::string& a_, const std::string b_) : a(a_), b(b_) {}
- std::string a {};
- std::string b {};
-};
-
-void from_json(const json& j, Data& data)
-{
- j["a"].get_to(data.a);
- j["b"].get_to(data.b);
}
-bool operator==(Data const& lhs, Data const& rhs)
-{
- return lhs.a == rhs.a && lhs.b == rhs.b;
-}
-
-//bool operator!=(Data const& lhs, Data const& rhs)
-//{
-// return !(lhs == rhs);
-//}
-}
-
-/////////////////////////////////////////////////////////////////////
-// for #1021
-/////////////////////////////////////////////////////////////////////
-
-using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
-
-/////////////////////////////////////////////////////////////////////
-// for #1647
-/////////////////////////////////////////////////////////////////////
-namespace
-{
-struct NonDefaultFromJsonStruct { };
-
-inline bool operator== (NonDefaultFromJsonStruct const&, NonDefaultFromJsonStruct const&)
-{
- return true;
-}
-
-enum class for_1647 { one, two };
-
-NLOHMANN_JSON_SERIALIZE_ENUM(for_1647,
-{
- {for_1647::one, "one"},
- {for_1647::two, "two"},
-})
-}
-
-namespace nlohmann
-{
-template <>
-struct adl_serializer<NonDefaultFromJsonStruct>
-{
- static NonDefaultFromJsonStruct from_json (json const&) noexcept
- {
- return {};
- }
-};
-}
-
-/////////////////////////////////////////////////////////////////////
-// for #1805
-/////////////////////////////////////////////////////////////////////
-
-struct NotSerializableData
-{
- int mydata;
- float myfloat;
-};
-
-
-TEST_CASE("regression tests")
+TEST_CASE("regression tests 1")
{
SECTION("issue #60 - Double quotation mark is not parsed correctly")
{
@@ -1651,331 +1577,6 @@
CHECK(ff.x == 3);
nlohmann::json nj = lj; // This line works as expected
}
-
- SECTION("issue #1001 - Fix memory leak during parser callback")
- {
- auto geojsonExample = R"(
- { "type": "FeatureCollection",
- "features": [
- { "type": "Feature",
- "geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
- "properties": {"prop0": "value0"}
- },
- { "type": "Feature",
- "geometry": {
- "type": "LineString",
- "coordinates": [
- [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
- ]
- },
- "properties": {
- "prop0": "value0",
- "prop1": 0.0
- }
- },
- { "type": "Feature",
- "geometry": {
- "type": "Polygon",
- "coordinates": [
- [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
- [100.0, 1.0], [100.0, 0.0] ]
- ]
- },
- "properties": {
- "prop0": "value0",
- "prop1": {"this": "that"}
- }
- }
- ]
- })";
-
- json::parser_callback_t cb = [&](int, json::parse_event_t event, json & parsed)
- {
- // skip uninteresting events
- if (event == json::parse_event_t::value && !parsed.is_primitive())
- {
- return false;
- }
-
- switch (event)
- {
- case json::parse_event_t::key:
- {
- return true;
- }
- case json::parse_event_t::value:
- {
- return false;
- }
- case json::parse_event_t::object_start:
- {
- return true;
- }
- case json::parse_event_t::object_end:
- {
- return false;
- }
- case json::parse_event_t::array_start:
- {
- return true;
- }
- case json::parse_event_t::array_end:
- {
- return false;
- }
-
- default:
- {
- return true;
- }
- }
- };
-
- auto j = json::parse(geojsonExample, cb, true);
- CHECK(j == json());
- }
-
- SECTION("issue #1021 - to/from_msgpack only works with standard typization")
- {
- float_json j = 1000.0;
- CHECK(float_json::from_cbor(float_json::to_cbor(j)) == j);
- CHECK(float_json::from_msgpack(float_json::to_msgpack(j)) == j);
- CHECK(float_json::from_ubjson(float_json::to_ubjson(j)) == j);
-
- float_json j2 = {1000.0, 2000.0, 3000.0};
- CHECK(float_json::from_ubjson(float_json::to_ubjson(j2, true, true)) == j2);
- }
-
- SECTION("issue #1045 - Using STL algorithms with JSON containers with expected results?")
- {
- json diffs = nlohmann::json::array();
- json m1{{"key1", 42}};
- json m2{{"key2", 42}};
- auto p1 = m1.items();
- auto p2 = m2.items();
-
- using it_type = decltype(p1.begin());
-
- std::set_difference(
- p1.begin(), p1.end(),
- p2.begin(), p2.end(),
- std::inserter(diffs, diffs.end()), [&](const it_type & e1, const it_type & e2) -> bool
- {
- using comper_pair = std::pair<std::string, decltype(e1.value())>; // Trying to avoid unneeded copy
- return comper_pair(e1.key(), e1.value()) < comper_pair(e2.key(), e2.value()); // Using pair comper
- });
-
- CHECK(diffs.size() == 1); // Note the change here, was 2
- }
-
-#ifdef JSON_HAS_CPP_17
- SECTION("issue #1292 - Serializing std::variant causes stack overflow")
- {
- static_assert(
- !std::is_constructible<json, std::variant<int, float>>::value, "");
- }
-#endif
-
- SECTION("issue #1299 - compile error in from_json converting to container "
- "with std::pair")
- {
- json j =
- {
- {"1", {{"a", "testa_1"}, {"b", "testb_1"}}},
- {"2", {{"a", "testa_2"}, {"b", "testb_2"}}},
- {"3", {{"a", "testa_3"}, {"b", "testb_3"}}},
- };
-
- std::map<std::string, Data> expected
- {
- {"1", {"testa_1", "testb_1"}},
- {"2", {"testa_2", "testb_2"}},
- {"3", {"testa_3", "testb_3"}},
- };
- const auto data = j.get<decltype(expected)>();
- CHECK(expected == data);
- }
-
- SECTION("issue #1445 - buffer overflow in dumping invalid utf-8 strings")
- {
- SECTION("a bunch of -1, ensure_ascii=true")
- {
- const auto length = 300;
-
- json dump_test;
- dump_test["1"] = std::string(length, -1);
-
- std::string expected = "{\"1\":\"";
- for (int i = 0; i < length; ++i)
- {
- expected += "\\ufffd";
- }
- expected += "\"}";
-
- auto s = dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
- CHECK(s == expected);
- }
- SECTION("a bunch of -2, ensure_ascii=false")
- {
- const auto length = 500;
-
- json dump_test;
- dump_test["1"] = std::string(length, -2);
-
- std::string expected = "{\"1\":\"";
- for (int i = 0; i < length; ++i)
- {
- expected += "\xEF\xBF\xBD";
- }
- expected += "\"}";
-
- auto s = dump_test.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
- CHECK(s == expected);
- }
- SECTION("test case in issue #1445")
- {
- nlohmann::json dump_test;
- const int data[] =
- {
- 109, 108, 103, 125, -122, -53, 115,
- 18, 3, 0, 102, 19, 1, 15,
- -110, 13, -3, -1, -81, 32, 2,
- 0, 0, 0, 0, 0, 0, 0,
- 8, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, -80, 2,
- 0, 0, 96, -118, 46, -116, 46,
- 109, -84, -87, 108, 14, 109, -24,
- -83, 13, -18, -51, -83, -52, -115,
- 14, 6, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
- 64, 3, 0, 0, 0, 35, -74,
- -73, 55, 57, -128, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 33, 0, 0, 0, -96,
- -54, -28, -26
- };
- std::string s;
- for (unsigned i = 0; i < sizeof(data) / sizeof(int); i++)
- {
- s += static_cast<char>(data[i]);
- }
- dump_test["1"] = s;
- dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
- }
- }
-
- SECTION("issue #1447 - Integer Overflow (OSS-Fuzz 12506)")
- {
- json j = json::parse("[-9223372036854775808]");
- CHECK(j.dump() == "[-9223372036854775808]");
- }
-
- SECTION("issue #1708 - minimum value of int64_t can be outputted")
- {
- constexpr auto smallest = (std::numeric_limits<int64_t>::min)();
- json j = smallest;
- CHECK(j.dump() == std::to_string(smallest));
- }
-
- SECTION("issue #1727 - Contains with non-const lvalue json_pointer picks the wrong overload")
- {
- json j = {{"root", {{"settings", {{"logging", true}}}}}};
-
- auto jptr1 = "/root/settings/logging"_json_pointer;
- auto jptr2 = json::json_pointer{"/root/settings/logging"};
-
- CHECK(j.contains(jptr1));
- CHECK(j.contains(jptr2));
- }
-
- SECTION("issue #1647 - compile error when deserializing enum if both non-default from_json and non-member operator== exists for other type")
- {
- {
- json j;
- NonDefaultFromJsonStruct x(j);
- NonDefaultFromJsonStruct y;
- CHECK(x == y);
- }
-
- auto val = nlohmann::json("one").get<for_1647>();
- CHECK(val == for_1647::one);
- json j = val;
- }
-
- SECTION("issue #1715 - json::from_cbor does not respect allow_exceptions = false when input is string literal")
- {
- SECTION("string literal")
- {
- json cbor = json::from_cbor("B", true, false);
- CHECK(cbor.is_discarded());
- }
-
- SECTION("string array")
- {
- const char input[] = { 'B', 0x00 };
- json cbor = json::from_cbor(input, true, false);
- CHECK(cbor.is_discarded());
- }
-
- SECTION("std::string")
- {
- json cbor = json::from_cbor(std::string("B"), true, false);
- CHECK(cbor.is_discarded());
- }
- }
-
- SECTION("issue #1805 - A pair<T1, T2> is json constructible only if T1 and T2 are json constructible")
- {
- static_assert(!std::is_constructible<json, std::pair<std::string, NotSerializableData>>::value, "");
- static_assert(!std::is_constructible<json, std::pair<NotSerializableData, std::string>>::value, "");
- static_assert(std::is_constructible<json, std::pair<int, std::string>>::value, "");
- }
- SECTION("issue #1825 - A tuple<Args..> is json constructible only if all T in Args are json constructible")
- {
- static_assert(!std::is_constructible<json, std::tuple<std::string, NotSerializableData>>::value, "");
- static_assert(!std::is_constructible<json, std::tuple<NotSerializableData, std::string>>::value, "");
- static_assert(std::is_constructible<json, std::tuple<int, std::string>>::value, "");
- }
-
- SECTION("issue #1983 - JSON patch diff for op=add formation is not as per standard (RFC 6902)")
- {
- const auto source = R"({ "foo": [ "1", "2" ] })"_json;
- const auto target = R"({"foo": [ "1", "2", "3" ]})"_json;
- const auto result = json::diff(source, target);
- CHECK(result.dump() == R"([{"op":"add","path":"/foo/-","value":"3"}])");
- }
-
- SECTION("issue #2067 - cannot serialize binary data to text JSON")
- {
- const unsigned char data[] = {0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30};
- json j = json::from_msgpack(data, sizeof(data) / sizeof(data[0]));
- CHECK_NOTHROW(
- j.dump(4, // Indent
- ' ', // Indent char
- false, // Ensure ascii
- json::error_handler_t::strict // Error
- )
- );
- }
-
- SECTION("PR #2181 - regression bug with lvalue")
- {
- // see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060
- json j{{"x", "test"}};
- std::string defval = "default value";
- auto val = j.value("x", defval);
- auto val2 = j.value("y", defval);
- }
-
- SECTION("issue #2293 - eof doesnt cause parsing to stop")
- {
- std::vector<uint8_t> data =
- {
- 0x7B, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x4F, 0x42
- };
- json result = json::from_cbor(data, true, false);
- CHECK(result.is_discarded());
- }
}
#if !defined(JSON_NOEXCEPTION)
diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp
new file mode 100644
index 0000000..d445f06
--- /dev/null
+++ b/test/src/unit-regression2.cpp
@@ -0,0 +1,481 @@
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++ (test suite)
+| | |__ | | | | | | version 3.9.0
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+SPDX-License-Identifier: MIT
+Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "doctest_compatibility.h"
+DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal")
+
+// for some reason including this after the json header leads to linker errors with VS 2017...
+#include <locale>
+
+#define private public
+#include <nlohmann/json.hpp>
+using nlohmann::json;
+#undef private
+
+#include <fstream>
+#include <sstream>
+#include <list>
+#include <cstdio>
+#include <test_data.hpp>
+
+#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+ #define JSON_HAS_CPP_17
+#endif
+
+#ifdef JSON_HAS_CPP_17
+ #include <variant>
+#endif
+
+/////////////////////////////////////////////////////////////////////
+// for #1021
+/////////////////////////////////////////////////////////////////////
+
+using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
+
+/////////////////////////////////////////////////////////////////////
+// for #1647
+/////////////////////////////////////////////////////////////////////
+namespace
+{
+struct NonDefaultFromJsonStruct { };
+
+inline bool operator== (NonDefaultFromJsonStruct const&, NonDefaultFromJsonStruct const&)
+{
+ return true;
+}
+
+enum class for_1647 { one, two };
+
+NLOHMANN_JSON_SERIALIZE_ENUM(for_1647,
+{
+ {for_1647::one, "one"},
+ {for_1647::two, "two"},
+})
+}
+
+/////////////////////////////////////////////////////////////////////
+// for #1299
+/////////////////////////////////////////////////////////////////////
+
+struct Data
+{
+ Data() = default;
+ Data(const std::string& a_, const std::string b_) : a(a_), b(b_) {}
+ std::string a {};
+ std::string b {};
+};
+
+void from_json(const json& j, Data& data)
+{
+ j["a"].get_to(data.a);
+ j["b"].get_to(data.b);
+}
+
+bool operator==(Data const& lhs, Data const& rhs)
+{
+ return lhs.a == rhs.a && lhs.b == rhs.b;
+}
+
+//bool operator!=(Data const& lhs, Data const& rhs)
+//{
+// return !(lhs == rhs);
+//}
+
+namespace nlohmann
+{
+template <>
+struct adl_serializer<NonDefaultFromJsonStruct>
+{
+ static NonDefaultFromJsonStruct from_json (json const&) noexcept
+ {
+ return {};
+ }
+};
+}
+
+/////////////////////////////////////////////////////////////////////
+// for #1805
+/////////////////////////////////////////////////////////////////////
+
+struct NotSerializableData
+{
+ int mydata;
+ float myfloat;
+};
+
+
+TEST_CASE("regression tests 2")
+{
+ SECTION("issue #1001 - Fix memory leak during parser callback")
+ {
+ auto geojsonExample = R"(
+ { "type": "FeatureCollection",
+ "features": [
+ { "type": "Feature",
+ "geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
+ "properties": {"prop0": "value0"}
+ },
+ { "type": "Feature",
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
+ ]
+ },
+ "properties": {
+ "prop0": "value0",
+ "prop1": 0.0
+ }
+ },
+ { "type": "Feature",
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
+ [100.0, 1.0], [100.0, 0.0] ]
+ ]
+ },
+ "properties": {
+ "prop0": "value0",
+ "prop1": {"this": "that"}
+ }
+ }
+ ]
+ })";
+
+ json::parser_callback_t cb = [&](int, json::parse_event_t event, json & parsed)
+ {
+ // skip uninteresting events
+ if (event == json::parse_event_t::value && !parsed.is_primitive())
+ {
+ return false;
+ }
+
+ switch (event)
+ {
+ case json::parse_event_t::key:
+ {
+ return true;
+ }
+ case json::parse_event_t::value:
+ {
+ return false;
+ }
+ case json::parse_event_t::object_start:
+ {
+ return true;
+ }
+ case json::parse_event_t::object_end:
+ {
+ return false;
+ }
+ case json::parse_event_t::array_start:
+ {
+ return true;
+ }
+ case json::parse_event_t::array_end:
+ {
+ return false;
+ }
+
+ default:
+ {
+ return true;
+ }
+ }
+ };
+
+ auto j = json::parse(geojsonExample, cb, true);
+ CHECK(j == json());
+ }
+
+ SECTION("issue #1021 - to/from_msgpack only works with standard typization")
+ {
+ float_json j = 1000.0;
+ CHECK(float_json::from_cbor(float_json::to_cbor(j)) == j);
+ CHECK(float_json::from_msgpack(float_json::to_msgpack(j)) == j);
+ CHECK(float_json::from_ubjson(float_json::to_ubjson(j)) == j);
+
+ float_json j2 = {1000.0, 2000.0, 3000.0};
+ CHECK(float_json::from_ubjson(float_json::to_ubjson(j2, true, true)) == j2);
+ }
+
+ SECTION("issue #1045 - Using STL algorithms with JSON containers with expected results?")
+ {
+ json diffs = nlohmann::json::array();
+ json m1{{"key1", 42}};
+ json m2{{"key2", 42}};
+ auto p1 = m1.items();
+ auto p2 = m2.items();
+
+ using it_type = decltype(p1.begin());
+
+ std::set_difference(
+ p1.begin(), p1.end(),
+ p2.begin(), p2.end(),
+ std::inserter(diffs, diffs.end()), [&](const it_type & e1, const it_type & e2) -> bool
+ {
+ using comper_pair = std::pair<std::string, decltype(e1.value())>; // Trying to avoid unneeded copy
+ return comper_pair(e1.key(), e1.value()) < comper_pair(e2.key(), e2.value()); // Using pair comper
+ });
+
+ CHECK(diffs.size() == 1); // Note the change here, was 2
+ }
+
+#ifdef JSON_HAS_CPP_17
+ SECTION("issue #1292 - Serializing std::variant causes stack overflow")
+ {
+ static_assert(
+ !std::is_constructible<json, std::variant<int, float>>::value, "");
+ }
+#endif
+
+ SECTION("issue #1299 - compile error in from_json converting to container "
+ "with std::pair")
+ {
+ json j =
+ {
+ {"1", {{"a", "testa_1"}, {"b", "testb_1"}}},
+ {"2", {{"a", "testa_2"}, {"b", "testb_2"}}},
+ {"3", {{"a", "testa_3"}, {"b", "testb_3"}}},
+ };
+
+ std::map<std::string, Data> expected
+ {
+ {"1", {"testa_1", "testb_1"}},
+ {"2", {"testa_2", "testb_2"}},
+ {"3", {"testa_3", "testb_3"}},
+ };
+ const auto data = j.get<decltype(expected)>();
+ CHECK(expected == data);
+ }
+
+ SECTION("issue #1445 - buffer overflow in dumping invalid utf-8 strings")
+ {
+ SECTION("a bunch of -1, ensure_ascii=true")
+ {
+ const auto length = 300;
+
+ json dump_test;
+ dump_test["1"] = std::string(length, -1);
+
+ std::string expected = "{\"1\":\"";
+ for (int i = 0; i < length; ++i)
+ {
+ expected += "\\ufffd";
+ }
+ expected += "\"}";
+
+ auto s = dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
+ CHECK(s == expected);
+ }
+ SECTION("a bunch of -2, ensure_ascii=false")
+ {
+ const auto length = 500;
+
+ json dump_test;
+ dump_test["1"] = std::string(length, -2);
+
+ std::string expected = "{\"1\":\"";
+ for (int i = 0; i < length; ++i)
+ {
+ expected += "\xEF\xBF\xBD";
+ }
+ expected += "\"}";
+
+ auto s = dump_test.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
+ CHECK(s == expected);
+ }
+ SECTION("test case in issue #1445")
+ {
+ nlohmann::json dump_test;
+ const int data[] =
+ {
+ 109, 108, 103, 125, -122, -53, 115,
+ 18, 3, 0, 102, 19, 1, 15,
+ -110, 13, -3, -1, -81, 32, 2,
+ 0, 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -80, 2,
+ 0, 0, 96, -118, 46, -116, 46,
+ 109, -84, -87, 108, 14, 109, -24,
+ -83, 13, -18, -51, -83, -52, -115,
+ 14, 6, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,
+ 64, 3, 0, 0, 0, 35, -74,
+ -73, 55, 57, -128, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 33, 0, 0, 0, -96,
+ -54, -28, -26
+ };
+ std::string s;
+ for (unsigned i = 0; i < sizeof(data) / sizeof(int); i++)
+ {
+ s += static_cast<char>(data[i]);
+ }
+ dump_test["1"] = s;
+ dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
+ }
+ }
+
+ SECTION("issue #1447 - Integer Overflow (OSS-Fuzz 12506)")
+ {
+ json j = json::parse("[-9223372036854775808]");
+ CHECK(j.dump() == "[-9223372036854775808]");
+ }
+
+ SECTION("issue #1708 - minimum value of int64_t can be outputted")
+ {
+ constexpr auto smallest = (std::numeric_limits<int64_t>::min)();
+ json j = smallest;
+ CHECK(j.dump() == std::to_string(smallest));
+ }
+
+ SECTION("issue #1727 - Contains with non-const lvalue json_pointer picks the wrong overload")
+ {
+ json j = {{"root", {{"settings", {{"logging", true}}}}}};
+
+ auto jptr1 = "/root/settings/logging"_json_pointer;
+ auto jptr2 = json::json_pointer{"/root/settings/logging"};
+
+ CHECK(j.contains(jptr1));
+ CHECK(j.contains(jptr2));
+ }
+
+ SECTION("issue #1647 - compile error when deserializing enum if both non-default from_json and non-member operator== exists for other type")
+ {
+ {
+ json j;
+ NonDefaultFromJsonStruct x(j);
+ NonDefaultFromJsonStruct y;
+ CHECK(x == y);
+ }
+
+ auto val = nlohmann::json("one").get<for_1647>();
+ CHECK(val == for_1647::one);
+ json j = val;
+ }
+
+ SECTION("issue #1715 - json::from_cbor does not respect allow_exceptions = false when input is string literal")
+ {
+ SECTION("string literal")
+ {
+ json cbor = json::from_cbor("B", true, false);
+ CHECK(cbor.is_discarded());
+ }
+
+ SECTION("string array")
+ {
+ const char input[] = { 'B', 0x00 };
+ json cbor = json::from_cbor(input, true, false);
+ CHECK(cbor.is_discarded());
+ }
+
+ SECTION("std::string")
+ {
+ json cbor = json::from_cbor(std::string("B"), true, false);
+ CHECK(cbor.is_discarded());
+ }
+ }
+
+ SECTION("issue #1805 - A pair<T1, T2> is json constructible only if T1 and T2 are json constructible")
+ {
+ static_assert(!std::is_constructible<json, std::pair<std::string, NotSerializableData>>::value, "");
+ static_assert(!std::is_constructible<json, std::pair<NotSerializableData, std::string>>::value, "");
+ static_assert(std::is_constructible<json, std::pair<int, std::string>>::value, "");
+ }
+ SECTION("issue #1825 - A tuple<Args..> is json constructible only if all T in Args are json constructible")
+ {
+ static_assert(!std::is_constructible<json, std::tuple<std::string, NotSerializableData>>::value, "");
+ static_assert(!std::is_constructible<json, std::tuple<NotSerializableData, std::string>>::value, "");
+ static_assert(std::is_constructible<json, std::tuple<int, std::string>>::value, "");
+ }
+
+ SECTION("issue #1983 - JSON patch diff for op=add formation is not as per standard (RFC 6902)")
+ {
+ const auto source = R"({ "foo": [ "1", "2" ] })"_json;
+ const auto target = R"({"foo": [ "1", "2", "3" ]})"_json;
+ const auto result = json::diff(source, target);
+ CHECK(result.dump() == R"([{"op":"add","path":"/foo/-","value":"3"}])");
+ }
+
+ SECTION("issue #2067 - cannot serialize binary data to text JSON")
+ {
+ const unsigned char data[] = {0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30};
+ json j = json::from_msgpack(data, sizeof(data) / sizeof(data[0]));
+ CHECK_NOTHROW(
+ j.dump(4, // Indent
+ ' ', // Indent char
+ false, // Ensure ascii
+ json::error_handler_t::strict // Error
+ )
+ );
+ }
+
+ SECTION("PR #2181 - regression bug with lvalue")
+ {
+ // see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060
+ json j{{"x", "test"}};
+ std::string defval = "default value";
+ auto val = j.value("x", defval);
+ auto val2 = j.value("y", defval);
+ }
+
+ SECTION("issue #2293 - eof doesnt cause parsing to stop")
+ {
+ std::vector<uint8_t> data =
+ {
+ 0x7B, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x4F, 0x42
+ };
+ json result = json::from_cbor(data, true, false);
+ CHECK(result.is_discarded());
+ }
+
+ SECTION("issue #2315 - json.update and vector<pair>does not work with ordered_json")
+ {
+ nlohmann::ordered_json jsonAnimals = {{"animal", "dog"}};
+ nlohmann::ordered_json jsonCat = {{"animal", "cat"}};
+ jsonAnimals.update(jsonCat);
+ CHECK(jsonAnimals["animal"] == "cat");
+
+ auto jsonAnimals_parsed = nlohmann::ordered_json::parse(jsonAnimals.dump());
+ CHECK(jsonAnimals == jsonAnimals_parsed);
+
+ std::vector<std::pair<std::string, int64_t>> intData = {std::make_pair("aaaa", 11),
+ std::make_pair("bbb", 222)
+ };
+ nlohmann::ordered_json jsonObj;
+ for (const auto& data : intData)
+ {
+ jsonObj[data.first] = data.second;
+ }
+ CHECK(jsonObj["aaaa"] == 11);
+ CHECK(jsonObj["bbb"] == 222);
+ }
+}