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
|
||||
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();
|
||||
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;
|
||||
constexpr std::size_t limit = 4;
|
||||
const std::size_t n = (std::min)((std::min)(a.size(), b.size()), limit);
|
||||
string_view_t ka[limit];
|
||||
string_view_t kb[limit];
|
||||
a.first_keys(ka, n);
|
||||
b.first_keys(kb, n);
|
||||
std::array<string_view_t, limit> ka{};
|
||||
std::array<string_view_t, limit> kb{};
|
||||
a.first_keys(ka.data(), n);
|
||||
b.first_keys(kb.data(), n);
|
||||
for (std::size_t i = 0; i < n; ++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;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// limited by memory only
|
||||
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;
|
||||
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
||||
|
||||
constexpr std::size_t npos = static_cast<std::size_t>(-1);
|
||||
struct frame
|
||||
{
|
||||
A array_a{};
|
||||
@@ -851,8 +853,8 @@ bool equal(const A& a0, const B& b0)
|
||||
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_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_b = npos;
|
||||
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 = document_order;
|
||||
std::size_t index_mark = 0; ///< the size of indices before this frame
|
||||
bool object = false;
|
||||
std::size_t next = 0;
|
||||
@@ -983,9 +985,20 @@ bool equal(const A& a0, const B& b0)
|
||||
else if (numbers && ta == tb)
|
||||
{
|
||||
// numbers of one type: as basic_json compares them
|
||||
if (ta == value_t::number_integer ? a.integer() != b.integer()
|
||||
: ta == value_t::number_unsigned ? a.unsigned_integer() != b.unsigned_integer()
|
||||
: !floats_equal(a, b))
|
||||
bool differ = false;
|
||||
if (ta == value_t::number_integer)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -1018,8 +1031,8 @@ bool equal(const A& a0, const B& b0)
|
||||
}
|
||||
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_b = members_b[f.base_b + (f.index_b == npos ? f.next : indices[f.index_b + 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 == document_order ? f.next : indices[f.index_b + f.next])];
|
||||
if (!(member_a.first == member_b.first))
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||
const std::uint64_t x = read_eight_bytes(q) ^ (ones * 0x30u);
|
||||
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
|
||||
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();
|
||||
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;
|
||||
constexpr std::size_t limit = 4;
|
||||
const std::size_t n = (std::min)((std::min)(a.size(), b.size()), limit);
|
||||
string_view_t ka[limit];
|
||||
string_view_t kb[limit];
|
||||
a.first_keys(ka, n);
|
||||
b.first_keys(kb, n);
|
||||
std::array<string_view_t, limit> ka{};
|
||||
std::array<string_view_t, limit> kb{};
|
||||
a.first_keys(ka.data(), n);
|
||||
b.first_keys(kb.data(), n);
|
||||
for (std::size_t i = 0; i < n; ++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;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// limited by memory only
|
||||
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;
|
||||
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
||||
|
||||
constexpr std::size_t npos = static_cast<std::size_t>(-1);
|
||||
struct frame
|
||||
{
|
||||
A array_a{};
|
||||
@@ -3539,8 +3541,8 @@ bool equal(const A& a0, const B& b0)
|
||||
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_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_b = npos;
|
||||
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 = document_order;
|
||||
std::size_t index_mark = 0; ///< the size of indices before this frame
|
||||
bool object = false;
|
||||
std::size_t next = 0;
|
||||
@@ -3671,9 +3673,20 @@ bool equal(const A& a0, const B& b0)
|
||||
else if (numbers && ta == tb)
|
||||
{
|
||||
// numbers of one type: as basic_json compares them
|
||||
if (ta == value_t::number_integer ? a.integer() != b.integer()
|
||||
: ta == value_t::number_unsigned ? a.unsigned_integer() != b.unsigned_integer()
|
||||
: !floats_equal(a, b))
|
||||
bool differ = false;
|
||||
if (ta == value_t::number_integer)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -3706,8 +3719,8 @@ bool equal(const A& a0, const B& b0)
|
||||
}
|
||||
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_b = members_b[f.base_b + (f.index_b == npos ? f.next : indices[f.index_b + 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 == document_order ? f.next : indices[f.index_b + f.next])];
|
||||
if (!(member_a.first == member_b.first))
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||
const std::uint64_t x = read_eight_bytes(q) ^ (ones * 0x30u);
|
||||
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")
|
||||
{
|
||||
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,"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}])"
|
||||
{
|
||||
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,"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)
|
||||
{
|
||||
@@ -426,12 +428,19 @@ TEST_CASE("json_view comparison")
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
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
|
||||
const std::string text = head + "7" + tail;
|
||||
const std::string changed = head + "8" + tail;
|
||||
const std::string spelled = head + "7.0" + tail;
|
||||
const auto with_number = [&](const char* number)
|
||||
{
|
||||
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 b = json_document::parse(text);
|
||||
const json_document c = json_document::parse(changed);
|
||||
@@ -500,7 +509,22 @@ TEST_CASE("json_view comparison")
|
||||
{
|
||||
text += (i != 0 ? "," : "");
|
||||
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}})";
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user