Shamelessly copy Josiah

This commit is contained in:
jordan4ibanez
2023-11-26 01:34:57 -05:00
parent 650ccf9844
commit d5360a20a6
1080 changed files with 39 additions and 0 deletions

View File

@ -0,0 +1,39 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on
an implementation of the `LLVMFuzzerTestOneInput` function which processes a
passed byte array.
*/
#include <vector> // for vector
#include <cstdint> // for uint8_t
#include <iostream> // for cin
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
int main()
{
#ifdef __AFL_HAVE_MANUAL_CONTROL
while (__AFL_LOOP(1000))
{
#endif
// copy stdin to byte vector
std::vector<uint8_t> vec;
char c = 0;
while (std::cin.get(c))
{
vec.push_back(static_cast<uint8_t>(c));
}
LLVMFuzzerTestOneInput(vec.data(), vec.size());
#ifdef __AFL_HAVE_MANUAL_CONTROL
}
#endif
}

View File

@ -0,0 +1,85 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a parser test suitable for fuzz testing. Given a byte
array data, it performs the following steps:
- j1 = from_bjdata(data)
- vec = to_bjdata(j1)
- j2 = from_bjdata(vec)
- assert(j1 == j2)
- vec2 = to_bjdata(j1, use_size = true, use_type = false)
- j3 = from_bjdata(vec2)
- assert(j1 == j3)
- vec3 = to_bjdata(j1, use_size = true, use_type = true)
- j4 = from_bjdata(vec3)
- assert(j1 == j4)
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers.
*/
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
try
{
// step 1: parse input
std::vector<uint8_t> const vec1(data, data + size);
json const j1 = json::from_bjdata(vec1);
try
{
// step 2.1: round trip without adding size annotations to container types
std::vector<uint8_t> const vec2 = json::to_bjdata(j1, false, false);
// step 2.2: round trip with adding size annotations but without adding type annotations to container types
std::vector<uint8_t> const vec3 = json::to_bjdata(j1, true, false);
// step 2.3: round trip with adding size as well as type annotations to container types
std::vector<uint8_t> const vec4 = json::to_bjdata(j1, true, true);
// parse serialization
json const j2 = json::from_bjdata(vec2);
json const j3 = json::from_bjdata(vec3);
json const j4 = json::from_bjdata(vec4);
// serializations must match
assert(json::to_bjdata(j2, false, false) == vec2);
assert(json::to_bjdata(j3, true, false) == vec3);
assert(json::to_bjdata(j4, true, true) == vec4);
}
catch (const json::parse_error&)
{
// parsing a BJData serialization must not fail
assert(false);
}
}
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const json::type_error&)
{
// type errors can occur during parsing, too
}
catch (const json::out_of_range&)
{
// out of range errors may happen if provided sizes are excessive
}
// return 0 - non-zero return values are reserved for future use
return 0;
}

View File

@ -0,0 +1,74 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a parser test suitable for fuzz testing. Given a byte
array data, it performs the following steps:
- j1 = from_bson(data)
- vec = to_bson(j1)
- j2 = from_bson(vec)
- assert(j1 == j2)
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers.
*/
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
try
{
// step 1: parse input
std::vector<uint8_t> const vec1(data, data + size);
json const j1 = json::from_bson(vec1);
if (j1.is_discarded())
{
return 0;
}
try
{
// step 2: round trip
std::vector<uint8_t> const vec2 = json::to_bson(j1);
// parse serialization
json const j2 = json::from_bson(vec2);
// serializations must match
assert(json::to_bson(j2) == vec2);
}
catch (const json::parse_error&)
{
// parsing a BSON serialization must not fail
assert(false);
}
}
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const json::type_error&)
{
// type errors can occur during parsing, too
}
catch (const json::out_of_range&)
{
// out of range errors can occur during parsing, too
}
// return 0 - non-zero return values are reserved for future use
return 0;
}

View File

@ -0,0 +1,69 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a parser test suitable for fuzz testing. Given a byte
array data, it performs the following steps:
- j1 = from_cbor(data)
- vec = to_cbor(j1)
- j2 = from_cbor(vec)
- assert(j1 == j2)
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers.
*/
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
try
{
// step 1: parse input
std::vector<uint8_t> const vec1(data, data + size);
json const j1 = json::from_cbor(vec1);
try
{
// step 2: round trip
std::vector<uint8_t> const vec2 = json::to_cbor(j1);
// parse serialization
json const j2 = json::from_cbor(vec2);
// serializations must match
assert(json::to_cbor(j2) == vec2);
}
catch (const json::parse_error&)
{
// parsing a CBOR serialization must not fail
assert(false);
}
}
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const json::type_error&)
{
// type errors can occur during parsing, too
}
catch (const json::out_of_range&)
{
// out of range errors can occur during parsing, too
}
// return 0 - non-zero return values are reserved for future use
return 0;
}

View File

@ -0,0 +1,70 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a parser test suitable for fuzz testing. Given a byte
array data, it performs the following steps:
- j1 = parse(data)
- s1 = serialize(j1)
- j2 = parse(s1)
- s2 = serialize(j2)
- assert(s1 == s2)
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers.
*/
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
try
{
// step 1: parse input
json const j1 = json::parse(data, data + size);
try
{
// step 2: round trip
// first serialization
std::string const s1 = j1.dump();
// parse serialization
json const j2 = json::parse(s1);
// second serialization
std::string const s2 = j2.dump();
// serializations must match
assert(s1 == s2);
}
catch (const json::parse_error&)
{
// parsing a JSON serialization must not fail
assert(false);
}
}
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const json::out_of_range&)
{
// out of range errors may happen if provided sizes are excessive
}
// return 0 - non-zero return values are reserved for future use
return 0;
}

View File

@ -0,0 +1,69 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a parser test suitable for fuzz testing. Given a byte
array data, it performs the following steps:
- j1 = from_msgpack(data)
- vec = to_msgpack(j1)
- j2 = from_msgpack(vec)
- assert(j1 == j2)
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers.
*/
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
try
{
// step 1: parse input
std::vector<uint8_t> const vec1(data, data + size);
json const j1 = json::from_msgpack(vec1);
try
{
// step 2: round trip
std::vector<uint8_t> const vec2 = json::to_msgpack(j1);
// parse serialization
json const j2 = json::from_msgpack(vec2);
// serializations must match
assert(json::to_msgpack(j2) == vec2);
}
catch (const json::parse_error&)
{
// parsing a MessagePack serialization must not fail
assert(false);
}
}
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const json::type_error&)
{
// type errors can occur during parsing, too
}
catch (const json::out_of_range&)
{
// out of range errors may happen if provided sizes are excessive
}
// return 0 - non-zero return values are reserved for future use
return 0;
}

View File

@ -0,0 +1,85 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/*
This file implements a parser test suitable for fuzz testing. Given a byte
array data, it performs the following steps:
- j1 = from_ubjson(data)
- vec = to_ubjson(j1)
- j2 = from_ubjson(vec)
- assert(j1 == j2)
- vec2 = to_ubjson(j1, use_size = true, use_type = false)
- j3 = from_ubjson(vec2)
- assert(j1 == j3)
- vec3 = to_ubjson(j1, use_size = true, use_type = true)
- j4 = from_ubjson(vec3)
- assert(j1 == j4)
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
drivers.
*/
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
try
{
// step 1: parse input
std::vector<uint8_t> const vec1(data, data + size);
json const j1 = json::from_ubjson(vec1);
try
{
// step 2.1: round trip without adding size annotations to container types
std::vector<uint8_t> const vec2 = json::to_ubjson(j1, false, false);
// step 2.2: round trip with adding size annotations but without adding type annotations to container types
std::vector<uint8_t> const vec3 = json::to_ubjson(j1, true, false);
// step 2.3: round trip with adding size as well as type annotations to container types
std::vector<uint8_t> const vec4 = json::to_ubjson(j1, true, true);
// parse serialization
json const j2 = json::from_ubjson(vec2);
json const j3 = json::from_ubjson(vec3);
json const j4 = json::from_ubjson(vec4);
// serializations must match
assert(json::to_ubjson(j2, false, false) == vec2);
assert(json::to_ubjson(j3, true, false) == vec3);
assert(json::to_ubjson(j4, true, true) == vec4);
}
catch (const json::parse_error&)
{
// parsing a UBJSON serialization must not fail
assert(false);
}
}
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const json::type_error&)
{
// type errors can occur during parsing, too
}
catch (const json::out_of_range&)
{
// out of range errors may happen if provided sizes are excessive
}
// return 0 - non-zero return values are reserved for future use
return 0;
}

View File

@ -0,0 +1,30 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstdio> // fopen, fclose, FILE
#include <memory> // unique_ptr
#include <test_data.hpp>
#include <doctest.h>
namespace utils
{
inline bool check_testsuite_downloaded()
{
const std::unique_ptr<std::FILE, decltype(&std::fclose)> file(std::fopen(TEST_DATA_DIRECTORY "/README.md", "r"), &std::fclose);
return file != nullptr;
}
TEST_CASE("check test suite is downloaded")
{
REQUIRE_MESSAGE(utils::check_testsuite_downloaded(), "Test data not found in '" TEST_DATA_DIRECTORY "'. Please execute target 'download_test_data' before running this test suite. See <https://github.com/nlohmann/json#execute-unit-tests> for more information.");
}
} // namespace utils

View File

@ -0,0 +1,33 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstdint> // uint8_t
#include <fstream> // ifstream, istreambuf_iterator, ios
#include <vector> // vector
namespace utils
{
inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
{
std::ifstream file(filename, std::ios::binary);
file.unsetf(std::ios::skipws);
file.seekg(0, std::ios::end);
const auto size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<std::uint8_t> byte_vector;
byte_vector.reserve(static_cast<std::size_t>(size));
byte_vector.insert(byte_vector.begin(), std::istream_iterator<std::uint8_t>(file), std::istream_iterator<std::uint8_t>());
return byte_vector;
}
} // namespace utils

View File

@ -0,0 +1,134 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <climits> // SIZE_MAX
#include <limits> // numeric_limits
template <typename OfType, typename T, bool MinInRange, bool MaxInRange>
struct trait_test_arg
{
using of_type = OfType;
using type = T;
static constexpr bool min_in_range = MinInRange;
static constexpr bool max_in_range = MaxInRange;
};
TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test)
{
using nlohmann::detail::value_in_range_of;
using of_type = typename T::of_type;
using type = typename T::type;
constexpr bool min_in_range = T::min_in_range;
constexpr bool max_in_range = T::max_in_range;
type const val_min = std::numeric_limits<type>::min();
type const val_min2 = val_min + 1;
type const val_max = std::numeric_limits<type>::max();
type const val_max2 = val_max - 1;
REQUIRE(CHAR_BIT == 8);
std::string of_type_str;
if (std::is_unsigned<of_type>::value)
{
of_type_str += "u";
}
of_type_str += "int";
of_type_str += std::to_string(sizeof(of_type) * 8);
INFO("of_type := ", of_type_str);
std::string type_str;
if (std::is_unsigned<type>::value)
{
type_str += "u";
}
type_str += "int";
type_str += std::to_string(sizeof(type) * 8);
INFO("type := ", type_str);
CAPTURE(val_min);
CAPTURE(min_in_range);
CAPTURE(val_max);
CAPTURE(max_in_range);
if (min_in_range)
{
CHECK(value_in_range_of<of_type>(val_min));
CHECK(value_in_range_of<of_type>(val_min2));
}
else
{
CHECK_FALSE(value_in_range_of<of_type>(val_min));
CHECK_FALSE(value_in_range_of<of_type>(val_min2));
}
if (max_in_range)
{
CHECK(value_in_range_of<of_type>(val_max));
CHECK(value_in_range_of<of_type>(val_max2));
}
else
{
CHECK_FALSE(value_in_range_of<of_type>(val_max));
CHECK_FALSE(value_in_range_of<of_type>(val_max2));
}
}
TEST_CASE("32bit")
{
REQUIRE(SIZE_MAX == 0xffffffff);
}
TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \
trait_test_arg<std::size_t, std::int32_t, false, true>, \
trait_test_arg<std::size_t, std::uint32_t, true, true>, \
trait_test_arg<std::size_t, std::int64_t, false, false>, \
trait_test_arg<std::size_t, std::uint64_t, true, false>);
TEST_CASE("BJData")
{
SECTION("parse errors")
{
SECTION("array")
{
SECTION("optimized array: negative size")
{
std::vector<uint8_t> const vM = {'[', '$', 'M', '#', '[', 'I', 0x00, 0x20, 'M', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, ']'};
std::vector<uint8_t> const vMX = {'[', '$', 'U', '#', '[', 'M', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 'U', 0x01, ']'};
json _;
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
CHECK(json::from_bjdata(vM, true, false).is_discarded());
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
CHECK(json::from_bjdata(vMX, true, false).is_discarded());
}
SECTION("optimized array: integer value overflow")
{
std::vector<uint8_t> const vL = {'[', '#', 'L', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F};
std::vector<uint8_t> const vM = {'[', '$', 'M', '#', '[', 'I', 0x00, 0x20, 'M', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, ']'};
json _;
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vL), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
CHECK(json::from_bjdata(vL, true, false).is_discarded());
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
CHECK(json::from_bjdata(vM, true, false).is_discarded());
}
}
}
}

View File

@ -0,0 +1,365 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <algorithm>
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("algorithms")
{
json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"};
json j_object = {{"one", 1}, {"two", 2}};
SECTION("non-modifying sequence operations")
{
SECTION("std::all_of")
{
CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value)
{
return !value.empty();
}));
CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value)
{
return value.type() == json::value_t::number_integer;
}));
}
SECTION("std::any_of")
{
CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value)
{
return value.is_string() && value.get<std::string>() == "foo";
}));
CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value)
{
return value.get<int>() > 1;
}));
}
SECTION("std::none_of")
{
CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value)
{
return value.empty();
}));
CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value)
{
return value.get<int>() <= 0;
}));
}
SECTION("std::for_each")
{
SECTION("reading")
{
int sum = 0;
std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value)
{
if (value.is_number())
{
sum += static_cast<int>(value);
}
});
CHECK(sum == 45);
}
SECTION("writing")
{
auto add17 = [](json & value)
{
if (value.is_array())
{
value.push_back(17);
}
};
std::for_each(j_array.begin(), j_array.end(), add17);
CHECK(j_array[6] == json({1, 2, 3, 17}));
}
}
SECTION("std::count")
{
CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1);
}
SECTION("std::count_if")
{
CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value)
{
return (value.is_number());
}) == 3);
CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&)
{
return true;
}) == 9);
}
SECTION("std::mismatch")
{
json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"};
auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin());
CHECK(*res.first == json({{"one", 1}, {"two", 2}}));
CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}}));
}
SECTION("std::equal")
{
SECTION("using operator==")
{
CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin()));
CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin()));
CHECK(!std::equal(j_array.begin(), j_array.end(), j_object.begin()));
}
SECTION("using user-defined comparison")
{
// compare objects only by size of its elements
json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"};
CHECK(!std::equal(j_array.begin(), j_array.end(), j_array2.begin()));
CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(),
[](const json & a, const json & b)
{
return (a.size() == b.size());
}));
}
}
SECTION("std::find")
{
auto it = std::find(j_array.begin(), j_array.end(), json(false));
CHECK(std::distance(j_array.begin(), it) == 5);
}
SECTION("std::find_if")
{
auto it = std::find_if(j_array.begin(), j_array.end(),
[](const json & value)
{
return value.is_boolean();
});
CHECK(std::distance(j_array.begin(), it) == 4);
}
SECTION("std::find_if_not")
{
auto it = std::find_if_not(j_array.begin(), j_array.end(),
[](const json & value)
{
return value.is_number();
});
CHECK(std::distance(j_array.begin(), it) == 3);
}
SECTION("std::adjacent_find")
{
CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end());
CHECK(std::adjacent_find(j_array.begin(), j_array.end(),
[](const json & v1, const json & v2)
{
return v1.type() == v2.type();
}) == j_array.begin());
}
}
SECTION("modifying sequence operations")
{
SECTION("std::reverse")
{
std::reverse(j_array.begin(), j_array.end());
CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13}));
}
SECTION("std::rotate")
{
std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end());
CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13}));
}
SECTION("std::partition")
{
auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v)
{
return v.is_string();
});
CHECK(std::distance(j_array.begin(), it) == 2);
CHECK(!it[2].is_string());
}
}
SECTION("sorting operations")
{
SECTION("std::sort")
{
SECTION("with standard comparison")
{
json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
std::sort(j.begin(), j.end());
CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
}
SECTION("with user-defined comparison")
{
json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr};
std::sort(j.begin(), j.end(), [](const json & a, const json & b)
{
return a.size() < b.size();
});
CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}}));
}
SECTION("sorting an object")
{
json j({{"one", 1}, {"two", 2}});
CHECK_THROWS_WITH_AS(std::sort(j.begin(), j.end()), "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
}
SECTION("std::partial_sort")
{
json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
std::partial_sort(j.begin(), j.begin() + 4, j.end());
CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13}));
}
}
SECTION("set operations")
{
SECTION("std::merge")
{
{
json j1 = {2, 4, 6, 8};
json j2 = {1, 2, 3, 5, 7};
json j3;
std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8}));
}
}
SECTION("std::set_difference")
{
json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
json j2 = {1, 2, 3, 5, 7};
json j3;
std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
CHECK(j3 == json({4, 6, 8}));
}
SECTION("std::set_intersection")
{
json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
json j2 = {1, 2, 3, 5, 7};
json j3;
std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
CHECK(j3 == json({1, 2, 3, 5, 7}));
}
SECTION("std::set_union")
{
json j1 = {2, 4, 6, 8};
json j2 = {1, 2, 3, 5, 7};
json j3;
std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8}));
}
SECTION("std::set_symmetric_difference")
{
json j1 = {2, 4, 6, 8};
json j2 = {1, 2, 3, 5, 7};
json j3;
std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8}));
}
}
SECTION("heap operations")
{
std::make_heap(j_array.begin(), j_array.end());
CHECK(std::is_heap(j_array.begin(), j_array.end()));
std::sort_heap(j_array.begin(), j_array.end());
CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
}
SECTION("iota")
{
SECTION("int")
{
json json_arr = {0, 5, 2, 4, 10, 20, 30, 40, 50, 1};
std::iota(json_arr.begin(), json_arr.end(), 0);
CHECK(json_arr == json({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
}
SECTION("double")
{
json json_arr = {0.5, 1.5, 1.3, 4.1, 10.2, 20.5, 30.6, 40.1, 50.22, 1.5};
std::iota(json_arr.begin(), json_arr.end(), 0.5);
CHECK(json_arr == json({0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5}));
}
SECTION("char")
{
json json_arr = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1'};
std::iota(json_arr.begin(), json_arr.end(), '0');
CHECK(json_arr == json({'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}));
}
}
SECTION("copy")
{
SECTION("copy without if")
{
json dest_arr;
const json source_arr = {1, 2, 3, 4};
std::copy(source_arr.begin(), source_arr.end(), std::back_inserter(dest_arr));
CHECK(dest_arr == source_arr);
}
SECTION("copy if")
{
json dest_arr;
const json source_arr = {0, 3, 6, 9, 12, 15, 20};
std::copy_if(source_arr.begin(), source_arr.end(), std::back_inserter(dest_arr), [](const json & _value)
{
return _value.get<int>() % 3 == 0;
});
CHECK(dest_arr == json({0, 3, 6, 9, 12, 15}));
}
SECTION("copy n")
{
const json source_arr = {0, 1, 2, 3, 4, 5, 6, 7};
json dest_arr;
const unsigned char numToCopy = 2;
std::copy_n(source_arr.begin(), numToCopy, std::back_inserter(dest_arr));
CHECK(dest_arr == json{0, 1});
}
SECTION("copy n chars")
{
const json source_arr = {'1', '2', '3', '4', '5', '6', '7'};
json dest_arr;
const unsigned char numToCopy = 4;
std::copy_n(source_arr.begin(), numToCopy, std::back_inserter(dest_arr));
CHECK(dest_arr == json{'1', '2', '3', '4'});
}
}
}

View File

@ -0,0 +1,263 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
namespace
{
// special test case to check if memory is leaked if constructor throws
template<class T>
struct bad_allocator : std::allocator<T>
{
using std::allocator<T>::allocator;
bad_allocator() = default;
template<class U> bad_allocator(const bad_allocator<U>& /*unused*/) { }
template<class... Args>
void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
{
throw std::bad_alloc();
}
template <class U>
struct rebind
{
using other = bad_allocator<U>;
};
};
} // namespace
TEST_CASE("bad_alloc")
{
SECTION("bad_alloc")
{
// create JSON type using the throwing allocator
using bad_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
bad_allocator>;
// creating an object should throw
CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
}
}
namespace
{
bool next_construct_fails = false;
bool next_destroy_fails = false;
bool next_deallocate_fails = false;
template<class T>
struct my_allocator : std::allocator<T>
{
using std::allocator<T>::allocator;
template<class... Args>
void construct(T* p, Args&& ... args)
{
if (next_construct_fails)
{
next_construct_fails = false;
throw std::bad_alloc();
}
::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
}
void deallocate(T* p, std::size_t n)
{
if (next_deallocate_fails)
{
next_deallocate_fails = false;
throw std::bad_alloc();
}
std::allocator<T>::deallocate(p, n);
}
void destroy(T* p)
{
if (next_destroy_fails)
{
next_destroy_fails = false;
throw std::bad_alloc();
}
static_cast<void>(p); // fix MSVC's C4100 warning
p->~T();
}
template <class U>
struct rebind
{
using other = my_allocator<U>;
};
};
// allows deletion of raw pointer, usually hold by json_value
template<class T>
void my_allocator_clean_up(T* p)
{
assert(p != nullptr);
my_allocator<T> alloc;
alloc.destroy(p);
alloc.deallocate(p, 1);
}
} // namespace
TEST_CASE("controlled bad_alloc")
{
// create JSON type using the throwing allocator
using my_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
my_allocator>;
SECTION("class json_value")
{
SECTION("json_value(value_t)")
{
SECTION("object")
{
next_construct_fails = false;
auto t = my_json::value_t::object;
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
next_construct_fails = true;
CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
next_construct_fails = false;
}
SECTION("array")
{
next_construct_fails = false;
auto t = my_json::value_t::array;
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
next_construct_fails = true;
CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
next_construct_fails = false;
}
SECTION("string")
{
next_construct_fails = false;
auto t = my_json::value_t::string;
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
next_construct_fails = true;
CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
next_construct_fails = false;
}
}
SECTION("json_value(const string_t&)")
{
next_construct_fails = false;
const my_json::string_t v("foo");
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
next_construct_fails = true;
CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc&);
next_construct_fails = false;
}
}
SECTION("class basic_json")
{
SECTION("basic_json(const CompatibleObjectType&)")
{
next_construct_fails = false;
const std::map<std::string, std::string> v {{"foo", "bar"}};
CHECK_NOTHROW(my_json(v));
next_construct_fails = true;
CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
next_construct_fails = false;
}
SECTION("basic_json(const CompatibleArrayType&)")
{
next_construct_fails = false;
const std::vector<std::string> v {"foo", "bar", "baz"};
CHECK_NOTHROW(my_json(v));
next_construct_fails = true;
CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
next_construct_fails = false;
}
SECTION("basic_json(const typename string_t::value_type*)")
{
next_construct_fails = false;
CHECK_NOTHROW(my_json("foo"));
next_construct_fails = true;
CHECK_THROWS_AS(my_json("foo"), std::bad_alloc&);
next_construct_fails = false;
}
SECTION("basic_json(const typename string_t::value_type*)")
{
next_construct_fails = false;
const std::string s("foo");
CHECK_NOTHROW(my_json(s));
next_construct_fails = true;
CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
next_construct_fails = false;
}
}
}
namespace
{
template<class T>
struct allocator_no_forward : std::allocator<T>
{
allocator_no_forward() = default;
template <class U>
allocator_no_forward(allocator_no_forward<U> /*unused*/) {}
template <class U>
struct rebind
{
using other = allocator_no_forward<U>;
};
template <class... Args>
void construct(T* p, const Args& ... args) noexcept(noexcept(::new (static_cast<void*>(p)) T(args...)))
{
// force copy even if move is available
::new (static_cast<void*>(p)) T(args...);
}
};
} // namespace
TEST_CASE("bad my_allocator::construct")
{
SECTION("my_allocator::construct doesn't forward")
{
using bad_alloc_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
allocator_no_forward>;
bad_alloc_json j;
j["test"] = bad_alloc_json::array_t();
j["test"].push_back("should not leak");
}
}

View File

@ -0,0 +1,322 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2018 Vitaliy Manushkin <agri@akamo.info>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
#include <string>
#include <utility>
/* forward declarations */
class alt_string;
bool operator<(const char* op1, const alt_string& op2) noexcept;
void int_to_string(alt_string& target, std::size_t value);
/*
* This is virtually a string class.
* It covers std::string under the hood.
*/
class alt_string
{
public:
using value_type = std::string::value_type;
static constexpr auto npos = static_cast<std::size_t>(-1);
alt_string(const char* str): str_impl(str) {}
alt_string(const char* str, std::size_t count): str_impl(str, count) {}
alt_string(size_t count, char chr): str_impl(count, chr) {}
alt_string() = default;
template <typename...TParams>
alt_string& append(TParams&& ...params)
{
str_impl.append(std::forward<TParams>(params)...);
return *this;
}
void push_back(char c)
{
str_impl.push_back(c);
}
template <typename op_type>
bool operator==(const op_type& op) const
{
return str_impl == op;
}
bool operator==(const alt_string& op) const
{
return str_impl == op.str_impl;
}
template <typename op_type>
bool operator!=(const op_type& op) const
{
return str_impl != op;
}
bool operator!=(const alt_string& op) const
{
return str_impl != op.str_impl;
}
std::size_t size() const noexcept
{
return str_impl.size();
}
void resize (std::size_t n)
{
str_impl.resize(n);
}
void resize (std::size_t n, char c)
{
str_impl.resize(n, c);
}
template <typename op_type>
bool operator<(const op_type& op) const noexcept
{
return str_impl < op;
}
bool operator<(const alt_string& op) const noexcept
{
return str_impl < op.str_impl;
}
const char* c_str() const
{
return str_impl.c_str();
}
char& operator[](std::size_t index)
{
return str_impl[index];
}
const char& operator[](std::size_t index) const
{
return str_impl[index];
}
char& back()
{
return str_impl.back();
}
const char& back() const
{
return str_impl.back();
}
void clear()
{
str_impl.clear();
}
const value_type* data() const
{
return str_impl.data();
}
bool empty() const
{
return str_impl.empty();
}
std::size_t find(const alt_string& str, std::size_t pos = 0) const
{
return str_impl.find(str.str_impl, pos);
}
std::size_t find_first_of(char c, std::size_t pos = 0) const
{
return str_impl.find_first_of(c, pos);
}
alt_string substr(std::size_t pos = 0, std::size_t count = npos) const
{
const std::string s = str_impl.substr(pos, count);
return {s.data(), s.size()};
}
alt_string& replace(std::size_t pos, std::size_t count, const alt_string& str)
{
str_impl.replace(pos, count, str.str_impl);
return *this;
}
private:
std::string str_impl {};
friend bool operator<(const char* /*op1*/, const alt_string& /*op2*/) noexcept;
};
void int_to_string(alt_string& target, std::size_t value)
{
target = std::to_string(value).c_str();
}
using alt_json = nlohmann::basic_json <
std::map,
std::vector,
alt_string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer >;
bool operator<(const char* op1, const alt_string& op2) noexcept
{
return op1 < op2.str_impl;
}
TEST_CASE("alternative string type")
{
SECTION("dump")
{
{
alt_json doc;
doc["pi"] = 3.141;
alt_string dump = doc.dump();
CHECK(dump == R"({"pi":3.141})");
}
{
alt_json doc;
doc["happy"] = true;
alt_string dump = doc.dump();
CHECK(dump == R"({"happy":true})");
}
{
alt_json doc;
doc["name"] = "I'm Batman";
alt_string dump = doc.dump();
CHECK(dump == R"({"name":"I'm Batman"})");
}
{
alt_json doc;
doc["nothing"] = nullptr;
alt_string dump = doc.dump();
CHECK(dump == R"({"nothing":null})");
}
{
alt_json doc;
doc["answer"]["everything"] = 42;
alt_string dump = doc.dump();
CHECK(dump == R"({"answer":{"everything":42}})");
}
{
alt_json doc;
doc["list"] = { 1, 0, 2 };
alt_string dump = doc.dump();
CHECK(dump == R"({"list":[1,0,2]})");
}
{
alt_json doc;
doc["object"] = { {"currency", "USD"}, {"value", 42.99} };
alt_string dump = doc.dump();
CHECK(dump == R"({"object":{"currency":"USD","value":42.99}})");
}
}
SECTION("parse")
{
auto doc = alt_json::parse(R"({"foo": "bar"})");
alt_string dump = doc.dump();
CHECK(dump == R"({"foo":"bar"})");
}
SECTION("items")
{
auto doc = alt_json::parse(R"({"foo": "bar"})");
for (const auto& item : doc.items())
{
CHECK(item.key() == "foo");
CHECK(item.value() == "bar");
}
auto doc_array = alt_json::parse(R"(["foo", "bar"])");
for (const auto& item : doc_array.items())
{
if (item.key() == "0" )
{
CHECK( item.value() == "foo" );
}
else if (item.key() == "1" )
{
CHECK(item.value() == "bar");
}
else
{
CHECK(false);
}
}
}
SECTION("equality")
{
alt_json doc;
doc["Who are you?"] = "I'm Batman";
CHECK("I'm Batman" == doc["Who are you?"]);
CHECK(doc["Who are you?"] == "I'm Batman");
CHECK_FALSE("I'm Batman" != doc["Who are you?"]);
CHECK_FALSE(doc["Who are you?"] != "I'm Batman");
CHECK("I'm Bruce Wayne" != doc["Who are you?"]);
CHECK(doc["Who are you?"] != "I'm Bruce Wayne");
CHECK_FALSE("I'm Bruce Wayne" == doc["Who are you?"]);
CHECK_FALSE(doc["Who are you?"] == "I'm Bruce Wayne");
{
const alt_json& const_doc = doc;
CHECK("I'm Batman" == const_doc["Who are you?"]);
CHECK(const_doc["Who are you?"] == "I'm Batman");
CHECK_FALSE("I'm Batman" != const_doc["Who are you?"]);
CHECK_FALSE(const_doc["Who are you?"] != "I'm Batman");
CHECK("I'm Bruce Wayne" != const_doc["Who are you?"]);
CHECK(const_doc["Who are you?"] != "I'm Bruce Wayne");
CHECK_FALSE("I'm Bruce Wayne" == const_doc["Who are you?"]);
CHECK_FALSE(const_doc["Who are you?"] == "I'm Bruce Wayne");
}
}
SECTION("JSON pointer")
{
// conversion from json to alt_json fails to compile (see #3425);
// attempted fix(*) produces: [[['b','a','r'],['b','a','z']]] (with each char being an integer)
// (*) disable implicit conversion for json_refs of any basic_json type
// alt_json j = R"(
// {
// "foo": ["bar", "baz"]
// }
// )"_json;
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]);
CHECK(j.at(alt_json::json_pointer("/foo/1")) == j["foo"][1]);
}
}

View File

@ -0,0 +1,48 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// avoid warning when assert does not abort
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wstrict-overflow")
/// global variable to record side effect of assert calls
static int assert_counter;
/// set failure variable to true instead of calling assert(x)
#define JSON_ASSERT(x) {if (!(x)) ++assert_counter; }
#include <nlohmann/json.hpp>
using nlohmann::json;
// the test assumes exceptions to work
#if !defined(JSON_NOEXCEPTION)
TEST_CASE("JSON_ASSERT(x)")
{
SECTION("basic_json(first, second)")
{
assert_counter = 0;
CHECK(assert_counter == 0);
const json::iterator it{};
json j;
// in case assertions do not abort execution, an exception is thrown
CHECK_THROWS_WITH_AS(json(it, j.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator);
// check that assertion actually happened
CHECK(assert_counter == 1);
}
}
#endif
DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,211 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <fstream>
#include "make_test_data_available.hpp"
TEST_CASE("Binary Formats" * doctest::skip())
{
SECTION("canada.json")
{
const auto* filename = TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json";
const json j = json::parse(std::ifstream(filename));
const auto json_size = j.dump().size();
const auto bjdata_1_size = json::to_bjdata(j).size();
const auto bjdata_2_size = json::to_bjdata(j, true).size();
const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
const auto bson_size = json::to_bson(j).size();
const auto cbor_size = json::to_cbor(j).size();
const auto msgpack_size = json::to_msgpack(j).size();
const auto ubjson_1_size = json::to_ubjson(j).size();
const auto ubjson_2_size = json::to_ubjson(j, true).size();
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
CHECK(json_size == 2090303);
CHECK(bjdata_1_size == 1112030);
CHECK(bjdata_2_size == 1224148);
CHECK(bjdata_3_size == 1224148);
CHECK(bson_size == 1794522);
CHECK(cbor_size == 1055552);
CHECK(msgpack_size == 1056145);
CHECK(ubjson_1_size == 1112030);
CHECK(ubjson_2_size == 1224148);
CHECK(ubjson_3_size == 1169069);
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(53.199));
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(58.563));
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(58.563));
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(85.849));
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(50.497));
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(50.526));
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(53.199));
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(58.563));
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(55.928));
}
SECTION("twitter.json")
{
const auto* filename = TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json";
const json j = json::parse(std::ifstream(filename));
const auto json_size = j.dump().size();
const auto bjdata_1_size = json::to_bjdata(j).size();
const auto bjdata_2_size = json::to_bjdata(j, true).size();
const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
const auto bson_size = json::to_bson(j).size();
const auto cbor_size = json::to_cbor(j).size();
const auto msgpack_size = json::to_msgpack(j).size();
const auto ubjson_1_size = json::to_ubjson(j).size();
const auto ubjson_2_size = json::to_ubjson(j, true).size();
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
CHECK(json_size == 466906);
CHECK(bjdata_1_size == 425342);
CHECK(bjdata_2_size == 429970);
CHECK(bjdata_3_size == 429970);
CHECK(bson_size == 444568);
CHECK(cbor_size == 402814);
CHECK(msgpack_size == 401510);
CHECK(ubjson_1_size == 426160);
CHECK(ubjson_2_size == 430788);
CHECK(ubjson_3_size == 430798);
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(91.097));
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(92.089));
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(92.089));
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(95.215));
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(86.273));
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(85.993));
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(91.273));
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(92.264));
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(92.266));
}
SECTION("citm_catalog.json")
{
const auto* filename = TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json";
const json j = json::parse(std::ifstream(filename));
const auto json_size = j.dump().size();
const auto bjdata_1_size = json::to_bjdata(j).size();
const auto bjdata_2_size = json::to_bjdata(j, true).size();
const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
const auto bson_size = json::to_bson(j).size();
const auto cbor_size = json::to_cbor(j).size();
const auto msgpack_size = json::to_msgpack(j).size();
const auto ubjson_1_size = json::to_ubjson(j).size();
const auto ubjson_2_size = json::to_ubjson(j, true).size();
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
CHECK(json_size == 500299);
CHECK(bjdata_1_size == 390781);
CHECK(bjdata_2_size == 433557);
CHECK(bjdata_3_size == 432964);
CHECK(bson_size == 479430);
CHECK(cbor_size == 342373);
CHECK(msgpack_size == 342473);
CHECK(ubjson_1_size == 391463);
CHECK(ubjson_2_size == 434239);
CHECK(ubjson_3_size == 425073);
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(78.109));
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(86.659));
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(86.541));
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(95.828));
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(68.433));
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(68.453));
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(78.245));
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(86.795));
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(84.963));
}
SECTION("jeopardy.json")
{
const auto* filename = TEST_DATA_DIRECTORY "/jeopardy/jeopardy.json";
json j = json::parse(std::ifstream(filename));
const auto json_size = j.dump().size();
const auto bjdata_1_size = json::to_bjdata(j).size();
const auto bjdata_2_size = json::to_bjdata(j, true).size();
const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
const auto bson_size = json::to_bson({{"", j}}).size(); // wrap array in object for BSON
const auto cbor_size = json::to_cbor(j).size();
const auto msgpack_size = json::to_msgpack(j).size();
const auto ubjson_1_size = json::to_ubjson(j).size();
const auto ubjson_2_size = json::to_ubjson(j, true).size();
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
CHECK(json_size == 52508728);
CHECK(bjdata_1_size == 50710965);
CHECK(bjdata_2_size == 51144830);
CHECK(bjdata_3_size == 51144830);
CHECK(bson_size == 56008520);
CHECK(cbor_size == 46187320);
CHECK(msgpack_size == 46158575);
CHECK(ubjson_1_size == 50710965);
CHECK(ubjson_2_size == 51144830);
CHECK(ubjson_3_size == 49861422);
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(96.576));
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(97.402));
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(97.402));
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(106.665));
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(87.961));
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(87.906));
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(96.576));
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(97.402));
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(94.958));
}
SECTION("sample.json")
{
const auto* filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json";
const json j = json::parse(std::ifstream(filename));
const auto json_size = j.dump().size();
const auto bjdata_1_size = json::to_bjdata(j).size();
const auto bjdata_2_size = json::to_bjdata(j, true).size();
const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
// BSON cannot process the file as it contains code point U+0000
const auto cbor_size = json::to_cbor(j).size();
const auto msgpack_size = json::to_msgpack(j).size();
const auto ubjson_1_size = json::to_ubjson(j).size();
const auto ubjson_2_size = json::to_ubjson(j, true).size();
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
CHECK(json_size == 168677);
CHECK(bjdata_1_size == 148695);
CHECK(bjdata_2_size == 150569);
CHECK(bjdata_3_size == 150569);
CHECK(cbor_size == 147095);
CHECK(msgpack_size == 147017);
CHECK(ubjson_1_size == 148695);
CHECK(ubjson_2_size == 150569);
CHECK(ubjson_3_size == 150883);
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(88.153));
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(89.264));
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(89.264));
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(87.205));
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(87.158));
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(88.153));
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(89.264));
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(89.450));
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("byte_container_with_subtype")
{
using subtype_type = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>::subtype_type;
SECTION("empty container")
{
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container;
CHECK(!container.has_subtype());
CHECK(container.subtype() == static_cast<subtype_type>(-1));
container.clear_subtype();
CHECK(!container.has_subtype());
CHECK(container.subtype() == static_cast<subtype_type>(-1));
container.set_subtype(42);
CHECK(container.has_subtype());
CHECK(container.subtype() == 42);
}
SECTION("subtyped container")
{
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container({}, 42);
CHECK(container.has_subtype());
CHECK(container.subtype() == 42);
container.clear_subtype();
CHECK(!container.has_subtype());
CHECK(container.subtype() == static_cast<subtype_type>(-1));
}
SECTION("comparisons")
{
std::vector<std::uint8_t> const bytes = {{0xCA, 0xFE, 0xBA, 0xBE}};
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container1;
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container2({}, 42);
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container3(bytes);
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container4(bytes, 42);
CHECK(container1 == container1);
CHECK(container1 != container2);
CHECK(container1 != container3);
CHECK(container1 != container4);
CHECK(container2 != container1);
CHECK(container2 == container2);
CHECK(container2 != container3);
CHECK(container2 != container4);
CHECK(container3 != container1);
CHECK(container3 != container2);
CHECK(container3 == container3);
CHECK(container3 != container4);
CHECK(container4 != container1);
CHECK(container4 != container2);
CHECK(container4 != container3);
CHECK(container4 == container4);
container3.clear();
container4.clear();
CHECK(container1 == container3);
CHECK(container2 == container4);
}
}

