mirror of
https://github.com/nlohmann/json.git
synced 2026-11-12 07:00:46 +00:00
Do not compare an edited NaN equal to itself in json_view (#6085)
Two views compared equal when the tokens of two floats were equal, and the same node was equal to itself. An edit writes a NaN as the token "nan", so a view of it (or of an array holding it) was equal to itself and to another NaN, while json(NaN) == json(NaN) and view == json are false. The token shortcut now excludes the edited "nan" token (found by the missing digit layout), dispatched on the document mode by a tag: a read-only document cannot hold a NaN, and its walk is unchanged (instruction count and cycles on canada, citm and twitter within noise). For editable documents the check costs about 2 % instructions on canada (cycles within noise). The same-node shortcut is kept for read-only documents and for editable ones that were never edited; a document with edits is walked, so comparing it with itself is linear instead of constant. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
1 parent
9d35fe6d21
commit
02d57db31e
4 files changed
+87
-9
No files matched your search
Generated
+23
-4
@@ -3262,6 +3262,14 @@ NLOHMANN_VIEW_ALWAYS_INLINE bool bytes_equal(const char* x, const char* y, std::
|
||||
return true;
|
||||
}
|
||||
|
||||
/// whether a float node holds the text nan, which an edit writes for a NaN (and records without a digit layout, so
|
||||
/// that a parsed float is told apart by one comparison of what the walk has loaded anyway): equal tokens are an
|
||||
/// equal value, except for this one
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE bool float_token_is_nan(const node& n, const char* s) noexcept
|
||||
{
|
||||
return NLOHMANN_VIEW_UNLIKELY(n.extra == 0xFFFFu) && n.len == 3 && s[0] == 'n';
|
||||
}
|
||||
|
||||
/// whether the n1 bytes at x are before the n2 bytes at y (memcmp() order, then the shorter first);
|
||||
/// the first difference is found eight bytes at a time
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE bool bytes_less(const char* x, std::size_t n1, const char* y, std::size_t n2) noexcept
|
||||
@@ -3871,7 +3879,7 @@ class lockstep
|
||||
case value_t::string:
|
||||
return a->len == b->len && bytes_equal(m_a.str(*a), m_b.str(*b), a->len);
|
||||
case value_t::number_float: // (the token, and the digit layout that the value is read with: they only differ in a damaged image)
|
||||
return a->extra == b->extra && a->len == b->len && bytes_equal(m_a.str(*a), m_b.str(*b), a->len);
|
||||
return same_float(a, b, std::integral_constant<bool, EA> {});
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
return integer_bits(*a) == integer_bits(*b);
|
||||
@@ -3966,6 +3974,17 @@ class lockstep
|
||||
return true;
|
||||
}
|
||||
|
||||
/// (a read-only document holds no NaN)
|
||||
bool same_float(const node* a, const node* b, std::false_type /*editable*/) const noexcept
|
||||
{
|
||||
return a->extra == b->extra && a->len == b->len && bytes_equal(m_a.str(*a), m_b.str(*b), a->len);
|
||||
}
|
||||
|
||||
bool same_float(const node* a, const node* b, std::true_type /*editable*/) const noexcept
|
||||
{
|
||||
return a->extra == b->extra && a->len == b->len && bytes_equal(m_a.str(*a), m_b.str(*b), a->len) && !float_token_is_nan(*a, m_a.str(*a));
|
||||
}
|
||||
|
||||
bool elements(const node* a, const node* b, unsigned budget, lockstep_path::entry& stop) noexcept
|
||||
{
|
||||
const node* pa = nav_a::first(m_a, a);
|
||||
@@ -4022,9 +4041,9 @@ bool certainly_equal(const view_side<BasicJsonType, ViewA>& a, const view_side<B
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (na == nb)
|
||||
if (na == nb && (!view_side<BasicJsonType, ViewA>::nav::editable() || a.document().edits == nullptr))
|
||||
{
|
||||
return true; // (the same node of the same document: documents have distinct nodes)
|
||||
return true; // (the same node of the same document: documents have distinct nodes; a document with edits is walked, as an edit can have written a NaN, which is not equal to itself)
|
||||
}
|
||||
path.resume = nullptr;
|
||||
if (path.next < path.length && path.nodes[path.next].a == na && path.nodes[path.next].b == nb)
|
||||
@@ -4069,7 +4088,7 @@ void resume_array(const lockstep_path& /*path*/, const A& /*a*/, const B& /*b*/,
|
||||
template<typename BasicJsonType, typename ViewA, typename ViewB>
|
||||
bool floats_equal(const view_side<BasicJsonType, ViewA>& a, const view_side<BasicJsonType, ViewB>& b)
|
||||
{
|
||||
return (a.value_node()->extra == b.value_node()->extra && a.float_token() == b.float_token()) || std::equal_to<typename BasicJsonType::number_float_t>()(a.floating(), b.floating());
|
||||
return (a.value_node()->extra == b.value_node()->extra && a.float_token() == b.float_token() && !(view_side<BasicJsonType, ViewA>::nav::editable() && float_token_is_nan(*a.value_node(), a.float_token().data()))) || std::equal_to<typename BasicJsonType::number_float_t>()(a.floating(), b.floating());
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
|
||||
Reference in new issue
Block a user