mirror of
https://github.com/nlohmann/json.git
synced 2026-10-23 06:18:56 +00:00
Fix the compiler and lint failures of the json_view comparison
- all_digits(): the lambda declares its constant itself; MSVC does not let a lambda without captures read a constexpr local of the function - equal(): the frame's "in document order" index is a namespace-scope constant (document_order); the local constexpr npos in the default member initializers of the local struct crashed GCC 9 and MinGW GCC and was not found by MSVC - keys_differ_early() keeps the first keys in std::arrays (cpplint: variable-length arrays; clang-tidy: C arrays), and the basic_json side's first_keys() is not noexcept (key() may throw) - the numbers of one type are compared in an if chain instead of a nested conditional operator - the dump test avoids C arrays and temporary string concatenations View comparison instructions are unchanged (Apple clang, twitter, citm, canada: -0.1 % to +0.1 %). Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
4 files changed
+88
-38
No files matched your search
@@ -479,7 +479,7 @@ class json_side
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// the keys of the first n members of this object (n <= its size), in document order
|
/// the keys of the first n members of this object (n <= its size), in document order
|
||||||
void first_keys(string_view_t* out, std::size_t n) const noexcept
|
void first_keys(string_view_t* out, std::size_t n) const
|
||||||
{
|
{
|
||||||
auto it = m_json->cbegin();
|
auto it = m_json->cbegin();
|
||||||
for (std::size_t i = 0; i < n; ++i, ++it)
|
for (std::size_t i = 0; i < n; ++i, ++it)
|
||||||
@@ -808,10 +808,10 @@ bool keys_differ_early(const SideA& a, const SideB& b)
|
|||||||
using string_view_t = typename SideA::string_view_t;
|
using string_view_t = typename SideA::string_view_t;
|
||||||
constexpr std::size_t limit = 4;
|
constexpr std::size_t limit = 4;
|
||||||
const std::size_t n = (std::min)((std::min)(a.size(), b.size()), limit);
|
const std::size_t n = (std::min)((std::min)(a.size(), b.size()), limit);
|
||||||
string_view_t ka[limit];
|
std::array<string_view_t, limit> ka{};
|
||||||
string_view_t kb[limit];
|
std::array<string_view_t, limit> kb{};
|
||||||
a.first_keys(ka, n);
|
a.first_keys(ka.data(), n);
|
||||||
b.first_keys(kb, n);
|
b.first_keys(kb.data(), n);
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
for (std::size_t i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
if (ka[i] != kb[i])
|
if (ka[i] != kb[i])
|
||||||
@@ -834,6 +834,9 @@ inline bool is_number(value_t t) noexcept
|
|||||||
return t == value_t::number_integer || t == value_t::number_unsigned || t == value_t::number_float;
|
return t == value_t::number_integer || t == value_t::number_unsigned || t == value_t::number_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the index_a/index_b of a frame of equal() whose members are compared in document order
|
||||||
|
constexpr std::size_t document_order = static_cast<std::size_t>(-1);
|
||||||
|
|
||||||
/// whether two sides are equal; iterative, so that the nesting depth is
|
/// whether two sides are equal; iterative, so that the nesting depth is
|
||||||
/// limited by memory only
|
/// limited by memory only
|
||||||
template<typename BasicJsonType, typename A, typename B>
|
template<typename BasicJsonType, typename A, typename B>
|
||||||
@@ -842,7 +845,6 @@ bool equal(const A& a0, const B& b0)
|
|||||||
using string_view_t = typename A::string_view_t;
|
using string_view_t = typename A::string_view_t;
|
||||||
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
||||||
|
|
||||||
constexpr std::size_t npos = static_cast<std::size_t>(-1);
|
|
||||||
struct frame
|
struct frame
|
||||||
{
|
{
|
||||||
A array_a{};
|
A array_a{};
|
||||||
@@ -851,8 +853,8 @@ bool equal(const A& a0, const B& b0)
|
|||||||
typename B::cursor cursor_b{};
|
typename B::cursor cursor_b{};
|
||||||
std::size_t base_a = 0; ///< the members of an object: from this position in members_a
|
std::size_t base_a = 0; ///< the members of an object: from this position in members_a
|
||||||
std::size_t base_b = 0;
|
std::size_t base_b = 0;
|
||||||
std::size_t index_a = npos; ///< their order in the comparison: from this position in indices (npos: as they are)
|
std::size_t index_a = document_order; ///< their order in the comparison: from this position in indices (document_order: as they are)
|
||||||
std::size_t index_b = npos;
|
std::size_t index_b = document_order;
|
||||||
std::size_t index_mark = 0; ///< the size of indices before this frame
|
std::size_t index_mark = 0; ///< the size of indices before this frame
|
||||||
bool object = false;
|
bool object = false;
|
||||||
std::size_t next = 0;
|
std::size_t next = 0;
|
||||||
@@ -983,9 +985,20 @@ bool equal(const A& a0, const B& b0)
|
|||||||
else if (numbers && ta == tb)
|
else if (numbers && ta == tb)
|
||||||
{
|
{
|
||||||
// numbers of one type: as basic_json compares them
|
// numbers of one type: as basic_json compares them
|
||||||
if (ta == value_t::number_integer ? a.integer() != b.integer()
|
bool differ = false;
|
||||||
: ta == value_t::number_unsigned ? a.unsigned_integer() != b.unsigned_integer()
|
if (ta == value_t::number_integer)
|
||||||
: !floats_equal(a, b))
|
{
|
||||||
|
differ = a.integer() != b.integer();
|
||||||
|
}
|
||||||
|
else if (ta == value_t::number_unsigned)
|
||||||
|
{
|
||||||
|
differ = a.unsigned_integer() != b.unsigned_integer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
differ = !floats_equal(a, b);
|
||||||
|
}
|
||||||
|
if (differ)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1018,8 +1031,8 @@ bool equal(const A& a0, const B& b0)
|
|||||||
}
|
}
|
||||||
if (f.object)
|
if (f.object)
|
||||||
{
|
{
|
||||||
const auto& member_a = members_a[f.base_a + (f.index_a == npos ? f.next : indices[f.index_a + f.next])];
|
const auto& member_a = members_a[f.base_a + (f.index_a == document_order ? f.next : indices[f.index_a + f.next])];
|
||||||
const auto& member_b = members_b[f.base_b + (f.index_b == npos ? f.next : indices[f.index_b + f.next])];
|
const auto& member_b = members_b[f.base_b + (f.index_b == document_order ? f.next : indices[f.index_b + f.next])];
|
||||||
if (!(member_a.first == member_b.first))
|
if (!(member_a.first == member_b.first))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -282,9 +282,9 @@ inline const unsigned char* digit_run(const unsigned char* p, const unsigned cha
|
|||||||
/// whether the n bytes at p are ASCII digits (n > 0)
|
/// whether the n bytes at p are ASCII digits (n > 0)
|
||||||
inline bool all_digits(const unsigned char* p, std::size_t n) noexcept
|
inline bool all_digits(const unsigned char* p, std::size_t n) noexcept
|
||||||
{
|
{
|
||||||
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
|
||||||
const auto digits = [](const unsigned char* q)
|
const auto digits = [](const unsigned char* q)
|
||||||
{
|
{
|
||||||
|
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||||
const std::uint64_t x = read_eight_bytes(q) ^ (ones * 0x30u);
|
const std::uint64_t x = read_eight_bytes(q) ^ (ones * 0x30u);
|
||||||
return (((x + (ones * 0x76u)) | x) & (ones * 0x80u)) == 0;
|
return (((x + (ones * 0x76u)) | x) & (ones * 0x80u)) == 0;
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+27
-14
@@ -3167,7 +3167,7 @@ class json_side
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// the keys of the first n members of this object (n <= its size), in document order
|
/// the keys of the first n members of this object (n <= its size), in document order
|
||||||
void first_keys(string_view_t* out, std::size_t n) const noexcept
|
void first_keys(string_view_t* out, std::size_t n) const
|
||||||
{
|
{
|
||||||
auto it = m_json->cbegin();
|
auto it = m_json->cbegin();
|
||||||
for (std::size_t i = 0; i < n; ++i, ++it)
|
for (std::size_t i = 0; i < n; ++i, ++it)
|
||||||
@@ -3496,10 +3496,10 @@ bool keys_differ_early(const SideA& a, const SideB& b)
|
|||||||
using string_view_t = typename SideA::string_view_t;
|
using string_view_t = typename SideA::string_view_t;
|
||||||
constexpr std::size_t limit = 4;
|
constexpr std::size_t limit = 4;
|
||||||
const std::size_t n = (std::min)((std::min)(a.size(), b.size()), limit);
|
const std::size_t n = (std::min)((std::min)(a.size(), b.size()), limit);
|
||||||
string_view_t ka[limit];
|
std::array<string_view_t, limit> ka{};
|
||||||
string_view_t kb[limit];
|
std::array<string_view_t, limit> kb{};
|
||||||
a.first_keys(ka, n);
|
a.first_keys(ka.data(), n);
|
||||||
b.first_keys(kb, n);
|
b.first_keys(kb.data(), n);
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
for (std::size_t i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
if (ka[i] != kb[i])
|
if (ka[i] != kb[i])
|
||||||
@@ -3522,6 +3522,9 @@ inline bool is_number(value_t t) noexcept
|
|||||||
return t == value_t::number_integer || t == value_t::number_unsigned || t == value_t::number_float;
|
return t == value_t::number_integer || t == value_t::number_unsigned || t == value_t::number_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the index_a/index_b of a frame of equal() whose members are compared in document order
|
||||||
|
constexpr std::size_t document_order = static_cast<std::size_t>(-1);
|
||||||
|
|
||||||
/// whether two sides are equal; iterative, so that the nesting depth is
|
/// whether two sides are equal; iterative, so that the nesting depth is
|
||||||
/// limited by memory only
|
/// limited by memory only
|
||||||
template<typename BasicJsonType, typename A, typename B>
|
template<typename BasicJsonType, typename A, typename B>
|
||||||
@@ -3530,7 +3533,6 @@ bool equal(const A& a0, const B& b0)
|
|||||||
using string_view_t = typename A::string_view_t;
|
using string_view_t = typename A::string_view_t;
|
||||||
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
||||||
|
|
||||||
constexpr std::size_t npos = static_cast<std::size_t>(-1);
|
|
||||||
struct frame
|
struct frame
|
||||||
{
|
{
|
||||||
A array_a{};
|
A array_a{};
|
||||||
@@ -3539,8 +3541,8 @@ bool equal(const A& a0, const B& b0)
|
|||||||
typename B::cursor cursor_b{};
|
typename B::cursor cursor_b{};
|
||||||
std::size_t base_a = 0; ///< the members of an object: from this position in members_a
|
std::size_t base_a = 0; ///< the members of an object: from this position in members_a
|
||||||
std::size_t base_b = 0;
|
std::size_t base_b = 0;
|
||||||
std::size_t index_a = npos; ///< their order in the comparison: from this position in indices (npos: as they are)
|
std::size_t index_a = document_order; ///< their order in the comparison: from this position in indices (document_order: as they are)
|
||||||
std::size_t index_b = npos;
|
std::size_t index_b = document_order;
|
||||||
std::size_t index_mark = 0; ///< the size of indices before this frame
|
std::size_t index_mark = 0; ///< the size of indices before this frame
|
||||||
bool object = false;
|
bool object = false;
|
||||||
std::size_t next = 0;
|
std::size_t next = 0;
|
||||||
@@ -3671,9 +3673,20 @@ bool equal(const A& a0, const B& b0)
|
|||||||
else if (numbers && ta == tb)
|
else if (numbers && ta == tb)
|
||||||
{
|
{
|
||||||
// numbers of one type: as basic_json compares them
|
// numbers of one type: as basic_json compares them
|
||||||
if (ta == value_t::number_integer ? a.integer() != b.integer()
|
bool differ = false;
|
||||||
: ta == value_t::number_unsigned ? a.unsigned_integer() != b.unsigned_integer()
|
if (ta == value_t::number_integer)
|
||||||
: !floats_equal(a, b))
|
{
|
||||||
|
differ = a.integer() != b.integer();
|
||||||
|
}
|
||||||
|
else if (ta == value_t::number_unsigned)
|
||||||
|
{
|
||||||
|
differ = a.unsigned_integer() != b.unsigned_integer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
differ = !floats_equal(a, b);
|
||||||
|
}
|
||||||
|
if (differ)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -3706,8 +3719,8 @@ bool equal(const A& a0, const B& b0)
|
|||||||
}
|
}
|
||||||
if (f.object)
|
if (f.object)
|
||||||
{
|
{
|
||||||
const auto& member_a = members_a[f.base_a + (f.index_a == npos ? f.next : indices[f.index_a + f.next])];
|
const auto& member_a = members_a[f.base_a + (f.index_a == document_order ? f.next : indices[f.index_a + f.next])];
|
||||||
const auto& member_b = members_b[f.base_b + (f.index_b == npos ? f.next : indices[f.index_b + f.next])];
|
const auto& member_b = members_b[f.base_b + (f.index_b == document_order ? f.next : indices[f.index_b + f.next])];
|
||||||
if (!(member_a.first == member_b.first))
|
if (!(member_a.first == member_b.first))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -5739,9 +5752,9 @@ inline const unsigned char* digit_run(const unsigned char* p, const unsigned cha
|
|||||||
/// whether the n bytes at p are ASCII digits (n > 0)
|
/// whether the n bytes at p are ASCII digits (n > 0)
|
||||||
inline bool all_digits(const unsigned char* p, std::size_t n) noexcept
|
inline bool all_digits(const unsigned char* p, std::size_t n) noexcept
|
||||||
{
|
{
|
||||||
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
|
||||||
const auto digits = [](const unsigned char* q)
|
const auto digits = [](const unsigned char* q)
|
||||||
{
|
{
|
||||||
|
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||||
const std::uint64_t x = read_eight_bytes(q) ^ (ones * 0x30u);
|
const std::uint64_t x = read_eight_bytes(q) ^ (ones * 0x30u);
|
||||||
return (((x + (ones * 0x76u)) | x) & (ones * 0x80u)) == 0;
|
return (((x + (ones * 0x76u)) | x) & (ones * 0x80u)) == 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -347,12 +347,14 @@ TEST_CASE("json_view comparison")
|
|||||||
|
|
||||||
SECTION("objects whose first keys differ or repeat")
|
SECTION("objects whose first keys differ or repeat")
|
||||||
{
|
{
|
||||||
const char* const texts[] =
|
const std::array<const char*, 17> texts =
|
||||||
{
|
{
|
||||||
R"({"a":1,"b":2})", R"({"a":1,"c":2})", R"({"b":1,"a":2})", R"({"a":1,"a":2,"b":3})", R"({"a":2,"b":3})",
|
{
|
||||||
R"({"a":1,"b":1,"a":2})", R"({"a":2,"b":1})", R"({"a":1,"a":2})", R"({"b":1})", R"({"a":1,"b":2,"c":3,"d":4,"e":5})",
|
R"({"a":1,"b":2})", R"({"a":1,"c":2})", R"({"b":1,"a":2})", R"({"a":1,"a":2,"b":3})", R"({"a":2,"b":3})",
|
||||||
R"({"a":1,"b":2,"c":3,"d":4,"f":5})", R"({"a":1,"b":2,"c":3,"a":4,"e":5})", R"({"a":4,"b":2,"c":3,"e":5})",
|
R"({"a":1,"b":1,"a":2})", R"({"a":2,"b":1})", R"({"a":1,"a":2})", R"({"b":1})", R"({"a":1,"b":2,"c":3,"d":4,"e":5})",
|
||||||
R"({"\u0061":1,"b":2})", R"({})", R"([{"a":1,"b":2}])", R"([{"a":1,"c":2}])"
|
R"({"a":1,"b":2,"c":3,"d":4,"f":5})", R"({"a":1,"b":2,"c":3,"a":4,"e":5})", R"({"a":4,"b":2,"c":3,"e":5})",
|
||||||
|
R"({"\u0061":1,"b":2})", R"({})", R"([{"a":1,"b":2}])", R"([{"a":1,"c":2}])"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
for (const char* const x : texts)
|
for (const char* const x : texts)
|
||||||
{
|
{
|
||||||
@@ -426,12 +428,19 @@ TEST_CASE("json_view comparison")
|
|||||||
for (int i = 0; i < depth; ++i)
|
for (int i = 0; i < depth; ++i)
|
||||||
{
|
{
|
||||||
head += i % 2 == 0 ? R"({"k":[1,"s",{"x":true},)" : R"([null,2.5,{"y":)";
|
head += i % 2 == 0 ? R"({"k":[1,"s",{"x":true},)" : R"([null,2.5,{"y":)";
|
||||||
tail = (i % 2 == 0 ? "]}" : "}]") + tail;
|
tail.insert(0, i % 2 == 0 ? "]}" : "}]");
|
||||||
}
|
}
|
||||||
// the same text, one number changed, and one number written differently
|
// the same text, one number changed, and one number written differently
|
||||||
const std::string text = head + "7" + tail;
|
const auto with_number = [&](const char* number)
|
||||||
const std::string changed = head + "8" + tail;
|
{
|
||||||
const std::string spelled = head + "7.0" + tail;
|
std::string result = head;
|
||||||
|
result += number;
|
||||||
|
result += tail;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const std::string text = with_number("7");
|
||||||
|
const std::string changed = with_number("8");
|
||||||
|
const std::string spelled = with_number("7.0");
|
||||||
const json_document a = json_document::parse(text);
|
const json_document a = json_document::parse(text);
|
||||||
const json_document b = json_document::parse(text);
|
const json_document b = json_document::parse(text);
|
||||||
const json_document c = json_document::parse(changed);
|
const json_document c = json_document::parse(changed);
|
||||||
@@ -500,7 +509,22 @@ TEST_CASE("json_view comparison")
|
|||||||
{
|
{
|
||||||
text += (i != 0 ? "," : "");
|
text += (i != 0 ? "," : "");
|
||||||
const std::string id = std::to_string(i == changed ? i + 1 : i);
|
const std::string id = std::to_string(i == changed ? i + 1 : i);
|
||||||
text += swap_members && i % 3 == 0 ? R"({"v":[1.5,"s",{"k":)" + id + R"(}],"id":)" + id + "}" : R"({"id":)" + id + R"(,"v":[1.5,"s",{"k":)" + id + "}]}";
|
if (swap_members && i % 3 == 0)
|
||||||
|
{
|
||||||
|
text += R"({"v":[1.5,"s",{"k":)";
|
||||||
|
text += id;
|
||||||
|
text += R"(}],"id":)";
|
||||||
|
text += id;
|
||||||
|
text += "}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text += R"({"id":)";
|
||||||
|
text += id;
|
||||||
|
text += R"(,"v":[1.5,"s",{"k":)";
|
||||||
|
text += id;
|
||||||
|
text += "}]}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return text + R"(],"a":{"y":2,"x":3}})";
|
return text + R"(],"a":{"y":2,"x":3}})";
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in new issue
Block a user