View File

@ -0,0 +1,543 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("capacity")
{
SECTION("empty()")
{
SECTION("boolean")
{
json j = true; // NOLINT(misc-const-correctness)
const json j_const = true;
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
SECTION("string")
{
json j = "hello world"; // NOLINT(misc-const-correctness)
const json j_const = "hello world";
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array(); // NOLINT(misc-const-correctness)
const json j_const = json::array();
SECTION("result of empty")
{
CHECK(j.empty() == true);
CHECK(j_const.empty() == true);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
SECTION("filled array")
{
json j = {1, 2, 3}; // NOLINT(misc-const-correctness)
const json j_const = {1, 2, 3};
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object(); // NOLINT(misc-const-correctness)
const json j_const = json::object();
SECTION("result of empty")
{
CHECK(j.empty() == true);
CHECK(j_const.empty() == true);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}}; // NOLINT(misc-const-correctness)
const json j_const = {{"one", 1}, {"two", 2}, {"three", 3}};
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
}
SECTION("number (integer)")
{
json j = -23; // NOLINT(misc-const-correctness)
const json j_const = -23;
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
SECTION("number (unsigned)")
{
json j = 23u; // NOLINT(misc-const-correctness)
const json j_const = 23u;
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
SECTION("number (float)")
{
json j = 23.42; // NOLINT(misc-const-correctness)
const json j_const = 23.42;
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
SECTION("null")
{
json j = nullptr; // NOLINT(misc-const-correctness)
const json j_const = nullptr;
SECTION("result of empty")
{
CHECK(j.empty() == true);
CHECK(j_const.empty() == true);
}
SECTION("definition of empty")
{
CHECK(j.empty() == (j.begin() == j.end()));
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
}
}
}
SECTION("size()")
{
SECTION("boolean")
{
json j = true; // NOLINT(misc-const-correctness)
const json j_const = true;
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("string")
{
json j = "hello world"; // NOLINT(misc-const-correctness)
const json j_const = "hello world";
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array(); // NOLINT(misc-const-correctness)
const json j_const = json::array();
SECTION("result of size")
{
CHECK(j.size() == 0);
CHECK(j_const.size() == 0);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("filled array")
{
json j = {1, 2, 3}; // NOLINT(misc-const-correctness)
const json j_const = {1, 2, 3};
SECTION("result of size")
{
CHECK(j.size() == 3);
CHECK(j_const.size() == 3);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object(); // NOLINT(misc-const-correctness)
const json j_const = json::object();
SECTION("result of size")
{
CHECK(j.size() == 0);
CHECK(j_const.size() == 0);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}}; // NOLINT(misc-const-correctness)
const json j_const = {{"one", 1}, {"two", 2}, {"three", 3}};
SECTION("result of size")
{
CHECK(j.size() == 3);
CHECK(j_const.size() == 3);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
}
SECTION("number (integer)")
{
json j = -23; // NOLINT(misc-const-correctness)
const json j_const = -23;
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("number (unsigned)")
{
json j = 23u; // NOLINT(misc-const-correctness)
const json j_const = 23u;
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("number (float)")
{
json j = 23.42; // NOLINT(misc-const-correctness)
const json j_const = 23.42;
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("null")
{
json j = nullptr; // NOLINT(misc-const-correctness)
const json j_const = nullptr;
SECTION("result of size")
{
CHECK(j.size() == 0);
CHECK(j_const.size() == 0);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
}
SECTION("max_size()")
{
SECTION("boolean")
{
json j = true; // NOLINT(misc-const-correctness)
const json j_const = true;
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("string")
{
json j = "hello world"; // NOLINT(misc-const-correctness)
const json j_const = "hello world";
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array(); // NOLINT(misc-const-correctness)
const json j_const = json::array();
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
SECTION("filled array")
{
json j = {1, 2, 3}; // NOLINT(misc-const-correctness)
const json j_const = {1, 2, 3};
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object(); // NOLINT(misc-const-correctness)
const json j_const = json::object();
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}}; // NOLINT(misc-const-correctness)
const json j_const = {{"one", 1}, {"two", 2}, {"three", 3}};
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
}
SECTION("number (integer)")
{
json j = -23; // NOLINT(misc-const-correctness)
const json j_const = -23;
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("number (unsigned)")
{
json j = 23u; // NOLINT(misc-const-correctness)
const json j_const = 23u;
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("number (float)")
{
json j = 23.42; // NOLINT(misc-const-correctness)
const json j_const = 23.42;
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("null")
{
json j = nullptr; // NOLINT(misc-const-correctness)
const json j_const = nullptr;
SECTION("result of max_size")
{
CHECK(j.max_size() == 0);
CHECK(j_const.max_size() == 0);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,393 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("const_iterator class")
{
SECTION("construction")
{
SECTION("constructor")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator const it(&j);
}
SECTION("object")
{
json const j(json::value_t::object);
json::const_iterator const it(&j);
}
SECTION("array")
{
json const j(json::value_t::array);
json::const_iterator const it(&j);
}
}
SECTION("copy assignment")
{
json const j(json::value_t::null);
json::const_iterator const it(&j);
json::const_iterator it2(&j);
it2 = it;
}
SECTION("copy constructor from non-const iterator")
{
SECTION("create from uninitialized iterator")
{
const json::iterator it {};
json::const_iterator const cit(it);
}
SECTION("create from initialized iterator")
{
json j;
const json::iterator it = j.begin();
json::const_iterator const cit(it);
}
}
}
SECTION("initialization")
{
SECTION("set_begin")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator it(&j);
it.set_begin();
CHECK((it == j.cbegin()));
}
SECTION("object")
{
json const j(json::value_t::object);
json::const_iterator it(&j);
it.set_begin();
CHECK((it == j.cbegin()));
}
SECTION("array")
{
json const j(json::value_t::array);
json::const_iterator it(&j);
it.set_begin();
CHECK((it == j.cbegin()));
}
}
SECTION("set_end")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator it(&j);
it.set_end();
CHECK((it == j.cend()));
}
SECTION("object")
{
json const j(json::value_t::object);
json::const_iterator it(&j);
it.set_end();
CHECK((it == j.cend()));
}
SECTION("array")
{
json const j(json::value_t::array);
json::const_iterator it(&j);
it.set_end();
CHECK((it == j.cend()));
}
}
}
SECTION("element access")
{
SECTION("operator*")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator const it = j.cbegin();
CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("number")
{
json const j(17);
json::const_iterator it = j.cbegin();
CHECK(*it == json(17));
it = j.cend();
CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("object")
{
json const j({{"foo", "bar"}});
json::const_iterator const it = j.cbegin();
CHECK(*it == json("bar"));
}
SECTION("array")
{
json const j({1, 2, 3, 4});
json::const_iterator const it = j.cbegin();
CHECK(*it == json(1));
}
}
SECTION("operator->")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator const it = j.cbegin();
CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("number")
{
json const j(17);
json::const_iterator it = j.cbegin();
CHECK(std::string(it->type_name()) == "number");
it = j.cend();
CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("object")
{
json const j({{"foo", "bar"}});
json::const_iterator const it = j.cbegin();
CHECK(std::string(it->type_name()) == "string");
}
SECTION("array")
{
json const j({1, 2, 3, 4});
json::const_iterator const it = j.cbegin();
CHECK(std::string(it->type_name()) == "number");
}
}
}
SECTION("increment/decrement")
{
SECTION("post-increment")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json const j(17);
json::const_iterator it = j.cbegin();
CHECK((it.m_it.primitive_iterator.m_it == 0));
it++;
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json const j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
it++;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
}
SECTION("array")
{
json const j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
}
}
SECTION("pre-increment")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json const j(17);
json::const_iterator it = j.cbegin();
CHECK((it.m_it.primitive_iterator.m_it == 0));
++it;
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json const j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
++it;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
}
SECTION("array")
{
json const j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
}
}
SECTION("post-decrement")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator const it = j.cend();
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json const j(17);
json::const_iterator it = j.cend();
CHECK((it.m_it.primitive_iterator.m_it == 1));
it--;
CHECK((it.m_it.primitive_iterator.m_it == 0));
it--;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json const j({{"foo", "bar"}});
json::const_iterator it = j.cend();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
it--;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
}
SECTION("array")
{
json const j({1, 2, 3, 4});
json::const_iterator it = j.cend();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
}
}
SECTION("pre-decrement")
{
SECTION("null")
{
json const j(json::value_t::null);
json::const_iterator const it = j.cend();
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json const j(17);
json::const_iterator it = j.cend();
CHECK((it.m_it.primitive_iterator.m_it == 1));
--it;
CHECK((it.m_it.primitive_iterator.m_it == 0));
--it;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json const j({{"foo", "bar"}});
json::const_iterator it = j.cend();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
--it;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
}
SECTION("array")
{
json const j({1, 2, 3, 4});
json::const_iterator it = j.cend();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
}
}
}
}

View File

@ -0,0 +1,468 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
template<typename Iter>
using can_post_increment_temporary = decltype((std::declval<Iter>()++)++);
template<typename Iter>
using can_post_decrement_temporary = decltype((std::declval<Iter>()--)--);
TEST_CASE("iterator class")
{
SECTION("construction")
{
SECTION("constructor")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator const it(&j);
}
SECTION("object")
{
json j(json::value_t::object);
json::iterator const it(&j);
}
SECTION("array")
{
json j(json::value_t::array);
json::iterator const it(&j);
}
}
SECTION("copy assignment")
{
json j(json::value_t::null);
json::iterator const it(&j);
json::iterator it2(&j);
it2 = it;
}
}
SECTION("initialization")
{
SECTION("set_begin")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it(&j);
it.set_begin();
CHECK((it == j.begin()));
}
SECTION("object")
{
json j(json::value_t::object);
json::iterator it(&j);
it.set_begin();
CHECK((it == j.begin()));
}
SECTION("array")
{
json j(json::value_t::array);
json::iterator it(&j);
it.set_begin();
CHECK((it == j.begin()));
}
}
SECTION("set_end")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it(&j);
it.set_end();
CHECK((it == j.end()));
}
SECTION("object")
{
json j(json::value_t::object);
json::iterator it(&j);
it.set_end();
CHECK((it == j.end()));
}
SECTION("array")
{
json j(json::value_t::array);
json::iterator it(&j);
it.set_end();
CHECK((it == j.end()));
}
}
}
SECTION("element access")
{
SECTION("operator*")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator const it = j.begin();
CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(*it == json(17));
it = j.end();
CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator const it = j.begin();
CHECK(*it == json("bar"));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator const it = j.begin();
CHECK(*it == json(1));
}
}
SECTION("operator->")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator const it = j.begin();
CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(std::string(it->type_name()) == "number");
it = j.end();
CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator const it = j.begin();
CHECK(std::string(it->type_name()) == "string");
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator const it = j.begin();
CHECK(std::string(it->type_name()) == "number");
}
}
}
SECTION("increment/decrement")
{
SECTION("post-increment")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK((it.m_it.primitive_iterator.m_it == 0));
it++;
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
it++;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it++;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
}
}
SECTION("pre-increment")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK((it.m_it.primitive_iterator.m_it == 0));
++it;
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
++it;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
++it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
}
}
SECTION("post-decrement")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator const it = j.end();
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK((it.m_it.primitive_iterator.m_it == 1));
it--;
CHECK((it.m_it.primitive_iterator.m_it == 0));
it--;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.end();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
it--;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.end();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
it--;
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
}
}
SECTION("pre-decrement")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator const it = j.end();
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK((it.m_it.primitive_iterator.m_it == 1));
--it;
CHECK((it.m_it.primitive_iterator.m_it == 0));
--it;
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.end();
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->end()));
--it;
CHECK((it.m_it.object_iterator == it.m_object->m_data.m_value.object->begin()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.end();
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
--it;
CHECK((it.m_it.array_iterator == it.m_object->m_data.m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_data.m_value.array->end()));
}
}
}
SECTION("equality-preserving")
{
SECTION("post-increment")
{
SECTION("primitive_iterator_t")
{
using Iter = nlohmann::detail::primitive_iterator_t;
CHECK(std::is_same < decltype(std::declval<Iter&>()++), Iter >::value);
}
SECTION("iter_impl")
{
using Iter = nlohmann::detail::iter_impl<json>;
CHECK(std::is_same < decltype(std::declval<Iter&>()++), Iter >::value);
}
SECTION("json_reverse_iterator")
{
using Base = nlohmann::detail::iter_impl<json>;
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
CHECK(std::is_same < decltype(std::declval<Iter&>()++), Iter >::value);
}
}
SECTION("post-decrement")
{
SECTION("primitive_iterator_t")
{
using Iter = nlohmann::detail::primitive_iterator_t;
CHECK(std::is_same < decltype(std::declval<Iter&>()--), Iter >::value);
}
SECTION("iter_impl")
{
using Iter = nlohmann::detail::iter_impl<json>;
CHECK(std::is_same < decltype(std::declval<Iter&>()--), Iter >::value );
}
SECTION("json_reverse_iterator")
{
using Base = nlohmann::detail::iter_impl<json>;
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
CHECK(std::is_same < decltype(std::declval<Iter&>()--), Iter >::value );
}
}
}
// prevent "accidental mutation of a temporary object"
SECTION("cert-dcl21-cpp")
{
using nlohmann::detail::is_detected;
SECTION("post-increment")
{
SECTION("primitive_iterator_t")
{
using Iter = nlohmann::detail::primitive_iterator_t;
CHECK_FALSE(is_detected<can_post_increment_temporary, Iter&>::value);
}
SECTION("iter_impl")
{
using Iter = nlohmann::detail::iter_impl<json>;
CHECK_FALSE(is_detected<can_post_increment_temporary, Iter&>::value);
}
SECTION("json_reverse_iterator")
{
using Base = nlohmann::detail::iter_impl<json>;
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
CHECK_FALSE(is_detected<can_post_increment_temporary, Iter&>::value);
}
}
SECTION("post-decrement")
{
SECTION("primitive_iterator_t")
{
using Iter = nlohmann::detail::primitive_iterator_t;
CHECK_FALSE(is_detected<can_post_decrement_temporary, Iter&>::value);
}
SECTION("iter_impl")
{
using Iter = nlohmann::detail::iter_impl<json>;
CHECK_FALSE(is_detected<can_post_decrement_temporary, Iter&>::value);
}
SECTION("json_reverse_iterator")
{
using Base = nlohmann::detail::iter_impl<json>;
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
CHECK_FALSE(is_detected<can_post_decrement_temporary, Iter&>::value);
}
}
}
}

View File

@ -0,0 +1,226 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
namespace
{
// shortcut to scan a string literal
json::lexer::token_type scan_string(const char* s, bool ignore_comments = false);
json::lexer::token_type scan_string(const char* s, const bool ignore_comments)
{
auto ia = nlohmann::detail::input_adapter(s);
return nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments).scan(); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
}
} // namespace
std::string get_error_message(const char* s, bool ignore_comments = false);
std::string get_error_message(const char* s, const bool ignore_comments)
{
auto ia = nlohmann::detail::input_adapter(s);
auto lexer = nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
lexer.scan();
return lexer.get_error_message();
}
TEST_CASE("lexer class")
{
SECTION("scan")
{
SECTION("structural characters")
{
CHECK((scan_string("[") == json::lexer::token_type::begin_array));
CHECK((scan_string("]") == json::lexer::token_type::end_array));
CHECK((scan_string("{") == json::lexer::token_type::begin_object));
CHECK((scan_string("}") == json::lexer::token_type::end_object));
CHECK((scan_string(",") == json::lexer::token_type::value_separator));
CHECK((scan_string(":") == json::lexer::token_type::name_separator));
}
SECTION("literal names")
{
CHECK((scan_string("null") == json::lexer::token_type::literal_null));
CHECK((scan_string("true") == json::lexer::token_type::literal_true));
CHECK((scan_string("false") == json::lexer::token_type::literal_false));
}
SECTION("numbers")
{
CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("3") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("4") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("5") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("6") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
}
SECTION("whitespace")
{
// result is end_of_input, because not token is following
CHECK((scan_string(" ") == json::lexer::token_type::end_of_input));
CHECK((scan_string("\t") == json::lexer::token_type::end_of_input));
CHECK((scan_string("\n") == json::lexer::token_type::end_of_input));
CHECK((scan_string("\r") == json::lexer::token_type::end_of_input));
CHECK((scan_string(" \t\n\r\n\t ") == json::lexer::token_type::end_of_input));
}
}
SECTION("token_type_name")
{
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::uninitialized)) == "<uninitialized>"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_true)) == "true literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_false)) == "false literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_null)) == "null literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_string)) == "string literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_unsigned)) == "number literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_integer)) == "number literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_float)) == "number literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_array)) == "'['"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_object)) == "'{'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_array)) == "']'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_object)) == "'}'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::name_separator)) == "':'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_separator)) == "','"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::parse_error)) == "<parse error>"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_of_input)) == "end of input"));
}
SECTION("parse errors on first character")
{
for (int c = 1; c < 128; ++c)
{
// create string from the ASCII code
const auto s = std::string(1, static_cast<char>(c));
// store scan() result
const auto res = scan_string(s.c_str());
CAPTURE(s)
switch (c)
{
// single characters that are valid tokens
case ('['):
case (']'):
case ('{'):
case ('}'):
case (','):
case (':'):
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
{
CHECK((res != json::lexer::token_type::parse_error));
break;
}
// whitespace
case (' '):
case ('\t'):
case ('\n'):
case ('\r'):
{
CHECK((res == json::lexer::token_type::end_of_input));
break;
}
// anything else is not expected
default:
{
CHECK((res == json::lexer::token_type::parse_error));
break;
}
}
}
}
SECTION("very large string")
{
// strings larger than 1024 bytes yield a resize of the lexer's yytext buffer
std::string s("\"");
s += std::string(2048, 'x');
s += "\"";
CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string));
}
SECTION("fail on comments")
{
CHECK((scan_string("/", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/", false) == "invalid literal");
CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/!", false) == "invalid literal");
CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/*", false) == "invalid literal");
CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/**", false) == "invalid literal");
CHECK((scan_string("//", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("//", false) == "invalid literal");
CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/**/", false) == "invalid literal");
CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/** /", false) == "invalid literal");
CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/***/", false) == "invalid literal");
CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/* true */", false) == "invalid literal");
CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/*/**/", false) == "invalid literal");
CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/*/* */", false) == "invalid literal");
}
SECTION("ignore comments")
{
CHECK((scan_string("/", true) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'");
CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'");
CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'");
CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'");
CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input));
CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input));
CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error));
CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'");
CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input));
CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input));
CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input));
CHECK((scan_string("/*/* */", true) == json::lexer::token_type::end_of_input));
CHECK((scan_string("//\n//\n", true) == json::lexer::token_type::end_of_input));
CHECK((scan_string("/**//**//**/", true) == json::lexer::token_type::end_of_input));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,595 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
#if JSON_HAS_THREE_WAY_COMPARISON
// this can be replaced with the doctest stl extension header in version 2.5
namespace doctest
{
template<> struct StringMaker<std::partial_ordering>
{
static String convert(const std::partial_ordering& order)
{
if (order == std::partial_ordering::less)
{
return "std::partial_ordering::less";
}
if (order == std::partial_ordering::equivalent)
{
return "std::partial_ordering::equivalent";
}
if (order == std::partial_ordering::greater)
{
return "std::partial_ordering::greater";
}
if (order == std::partial_ordering::unordered)
{
return "std::partial_ordering::unordered";
}
return "{?}";
}
};
} // namespace doctest
#endif
namespace
{
// helper function to check std::less<json::value_t>
// see https://en.cppreference.com/w/cpp/utility/functional/less
template <typename A, typename B, typename U = std::less<json::value_t>>
bool f(A a, B b, U u = U())
{
return u(a, b);
}
} // namespace
TEST_CASE("lexicographical comparison operators")
{
constexpr auto f_ = false;
constexpr auto _t = true;
constexpr auto nan = std::numeric_limits<json::number_float_t>::quiet_NaN();
#if JSON_HAS_THREE_WAY_COMPARISON
constexpr auto lt = std::partial_ordering::less;
constexpr auto gt = std::partial_ordering::greater;
constexpr auto eq = std::partial_ordering::equivalent;
constexpr auto un = std::partial_ordering::unordered;
#endif
#if JSON_HAS_THREE_WAY_COMPARISON
INFO("using 3-way comparison");
#endif
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
INFO("using legacy comparison");
#endif
//REQUIRE(std::numeric_limits<json::number_float_t>::has_quiet_NaN);
REQUIRE(std::isnan(nan));
SECTION("types")
{
std::vector<json::value_t> j_types =
{
json::value_t::null,
json::value_t::boolean,
json::value_t::number_integer,
json::value_t::number_unsigned,
json::value_t::number_float,
json::value_t::object,
json::value_t::array,
json::value_t::string,
json::value_t::binary,
json::value_t::discarded
};
std::vector<std::vector<bool>> expected_lt =
{
//0 1 2 3 4 5 6 7 8 9
{f_, _t, _t, _t, _t, _t, _t, _t, _t, f_}, // 0
{f_, f_, _t, _t, _t, _t, _t, _t, _t, f_}, // 1
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 2
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 3
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 4
{f_, f_, f_, f_, f_, f_, _t, _t, _t, f_}, // 5
{f_, f_, f_, f_, f_, f_, f_, _t, _t, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
};
SECTION("comparison: less")
{
REQUIRE(expected_lt.size() == j_types.size());
for (size_t i = 0; i < j_types.size(); ++i)
{
REQUIRE(expected_lt[i].size() == j_types.size());
for (size_t j = 0; j < j_types.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check precomputed values
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
CHECK((j_types[i] < j_types[j]) == expected_lt[i][j]);
#else
CHECK(operator<(j_types[i], j_types[j]) == expected_lt[i][j]);
#endif
CHECK(f(j_types[i], j_types[j]) == expected_lt[i][j]);
}
}
}
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
SECTION("comparison: 3-way")
{
std::vector<std::vector<std::partial_ordering>> expected =
{
//0 1 2 3 4 5 6 7 8 9
{eq, lt, lt, lt, lt, lt, lt, lt, lt, un}, // 0
{gt, eq, lt, lt, lt, lt, lt, lt, lt, un}, // 1
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 2
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 3
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 4
{gt, gt, gt, gt, gt, eq, lt, lt, lt, un}, // 5
{gt, gt, gt, gt, gt, gt, eq, lt, lt, un}, // 6
{gt, gt, gt, gt, gt, gt, gt, eq, lt, un}, // 7
{gt, gt, gt, gt, gt, gt, gt, gt, eq, un}, // 8
{un, un, un, un, un, un, un, un, un, un}, // 9
};
// check expected partial_ordering against expected boolean
REQUIRE(expected.size() == expected_lt.size());
for (size_t i = 0; i < expected.size(); ++i)
{
REQUIRE(expected[i].size() == expected_lt[i].size());
for (size_t j = 0; j < expected[i].size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK(std::is_lt(expected[i][j]) == expected_lt[i][j]);
}
}
// check 3-way comparison against expected partial_ordering
REQUIRE(expected.size() == j_types.size());
for (size_t i = 0; i < j_types.size(); ++i)
{
REQUIRE(expected[i].size() == j_types.size());
for (size_t j = 0; j < j_types.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK((j_types[i] <=> j_types[j]) == expected[i][j]); // *NOPAD*
}
}
}
#endif
}
SECTION("values")
{
json j_values =
{
nullptr, nullptr, // 0 1
-17, 42, // 2 3
8u, 13u, // 4 5
3.14159, 23.42, // 6 7
nan, nan, // 8 9
"foo", "bar", // 10 11
true, false, // 12 13
{1, 2, 3}, {"one", "two", "three"}, // 14 15
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}, // 16 17
json::binary({1, 2, 3}), json::binary({1, 2, 4}), // 18 19
json(json::value_t::discarded), json(json::value_t::discarded) // 20 21
};
std::vector<std::vector<bool>> expected_eq =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 0
{_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 1
{f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 2
{f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 3
{f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 4
{f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 5
{f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
std::vector<std::vector<bool>> expected_lt =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, // 0
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, // 1
{f_, f_, f_, _t, _t, _t, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 3
{f_, f_, f_, _t, f_, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 4
{f_, f_, f_, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 5
{f_, f_, f_, _t, _t, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 6
{f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 11
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 12
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, f_, _t, _t, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
SECTION("compares unordered")
{
std::vector<std::vector<bool>> expected =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 0
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 1
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 3
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 4
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 5
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 7
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 8
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 19
{_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 20
{_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 21
};
// check if two values compare unordered as expected
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK(json::compares_unordered(j_values[i], j_values[j]) == expected[i][j]);
}
}
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
SECTION("compares unordered (inverse)")
{
std::vector<std::vector<bool>> expected =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 0
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 1
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 3
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 4
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 5
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 7
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
// check that two values compare unordered as expected (with legacy-mode enabled)
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CAPTURE(j_values[i])
CAPTURE(j_values[j])
CHECK(json::compares_unordered(j_values[i], j_values[j], true) == expected[i][j]);
}
}
}
#endif
SECTION("comparison: equal")
{
// check that two values compare equal
REQUIRE(expected_eq.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected_eq[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK((j_values[i] == j_values[j]) == expected_eq[i][j]);
}
}
// compare with null pointer
json j_null;
CHECK(j_null == nullptr);
CHECK(nullptr == j_null);
}
SECTION("comparison: not equal")
{
// check that two values compare unequal as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j], true))
{
// if two values compare unordered,
// check that the boolean comparison result is always false
CHECK_FALSE(j_values[i] != j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of equal
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
}
}
}
// compare with null pointer
const json j_null;
CHECK((j_null != nullptr) == false);
CHECK((nullptr != j_null) == false);
CHECK((j_null != nullptr) == !(j_null == nullptr));
CHECK((nullptr != j_null) == !(nullptr == j_null));
}
SECTION("comparison: less")
{
// check that two values compare less than as expected
REQUIRE(expected_lt.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected_lt[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK((j_values[i] < j_values[j]) == expected_lt[i][j]);
}
}
}
SECTION("comparison: less than or equal equal")
{
// check that two values compare less than or equal as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j], true))
{
// if two values compare unordered,
// check that the boolean comparison result is always false
CHECK_FALSE(j_values[i] <= j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of less than with the operand order reversed
CHECK((j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]));
}
}
}
}
SECTION("comparison: greater than")
{
// check that two values compare greater than as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j]))
{
// if two values compare unordered,
// check that the boolean comparison result is always false
CHECK_FALSE(j_values[i] > j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of less than or equal which is defined as
// the inverse of less than with the operand order reversed
CHECK((j_values[i] > j_values[j]) == !(j_values[i] <= j_values[j]));
CHECK((j_values[i] > j_values[j]) == !!(j_values[j] < j_values[i]));
}
}
}
}
SECTION("comparison: greater than or equal")
{
// check that two values compare greater than or equal as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j], true))
{
// if two values compare unordered,
// check that the boolean result is always false
CHECK_FALSE(j_values[i] >= j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of less than
CHECK((j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j]));
}
}
}
}
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
SECTION("comparison: 3-way")
{
std::vector<std::vector<std::partial_ordering>> expected =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{eq, eq, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, un, un}, // 0
{eq, eq, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, un, un}, // 1
{gt, gt, eq, lt, lt, lt, lt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 2
{gt, gt, gt, eq, gt, gt, gt, gt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 3
{gt, gt, gt, lt, eq, lt, gt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 4
{gt, gt, gt, lt, gt, eq, gt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 5
{gt, gt, gt, lt, lt, lt, eq, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 6
{gt, gt, gt, lt, gt, gt, gt, eq, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 7
{gt, gt, un, un, un, un, un, un, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 8
{gt, gt, un, un, un, un, un, un, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 9
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, gt, gt, gt, gt, gt, gt, gt, lt, lt, un, un}, // 10
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, eq, gt, gt, gt, gt, gt, gt, lt, lt, un, un}, // 11
{gt, gt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, eq, gt, lt, lt, lt, lt, lt, lt, un, un}, // 12
{gt, gt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, eq, lt, lt, lt, lt, lt, lt, un, un}, // 13
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, eq, lt, gt, gt, lt, lt, un, un}, // 14
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, gt, eq, gt, gt, lt, lt, un, un}, // 15
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, lt, lt, eq, gt, lt, lt, un, un}, // 16
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, lt, lt, lt, eq, lt, lt, un, un}, // 17
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, lt, un, un}, // 18
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, un, un}, // 19
{un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un}, // 20
{un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un}, // 21
};
// check expected partial_ordering against expected booleans
REQUIRE(expected.size() == expected_eq.size());
REQUIRE(expected.size() == expected_lt.size());
for (size_t i = 0; i < expected.size(); ++i)
{
REQUIRE(expected[i].size() == expected_eq[i].size());
REQUIRE(expected[i].size() == expected_lt[i].size());
for (size_t j = 0; j < expected[i].size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK(std::is_eq(expected[i][j]) == expected_eq[i][j]);
CHECK(std::is_lt(expected[i][j]) == expected_lt[i][j]);
if (std::is_gt(expected[i][j]))
{
CHECK((!expected_eq[i][j] && !expected_lt[i][j]));
}
}
}
// check that two values compare according to their expected ordering
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK((j_values[i] <=> j_values[j]) == expected[i][j]); // *NOPAD*
}
}
}
#endif
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
SECTION("parser callback regression")
{
SECTION("filter specific element")
{
const auto* s_object = R"(
{
"foo": 2,
"bar": {
"baz": 1
}
}
)";
const auto* s_array = R"(
[1,2,[3,4,5],4,5]
)";
json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
{
// filter all number(2) elements
return j != json(2);
});
CHECK (j_object == json({{"bar", {{"baz", 1}}}}));
json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
{
return j != json(2);
});
CHECK (j_array == json({1, {3, 4, 5}, 4, 5}));
}
}
#endif
}

View File

