From 217e3c509d3da05180a558f78bcfa801a0aefdce Mon Sep 17 00:00:00 2001 From: Captain Yoshi Date: Wed, 13 Apr 2022 11:35:56 -0400 Subject: [PATCH 1/2] [impl] add subset string view comparator --- src/c4/yml/tree.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++ src/c4/yml/tree.hpp | 13 ++++ 2 files changed, 168 insertions(+) diff --git a/src/c4/yml/tree.cpp b/src/c4/yml/tree.cpp index 7b7eaa582..66b11573d 100644 --- a/src/c4/yml/tree.cpp +++ b/src/c4/yml/tree.cpp @@ -1280,6 +1280,161 @@ void Tree::merge_with(Tree const *src, size_t src_node, size_t dst_node) } +//----------------------------------------------------------------------------- + +bool Tree::is_subset_strview(Tree const *dst, size_t dst_node, size_t src_node) +{ + return _is_subset_strview_init(dst, dst_node, src_node, false /* don't skip values */); +} + +bool Tree::is_subset_strview_skipval(Tree const *dst, size_t dst_node, size_t src_node) +{ + return _is_subset_strview_init(dst, dst_node, src_node, true /* skip values */); +} + +bool Tree::_is_subset_strview_init(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val) +{ + _RYML_CB_ASSERT(m_callbacks, dst != nullptr); + if (src_node == NONE) + src_node = root_id(); + if (dst_node == NONE) + dst_node = dst->root_id(); + + // guard against different types + if(!_is_subset_type_equal(dst, dst_node, src_node)) + return false; + + // stream + if (is_stream(src_node)) + { + if (num_children(src_node) > dst->num_children(dst_node)) + return false; + + size_t dch = dst->first_child(dst_node); + for (size_t sch = first_child(src_node); sch != NONE;sch = next_sibling(sch)) + { + if (!_is_subset_strview_recursive(dst, dch, sch, skip_val)) + return false; + dch = dst->next_sibling(dch); + } + return true; + } + else + { + return _is_subset_strview_recursive(dst, dst_node, src_node, skip_val); + } +} + +bool Tree::_is_subset_strview_recursive(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val) +{ + // guard against different types + if(!_is_subset_type_equal(dst, dst_node, src_node)) + return false; + + // docval | val + if (is_val(src_node)) + { + if (is_val_anchor(src_node)) + { + if (val_anchor(src_node) != dst->val_anchor(dst_node)) + return false; + } + + if(skip_val) + return true; + + if (val(src_node) == dst->val(dst_node)) + return true; + return false; + } + // keyval + else if (is_keyval(src_node)) + { + if (is_key_anchor(src_node)) + { + if (key_anchor(src_node) != dst->key_anchor(dst_node)) + return false; + } + + if (is_val_anchor(src_node)) + { + if (val_anchor(src_node) != dst->val_anchor(dst_node)) + return false; + } + + if (key(src_node) == dst->key(dst_node) && (skip_val || val(src_node) == dst->val(dst_node))) + return true; + return false; + } + // docseq | keyseq | seq + else if (is_seq(src_node)) + { + if (num_children(src_node) > dst->num_children(dst_node)) + return false; + + size_t dch = dst->first_child(dst_node); + for (size_t sch = first_child(src_node); sch != NONE; sch = next_sibling(sch)) + { + if (!_is_subset_strview_recursive(dst, dch, sch, skip_val)) + return false; + dch = dst->next_sibling(dch); + } + return true; + } + // docmap | keymap | map + else if (is_map(src_node)) + { + if (num_children(src_node) > dst->num_children(dst_node)) + return false; + + for (size_t sch = first_child(src_node); sch != NONE; sch = next_sibling(sch)) + { + size_t dch = dst->find_child(dst_node, key(sch)); + if (dch == NONE) + return false; + if (!_is_subset_strview_recursive(dst, dch, sch, skip_val)) + return false; + } + return true; + } + // notype + else if(type(src_node) == NOTYPE) + { + return true; + } + else + { + C4_NEVER_REACH(); + } +} + +bool Tree::_is_subset_type_equal(Tree const *dst, size_t dst_node, size_t src_node) +{ + /* compare different node types for equality. A MAP/SEQ which only + * differs from one of these types (see below) is accepted because a subset + * comparator does not care if the MAP/SEQ was from a doc or a key. + * + * interchangeable valid types: DOC[MAP/SEQ], KEY[MAP/SEQ] and MAP/SEQ */ + if (type(src_node) != dst->type(dst_node)) + { + if(is_map(src_node)) + { + // recheck if the type matches when removing interchangeable types + if((type(src_node) & ~(DOCMAP | KEYMAP | MAP)) != (dst->type(dst_node) & ~(DOCMAP | KEYMAP | MAP))) + return false; + } + else if(is_seq(src_node)) + { + if((type(src_node) & ~(DOCSEQ | KEYSEQ | SEQ)) != (dst->type(dst_node) & ~(DOCSEQ | KEYSEQ | SEQ))) + return false; + } + else + return false; + } + return true; +} + + //----------------------------------------------------------------------------- namespace detail { diff --git a/src/c4/yml/tree.hpp b/src/c4/yml/tree.hpp index b54a48e4a..f11f95cdd 100644 --- a/src/c4/yml/tree.hpp +++ b/src/c4/yml/tree.hpp @@ -927,6 +927,19 @@ class RYML_EXPORT Tree void merge_with(Tree const* src, size_t src_node=NONE, size_t dst_root=NONE); +public: + + /** compare string views to see if src node is a subset of dst node */ + bool is_subset_strview(Tree const *dst, size_t dst_node = NONE, size_t src_node = NONE); + bool is_subset_strview_skipval(Tree const *dst, size_t dst_node = NONE, size_t src_node = NONE); + +private: + /** compare subset node types */ + bool _is_subset_type_equal(Tree const *dst, size_t dst_node, size_t src_node); + + bool _is_subset_strview_init(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val); + bool _is_subset_strview_recursive(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val); + /** @} */ public: From 857af21373f5a4a567fbc0b581544b45755c7278 Mon Sep 17 00:00:00 2001 From: Captain Yoshi Date: Thu, 14 Apr 2022 15:40:19 -0400 Subject: [PATCH 2/2] [v2] squash with prev commit --- src/c4/yml/tree.cpp | 156 ++++++++++++++------------------------------ src/c4/yml/tree.hpp | 15 +++-- 2 files changed, 56 insertions(+), 115 deletions(-) diff --git a/src/c4/yml/tree.cpp b/src/c4/yml/tree.cpp index 66b11573d..12357d476 100644 --- a/src/c4/yml/tree.cpp +++ b/src/c4/yml/tree.cpp @@ -1282,124 +1282,90 @@ void Tree::merge_with(Tree const *src, size_t src_node, size_t dst_node) //----------------------------------------------------------------------------- -bool Tree::is_subset_strview(Tree const *dst, size_t dst_node, size_t src_node) +bool Tree::has_all(Tree const* reftree, size_t refnode, size_t subject_node) const { - return _is_subset_strview_init(dst, dst_node, src_node, false /* don't skip values */); -} + _RYML_CB_ASSERT(m_callbacks, reftree != nullptr); + if (subject_node == NONE) + subject_node = root_id(); + if (refnode == NONE) + refnode = reftree->root_id(); -bool Tree::is_subset_strview_skipval(Tree const *dst, size_t dst_node, size_t src_node) -{ - return _is_subset_strview_init(dst, dst_node, src_node, true /* skip values */); + return _has_all_recursive(reftree, refnode, subject_node); } -bool Tree::_is_subset_strview_init(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val) +bool Tree::_has_all_recursive(Tree const* reftree, size_t refnode, size_t subject_node) const { - _RYML_CB_ASSERT(m_callbacks, dst != nullptr); - if (src_node == NONE) - src_node = root_id(); - if (dst_node == NONE) - dst_node = dst->root_id(); - - // guard against different types - if(!_is_subset_type_equal(dst, dst_node, src_node)) - return false; - - // stream - if (is_stream(src_node)) + if (is_val(subject_node)) { - if (num_children(src_node) > dst->num_children(dst_node)) + if( ! reftree->is_val(refnode)) return false; - - size_t dch = dst->first_child(dst_node); - for (size_t sch = first_child(src_node); sch != NONE;sch = next_sibling(sch)) - { - if (!_is_subset_strview_recursive(dst, dch, sch, skip_val)) - return false; - dch = dst->next_sibling(dch); - } + // skip value comparison return true; } - else - { - return _is_subset_strview_recursive(dst, dst_node, src_node, skip_val); - } -} - -bool Tree::_is_subset_strview_recursive(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val) -{ - // guard against different types - if(!_is_subset_type_equal(dst, dst_node, src_node)) - return false; - - // docval | val - if (is_val(src_node)) + else if (is_keyval(subject_node)) { - if (is_val_anchor(src_node)) - { - if (val_anchor(src_node) != dst->val_anchor(dst_node)) - return false; - } - - if(skip_val) - return true; - - if (val(src_node) == dst->val(dst_node)) + if( ! reftree->is_keyval(refnode)) + return false; + if (key(subject_node) == reftree->key(refnode)) return true; return false; } - // keyval - else if (is_keyval(src_node)) + else if (is_map(subject_node)) { - if (is_key_anchor(src_node)) - { - if (key_anchor(src_node) != dst->key_anchor(dst_node)) - return false; - } + if( ! reftree->is_map(refnode)) + return false; - if (is_val_anchor(src_node)) + if (num_children(subject_node) > reftree->num_children(refnode)) + return false; + + for (size_t sch = first_child(subject_node); sch != NONE; sch = next_sibling(sch)) { - if (val_anchor(src_node) != dst->val_anchor(dst_node)) + size_t rch = reftree->find_child(refnode, key(sch)); + if (rch == NONE) + return false; + if ( ! _has_all_recursive(reftree, rch, sch)) return false; } - - if (key(src_node) == dst->key(dst_node) && (skip_val || val(src_node) == dst->val(dst_node))) - return true; - return false; + return true; } - // docseq | keyseq | seq - else if (is_seq(src_node)) + else if (is_seq(subject_node)) { - if (num_children(src_node) > dst->num_children(dst_node)) + if( ! reftree->is_seq(refnode)) + return false; + + if (num_children(subject_node) > reftree->num_children(refnode)) return false; - size_t dch = dst->first_child(dst_node); - for (size_t sch = first_child(src_node); sch != NONE; sch = next_sibling(sch)) + size_t rch = reftree->first_child(refnode); + for (size_t sch = first_child(subject_node); sch != NONE; sch = next_sibling(sch)) { - if (!_is_subset_strview_recursive(dst, dch, sch, skip_val)) + if ( ! _has_all_recursive(reftree, rch, sch)) return false; - dch = dst->next_sibling(dch); + rch = reftree->next_sibling(rch); } return true; } - // docmap | keymap | map - else if (is_map(src_node)) + else if (is_stream(subject_node)) { - if (num_children(src_node) > dst->num_children(dst_node)) + if( ! reftree->is_stream(refnode)) + return false; + + if (num_children(subject_node) > reftree->num_children(refnode)) return false; - for (size_t sch = first_child(src_node); sch != NONE; sch = next_sibling(sch)) + size_t rch = reftree->first_child(refnode); + for (size_t sch = first_child(subject_node); sch != NONE;sch = next_sibling(sch)) { - size_t dch = dst->find_child(dst_node, key(sch)); - if (dch == NONE) - return false; - if (!_is_subset_strview_recursive(dst, dch, sch, skip_val)) + if ( ! _has_all_recursive(reftree, rch, sch)) return false; + rch = reftree->next_sibling(rch); } return true; } - // notype - else if(type(src_node) == NOTYPE) + else if(type(subject_node) == NOTYPE) { + if(reftree->type(refnode) != NOTYPE) + return false; return true; } else @@ -1408,32 +1374,6 @@ bool Tree::_is_subset_strview_recursive(Tree const *dst, size_t dst_node, size_t } } -bool Tree::_is_subset_type_equal(Tree const *dst, size_t dst_node, size_t src_node) -{ - /* compare different node types for equality. A MAP/SEQ which only - * differs from one of these types (see below) is accepted because a subset - * comparator does not care if the MAP/SEQ was from a doc or a key. - * - * interchangeable valid types: DOC[MAP/SEQ], KEY[MAP/SEQ] and MAP/SEQ */ - if (type(src_node) != dst->type(dst_node)) - { - if(is_map(src_node)) - { - // recheck if the type matches when removing interchangeable types - if((type(src_node) & ~(DOCMAP | KEYMAP | MAP)) != (dst->type(dst_node) & ~(DOCMAP | KEYMAP | MAP))) - return false; - } - else if(is_seq(src_node)) - { - if((type(src_node) & ~(DOCSEQ | KEYSEQ | SEQ)) != (dst->type(dst_node) & ~(DOCSEQ | KEYSEQ | SEQ))) - return false; - } - else - return false; - } - return true; -} - //----------------------------------------------------------------------------- diff --git a/src/c4/yml/tree.hpp b/src/c4/yml/tree.hpp index f11f95cdd..df0aeb5a9 100644 --- a/src/c4/yml/tree.hpp +++ b/src/c4/yml/tree.hpp @@ -929,16 +929,17 @@ class RYML_EXPORT Tree public: - /** compare string views to see if src node is a subset of dst node */ - bool is_subset_strview(Tree const *dst, size_t dst_node = NONE, size_t src_node = NONE); - bool is_subset_strview_skipval(Tree const *dst, size_t dst_node = NONE, size_t src_node = NONE); + /** non-recursive predicates: */ + + /** return true if subject_node has all the keys or indices in refnode from a reftree + * @note does not check values, only keys (for maps) or indices (for seqs) */ + bool has_all(Tree const* reftree, size_t refnode = NONE, size_t subject_node = NONE ) const; private: - /** compare subset node types */ - bool _is_subset_type_equal(Tree const *dst, size_t dst_node, size_t src_node); - bool _is_subset_strview_init(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val); - bool _is_subset_strview_recursive(Tree const *dst, size_t dst_node, size_t src_node, bool skip_val); + /** and helper functions to drive the recursive descent: */ + + bool _has_all_recursive(Tree const* reftree, size_t refnode, size_t subject_node) const; /** @} */