@ -0,0 +1,150 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("concepts")
{
SECTION("container requirements for json")
{
// X: container class: json
// T: type of objects: json
// a, b: values of type X: json
// TABLE 96 - Container Requirements
// X::value_type must return T
CHECK((std::is_same<json::value_type, json>::value));
// X::reference must return lvalue of T
CHECK((std::is_same<json::reference, json&>::value));
// X::const_reference must return const lvalue of T
CHECK((std::is_same<json::const_reference, const json&>::value));
// X::iterator must return iterator whose value_type is T
CHECK((std::is_same<json::iterator::value_type, json>::value));
// X::iterator must meet the forward iterator requirements
CHECK((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::iterator>::iterator_category>::value));
// X::iterator must be convertible to X::const_iterator
CHECK((std::is_convertible<json::iterator, json::const_iterator>::value));
// X::const_iterator must return iterator whose value_type is T
CHECK((std::is_same<json::const_iterator::value_type, json>::value));
// X::const_iterator must meet the forward iterator requirements
CHECK((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::const_iterator>::iterator_category>::value));
// X::difference_type must return a signed integer
CHECK((std::is_signed<json::difference_type>::value));
// X::difference_type must be identical to X::iterator::difference_type
CHECK((std::is_same<json::difference_type, json::iterator::difference_type>::value));
// X::difference_type must be identical to X::const_iterator::difference_type
CHECK((std::is_same<json::difference_type, json::const_iterator::difference_type>::value));
// X::size_type must return an unsigned integer
CHECK((std::is_unsigned<json::size_type>::value));
// X::size_type can represent any non-negative value of X::difference_type
CHECK(static_cast<json::size_type>((std::numeric_limits<json::difference_type>::max)()) <=
(std::numeric_limits<json::size_type>::max)());
// the expression "X u" has the post-condition "u.empty()"
{
const json u;
CHECK(u.empty());
}
// the expression "X()" has the post-condition "X().empty()"
CHECK(json().empty());
}
SECTION("class json")
{
SECTION("DefaultConstructible")
{
CHECK(std::is_nothrow_default_constructible<json>::value);
}
SECTION("MoveConstructible")
{
CHECK(std::is_move_constructible<json>::value);
CHECK(std::is_nothrow_move_constructible<json>::value);
}
SECTION("CopyConstructible")
{
CHECK(std::is_copy_constructible<json>::value);
}
SECTION("MoveAssignable")
{
CHECK(std::is_nothrow_move_assignable<json>::value);
}
SECTION("CopyAssignable")
{
CHECK(std::is_copy_assignable<json>::value);
}
SECTION("Destructible")
{
CHECK(std::is_nothrow_destructible<json>::value);
}
SECTION("StandardLayoutType")
{
CHECK(std::is_standard_layout<json>::value);
}
}
SECTION("class iterator")
{
SECTION("CopyConstructible")
{
CHECK(std::is_nothrow_copy_constructible<json::iterator>::value);
CHECK(std::is_nothrow_copy_constructible<json::const_iterator>::value);
}
SECTION("CopyAssignable")
{
// STL iterators used by json::iterator don't pass this test in Debug mode
#if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0)
CHECK(std::is_nothrow_copy_assignable<json::iterator>::value);
CHECK(std::is_nothrow_copy_assignable<json::const_iterator>::value);
#endif
}
SECTION("Destructible")
{
CHECK(std::is_nothrow_destructible<json::iterator>::value);
CHECK(std::is_nothrow_destructible<json::const_iterator>::value);
}
SECTION("Swappable")
{
{
json j {1, 2, 3};
json::iterator it1 = j.begin();
json::iterator it2 = j.end();
swap(it1, it2);
CHECK(it1 == j.end());
CHECK(it2 == j.begin());
}
{
json j {1, 2, 3};
json::const_iterator it1 = j.cbegin();
json::const_iterator it2 = j.cend();
swap(it1, it2);
CHECK(it1 == j.end());
CHECK(it2 == j.begin());
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,186 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("other constructors and destructor")
{
SECTION("copy constructor")
{
SECTION("object")
{
json j {{"foo", 1}, {"bar", false}};
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("array")
{
json j {"foo", 1, 42.23, false};
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("null")
{
json j(nullptr);
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("boolean")
{
json j(true);
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("string")
{
json j("Hello world");
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("number (integer)")
{
json j(42);
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("number (unsigned)")
{
json j(42u);
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("number (floating-point)")
{
json j(42.23);
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
SECTION("binary")
{
json j = json::binary({1, 2, 3});
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
CHECK(j == k);
}
}
SECTION("move constructor")
{
json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}};
CHECK(j.type() == json::value_t::object);
const json k(std::move(j));
CHECK(k.type() == json::value_t::object);
CHECK(j.type() == json::value_t::null); // NOLINT: access after move is OK here
}
SECTION("copy assignment")
{
SECTION("object")
{
json j {{"foo", 1}, {"bar", false}};
json k;
k = j;
CHECK(j == k);
}
SECTION("array")
{
json j {"foo", 1, 42.23, false};
json k;
k = j;
CHECK(j == k);
}
SECTION("null")
{
json j(nullptr);
json k;
k = j;
CHECK(j == k);
}
SECTION("boolean")
{
json j(true);
json k;
k = j;
CHECK(j == k);
}
SECTION("string")
{
json j("Hello world");
json k;
k = j;
CHECK(j == k);
}
SECTION("number (integer)")
{
json j(42);
json k;
k = j;
CHECK(j == k);
}
SECTION("number (unsigned)")
{
json j(42u);
json k;
k = j;
CHECK(j == k);
}
SECTION("number (floating-point)")
{
json j(42.23);
json k;
k = j;
CHECK(j == k);
}
SECTION("binary")
{
json j = json::binary({1, 2, 3});
json k;
k = j;
CHECK(j == k);
}
}
SECTION("destructor")
{
SECTION("object")
{
auto* j = new json {{"foo", 1}, {"bar", false}}; // NOLINT(cppcoreguidelines-owning-memory)
delete j; // NOLINT(cppcoreguidelines-owning-memory)
}
SECTION("array")
{
auto* j = new json {"foo", 1, 1u, false, 23.42}; // NOLINT(cppcoreguidelines-owning-memory)
delete j; // NOLINT(cppcoreguidelines-owning-memory)
}
SECTION("string")
{
auto* j = new json("Hello world"); // NOLINT(cppcoreguidelines-owning-memory)
delete j; // NOLINT(cppcoreguidelines-owning-memory)
}
}
}

View File

@ -0,0 +1,205 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <sstream>
namespace
{
struct alt_string_iter
{
alt_string_iter() = default;
alt_string_iter(const char* cstr)
: impl(cstr)
{}
void reserve(std::size_t s)
{
impl.reserve(s);
}
template<typename Iter>
void append(Iter first, Iter last)
{
impl.append(first, last);
}
std::string::const_iterator begin() const noexcept
{
return impl.begin();
}
std::string::const_iterator end() const noexcept
{
return impl.end();
}
std::size_t size() const noexcept
{
return impl.size();
}
alt_string_iter& operator+=(const char c)
{
impl += c;
return *this;
}
std::string impl{};
};
struct alt_string_data
{
alt_string_data() = default;
alt_string_data(const char* cstr)
: impl(cstr)
{}
void reserve(std::size_t s)
{
impl.reserve(s);
}
void append(const char* p, std::size_t s)
{
impl.append(p, s);
}
const char* data() const
{
return impl.data();
}
std::size_t size() const
{
return impl.size();
}
alt_string_data& operator+=(const char c)
{
impl += c;
return *this;
}
std::string impl{};
};
void check_escaped(const char* original, const char* escaped = "", bool ensure_ascii = false);
void check_escaped(const char* original, const char* escaped, const bool ensure_ascii)
{
std::stringstream ss;
json::serializer s(nlohmann::detail::output_adapter<char>(ss), ' ');
s.dump_escaped(original, ensure_ascii);
CHECK(ss.str() == escaped);
}
} // namespace
TEST_CASE("convenience functions")
{
SECTION("type name as string")
{
CHECK(std::string(json(json::value_t::null).type_name()) == "null");
CHECK(std::string(json(json::value_t::object).type_name()) == "object");
CHECK(std::string(json(json::value_t::array).type_name()) == "array");
CHECK(std::string(json(json::value_t::number_integer).type_name()) == "number");
CHECK(std::string(json(json::value_t::number_unsigned).type_name()) == "number");
CHECK(std::string(json(json::value_t::number_float).type_name()) == "number");
CHECK(std::string(json(json::value_t::binary).type_name()) == "binary");
CHECK(std::string(json(json::value_t::boolean).type_name()) == "boolean");
CHECK(std::string(json(json::value_t::string).type_name()) == "string");
CHECK(std::string(json(json::value_t::discarded).type_name()) == "discarded");
}
SECTION("string escape")
{
check_escaped("\"", "\\\"");
check_escaped("\\", "\\\\");
check_escaped("\b", "\\b");
check_escaped("\f", "\\f");
check_escaped("\n", "\\n");
check_escaped("\r", "\\r");
check_escaped("\t", "\\t");
check_escaped("\x01", "\\u0001");
check_escaped("\x02", "\\u0002");
check_escaped("\x03", "\\u0003");
check_escaped("\x04", "\\u0004");
check_escaped("\x05", "\\u0005");
check_escaped("\x06", "\\u0006");
check_escaped("\x07", "\\u0007");
check_escaped("\x08", "\\b");
check_escaped("\x09", "\\t");
check_escaped("\x0a", "\\n");
check_escaped("\x0b", "\\u000b");
check_escaped("\x0c", "\\f");
check_escaped("\x0d", "\\r");
check_escaped("\x0e", "\\u000e");
check_escaped("\x0f", "\\u000f");
check_escaped("\x10", "\\u0010");
check_escaped("\x11", "\\u0011");
check_escaped("\x12", "\\u0012");
check_escaped("\x13", "\\u0013");
check_escaped("\x14", "\\u0014");
check_escaped("\x15", "\\u0015");
check_escaped("\x16", "\\u0016");
check_escaped("\x17", "\\u0017");
check_escaped("\x18", "\\u0018");
check_escaped("\x19", "\\u0019");
check_escaped("\x1a", "\\u001a");
check_escaped("\x1b", "\\u001b");
check_escaped("\x1c", "\\u001c");
check_escaped("\x1d", "\\u001d");
check_escaped("\x1e", "\\u001e");
check_escaped("\x1f", "\\u001f");
// invalid UTF-8 characters
CHECK_THROWS_WITH_AS(check_escaped("ä\xA9ü"), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
}
SECTION("string concat")
{
using nlohmann::detail::concat;
const char* expected = "Hello, world!";
alt_string_iter const hello_iter{"Hello, "};
alt_string_data const hello_data{"Hello, "};
std::string const world = "world";
SECTION("std::string")
{
std::string str1 = concat(hello_iter, world, '!');
std::string str2 = concat(hello_data, world, '!');
std::string str3 = concat("Hello, ", world, '!');
CHECK(str1 == expected);
CHECK(str2 == expected);
CHECK(str3 == expected);
}
SECTION("alt_string_iter")
{
alt_string_iter str = concat<alt_string_iter>(hello_iter, world, '!');
CHECK(str.impl == expected);
}
SECTION("alt_string_data")
{
alt_string_data str = concat<alt_string_data>(hello_data, world, '!');
CHECK(str.impl == expected);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,356 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
| | |__ | | | | | | version 3.10.2
|_____|_____|_____|_|___| 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 <set>
#include <sstream>
#include <string>
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
// Test extending nlohmann::json by using a custom base class.
// Add some metadata to each node and test the behaviour of copy / move
template<class MetaDataType>
class json_metadata
{
public:
using metadata_t = MetaDataType;
metadata_t& metadata()
{
return m_metadata;
}
const metadata_t& metadata() const
{
return m_metadata;
}
private:
metadata_t m_metadata = {};
};
template<class T>
using json_with_metadata =
nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
json_metadata<T>
>;
TEST_CASE("JSON Node Metadata")
{
SECTION("type int")
{
using json = json_with_metadata<int>;
json null;
auto obj = json::object();
auto array = json::array();
null.metadata() = 1;
obj.metadata() = 2;
array.metadata() = 3;
auto copy = array;
CHECK(null.metadata() == 1);
CHECK(obj.metadata() == 2);
CHECK(array.metadata() == 3);
CHECK(copy.metadata() == 3);
}
SECTION("type vector<int>")
{
using json = json_with_metadata<std::vector<int>>;
json value;
value.metadata().emplace_back(1);
auto copy = value;
value.metadata().emplace_back(2);
CHECK(copy.metadata().size() == 1);
CHECK(copy.metadata().at(0) == 1);
CHECK(value.metadata().size() == 2);
CHECK(value.metadata().at(0) == 1);
CHECK(value.metadata().at(1) == 2);
}
SECTION("copy ctor")
{
using json = json_with_metadata<std::vector<int>>;
json value;
value.metadata().emplace_back(1);
value.metadata().emplace_back(2);
json copy = value;
CHECK(copy.metadata().size() == 2);
CHECK(copy.metadata().at(0) == 1);
CHECK(copy.metadata().at(1) == 2);
CHECK(value.metadata().size() == 2);
CHECK(value.metadata().at(0) == 1);
CHECK(value.metadata().at(1) == 2);
value.metadata().clear();
CHECK(copy.metadata().size() == 2);
CHECK(value.metadata().size() == 0);
}
SECTION("move ctor")
{
using json = json_with_metadata<std::vector<int>>;
json value;
value.metadata().emplace_back(1);
value.metadata().emplace_back(2);
const json moved = std::move(value);
CHECK(moved.metadata().size() == 2);
CHECK(moved.metadata().at(0) == 1);
CHECK(moved.metadata().at(1) == 2);
}
SECTION("move assign")
{
using json = json_with_metadata<std::vector<int>>;
json value;
value.metadata().emplace_back(1);
value.metadata().emplace_back(2);
json moved;
moved = std::move(value);
CHECK(moved.metadata().size() == 2);
CHECK(moved.metadata().at(0) == 1);
CHECK(moved.metadata().at(1) == 2);
}
SECTION("copy assign")
{
using json = json_with_metadata<std::vector<int>>;
json value;
value.metadata().emplace_back(1);
value.metadata().emplace_back(2);
json copy;
copy = value;
CHECK(copy.metadata().size() == 2);
CHECK(copy.metadata().at(0) == 1);
CHECK(copy.metadata().at(1) == 2);
CHECK(value.metadata().size() == 2);
CHECK(value.metadata().at(0) == 1);
CHECK(value.metadata().at(1) == 2);
value.metadata().clear();
CHECK(copy.metadata().size() == 2);
CHECK(value.metadata().size() == 0);
}
SECTION("type unique_ptr<int>")
{
using json = json_with_metadata<std::unique_ptr<int>>;
json value;
value.metadata().reset(new int(42)); // NOLINT(cppcoreguidelines-owning-memory)
auto moved = std::move(value);
CHECK(moved.metadata() != nullptr);
CHECK(*moved.metadata() == 42);
}
SECTION("type vector<int> in json array")
{
using json = json_with_metadata<std::vector<int>>;
json value;
value.metadata().emplace_back(1);
value.metadata().emplace_back(2);
json const array(10, value);
CHECK(value.metadata().size() == 2);
CHECK(value.metadata().at(0) == 1);
CHECK(value.metadata().at(1) == 2);
for (const auto& val : array)
{
CHECK(val.metadata().size() == 2);
CHECK(val.metadata().at(0) == 1);
CHECK(val.metadata().at(1) == 2);
}
}
}
// Test extending nlohmann::json by using a custom base class.
// Add a custom member function template iterating over the whole json tree.
class visitor_adaptor
{
public:
template <class Fnc>
void visit(const Fnc& fnc) const;
private:
template <class Ptr, class Fnc>
void do_visit(const Ptr& ptr, const Fnc& fnc) const;
};
using json_with_visitor_t = nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
visitor_adaptor
>;
template <class Fnc>
void visitor_adaptor::visit(const Fnc& fnc) const
{
do_visit(json_with_visitor_t::json_pointer{}, fnc);
}
template <class Ptr, class Fnc>
void visitor_adaptor::do_visit(const Ptr& ptr, const Fnc& fnc) const
{
using value_t = nlohmann::detail::value_t;
const json_with_visitor_t& json = *static_cast<const json_with_visitor_t*>(this); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
switch (json.type())
{
case value_t::object:
for (const auto& entry : json.items())
{
entry.value().do_visit(ptr / entry.key(), fnc);
}
break;
case value_t::array:
for (std::size_t i = 0; i < json.size(); ++i)
{
json.at(i).do_visit(ptr / std::to_string(i), fnc);
}
break;
case value_t::discarded:
break;
case value_t::null:
case value_t::string:
case value_t::boolean:
case value_t::number_integer:
case value_t::number_unsigned:
case value_t::number_float:
case value_t::binary:
default:
fnc(ptr, json);
}
}
TEST_CASE("JSON Visit Node")
{
json_with_visitor_t json;
json["null"];
json["int"] = -1;
json["uint"] = 1U;
json["float"] = 1.0;
json["boolean"] = true;
json["string"] = "string";
json["array"].push_back(0);
json["array"].push_back(1);
json["array"].push_back(json);
std::set<std::string> expected
{
"/null - null - null",
"/int - number_integer - -1",
"/uint - number_unsigned - 1",
"/float - number_float - 1.0",
"/boolean - boolean - true",
"/string - string - \"string\"",
"/array/0 - number_integer - 0",
"/array/1 - number_integer - 1",
"/array/2/null - null - null",
"/array/2/int - number_integer - -1",
"/array/2/uint - number_unsigned - 1",
"/array/2/float - number_float - 1.0",
"/array/2/boolean - boolean - true",
"/array/2/string - string - \"string\"",
"/array/2/array/0 - number_integer - 0",
"/array/2/array/1 - number_integer - 1"
};
json.visit(
[&](const json_with_visitor_t::json_pointer & p,
const json_with_visitor_t& j)
{
std::stringstream str;
str << p.to_string() << " - " ;
using value_t = nlohmann::detail::value_t;
switch (j.type())
{
case value_t::object:
str << "object";
break;
case value_t::array:
str << "array";
break;
case value_t::discarded:
str << "discarded";
break;
case value_t::null:
str << "null";
break;
case value_t::string:
str << "string";
break;
case value_t::boolean:
str << "boolean";
break;
case value_t::number_integer:
str << "number_integer";
break;
case value_t::number_unsigned:
str << "number_unsigned";
break;
case value_t::number_float:
str << "number_float";
break;
case value_t::binary:
str << "binary";
break;
default:
str << "error";
break;
}
str << " - " << j.dump();
CHECK(json.at(p) == j);
INFO(str.str());
CHECK(expected.count(str.str()) == 1);
expected.erase(str.str());
}
);
CHECK(expected.empty());
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#ifdef JSON_DIAGNOSTICS
#undef JSON_DIAGNOSTICS
#endif
#define JSON_DIAGNOSTICS 1
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("Better diagnostics")
{
SECTION("empty JSON Pointer")
{
json const j = 1;
std::string s;
CHECK_THROWS_WITH_AS(s = j.get<std::string>(), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
}
SECTION("invalid type")
{
json j;
j["a"]["b"]["c"] = 1;
std::string s;
CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"].get<std::string>(), "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error);
}
SECTION("missing key")
{
json j;
j["object"]["object"] = true;
CHECK_THROWS_WITH_AS(j["object"].at("not_found"), "[json.exception.out_of_range.403] (/object) key 'not_found' not found", json::out_of_range);
}
SECTION("array index out of range")
{
json j;
j["array"][4] = true;
CHECK_THROWS_WITH_AS(j["array"].at(5), "[json.exception.out_of_range.401] (/array) array index 5 is out of range", json::out_of_range);
}
SECTION("array index at wrong type")
{
json j;
j["array"][4] = true;
CHECK_THROWS_WITH_AS(j["array"][4][5], "[json.exception.type_error.305] (/array/4) cannot use operator[] with a numeric argument with boolean", json::type_error);
}
SECTION("wrong iterator")
{
json j;
j["array"] = json::array();
CHECK_THROWS_WITH_AS(j["array"].erase(j.begin()), "[json.exception.invalid_iterator.202] (/array) iterator does not fit current value", json::invalid_iterator);
}
SECTION("JSON Pointer escaping")
{
json j;
j["a/b"]["m~n"] = 1;
std::string s;
CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get<std::string>(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error);
}
SECTION("Parse error")
{
json _;
CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error);
}
SECTION("Wrong type in update()")
{
json j = {{"foo", "bar"}};
json k = {{"bla", 1}};
CHECK_THROWS_WITH_AS(j.update(k["bla"].begin(), k["bla"].end()), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
CHECK_THROWS_WITH_AS(j.update(k["bla"]), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
}
}
TEST_CASE("Regression tests for extended diagnostics")
{
SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448")
{
CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
}
SECTION("Regression test for https://github.com/nlohmann/json/pull/2562/files/380a613f2b5d32425021129cd1f371ddcfd54ddf#r563259793")
{
json j;
j["/foo"] = {1, 2, 3};
CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
}
SECTION("Regression test for issue #2838 - Assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
{
// void push_back(basic_json&& val)
{
json j_arr = json::array();
j_arr.push_back(json::object());
j_arr.push_back(json::object());
j_arr.push_back(json::object());
j_arr.push_back(json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// void push_back(const basic_json& val)
{
json j_arr = json::array();
auto object = json::object();
j_arr.push_back(object);
j_arr.push_back(object);
j_arr.push_back(object);
j_arr.push_back(object);
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// reference emplace_back(Args&& ... args)
{
json j_arr = json::array();
j_arr.emplace_back(json::object());
j_arr.emplace_back(json::object());
j_arr.emplace_back(json::object());
j_arr.emplace_back(json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// iterator insert(const_iterator pos, const basic_json& val)
{
json j_arr = json::array();
j_arr.insert(j_arr.begin(), json::object());
j_arr.insert(j_arr.begin(), json::object());
j_arr.insert(j_arr.begin(), json::object());
j_arr.insert(j_arr.begin(), json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
{
json j_arr = json::array();
j_arr.insert(j_arr.begin(), 2, json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// iterator insert(const_iterator pos, const_iterator first, const_iterator last)
{
json j_arr = json::array();
json j_objects = {json::object(), json::object()};
j_arr.insert(j_arr.begin(), j_objects.begin(), j_objects.end());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
}
SECTION("Regression test for issue #2962 - JSON_DIAGNOSTICS assertion for ordered_json")
{
nlohmann::ordered_json j;
nlohmann::ordered_json j2;
const std::string value;
j["first"] = value;
j["second"] = value;
j2["something"] = j;
}
SECTION("Regression test for issue #3007 - Parent pointers properly set when using update()")
{
// void update(const_reference j)
{
json j = json::object();
{
json j2 = json::object();
j2["one"] = 1;
j.update(j2);
}
// Must call operator[] on const element, otherwise m_parent gets updated.
auto const& constJ = j;
CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
}
// void update(const_iterator first, const_iterator last)
{
json j = json::object();
{
json j2 = json::object();
j2["one"] = 1;
j.update(j2.begin(), j2.end());
}
// Must call operator[] on const element, otherwise m_parent gets updated.
auto const& constJ = j;
CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
}
// Code from #3007 triggering unwanted assertion without fix to update().
{
json root = json::array();
json lower = json::object();
{
json lowest = json::object();
lowest["one"] = 1;
lower.update(lowest);
}
root.push_back(lower);
}
}
SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
{
// reference operator[](size_type idx)
{
json j_arr = json::array();
j_arr[0] = 0;
j_arr[1] = 1;
j_arr[2] = 2;
j_arr[3] = 3;
j_arr[4] = 4;
j_arr[5] = 5;
j_arr[6] = 6;
j_arr[7] = 7;
json const j_arr_copy = j_arr;
}
}
}

View File

@ -0,0 +1,52 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// disable -Wnoexcept as exceptions are switched off for this test suite
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
#include <nlohmann/json.hpp>
using json = nlohmann::json;
/////////////////////////////////////////////////////////////////////
// for #2824
/////////////////////////////////////////////////////////////////////
class sax_no_exception : public nlohmann::detail::json_sax_dom_parser<json>
{
public:
explicit sax_no_exception(json& j) : nlohmann::detail::json_sax_dom_parser<json>(j, false) {}
static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex)
{
error_string = new std::string(ex.what()); // NOLINT(cppcoreguidelines-owning-memory)
return false;
}
static std::string* error_string;
};
std::string* sax_no_exception::error_string = nullptr;
TEST_CASE("Tests with disabled exceptions")
{
SECTION("issue #2824 - encoding of json::exception::what()")
{
json j;
sax_no_exception sax(j);
CHECK (!json::sax_parse("xyz", &sax));
CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'");
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
}
}
DOCTEST_GCC_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,881 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("element access 1")
{
SECTION("array")
{
json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
const json j_const = j;
SECTION("access specified element with bounds checking")
{
SECTION("access within bounds")
{
CHECK(j.at(0) == json(1));
CHECK(j.at(1) == json(1u));
CHECK(j.at(2) == json(true));
CHECK(j.at(3) == json(nullptr));
CHECK(j.at(4) == json("string"));
CHECK(j.at(5) == json(42.23));
CHECK(j.at(6) == json::object());
CHECK(j.at(7) == json({1, 2, 3}));
CHECK(j_const.at(0) == json(1));
CHECK(j_const.at(1) == json(1u));
CHECK(j_const.at(2) == json(true));
CHECK(j_const.at(3) == json(nullptr));
CHECK(j_const.at(4) == json("string"));
CHECK(j_const.at(5) == json(42.23));
CHECK(j_const.at(6) == json::object());
CHECK(j_const.at(7) == json({1, 2, 3}));
}
SECTION("access outside bounds")
{
CHECK_THROWS_WITH_AS(j.at(8),
"[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&);
CHECK_THROWS_WITH_AS(j_const.at(8),
"[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&);
}
SECTION("access on non-array type")
{
SECTION("null")
{
json j_nonarray(json::value_t::null);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&);
}
SECTION("boolean")
{
json j_nonarray(json::value_t::boolean);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&);
}
SECTION("string")
{
json j_nonarray(json::value_t::string);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&);
}
SECTION("object")
{
json j_nonarray(json::value_t::object);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&);
}
SECTION("number (integer)")
{
json j_nonarray(json::value_t::number_integer);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
}
SECTION("number (unsigned)")
{
json j_nonarray(json::value_t::number_unsigned);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
}
SECTION("number (floating-point)")
{
json j_nonarray(json::value_t::number_float);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
}
}
}
SECTION("front and back")
{
CHECK(j.front() == json(1));
CHECK(j_const.front() == json(1));
CHECK(j.back() == json({1, 2, 3}));
CHECK(j_const.back() == json({1, 2, 3}));
}
SECTION("access specified element")
{
SECTION("access within bounds")
{
CHECK(j[0] == json(1));
CHECK(j[1] == json(1u));
CHECK(j[2] == json(true));
CHECK(j[3] == json(nullptr));
CHECK(j[4] == json("string"));
CHECK(j[5] == json(42.23));
CHECK(j[6] == json::object());
CHECK(j[7] == json({1, 2, 3}));
CHECK(j_const[0] == json(1));
CHECK(j_const[1] == json(1u));
CHECK(j_const[2] == json(true));
CHECK(j_const[3] == json(nullptr));
CHECK(j_const[4] == json("string"));
CHECK(j_const[5] == json(42.23));
CHECK(j_const[6] == json::object());
CHECK(j_const[7] == json({1, 2, 3}));
}
SECTION("access on non-array type")
{
SECTION("null")
{
SECTION("standard tests")
{
json j_nonarray(json::value_t::null);
const json j_nonarray_const(j_nonarray);
CHECK_NOTHROW(j_nonarray[0]);
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with null", json::type_error&);
}
SECTION("implicit transformation to properly filled array")
{
json j_nonarray;
j_nonarray[3] = 42;
CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42}));
}
}
SECTION("boolean")
{
json j_nonarray(json::value_t::boolean);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&);
}
SECTION("string")
{
json j_nonarray(json::value_t::string);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&);
}
SECTION("object")
{
json j_nonarray(json::value_t::object);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&);
}
SECTION("number (integer)")
{
json j_nonarray(json::value_t::number_integer);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
}
SECTION("number (unsigned)")
{
json j_nonarray(json::value_t::number_unsigned);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
}
SECTION("number (floating-point)")
{
json j_nonarray(json::value_t::number_float);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
}
}
}
SECTION("remove specified element")
{
SECTION("remove element by index")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(0);
CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(1);
CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(2);
CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(3);
CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(4);
CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(5);
CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(6);
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(7);
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
CHECK_THROWS_WITH_AS(jarray.erase(8), "[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&);
}
}
SECTION("remove element by iterator")
{
SECTION("erase(begin())")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator const it2 = jarray.erase(jarray.begin());
CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1u));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator const it2 = jarray.erase(jarray.cbegin());
CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1u));
}
}
SECTION("erase(begin(), end())")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator it2 = jarray.erase(jarray.begin(), jarray.end());
CHECK(jarray == json::array());
CHECK(it2 == jarray.end());
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend());
CHECK(jarray == json::array());
CHECK(it2 == jarray.cend());
}
}
SECTION("erase(begin(), begin())")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator const it2 = jarray.erase(jarray.begin(), jarray.begin());
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator const it2 = jarray.erase(jarray.cbegin(), jarray.cbegin());
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1));
}
}
SECTION("erase at offset")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator const it = jarray.begin() + 4;
json::iterator const it2 = jarray.erase(it);
CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(42.23));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator const it = jarray.cbegin() + 4;
json::const_iterator const it2 = jarray.erase(it);
CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(42.23));
}
}
SECTION("erase subrange")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator const it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6);
CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}}));
CHECK(*it2 == json::object());
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator const it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6);
CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}}));
CHECK(*it2 == json::object());
}
}
SECTION("different arrays")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json jarray2 = {"foo", "bar"};
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin()),
"[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(jarray.erase(jarray.begin(), jarray2.end()),
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray.end()),
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray2.end()),
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json const jarray2 = {"foo", "bar"};
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin()),
"[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(jarray.erase(jarray.cbegin(), jarray2.cend()),
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray.cend()),
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()),
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
}
}
}
SECTION("remove element by index in non-array type")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with boolean", json::type_error&);
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with string", json::type_error&);
}
SECTION("object")
{
json j_nonobject(json::value_t::object);
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with object", json::type_error&);
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
}
}
}
}
SECTION("other values")
{
SECTION("front and back")
{
SECTION("null")
{
{
json j;
CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
{
const json j{};
CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
}
SECTION("string")
{
{
json j = "foo";
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = "bar";
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (boolean)")
{
{
json j = false;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = true;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (integer)")
{
{
json j = 17;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = 17;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = 17u;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = 23.42;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
}
SECTION("erase with one valid iterator")
{
SECTION("null")
{
{
json j;
CHECK_THROWS_WITH_AS(j.erase(j.begin()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
}
{
json j;
CHECK_THROWS_WITH_AS(j.erase(j.begin()),
"[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
}
}
SECTION("string")
{
{
json j = "foo";
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = "bar";
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (boolean)")
{
{
json j = false;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = true;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (integer)")
{
{
json j = 17;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17u;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 23.42;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("binary")
{
{
json j = json::binary({1, 2, 3});
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = json::binary({1, 2, 3});
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
}
SECTION("erase with one invalid iterator")
{
SECTION("string")
{
{
json j = "foo";
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
{
json j = "bar";
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
}
SECTION("number (boolean)")
{
{
json j = false;
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
{
json j = true;
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
}
SECTION("number (integer)")
{
{
json j = 17;
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
{
json j = 17;
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
{
json j = 17u;
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
{
json j = 23.42;
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
}
}
}
SECTION("erase with two valid iterators")
{
SECTION("null")
{
{
json j;
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.end()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
}
{
json j;
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cend()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
}
}
SECTION("string")
{
{
json j = "foo";
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = "bar";
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (boolean)")
{
{
json j = false;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = true;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (integer)")
{
{
json j = 17;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17u;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 23.42;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("binary")
{
{
json j = json::binary({1, 2, 3});
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = json::binary({1, 2, 3});
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
}
SECTION("erase with two invalid iterators")
{
SECTION("string")
{
{
json j = "foo";
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
{
json j = "bar";
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
}
SECTION("number (boolean)")
{
{
json j = false;
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
{
json j = true;
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
}
SECTION("number (integer)")
{
{
json j = 17;
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
{
json j = 17;
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
{
json j = 17u;
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
{
json j = 23.42;
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using ordered_json = nlohmann::ordered_json;
#include <set>
TEST_CASE("hash<nlohmann::json>")
{
// Collect hashes for different JSON values and make sure that they are distinct
// We cannot compare against fixed values, because the implementation of
// std::hash may differ between compilers.
std::set<std::size_t> hashes;
// null
hashes.insert(std::hash<json> {}(json(nullptr)));
// boolean
hashes.insert(std::hash<json> {}(json(true)));
hashes.insert(std::hash<json> {}(json(false)));
// string
hashes.insert(std::hash<json> {}(json("")));
hashes.insert(std::hash<json> {}(json("foo")));
// number
hashes.insert(std::hash<json> {}(json(0)));
hashes.insert(std::hash<json> {}(json(static_cast<unsigned>(0))));
hashes.insert(std::hash<json> {}(json(-1)));
hashes.insert(std::hash<json> {}(json(0.0)));
hashes.insert(std::hash<json> {}(json(42.23)));
// array
hashes.insert(std::hash<json> {}(json::array()));
hashes.insert(std::hash<json> {}(json::array({1, 2, 3})));
// object
hashes.insert(std::hash<json> {}(json::object()));
hashes.insert(std::hash<json> {}(json::object({{"foo", "bar"}})));
// binary
hashes.insert(std::hash<json> {}(json::binary({})));
hashes.insert(std::hash<json> {}(json::binary({}, 0)));
hashes.insert(std::hash<json> {}(json::binary({}, 42)));
hashes.insert(std::hash<json> {}(json::binary({1, 2, 3})));
hashes.insert(std::hash<json> {}(json::binary({1, 2, 3}, 0)));
hashes.insert(std::hash<json> {}(json::binary({1, 2, 3}, 42)));
// discarded
hashes.insert(std::hash<json> {}(json(json::value_t::discarded)));
CHECK(hashes.size() == 21);
}
TEST_CASE("hash<nlohmann::ordered_json>")
{
// Collect hashes for different JSON values and make sure that they are distinct
// We cannot compare against fixed values, because the implementation of
// std::hash may differ between compilers.
std::set<std::size_t> hashes;
// null
hashes.insert(std::hash<ordered_json> {}(ordered_json(nullptr)));
// boolean
hashes.insert(std::hash<ordered_json> {}(ordered_json(true)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(false)));
// string
hashes.insert(std::hash<ordered_json> {}(ordered_json("")));
hashes.insert(std::hash<ordered_json> {}(ordered_json("foo")));
// number
hashes.insert(std::hash<ordered_json> {}(ordered_json(0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(static_cast<unsigned>(0))));
hashes.insert(std::hash<ordered_json> {}(ordered_json(-1)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(0.0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(42.23)));
// array
hashes.insert(std::hash<ordered_json> {}(ordered_json::array()));
hashes.insert(std::hash<ordered_json> {}(ordered_json::array({1, 2, 3})));
// object
hashes.insert(std::hash<ordered_json> {}(ordered_json::object()));
hashes.insert(std::hash<ordered_json> {}(ordered_json::object({{"foo", "bar"}})));
// binary
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({})));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({}, 0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({}, 42)));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3})));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3}, 0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3}, 42)));
// discarded
hashes.insert(std::hash<ordered_json> {}(ordered_json(ordered_json::value_t::discarded)));
CHECK(hashes.size() == 21);
}

View File

@ -0,0 +1,459 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <fstream>
#include <sstream>
#include "make_test_data_available.hpp"
TEST_CASE("object inspection")
{
SECTION("convenience type checker")
{
SECTION("object")
{
json const j {{"foo", 1}, {"bar", false}};
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(!j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(!j.is_primitive());
CHECK(j.is_structured());
}
SECTION("array")
{
json const j {"foo", 1, 1u, 42.23, false};
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(!j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(!j.is_primitive());
CHECK(j.is_structured());
}
SECTION("null")
{
json const j(nullptr);
CHECK(j.is_null());
CHECK(!j.is_boolean());
CHECK(!j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(j.is_primitive());
CHECK(!j.is_structured());
}
SECTION("boolean")
{
json const j(true);
CHECK(!j.is_null());
CHECK(j.is_boolean());
CHECK(!j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(j.is_primitive());
CHECK(!j.is_structured());
}
SECTION("string")
{
json const j("Hello world");
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(!j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(j.is_string());
CHECK(!j.is_discarded());
CHECK(j.is_primitive());
CHECK(!j.is_structured());
}
SECTION("number (integer)")
{
json const j(42);
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(j.is_number());
CHECK(j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(j.is_primitive());
CHECK(!j.is_structured());
}
SECTION("number (unsigned)")
{
json const j(42u);
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(j.is_number());
CHECK(j.is_number_integer());
CHECK(j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(j.is_primitive());
CHECK(!j.is_structured());
}
SECTION("number (floating-point)")
{
json const j(42.23);
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(j.is_primitive());
CHECK(!j.is_structured());
}
SECTION("binary")
{
json const j(json::value_t::binary);
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(!j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(!j.is_discarded());
CHECK(j.is_primitive());
CHECK(!j.is_structured());
}
SECTION("discarded")
{
json const j(json::value_t::discarded);
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(!j.is_number());
CHECK(!j.is_number_integer());
CHECK(!j.is_number_unsigned());
CHECK(!j.is_number_float());
CHECK(!j.is_binary());
CHECK(!j.is_object());
CHECK(!j.is_array());
CHECK(!j.is_string());
CHECK(j.is_discarded());
CHECK(!j.is_primitive());
CHECK(!j.is_structured());
}
}
SECTION("serialization")
{
json const j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
SECTION("no indent / indent=-1")
{
CHECK(j.dump() ==
"{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}");
CHECK(j.dump() == j.dump(-1));
}
SECTION("indent=0")
{
CHECK(j.dump(0) ==
"{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}");
}
SECTION("indent=1, space='\t'")
{
CHECK(j.dump(1, '\t') ==
"{\n\t\"array\": [\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t],\n\t\"boolean\": false,\n\t\"null\": null,\n\t\"number\": 42,\n\t\"object\": {},\n\t\"string\": \"Hello world\"\n}");
}
SECTION("indent=4")
{
CHECK(j.dump(4) ==
"{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}");
}
SECTION("indent=x")
{
CHECK(j.dump().size() == 94);
CHECK(j.dump(1).size() == 127);
CHECK(j.dump(2).size() == 142);
CHECK(j.dump(512).size() == 7792);
// important test, because it yields a resize of the indent_string
// inside the dump() function
CHECK(j.dump(1024).size() == 15472);
const auto binary = json::binary({1, 2, 3}, 128);
CHECK(binary.dump(1024).size() == 2086);
}
SECTION("dump and floating-point numbers")
{
auto s = json(42.23).dump();
CHECK(s.find("42.23") != std::string::npos);
}
SECTION("dump and small floating-point numbers")
{
auto s = json(1.23456e-78).dump();
CHECK(s.find("1.23456e-78") != std::string::npos);
}
SECTION("dump and non-ASCII characters")
{
CHECK(json("ä").dump() == "\"ä\"");
CHECK(json("Ö").dump() == "\"Ö\"");
CHECK(json("❤️").dump() == "\"❤️\"");
}
SECTION("dump with ensure_ascii and non-ASCII characters")
{
CHECK(json("ä").dump(-1, ' ', true) == "\"\\u00e4\"");
CHECK(json("Ö").dump(-1, ' ', true) == "\"\\u00d6\"");
CHECK(json("❤️").dump(-1, ' ', true) == "\"\\u2764\\ufe0f\"");
}
SECTION("full Unicode escaping to ASCII")
{
SECTION("parsing yields the same JSON value")
{
std::ifstream f_escaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode_ascii.json");
std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json");
json j1 = json::parse(f_escaped);
json j2 = json::parse(f_unescaped);
CHECK(j1 == j2);
}
SECTION("dumping yields the same JSON text")
{
std::ifstream f_escaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode_ascii.json");
std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json");
json const value = json::parse(f_unescaped);
std::string text = value.dump(4, ' ', true);
std::string expected((std::istreambuf_iterator<char>(f_escaped)),
std::istreambuf_iterator<char>());
CHECK(text == expected);
}
}
SECTION("serialization of discarded element")
{
json const j_discarded(json::value_t::discarded);
CHECK(j_discarded.dump() == "<discarded>");
}
SECTION("check that precision is reset after serialization")
{
// create stringstream and set precision
std::stringstream ss;
ss.precision(3);
ss << 3.141592653589793 << std::fixed;
CHECK(ss.str() == "3.14");
// reset stringstream
ss.str(std::string());
// use stringstream for JSON serialization
json const j_number = 3.14159265358979;
ss << j_number;
// check that precision has been overridden during serialization
CHECK(ss.str() == "3.14159265358979");
// check that precision has been restored
CHECK(ss.precision() == 3);
}
}
SECTION("round trips")
{
for (const auto& s :
{"3.141592653589793", "1000000000000000010E5"
})
{
json const j1 = json::parse(s);
std::string s1 = j1.dump();
json const j2 = json::parse(s1);
std::string s2 = j2.dump();
CHECK(s1 == s2);
}
}
SECTION("return the type of the object (explicit)")
{
SECTION("null")
{
json const j = nullptr;
CHECK(j.type() == json::value_t::null);
}
SECTION("object")
{
json const j = {{"foo", "bar"}};
CHECK(j.type() == json::value_t::object);
}
SECTION("array")
{
json const j = {1, 2, 3, 4};
CHECK(j.type() == json::value_t::array);
}
SECTION("boolean")
{
json const j = true;
CHECK(j.type() == json::value_t::boolean);
}
SECTION("string")
{
json const j = "Hello world";
CHECK(j.type() == json::value_t::string);
}
SECTION("number (integer)")
{
json const j = 23;
CHECK(j.type() == json::value_t::number_integer);
}
SECTION("number (unsigned)")
{
json const j = 23u;
CHECK(j.type() == json::value_t::number_unsigned);
}
SECTION("number (floating-point)")
{
json const j = 42.23;
CHECK(j.type() == json::value_t::number_float);
}
}
SECTION("return the type of the object (implicit)")
{
SECTION("null")
{
json const j = nullptr;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("object")
{
json const j = {{"foo", "bar"}};
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("array")
{
json const j = {1, 2, 3, 4};
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("boolean")
{
json const j = true;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("string")
{
json const j = "Hello world";
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("number (integer)")
{
json const j = 23;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("number (unsigned)")
{
json const j = 23u;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("number (floating-point)")
{
json const j = 42.23;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("binary")
{
json const j = json::binary({});
json::value_t t = j;
CHECK(t == j.type());
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,971 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#if JSON_HAS_RANGES
#include <algorithm>
#include <ranges>
#endif
TEST_CASE("iterators 2")
{
SECTION("iterator comparisons")
{
json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
for (json& j : j_values)
{
auto it1 = j.begin();
auto it2 = j.begin();
auto it3 = j.begin();
++it2;
++it3;
++it3;
auto it1_c = j.cbegin();
auto it2_c = j.cbegin();
auto it3_c = j.cbegin();
++it2_c;
++it3_c;
++it3_c;
// comparison: equal
{
CHECK(it1 == it1);
CHECK(!(it1 == it2));
CHECK(!(it1 == it3));
CHECK(!(it2 == it3));
CHECK(it1_c == it1_c);
CHECK(!(it1_c == it2_c));
CHECK(!(it1_c == it3_c));
CHECK(!(it2_c == it3_c));
}
// comparison: not equal
{
// check definition
CHECK( (it1 != it1) == !(it1 == it1) );
CHECK( (it1 != it2) == !(it1 == it2) );
CHECK( (it1 != it3) == !(it1 == it3) );
CHECK( (it2 != it3) == !(it2 == it3) );
CHECK( (it1_c != it1_c) == !(it1_c == it1_c) );
CHECK( (it1_c != it2_c) == !(it1_c == it2_c) );
CHECK( (it1_c != it3_c) == !(it1_c == it3_c) );
CHECK( (it2_c != it3_c) == !(it2_c == it3_c) );
}
// comparison: smaller
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
CHECK(!(it1 < it1));
CHECK(it1 < it2);
CHECK(it1 < it3);
CHECK(it2 < it3);
CHECK(!(it1_c < it1_c));
CHECK(it1_c < it2_c);
CHECK(it1_c < it3_c);
CHECK(it2_c < it3_c);
}
}
// comparison: less than or equal
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
// check definition
CHECK( (it1 <= it1) == !(it1 < it1) );
CHECK( (it1 <= it2) == !(it2 < it1) );
CHECK( (it1 <= it3) == !(it3 < it1) );
CHECK( (it2 <= it3) == !(it3 < it2) );
CHECK( (it1_c <= it1_c) == !(it1_c < it1_c) );
CHECK( (it1_c <= it2_c) == !(it2_c < it1_c) );
CHECK( (it1_c <= it3_c) == !(it3_c < it1_c) );
CHECK( (it2_c <= it3_c) == !(it3_c < it2_c) );
}
}
// comparison: greater than
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
// check definition
CHECK( (it1 > it1) == (it1 < it1) );
CHECK( (it1 > it2) == (it2 < it1) );
CHECK( (it1 > it3) == (it3 < it1) );
CHECK( (it2 > it3) == (it3 < it2) );
CHECK( (it1_c > it1_c) == (it1_c < it1_c) );
CHECK( (it1_c > it2_c) == (it2_c < it1_c) );
CHECK( (it1_c > it3_c) == (it3_c < it1_c) );
CHECK( (it2_c > it3_c) == (it3_c < it2_c) );
}
}
// comparison: greater than or equal
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
// check definition
CHECK( (it1 >= it1) == !(it1 < it1) );
CHECK( (it1 >= it2) == !(it1 < it2) );
CHECK( (it1 >= it3) == !(it1 < it3) );
CHECK( (it2 >= it3) == !(it2 < it3) );
CHECK( (it1_c >= it1_c) == !(it1_c < it1_c) );
CHECK( (it1_c >= it2_c) == !(it1_c < it2_c) );
CHECK( (it1_c >= it3_c) == !(it1_c < it3_c) );
CHECK( (it2_c >= it3_c) == !(it2_c < it3_c) );
}
}
}
// check exceptions if different objects are compared
for (auto j : j_values)
{
for (auto k : j_values)
{
if (j != k)
{
#if JSON_DIAGNOSTICS
// the output differs in each loop, so we cannot fix a string for the expected exception
#else
CHECK_THROWS_WITH_AS(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
#endif
}
}
}
}
SECTION("iterator arithmetic")
{
json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
json j_array = {1, 2, 3, 4, 5, 6};
json j_null = nullptr;
json j_value = 42;
SECTION("addition and subtraction")
{
SECTION("object")
{
{
auto it = j_object.begin();
CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.cbegin();
CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.begin();
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.cbegin();
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.begin();
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.cbegin();
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.begin();
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.cbegin();
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.begin();
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.cbegin();
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.begin();
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.cbegin();
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
}
SECTION("array")
{
{
auto it = j_array.begin();
it += 3;
CHECK((j_array.begin() + 3) == it);
CHECK((3 + j_array.begin()) == it);
CHECK((it - 3) == j_array.begin());
CHECK((it - j_array.begin()) == 3);
CHECK(*it == json(4));
it -= 2;
CHECK(*it == json(2));
}
{
auto it = j_array.cbegin();
it += 3;
CHECK((j_array.cbegin() + 3) == it);
CHECK((3 + j_array.cbegin()) == it);
CHECK((it - 3) == j_array.cbegin());
CHECK((it - j_array.cbegin()) == 3);
CHECK(*it == json(4));
it -= 2;
CHECK(*it == json(2));
}
}
SECTION("null")
{
{
auto it = j_null.begin();
it += 3;
CHECK((j_null.begin() + 3) == it);
CHECK((3 + j_null.begin()) == it);
CHECK((it - 3) == j_null.begin());
CHECK((it - j_null.begin()) == 3);
CHECK(it != j_null.end());
it -= 3;
CHECK(it == j_null.end());
}
{
auto it = j_null.cbegin();
it += 3;
CHECK((j_null.cbegin() + 3) == it);
CHECK((3 + j_null.cbegin()) == it);
CHECK((it - 3) == j_null.cbegin());
CHECK((it - j_null.cbegin()) == 3);
CHECK(it != j_null.cend());
it -= 3;
CHECK(it == j_null.cend());
}
}
SECTION("value")
{
{
auto it = j_value.begin();
it += 3;
CHECK((j_value.begin() + 3) == it);
CHECK((3 + j_value.begin()) == it);
CHECK((it - 3) == j_value.begin());
CHECK((it - j_value.begin()) == 3);
CHECK(it != j_value.end());
it -= 3;
CHECK(*it == json(42));
}
{
auto it = j_value.cbegin();
it += 3;
CHECK((j_value.cbegin() + 3) == it);
CHECK((3 + j_value.cbegin()) == it);
CHECK((it - 3) == j_value.cbegin());
CHECK((it - j_value.cbegin()) == 3);
CHECK(it != j_value.cend());
it -= 3;
CHECK(*it == json(42));
}
}
}
SECTION("subscript operator")
{
SECTION("object")
{
{
auto it = j_object.begin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
}
{
auto it = j_object.cbegin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
}
}
SECTION("array")
{
{
auto it = j_array.begin();
CHECK(it[0] == json(1));
CHECK(it[1] == json(2));
CHECK(it[2] == json(3));
CHECK(it[3] == json(4));
CHECK(it[4] == json(5));
CHECK(it[5] == json(6));
}
{
auto it = j_array.cbegin();
CHECK(it[0] == json(1));
CHECK(it[1] == json(2));
CHECK(it[2] == json(3));
CHECK(it[3] == json(4));
CHECK(it[4] == json(5));
CHECK(it[5] == json(6));
}
}
SECTION("null")
{
{
auto it = j_null.begin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
{
auto it = j_null.cbegin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
}
SECTION("value")
{
{
auto it = j_value.begin();
CHECK(it[0] == json(42));
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
{
auto it = j_value.cbegin();
CHECK(it[0] == json(42));
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
}
}
}
SECTION("reverse iterator comparisons")
{
json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
for (json& j : j_values)
{
auto it1 = j.rbegin();
auto it2 = j.rbegin();
auto it3 = j.rbegin();
++it2;
++it3;
++it3;
auto it1_c = j.crbegin();
auto it2_c = j.crbegin();
auto it3_c = j.crbegin();
++it2_c;
++it3_c;
++it3_c;
// comparison: equal
{
CHECK(it1 == it1);
CHECK(!(it1 == it2));
CHECK(!(it1 == it3));
CHECK(!(it2 == it3));
CHECK(it1_c == it1_c);
CHECK(!(it1_c == it2_c));
CHECK(!(it1_c == it3_c));
CHECK(!(it2_c == it3_c));
}
// comparison: not equal
{
// check definition
CHECK( (it1 != it1) == !(it1 == it1) );
CHECK( (it1 != it2) == !(it1 == it2) );
CHECK( (it1 != it3) == !(it1 == it3) );
CHECK( (it2 != it3) == !(it2 == it3) );
CHECK( (it1_c != it1_c) == !(it1_c == it1_c) );
CHECK( (it1_c != it2_c) == !(it1_c == it2_c) );
CHECK( (it1_c != it3_c) == !(it1_c == it3_c) );
CHECK( (it2_c != it3_c) == !(it2_c == it3_c) );
}
// comparison: smaller
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
CHECK(!(it1 < it1));
CHECK(it1 < it2);
CHECK(it1 < it3);
CHECK(it2 < it3);
CHECK(!(it1_c < it1_c));
CHECK(it1_c < it2_c);
CHECK(it1_c < it3_c);
CHECK(it2_c < it3_c);
}
}
// comparison: less than or equal
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
// check definition
CHECK( (it1 <= it1) == !(it1 < it1) );
CHECK( (it1 <= it2) == !(it2 < it1) );
CHECK( (it1 <= it3) == !(it3 < it1) );
CHECK( (it2 <= it3) == !(it3 < it2) );
CHECK( (it1_c <= it1_c) == !(it1_c < it1_c) );
CHECK( (it1_c <= it2_c) == !(it2_c < it1_c) );
CHECK( (it1_c <= it3_c) == !(it3_c < it1_c) );
CHECK( (it2_c <= it3_c) == !(it3_c < it2_c) );
}
}
// comparison: greater than
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
// check definition
CHECK( (it1 > it1) == (it1 < it1) );
CHECK( (it1 > it2) == (it2 < it1) );
CHECK( (it1 > it3) == (it3 < it1) );
CHECK( (it2 > it3) == (it3 < it2) );
CHECK( (it1_c > it1_c) == (it1_c < it1_c) );
CHECK( (it1_c > it2_c) == (it2_c < it1_c) );
CHECK( (it1_c > it3_c) == (it3_c < it1_c) );
CHECK( (it2_c > it3_c) == (it3_c < it2_c) );
}
}
// comparison: greater than or equal
{
if (j.type() == json::value_t::object)
{
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
#else
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
#endif
}
else
{
// check definition
CHECK( (it1 >= it1) == !(it1 < it1) );
CHECK( (it1 >= it2) == !(it1 < it2) );
CHECK( (it1 >= it3) == !(it1 < it3) );
CHECK( (it2 >= it3) == !(it2 < it3) );
CHECK( (it1_c >= it1_c) == !(it1_c < it1_c) );
CHECK( (it1_c >= it2_c) == !(it1_c < it2_c) );
CHECK( (it1_c >= it3_c) == !(it1_c < it3_c) );
CHECK( (it2_c >= it3_c) == !(it2_c < it3_c) );
}
}
}
// check exceptions if different objects are compared
for (auto j : j_values)
{
for (auto k : j_values)
{
if (j != k)
{
#if JSON_DIAGNOSTICS
// the output differs in each loop, so we cannot fix a string for the expected exception
#else
CHECK_THROWS_WITH_AS(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
#endif
}
}
}
}
SECTION("reverse iterator arithmetic")
{
json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
json j_array = {1, 2, 3, 4, 5, 6};
json j_null = nullptr;
json j_value = 42;
SECTION("addition and subtraction")
{
SECTION("object")
{
{
auto it = j_object.rbegin();
CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.crbegin();
CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.rbegin();
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.crbegin();
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.rbegin();
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.crbegin();
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.rbegin();
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.crbegin();
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.rbegin();
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.crbegin();
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.rbegin();
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.crbegin();
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
}
SECTION("array")
{
{
auto it = j_array.rbegin();
it += 3;
CHECK((j_array.rbegin() + 3) == it);
CHECK(json::reverse_iterator(3 + j_array.rbegin()) == it);
CHECK((it - 3) == j_array.rbegin());
CHECK((it - j_array.rbegin()) == 3);
CHECK(*it == json(3));
it -= 2;
CHECK(*it == json(5));
}
{
auto it = j_array.crbegin();
it += 3;
CHECK((j_array.crbegin() + 3) == it);
CHECK(json::const_reverse_iterator(3 + j_array.crbegin()) == it);
CHECK((it - 3) == j_array.crbegin());
CHECK((it - j_array.crbegin()) == 3);
CHECK(*it == json(3));
it -= 2;
CHECK(*it == json(5));
}
}
SECTION("null")
{
{
auto it = j_null.rbegin();
it += 3;
CHECK((j_null.rbegin() + 3) == it);
CHECK(json::reverse_iterator(3 + j_null.rbegin()) == it);
CHECK((it - 3) == j_null.rbegin());
CHECK((it - j_null.rbegin()) == 3);
CHECK(it != j_null.rend());
it -= 3;
CHECK(it == j_null.rend());
}
{
auto it = j_null.crbegin();
it += 3;
CHECK((j_null.crbegin() + 3) == it);
CHECK(json::const_reverse_iterator(3 + j_null.crbegin()) == it);
CHECK((it - 3) == j_null.crbegin());
CHECK((it - j_null.crbegin()) == 3);
CHECK(it != j_null.crend());
it -= 3;
CHECK(it == j_null.crend());
}
}
SECTION("value")
{
{
auto it = j_value.rbegin();
it += 3;
CHECK((j_value.rbegin() + 3) == it);
CHECK(json::reverse_iterator(3 + j_value.rbegin()) == it);
CHECK((it - 3) == j_value.rbegin());
CHECK((it - j_value.rbegin()) == 3);
CHECK(it != j_value.rend());
it -= 3;
CHECK(*it == json(42));
}
{
auto it = j_value.crbegin();
it += 3;
CHECK((j_value.crbegin() + 3) == it);
CHECK(json::const_reverse_iterator(3 + j_value.crbegin()) == it);
CHECK((it - 3) == j_value.crbegin());
CHECK((it - j_value.crbegin()) == 3);
CHECK(it != j_value.crend());
it -= 3;
CHECK(*it == json(42));
}
}
}
SECTION("subscript operator")
{
SECTION("object")
{
{
auto it = j_object.rbegin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
{
auto it = j_object.crbegin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
}
}
SECTION("array")
{
{
auto it = j_array.rbegin();
CHECK(it[0] == json(6));
CHECK(it[1] == json(5));
CHECK(it[2] == json(4));
CHECK(it[3] == json(3));
CHECK(it[4] == json(2));
CHECK(it[5] == json(1));
}
{
auto it = j_array.crbegin();
CHECK(it[0] == json(6));
CHECK(it[1] == json(5));
CHECK(it[2] == json(4));
CHECK(it[3] == json(3));
CHECK(it[4] == json(2));
CHECK(it[5] == json(1));
}
}
SECTION("null")
{
{
auto it = j_null.rbegin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
{
auto it = j_null.crbegin();
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
}
SECTION("value")
{
{
auto it = j_value.rbegin();
CHECK(it[0] == json(42));
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
{
auto it = j_value.crbegin();
CHECK(it[0] == json(42));
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
}
}
}
}
#if JSON_HAS_RANGES
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
SECTION("ranges")
{
SECTION("concepts")
{
using nlohmann::detail::iteration_proxy_value;
CHECK(std::bidirectional_iterator<json::iterator>);
CHECK(std::input_iterator<iteration_proxy_value<json::iterator>>);
CHECK(std::is_same<json::iterator, std::ranges::iterator_t<json>>::value);
CHECK(std::ranges::bidirectional_range<json>);
using nlohmann::detail::iteration_proxy;
using items_type = decltype(std::declval<json&>().items());
CHECK(std::is_same<items_type, iteration_proxy<json::iterator>>::value);
CHECK(std::is_same<iteration_proxy_value<json::iterator>, std::ranges::iterator_t<items_type>>::value);
CHECK(std::ranges::input_range<items_type>);
}
// libstdc++ algorithms don't work with Clang 15 (04/2022)
#if !DOCTEST_CLANG || (DOCTEST_CLANG && defined(__GLIBCXX__))
SECTION("algorithms")
{
SECTION("copy")
{
json j{"foo", "bar"};
auto j_copied = json::array();
std::ranges::copy(j, std::back_inserter(j_copied));
CHECK(j == j_copied);
}
SECTION("find_if")
{
json j{1, 3, 2, 4};
auto j_even = json::array();
#if JSON_USE_IMPLICIT_CONVERSIONS
auto it = std::ranges::find_if(j, [](int v) noexcept
{
return (v % 2) == 0;
});
#else
auto it = std::ranges::find_if(j, [](const json & j) noexcept
{
int v;
j.get_to(v);
return (v % 2) == 0;
});
#endif
CHECK(*it == 2);
}
}
#endif
// libstdc++ views don't work with Clang 15 (04/2022)
// libc++ hides limited ranges implementation behind guard macro
#if !(DOCTEST_CLANG && (defined(__GLIBCXX__) || defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)))
SECTION("views")
{
SECTION("reverse")
{
json j{1, 2, 3, 4, 5};
json j_expected{5, 4, 3, 2, 1};
auto reversed = j | std::views::reverse;
CHECK(std::ranges::equal(reversed, j_expected));
}
SECTION("transform")
{
json j
{
{ "a_key", "a_value"},
{ "b_key", "b_value"},
{ "c_key", "c_value"},
};
json j_expected{"a_key", "b_key", "c_key"};
auto transformed = j.items() | std::views::transform([](const auto & item)
{
return item.key();
});
auto j_transformed = json::array();
std::ranges::copy(transformed, std::back_inserter(j_transformed));
CHECK(j_transformed == j_expected);
}
}
#endif
}
#endif
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,787 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
#ifdef JSON_TEST_NO_GLOBAL_UDLS
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif
#include <map>
#include <sstream>
TEST_CASE("JSON pointers")
{
SECTION("errors")
{
CHECK_THROWS_WITH_AS(json::json_pointer("foo"),
"[json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo'", json::parse_error&);
CHECK_THROWS_WITH_AS(json::json_pointer("/~~"),
"[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&);
CHECK_THROWS_WITH_AS(json::json_pointer("/~"),
"[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&);
json::json_pointer p;
CHECK_THROWS_WITH_AS(p.top(),
"[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&);
CHECK_THROWS_WITH_AS(p.pop_back(),
"[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&);
SECTION("array index error")
{
json v = {1, 2, 3, 4};
json::json_pointer const ptr("/10e");
CHECK_THROWS_WITH_AS(v[ptr],
"[json.exception.out_of_range.404] unresolved reference token '10e'", json::out_of_range&);
}
}
SECTION("examples from RFC 6901")
{
SECTION("nonconst access")
{
json j = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_json;
// the whole document
CHECK(j[json::json_pointer()] == j);
CHECK(j[json::json_pointer("")] == j);
CHECK(j.contains(json::json_pointer()));
CHECK(j.contains(json::json_pointer("")));
// array access
CHECK(j[json::json_pointer("/foo")] == j["foo"]);
CHECK(j.contains(json::json_pointer("/foo")));
CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
CHECK(j.contains(json::json_pointer("/foo/0")));
CHECK(j.contains(json::json_pointer("/foo/1")));
CHECK(!j.contains(json::json_pointer("/foo/3")));
CHECK(!j.contains(json::json_pointer("/foo/+")));
CHECK(!j.contains(json::json_pointer("/foo/1+2")));
CHECK(!j.contains(json::json_pointer("/foo/-")));
// checked array access
CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
// empty string access
CHECK(j[json::json_pointer("/")] == j[""]);
CHECK(j.contains(json::json_pointer("")));
CHECK(j.contains(json::json_pointer("/")));
// other cases
CHECK(j[json::json_pointer("/ ")] == j[" "]);
CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
// contains
CHECK(j.contains(json::json_pointer("/ ")));
CHECK(j.contains(json::json_pointer("/c%d")));
CHECK(j.contains(json::json_pointer("/e^f")));
CHECK(j.contains(json::json_pointer("/g|h")));
CHECK(j.contains(json::json_pointer("/i\\j")));
CHECK(j.contains(json::json_pointer("/k\"l")));
// checked access
CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
// escaped access
CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
CHECK(j.contains(json::json_pointer("/a~1b")));
CHECK(j.contains(json::json_pointer("/m~0n")));
// unescaped access to nonexisting values yield object creation
CHECK(!j.contains(json::json_pointer("/a/b")));
CHECK_NOTHROW(j[json::json_pointer("/a/b")] = 42);
CHECK(j.contains(json::json_pointer("/a/b")));
CHECK(j["a"]["b"] == json(42));
CHECK(!j.contains(json::json_pointer("/a/c/1")));
CHECK_NOTHROW(j[json::json_pointer("/a/c/1")] = 42);
CHECK(j["a"]["c"] == json({nullptr, 42}));
CHECK(j.contains(json::json_pointer("/a/c/1")));
CHECK(!j.contains(json::json_pointer("/a/d/-")));
CHECK_NOTHROW(j[json::json_pointer("/a/d/-")] = 42);
CHECK(!j.contains(json::json_pointer("/a/d/-")));
CHECK(j["a"]["d"] == json::array({42}));
// "/a/b" works for JSON {"a": {"b": 42}}
CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42));
// unresolved access
json j_primitive = 1;
CHECK_THROWS_WITH_AS(j_primitive["/foo"_json_pointer],
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
CHECK_THROWS_WITH_AS(j_primitive.at("/foo"_json_pointer),
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
CHECK(!j_primitive.contains(json::json_pointer("/foo")));
}
SECTION("const access")
{
const json j = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_json;
// the whole document
CHECK(j[json::json_pointer()] == j);
CHECK(j[json::json_pointer("")] == j);
// array access
CHECK(j[json::json_pointer("/foo")] == j["foo"]);
CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
// checked array access
CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
// empty string access
CHECK(j[json::json_pointer("/")] == j[""]);
// other cases
CHECK(j[json::json_pointer("/ ")] == j[" "]);
CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
// checked access
CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
// escaped access
CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
// unescaped access
CHECK_THROWS_WITH_AS(j.at(json::json_pointer("/a/b")),
"[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&);
// unresolved access
const json j_primitive = 1;
CHECK_THROWS_WITH_AS(j_primitive["/foo"_json_pointer],
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
CHECK_THROWS_WITH_AS(j_primitive.at("/foo"_json_pointer),
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
}
SECTION("user-defined string literal")
{
json j = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_json;
// the whole document
CHECK(j[""_json_pointer] == j);
CHECK(j.contains(""_json_pointer));
// array access
CHECK(j["/foo"_json_pointer] == j["foo"]);
CHECK(j["/foo/0"_json_pointer] == j["foo"][0]);
CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
CHECK(j.contains("/foo"_json_pointer));
CHECK(j.contains("/foo/0"_json_pointer));
CHECK(j.contains("/foo/1"_json_pointer));
CHECK(!j.contains("/foo/-"_json_pointer));
}
}
SECTION("array access")
{
SECTION("nonconst access")
{
json j = {1, 2, 3};
const json j_const = j;
// check reading access
CHECK(j["/0"_json_pointer] == j[0]);
CHECK(j["/1"_json_pointer] == j[1]);
CHECK(j["/2"_json_pointer] == j[2]);
// assign to existing index
j["/1"_json_pointer] = 13;
CHECK(j[1] == json(13));
// assign to nonexisting index
j["/3"_json_pointer] = 33;
CHECK(j[3] == json(33));
// assign to nonexisting index (with gap)
j["/5"_json_pointer] = 55;
CHECK(j == json({1, 13, 3, 33, nullptr, 55}));
// error with leading 0
CHECK_THROWS_WITH_AS(j["/01"_json_pointer],
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
CHECK_THROWS_WITH_AS(j_const["/01"_json_pointer],
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
CHECK_THROWS_WITH_AS(j.at("/01"_json_pointer),
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
CHECK_THROWS_WITH_AS(j_const.at("/01"_json_pointer),
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
CHECK(!j.contains("/01"_json_pointer));
CHECK(!j.contains("/01"_json_pointer));
CHECK(!j_const.contains("/01"_json_pointer));
CHECK(!j_const.contains("/01"_json_pointer));
// error with incorrect numbers
CHECK_THROWS_WITH_AS(j["/one"_json_pointer] = 1,
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
CHECK_THROWS_WITH_AS(j_const["/one"_json_pointer] == 1,
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
CHECK_THROWS_WITH_AS(j.at("/one"_json_pointer) = 1,
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
CHECK_THROWS_WITH_AS(j_const.at("/one"_json_pointer) == 1,
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
CHECK_THROWS_WITH_AS(j["/+1"_json_pointer] = 1,
"[json.exception.parse_error.109] parse error: array index '+1' is not a number", json::parse_error&);
CHECK_THROWS_WITH_AS(j_const["/+1"_json_pointer] == 1,
"[json.exception.parse_error.109] parse error: array index '+1' is not a number", json::parse_error&);
CHECK_THROWS_WITH_AS(j["/1+1"_json_pointer] = 1,
"[json.exception.out_of_range.404] unresolved reference token '1+1'", json::out_of_range&);
CHECK_THROWS_WITH_AS(j_const["/1+1"_json_pointer] == 1,
"[json.exception.out_of_range.404] unresolved reference token '1+1'", json::out_of_range&);
{
auto too_large_index = std::to_string((std::numeric_limits<unsigned long long>::max)()) + "1";
json::json_pointer const jp(std::string("/") + too_large_index);
std::string const throw_msg = std::string("[json.exception.out_of_range.404] unresolved reference token '") + too_large_index + "'";
CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&);
CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&);
}
// on some machines, the check below is not constant
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
DOCTEST_MSVC_SUPPRESS_WARNING(4127)
if (sizeof(typename json::size_type) < sizeof(unsigned long long))
{
auto size_type_max_uul = static_cast<unsigned long long>((std::numeric_limits<json::size_type>::max)());
auto too_large_index = std::to_string(size_type_max_uul);
json::json_pointer const jp(std::string("/") + too_large_index);
std::string const throw_msg = std::string("[json.exception.out_of_range.410] array index ") + too_large_index + " exceeds size_type";
CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&);
CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&);
}
DOCTEST_MSVC_SUPPRESS_WARNING_POP
CHECK_THROWS_WITH_AS(j.at("/one"_json_pointer) = 1,
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
CHECK_THROWS_WITH_AS(j_const.at("/one"_json_pointer) == 1,
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
CHECK(!j.contains("/one"_json_pointer));
CHECK(!j.contains("/one"_json_pointer));
CHECK(!j_const.contains("/one"_json_pointer));
CHECK(!j_const.contains("/one"_json_pointer));
CHECK_THROWS_WITH_AS(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(),
"[json.exception.parse_error.109] parse error: array index 'three' is not a number", json::parse_error&);
// assign to "-"
j["/-"_json_pointer] = 99;
CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
// error when using "-" in const object
CHECK_THROWS_WITH_AS(j_const["/-"_json_pointer],
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
CHECK(!j_const.contains("/-"_json_pointer));
// error when using "-" with at
CHECK_THROWS_WITH_AS(j.at("/-"_json_pointer),
"[json.exception.out_of_range.402] array index '-' (7) is out of range", json::out_of_range&);
CHECK_THROWS_WITH_AS(j_const.at("/-"_json_pointer),
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
CHECK(!j_const.contains("/-"_json_pointer));
}
SECTION("const access")
{
const json j = {1, 2, 3};
// check reading access
CHECK(j["/0"_json_pointer] == j[0]);
CHECK(j["/1"_json_pointer] == j[1]);
CHECK(j["/2"_json_pointer] == j[2]);
// assign to nonexisting index
CHECK_THROWS_WITH_AS(j.at("/3"_json_pointer),
"[json.exception.out_of_range.401] array index 3 is out of range", json::out_of_range&);
CHECK(!j.contains("/3"_json_pointer));
// assign to nonexisting index (with gap)
CHECK_THROWS_WITH_AS(j.at("/5"_json_pointer),
"[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&);
CHECK(!j.contains("/5"_json_pointer));
// assign to "-"
CHECK_THROWS_WITH_AS(j["/-"_json_pointer],
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
CHECK_THROWS_WITH_AS(j.at("/-"_json_pointer),
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
CHECK(!j.contains("/-"_json_pointer));
}
}
SECTION("flatten")
{
json j =
{
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99},
{"", "empty string"},
{"/", "slash"},
{"~", "tilde"},
{"~1", "tilde1"}
}
}
};
json j_flatten =
{
{"/pi", 3.141},
{"/happy", true},
{"/name", "Niels"},
{"/nothing", nullptr},
{"/answer/everything", 42},
{"/list/0", 1},
{"/list/1", 0},
{"/list/2", 2},
{"/object/currency", "USD"},
{"/object/value", 42.99},
{"/object/", "empty string"},
{"/object/~1", "slash"},
{"/object/~0", "tilde"},
{"/object/~01", "tilde1"}
};
// check if flattened result is as expected
CHECK(j.flatten() == j_flatten);
// check if unflattened result is as expected
CHECK(j_flatten.unflatten() == j);
// error for nonobjects
CHECK_THROWS_WITH_AS(json(1).unflatten(),
"[json.exception.type_error.314] only objects can be unflattened", json::type_error&);
// error for nonprimitve values
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_AS(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] (/~11) values in object must be primitive", json::type_error&);
#else
CHECK_THROWS_WITH_AS(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] values in object must be primitive", json::type_error&);
#endif
// error for conflicting values
json const j_error = {{"", 42}, {"/foo", 17}};
CHECK_THROWS_WITH_AS(j_error.unflatten(),
"[json.exception.type_error.313] invalid value to unflatten", json::type_error&);
// explicit roundtrip check
CHECK(j.flatten().unflatten() == j);
// roundtrip for primitive values
json j_null;
CHECK(j_null.flatten().unflatten() == j_null);
json j_number = 42;
CHECK(j_number.flatten().unflatten() == j_number);
json j_boolean = false;
CHECK(j_boolean.flatten().unflatten() == j_boolean);
json j_string = "foo";
CHECK(j_string.flatten().unflatten() == j_string);
// roundtrip for empty structured values (will be unflattened to null)
json const j_array(json::value_t::array);
CHECK(j_array.flatten().unflatten() == json());
json const j_object(json::value_t::object);
CHECK(j_object.flatten().unflatten() == json());
}
SECTION("string representation")
{
for (const auto* ptr_str :
{"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n"
})
{
json::json_pointer const ptr(ptr_str);
std::stringstream ss;
ss << ptr;
CHECK(ptr.to_string() == ptr_str);
CHECK(std::string(ptr) == ptr_str);
CHECK(ss.str() == ptr_str);
}
}
SECTION("conversion")
{
SECTION("array")
{
json j;
// all numbers -> array
j["/12"_json_pointer] = 0;
CHECK(j.is_array());
}
SECTION("object")
{
json j;
// contains a number, but is not a number -> object
j["/a12"_json_pointer] = 0;
CHECK(j.is_object());
}
}
SECTION("empty, push, pop and parent")
{
const json j =
{
{"", "Hello"},
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99},
{"", "empty string"},
{"/", "slash"},
{"~", "tilde"},
{"~1", "tilde1"}
}
}
};
// empty json_pointer returns the root JSON-object
auto ptr = ""_json_pointer;
CHECK(ptr.empty());
CHECK(j[ptr] == j);
// simple field access
ptr.push_back("pi");
CHECK(!ptr.empty());
CHECK(j[ptr] == j["pi"]);
ptr.pop_back();
CHECK(ptr.empty());
CHECK(j[ptr] == j);
// object and children access
const std::string answer("answer");
ptr.push_back(answer);
ptr.push_back("everything");
CHECK(!ptr.empty());
CHECK(j[ptr] == j["answer"]["everything"]);
// check access via const pointer
const auto cptr = ptr;
CHECK(cptr.back() == "everything");
ptr.pop_back();
ptr.pop_back();
CHECK(ptr.empty());
CHECK(j[ptr] == j);
// push key which has to be encoded
ptr.push_back("object");
ptr.push_back("/");
CHECK(j[ptr] == j["object"]["/"]);
CHECK(ptr.to_string() == "/object/~1");
CHECK(j[ptr.parent_pointer()] == j["object"]);
ptr = ptr.parent_pointer().parent_pointer();
CHECK(ptr.empty());
CHECK(j[ptr] == j);
// parent-pointer of the empty json_pointer is empty
ptr = ptr.parent_pointer();
CHECK(ptr.empty());
CHECK(j[ptr] == j);
CHECK_THROWS_WITH(ptr.pop_back(),
"[json.exception.out_of_range.405] JSON pointer has no parent");
}
SECTION("operators")
{
const json j =
{
{"", "Hello"},
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99},
{"", "empty string"},
{"/", "slash"},
{"~", "tilde"},
{"~1", "tilde1"}
}
}
};
// empty json_pointer returns the root JSON-object
auto ptr = ""_json_pointer;
CHECK(j[ptr] == j);
// simple field access
ptr = ptr / "pi";
CHECK(j[ptr] == j["pi"]);
ptr.pop_back();
CHECK(j[ptr] == j);
// object and children access
const std::string answer("answer");
ptr /= answer;
ptr = ptr / "everything";
CHECK(j[ptr] == j["answer"]["everything"]);
ptr.pop_back();
ptr.pop_back();
CHECK(j[ptr] == j);
CHECK(ptr / ""_json_pointer == ptr);
CHECK(j["/answer"_json_pointer / "/everything"_json_pointer] == j["answer"]["everything"]);
// list children access
CHECK(j["/list"_json_pointer / 1] == j["list"][1]);
// push key which has to be encoded
ptr /= "object";
ptr = ptr / "/";
CHECK(j[ptr] == j["object"]["/"]);
CHECK(ptr.to_string() == "/object/~1");
}
SECTION("equality comparison")
{
const char* ptr_cpstring = "/foo/bar";
const char ptr_castring[] = "/foo/bar"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
std::string ptr_string{"/foo/bar"};
auto ptr1 = json::json_pointer(ptr_string);
auto ptr2 = json::json_pointer(ptr_string);
// build with C++20 to test rewritten candidates
// JSON_HAS_CPP_20
CHECK(ptr1 == ptr2);
CHECK(ptr1 == "/foo/bar");
CHECK(ptr1 == ptr_cpstring);
CHECK(ptr1 == ptr_castring);
CHECK(ptr1 == ptr_string);
CHECK("/foo/bar" == ptr1);
CHECK(ptr_cpstring == ptr1);
CHECK(ptr_castring == ptr1);
CHECK(ptr_string == ptr1);
CHECK_FALSE(ptr1 != ptr2);
CHECK_FALSE(ptr1 != "/foo/bar");
CHECK_FALSE(ptr1 != ptr_cpstring);
CHECK_FALSE(ptr1 != ptr_castring);
CHECK_FALSE(ptr1 != ptr_string);
CHECK_FALSE("/foo/bar" != ptr1);
CHECK_FALSE(ptr_cpstring != ptr1);
CHECK_FALSE(ptr_castring != ptr1);
CHECK_FALSE(ptr_string != ptr1);
SECTION("exceptions")
{
CHECK_THROWS_WITH_AS(ptr1 == "foo",
"[json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo'", json::parse_error&);
CHECK_THROWS_WITH_AS("foo" == ptr1,
"[json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo'", json::parse_error&);
CHECK_THROWS_WITH_AS(ptr1 == "/~~",
"[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&);
CHECK_THROWS_WITH_AS("/~~" == ptr1,
"[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&);
}
}
SECTION("less-than comparison")
{
auto ptr1 = json::json_pointer("/foo/a");
auto ptr2 = json::json_pointer("/foo/b");
CHECK(ptr1 < ptr2);
CHECK_FALSE(ptr2 < ptr1);
// build with C++20
// JSON_HAS_CPP_20
#if JSON_HAS_THREE_WAY_COMPARISON
CHECK((ptr1 <=> ptr2) == std::strong_ordering::less); // *NOPAD*
CHECK(ptr2 > ptr1);
#endif
}
SECTION("usable as map key")
{
auto ptr = json::json_pointer("/foo");
std::map<json::json_pointer, int> m;
m[ptr] = 42;
CHECK(m.find(ptr) != m.end());
}
SECTION("backwards compatibility and mixing")
{
json j = R"(
{
"foo": ["bar", "baz"]
}
)"_json;
using nlohmann::ordered_json;
using json_ptr_str = nlohmann::json_pointer<std::string>;
using json_ptr_j = nlohmann::json_pointer<json>;
using json_ptr_oj = nlohmann::json_pointer<ordered_json>;
CHECK(std::is_same<json_ptr_str::string_t, json::json_pointer::string_t>::value);
CHECK(std::is_same<json_ptr_str::string_t, ordered_json::json_pointer::string_t>::value);
CHECK(std::is_same<json_ptr_str::string_t, json_ptr_j::string_t>::value);
CHECK(std::is_same<json_ptr_str::string_t, json_ptr_oj::string_t>::value);
std::string const ptr_string{"/foo/0"};
json_ptr_str ptr{ptr_string};
json_ptr_j ptr_j{ptr_string};
json_ptr_oj ptr_oj{ptr_string};
CHECK(j.contains(ptr));
CHECK(j.contains(ptr_j));
CHECK(j.contains(ptr_oj));
CHECK(j.at(ptr) == j.at(ptr_j));
CHECK(j.at(ptr) == j.at(ptr_oj));
CHECK(j[ptr] == j[ptr_j]);
CHECK(j[ptr] == j[ptr_oj]);
CHECK(j.value(ptr, "x") == j.value(ptr_j, "x"));
CHECK(j.value(ptr, "x") == j.value(ptr_oj, "x"));
CHECK(ptr == ptr_j);
CHECK(ptr == ptr_oj);
CHECK_FALSE(ptr != ptr_j);
CHECK_FALSE(ptr != ptr_oj);
SECTION("equality comparison")
{
// build with C++20 to test rewritten candidates
// JSON_HAS_CPP_20
CHECK(ptr == ptr_j);
CHECK(ptr == ptr_oj);
CHECK(ptr_j == ptr);
CHECK(ptr_j == ptr_oj);
CHECK(ptr_oj == ptr_j);
CHECK(ptr_oj == ptr);
CHECK_FALSE(ptr != ptr_j);
CHECK_FALSE(ptr != ptr_oj);
CHECK_FALSE(ptr_j != ptr);
CHECK_FALSE(ptr_j != ptr_oj);
CHECK_FALSE(ptr_oj != ptr_j);
CHECK_FALSE(ptr_oj != ptr);
}
}
}

View File

@ -0,0 +1,29 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <algorithm>
TEST_CASE("tests on very large JSONs")
{
SECTION("issue #1419 - Segmentation fault (stack overflow) due to unbounded recursion")
{
const auto depth = 5000000;
std::string s(static_cast<std::size_t>(2 * depth), '[');
std::fill(s.begin() + depth, s.end(), ']');
json _;
CHECK_NOTHROW(_ = nlohmann::json::parse(s));
}
}

View File

@ -0,0 +1,244 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#ifdef JSON_TEST_NO_GLOBAL_UDLS
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif
TEST_CASE("JSON Merge Patch")
{
SECTION("examples from RFC 7396")
{
SECTION("Section 1")
{
json document = R"({
"a": "b",
"c": {
"d": "e",
"f": "g"
}
})"_json;
json const patch = R"({
"a": "z",
"c": {
"f": null
}
})"_json;
json expected = R"({
"a": "z",
"c": {
"d": "e"
}
})"_json;
document.merge_patch(patch);
CHECK(document == expected);
}
SECTION("Section 3")
{
json document = R"({
"title": "Goodbye!",
"author": {
"givenName": "John",
"familyName": "Doe"
},
"tags": [
"example",
"sample"
],
"content": "This will be unchanged"
})"_json;
json const patch = R"({
"title": "Hello!",
"phoneNumber": "+01-123-456-7890",
"author": {
"familyName": null
},
"tags": [
"example"
]
})"_json;
json expected = R"({
"title": "Hello!",
"author": {
"givenName": "John"
},
"tags": [
"example"
],
"content": "This will be unchanged",
"phoneNumber": "+01-123-456-7890"
})"_json;
document.merge_patch(patch);
CHECK(document == expected);
}
SECTION("Appendix A")
{
SECTION("Example 1")
{
json original = R"({"a":"b"})"_json;
json const patch = R"({"a":"c"})"_json;
json result = R"({"a":"c"})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 2")
{
json original = R"({"a":"b"})"_json;
json const patch = R"({"b":"c"})"_json;
json result = R"({"a":"b", "b":"c"})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 3")
{
json original = R"({"a":"b"})"_json;
json const patch = R"({"a":null})"_json;
json result = R"({})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 4")
{
json original = R"({"a":"b","b":"c"})"_json;
json const patch = R"({"a":null})"_json;
json result = R"({"b":"c"})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 5")
{
json original = R"({"a":["b"]})"_json;
json const patch = R"({"a":"c"})"_json;
json result = R"({"a":"c"})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 6")
{
json original = R"({"a":"c"})"_json;
json const patch = R"({"a":["b"]})"_json;
json result = R"({"a":["b"]})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 7")
{
json original = R"({"a":{"b": "c"}})"_json;
json const patch = R"({"a":{"b":"d","c":null}})"_json;
json result = R"({"a": {"b": "d"}})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 8")
{
json original = R"({"a":[{"b":"c"}]})"_json;
json const patch = R"({"a":[1]})"_json;
json result = R"({"a":[1]})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 9")
{
json original = R"(["a","b"])"_json;
json const patch = R"(["c","d"])"_json;
json result = R"(["c","d"])"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 10")
{
json original = R"({"a":"b"})"_json;
json const patch = R"(["c"])"_json;
json result = R"(["c"])"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 11")
{
json original = R"({"a":"foo"})"_json;
json const patch = R"(null)"_json;
json result = R"(null)"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 12")
{
json original = R"({"a":"foo"})"_json;
json const patch = R"("bar")"_json;
json result = R"("bar")"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 13")
{
json original = R"({"e":null})"_json;
json const patch = R"({"a":1})"_json;
json result = R"({"e":null,"a":1})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 14")
{
json original = R"([1,2])"_json;
json const patch = R"({"a":"b","c":null})"_json;
json result = R"({"a":"b"})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
SECTION("Example 15")
{
json original = R"({})"_json;
json const patch = R"({"a":{"bb":{"ccc":null}}})"_json;
json result = R"({"a":{"bb":{}}})"_json;
original.merge_patch(patch);
CHECK(original == result);
}
}
}
}

View File

@ -0,0 +1,36 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("version information")
{
SECTION("meta()")
{
json j = json::meta();
CHECK(j["name"] == "JSON for Modern C++");
CHECK(j["copyright"] == "(C) 2013-2022 Niels Lohmann");
CHECK(j["url"] == "https://github.com/nlohmann/json");
CHECK(j["version"] == json(
{
{"string", "3.11.2"},
{"major", 3},
{"minor", 11},
{"patch", 2}
}));
CHECK(j.find("platform") != j.end());
CHECK(j.at("compiler").find("family") != j.at("compiler").end());
CHECK(j.at("compiler").find("version") != j.at("compiler").end());
CHECK(j.at("compiler").find("c++") != j.at("compiler").end());
}
}

View File

@ -0,0 +1,953 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("modifiers")
{
SECTION("clear()")
{
SECTION("boolean")
{
json j = true;
json const k = j;
j.clear();
CHECK(j == json(json::value_t::boolean));
CHECK(j == json(k.type()));
}
SECTION("string")
{
json j = "hello world";
json const k = j;
j.clear();
CHECK(j == json(json::value_t::string));
CHECK(j == json(k.type()));
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array();
json const k = j;
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::array));
CHECK(j == json(k.type()));
}
SECTION("filled array")
{
json j = {1, 2, 3};
json const k = j;
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::array));
CHECK(j == json(k.type()));
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object();
json const k = j;
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::object));
CHECK(j == json(k.type()));
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}};
json const k = j;
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::object));
CHECK(j == json(k.type()));
}
}
SECTION("binary")
{
SECTION("empty binary")
{
json j = json::binary({});
json const k = j;
j.clear();
CHECK(!j.empty());
CHECK(j == json(json::value_t::binary));
CHECK(j == json(k.type()));
}
SECTION("filled binary")
{
json j = json::binary({1, 2, 3, 4, 5});
json const k = j;
j.clear();
CHECK(!j.empty());
CHECK(j == json(json::value_t::binary));
CHECK(j == json(k.type()));
}
}
SECTION("number (integer)")
{
json j = 23;
json const k = j;
j.clear();
CHECK(j == json(json::value_t::number_integer));
CHECK(j == json(k.type()));
}
SECTION("number (unsigned)")
{
json j = 23u;
json const k = j;
j.clear();
CHECK(j == json(json::value_t::number_integer));
CHECK(j == json(k.type()));
}
SECTION("number (float)")
{
json j = 23.42;
json const k = j;
j.clear();
CHECK(j == json(json::value_t::number_float));
CHECK(j == json(k.type()));
}
SECTION("null")
{
json j = nullptr;
json const k = j;
j.clear();
CHECK(j == json(json::value_t::null));
CHECK(j == json(k.type()));
}
}
SECTION("push_back()")
{
SECTION("to array")
{
SECTION("json&&")
{
SECTION("null")
{
json j;
j.push_back(1);
j.push_back(2);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2}));
}
SECTION("array")
{
json j = {1, 2, 3};
j.push_back("Hello");
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
CHECK_THROWS_WITH_AS(j.push_back("Hello"), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
}
}
SECTION("const json&")
{
SECTION("null")
{
json j;
json const k(1);
j.push_back(k);
j.push_back(k);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 1}));
}
SECTION("array")
{
json j = {1, 2, 3};
json const k("Hello");
j.push_back(k);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
json const k("Hello");
CHECK_THROWS_WITH_AS(j.push_back(k), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
}
}
}
SECTION("to object")
{
SECTION("null")
{
json j;
j.push_back(json::object_t::value_type({"one", 1}));
j.push_back(json::object_t::value_type({"two", 2}));
CHECK(j.type() == json::value_t::object);
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("object")
{
json j(json::value_t::object);
j.push_back(json::object_t::value_type({"one", 1}));
j.push_back(json::object_t::value_type({"two", 2}));
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("other type")
{
json j = 1;
json const k("Hello");
CHECK_THROWS_WITH_AS(j.push_back(json::object_t::value_type({"one", 1})), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
}
}
SECTION("with initializer_list")
{
SECTION("null")
{
json j;
j.push_back({"foo", "bar"});
CHECK(j == json::array({{"foo", "bar"}}));
json k;
k.push_back({1, 2, 3});
CHECK(k == json::array({{1, 2, 3}}));
}
SECTION("array")
{
json j = {1, 2, 3};
j.push_back({"foo", "bar"});
CHECK(j == json({1, 2, 3, {"foo", "bar"}}));
json k = {1, 2, 3};
k.push_back({1, 2, 3});
CHECK(k == json({1, 2, 3, {1, 2, 3}}));
}
SECTION("object")
{
json j = {{"key1", 1}};
j.push_back({"key2", "bar"});
CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
// invalid values (no string/val pair)
CHECK_THROWS_WITH_AS(j.push_back({1}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
CHECK_THROWS_WITH_AS(j.push_back({1, 2}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
CHECK_THROWS_WITH_AS(j.push_back({1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
}
}
}
SECTION("emplace_back()")
{
SECTION("to array")
{
SECTION("null")
{
json j;
auto& x1 = j.emplace_back(1);
CHECK(x1 == 1);
auto& x2 = j.emplace_back(2);
CHECK(x2 == 2);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2}));
}
SECTION("array")
{
json j = {1, 2, 3};
auto& x = j.emplace_back("Hello");
CHECK(x == "Hello");
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("multiple values")
{
json j;
auto& x = j.emplace_back(3, "foo");
CHECK(x == json({"foo", "foo", "foo"}));
CHECK(j.type() == json::value_t::array);
CHECK(j == json({{"foo", "foo", "foo"}}));
}
}
SECTION("other type")
{
json j = 1;
CHECK_THROWS_WITH_AS(j.emplace_back("Hello"), "[json.exception.type_error.311] cannot use emplace_back() with number", json::type_error&);
}
}
SECTION("emplace()")
{
SECTION("to object")
{
SECTION("null")
{
// start with a null value
json j;
// add a new key
auto res1 = j.emplace("foo", "bar");
CHECK(res1.second == true);
CHECK(*res1.first == "bar");
// the null value is changed to an object
CHECK(j.type() == json::value_t::object);
// add a new key
auto res2 = j.emplace("baz", "bam");
CHECK(res2.second == true);
CHECK(*res2.first == "bam");
// we try to insert at given key - no change
auto res3 = j.emplace("baz", "bad");
CHECK(res3.second == false);
CHECK(*res3.first == "bam");
// the final object
CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
}
SECTION("object")
{
// start with an object
json j = {{"foo", "bar"}};
// add a new key
auto res1 = j.emplace("baz", "bam");
CHECK(res1.second == true);
CHECK(*res1.first == "bam");
// add an existing key
auto res2 = j.emplace("foo", "bad");
CHECK(res2.second == false);
CHECK(*res2.first == "bar");
// check final object
CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
}
}
SECTION("other type")
{
json j = 1;
CHECK_THROWS_WITH_AS(j.emplace("foo", "bar"), "[json.exception.type_error.311] cannot use emplace() with number", json::type_error&);
}
}
SECTION("operator+=")
{
SECTION("to array")
{
SECTION("json&&")
{
SECTION("null")
{
json j;
j += 1;
j += 2;
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2}));
}
SECTION("array")
{
json j = {1, 2, 3};
j += "Hello";
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
CHECK_THROWS_WITH_AS(j += "Hello", "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
}
}
SECTION("const json&")
{
SECTION("null")
{
json j;
json const k(1);
j += k;
j += k;
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 1}));
}
SECTION("array")
{
json j = {1, 2, 3};
json const k("Hello");
j += k;
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
json const k("Hello");
CHECK_THROWS_WITH_AS(j += k, "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
}
}
}
SECTION("to object")
{
SECTION("null")
{
json j;
j += json::object_t::value_type({"one", 1});
j += json::object_t::value_type({"two", 2});
CHECK(j.type() == json::value_t::object);
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("object")
{
json j(json::value_t::object);
j += json::object_t::value_type({"one", 1});
j += json::object_t::value_type({"two", 2});
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("other type")
{
json j = 1;
json const k("Hello");
CHECK_THROWS_WITH_AS(j += json::object_t::value_type({"one", 1}), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
}
}
SECTION("with initializer_list")
{
SECTION("null")
{
json j;
j += {"foo", "bar"};
CHECK(j == json::array({{"foo", "bar"}}));
json k;
k += {1, 2, 3};
CHECK(k == json::array({{1, 2, 3}}));
}
SECTION("array")
{
json j = {1, 2, 3};
j += {"foo", "bar"};
CHECK(j == json({1, 2, 3, {"foo", "bar"}}));
json k = {1, 2, 3};
k += {1, 2, 3};
CHECK(k == json({1, 2, 3, {1, 2, 3}}));
}
SECTION("object")
{
json j = {{"key1", 1}};
j += {"key2", "bar"};
CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
json k = {{"key1", 1}};
CHECK_THROWS_WITH_AS((k += {1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
}
}
}
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 it = j_array.insert(j_array.end(), 0, 5);
CHECK(j_array.size() == 4);
// the returned iterator points to the first inserted element;
// there were 4 elements, so it should point to the 5th
CHECK(it == j_array.begin() + 4);
CHECK(j_array == json({1, 2, 3, 4}));
}
}
SECTION("range for array")
{
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")
{
json j_other_array2 = {"first", "second"};
CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.211] passed iterators may not belong to container",
json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), "[json.exception.invalid_iterator.210] iterators do not fit",
json::invalid_iterator&);
}
}
SECTION("range for object")
{
json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
json j_object2 = {{"eleven", "elf"}, {"seventeen", "siebzehn"}};
SECTION("proper usage")
{
j_object1.insert(j_object2.begin(), j_object2.end());
CHECK(j_object1.size() == 4);
}
SECTION("empty range")
{
j_object1.insert(j_object2.begin(), j_object2.begin());
CHECK(j_object1.size() == 2);
}
SECTION("invalid iterators")
{
json const j_other_array2 = {"first", "second"};
CHECK_THROWS_WITH_AS(j_array.insert(j_object2.begin(), j_object2.end()), "[json.exception.type_error.309] cannot use insert() with array", json::type_error&);
CHECK_THROWS_WITH_AS(j_object1.insert(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j_object1.insert(j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.202] iterators first and last must point to objects", json::invalid_iterator&);
}
}
SECTION("initializer list at position")
{
SECTION("insert before begin()")
{
auto it = j_array.insert(j_array.begin(), {7, 8, 9});
CHECK(j_array.size() == 7);
CHECK(*it == json(7));
CHECK(j_array.begin() == it);
CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4}));
}
SECTION("insert in the middle")
{
auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9});
CHECK(j_array.size() == 7);
CHECK(*it == json(7));
CHECK((it - j_array.begin()) == 2);
CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4}));
}
SECTION("insert before end()")
{
auto it = j_array.insert(j_array.end(), {7, 8, 9});
CHECK(j_array.size() == 7);
CHECK(*it == json(7));
CHECK((j_array.end() - it) == 3);
CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9}));
}
}
SECTION("invalid iterator")
{
// pass iterator to a different array
json j_another_array = {1, 2};
json j_yet_another_array = {"first", "second"};
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_value), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10, 11), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
}
SECTION("non-array type")
{
// call insert on a non-array type
json j_nonarray = 3;
json j_yet_another_array = {"first", "second"};
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_value), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
}
}
SECTION("update()")
{
SECTION("non-recursive (default)")
{
json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
json j_object2 = {{"three", "drei"}, {"two", "zwo"}};
json j_array = {1, 2, 3, 4};
SECTION("const reference")
{
SECTION("proper usage")
{
j_object1.update(j_object2);
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));
json j_null;
j_null.update(j_object2);
CHECK(j_null == j_object2);
}
SECTION("wrong types")
{
CHECK_THROWS_WITH_AS(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
CHECK_THROWS_WITH_AS(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
}
}
SECTION("iterator range")
{
SECTION("proper usage")
{
j_object1.update(j_object2.begin(), j_object2.end());
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));
json j_null;
j_null.update(j_object2.begin(), j_object2.end());
CHECK(j_null == j_object2);
}
SECTION("empty range")
{
j_object1.update(j_object2.begin(), j_object2.begin());
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwei"}}));
}
SECTION("invalid iterators")
{
json const j_other_array2 = {"first", "second"};
CHECK_THROWS_WITH_AS(j_array.update(j_object2.begin(), j_object2.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
CHECK_THROWS_WITH_AS(j_object1.update(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(j_object1.update(j_array.begin(), j_array.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
}
}
}
SECTION("recursive")
{
SECTION("const reference")
{
SECTION("extend object")
{
json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
json const j2 = {{"string", "t"}, {"numbers", {{"two", 2}}}};
j1.update(j2, true);
CHECK(j1 == json({{"string", "t"}, {"numbers", {{"one", 1}, {"two", 2}}}}));
}
SECTION("replace object")
{
json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
json const j2 = {{"string", "t"}, {"numbers", 1}};
j1.update(j2, true);
CHECK(j1 == json({{"string", "t"}, {"numbers", 1}}));
}
}
}
}
SECTION("swap()")
{
SECTION("json")
{
SECTION("member swap")
{
json j("hello world");
json k(42.23);
j.swap(k);
CHECK(j == json(42.23));
CHECK(k == json("hello world"));
}
SECTION("nonmember swap")
{
json j("hello world");
json k(42.23);
using std::swap;
swap(j, k);
CHECK(j == json(42.23));
CHECK(k == json("hello world"));
}
}
SECTION("array_t")
{
SECTION("array_t type")
{
json j = {1, 2, 3, 4};
json::array_t a = {"foo", "bar", "baz"};
j.swap(a);
CHECK(j == json({"foo", "bar", "baz"}));
j.swap(a);
CHECK(j == json({1, 2, 3, 4}));
}
SECTION("non-array_t type")
{
json j = 17;
json::array_t a = {"foo", "bar", "baz"};
CHECK_THROWS_WITH_AS(j.swap(a), "[json.exception.type_error.310] cannot use swap(array_t&) with number", json::type_error&);
}
}
SECTION("object_t")
{
SECTION("object_t type")
{
json j = {{"one", 1}, {"two", 2}};
json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
j.swap(o);
CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}}));
j.swap(o);
CHECK(j == json({{"one", 1}, {"two", 2}}));
}
SECTION("non-object_t type")
{
json j = 17;
json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
CHECK_THROWS_WITH_AS(j.swap(o), "[json.exception.type_error.310] cannot use swap(object_t&) with number", json::type_error&);
}
}
SECTION("string_t")
{
SECTION("string_t type")
{
json j = "Hello world";
json::string_t s = "Hallo Welt";
j.swap(s);
CHECK(j == json("Hallo Welt"));
j.swap(s);
CHECK(j == json("Hello world"));
}
SECTION("non-string_t type")
{
json j = 17;
json::string_t s = "Hallo Welt";
CHECK_THROWS_WITH_AS(j.swap(s), "[json.exception.type_error.310] cannot use swap(string_t&) with number", json::type_error&);
}
}
SECTION("binary_t")
{
SECTION("binary_t type")
{
json j = json::binary({1, 2, 3, 4});
json::binary_t s = {{5, 6, 7, 8}};
j.swap(s);
CHECK(j == json::binary({5, 6, 7, 8}));
j.swap(s);
CHECK(j == json::binary({1, 2, 3, 4}));
}
SECTION("binary_t::container_type type")
{
json j = json::binary({1, 2, 3, 4});
std::vector<std::uint8_t> s = {{5, 6, 7, 8}};
j.swap(s);
CHECK(j == json::binary({5, 6, 7, 8}));
j.swap(s);
CHECK(j == json::binary({1, 2, 3, 4}));
}
SECTION("non-binary_t type")
{
json j = 17;
json::binary_t s1 = {{1, 2, 3, 4}};
std::vector<std::uint8_t> s2 = {{5, 6, 7, 8}};
CHECK_THROWS_WITH_AS(j.swap(s1), "[json.exception.type_error.310] cannot use swap(binary_t&) with number", json::type_error);
CHECK_THROWS_WITH_AS(j.swap(s2), "[json.exception.type_error.310] cannot use swap(binary_t::container_type&) with number", json::type_error);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
#include <exception>
#include <iostream>
struct Foo
{
int a;
int b;
};
namespace nlohmann
{
template <>
struct adl_serializer<Foo>
{
static void to_json(json& j, Foo const& f)
{
switch (f.b)
{
case 0:
j["a"] = f.a;
break;
case 1:
j[0] = f.a;
break;
default:
j = "test";
}
if (f.a == 1)
{
throw std::runtime_error("b is invalid");
}
}
};
} // namespace nlohmann
TEST_CASE("check_for_mem_leak_on_adl_to_json-1")
{
try
{
const nlohmann::json j = Foo {1, 0};
std::cout << j.dump() << "\n";
}
catch (...) // NOLINT(bugprone-empty-catch)
{
// just ignore the exception in this POC
}
}
TEST_CASE("check_for_mem_leak_on_adl_to_json-2")
{
try
{
const nlohmann::json j = Foo {1, 1};
std::cout << j.dump() << "\n";
}
catch (...) // NOLINT(bugprone-empty-catch)
{
// just ignore the exception in this POC
}
}
TEST_CASE("check_for_mem_leak_on_adl_to_json-2")
{
try
{
const nlohmann::json j = Foo {1, 2};
std::cout << j.dump() << "\n";
}
catch (...) // NOLINT(bugprone-empty-catch)
{
// just ignore the exception in this POC
}
}

View File

@ -0,0 +1,74 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// disable -Wnoexcept due to struct pod_bis
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
#include <nlohmann/json.hpp>
using nlohmann::json;
namespace
{
enum test {};
struct pod {};
struct pod_bis {};
void to_json(json& /*unused*/, pod /*unused*/) noexcept;
void to_json(json& /*unused*/, pod_bis /*unused*/);
void from_json(const json& /*unused*/, pod /*unused*/) noexcept;
void from_json(const json& /*unused*/, pod_bis /*unused*/);
void to_json(json& /*unused*/, pod /*unused*/) noexcept {}
void to_json(json& /*unused*/, pod_bis /*unused*/) {}
void from_json(const json& /*unused*/, pod /*unused*/) noexcept {}
void from_json(const json& /*unused*/, pod_bis /*unused*/) {}
static_assert(noexcept(json{}), "");
static_assert(noexcept(nlohmann::to_json(std::declval<json&>(), 2)), "");
static_assert(noexcept(nlohmann::to_json(std::declval<json&>(), 2.5)), "");
static_assert(noexcept(nlohmann::to_json(std::declval<json&>(), true)), "");
static_assert(noexcept(nlohmann::to_json(std::declval<json&>(), test{})), "");
static_assert(noexcept(nlohmann::to_json(std::declval<json&>(), pod{})), "");
static_assert(!noexcept(nlohmann::to_json(std::declval<json&>(), pod_bis{})), "");
static_assert(noexcept(json(2)), "");
static_assert(noexcept(json(test{})), "");
static_assert(noexcept(json(pod{})), "");
static_assert(noexcept(std::declval<json>().get<pod>()), "");
static_assert(!noexcept(std::declval<json>().get<pod_bis>()), "");
static_assert(noexcept(json(pod{})), "");
} // namespace
TEST_CASE("noexcept")
{
// silence -Wunneeded-internal-declaration errors
static_cast<void>(static_cast<void(*)(json&, pod)>(&to_json));
static_cast<void>(static_cast<void(*)(json&, pod_bis)>(&to_json));
static_cast<void>(static_cast<void(*)(const json&, pod)>(&from_json));
static_cast<void>(static_cast<void(*)(const json&, pod_bis)>(&from_json));
SECTION("nothrow-copy-constructible exceptions")
{
// for ERR60-CPP (https://github.com/nlohmann/json/issues/531):
// Exceptions should be nothrow-copy-constructible. However, compilers
// treat std::runtime_exception differently in this regard. Therefore,
// we can only demand nothrow-copy-constructibility for our exceptions
// if std::runtime_exception is.
CHECK(std::is_nothrow_copy_constructible<json::exception>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
CHECK(std::is_nothrow_copy_constructible<json::parse_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
CHECK(std::is_nothrow_copy_constructible<json::invalid_iterator>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
CHECK(std::is_nothrow_copy_constructible<json::type_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
CHECK(std::is_nothrow_copy_constructible<json::out_of_range>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
CHECK(std::is_nothrow_copy_constructible<json::other_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
}
}
DOCTEST_GCC_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,71 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
using nlohmann::ordered_json;
TEST_CASE("ordered_json")
{
json j;
ordered_json oj;
j["element3"] = 3;
j["element1"] = 1;
j["element2"] = 2;
oj["element3"] = 3;
oj["element1"] = 1;
oj["element2"] = 2;
CHECK(j.dump() == "{\"element1\":1,\"element2\":2,\"element3\":3}");
CHECK(oj.dump() == "{\"element3\":3,\"element1\":1,\"element2\":2}");
CHECK(j == json(oj));
CHECK(ordered_json(json(oj)) == ordered_json(j));
j.erase("element1");
oj.erase("element1");
CHECK(j.dump() == "{\"element2\":2,\"element3\":3}");
CHECK(oj.dump() == "{\"element3\":3,\"element2\":2}");
// remove again and nothing changes
j.erase("element1");
oj.erase("element1");
CHECK(j.dump() == "{\"element2\":2,\"element3\":3}");
CHECK(oj.dump() == "{\"element3\":3,\"element2\":2}");
// There are no dup keys cause constructor calls emplace...
json const multi {{"z", 1}, {"m", 2}, {"m", 3}, {"y", 4}, {"m", 5}};
CHECK(multi.size() == 3);
CHECK(multi.dump() == "{\"m\":2,\"y\":4,\"z\":1}");
ordered_json multi_ordered {{"z", 1}, {"m", 2}, {"m", 3}, {"y", 4}, {"m", 5}};
CHECK(multi_ordered.size() == 3);
CHECK(multi_ordered.dump() == "{\"z\":1,\"m\":2,\"y\":4}");
CHECK(multi_ordered.erase("m") == 1);
CHECK(multi_ordered.dump() == "{\"z\":1,\"y\":4}");
// Ranged insert test.
// It seems that values shouldn't be overwritten. Only new values are added
json j1 {{"c", 1}, {"b", 2}, {"a", 3}};
const json j2 {{"c", 77}, {"d", 42}, {"a", 4}};
j1.insert( j2.cbegin(), j2.cend() );
CHECK(j1.size() == 4);
CHECK(j1.dump() == "{\"a\":3,\"b\":2,\"c\":1,\"d\":42}");
ordered_json oj1 {{"c", 1}, {"b", 2}, {"a", 3}};
const ordered_json oj2 {{"c", 77}, {"d", 42}, {"a", 4}};
oj1.insert( oj2.cbegin(), oj2.cend() );
CHECK(oj1.size() == 4);
CHECK(oj1.dump() == "{\"c\":1,\"b\":2,\"a\":3,\"d\":42}");
}

View File

@ -0,0 +1,310 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#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> const 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;
om.clear(); // silence a warning by forbidding having "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("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")
{
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 const vt1 {"eins", "1"};
ordered_map<std::string, std::string>::value_type const 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);
}
}
}

View File

@ -0,0 +1,479 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("pointer access")
{
SECTION("pointer access to object_t")
{
using test_type = json::object_t;
json value = {{"one", 1}, {"two", 2}};
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() != nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
}
SECTION("pointer access to const object_t")
{
using test_type = const json::object_t;
const json value = {{"one", 1}, {"two", 2}};
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() != nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
}
SECTION("pointer access to array_t")
{
using test_type = json::array_t;
json value = {1, 2, 3, 4};
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() != nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
}
SECTION("pointer access to const array_t")
{
using test_type = const json::array_t;
const json value = {1, 2, 3, 4};
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() != nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
}
SECTION("pointer access to string_t")
{
using test_type = json::string_t;
json value = "hello";
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() != nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
}
SECTION("pointer access to const string_t")
{
using test_type = const json::string_t;
const json value = "hello";
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() != nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
}
SECTION("pointer access to boolean_t")
{
using test_type = json::boolean_t;
json value = false;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() != nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
}
SECTION("pointer access to const boolean_t")
{
using test_type = const json::boolean_t;
const json value = false;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
//CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() != nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
}
SECTION("pointer access to number_integer_t")
{
using test_type = json::number_integer_t;
json value = 23;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() != nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
}
SECTION("pointer access to const number_integer_t")
{
using test_type = const json::number_integer_t;
const json value = 23;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() != nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
}
SECTION("pointer access to number_unsigned_t")
{
using test_type = json::number_unsigned_t;
json value = 23u;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() != nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() != nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
}
SECTION("pointer access to const number_unsigned_t")
{
using test_type = const json::number_unsigned_t;
const json value = 23u;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() != nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() != nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
}
SECTION("pointer access to number_float_t")
{
using test_type = json::number_float_t;
json value = 42.23;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == Approx(value.get<test_type>()));
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == Approx(value.get<test_type>()));
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == Approx(value.get<test_type>()));
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() != nullptr);
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
}
SECTION("pointer access to const number_float_t")
{
using test_type = const json::number_float_t;
const json value = 42.23;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == Approx(value.get<test_type>()));
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == Approx(value.get<test_type>()));
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == Approx(value.get<test_type>()));
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() != nullptr);
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
}
SECTION("pointer access to const binary_t")
{
using test_type = const json::binary_t;
const json value = json::binary({1, 2, 3});
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() != nullptr);
}
SECTION("pointer access to const binary_t")
{
using test_type = const json::binary_t;
const json value = json::binary({});
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p2 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p3 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
CHECK(value.get_ptr<const json::binary_t*>() != nullptr);
}
}

View File

@ -0,0 +1,304 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#ifdef JSON_TEST_NO_GLOBAL_UDLS
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif
#include <deque>
#include <forward_list>
#include <list>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <iostream>
#include <sstream>
#include <iomanip>
// local variable is initialized but not referenced
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
DOCTEST_MSVC_SUPPRESS_WARNING(4189)
TEST_CASE("README" * doctest::skip())
{
{
// redirect std::cout for the README file
auto* old_cout_buffer = std::cout.rdbuf();
std::ostringstream const new_stream;
std::cout.rdbuf(new_stream.rdbuf());
{
// create an empty structure (null)
json j;
// add a number that is stored as double (note the implicit conversion of j to an object)
j["pi"] = 3.141;
// add a Boolean that is stored as bool
j["happy"] = true;
// add a string that is stored as std::string
j["name"] = "Niels";
// add another null object by passing nullptr
j["nothing"] = nullptr;
// add an object inside the object
j["answer"]["everything"] = 42;
// add an array that is stored as std::vector (using an initializer list)
j["list"] = { 1, 0, 2 };
// add another object (using an initializer list of pairs)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };
// instead, you could also write (which looks very similar to the JSON above)
json const j2 =
{
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99}
}
}
};
}
{
// ways to express the empty array []
json const empty_array_implicit = {{}};
CHECK(empty_array_implicit.is_array());
json const empty_array_explicit = json::array();
CHECK(empty_array_explicit.is_array());
// a way to express the empty object {}
json const empty_object_explicit = json::object();
CHECK(empty_object_explicit.is_object());
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });
CHECK(array_not_object.is_array());
CHECK(array_not_object.size() == 2);
CHECK(array_not_object[0].is_array());
CHECK(array_not_object[1].is_array());
}
{
// create object from string literal
json const j = "{ \"happy\": true, \"pi\": 3.141 }"_json; // NOLINT(modernize-raw-string-literal)
// or even nicer with a raw string literal
auto j2 = R"({
"happy": true,
"pi": 3.141
})"_json;
// or explicitly
auto j3 = json::parse(R"({"happy": true, "pi": 3.141})");
// explicit conversion to string
std::string const s = j.dump(); // {\"happy\":true,\"pi\":3.141}
// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump(4) << std::endl; // NOLINT(performance-avoid-endl)
// {
// "happy": true,
// "pi": 3.141
// }
std::cout << std::setw(2) << j << std::endl; // NOLINT(performance-avoid-endl)
}
{
// create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);
// comparison
bool x = (j == R"(["foo", 1, true])"_json); // true
CHECK(x == true);
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) // NOLINT(modernize-loop-convert)
{
std::cout << *it << '\n';
}
// range-based for
for (auto& element : j)
{
std::cout << element << '\n';
}
// getter/setter
const auto tmp = j[0].get<std::string>();
j[1] = 42;
bool foo{j.at(2)};
CHECK(foo == true);
// other stuff
CHECK(j.size() == 3); // 3 entries
CHECK_FALSE(j.empty()); // false
CHECK(j.type() == json::value_t::array); // json::value_t::array
j.clear(); // the array is empty again
// create an object
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;
// find an entry
CHECK(o.find("foo") != o.end());
if (o.find("foo") != o.end())
{
// there is an entry with key "foo"
}
}
{
std::vector<int> const c_vector {1, 2, 3, 4};
json const j_vec(c_vector);
// [1, 2, 3, 4]
std::deque<float> const c_deque {1.2f, 2.3f, 3.4f, 5.6f};
json const j_deque(c_deque);
// [1.2, 2.3, 3.4, 5.6]
std::list<bool> const c_list {true, true, false, true};
json const j_list(c_list);
// [true, true, false, true]
std::forward_list<int64_t> const c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json const j_flist(c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
std::array<unsigned long, 4> const c_array {{1, 2, 3, 4}};
json const j_array(c_array);
// [1, 2, 3, 4]
std::set<std::string> const c_set {"one", "two", "three", "four", "one"};
json const j_set(c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]
std::unordered_set<std::string> const c_uset {"one", "two", "three", "four", "one"};
json const j_uset(c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]
std::multiset<std::string> const c_mset {"one", "two", "one", "four"};
json const j_mset(c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
std::unordered_multiset<std::string> const c_umset {"one", "two", "one", "four"};
json const j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
}
{
std::map<std::string, int> const c_map { {"one", 1}, {"two", 2}, {"three", 3} };
json const j_map(c_map);
// {"one": 1, "two": 2, "three": 3}
std::unordered_map<const char*, float> const c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} };
json const j_umap(c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}
std::multimap<std::string, bool> const c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json const j_mmap(c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
std::unordered_multimap<std::string, bool> const c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json const j_ummap(c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
}
{
// strings
std::string const s1 = "Hello, world!";
json const js = s1;
auto s2 = js.get<std::string>();
// Booleans
bool const b1 = true;
json const jb = b1;
bool b2{jb};
CHECK(b2 == true);
// numbers
int const i = 42;
json const jn = i;
double f{jn};
CHECK(f == 42);
// etc.
std::string const vs = js.get<std::string>();
bool vb = jb.get<bool>();
CHECK(vb == true);
int vi = jn.get<int>();
CHECK(vi == 42);
// etc.
}
{
// a JSON value
json j_original = R"({
"baz": ["one", "two", "three"],
"foo": "bar"
})"_json;
// access members with a JSON pointer (RFC 6901)
j_original["/baz/1"_json_pointer];
// "two"
// a JSON patch (RFC 6902)
json const j_patch = R"([
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
])"_json;
// apply the patch
json const j_result = j_original.patch(j_patch);
// {
// "baz": "boo",
// "hello": ["world"]
// }
// calculate a JSON patch from two JSON values
auto res = json::diff(j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op":"remove","path":"/hello" },
// { "op":"add","path":"/foo","value":"bar" }
// ]
}
// restore old std::cout
std::cout.rdbuf(old_cout_buffer);
}
}
DOCTEST_MSVC_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,247 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("reference access")
{
// create a JSON value with different types
const json json_types =
{
{"boolean", true},
{
"number", {
{"integer", 42},
{"floating-point", 17.23}
}
},
{"string", "Hello, world!"},
{"array", {1, 2, 3, 4, 5}},
{"null", nullptr}
};
SECTION("reference access to object_t")
{
using test_type = json::object_t;
json value = {{"one", 1}, {"two", 2}};
// check if references are returned correctly
auto& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_NOTHROW(value.get_ref<json::object_t&>());
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
}
SECTION("const reference access to const object_t")
{
using test_type = json::object_t;
const json value = {{"one", 1}, {"two", 2}};
// this should not compile
// test_type& p1 = value.get_ref<test_type&>();
// check if references are returned correctly
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
}
SECTION("reference access to array_t")
{
using test_type = json::array_t;
json value = {1, 2, 3, 4};
// check if references are returned correctly
auto& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS_WITH_AS(value.get_ref<json::object_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
CHECK_NOTHROW(value.get_ref<json::array_t&>());
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
}
SECTION("reference access to string_t")
{
using test_type = json::string_t;
json value = "hello";
// check if references are returned correctly
auto& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS_WITH_AS(value.get_ref<json::object_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
CHECK_NOTHROW(value.get_ref<json::string_t&>());
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
}
SECTION("reference access to boolean_t")
{
using test_type = json::boolean_t;
json value = false;
// check if references are returned correctly
auto& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS_WITH_AS(value.get_ref<json::object_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
CHECK_NOTHROW(value.get_ref<json::boolean_t&>());
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
}
SECTION("reference access to number_integer_t")
{
using test_type = json::number_integer_t;
json value = -23;
// check if references are returned correctly
auto& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS_WITH_AS(value.get_ref<json::object_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_NOTHROW(value.get_ref<json::number_integer_t&>());
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
}
SECTION("reference access to number_unsigned_t")
{
using test_type = json::number_unsigned_t;
json value = 23u;
// check if references are returned correctly
auto& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS_WITH_AS(value.get_ref<json::object_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
//CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
// "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_NOTHROW(value.get_ref<json::number_unsigned_t&>());
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
}
SECTION("reference access to number_float_t")
{
using test_type = json::number_float_t;
json value = 42.23;
// check if references are returned correctly
auto& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const auto& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS_WITH_AS(value.get_ref<json::object_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
CHECK_NOTHROW(value.get_ref<json::number_float_t&>());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,941 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using ordered_json = nlohmann::ordered_json;
#ifdef JSON_TEST_NO_GLOBAL_UDLS
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif
#include <cstdio>
#include <list>
#include <type_traits>
#include <utility>
#ifdef JSON_HAS_CPP_17
#include <any>
#include <variant>
#endif
#ifdef JSON_HAS_CPP_20
#if __has_include(<span>)
#include <span>
#endif
#endif
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
/////////////////////////////////////////////////////////////////////
// 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& /*unused*/, NonDefaultFromJsonStruct const& /*unused*/)
{
return true;
}
enum class for_1647
{
one,
two
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): this is a false positive
NLOHMANN_JSON_SERIALIZE_ENUM(for_1647,
{
{for_1647::one, "one"},
{for_1647::two, "two"},
})
} // namespace
/////////////////////////////////////////////////////////////////////
// for #1299
/////////////////////////////////////////////////////////////////////
struct Data
{
Data() = default;
Data(std::string a_, std::string b_)
: a(std::move(a_))
, b(std::move(b_))
{}
std::string a{};
std::string b{};
};
void from_json(const json& j, Data& data);
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);
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& /*unused*/) noexcept
{
return {};
}
};
} // namespace nlohmann
/////////////////////////////////////////////////////////////////////
// for #1805
/////////////////////////////////////////////////////////////////////
struct NotSerializableData
{
int mydata;
float myfloat;
};
/////////////////////////////////////////////////////////////////////
// for #2574
/////////////////////////////////////////////////////////////////////
struct NonDefaultConstructible
{
explicit NonDefaultConstructible(int a)
: x(a)
{}
int x;
};
namespace nlohmann
{
template<>
struct adl_serializer<NonDefaultConstructible>
{
static NonDefaultConstructible from_json(json const& j)
{
return NonDefaultConstructible(j.get<int>());
}
};
} // namespace nlohmann
/////////////////////////////////////////////////////////////////////
// for #2824
/////////////////////////////////////////////////////////////////////
class sax_no_exception : public nlohmann::detail::json_sax_dom_parser<json>
{
public:
explicit sax_no_exception(json& j)
: nlohmann::detail::json_sax_dom_parser<json>(j, false)
{}
static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex)
{
error_string = new std::string(ex.what()); // NOLINT(cppcoreguidelines-owning-memory)
return false;
}
static std::string* error_string;
};
std::string* sax_no_exception::error_string = nullptr;
/////////////////////////////////////////////////////////////////////
// for #2982
/////////////////////////////////////////////////////////////////////
template<class T>
class my_allocator : public std::allocator<T>
{
public:
using std::allocator<T>::allocator;
my_allocator() = default;
template<class U> my_allocator(const my_allocator<U>& /*unused*/) { }
template <class U>
struct rebind
{
using other = my_allocator<U>;
};
};
/////////////////////////////////////////////////////////////////////
// for #3077
/////////////////////////////////////////////////////////////////////
class FooAlloc
{};
class Foo
{
public:
explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {}
bool value = false;
};
class FooBar
{
public:
Foo foo{};
};
inline void from_json(const nlohmann::json& j, FooBar& fb)
{
j.at("value").get_to(fb.foo.value);
}
/////////////////////////////////////////////////////////////////////
// for #3171
/////////////////////////////////////////////////////////////////////
struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions)
{
for_3171_base(const std::string& /*unused*/ = {}) {}
virtual ~for_3171_base() = default;
virtual void _from_json(const json& j)
{
j.at("str").get_to(str);
}
std::string str{};
};
struct for_3171_derived : public for_3171_base
{
for_3171_derived() = default;
explicit for_3171_derived(const std::string& /*unused*/) { }
};
inline void from_json(const json& j, for_3171_base& tb)
{
tb._from_json(j);
}
/////////////////////////////////////////////////////////////////////
// for #3312
/////////////////////////////////////////////////////////////////////
#ifdef JSON_HAS_CPP_20
struct for_3312
{
std::string name;
};
inline void from_json(const json& j, for_3312& obj)
{
j.at("name").get_to(obj.name);
}
#endif
/////////////////////////////////////////////////////////////////////
// for #3204
/////////////////////////////////////////////////////////////////////
struct for_3204_foo
{
for_3204_foo() = default;
explicit for_3204_foo(std::string /*unused*/) {} // NOLINT(performance-unnecessary-value-param)
};
struct for_3204_bar
{
enum constructed_from_t
{
constructed_from_none = 0,
constructed_from_foo = 1,
constructed_from_json = 2
};
explicit for_3204_bar(std::function<void(for_3204_foo)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
: constructed_from(constructed_from_foo) {}
explicit for_3204_bar(std::function<void(json)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
: constructed_from(constructed_from_json) {}
constructed_from_t constructed_from = constructed_from_none;
};
/////////////////////////////////////////////////////////////////////
// for #3333
/////////////////////////////////////////////////////////////////////
struct for_3333 final
{
for_3333(int x_ = 0, int y_ = 0) : x(x_), y(y_) {}
template <class T>
for_3333(const T& /*unused*/)
{
CHECK(false);
}
int x = 0;
int y = 0;
};
template <>
inline for_3333::for_3333(const json& j)
: for_3333(j.value("x", 0), j.value("y", 0))
{}
TEST_CASE("regression tests 2")
{
SECTION("issue #1001 - Fix memory leak during parser callback")
{
const 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"}
}
}
]
})";
const json::parser_callback_t cb = [&](int /*level*/, json::parse_event_t event, json & parsed) noexcept
{
// 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, "unexpected value");
}
#endif
SECTION("issue #1299 - compile error in from_json converting to container "
"with std::pair")
{
const 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 = R"({"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 = R"({"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 std::array<int, 108> 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 (const int i : data)
{
s += static_cast<char>(i);
}
dump_test["1"] = s;
dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
}
}
SECTION("issue #1447 - Integer Overflow (OSS-Fuzz 12506)")
{
const 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)();
const json j = smallest;
CHECK(j.dump() == std::to_string(smallest));
}
SECTION("issue #1727 - Contains with non-const lvalue json_pointer picks the wrong overload")
{
const 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")
{
// does not compile on ICPC when targeting C++20
#if !(defined(__INTEL_COMPILER) && __cplusplus >= 202000)
{
const json j;
NonDefaultFromJsonStruct x(j);
NonDefaultFromJsonStruct y;
CHECK(x == y);
}
#endif
auto val = nlohmann::json("one").get<for_1647>();
CHECK(val == for_1647::one);
const json j = val;
}
SECTION("issue #1715 - json::from_cbor does not respect allow_exceptions = false when input is string literal")
{
SECTION("string literal")
{
const json cbor = json::from_cbor("B", true, false);
CHECK(cbor.is_discarded());
}
SECTION("string array")
{
const std::array<char, 2> input = {{'B', 0x00}};
const json cbor = json::from_cbor(input, true, false);
CHECK(cbor.is_discarded());
}
SECTION("std::string")
{
const 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, "unexpected result");
static_assert(!std::is_constructible<json, std::pair<NotSerializableData, std::string>>::value, "unexpected result");
static_assert(std::is_constructible<json, std::pair<int, std::string>>::value, "unexpected result");
}
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, "unexpected result");
static_assert(!std::is_constructible<json, std::tuple<NotSerializableData, std::string>>::value, "unexpected result");
static_assert(std::is_constructible<json, std::tuple<int, std::string>>::value, "unexpected result");
}
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 std::array<unsigned char, 23> data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}};
const json j = json::from_msgpack(data.data(), data.size());
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
const json j{{"x", "test"}};
const std::string defval = "default value";
auto val = j.value("x", defval);
auto val2 = j.value("y", defval);
}
SECTION("issue #2293 - eof doesn't cause parsing to stop")
{
const std::vector<uint8_t> data =
{
0x7B,
0x6F,
0x62,
0x6A,
0x65,
0x63,
0x74,
0x20,
0x4F,
0x42
};
const 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"}};
const 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);
const 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);
}
SECTION("issue #2330 - ignore_comment=true fails on multiple consecutive lines starting with comments")
{
const std::string ss = "//\n//\n{\n}\n";
const json j = json::parse(ss, nullptr, true, true);
CHECK(j.dump() == "{}");
}
#ifdef JSON_HAS_CPP_20
#if __has_include(<span>)
SECTION("issue #2546 - parsing containers of std::byte")
{
const char DATA[] = R"("Hello, world!")"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
const auto s = std::as_bytes(std::span(DATA));
const json j = json::parse(s);
CHECK(j.dump() == "\"Hello, world!\"");
}
#endif
#endif
SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails")
{
SECTION("std::array")
{
{
const json j = {7, 4};
auto arr = j.get<std::array<NonDefaultConstructible, 2>>();
CHECK(arr[0].x == 7);
CHECK(arr[1].x == 4);
}
{
const json j = 7;
CHECK_THROWS_AS((j.get<std::array<NonDefaultConstructible, 1>>()), json::type_error);
}
}
SECTION("std::pair")
{
{
const json j = {3, 8};
auto p = j.get<std::pair<NonDefaultConstructible, NonDefaultConstructible>>();
CHECK(p.first.x == 3);
CHECK(p.second.x == 8);
}
{
const json j = {4, 1};
auto p = j.get<std::pair<int, NonDefaultConstructible>>();
CHECK(p.first == 4);
CHECK(p.second.x == 1);
}
{
const json j = {6, 7};
auto p = j.get<std::pair<NonDefaultConstructible, int>>();
CHECK(p.first.x == 6);
CHECK(p.second == 7);
}
{
const json j = 7;
CHECK_THROWS_AS((j.get<std::pair<NonDefaultConstructible, int>>()), json::type_error);
}
}
SECTION("std::tuple")
{
{
const json j = {9};
auto t = j.get<std::tuple<NonDefaultConstructible>>();
CHECK(std::get<0>(t).x == 9);
}
{
const json j = {9, 8, 7};
auto t = j.get<std::tuple<NonDefaultConstructible, int, NonDefaultConstructible>>();
CHECK(std::get<0>(t).x == 9);
CHECK(std::get<1>(t) == 8);
CHECK(std::get<2>(t).x == 7);
}
{
const json j = 7;
CHECK_THROWS_AS((j.get<std::tuple<NonDefaultConstructible>>()), json::type_error);
}
}
}
SECTION("issue #2865 - ASAN detects memory leaks")
{
// the code below is expected to not leak memory
{
nlohmann::json o;
const std::string s = "bar";
nlohmann::to_json(o["foo"], s);
nlohmann::json p = o;
// call to_json with a non-null JSON value
nlohmann::to_json(p["foo"], s);
}
{
nlohmann::json o;
const std::string s = "bar";
nlohmann::to_json(o["foo"], s);
// call to_json with a non-null JSON value
nlohmann::to_json(o["foo"], s);
}
}
SECTION("issue #2824 - encoding of json::exception::what()")
{
json j;
sax_no_exception sax(j);
CHECK(!json::sax_parse("xyz", &sax));
CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'");
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
}
SECTION("issue #2825 - Properly constrain the basic_json conversion operator")
{
static_assert(std::is_copy_assignable<nlohmann::ordered_json>::value, "ordered_json must be copy assignable");
}
SECTION("issue #2958 - Inserting in unordered json using a pointer retains the leading slash")
{
const std::string p = "/root";
json test1;
test1[json::json_pointer(p)] = json::object();
CHECK(test1.dump() == "{\"root\":{}}");
ordered_json test2;
test2[ordered_json::json_pointer(p)] = json::object();
CHECK(test2.dump() == "{\"root\":{}}");
// json::json_pointer and ordered_json::json_pointer are the same type; behave as above
ordered_json test3;
test3[json::json_pointer(p)] = json::object();
CHECK(std::is_same<json::json_pointer::string_t, ordered_json::json_pointer::string_t>::value);
CHECK(test3.dump() == "{\"root\":{}}");
}
SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type")
{
std::vector<std::uint8_t, my_allocator<std::uint8_t>> my_vector;
json j = {1, 2, 3, 4};
json::to_cbor(j, my_vector);
json k = json::from_cbor(my_vector);
CHECK(j == k);
}
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
// JSON_HAS_CPP_17 (do not remove; see note at top of file)
SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
{
nlohmann::detail::std_fs::path text_path("/tmp/text.txt");
const json j(text_path);
const auto j_path = j.get<nlohmann::detail::std_fs::path>();
CHECK(j_path == text_path);
#if DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(8, 4, 0)
// only known to work on Clang and GCC >=8.4
CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
#endif
}
#endif
SECTION("issue #3077 - explicit constructor with default does not compile")
{
json j;
j[0]["value"] = true;
std::vector<FooBar> 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]");
}
SECTION("issue #3343 - json and ordered_json are not interchangable")
{
json::object_t jobj({ { "product", "one" } });
ordered_json::object_t ojobj({{"product", "one"}});
auto jit = jobj.begin();
auto ojit = ojobj.begin();
CHECK(jit->first == ojit->first);
CHECK(jit->second.get<std::string>() == ojit->second.get<std::string>());
}
SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed")
{
const json j{{ "str", "value"}};
// failed with: error: no match for operator= (operand types are for_3171_derived and const nlohmann::basic_json<>::string_t
// {aka const std::__cxx11::basic_string<char>})
// s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
auto td = j.get<for_3171_derived>();
CHECK(td.str == "value");
}
#ifdef JSON_HAS_CPP_20
SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20")
{
// see test for #3171
const ordered_json j = {{"name", "class"}};
for_3312 obj{};
j.get_to(obj);
CHECK(obj.name == "class");
}
#endif
#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any")
{
const json j;
const std::any a1 = j;
std::any&& a2 = j;
CHECK(a1.type() == typeid(j));
CHECK(a2.type() == typeid(j));
}
#endif
SECTION("issue #3204 - ambiguous regression")
{
for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param)
for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param)
CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo);
CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json);
}
SECTION("issue #3333 - Ambiguous conversion from nlohmann::basic_json<> to custom class")
{
const json j
{
{"x", 1},
{"y", 2}
};
for_3333 p = j;
CHECK(p.x == 1);
CHECK(p.y == 2);
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,297 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <sstream>
#include <iomanip>
TEST_CASE("serialization")
{
SECTION("operator<<")
{
SECTION("no given width")
{
std::stringstream ss;
const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss << j;
CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]");
}
SECTION("given width")
{
std::stringstream ss;
const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss << std::setw(4) << j;
CHECK(ss.str() ==
"[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]");
}
SECTION("given fill")
{
std::stringstream ss;
const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss << std::setw(1) << std::setfill('\t') << j;
CHECK(ss.str() ==
"[\n\t\"foo\",\n\t1,\n\t2,\n\t3,\n\tfalse,\n\t{\n\t\t\"one\": 1\n\t}\n]");
}
}
SECTION("operator>>")
{
SECTION("no given width")
{
std::stringstream ss;
const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
j >> ss;
CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]");
}
SECTION("given width")
{
std::stringstream ss;
const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss.width(4);
j >> ss;
CHECK(ss.str() ==
"[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]");
}
SECTION("given fill")
{
std::stringstream ss;
const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss.width(1);
ss.fill('\t');
j >> ss;
CHECK(ss.str() ==
"[\n\t\"foo\",\n\t1,\n\t2,\n\t3,\n\tfalse,\n\t{\n\t\t\"one\": 1\n\t}\n]");
}
}
SECTION("dump")
{
SECTION("invalid character")
{
const json j = "ä\xA9ü";
CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
CHECK_THROWS_WITH_AS(j.dump(1, ' ', false, json::error_handler_t::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\"");
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\"");
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\"");
}
SECTION("ending with incomplete character")
{
const json j = "123\xC2";
CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&);
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\"");
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\"");
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\"");
}
SECTION("unexpected character")
{
const json j = "123\xF1\xB0\x34\x35\x36";
CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&);
CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&);
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\"");
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\"");
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\"");
}
SECTION("U+FFFD Substitution of Maximal Subparts")
{
// Some tests (mostly) from
// https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf
// Section 3.9 -- U+FFFD Substitution of Maximal Subparts
auto test = [&](std::string const & input, std::string const & expected)
{
const json j = input;
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"" + expected + "\"");
};
test("\xC2", "\\ufffd");
test("\xC2\x41\x42", "\\ufffd" "\x41" "\x42");
test("\xC2\xF4", "\\ufffd" "\\ufffd");
test("\xF0\x80\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
test("\xF1\x80\x80\x41", "\\ufffd" "\x41");
test("\xF2\x80\x80\x41", "\\ufffd" "\x41");
test("\xF3\x80\x80\x41", "\\ufffd" "\x41");
test("\xF4\x80\x80\x41", "\\ufffd" "\x41");
test("\xF5\x80\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
test("\xF0\x90\x80\x41", "\\ufffd" "\x41");
test("\xF1\x90\x80\x41", "\\ufffd" "\x41");
test("\xF2\x90\x80\x41", "\\ufffd" "\x41");
test("\xF3\x90\x80\x41", "\\ufffd" "\x41");
test("\xF4\x90\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
test("\xF5\x90\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
test("\xC0\xAF\xE0\x80\xBF\xF0\x81\x82\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
test("\xED\xA0\x80\xED\xBF\xBF\xED\xAF\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
test("\xF4\x91\x92\x93\xFF\x41\x80\xBF\x42", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41" "\\ufffd""\\ufffd" "\x42");
test("\xE1\x80\xE2\xF0\x91\x92\xF1\xBF\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
}
}
SECTION("to_string")
{
auto test = [&](std::string const & input, std::string const & expected)
{
using std::to_string;
const json j = input;
CHECK(to_string(j) == "\"" + expected + "\"");
};
test(R"({"x":5,"y":6})", R"({\"x\":5,\"y\":6})");
test("{\"x\":[10,null,null,null]}", R"({\"x\":[10,null,null,null]})");
test("test", "test");
test("[3,\"false\",false]", R"([3,\"false\",false])");
}
}
TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint32_t, int64_t, uint64_t)
{
SECTION("minimum")
{
constexpr auto minimum = (std::numeric_limits<T>::min)();
const json j = minimum;
CHECK(j.dump() == std::to_string(minimum));
}
SECTION("maximum")
{
constexpr auto maximum = (std::numeric_limits<T>::max)();
const json j = maximum;
CHECK(j.dump() == std::to_string(maximum));
}
}
TEST_CASE("dump with binary values")
{
auto binary = json::binary({1, 2, 3, 4});
auto binary_empty = json::binary({});
auto binary_with_subtype = json::binary({1, 2, 3, 4}, 128);
auto binary_empty_with_subtype = json::binary({}, 128);
const json object = {{"key", binary}};
const json object_empty = {{"key", binary_empty}};
const json object_with_subtype = {{"key", binary_with_subtype}};
const json object_empty_with_subtype = {{"key", binary_empty_with_subtype}};
const json array = {"value", 1, binary};
const json array_empty = {"value", 1, binary_empty};
const json array_with_subtype = {"value", 1, binary_with_subtype};
const json array_empty_with_subtype = {"value", 1, binary_empty_with_subtype};
SECTION("normal")
{
CHECK(binary.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":null}");
CHECK(binary_empty.dump() == "{\"bytes\":[],\"subtype\":null}");
CHECK(binary_with_subtype.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":128}");
CHECK(binary_empty_with_subtype.dump() == "{\"bytes\":[],\"subtype\":128}");
CHECK(object.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":null}}");
CHECK(object_empty.dump() == "{\"key\":{\"bytes\":[],\"subtype\":null}}");
CHECK(object_with_subtype.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":128}}");
CHECK(object_empty_with_subtype.dump() == "{\"key\":{\"bytes\":[],\"subtype\":128}}");
CHECK(array.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":null}]");
CHECK(array_empty.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":null}]");
CHECK(array_with_subtype.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":128}]");
CHECK(array_empty_with_subtype.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":128}]");
}
SECTION("pretty-printed")
{
CHECK(binary.dump(4) == "{\n"
" \"bytes\": [1, 2, 3, 4],\n"
" \"subtype\": null\n"
"}");
CHECK(binary_empty.dump(4) == "{\n"
" \"bytes\": [],\n"
" \"subtype\": null\n"
"}");
CHECK(binary_with_subtype.dump(4) == "{\n"
" \"bytes\": [1, 2, 3, 4],\n"
" \"subtype\": 128\n"
"}");
CHECK(binary_empty_with_subtype.dump(4) == "{\n"
" \"bytes\": [],\n"
" \"subtype\": 128\n"
"}");
CHECK(object.dump(4) == "{\n"
" \"key\": {\n"
" \"bytes\": [1, 2, 3, 4],\n"
" \"subtype\": null\n"
" }\n"
"}");
CHECK(object_empty.dump(4) == "{\n"
" \"key\": {\n"
" \"bytes\": [],\n"
" \"subtype\": null\n"
" }\n"
"}");
CHECK(object_with_subtype.dump(4) == "{\n"
" \"key\": {\n"
" \"bytes\": [1, 2, 3, 4],\n"
" \"subtype\": 128\n"
" }\n"
"}");
CHECK(object_empty_with_subtype.dump(4) == "{\n"
" \"key\": {\n"
" \"bytes\": [],\n"
" \"subtype\": 128\n"
" }\n"
"}");
CHECK(array.dump(4) == "[\n"
" \"value\",\n"
" 1,\n"
" {\n"
" \"bytes\": [1, 2, 3, 4],\n"
" \"subtype\": null\n"
" }\n"
"]");
CHECK(array_empty.dump(4) == "[\n"
" \"value\",\n"
" 1,\n"
" {\n"
" \"bytes\": [],\n"
" \"subtype\": null\n"
" }\n"
"]");
CHECK(array_with_subtype.dump(4) == "[\n"
" \"value\",\n"
" 1,\n"
" {\n"
" \"bytes\": [1, 2, 3, 4],\n"
" \"subtype\": 128\n"
" }\n"
"]");
CHECK(array_empty_with_subtype.dump(4) == "[\n"
" \"value\",\n"
" 1,\n"
" {\n"
" \"bytes\": [],\n"
" \"subtype\": 128\n"
" }\n"
"]");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,516 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// XXX:
// Only compile these tests if 'float' and 'double' are IEEE-754 single- and
// double-precision numbers, resp.
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::detail::dtoa_impl::reinterpret_bits;
namespace
{
float make_float(uint32_t sign_bit, uint32_t biased_exponent, uint32_t significand)
{
assert(sign_bit == 0 || sign_bit == 1);
assert(biased_exponent <= 0xFF);
assert(significand <= 0x007FFFFF);
uint32_t bits = 0;
bits |= sign_bit << 31;
bits |= biased_exponent << 23;
bits |= significand;
return reinterpret_bits<float>(bits);
}
// ldexp -- convert f * 2^e to IEEE single precision
float make_float(uint64_t f, int e)
{
constexpr uint64_t kHiddenBit = 0x00800000;
constexpr uint64_t kSignificandMask = 0x007FFFFF;
constexpr int kPhysicalSignificandSize = 23; // Excludes the hidden bit.
constexpr int kExponentBias = 0x7F + kPhysicalSignificandSize;
constexpr int kDenormalExponent = 1 - kExponentBias;
constexpr int kMaxExponent = 0xFF - kExponentBias;
while (f > kHiddenBit + kSignificandMask)
{
f >>= 1;
e++;
}
if (e >= kMaxExponent)
{
return std::numeric_limits<float>::infinity();
}
if (e < kDenormalExponent)
{
return 0.0;
}
while (e > kDenormalExponent && (f & kHiddenBit) == 0)
{
f <<= 1;
e--;
}
const uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0)
? 0
: static_cast<uint64_t>(e + kExponentBias);
const uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
return reinterpret_bits<float>(static_cast<uint32_t>(bits));
}
double make_double(uint64_t sign_bit, uint64_t biased_exponent, uint64_t significand)
{
assert(sign_bit == 0 || sign_bit == 1);
assert(biased_exponent <= 0x7FF);
assert(significand <= 0x000FFFFFFFFFFFFF);
uint64_t bits = 0;
bits |= sign_bit << 63;
bits |= biased_exponent << 52;
bits |= significand;
return reinterpret_bits<double>(bits);
}
// ldexp -- convert f * 2^e to IEEE double precision
double make_double(uint64_t f, int e)
{
constexpr uint64_t kHiddenBit = 0x0010000000000000;
constexpr uint64_t kSignificandMask = 0x000FFFFFFFFFFFFF;
constexpr int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
constexpr int kExponentBias = 0x3FF + kPhysicalSignificandSize;
constexpr int kDenormalExponent = 1 - kExponentBias;
constexpr int kMaxExponent = 0x7FF - kExponentBias;
while (f > kHiddenBit + kSignificandMask)
{
f >>= 1;
e++;
}
if (e >= kMaxExponent)
{
return std::numeric_limits<double>::infinity();
}
if (e < kDenormalExponent)
{
return 0.0;
}
while (e > kDenormalExponent && (f & kHiddenBit) == 0)
{
f <<= 1;
e--;
}
const uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0)
? 0
: static_cast<uint64_t>(e + kExponentBias);
const uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
return reinterpret_bits<double>(bits);
}
} // namespace
TEST_CASE("digit gen")
{
SECTION("single precision")
{
auto check_float = [](float number, const std::string & digits, int expected_exponent)
{
CAPTURE(number)
CAPTURE(digits)
CAPTURE(expected_exponent)
std::array<char, 32> buf{};
int len = 0;
int exponent = 0;
nlohmann::detail::dtoa_impl::grisu2(buf.data(), len, exponent, number);
CHECK(digits == std::string(buf.data(), buf.data() + len));
CHECK(expected_exponent == exponent);
};
check_float(make_float(0, 0, 0x00000001), "1", -45); // min denormal
check_float(make_float(0, 0, 0x007FFFFF), "11754942", -45); // max denormal
check_float(make_float(0, 1, 0x00000000), "11754944", -45); // min normal
check_float(make_float(0, 1, 0x00000001), "11754945", -45);
check_float(make_float(0, 1, 0x007FFFFF), "23509886", -45);
check_float(make_float(0, 2, 0x00000000), "23509887", -45);
check_float(make_float(0, 2, 0x00000001), "2350989", -44);
check_float(make_float(0, 24, 0x00000000), "98607613", -39); // fail if no special case in normalized boundaries
check_float(make_float(0, 30, 0x00000000), "63108872", -37); // fail if no special case in normalized boundaries
check_float(make_float(0, 31, 0x00000000), "12621775", -36); // fail if no special case in normalized boundaries
check_float(make_float(0, 57, 0x00000000), "84703295", -29); // fail if no special case in normalized boundaries
check_float(make_float(0, 254, 0x007FFFFE), "34028233", 31);
check_float(make_float(0, 254, 0x007FFFFF), "34028235", 31); // max normal
// V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal Conversion", manuscript, May 1991,
// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z (report)
// ftp://ftp.ee.lbl.gov/testbase.tar.Z (program)
// Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP
check_float(make_float(12676506, -102), "25", -25);
check_float(make_float(12676506, -103), "125", -26);
check_float(make_float(15445013, 86), "1195", 30);
check_float(make_float(13734123, -138), "39415", -39);
check_float(make_float(12428269, -130), "913085", -38);
check_float(make_float(15334037, -146), "1719005", -43);
check_float(make_float(11518287, -41), "52379105", -13);
check_float(make_float(12584953, -145), "2821644", -43);
check_float(make_float(15961084, -125), "37524328", -38);
check_float(make_float(14915817, -146), "16721209", -44);
check_float(make_float(10845484, -102), "21388946", -31);
check_float(make_float(16431059, -61), "7125836", -18);
// Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP
check_float(make_float(16093626, 69), "95", 26);
check_float(make_float( 9983778, 25), "335", 12);
check_float(make_float(12745034, 104), "2585", 35);
check_float(make_float(12706553, 72), "60005", 24);
check_float(make_float(11005028, 45), "387205", 15);
check_float(make_float(15059547, 71), "3555835", 22);
check_float(make_float(16015691, -99), "25268305", -30);
check_float(make_float( 8667859, 56), "6245851", 17);
check_float(make_float(14855922, -82), "30721327", -25);
check_float(make_float(14855922, -83), "15360663", -25);
check_float(make_float(10144164, -110), "781478", -32);
check_float(make_float(13248074, 95), "52481028", 28);
}
SECTION("double precision")
{
auto check_double = [](double number, const std::string & digits, int expected_exponent)
{
CAPTURE(number)
CAPTURE(digits)
CAPTURE(expected_exponent)
std::array<char, 32> buf{};
int len = 0;
int exponent = 0;
nlohmann::detail::dtoa_impl::grisu2(buf.data(), len, exponent, number);
CHECK(digits == std::string(buf.data(), buf.data() + len));
CHECK(expected_exponent == exponent);
};
check_double(make_double(0, 0, 0x0000000000000001), "5", -324); // min denormal
check_double(make_double(0, 0, 0x000FFFFFFFFFFFFF), "2225073858507201", -323); // max denormal
check_double(make_double(0, 1, 0x0000000000000000), "22250738585072014", -324); // min normal
check_double(make_double(0, 1, 0x0000000000000001), "2225073858507202", -323);
check_double(make_double(0, 1, 0x000FFFFFFFFFFFFF), "44501477170144023", -324);
check_double(make_double(0, 2, 0x0000000000000000), "4450147717014403", -323);
check_double(make_double(0, 2, 0x0000000000000001), "4450147717014404", -323);
check_double(make_double(0, 4, 0x0000000000000000), "17800590868057611", -323); // fail if no special case in normalized boundaries
check_double(make_double(0, 5, 0x0000000000000000), "35601181736115222", -323); // fail if no special case in normalized boundaries
check_double(make_double(0, 6, 0x0000000000000000), "7120236347223045", -322); // fail if no special case in normalized boundaries
check_double(make_double(0, 10, 0x0000000000000000), "11392378155556871", -321); // fail if no special case in normalized boundaries
check_double(make_double(0, 2046, 0x000FFFFFFFFFFFFE), "17976931348623155", 292);
check_double(make_double(0, 2046, 0x000FFFFFFFFFFFFF), "17976931348623157", 292); // max normal
// Test different paths in DigitGen
check_double( 10000, "1", 4);
check_double( 1200000, "12", 5);
check_double(4.9406564584124654e-324, "5", -324); // exit integral loop
check_double(2.2250738585072009e-308, "2225073858507201", -323); // exit fractional loop
check_double( 1.82877982605164e-99, "182877982605164", -113);
check_double( 1.1505466208671903e-09, "11505466208671903", -25);
check_double( 5.5645893133766722e+20, "5564589313376672", 5);
check_double( 53.034830388866226, "53034830388866226", -15);
check_double( 0.0021066531670178605, "21066531670178605", -19);
// V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal Conversion", manuscript, May 1991,
// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z (report)
// ftp://ftp.ee.lbl.gov/testbase.tar.Z (program)
// Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP
check_double(make_double(8511030020275656, -342) /* 9.5e-088 */, "95", -89);
check_double(make_double(5201988407066741, -824) /* 4.65e-233 */, "465", -235);
check_double(make_double(6406892948269899, +237) /* 1.415e+087 */, "1415", 84);
check_double(make_double(8431154198732492, +72) /* 3.9815e+037 */, "39815", 33);
check_double(make_double(6475049196144587, +99) /* 4.10405e+045 */, "410405", 40);
check_double(make_double(8274307542972842, +726) /* 2.920845e+234 */, "2920845", 228);
check_double(make_double(5381065484265332, -456) /* 2.8919465e-122 */, "28919465", -129);
check_double(make_double(6761728585499734, -1057) /* 4.37877185e-303 */, "437877185", -311);
check_double(make_double(7976538478610756, +376) /* 1.227701635e+129 */, "1227701635", 120);
check_double(make_double(5982403858958067, +377) /* 1.8415524525e+129 */, "18415524525", 119);
check_double(make_double(5536995190630837, +93) /* 5.48357443505e+043 */, "548357443505", 32);
check_double(make_double(7225450889282194, +710) /* 3.891901811465e+229 */, "3891901811465", 217);
check_double(make_double(7225450889282194, +709) /* 1.9459509057325e+229 */, "19459509057325", 216);
check_double(make_double(8703372741147379, +117) /* 1.44609583816055e+051 */, "144609583816055", 37);
check_double(make_double(8944262675275217, -1001) /* 4.173677474585315e-286 */, "4173677474585315", -301);
check_double(make_double(7459803696087692, -707) /* 1.1079507728788885e-197 */, "11079507728788885", -213);
check_double(make_double(6080469016670379, -381) /* 1.234550136632744e-099 */, "1234550136632744", -114);
check_double(make_double(8385515147034757, +721) /* 9.2503171196036502e+232 */, "925031711960365", 218);
check_double(make_double(7514216811389786, -828) /* 4.1980471502848898e-234 */, "419804715028489", -248);
check_double(make_double(8397297803260511, -345) /* 1.1716315319786511e-088 */, "11716315319786511", -104);
check_double(make_double(6733459239310543, +202) /* 4.3281007284461249e+076 */, "4328100728446125", 61);
check_double(make_double(8091450587292794, -473) /* 3.3177101181600311e-127 */, "3317710118160031", -142);
// Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP
check_double(make_double(6567258882077402, +952) /* 2.5e+302 */, "25", 301);
check_double(make_double(6712731423444934, +535) /* 7.55e+176 */, "755", 174);
check_double(make_double(6712731423444934, +534) /* 3.775e+176 */, "3775", 173);
check_double(make_double(5298405411573037, -957) /* 4.3495e-273 */, "43495", -277);
check_double(make_double(5137311167659507, -144) /* 2.30365e-028 */, "230365", -33);
check_double(make_double(6722280709661868, +363) /* 1.263005e+125 */, "1263005", 119);
check_double(make_double(5344436398034927, -169) /* 7.1422105e-036 */, "71422105", -43);
check_double(make_double(8369123604277281, -853) /* 1.39345735e-241 */, "139345735", -249);
check_double(make_double(8995822108487663, -780) /* 1.414634485e-219 */, "1414634485", -228);
check_double(make_double(8942832835564782, -383) /* 4.5392779195e-100 */, "45392779195", -110);
check_double(make_double(8942832835564782, -384) /* 2.26963895975e-100 */, "226963895975", -111);
check_double(make_double(8942832835564782, -385) /* 1.134819479875e-100 */, "1134819479875", -112);
check_double(make_double(6965949469487146, -249) /* 7.7003665618895e-060 */, "77003665618895", -73);
check_double(make_double(6965949469487146, -250) /* 3.85018328094475e-060 */, "385018328094475", -74);
check_double(make_double(6965949469487146, -251) /* 1.925091640472375e-060 */, "1925091640472375", -75);
check_double(make_double(7487252720986826, +548) /* 6.8985865317742005e+180 */, "68985865317742005", 164);
check_double(make_double(5592117679628511, +164) /* 1.3076622631878654e+065 */, "13076622631878654", 49);
check_double(make_double(8887055249355788, +665) /* 1.3605202075612124e+216 */, "13605202075612124", 200);
check_double(make_double(6994187472632449, +690) /* 3.5928102174759597e+223 */, "35928102174759597", 207);
check_double(make_double(8797576579012143, +588) /* 8.9125197712484552e+192 */, "8912519771248455", 177);
check_double(make_double(7363326733505337, +272) /* 5.5876975736230114e+097 */, "55876975736230114", 81);
check_double(make_double(8549497411294502, -448) /* 1.1762578307285404e-119 */, "11762578307285404", -135);
// Table 20: Stress Inputs for Converting 56-bit Binary to Decimal, < 1/2 ULP
check_double(make_double(50883641005312716, -172) /* 8.4999999999999993e-036 */, "8499999999999999", -51);
check_double(make_double(38162730753984537, -170) /* 2.5499999999999999e-035 */, "255", -37);
check_double(make_double(50832789069151999, -101) /* 2.0049999999999997e-014 */, "20049999999999997", -30);
check_double(make_double(51822367833714164, -109) /* 7.9844999999999994e-017 */, "7984499999999999", -32);
check_double(make_double(66840152193508133, -172) /* 1.1165499999999999e-035 */, "11165499999999999", -51);
check_double(make_double(55111239245584393, -138) /* 1.581615e-025 */, "1581615", -31);
check_double(make_double(71704866733321482, -112) /* 1.3809855e-017 */, "13809855", -24);
check_double(make_double(67160949328233173, -142) /* 1.2046404499999999e-026 */, "12046404499999999", -42);
check_double(make_double(53237141308040189, -152) /* 9.3251405449999991e-030 */, "9325140544999999", -45);
check_double(make_double(62785329394975786, -112) /* 1.2092014595e-017 */, "12092014595", -27);
check_double(make_double(48367680154689523, -77) /* 3.2007045838499998e-007 */, "320070458385", -18);
check_double(make_double(42552223180606797, -102) /* 8.391946324354999e-015 */, "8391946324354999", -30);
check_double(make_double(63626356173011241, -112) /* 1.2253990460585e-017 */, "12253990460585", -30);
check_double(make_double(43566388595783643, -99) /* 6.8735641489760495e-014 */, "687356414897605", -28);
check_double(make_double(54512669636675272, -159) /* 7.459816430480385e-032 */, "7459816430480385", -47);
check_double(make_double(52306490527514614, -167) /* 2.7960588398142552e-034 */, "2796058839814255", -49);
check_double(make_double(52306490527514614, -168) /* 1.3980294199071276e-034 */, "13980294199071276", -50);
check_double(make_double(41024721590449423, -89) /* 6.6279012373057359e-011 */, "6627901237305736", -26);
check_double(make_double(37664020415894738, -132) /* 6.9177880043968072e-024 */, "6917788004396807", -39);
check_double(make_double(37549883692866294, -93) /* 3.7915693108349708e-012 */, "3791569310834971", -27);
check_double(make_double(69124110374399839, -104) /* 3.4080817676591365e-015 */, "34080817676591365", -31);
check_double(make_double(69124110374399839, -105) /* 1.7040408838295683e-015 */, "17040408838295683", -31);
// Table 21: Stress Inputs for Converting 56-bit Binary to Decimal, > 1/2 ULP
check_double(make_double(49517601571415211, -94) /* 2.4999999999999998e-012 */, "25", -13);
check_double(make_double(49517601571415211, -95) /* 1.2499999999999999e-012 */, "125", -14);
check_double(make_double(54390733528642804, -133) /* 4.9949999999999996e-024 */, "49949999999999996", -40); // shortest: 4995e-27
check_double(make_double(71805402319113924, -157) /* 3.9304999999999998e-031 */, "39304999999999998", -47); // shortest: 39305e-35
check_double(make_double(40435277969631694, -179) /* 5.2770499999999992e-038 */, "5277049999999999", -53);
check_double(make_double(57241991568619049, -165) /* 1.223955e-033 */, "1223955", -39);
check_double(make_double(65224162876242886, +58) /* 1.8799584999999998e+034 */, "18799584999999998", 18);
check_double(make_double(70173376848895368, -138) /* 2.01387715e-025 */, "201387715", -33);
check_double(make_double(37072848117383207, -99) /* 5.8490641049999989e-014 */, "5849064104999999", -29);
check_double(make_double(56845051585389697, -176) /* 5.9349003054999999e-037 */, "59349003055", -47);
check_double(make_double(54791673366936431, -145) /* 1.2284718039499998e-027 */, "12284718039499998", -43);
check_double(make_double(66800318669106231, -169) /* 8.9270767180849991e-035 */, "8927076718084999", -50);
check_double(make_double(66800318669106231, -170) /* 4.4635383590424995e-035 */, "44635383590424995", -51);
check_double(make_double(66574323440112438, -119) /* 1.0016990862549499e-019 */, "10016990862549499", -35);
check_double(make_double(65645179969330963, -173) /* 5.4829412628024647e-036 */, "5482941262802465", -51);
check_double(make_double(61847254334681076, -109) /* 9.5290783281036439e-017 */, "9529078328103644", -32);
check_double(make_double(39990712921393606, -145) /* 8.9662279366405553e-028 */, "8966227936640555", -43);
check_double(make_double(59292318184400283, -149) /* 8.3086234418058538e-029 */, "8308623441805854", -44);
check_double(make_double(69116558615326153, -143) /* 6.1985873566126555e-027 */, "61985873566126555", -43);
check_double(make_double(69116558615326153, -144) /* 3.0992936783063277e-027 */, "30992936783063277", -43);
check_double(make_double(39462549494468513, -152) /* 6.9123512506176015e-030 */, "6912351250617602", -45);
check_double(make_double(39462549494468513, -153) /* 3.4561756253088008e-030 */, "3456175625308801", -45);
}
}
TEST_CASE("formatting")
{
SECTION("single precision")
{
auto check_float = [](float number, const std::string & expected)
{
std::array<char, 33> buf{};
char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
std::string actual(buf.data(), end);
CHECK(actual == expected);
};
// %.9g
check_float( -1.2345e-22f, "-1.2345e-22" ); // -1.23450004e-22
check_float( -1.2345e-21f, "-1.2345e-21" ); // -1.23450002e-21
check_float( -1.2345e-20f, "-1.2345e-20" ); // -1.23450002e-20
check_float( -1.2345e-19f, "-1.2345e-19" ); // -1.23449999e-19
check_float( -1.2345e-18f, "-1.2345e-18" ); // -1.23449996e-18
check_float( -1.2345e-17f, "-1.2345e-17" ); // -1.23449998e-17
check_float( -1.2345e-16f, "-1.2345e-16" ); // -1.23449996e-16
check_float( -1.2345e-15f, "-1.2345e-15" ); // -1.23450002e-15
check_float( -1.2345e-14f, "-1.2345e-14" ); // -1.23450004e-14
check_float( -1.2345e-13f, "-1.2345e-13" ); // -1.23449997e-13
check_float( -1.2345e-12f, "-1.2345e-12" ); // -1.23450002e-12
check_float( -1.2345e-11f, "-1.2345e-11" ); // -1.2345e-11
check_float( -1.2345e-10f, "-1.2345e-10" ); // -1.2345e-10
check_float( -1.2345e-9f, "-1.2345e-09" ); // -1.23449995e-09
check_float( -1.2345e-8f, "-1.2345e-08" ); // -1.23449997e-08
check_float( -1.2345e-7f, "-1.2345e-07" ); // -1.23449993e-07
check_float( -1.2345e-6f, "-1.2345e-06" ); // -1.23450002e-06
check_float( -1.2345e-5f, "-1.2345e-05" ); // -1.2345e-05
check_float( -1.2345e-4f, "-0.00012345" ); // -0.000123449994
check_float( -1.2345e-3f, "-0.0012345" ); // -0.00123449997
check_float( -1.2345e-2f, "-0.012345" ); // -0.0123450002
check_float( -1.2345e-1f, "-0.12345" ); // -0.123450004
check_float( -0.0f, "-0.0" ); // -0
check_float( 0.0f, "0.0" ); // 0
check_float( 1.2345e+0f, "1.2345" ); // 1.23450005
check_float( 1.2345e+1f, "12.345" ); // 12.3450003
check_float( 1.2345e+2f, "123.45" ); // 123.449997
check_float( 1.2345e+3f, "1234.5" ); // 1234.5
check_float( 1.2345e+4f, "12345.0" ); // 12345
check_float( 1.2345e+5f, "123450.0" ); // 123450
check_float( 1.2345e+6f, "1.2345e+06" ); // 1234500
check_float( 1.2345e+7f, "1.2345e+07" ); // 12345000
check_float( 1.2345e+8f, "1.2345e+08" ); // 123450000
check_float( 1.2345e+9f, "1.2345e+09" ); // 1.23449997e+09
check_float( 1.2345e+10f, "1.2345e+10" ); // 1.23449999e+10
check_float( 1.2345e+11f, "1.2345e+11" ); // 1.23449999e+11
check_float( 1.2345e+12f, "1.2345e+12" ); // 1.23450006e+12
check_float( 1.2345e+13f, "1.2345e+13" ); // 1.23449995e+13
check_float( 1.2345e+14f, "1.2345e+14" ); // 1.23450002e+14
check_float( 1.2345e+15f, "1.2345e+15" ); // 1.23450003e+15
check_float( 1.2345e+16f, "1.2345e+16" ); // 1.23449998e+16
check_float( 1.2345e+17f, "1.2345e+17" ); // 1.23449996e+17
check_float( 1.2345e+18f, "1.2345e+18" ); // 1.23450004e+18
check_float( 1.2345e+19f, "1.2345e+19" ); // 1.23449999e+19
check_float( 1.2345e+20f, "1.2345e+20" ); // 1.23449999e+20
check_float( 1.2345e+21f, "1.2345e+21" ); // 1.23449999e+21
check_float( 1.2345e+22f, "1.2345e+22" ); // 1.23450005e+22
}
SECTION("double precision")
{
auto check_double = [](double number, const std::string & expected)
{
std::array<char, 33> buf{};
char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
std::string actual(buf.data(), end);
CHECK(actual == expected);
};
// dtoa %.15g %.17g shortest
check_double( -1.2345e-22, "-1.2345e-22" ); // -1.2345e-22 -1.2345000000000001e-22 -1.2345e-22
check_double( -1.2345e-21, "-1.2345e-21" ); // -1.2345e-21 -1.2345000000000001e-21 -1.2345e-21
check_double( -1.2345e-20, "-1.2345e-20" ); // -1.2345e-20 -1.2345e-20 -1.2345e-20
check_double( -1.2345e-19, "-1.2345e-19" ); // -1.2345e-19 -1.2345000000000001e-19 -1.2345e-19
check_double( -1.2345e-18, "-1.2345e-18" ); // -1.2345e-18 -1.2345000000000001e-18 -1.2345e-18
check_double( -1.2345e-17, "-1.2345e-17" ); // -1.2345e-17 -1.2345e-17 -1.2345e-17
check_double( -1.2345e-16, "-1.2345e-16" ); // -1.2345e-16 -1.2344999999999999e-16 -1.2345e-16
check_double( -1.2345e-15, "-1.2345e-15" ); // -1.2345e-15 -1.2345e-15 -1.2345e-15
check_double( -1.2345e-14, "-1.2345e-14" ); // -1.2345e-14 -1.2345e-14 -1.2345e-14
check_double( -1.2345e-13, "-1.2345e-13" ); // -1.2345e-13 -1.2344999999999999e-13 -1.2345e-13
check_double( -1.2345e-12, "-1.2345e-12" ); // -1.2345e-12 -1.2345e-12 -1.2345e-12
check_double( -1.2345e-11, "-1.2345e-11" ); // -1.2345e-11 -1.2345e-11 -1.2345e-11
check_double( -1.2345e-10, "-1.2345e-10" ); // -1.2345e-10 -1.2345e-10 -1.2345e-10
check_double( -1.2345e-9, "-1.2345e-09" ); // -1.2345e-09 -1.2345e-09 -1.2345e-9
check_double( -1.2345e-8, "-1.2345e-08" ); // -1.2345e-08 -1.2345000000000001e-08 -1.2345e-8
check_double( -1.2345e-7, "-1.2345e-07" ); // -1.2345e-07 -1.2345000000000001e-07 -1.2345e-7
check_double( -1.2345e-6, "-1.2345e-06" ); // -1.2345e-06 -1.2345e-06 -1.2345e-6
check_double( -1.2345e-5, "-1.2345e-05" ); // -1.2345e-05 -1.2345e-05 -1.2345e-5
check_double( -1.2345e-4, "-0.00012345" ); // -0.00012345 -0.00012344999999999999 -0.00012345
check_double( -1.2345e-3, "-0.0012345" ); // -0.0012345 -0.0012344999999999999 -0.0012345
check_double( -1.2345e-2, "-0.012345" ); // -0.012345 -0.012345 -0.012345
check_double( -1.2345e-1, "-0.12345" ); // -0.12345 -0.12345 -0.12345
check_double( -0.0, "-0.0" ); // -0 -0 -0
check_double( 0.0, "0.0" ); // 0 0 0
check_double( 1.2345e+0, "1.2345" ); // 1.2345 1.2344999999999999 1.2345
check_double( 1.2345e+1, "12.345" ); // 12.345 12.345000000000001 12.345
check_double( 1.2345e+2, "123.45" ); // 123.45 123.45 123.45
check_double( 1.2345e+3, "1234.5" ); // 1234.5 1234.5 1234.5
check_double( 1.2345e+4, "12345.0" ); // 12345 12345 12345
check_double( 1.2345e+5, "123450.0" ); // 123450 123450 123450
check_double( 1.2345e+6, "1234500.0" ); // 1234500 1234500 1234500
check_double( 1.2345e+7, "12345000.0" ); // 12345000 12345000 12345000
check_double( 1.2345e+8, "123450000.0" ); // 123450000 123450000 123450000
check_double( 1.2345e+9, "1234500000.0" ); // 1234500000 1234500000 1234500000
check_double( 1.2345e+10, "12345000000.0" ); // 12345000000 12345000000 12345000000
check_double( 1.2345e+11, "123450000000.0" ); // 123450000000 123450000000 123450000000
check_double( 1.2345e+12, "1234500000000.0" ); // 1234500000000 1234500000000 1234500000000
check_double( 1.2345e+13, "12345000000000.0" ); // 12345000000000 12345000000000 12345000000000
check_double( 1.2345e+14, "123450000000000.0" ); // 123450000000000 123450000000000 123450000000000
check_double( 1.2345e+15, "1.2345e+15" ); // 1.2345e+15 1234500000000000 1.2345e15
check_double( 1.2345e+16, "1.2345e+16" ); // 1.2345e+16 12345000000000000 1.2345e16
check_double( 1.2345e+17, "1.2345e+17" ); // 1.2345e+17 1.2345e+17 1.2345e17
check_double( 1.2345e+18, "1.2345e+18" ); // 1.2345e+18 1.2345e+18 1.2345e18
check_double( 1.2345e+19, "1.2345e+19" ); // 1.2345e+19 1.2345e+19 1.2345e19
check_double( 1.2345e+20, "1.2345e+20" ); // 1.2345e+20 1.2345e+20 1.2345e20
check_double( 1.2345e+21, "1.2344999999999999e+21" ); // 1.2345e+21 1.2344999999999999e+21 1.2345e21
check_double( 1.2345e+22, "1.2345e+22" ); // 1.2345e+22 1.2345e+22 1.2345e22
}
SECTION("integer")
{
auto check_integer = [](std::int64_t number, const std::string & expected)
{
const nlohmann::json j = number;
CHECK(j.dump() == expected);
};
// edge cases
check_integer(INT64_MIN, "-9223372036854775808");
check_integer(INT64_MAX, "9223372036854775807");
// few random big integers
check_integer(-3456789012345678901LL, "-3456789012345678901");
check_integer(3456789012345678901LL, "3456789012345678901");
check_integer(-5678901234567890123LL, "-5678901234567890123");
check_integer(5678901234567890123LL, "5678901234567890123");
// integers with various digit counts
check_integer(-1000000000000000000LL, "-1000000000000000000");
check_integer(-100000000000000000LL, "-100000000000000000");
check_integer(-10000000000000000LL, "-10000000000000000");
check_integer(-1000000000000000LL, "-1000000000000000");
check_integer(-100000000000000LL, "-100000000000000");
check_integer(-10000000000000LL, "-10000000000000");
check_integer(-1000000000000LL, "-1000000000000");
check_integer(-100000000000LL, "-100000000000");
check_integer(-10000000000LL, "-10000000000");
check_integer(-1000000000LL, "-1000000000");
check_integer(-100000000LL, "-100000000");
check_integer(-10000000LL, "-10000000");
check_integer(-1000000LL, "-1000000");
check_integer(-100000LL, "-100000");
check_integer(-10000LL, "-10000");
check_integer(-1000LL, "-1000");
check_integer(-100LL, "-100");
check_integer(-10LL, "-10");
check_integer(-1LL, "-1");
check_integer(0, "0");
check_integer(1LL, "1");
check_integer(10LL, "10");
check_integer(100LL, "100");
check_integer(1000LL, "1000");
check_integer(10000LL, "10000");
check_integer(100000LL, "100000");
check_integer(1000000LL, "1000000");
check_integer(10000000LL, "10000000");
check_integer(100000000LL, "100000000");
check_integer(1000000000LL, "1000000000");
check_integer(10000000000LL, "10000000000");
check_integer(100000000000LL, "100000000000");
check_integer(1000000000000LL, "1000000000000");
check_integer(10000000000000LL, "10000000000000");
check_integer(100000000000000LL, "100000000000000");
check_integer(1000000000000000LL, "1000000000000000");
check_integer(10000000000000000LL, "10000000000000000");
check_integer(100000000000000000LL, "100000000000000000");
check_integer(1000000000000000000LL, "1000000000000000000");
}
}

View File

@ -0,0 +1,56 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#if JSON_TEST_USING_MULTIPLE_HEADERS
#include <nlohmann/detail/meta/type_traits.hpp>
#else
#include <nlohmann/json.hpp>
#endif
TEST_CASE("type traits")
{
SECTION("is_c_string")
{
using nlohmann::detail::is_c_string;
using nlohmann::detail::is_c_string_uncvref;
SECTION("char *")
{
CHECK(is_c_string<char*>::value);
CHECK(is_c_string<const char*>::value);
CHECK(is_c_string<char* const>::value);
CHECK(is_c_string<const char* const>::value);
CHECK_FALSE(is_c_string<char*&>::value);
CHECK_FALSE(is_c_string<const char*&>::value);
CHECK_FALSE(is_c_string<char* const&>::value);
CHECK_FALSE(is_c_string<const char* const&>::value);
CHECK(is_c_string_uncvref<char*&>::value);
CHECK(is_c_string_uncvref<const char*&>::value);
CHECK(is_c_string_uncvref<char* const&>::value);
CHECK(is_c_string_uncvref<const char* const&>::value);
}
SECTION("char[]")
{
// NOLINTBEGIN(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
CHECK(is_c_string<char[]>::value);
CHECK(is_c_string<const char[]>::value);
CHECK_FALSE(is_c_string<char(&)[]>::value);
CHECK_FALSE(is_c_string<const char(&)[]>::value);
CHECK(is_c_string_uncvref<char(&)[]>::value);
CHECK(is_c_string_uncvref<const char(&)[]>::value);
// NOLINTEND(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
TEST_CASE("user-defined string literals")
{
auto j_expected = nlohmann::json::parse(R"({"foo": "bar", "baz": 42})");
auto ptr_expected = nlohmann::json::json_pointer("/foo/bar");
SECTION("using namespace nlohmann::literals::json_literals")
{
using namespace nlohmann::literals::json_literals; // NOLINT(google-build-using-namespace)
CHECK(R"({"foo": "bar", "baz": 42})"_json == j_expected);
CHECK("/foo/bar"_json_pointer == ptr_expected);
}
SECTION("using namespace nlohmann::json_literals")
{
using namespace nlohmann::json_literals; // NOLINT(google-build-using-namespace)
CHECK(R"({"foo": "bar", "baz": 42})"_json == j_expected);
CHECK("/foo/bar"_json_pointer == ptr_expected);
}
SECTION("using namespace nlohmann::literals")
{
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
CHECK(R"({"foo": "bar", "baz": 42})"_json == j_expected);
CHECK("/foo/bar"_json_pointer == ptr_expected);
}
SECTION("using namespace nlohmann")
{
using namespace nlohmann; // NOLINT(google-build-using-namespace)
CHECK(R"({"foo": "bar", "baz": 42})"_json == j_expected);
CHECK("/foo/bar"_json_pointer == ptr_expected);
}
#ifndef JSON_TEST_NO_GLOBAL_UDLS
SECTION("global namespace")
{
CHECK(R"({"foo": "bar", "baz": 42})"_json == j_expected);
CHECK("/foo/bar"_json_pointer == ptr_expected);
}
#endif
}

View File

@ -0,0 +1,864 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// disable -Wnoexcept due to class Evil
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
#include <nlohmann/json.hpp>
using nlohmann::json;
#ifdef JSON_TEST_NO_GLOBAL_UDLS
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif
#include <map>
#include <memory>
#include <string>
#include <utility>
namespace udt
{
enum class country
{
china,
france,
russia
};
struct age
{
int m_val;
age(int rhs = 0) : m_val(rhs) {}
};
struct name
{
std::string m_val;
name(std::string rhs = "") : m_val(std::move(rhs)) {}
};
struct address
{
std::string m_val;
address(std::string rhs = "") : m_val(std::move(rhs)) {}
};
struct person
{
age m_age{};
name m_name{};
country m_country{};
person() = default;
person(const age& a, name n, const country& c) : m_age(a), m_name(std::move(n)), m_country(c) {}
};
struct contact
{
person m_person{};
address m_address{};
contact() = default;
contact(person p, address a) : m_person(std::move(p)), m_address(std::move(a)) {}
};
struct contact_book
{
name m_book_name{};
std::vector<contact> m_contacts{};
contact_book() = default;
contact_book(name n, std::vector<contact> c) : m_book_name(std::move(n)), m_contacts(std::move(c)) {}
};
} // namespace udt
// to_json methods
namespace udt
{
// templates because of the custom_json tests (see below)
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, age a)
{
j = a.m_val;
}
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, const name& n)
{
j = n.m_val;
}
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, country c)
{
switch (c)
{
case country::china:
j = "中华人民共和国";
return;
case country::france:
j = "France";
return;
case country::russia:
j = "Российская Федерация";
return;
default:
break;
}
}
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, const person& p)
{
j = BasicJsonType{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}};
}
static void to_json(nlohmann::json& j, const address& a)
{
j = a.m_val;
}
static void to_json(nlohmann::json& j, const contact& c)
{
j = json{{"person", c.m_person}, {"address", c.m_address}};
}
static void to_json(nlohmann::json& j, const contact_book& cb)
{
j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}};
}
// operators
static bool operator==(age lhs, age rhs)
{
return lhs.m_val == rhs.m_val;
}
static bool operator==(const address& lhs, const address& rhs)
{
return lhs.m_val == rhs.m_val;
}
static bool operator==(const name& lhs, const name& rhs)
{
return lhs.m_val == rhs.m_val;
}
static bool operator==(const person& lhs, const person& rhs)
{
return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age);
}
static bool operator==(const contact& lhs, const contact& rhs)
{
return std::tie(lhs.m_person, lhs.m_address) ==
std::tie(rhs.m_person, rhs.m_address);
}
static bool operator==(const contact_book& lhs, const contact_book& rhs)
{
return std::tie(lhs.m_book_name, lhs.m_contacts) ==
std::tie(rhs.m_book_name, rhs.m_contacts);
}
} // namespace udt
// from_json methods
namespace udt
{
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, age& a)
{
a.m_val = j.template get<int>();
}
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, name& n)
{
n.m_val = j.template get<std::string>();
}
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, country& c)
{
const auto str = j.template get<std::string>();
const std::map<std::string, country> m =
{
{"中华人民共和国", country::china},
{"France", country::france},
{"Российская Федерация", country::russia}
};
const auto it = m.find(str);
// TODO(nlohmann) test exceptions
c = it->second;
}
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, person& p)
{
p.m_age = j["age"].template get<age>();
p.m_name = j["name"].template get<name>();
p.m_country = j["country"].template get<country>();
}
static void from_json(const nlohmann::json& j, address& a)
{
a.m_val = j.get<std::string>();
}
static void from_json(const nlohmann::json& j, contact& c)
{
c.m_person = j["person"].get<person>();
c.m_address = j["address"].get<address>();
}
static void from_json(const nlohmann::json& j, contact_book& cb)
{
cb.m_book_name = j["name"].get<name>();
cb.m_contacts = j["contacts"].get<std::vector<contact>>();
}
} // namespace udt
TEST_CASE("basic usage" * doctest::test_suite("udt"))
{
// a bit narcissistic maybe :) ?
const udt::age a
{
23
};
const udt::name n{"theo"};
const udt::country c{udt::country::france};
const udt::person sfinae_addict{a, n, c};
const udt::person senior_programmer{{42}, {"王芳"}, udt::country::china};
const udt::address addr{"Paris"};
const udt::contact cpp_programmer{sfinae_addict, addr};
const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}};
SECTION("conversion to json via free-functions")
{
CHECK(json(a) == json(23));
CHECK(json(n) == json("theo"));
CHECK(json(c) == json("France"));
CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json);
CHECK(json("Paris") == json(addr));
CHECK(json(cpp_programmer) ==
R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json);
CHECK(
json(book) ==
R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"", "name":""}, "address":"Paris"}]})"_json);
}
SECTION("conversion from json via free-functions")
{
const auto big_json =
R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"", "name":""}, "address":"Paris"}]})"_json;
SECTION("via explicit calls to get")
{
const auto parsed_book = big_json.get<udt::contact_book>();
const auto book_name = big_json["name"].get<udt::name>();
const auto contacts =
big_json["contacts"].get<std::vector<udt::contact>>();
const auto contact_json = big_json["contacts"].at(0);
const auto contact = contact_json.get<udt::contact>();
const auto person = contact_json["person"].get<udt::person>();
const auto address = contact_json["address"].get<udt::address>();
const auto age = contact_json["person"]["age"].get<udt::age>();
const auto country =
contact_json["person"]["country"].get<udt::country>();
const auto name = contact_json["person"]["name"].get<udt::name>();
CHECK(age == a);
CHECK(name == n);
CHECK(country == c);
CHECK(address == addr);
CHECK(person == sfinae_addict);
CHECK(contact == cpp_programmer);
CHECK(contacts == book.m_contacts);
CHECK(book_name == udt::name{"C++"});
CHECK(book == parsed_book);
}
SECTION("via explicit calls to get_to")
{
udt::person person;
udt::name name;
json person_json = big_json["contacts"][0]["person"];
CHECK(person_json.get_to(person) == sfinae_addict);
// correct reference gets returned
person_json["name"].get_to(name).m_val = "new name";
CHECK(name.m_val == "new name");
}
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("implicit conversions")
{
const udt::contact_book parsed_book = big_json;
const udt::name book_name = big_json["name"];
const std::vector<udt::contact> contacts = big_json["contacts"];
const auto contact_json = big_json["contacts"].at(0);
const udt::contact contact = contact_json;
const udt::person person = contact_json["person"];
const udt::address address = contact_json["address"];
const udt::age age = contact_json["person"]["age"];
const udt::country country = contact_json["person"]["country"];
const udt::name name = contact_json["person"]["name"];
CHECK(age == a);
CHECK(name == n);
CHECK(country == c);
CHECK(address == addr);
CHECK(person == sfinae_addict);
CHECK(contact == cpp_programmer);
CHECK(contacts == book.m_contacts);
CHECK(book_name == udt::name{"C++"});
CHECK(book == parsed_book);
}
#endif
}
}
namespace udt
{
struct legacy_type
{
std::string number{};
legacy_type() = default;
legacy_type(std::string n) : number(std::move(n)) {}
};
} // namespace udt
namespace nlohmann
{
template <typename T>
struct adl_serializer<std::shared_ptr<T>>
{
static void to_json(json& j, const std::shared_ptr<T>& opt)
{
if (opt)
{
j = *opt;
}
else
{
j = nullptr;
}
}
static void from_json(const json& j, std::shared_ptr<T>& opt)
{
if (j.is_null())
{
opt = nullptr;
}
else
{
opt.reset(new T(j.get<T>())); // NOLINT(cppcoreguidelines-owning-memory)
}
}
};
template <>
struct adl_serializer<udt::legacy_type>
{
static void to_json(json& j, const udt::legacy_type& l)
{
j = std::stoi(l.number);
}
static void from_json(const json& j, udt::legacy_type& l)
{
l.number = std::to_string(j.get<int>());
}
};
} // namespace nlohmann
TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt"))
{
SECTION("partial specialization")
{
SECTION("to_json")
{
std::shared_ptr<udt::person> optPerson;
json j = optPerson;
CHECK(j.is_null());
optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-shared)
j = optPerson;
CHECK_FALSE(j.is_null());
CHECK(j.get<udt::person>() == *optPerson);
}
SECTION("from_json")
{
auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
json j = person;
auto optPerson = j.get<std::shared_ptr<udt::person>>();
REQUIRE(optPerson);
CHECK(*optPerson == person);
j = nullptr;
optPerson = j.get<std::shared_ptr<udt::person>>();
CHECK(!optPerson);
}
}
SECTION("total specialization")
{
SECTION("to_json")
{
udt::legacy_type const lt{"4242"};
json const j = lt;
CHECK(j.get<int>() == 4242);
}
SECTION("from_json")
{
json const j = 4242;
auto lt = j.get<udt::legacy_type>();
CHECK(lt.number == "4242");
}
}
}
namespace nlohmann
{
template <>
struct adl_serializer<std::vector<float>>
{
using type = std::vector<float>;
static void to_json(json& j, const type& /*type*/)
{
j = "hijacked!";
}
static void from_json(const json& /*unnamed*/, type& opt)
{
opt = {42.0, 42.0, 42.0};
}
// preferred version
static type from_json(const json& /*unnamed*/)
{
return {4.0, 5.0, 6.0};
}
};
} // namespace nlohmann
TEST_CASE("even supported types can be specialized" * doctest::test_suite("udt"))
{
json const j = std::vector<float> {1.0, 2.0, 3.0};
CHECK(j.dump() == R"("hijacked!")");
auto f = j.get<std::vector<float>>();
// the single argument from_json method is preferred
CHECK((f == std::vector<float> {4.0, 5.0, 6.0}));
}
namespace nlohmann
{
template <typename T>
struct adl_serializer<std::unique_ptr<T>>
{
static void to_json(json& j, const std::unique_ptr<T>& opt)
{
if (opt)
{
j = *opt;
}
else
{
j = nullptr;
}
}
// this is the overload needed for non-copyable types,
static std::unique_ptr<T> from_json(const json& j)
{
if (j.is_null())
{
return nullptr;
}
return std::unique_ptr<T>(new T(j.get<T>()));
}
};
} // namespace nlohmann
TEST_CASE("Non-copyable types" * doctest::test_suite("udt"))
{
SECTION("to_json")
{
std::unique_ptr<udt::person> optPerson;
json j = optPerson;
CHECK(j.is_null());
optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-unique)
j = optPerson;
CHECK_FALSE(j.is_null());
CHECK(j.get<udt::person>() == *optPerson);
}
SECTION("from_json")
{
auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
json j = person;
auto optPerson = j.get<std::unique_ptr<udt::person>>();
REQUIRE(optPerson);
CHECK(*optPerson == person);
j = nullptr;
optPerson = j.get<std::unique_ptr<udt::person>>();
CHECK(!optPerson);
}
}
// custom serializer - advanced usage
// pack structs that are pod-types (but not scalar types)
// relies on adl for any other type
template <typename T, typename = void>
struct pod_serializer
{
// use adl for non-pods, or scalar types
template <
typename BasicJsonType, typename U = T,
typename std::enable_if <
!(std::is_pod<U>::value && std::is_class<U>::value), int >::type = 0 >
static void from_json(const BasicJsonType& j, U& t)
{
using nlohmann::from_json;
from_json(j, t);
}
// special behaviour for pods
template < typename BasicJsonType, typename U = T,
typename std::enable_if <
std::is_pod<U>::value && std::is_class<U>::value, int >::type = 0 >
static void from_json(const BasicJsonType& j, U& t)
{
std::uint64_t value = 0;
// The following block is no longer relevant in this serializer, make another one that shows the issue
// the problem arises only when one from_json method is defined without any constraint
//
// Why cannot we simply use: j.get<std::uint64_t>() ?
// Well, with the current experiment, the get method looks for a from_json
// function, which we are currently defining!
// This would end up in a stack overflow. Calling nlohmann::from_json is a
// workaround (is it?).
// I shall find a good way to avoid this once all constructors are converted
// to free methods
//
// In short, constructing a json by constructor calls to_json
// calling get calls from_json, for now, we cannot do this in custom
// serializers
nlohmann::from_json(j, value);
auto* bytes = static_cast<char*>(static_cast<void*>(&value)); // NOLINT(bugprone-casting-through-void)
std::memcpy(&t, bytes, sizeof(value));
}
template <
typename BasicJsonType, typename U = T,
typename std::enable_if <
!(std::is_pod<U>::value && std::is_class<U>::value), int >::type = 0 >
static void to_json(BasicJsonType& j, const T& t)
{
using nlohmann::to_json;
to_json(j, t);
}
template < typename BasicJsonType, typename U = T,
typename std::enable_if <
std::is_pod<U>::value && std::is_class<U>::value, int >::type = 0 >
static void to_json(BasicJsonType& j, const T& t) noexcept
{
const auto* bytes = static_cast< const unsigned char*>(static_cast<const void*>(&t)); // NOLINT(bugprone-casting-through-void)
std::uint64_t value = 0;
std::memcpy(&value, bytes, sizeof(value));
nlohmann::to_json(j, value);
}
};
namespace udt
{
struct small_pod
{
int begin;
char middle;
short end;
};
struct non_pod
{
std::string s{};
non_pod() = default;
non_pod(std::string S) : s(std::move(S)) {}
};
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, const non_pod& np)
{
j = np.s;
}
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, non_pod& np)
{
np.s = j.template get<std::string>();
}
static bool operator==(small_pod lhs, small_pod rhs) noexcept
{
return std::tie(lhs.begin, lhs.middle, lhs.end) ==
std::tie(rhs.begin, rhs.middle, rhs.end);
}
static bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept
{
return lhs.s == rhs.s;
}
static std::ostream& operator<<(std::ostream& os, small_pod l)
{
return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end;
}
} // namespace udt
TEST_CASE("custom serializer for pods" * doctest::test_suite("udt"))
{
using custom_json =
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator, pod_serializer>;
auto p = udt::small_pod{42, '/', 42};
custom_json const j = p;
auto p2 = j.get<udt::small_pod>();
CHECK(p == p2);
auto np = udt::non_pod{{"non-pod"}};
custom_json const j2 = np;
auto np2 = j2.get<udt::non_pod>();
CHECK(np == np2);
}
template <typename T, typename>
struct another_adl_serializer;
using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>;
template <typename T, typename>
struct another_adl_serializer
{
static void from_json(const custom_json& j, T& t)
{
using nlohmann::from_json;
from_json(j, t);
}
static void to_json(custom_json& j, const T& t)
{
using nlohmann::to_json;
to_json(j, t);
}
};
TEST_CASE("custom serializer that does adl by default" * doctest::test_suite("udt"))
{
auto me = udt::person{{23}, {"theo"}, udt::country::france};
json const j = me;
custom_json const cj = me;
CHECK(j.dump() == cj.dump());
CHECK(me == j.get<udt::person>());
CHECK(me == cj.get<udt::person>());
}
TEST_CASE("different basic_json types conversions")
{
SECTION("null")
{
json const j;
custom_json cj = j;
CHECK(cj == nullptr);
}
SECTION("boolean")
{
json const j = true;
custom_json cj = j;
CHECK(cj == true);
}
SECTION("discarded")
{
json const j(json::value_t::discarded);
custom_json cj;
CHECK_NOTHROW(cj = j);
CHECK(cj.type() == custom_json::value_t::discarded);
}
SECTION("array")
{
json const j = {1, 2, 3};
custom_json const cj = j;
CHECK((cj == std::vector<int> {1, 2, 3}));
}
SECTION("integer")
{
json const j = 42;
custom_json cj = j;
CHECK(cj == 42);
}
SECTION("float")
{
json const j = 42.0;
custom_json cj = j;
CHECK(cj == 42.0);
}
SECTION("unsigned")
{
json const j = 42u;
custom_json cj = j;
CHECK(cj == 42u);
}
SECTION("string")
{
json const j = "forty-two";
custom_json cj = j;
CHECK(cj == "forty-two");
}
SECTION("binary")
{
json j = json::binary({1, 2, 3}, 42);
custom_json cj = j;
CHECK(cj.get_binary().subtype() == 42);
std::vector<std::uint8_t> cv = cj.get_binary();
std::vector<std::uint8_t> v = j.get_binary();
CHECK(cv == v);
}
SECTION("object")
{
json const j = {{"forty", "two"}};
custom_json cj = j;
auto m = j.get<std::map<std::string, std::string>>();
CHECK(cj == m);
}
SECTION("get<custom_json>")
{
json const j = 42;
custom_json cj = j.get<custom_json>();
CHECK(cj == 42);
}
}
namespace
{
struct incomplete;
// std::is_constructible is broken on macOS' libc++
// use the cppreference implementation
template <typename T, typename = void>
struct is_constructible_patched : std::false_type {};
template <typename T>
struct is_constructible_patched<T, decltype(void(json(std::declval<T>())))> : std::true_type {};
} // namespace
TEST_CASE("an incomplete type does not trigger a compiler error in non-evaluated context" * doctest::test_suite("udt"))
{
static_assert(!is_constructible_patched<json, incomplete>::value, "");
}
namespace
{
class Evil
{
public:
Evil() = default;
template <typename T>
Evil(T t) : m_i(sizeof(t))
{
static_cast<void>(t); // fix MSVC's C4100 warning
}
int m_i = 0;
};
void from_json(const json& /*unused*/, Evil& /*unused*/) {}
} // namespace
TEST_CASE("Issue #924")
{
// Prevent get<std::vector<Evil>>() to throw
auto j = json::array();
CHECK_NOTHROW(j.get<Evil>());
CHECK_NOTHROW(j.get<std::vector<Evil>>());
// silence Wunused-template warnings
Evil e(1);
CHECK(e.m_i >= 0);
}
TEST_CASE("Issue #1237")
{
struct non_convertible_type {};
static_assert(!std::is_convertible<json, non_convertible_type>::value, "");
}
namespace
{
class no_iterator_type
{
public:
no_iterator_type(std::initializer_list<int> l)
: _v(l)
{}
std::vector<int>::const_iterator begin() const
{
return _v.begin();
}
std::vector<int>::const_iterator end() const
{
return _v.end();
}
private:
std::vector<int> _v;
};
} // namespace
TEST_CASE("compatible array type, without iterator type alias")
{
no_iterator_type const vec{1, 2, 3};
json const j = vec;
}
DOCTEST_GCC_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,414 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include <string>
#include <vector>
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
namespace persons
{
class person_with_private_data
{
private:
std::string name{};
int age = 0;
json metadata = nullptr;
public:
bool operator==(const person_with_private_data& rhs) const
{
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
}
person_with_private_data() = default;
person_with_private_data(std::string name_, int age_, json metadata_)
: name(std::move(name_))
, age(age_)
, metadata(std::move(metadata_))
{}
NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata)
};
class person_with_private_data_2
{
private:
std::string name{};
int age = 0;
json metadata = nullptr;
public:
bool operator==(const person_with_private_data_2& rhs) const
{
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
}
person_with_private_data_2() = default;
person_with_private_data_2(std::string name_, int age_, json metadata_)
: name(std::move(name_))
, age(age_)
, metadata(std::move(metadata_))
{}
std::string getName() const
{
return name;
}
int getAge() const
{
return age;
}
json getMetadata() const
{
return metadata;
}
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person_with_private_data_2, age, name, metadata)
};
class person_without_private_data_1
{
public:
std::string name{};
int age = 0;
json metadata = nullptr;
bool operator==(const person_without_private_data_1& rhs) const
{
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
}
person_without_private_data_1() = default;
person_without_private_data_1(std::string name_, int age_, json metadata_)
: name(std::move(name_))
, age(age_)
, metadata(std::move(metadata_))
{}
NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata)
};
class person_without_private_data_2
{
public:
std::string name{};
int age = 0;
json metadata = nullptr;
bool operator==(const person_without_private_data_2& rhs) const
{
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
}
person_without_private_data_2() = default;
person_without_private_data_2(std::string name_, int age_, json metadata_)
: name(std::move(name_))
, age(age_)
, metadata(std::move(metadata_))
{}
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata)
class person_without_private_data_3
{
public:
std::string name{};
int age = 0;
json metadata = nullptr;
bool operator==(const person_without_private_data_3& rhs) const
{
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
}
person_without_private_data_3() = default;
person_without_private_data_3(std::string name_, int age_, json metadata_)
: name(std::move(name_))
, age(age_)
, metadata(std::move(metadata_))
{}
std::string getName() const
{
return name;
}
int getAge() const
{
return age;
}
json getMetadata() const
{
return metadata;
}
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person_without_private_data_3, age, name, metadata)
class person_with_private_alphabet
{
public:
bool operator==(const person_with_private_alphabet& other) const
{
return a == other.a &&
b == other.b &&
c == other.c &&
d == other.d &&
e == other.e &&
f == other.f &&
g == other.g &&
h == other.h &&
i == other.i &&
j == other.j &&
k == other.k &&
l == other.l &&
m == other.m &&
n == other.n &&
o == other.o &&
p == other.p &&
q == other.q &&
r == other.r &&
s == other.s &&
t == other.t &&
u == other.u &&
v == other.v &&
w == other.w &&
x == other.x &&
y == other.y &&
z == other.z;
}
private:
int a = 0;
int b = 0;
int c = 0;
int d = 0;
int e = 0;
int f = 0;
int g = 0;
int h = 0;
int i = 0;
int j = 0;
int k = 0;
int l = 0;
int m = 0;
int n = 0;
int o = 0;
int p = 0;
int q = 0;
int r = 0;
int s = 0;
int t = 0;
int u = 0;
int v = 0;
int w = 0;
int x = 0;
int y = 0;
int z = 0;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)
};
class person_with_public_alphabet
{
public:
bool operator==(const person_with_public_alphabet& other) const
{
return a == other.a &&
b == other.b &&
c == other.c &&
d == other.d &&
e == other.e &&
f == other.f &&
g == other.g &&
h == other.h &&
i == other.i &&
j == other.j &&
k == other.k &&
l == other.l &&
m == other.m &&
n == other.n &&
o == other.o &&
p == other.p &&
q == other.q &&
r == other.r &&
s == other.s &&
t == other.t &&
u == other.u &&
v == other.v &&
w == other.w &&
x == other.x &&
y == other.y &&
z == other.z;
}
int a = 0;
int b = 0;
int c = 0;
int d = 0;
int e = 0;
int f = 0;
int g = 0;
int h = 0;
int i = 0;
int j = 0;
int k = 0;
int l = 0;
int m = 0;
int n = 0;
int o = 0;
int p = 0;
int q = 0;
int r = 0;
int s = 0;
int t = 0;
int u = 0;
int v = 0;
int w = 0;
int x = 0;
int y = 0;
int z = 0;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_with_public_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)
} // namespace persons
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
persons::person_with_private_data,
persons::person_without_private_data_1,
persons::person_without_private_data_2)
{
SECTION("person")
{
// serialization
T p1("Erik", 1, {{"haircuts", 2}});
CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
// deserialization
auto p2 = json(p1).get<T>();
CHECK(p2 == p1);
// roundtrip
CHECK(T(json(p1)) == p1);
CHECK(json(T(json(p1))) == json(p1));
// check exception in case of missing field
json j = json(p1);
j.erase("age");
CHECK_THROWS_WITH_AS(j.get<T>(), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range);
}
}
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", T,
persons::person_with_private_data_2,
persons::person_without_private_data_3)
{
SECTION("person with default values")
{
// serialization of default constructed object
T p0;
CHECK(json(p0).dump() == "{\"age\":0,\"metadata\":null,\"name\":\"\"}");
// serialization
T p1("Erik", 1, {{"haircuts", 2}});
CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
// deserialization
auto p2 = json(p1).get<T>();
CHECK(p2 == p1);
// roundtrip
CHECK(T(json(p1)) == p1);
CHECK(json(T(json(p1))) == json(p1));
// check default value in case of missing field
json j = json(p1);
j.erase("name");
j.erase("age");
j.erase("metadata");
T p3 = j.get<T>();
CHECK(p3.getName() == "");
CHECK(p3.getAge() == 0);
CHECK(p3.getMetadata() == nullptr);
}
}
TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
persons::person_with_private_alphabet,
persons::person_with_public_alphabet)
{
SECTION("alphabet")
{
{
T obj1;
nlohmann::json const j = obj1; //via json object
T obj2;
j.get_to(obj2);
bool ok = (obj1 == obj2);
CHECK(ok);
}
{
T obj1;
nlohmann::json const j1 = obj1; //via json string
std::string const s = j1.dump();
nlohmann::json const j2 = nlohmann::json::parse(s);
T obj2;
j2.get_to(obj2);
bool ok = (obj1 == obj2);
CHECK(ok);
}
{
T obj1;
nlohmann::json const j1 = obj1; //via msgpack
std::vector<uint8_t> const buf = nlohmann::json::to_msgpack(j1);
nlohmann::json const j2 = nlohmann::json::from_msgpack(buf);
T obj2;
j2.get_to(obj2);
bool ok = (obj1 == obj2);
CHECK(ok);
}
{
T obj1;
nlohmann::json const j1 = obj1; //via bson
std::vector<uint8_t> const buf = nlohmann::json::to_bson(j1);
nlohmann::json const j2 = nlohmann::json::from_bson(buf);
T obj2;
j2.get_to(obj2);
bool ok = (obj1 == obj2);
CHECK(ok);
}
{
T obj1;
nlohmann::json const j1 = obj1; //via cbor
std::vector<uint8_t> const buf = nlohmann::json::to_cbor(j1);
nlohmann::json const j2 = nlohmann::json::from_cbor(buf);
T obj2;
j2.get_to(obj2);
bool ok = (obj1 == obj2);
CHECK(ok);
}
{
T obj1;
nlohmann::json const j1 = obj1; //via ubjson
std::vector<uint8_t> const buf = nlohmann::json::to_ubjson(j1);
nlohmann::json const j2 = nlohmann::json::from_ubjson(buf);
T obj2;
j2.get_to(obj2);
bool ok = (obj1 == obj2);
CHECK(ok);
}
}
}

View File

@ -0,0 +1,620 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <fstream>
#include <sstream>
#include <iomanip>
#include "make_test_data_available.hpp"
TEST_CASE("Unicode (1/5)" * doctest::skip())
{
SECTION("\\uxxxx sequences")
{
// create an escaped string from a code point
const auto codepoint_to_unicode = [](std::size_t cp)
{
// code points are represented as a six-character sequence: a
// reverse solidus, followed by the lowercase letter u, followed
// by four hexadecimal digits that encode the character's code
// point
std::stringstream ss;
ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp;
return ss.str();
};
SECTION("correct sequences")
{
// generate all UTF-8 code points; in total, 1112064 code points are
// generated: 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
{
// string to store the code point as in \uxxxx format
std::string json_text = "\"";
// decide whether to use one or two \uxxxx sequences
if (cp < 0x10000u)
{
// The Unicode standard permanently reserves these code point
// values for UTF-16 encoding of the high and low surrogates, and
// they will never be assigned a character, so there should be no
// reason to encode them. The official Unicode standard says that
// no UTF forms, including UTF-16, can encode these code points.
if (cp >= 0xD800u && cp <= 0xDFFFu)
{
// if we would not skip these code points, we would get a
// "missing low surrogate" exception
continue;
}
// code points in the Basic Multilingual Plane can be
// represented with one \uxxxx sequence
json_text += codepoint_to_unicode(cp);
}
else
{
// To escape an extended character that is not in the Basic
// Multilingual Plane, the character is represented as a
// 12-character sequence, encoding the UTF-16 surrogate pair
const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
json_text += codepoint_to_unicode(codepoint1) + codepoint_to_unicode(codepoint2);
}
json_text += "\"";
CAPTURE(json_text)
json _;
CHECK_NOTHROW(_ = json::parse(json_text));
}
}
SECTION("incorrect sequences")
{
SECTION("incorrect surrogate values")
{
json _;
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uDC00\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uDC00'", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD7FF\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uD7FF\\uDC00'", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800]\""), "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800]'", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\v\""), "[json.exception.parse_error.101] parse error at line 1, column 9: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\v'", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\u123\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\uD800\\u123\"'", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uDBFF\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uDBFF'", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uE000\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uE000'", json::parse_error&);
}
}
#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if)
SECTION("incorrect sequences")
{
SECTION("high surrogate without low surrogate")
{
// D800..DBFF are high surrogates and must be followed by low
// surrogates DC00..DFFF; here, nothing follows
for (std::size_t cp = 0xD800u; cp <= 0xDBFFu; ++cp)
{
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
CAPTURE(json_text)
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
}
}
SECTION("high surrogate with wrong low surrogate")
{
// D800..DBFF are high surrogates and must be followed by low
// surrogates DC00..DFFF; here a different sequence follows
for (std::size_t cp1 = 0xD800u; cp1 <= 0xDBFFu; ++cp1)
{
for (std::size_t cp2 = 0x0000u; cp2 <= 0xFFFFu; ++cp2)
{
if (0xDC00u <= cp2 && cp2 <= 0xDFFFu)
{
continue;
}
std::string json_text = "\"" + codepoint_to_unicode(cp1) + codepoint_to_unicode(cp2) + "\"";
CAPTURE(json_text)
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
}
}
}
SECTION("low surrogate without high surrogate")
{
// low surrogates DC00..DFFF must follow high surrogates; here,
// they occur alone
for (std::size_t cp = 0xDC00u; cp <= 0xDFFFu; ++cp)
{
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
CAPTURE(json_text)
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
}
}
}
#endif
}
SECTION("read all unicode characters")
{
// read a file with all unicode characters stored as single-character
// strings in a JSON array
std::ifstream f(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json");
json j;
CHECK_NOTHROW(f >> j);
// the array has 1112064 + 1 elements (a terminating "null" value)
// Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
CHECK(j.size() == 1112065);
SECTION("check JSON Pointers")
{
for (const auto& s : j)
{
// skip non-string JSON values
if (!s.is_string())
{
continue;
}
auto ptr = s.get<std::string>();
// tilde must be followed by 0 or 1
if (ptr == "~")
{
ptr += "0";
}
// JSON Pointers must begin with "/"
ptr.insert(0, "/");
CHECK_NOTHROW(json::json_pointer("/" + ptr));
// check escape/unescape roundtrip
auto escaped = nlohmann::detail::escape(ptr);
nlohmann::detail::unescape(escaped);
CHECK(escaped == ptr);
}
}
}
SECTION("ignore byte-order-mark")
{
SECTION("in a stream")
{
// read a file with a UTF-8 BOM
std::ifstream f(TEST_DATA_DIRECTORY "/json_nlohmann_tests/bom.json");
json j;
CHECK_NOTHROW(f >> j);
}
SECTION("with an iterator")
{
std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}";
json _;
CHECK_NOTHROW(_ = json::parse(i.begin(), i.end()));
}
}
SECTION("error for incomplete/wrong BOM")
{
json _;
CHECK_THROWS_AS(_ = json::parse("\xef\xbb"), json::parse_error&);
CHECK_THROWS_AS(_ = json::parse("\xef\xbb\xbb"), json::parse_error&);
}
}
namespace
{
void roundtrip(bool success_expected, const std::string& s);
void roundtrip(bool success_expected, const std::string& s)
{
CAPTURE(s)
json _;
// create JSON string value
const json j = s;
// create JSON text
const std::string ps = std::string("\"") + s + "\"";
if (success_expected)
{
// serialization succeeds
CHECK_NOTHROW(j.dump());
// exclude parse test for U+0000
if (s[0] != '\0')
{
// parsing JSON text succeeds
CHECK_NOTHROW(_ = json::parse(ps));
}
// roundtrip succeeds
CHECK_NOTHROW(_ = json::parse(j.dump()));
// after roundtrip, the same string is stored
const json jr = json::parse(j.dump());
CHECK(jr.get<std::string>() == s);
}
else
{
// serialization fails
CHECK_THROWS_AS(j.dump(), json::type_error&);
// parsing JSON text fails
CHECK_THROWS_AS(_ = json::parse(ps), json::parse_error&);
}
}
} // namespace
TEST_CASE("Markus Kuhn's UTF-8 decoder capability and stress test")
{
// Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> - 2015-08-28 - CC BY 4.0
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
SECTION("1 Some correct UTF-8 text")
{
roundtrip(true, "κόσμε");
}
SECTION("2 Boundary condition test cases")
{
SECTION("2.1 First possible sequence of a certain length")
{
// 2.1.1 1 byte (U-00000000)
roundtrip(true, std::string("\0", 1));
// 2.1.2 2 bytes (U-00000080)
roundtrip(true, "\xc2\x80");
// 2.1.3 3 bytes (U-00000800)
roundtrip(true, "\xe0\xa0\x80");
// 2.1.4 4 bytes (U-00010000)
roundtrip(true, "\xf0\x90\x80\x80");
// 2.1.5 5 bytes (U-00200000)
roundtrip(false, "\xF8\x88\x80\x80\x80");
// 2.1.6 6 bytes (U-04000000)
roundtrip(false, "\xFC\x84\x80\x80\x80\x80");
}
SECTION("2.2 Last possible sequence of a certain length")
{
// 2.2.1 1 byte (U-0000007F)
roundtrip(true, "\x7f");
// 2.2.2 2 bytes (U-000007FF)
roundtrip(true, "\xdf\xbf");
// 2.2.3 3 bytes (U-0000FFFF)
roundtrip(true, "\xef\xbf\xbf");
// 2.2.4 4 bytes (U-001FFFFF)
roundtrip(false, "\xF7\xBF\xBF\xBF");
// 2.2.5 5 bytes (U-03FFFFFF)
roundtrip(false, "\xFB\xBF\xBF\xBF\xBF");
// 2.2.6 6 bytes (U-7FFFFFFF)
roundtrip(false, "\xFD\xBF\xBF\xBF\xBF\xBF");
}
SECTION("2.3 Other boundary conditions")
{
// 2.3.1 U-0000D7FF = ed 9f bf
roundtrip(true, "\xed\x9f\xbf");
// 2.3.2 U-0000E000 = ee 80 80
roundtrip(true, "\xee\x80\x80");
// 2.3.3 U-0000FFFD = ef bf bd
roundtrip(true, "\xef\xbf\xbd");
// 2.3.4 U-0010FFFF = f4 8f bf bf
roundtrip(true, "\xf4\x8f\xbf\xbf");
// 2.3.5 U-00110000 = f4 90 80 80
roundtrip(false, "\xf4\x90\x80\x80");
}
}
SECTION("3 Malformed sequences")
{
SECTION("3.1 Unexpected continuation bytes")
{
// Each unexpected continuation byte should be separately signalled as a
// malformed sequence of its own.
// 3.1.1 First continuation byte 0x80
roundtrip(false, "\x80");
// 3.1.2 Last continuation byte 0xbf
roundtrip(false, "\xbf");
// 3.1.3 2 continuation bytes
roundtrip(false, "\x80\xbf");
// 3.1.4 3 continuation bytes
roundtrip(false, "\x80\xbf\x80");
// 3.1.5 4 continuation bytes
roundtrip(false, "\x80\xbf\x80\xbf");
// 3.1.6 5 continuation bytes
roundtrip(false, "\x80\xbf\x80\xbf\x80");
// 3.1.7 6 continuation bytes
roundtrip(false, "\x80\xbf\x80\xbf\x80\xbf");
// 3.1.8 7 continuation bytes
roundtrip(false, "\x80\xbf\x80\xbf\x80\xbf\x80");
// 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf)
roundtrip(false, "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf");
}
SECTION("3.2 Lonely start characters")
{
// 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf)
roundtrip(false, "\xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf");
// 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef)
roundtrip(false, "\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef");
// 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7)
roundtrip(false, "\xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7");
// 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb)
roundtrip(false, "\xf8 \xf9 \xfa \xfb");
// 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd)
roundtrip(false, "\xfc \xfd");
}
SECTION("3.3 Sequences with last continuation byte missing")
{
// All bytes of an incomplete sequence should be signalled as a single
// malformed sequence, i.e., you should see only a single replacement
// character in each of the next 10 tests. (Characters as in section 2)
// 3.3.1 2-byte sequence with last byte missing (U+0000)
roundtrip(false, "\xc0");
// 3.3.2 3-byte sequence with last byte missing (U+0000)
roundtrip(false, "\xe0\x80");
// 3.3.3 4-byte sequence with last byte missing (U+0000)
roundtrip(false, "\xf0\x80\x80");
// 3.3.4 5-byte sequence with last byte missing (U+0000)
roundtrip(false, "\xf8\x80\x80\x80");
// 3.3.5 6-byte sequence with last byte missing (U+0000)
roundtrip(false, "\xfc\x80\x80\x80\x80");
// 3.3.6 2-byte sequence with last byte missing (U-000007FF)
roundtrip(false, "\xdf");
// 3.3.7 3-byte sequence with last byte missing (U-0000FFFF)
roundtrip(false, "\xef\xbf");
// 3.3.8 4-byte sequence with last byte missing (U-001FFFFF)
roundtrip(false, "\xf7\xbf\xbf");
// 3.3.9 5-byte sequence with last byte missing (U-03FFFFFF)
roundtrip(false, "\xfb\xbf\xbf\xbf");
// 3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF)
roundtrip(false, "\xfd\xbf\xbf\xbf\xbf");
}
SECTION("3.4 Concatenation of incomplete sequences")
{
// All the 10 sequences of 3.3 concatenated, you should see 10 malformed
// sequences being signalled:
roundtrip(false, "\xc0\xe0\x80\xf0\x80\x80\xf8\x80\x80\x80\xfc\x80\x80\x80\x80\xdf\xef\xbf\xf7\xbf\xbf\xfb\xbf\xbf\xbf\xfd\xbf\xbf\xbf\xbf");
}
SECTION("3.5 Impossible bytes")
{
// The following two bytes cannot appear in a correct UTF-8 string
// 3.5.1 fe
roundtrip(false, "\xfe");
// 3.5.2 ff
roundtrip(false, "\xff");
// 3.5.3 fe fe ff ff
roundtrip(false, "\xfe\xfe\xff\xff");
}
}
SECTION("4 Overlong sequences")
{
// The following sequences are not malformed according to the letter of
// the Unicode 2.0 standard. However, they are longer then necessary and
// a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8
// decoder" should reject them just like malformed sequences for two
// reasons: (1) It helps to debug applications if overlong sequences are
// not treated as valid representations of characters, because this helps
// to spot problems more quickly. (2) Overlong sequences provide
// alternative representations of characters, that could maliciously be
// used to bypass filters that check only for ASCII characters. For
// instance, a 2-byte encoded line feed (LF) would not be caught by a
// line counter that counts only 0x0a bytes, but it would still be
// processed as a line feed by an unsafe UTF-8 decoder later in the
// pipeline. From a security point of view, ASCII compatibility of UTF-8
// sequences means also, that ASCII characters are *only* allowed to be
// represented by ASCII bytes in the range 0x00-0x7f. To ensure this
// aspect of ASCII compatibility, use only "safe UTF-8 decoders" that
// reject overlong UTF-8 sequences for which a shorter encoding exists.
SECTION("4.1 Examples of an overlong ASCII character")
{
// With a safe UTF-8 decoder, all the following five overlong
// representations of the ASCII character slash ("/") should be rejected
// like a malformed UTF-8 sequence, for instance by substituting it with
// a replacement character. If you see a slash below, you do not have a
// safe UTF-8 decoder!
// 4.1.1 U+002F = c0 af
roundtrip(false, "\xc0\xaf");
// 4.1.2 U+002F = e0 80 af
roundtrip(false, "\xe0\x80\xaf");
// 4.1.3 U+002F = f0 80 80 af
roundtrip(false, "\xf0\x80\x80\xaf");
// 4.1.4 U+002F = f8 80 80 80 af
roundtrip(false, "\xf8\x80\x80\x80\xaf");
// 4.1.5 U+002F = fc 80 80 80 80 af
roundtrip(false, "\xfc\x80\x80\x80\x80\xaf");
}
SECTION("4.2 Maximum overlong sequences")
{
// Below you see the highest Unicode value that is still resulting in an
// overlong sequence if represented with the given number of bytes. This
// is a boundary test for safe UTF-8 decoders. All five characters should
// be rejected like malformed UTF-8 sequences.
// 4.2.1 U-0000007F = c1 bf
roundtrip(false, "\xc1\xbf");
// 4.2.2 U-000007FF = e0 9f bf
roundtrip(false, "\xe0\x9f\xbf");
// 4.2.3 U-0000FFFF = f0 8f bf bf
roundtrip(false, "\xf0\x8f\xbf\xbf");
// 4.2.4 U-001FFFFF = f8 87 bf bf bf
roundtrip(false, "\xf8\x87\xbf\xbf\xbf");
// 4.2.5 U-03FFFFFF = fc 83 bf bf bf bf
roundtrip(false, "\xfc\x83\xbf\xbf\xbf\xbf");
}
SECTION("4.3 Overlong representation of the NUL character")
{
// The following five sequences should also be rejected like malformed
// UTF-8 sequences and should not be treated like the ASCII NUL
// character.
// 4.3.1 U+0000 = c0 80
roundtrip(false, "\xc0\x80");
// 4.3.2 U+0000 = e0 80 80
roundtrip(false, "\xe0\x80\x80");
// 4.3.3 U+0000 = f0 80 80 80
roundtrip(false, "\xf0\x80\x80\x80");
// 4.3.4 U+0000 = f8 80 80 80 80
roundtrip(false, "\xf8\x80\x80\x80\x80");
// 4.3.5 U+0000 = fc 80 80 80 80 80
roundtrip(false, "\xfc\x80\x80\x80\x80\x80");
}
}
SECTION("5 Illegal code positions")
{
// The following UTF-8 sequences should be rejected like malformed
// sequences, because they never represent valid ISO 10646 characters and
// a UTF-8 decoder that accepts them might introduce security problems
// comparable to overlong UTF-8 sequences.
SECTION("5.1 Single UTF-16 surrogates")
{
// 5.1.1 U+D800 = ed a0 80
roundtrip(false, "\xed\xa0\x80");
// 5.1.2 U+DB7F = ed ad bf
roundtrip(false, "\xed\xad\xbf");
// 5.1.3 U+DB80 = ed ae 80
roundtrip(false, "\xed\xae\x80");
// 5.1.4 U+DBFF = ed af bf
roundtrip(false, "\xed\xaf\xbf");
// 5.1.5 U+DC00 = ed b0 80
roundtrip(false, "\xed\xb0\x80");
// 5.1.6 U+DF80 = ed be 80
roundtrip(false, "\xed\xbe\x80");
// 5.1.7 U+DFFF = ed bf bf
roundtrip(false, "\xed\xbf\xbf");
}
SECTION("5.2 Paired UTF-16 surrogates")
{
// 5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80
roundtrip(false, "\xed\xa0\x80\xed\xb0\x80");
// 5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf
roundtrip(false, "\xed\xa0\x80\xed\xbf\xbf");
// 5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80
roundtrip(false, "\xed\xad\xbf\xed\xb0\x80");
// 5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf
roundtrip(false, "\xed\xad\xbf\xed\xbf\xbf");
// 5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80
roundtrip(false, "\xed\xae\x80\xed\xb0\x80");
// 5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf
roundtrip(false, "\xed\xae\x80\xed\xbf\xbf");
// 5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80
roundtrip(false, "\xed\xaf\xbf\xed\xb0\x80");
// 5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf
roundtrip(false, "\xed\xaf\xbf\xed\xbf\xbf");
}
SECTION("5.3 Noncharacter code positions")
{
// The following "noncharacters" are "reserved for internal use" by
// applications, and according to older versions of the Unicode Standard
// "should never be interchanged". Unicode Corrigendum #9 dropped the
// latter restriction. Nevertheless, their presence in incoming UTF-8 data
// can remain a potential security risk, depending on what use is made of
// these codes subsequently. Examples of such internal use:
//
// - Some file APIs with 16-bit characters may use the integer value -1
// = U+FFFF to signal an end-of-file (EOF) or error condition.
//
// - In some UTF-16 receivers, code point U+FFFE might trigger a
// byte-swap operation (to convert between UTF-16LE and UTF-16BE).
//
// With such internal use of noncharacters, it may be desirable and safer
// to block those code points in UTF-8 decoders, as they should never
// occur legitimately in incoming UTF-8 data, and could trigger unsafe
// behaviour in subsequent processing.
// Particularly problematic noncharacters in 16-bit applications:
// 5.3.1 U+FFFE = ef bf be
roundtrip(true, "\xef\xbf\xbe");
// 5.3.2 U+FFFF = ef bf bf
roundtrip(true, "\xef\xbf\xbf");
// 5.3.3 U+FDD0 .. U+FDEF
roundtrip(true, "\xEF\xB7\x90");
roundtrip(true, "\xEF\xB7\x91");
roundtrip(true, "\xEF\xB7\x92");
roundtrip(true, "\xEF\xB7\x93");
roundtrip(true, "\xEF\xB7\x94");
roundtrip(true, "\xEF\xB7\x95");
roundtrip(true, "\xEF\xB7\x96");
roundtrip(true, "\xEF\xB7\x97");
roundtrip(true, "\xEF\xB7\x98");
roundtrip(true, "\xEF\xB7\x99");
roundtrip(true, "\xEF\xB7\x9A");
roundtrip(true, "\xEF\xB7\x9B");
roundtrip(true, "\xEF\xB7\x9C");
roundtrip(true, "\xEF\xB7\x9D");
roundtrip(true, "\xEF\xB7\x9E");
roundtrip(true, "\xEF\xB7\x9F");
roundtrip(true, "\xEF\xB7\xA0");
roundtrip(true, "\xEF\xB7\xA1");
roundtrip(true, "\xEF\xB7\xA2");
roundtrip(true, "\xEF\xB7\xA3");
roundtrip(true, "\xEF\xB7\xA4");
roundtrip(true, "\xEF\xB7\xA5");
roundtrip(true, "\xEF\xB7\xA6");
roundtrip(true, "\xEF\xB7\xA7");
roundtrip(true, "\xEF\xB7\xA8");
roundtrip(true, "\xEF\xB7\xA9");
roundtrip(true, "\xEF\xB7\xAA");
roundtrip(true, "\xEF\xB7\xAB");
roundtrip(true, "\xEF\xB7\xAC");
roundtrip(true, "\xEF\xB7\xAD");
roundtrip(true, "\xEF\xB7\xAE");
roundtrip(true, "\xEF\xB7\xAF");
// 5.3.4 U+nFFFE U+nFFFF (for n = 1..10)
roundtrip(true, "\xF0\x9F\xBF\xBF");
roundtrip(true, "\xF0\xAF\xBF\xBF");
roundtrip(true, "\xF0\xBF\xBF\xBF");
roundtrip(true, "\xF1\x8F\xBF\xBF");
roundtrip(true, "\xF1\x9F\xBF\xBF");
roundtrip(true, "\xF1\xAF\xBF\xBF");
roundtrip(true, "\xF1\xBF\xBF\xBF");
roundtrip(true, "\xF2\x8F\xBF\xBF");
roundtrip(true, "\xF2\x9F\xBF\xBF");
roundtrip(true, "\xF2\xAF\xBF\xBF");
}
}
}

View File

@ -0,0 +1,610 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "make_test_data_available.hpp"
// this test suite uses static variables with non-trivial destructors
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
namespace
{
extern size_t calls;
size_t calls = 0;
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
static std::string json_string;
json_string.clear();
CAPTURE(byte1)
CAPTURE(byte2)
CAPTURE(byte3)
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
json_string += std::string(1, static_cast<char>(byte4));
}
CAPTURE(json_string)
// store the string in a JSON value
static json j;
static json j2;
j = json_string;
j2 = "abc" + json_string + "xyz";
static std::string s_ignored;
static std::string s_ignored2;
static std::string s_ignored_ascii;
static std::string s_ignored2_ascii;
static std::string s_replaced;
static std::string s_replaced2;
static std::string s_replaced_ascii;
static std::string s_replaced2_ascii;
// dumping with ignore/replace must not throw in any case
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
if (success_expected)
{
static std::string s_strict;
// strict mode must not throw if success is expected
s_strict = j.dump();
// all dumps should agree on the string
CHECK(s_strict == s_ignored);
CHECK(s_strict == s_replaced);
}
else
{
// strict mode must throw if success is not expected
CHECK_THROWS_AS(j.dump(), json::type_error&);
// ignore and replace must create different dumps
CHECK(s_ignored != s_replaced);
// check that replace string contains a replacement character
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
}
// check that prefix and suffix are preserved
CHECK(s_ignored2.substr(1, 3) == "abc");
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
CHECK(s_replaced2.substr(1, 3) == "abc");
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
}
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
// create and check a JSON string with up to four UTF-8 bytes
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
if (++calls % 100000 == 0)
{
std::cout << calls << " of 455355 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
}
static std::string json_string;
json_string = "\"";
CAPTURE(byte1)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
CAPTURE(byte2)
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
CAPTURE(byte3)
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte4));
}
json_string += "\"";
CAPTURE(json_string)
json _;
if (success_expected)
{
CHECK_NOTHROW(_ = json::parse(json_string));
}
else
{
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
}
}
} // namespace
TEST_CASE("Unicode (2/5)" * doctest::skip())
{
SECTION("RFC 3629")
{
/*
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
follows:
A UTF-8 string is a sequence of octets representing a sequence of UCS
characters. An octet sequence is valid UTF-8 only if it matches the
following syntax, which is derived from the rules for encoding UTF-8
and is expressed in the ABNF of [RFC2234].
UTF8-octets = *( UTF8-char )
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
UTF8-1 = %x00-7F
UTF8-2 = %xC2-DF UTF8-tail
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
%xF4 %x80-8F 2( UTF8-tail )
UTF8-tail = %x80-BF
*/
SECTION("ill-formed first byte")
{
for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("UTF8-1 (x00-x7F)")
{
SECTION("well-formed")
{
for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1)
{
// unescaped control characters are parse errors in JSON
if (0x00 <= byte1 && byte1 <= 0x1F)
{
check_utf8string(false, byte1);
continue;
}
// a single quote is a parse error in JSON
if (byte1 == 0x22)
{
check_utf8string(false, byte1);
continue;
}
// a single backslash is a parse error in JSON
if (byte1 == 0x5C)
{
check_utf8string(false, byte1);
continue;
}
// all other characters are OK
check_utf8string(true, byte1);
check_utf8dump(true, byte1);
}
}
}
SECTION("UTF8-2 (xC2-xDF UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(true, byte1, byte2);
check_utf8dump(true, byte1, byte2);
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 && byte2 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
}
SECTION("UTF8-3 (xE0 xA0-BF UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
check_utf8dump(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0xA0 <= byte2 && byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
}
SECTION("UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
check_utf8dump(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 && byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
}
SECTION("UTF8-3 (xED x80-9F UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
check_utf8dump(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
{
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 && byte2 <= 0x9F)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
}
SECTION("UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
check_utf8dump(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 && byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
}
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,324 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "make_test_data_available.hpp"
// this test suite uses static variables with non-trivial destructors
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
namespace
{
extern size_t calls;
size_t calls = 0;
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
static std::string json_string;
json_string.clear();
CAPTURE(byte1)
CAPTURE(byte2)
CAPTURE(byte3)
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
json_string += std::string(1, static_cast<char>(byte4));
}
CAPTURE(json_string)
// store the string in a JSON value
static json j;
static json j2;
j = json_string;
j2 = "abc" + json_string + "xyz";
static std::string s_ignored;
static std::string s_ignored2;
static std::string s_ignored_ascii;
static std::string s_ignored2_ascii;
static std::string s_replaced;
static std::string s_replaced2;
static std::string s_replaced_ascii;
static std::string s_replaced2_ascii;
// dumping with ignore/replace must not throw in any case
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
if (success_expected)
{
static std::string s_strict;
// strict mode must not throw if success is expected
s_strict = j.dump();
// all dumps should agree on the string
CHECK(s_strict == s_ignored);
CHECK(s_strict == s_replaced);
}
else
{
// strict mode must throw if success is not expected
CHECK_THROWS_AS(j.dump(), json::type_error&);
// ignore and replace must create different dumps
CHECK(s_ignored != s_replaced);
// check that replace string contains a replacement character
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
}
// check that prefix and suffix are preserved
CHECK(s_ignored2.substr(1, 3) == "abc");
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
CHECK(s_replaced2.substr(1, 3) == "abc");
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
}
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
// create and check a JSON string with up to four UTF-8 bytes
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
if (++calls % 100000 == 0)
{
std::cout << calls << " of 1641521 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
}
static std::string json_string;
json_string = "\"";
CAPTURE(byte1)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
CAPTURE(byte2)
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
CAPTURE(byte3)
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte4));
}
json_string += "\"";
CAPTURE(json_string)
json _;
if (success_expected)
{
CHECK_NOTHROW(_ = json::parse(json_string));
}
else
{
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
}
}
} // namespace
TEST_CASE("Unicode (3/5)" * doctest::skip())
{
SECTION("RFC 3629")
{
/*
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
follows:
A UTF-8 string is a sequence of octets representing a sequence of UCS
characters. An octet sequence is valid UTF-8 only if it matches the
following syntax, which is derived from the rules for encoding UTF-8
and is expressed in the ABNF of [RFC2234].
UTF8-octets = *( UTF8-char )
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
UTF8-1 = %x00-7F
UTF8-2 = %xC2-DF UTF8-tail
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
%xF4 %x80-8F 2( UTF8-tail )
UTF8-tail = %x80-BF
*/
SECTION("UTF8-4 (xF0 x90-BF UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(true, byte1, byte2, byte3, byte4);
check_utf8dump(true, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
SECTION("ill-formed: missing fourth byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x90 <= byte2 && byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong fourth byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
{
// skip fourth second byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
}
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,324 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "make_test_data_available.hpp"
// this test suite uses static variables with non-trivial destructors
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
namespace
{
extern size_t calls;
size_t calls = 0;
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
static std::string json_string;
json_string.clear();
CAPTURE(byte1)
CAPTURE(byte2)
CAPTURE(byte3)
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
json_string += std::string(1, static_cast<char>(byte4));
}
CAPTURE(json_string)
// store the string in a JSON value
static json j;
static json j2;
j = json_string;
j2 = "abc" + json_string + "xyz";
static std::string s_ignored;
static std::string s_ignored2;
static std::string s_ignored_ascii;
static std::string s_ignored2_ascii;
static std::string s_replaced;
static std::string s_replaced2;
static std::string s_replaced_ascii;
static std::string s_replaced2_ascii;
// dumping with ignore/replace must not throw in any case
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
if (success_expected)
{
static std::string s_strict;
// strict mode must not throw if success is expected
s_strict = j.dump();
// all dumps should agree on the string
CHECK(s_strict == s_ignored);
CHECK(s_strict == s_replaced);
}
else
{
// strict mode must throw if success is not expected
CHECK_THROWS_AS(j.dump(), json::type_error&);
// ignore and replace must create different dumps
CHECK(s_ignored != s_replaced);
// check that replace string contains a replacement character
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
}
// check that prefix and suffix are preserved
CHECK(s_ignored2.substr(1, 3) == "abc");
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
CHECK(s_replaced2.substr(1, 3) == "abc");
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
}
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
// create and check a JSON string with up to four UTF-8 bytes
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
if (++calls % 100000 == 0)
{
std::cout << calls << " of 5517507 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
}
static std::string json_string;
json_string = "\"";
CAPTURE(byte1)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
CAPTURE(byte2)
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
CAPTURE(byte3)
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte4));
}
json_string += "\"";
CAPTURE(json_string)
json _;
if (success_expected)
{
CHECK_NOTHROW(_ = json::parse(json_string));
}
else
{
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
}
}
} // namespace
TEST_CASE("Unicode (4/5)" * doctest::skip())
{
SECTION("RFC 3629")
{
/*
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
follows:
A UTF-8 string is a sequence of octets representing a sequence of UCS
characters. An octet sequence is valid UTF-8 only if it matches the
following syntax, which is derived from the rules for encoding UTF-8
and is expressed in the ABNF of [RFC2234].
UTF8-octets = *( UTF8-char )
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
UTF8-1 = %x00-7F
UTF8-2 = %xC2-DF UTF8-tail
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
%xF4 %x80-8F 2( UTF8-tail )
UTF8-tail = %x80-BF
*/
SECTION("UTF8-4 (xF1-F3 UTF8-tail UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(true, byte1, byte2, byte3, byte4);
check_utf8dump(true, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
SECTION("ill-formed: missing fourth byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 && byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong fourth byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
{
// skip correct fourth byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
}
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,324 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "make_test_data_available.hpp"
// this test suite uses static variables with non-trivial destructors
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
namespace
{
extern size_t calls;
size_t calls = 0;
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
static std::string json_string;
json_string.clear();
CAPTURE(byte1)
CAPTURE(byte2)
CAPTURE(byte3)
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
json_string += std::string(1, static_cast<char>(byte4));
}
CAPTURE(json_string)
// store the string in a JSON value
static json j;
static json j2;
j = json_string;
j2 = "abc" + json_string + "xyz";
static std::string s_ignored;
static std::string s_ignored2;
static std::string s_ignored_ascii;
static std::string s_ignored2_ascii;
static std::string s_replaced;
static std::string s_replaced2;
static std::string s_replaced_ascii;
static std::string s_replaced2_ascii;
// dumping with ignore/replace must not throw in any case
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
if (success_expected)
{
static std::string s_strict;
// strict mode must not throw if success is expected
s_strict = j.dump();
// all dumps should agree on the string
CHECK(s_strict == s_ignored);
CHECK(s_strict == s_replaced);
}
else
{
// strict mode must throw if success is not expected
CHECK_THROWS_AS(j.dump(), json::type_error&);
// ignore and replace must create different dumps
CHECK(s_ignored != s_replaced);
// check that replace string contains a replacement character
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
}
// check that prefix and suffix are preserved
CHECK(s_ignored2.substr(1, 3) == "abc");
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
CHECK(s_replaced2.substr(1, 3) == "abc");
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
}
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
// create and check a JSON string with up to four UTF-8 bytes
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
if (++calls % 100000 == 0)
{
std::cout << calls << " of 1246225 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
}
static std::string json_string;
json_string = "\"";
CAPTURE(byte1)
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
CAPTURE(byte2)
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
CAPTURE(byte3)
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
CAPTURE(byte4)
json_string += std::string(1, static_cast<char>(byte4));
}
json_string += "\"";
CAPTURE(json_string)
json _;
if (success_expected)
{
CHECK_NOTHROW(_ = json::parse(json_string));
}
else
{
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
}
}
} // namespace
TEST_CASE("Unicode (5/5)" * doctest::skip())
{
SECTION("RFC 3629")
{
/*
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
follows:
A UTF-8 string is a sequence of octets representing a sequence of UCS
characters. An octet sequence is valid UTF-8 only if it matches the
following syntax, which is derived from the rules for encoding UTF-8
and is expressed in the ABNF of [RFC2234].
UTF8-octets = *( UTF8-char )
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
UTF8-1 = %x00-7F
UTF8-2 = %xC2-DF UTF8-tail
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
%xF4 %x80-8F 2( UTF8-tail )
UTF8-tail = %x80-BF
*/
SECTION("UTF8-4 (xF4 x80-8F UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(true, byte1, byte2, byte3, byte4);
check_utf8dump(true, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
check_utf8string(false, byte1);
check_utf8dump(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
check_utf8string(false, byte1, byte2);
check_utf8dump(false, byte1, byte2);
}
}
}
SECTION("ill-formed: missing fourth byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
check_utf8dump(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 && byte2 <= 0x8F)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong fourth byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
{
// skip correct fourth byte
if (0x80 <= byte3 && byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3, byte4);
check_utf8dump(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
}
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -0,0 +1,130 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <list>
namespace
{
TEST_CASE("Use arbitrary stdlib container")
{
std::string raw_data = "[1,2,3,4]";
std::list<char> data(raw_data.begin(), raw_data.end());
json as_json = json::parse(data.begin(), data.end());
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
struct MyContainer
{
const char* data;
};
const char* begin(const MyContainer& c)
{
return c.data;
}
const char* end(const MyContainer& c)
{
return c.data + strlen(c.data); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
TEST_CASE("Custom container non-member begin/end")
{
const MyContainer data{"[1,2,3,4]"};
json as_json = json::parse(data);
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
TEST_CASE("Custom container member begin/end")
{
struct MyContainer2
{
const char* data;
const char* begin() const
{
return data;
}
const char* end() const
{
return data + strlen(data); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
};
const MyContainer2 data{"[1,2,3,4]"};
json as_json = json::parse(data);
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
TEST_CASE("Custom iterator")
{
const char* raw_data = "[1,2,3,4]";
struct MyIterator
{
using difference_type = std::size_t;
using value_type = char;
using pointer = const char*;
using reference = const char&;
using iterator_category = std::input_iterator_tag;
MyIterator& operator++()
{
++ptr; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
return *this;
}
reference operator*() const
{
return *ptr;
}
bool operator!=(const MyIterator& rhs) const
{
return ptr != rhs.ptr;
}
const char* ptr;
};
// avoid -Wunused-local-typedefs
CHECK(std::is_same<MyIterator::difference_type, std::size_t>::value);
CHECK(std::is_same<MyIterator::value_type, char>::value);
CHECK(std::is_same<MyIterator::pointer, const char*>::value);
CHECK(std::is_same<MyIterator::reference, const char&>::value);
CHECK(std::is_same<MyIterator::iterator_category, std::input_iterator_tag>::value);
const MyIterator begin{raw_data};
const MyIterator end{raw_data + strlen(raw_data)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
json as_json = json::parse(begin, end);
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
} // namespace

View File

@ -0,0 +1,23 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
#ifdef _WIN32
#include <windows.h>
#endif
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("include windows.h")
{
CHECK(true);
}

View File

@ -0,0 +1,99 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
// ICPC errors out on multibyte character sequences in source files
#ifndef __INTEL_COMPILER
namespace
{
bool wstring_is_utf16();
bool wstring_is_utf16()
{
return (std::wstring(L"💩") == std::wstring(L"\U0001F4A9"));
}
bool u16string_is_utf16();
bool u16string_is_utf16()
{
return (std::u16string(u"💩") == std::u16string(u"\U0001F4A9"));
}
bool u32string_is_utf32();
bool u32string_is_utf32()
{
return (std::u32string(U"💩") == std::u32string(U"\U0001F4A9"));
}
} // namespace
TEST_CASE("wide strings")
{
SECTION("std::wstring")
{
if (wstring_is_utf16())
{
std::wstring const w = L"[12.2,\"Ⴥaäö💤🧢\"]";
json const j = json::parse(w);
CHECK(j.dump() == "[12.2,\"Ⴥaäö💤🧢\"]");
}
}
SECTION("invalid std::wstring")
{
if (wstring_is_utf16())
{
std::wstring const w = L"\"\xDBFF";
json _;
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
}
}
SECTION("std::u16string")
{
if (u16string_is_utf16())
{
std::u16string const w = u"[12.2,\"Ⴥaäö💤🧢\"]";
json const j = json::parse(w);
CHECK(j.dump() == "[12.2,\"Ⴥaäö💤🧢\"]");
}
}
SECTION("invalid std::u16string")
{
if (wstring_is_utf16())
{
std::u16string const w = u"\"\xDBFF";
json _;
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
}
}
SECTION("std::u32string")
{
if (u32string_is_utf32())
{
std::u32string const w = U"[12.2,\"Ⴥaäö💤🧢\"]";
json const j = json::parse(w);
CHECK(j.dump() == "[12.2,\"Ⴥaäö💤🧢\"]");
}
}
SECTION("invalid std::u32string")
{
if (u32string_is_utf32())
{
std::u32string const w = U"\"\x110000";
json _;
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
}
}
}
#endif

View File

@ -0,0 +1,10 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest_compatibility.h"