• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

biojppm / rapidyaml / 14071101254

25 Mar 2025 10:12PM UTC coverage: 97.53% (-0.06%) from 97.586%
14071101254

Pull #508

github

web-flow
Merge f121fe4e2 into d3132a25e
Pull Request #508: fix build with cmake 4

11568 of 11861 relevant lines covered (97.53%)

763005.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.17
/src/c4/yml/node.hpp
1
#ifndef _C4_YML_NODE_HPP_
2
#define _C4_YML_NODE_HPP_
3

4
/** @file node.hpp Node classes */
5

6
#include <cstddef>
7

8
#include "c4/yml/tree.hpp"
9
#include "c4/base64.hpp"
10

11
#ifdef __clang__
12
#   pragma clang diagnostic push
13
#   pragma clang diagnostic ignored "-Wtype-limits"
14
#   pragma clang diagnostic ignored "-Wold-style-cast"
15
#elif defined(__GNUC__)
16
#   pragma GCC diagnostic push
17
#   pragma GCC diagnostic ignored "-Wtype-limits"
18
#   pragma GCC diagnostic ignored "-Wold-style-cast"
19
#   pragma GCC diagnostic ignored "-Wuseless-cast"
20
#elif defined(_MSC_VER)
21
#   pragma warning(push)
22
#   pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/)
23
#   pragma warning(disable: 4296/*expression is always 'boolean_value'*/)
24
#endif
25

26
namespace c4 {
27
namespace yml {
28

29
/** @addtogroup doc_node_classes
30
 *
31
 * @{
32
 */
33

34

35
/** @defgroup doc_serialization_helpers Serialization helpers
36
 *
37
 * @{
38
 */
39
template<class K> struct Key { K & k; }; // NOLINT
40
template<> struct Key<fmt::const_base64_wrapper> { fmt::const_base64_wrapper wrapper; };
41
template<> struct Key<fmt::base64_wrapper> { fmt::base64_wrapper wrapper; };
42

43
template<class K> C4_ALWAYS_INLINE Key<K> key(K & k) { return Key<K>{k}; }
4,808✔
44
C4_ALWAYS_INLINE Key<fmt::const_base64_wrapper> key(fmt::const_base64_wrapper w) { return {w}; }
32✔
45
C4_ALWAYS_INLINE Key<fmt::base64_wrapper> key(fmt::base64_wrapper w) { return {w}; }
72✔
46

47

48
template<class T> void write(NodeRef *n, T const& v);
49

50
template<class T> inline bool read(ConstNodeRef const& C4_RESTRICT n, T *v);
51
template<class T> inline bool read(NodeRef const& C4_RESTRICT n, T *v);
52
template<class T> inline bool readkey(ConstNodeRef const& C4_RESTRICT n, T *v);
53
template<class T> inline bool readkey(NodeRef const& C4_RESTRICT n, T *v);
54

55
/** @} */
56

57

58
//-----------------------------------------------------------------------------
59
//-----------------------------------------------------------------------------
60
//-----------------------------------------------------------------------------
61

62
// forward decls
63
class NodeRef;
64
class ConstNodeRef;
65

66

67
//-----------------------------------------------------------------------------
68
//-----------------------------------------------------------------------------
69
//-----------------------------------------------------------------------------
70

71
/** @cond dev */
72
namespace detail {
73

74
template<class NodeRefType>
75
struct child_iterator
76
{
77
    using value_type = NodeRefType;
78
    using tree_type = typename NodeRefType::tree_type;
79

80
    tree_type * C4_RESTRICT m_tree;
81
    id_type m_child_id;
82

83
    child_iterator(tree_type * t, id_type id) : m_tree(t), m_child_id(id) {}
3,391,408✔
84

85
    child_iterator& operator++ () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->next_sibling(m_child_id); return *this; }
4,044,464✔
86
    child_iterator& operator-- () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->prev_sibling(m_child_id); return *this; }
87

88
    NodeRefType operator*  () const { return NodeRefType(m_tree, m_child_id); }
4,044,464✔
89
    NodeRefType operator-> () const { return NodeRefType(m_tree, m_child_id); }
90

91
    bool operator!= (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id != that.m_child_id; }
5,739,912✔
92
    bool operator== (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id == that.m_child_id; }
93
};
94

95
template<class NodeRefType>
96
struct children_view_
97
{
98
    using n_iterator = child_iterator<NodeRefType>;
99

100
    n_iterator b, e;
101

102
    children_view_(n_iterator const& C4_RESTRICT b_,
1,695,224✔
103
                          n_iterator const& C4_RESTRICT e_) : b(b_), e(e_) {}
1,695,224✔
104

105
    n_iterator begin() const { return b; }
1,695,032✔
106
    n_iterator end  () const { return e; }
1,695,032✔
107
};
108

109
template<class NodeRefType, class Visitor>
110
bool _visit(NodeRefType &node, Visitor fn, id_type indentation_level, bool skip_root=false)
111
{
112
    id_type increment = 0;
113
    if( ! (node.is_root() && skip_root))
114
    {
115
        if(fn(node, indentation_level))
116
            return true;
117
        ++increment;
118
    }
119
    if(node.has_children())
120
    {
121
        for(auto ch : node.children())
122
        {
123
            if(_visit(ch, fn, indentation_level + increment, false)) // no need to forward skip_root as it won't be root
124
            {
125
                return true;
126
            }
127
        }
128
    }
129
    return false;
130
}
131

132
template<class NodeRefType, class Visitor>
133
bool _visit_stacked(NodeRefType &node, Visitor fn, id_type indentation_level, bool skip_root=false)
134
{
135
    id_type increment = 0;
136
    if( ! (node.is_root() && skip_root))
137
    {
138
        if(fn(node, indentation_level))
139
        {
140
            return true;
141
        }
142
        ++increment;
143
    }
144
    if(node.has_children())
145
    {
146
        fn.push(node, indentation_level);
147
        for(auto ch : node.children())
148
        {
149
            if(_visit_stacked(ch, fn, indentation_level + increment, false)) // no need to forward skip_root as it won't be root
150
            {
151
                fn.pop(node, indentation_level);
152
                return true;
153
            }
154
        }
155
        fn.pop(node, indentation_level);
156
    }
157
    return false;
158
}
159

160
template<class Impl, class ConstImpl>
161
struct RoNodeMethods;
162
} // detail
163
/** @endcond */
164

165
//-----------------------------------------------------------------------------
166
//-----------------------------------------------------------------------------
167
//-----------------------------------------------------------------------------
168

169

170
/** a CRTP base providing read-only methods for @ref ConstNodeRef and @ref NodeRef */
171
namespace detail {
172
template<class Impl, class ConstImpl>
173
struct RoNodeMethods
174
{
175
    C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wcast-align")
176
    /** @cond dev */
177
    // helper CRTP macros, undefined at the end
178
    #define tree_ ((ConstImpl const* C4_RESTRICT)this)->m_tree
179
    #define id_ ((ConstImpl const* C4_RESTRICT)this)->m_id
180
    #define tree__ ((Impl const* C4_RESTRICT)this)->m_tree
181
    #define id__ ((Impl const* C4_RESTRICT)this)->m_id
182
    // require readable: this is a precondition for reading from the
183
    // tree using this object.
184
    #define _C4RR()                                       \
185
        RYML_ASSERT(tree_ != nullptr);                    \
186
        _RYML_CB_ASSERT(tree_->m_callbacks, id_ != NONE); \
187
        _RYML_CB_ASSERT(tree_->m_callbacks, (((Impl const* C4_RESTRICT)this)->readable()))
188
    // a SFINAE beautifier to enable a function only if the
189
    // implementation is mutable
190
    #define _C4_IF_MUTABLE(ty) typename std::enable_if<!std::is_same<U, ConstImpl>::value, ty>::type
191
    /** @endcond */
192

193
public:
194

195
    /** @name node property getters */
196
    /** @{ */
197

198
    /** returns the data or null when the id is NONE */
199
    C4_ALWAYS_INLINE NodeData const* get() const RYML_NOEXCEPT { return ((Impl const*)this)->readable() ? tree_->get(id_) : nullptr; }
7,800,024✔
200
    /** returns the data or null when the id is NONE */
201
    template<class U=Impl>
202
    C4_ALWAYS_INLINE auto get() RYML_NOEXCEPT -> _C4_IF_MUTABLE(NodeData*) { return ((Impl const*)this)->readable() ? tree__->get(id__) : nullptr; }
63,672✔
203

204
    C4_ALWAYS_INLINE NodeType    type()     const RYML_NOEXCEPT { _C4RR(); return tree_->type(id_); }     /**< Forward to @ref Tree::type_str(). Node must be readable. */
16,752✔
205
    C4_ALWAYS_INLINE const char* type_str() const RYML_NOEXCEPT { _C4RR(); return tree_->type_str(id_); } /**< Forward to @ref Tree::type_str(). Node must be readable. */
40✔
206

207
    C4_ALWAYS_INLINE csubstr key()        const RYML_NOEXCEPT { _C4RR(); return tree_->key(id_); }        /**< Forward to @ref Tree::key(). Node must be readable. */
5,636,832✔
208
    C4_ALWAYS_INLINE csubstr key_tag()    const RYML_NOEXCEPT { _C4RR(); return tree_->key_tag(id_); }    /**< Forward to @ref Tree::key_tag(). Node must be readable. */
20,608✔
209
    C4_ALWAYS_INLINE csubstr key_ref()    const RYML_NOEXCEPT { _C4RR(); return tree_->key_ref(id_); }    /**< Forward to @ref Tree::key_ref(). Node must be readable. */
6,368✔
210
    C4_ALWAYS_INLINE csubstr key_anchor() const RYML_NOEXCEPT { _C4RR(); return tree_->key_anchor(id_); } /**< Forward to @ref Tree::key_anchor(). Node must be readable. */
8,672✔
211

212
    C4_ALWAYS_INLINE csubstr val()        const RYML_NOEXCEPT { _C4RR(); return tree_->val(id_); }        /**< Forward to @ref Tree::val(). Node must be readable. */
1,471,224✔
213
    C4_ALWAYS_INLINE csubstr val_tag()    const RYML_NOEXCEPT { _C4RR(); return tree_->val_tag(id_); }    /**< Forward to @ref Tree::val_tag(). Node must be readable. */
32,176✔
214
    C4_ALWAYS_INLINE csubstr val_ref()    const RYML_NOEXCEPT { _C4RR(); return tree_->val_ref(id_); }    /**< Forward to @ref Tree::val_ref(). Node must be readable. */
9,632✔
215
    C4_ALWAYS_INLINE csubstr val_anchor() const RYML_NOEXCEPT { _C4RR(); return tree_->val_anchor(id_); } /**< Forward to @ref Tree::val_anchor(). Node must be readable. */
12,616✔
216

217
    C4_ALWAYS_INLINE NodeScalar const& keysc() const RYML_NOEXCEPT { _C4RR(); return tree_->keysc(id_); } /**< Forward to @ref Tree::keysc(). Node must be readable. */
40✔
218
    C4_ALWAYS_INLINE NodeScalar const& valsc() const RYML_NOEXCEPT { _C4RR(); return tree_->valsc(id_); } /**< Forward to @ref Tree::valsc(). Node must be readable. */
40✔
219

220
    C4_ALWAYS_INLINE bool key_is_null() const RYML_NOEXCEPT { _C4RR(); return tree_->key_is_null(id_); } /**< Forward to @ref Tree::key_is_null(). Node must be readable. */
663,480✔
221
    C4_ALWAYS_INLINE bool val_is_null() const RYML_NOEXCEPT { _C4RR(); return tree_->val_is_null(id_); } /**< Forward to @ref Tree::val_is_null(). Node must be readable. */
443,496✔
222

223
    C4_ALWAYS_INLINE bool is_key_unfiltered() const noexcept { _C4RR(); return tree_->is_key_unfiltered(id_); } /**< Forward to @ref Tree::is_key_unfiltered(). Node must be readable. */
240✔
224
    C4_ALWAYS_INLINE bool is_val_unfiltered() const noexcept { _C4RR(); return tree_->is_val_unfiltered(id_); } /**< Forward to @ref Tree::is_val_unfiltered(). Node must be readable. */
240✔
225

226
    /** @} */
227

228
public:
229

230
    /** @name node type predicates */
231
    /** @{ */
232

233
    C4_ALWAYS_INLINE bool empty()            const RYML_NOEXCEPT { _C4RR(); return tree_->empty(id_); } /**< Forward to @ref Tree::empty(). Node must be readable. */
989,160✔
234
    C4_ALWAYS_INLINE bool is_stream()        const RYML_NOEXCEPT { _C4RR(); return tree_->is_stream(id_); } /**< Forward to @ref Tree::is_stream(). Node must be readable. */
48,992✔
235
    C4_ALWAYS_INLINE bool is_doc()           const RYML_NOEXCEPT { _C4RR(); return tree_->is_doc(id_); } /**< Forward to @ref Tree::is_doc(). Node must be readable. */
2,706,456✔
236
    C4_ALWAYS_INLINE bool is_container()     const RYML_NOEXCEPT { _C4RR(); return tree_->is_container(id_); } /**< Forward to @ref Tree::is_container(). Node must be readable. */
2,231,568✔
237
    C4_ALWAYS_INLINE bool is_map()           const RYML_NOEXCEPT { _C4RR(); return tree_->is_map(id_); } /**< Forward to @ref Tree::is_map(). Node must be readable. */
1,674,744✔
238
    C4_ALWAYS_INLINE bool is_seq()           const RYML_NOEXCEPT { _C4RR(); return tree_->is_seq(id_); } /**< Forward to @ref Tree::is_seq(). Node must be readable. */
1,780,664✔
239
    C4_ALWAYS_INLINE bool has_val()          const RYML_NOEXCEPT { _C4RR(); return tree_->has_val(id_); } /**< Forward to @ref Tree::has_val(). Node must be readable. */
6,761,840✔
240
    C4_ALWAYS_INLINE bool has_key()          const RYML_NOEXCEPT { _C4RR(); return tree_->has_key(id_); } /**< Forward to @ref Tree::has_key(). Node must be readable. */
14,133,192✔
241
    C4_ALWAYS_INLINE bool is_val()           const RYML_NOEXCEPT { _C4RR(); return tree_->is_val(id_); } /**< Forward to @ref Tree::is_val(). Node must be readable. */
428,272✔
242
    C4_ALWAYS_INLINE bool is_keyval()        const RYML_NOEXCEPT { _C4RR(); return tree_->is_keyval(id_); } /**< Forward to @ref Tree::is_keyval(). Node must be readable. */
644,056✔
243
    C4_ALWAYS_INLINE bool has_key_tag()      const RYML_NOEXCEPT { _C4RR(); return tree_->has_key_tag(id_); } /**< Forward to @ref Tree::has_key_tag(). Node must be readable. */
62,184✔
244
    C4_ALWAYS_INLINE bool has_val_tag()      const RYML_NOEXCEPT { _C4RR(); return tree_->has_val_tag(id_); } /**< Forward to @ref Tree::has_val_tag(). Node must be readable. */
95,320✔
245
    C4_ALWAYS_INLINE bool has_key_anchor()   const RYML_NOEXCEPT { _C4RR(); return tree_->has_key_anchor(id_); } /**< Forward to @ref Tree::has_key_anchor(). Node must be readable. */
1,493,944✔
246
    C4_ALWAYS_INLINE bool has_val_anchor()   const RYML_NOEXCEPT { _C4RR(); return tree_->has_val_anchor(id_); } /**< Forward to @ref Tree::has_val_anchor(). Node must be readable. */
1,498,808✔
247
    C4_ALWAYS_INLINE bool has_anchor()       const RYML_NOEXCEPT { _C4RR(); return tree_->has_anchor(id_); } /**< Forward to @ref Tree::has_anchor(). Node must be readable. */
680✔
248
    C4_ALWAYS_INLINE bool is_key_ref()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_ref(id_); } /**< Forward to @ref Tree::is_key_ref(). Node must be readable. */
1,497,152✔
249
    C4_ALWAYS_INLINE bool is_val_ref()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_ref(id_); } /**< Forward to @ref Tree::is_val_ref(). Node must be readable. */
1,503,016✔
250
    C4_ALWAYS_INLINE bool is_ref()           const RYML_NOEXCEPT { _C4RR(); return tree_->is_ref(id_); } /**< Forward to @ref Tree::is_ref(). Node must be readable. */
528✔
251
    C4_ALWAYS_INLINE bool parent_is_seq()    const RYML_NOEXCEPT { _C4RR(); return tree_->parent_is_seq(id_); } /**< Forward to @ref Tree::parent_is_seq(). Node must be readable. */
448✔
252
    C4_ALWAYS_INLINE bool parent_is_map()    const RYML_NOEXCEPT { _C4RR(); return tree_->parent_is_map(id_); } /**< Forward to @ref Tree::parent_is_map(). Node must be readable. */
448✔
253

254
    RYML_DEPRECATED("use has_key_anchor()")  bool is_key_anchor() const noexcept { _C4RR(); return tree_->has_key_anchor(id_); }
255
    RYML_DEPRECATED("use has_val_anchor()")  bool is_val_hanchor() const noexcept { _C4RR(); return tree_->has_val_anchor(id_); }
256
    RYML_DEPRECATED("use has_anchor()")      bool is_anchor()     const noexcept { _C4RR(); return tree_->has_anchor(id_); }
257
    RYML_DEPRECATED("use has_anchor() || is_ref()") bool is_anchor_or_ref() const noexcept { _C4RR(); return tree_->is_anchor_or_ref(id_); }
258

259
    /** @} */
260

261
public:
262

263
    /** @name node container+scalar style predicates */
264
    /** @{ */
265

266
    // documentation to the right -->
267

268
    C4_ALWAYS_INLINE bool type_has_any(NodeType_e bits)  const RYML_NOEXCEPT { _C4RR(); return tree_->type_has_any(id_, bits); }  /**< Forward to @ref Tree::type_has_any(). Node must be readable. */
3,640✔
269
    C4_ALWAYS_INLINE bool type_has_all(NodeType_e bits)  const RYML_NOEXCEPT { _C4RR(); return tree_->type_has_all(id_, bits); }  /**< Forward to @ref Tree::type_has_all(). Node must be readable. */
2,968✔
270
    C4_ALWAYS_INLINE bool type_has_none(NodeType_e bits) const RYML_NOEXCEPT { _C4RR(); return tree_->type_has_none(id_, bits); } /**< Forward to @ref Tree::type_has_none(). Node must be readable. */
1,960✔
271

272
    C4_ALWAYS_INLINE bool is_container_styled() const RYML_NOEXCEPT { _C4RR(); return tree_->is_container_styled(id_); } /**< Forward to @ref Tree::is_container_styled(). Node must be readable. */
40✔
273
    C4_ALWAYS_INLINE bool is_block()            const RYML_NOEXCEPT { _C4RR(); return tree_->is_block(id_); }   /**< Forward to @ref Tree::is_block(). Node must be readable. */
616✔
274
    C4_ALWAYS_INLINE bool is_flow_sl()          const RYML_NOEXCEPT { _C4RR(); return tree_->is_flow_sl(id_); } /**< Forward to @ref Tree::is_flow_sl(). Node must be readable. */
616✔
275
    C4_ALWAYS_INLINE bool is_flow_ml()          const RYML_NOEXCEPT { _C4RR(); return tree_->is_flow_ml(id_); } /**< Forward to @ref Tree::is_flow_ml(). Node must be readable. */
616✔
276
    C4_ALWAYS_INLINE bool is_flow()             const RYML_NOEXCEPT { _C4RR(); return tree_->is_flow(id_); }    /**< Forward to @ref Tree::is_flow(). Node must be readable. */
616✔
277

278
    C4_ALWAYS_INLINE bool is_key_styled()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_styled(id_); }  /**< Forward to @ref Tree::is_key_styled(). Node must be readable. */
40✔
279
    C4_ALWAYS_INLINE bool is_val_styled()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_styled(id_); }  /**< Forward to @ref Tree::is_val_styled(). Node must be readable. */
40✔
280
    C4_ALWAYS_INLINE bool is_key_literal()      const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_literal(id_); } /**< Forward to @ref Tree::is_key_literal(). Node must be readable. */
832✔
281
    C4_ALWAYS_INLINE bool is_val_literal()      const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_literal(id_); } /**< Forward to @ref Tree::is_val_literal(). Node must be readable. */
832✔
282
    C4_ALWAYS_INLINE bool is_key_folded()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_folded(id_); }  /**< Forward to @ref Tree::is_key_folded(). Node must be readable. */
1,000✔
283
    C4_ALWAYS_INLINE bool is_val_folded()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_folded(id_); }  /**< Forward to @ref Tree::is_val_folded(). Node must be readable. */
1,000✔
284
    C4_ALWAYS_INLINE bool is_key_squo()         const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_squo(id_); }    /**< Forward to @ref Tree::is_key_squo(). Node must be readable. */
1,000✔
285
    C4_ALWAYS_INLINE bool is_val_squo()         const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_squo(id_); }    /**< Forward to @ref Tree::is_val_squo(). Node must be readable. */
1,000✔
286
    C4_ALWAYS_INLINE bool is_key_dquo()         const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_dquo(id_); }    /**< Forward to @ref Tree::is_key_dquo(). Node must be readable. */
832✔
287
    C4_ALWAYS_INLINE bool is_val_dquo()         const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_dquo(id_); }    /**< Forward to @ref Tree::is_val_dquo(). Node must be readable. */
1,072✔
288
    C4_ALWAYS_INLINE bool is_key_plain()        const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_plain(id_); }   /**< Forward to @ref Tree::is_key_plain(). Node must be readable. */
832✔
289
    C4_ALWAYS_INLINE bool is_val_plain()        const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_plain(id_); }   /**< Forward to @ref Tree::is_val_plain(). Node must be readable. */
832✔
290
    C4_ALWAYS_INLINE bool is_key_quoted()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_key_quoted(id_); }  /**< Forward to @ref Tree::is_key_quoted(). Node must be readable. */
649,600✔
291
    C4_ALWAYS_INLINE bool is_val_quoted()       const RYML_NOEXCEPT { _C4RR(); return tree_->is_val_quoted(id_); }  /**< Forward to @ref Tree::is_val_quoted(). Node must be readable. */
1,283,384✔
292
    C4_ALWAYS_INLINE bool is_quoted()           const RYML_NOEXCEPT { _C4RR(); return tree_->is_quoted(id_); }      /**< Forward to @ref Tree::is_quoted(). Node must be readable. */
2,608✔
293

294
    /** @} */
295

296
public:
297

298
    /** @name hierarchy predicates */
299
    /** @{ */
300

301
    // documentation to the right -->
302

303
    C4_ALWAYS_INLINE bool is_root()    const RYML_NOEXCEPT { _C4RR(); return tree_->is_root(id_); } /**< Forward to @ref Tree::is_root(). Node must be readable. */
1,192,888✔
304
    C4_ALWAYS_INLINE bool has_parent() const RYML_NOEXCEPT { _C4RR(); return tree_->has_parent(id_); } /**< Forward to @ref Tree::has_parent()  Node must be readable. */
352✔
305

306
    C4_ALWAYS_INLINE bool has_child(ConstImpl const& n) const RYML_NOEXCEPT { _C4RR(); return n.readable() ? tree_->has_child(id_, n.m_id) : false; } /**< Forward to @ref Tree::has_child(). Node must be readable. */
1,276,640✔
307
    C4_ALWAYS_INLINE bool has_child(id_type node) const RYML_NOEXCEPT { _C4RR(); return tree_->has_child(id_, node); } /**< Forward to @ref Tree::has_child(). Node must be readable. */
40✔
308
    C4_ALWAYS_INLINE bool has_child(csubstr name) const RYML_NOEXCEPT { _C4RR(); return tree_->has_child(id_, name); } /**< Forward to @ref Tree::has_child(). Node must be readable. */
3,288✔
309
    C4_ALWAYS_INLINE bool has_children() const RYML_NOEXCEPT { _C4RR(); return tree_->has_children(id_); } /**< Forward to @ref Tree::has_child(). Node must be readable. */
1,052,328✔
310

311
    C4_ALWAYS_INLINE bool has_sibling(ConstImpl const& n) const RYML_NOEXCEPT { _C4RR(); return n.readable() ? tree_->has_sibling(id_, n.m_id) : false; } /**< Forward to @ref Tree::has_sibling(). Node must be readable. */
15,256,680✔
312
    C4_ALWAYS_INLINE bool has_sibling(id_type node) const RYML_NOEXCEPT { _C4RR(); return tree_->has_sibling(id_, node); } /**< Forward to @ref Tree::has_sibling(). Node must be readable. */
313
    C4_ALWAYS_INLINE bool has_sibling(csubstr name) const RYML_NOEXCEPT { _C4RR(); return tree_->has_sibling(id_, name); } /**< Forward to @ref Tree::has_sibling(). Node must be readable. */
4,077,112✔
314
    C4_ALWAYS_INLINE bool has_other_siblings() const RYML_NOEXCEPT { _C4RR(); return tree_->has_other_siblings(id_); }  /**< Forward to @ref Tree::has_sibling(). Node must be readable. */
989,128✔
315

316
    RYML_DEPRECATED("use has_other_siblings()") bool has_siblings() const RYML_NOEXCEPT { _C4RR(); return tree_->has_siblings(id_); }
317

318
    /** @} */
319

320
public:
321

322
    /** @name hierarchy getters */
323
    /** @{ */
324

325
    // documentation to the right -->
326

327
    template<class U=Impl>
328
    C4_ALWAYS_INLINE auto doc(id_type i) RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { RYML_ASSERT(tree_); return {tree__, tree__->doc(i)}; } /**< Forward to @ref Tree::doc(). Node must be readable. */
80✔
329
    C4_ALWAYS_INLINE ConstImpl doc(id_type i) const RYML_NOEXCEPT { RYML_ASSERT(tree_); return {tree_, tree_->doc(i)}; }                /**< Forward to @ref Tree::doc(). Node must be readable. succeeds even when the node may have invalid or seed id */
1,032✔
330

331
    template<class U=Impl>
332
    C4_ALWAYS_INLINE auto parent() RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->parent(id__)}; } /**< Forward to @ref Tree::parent(). Node must be readable. */
48✔
333
    C4_ALWAYS_INLINE ConstImpl parent() const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->parent(id_)}; }                 /**< Forward to @ref Tree::parent(). Node must be readable. */
15,098,080✔
334

335
    template<class U=Impl>
336
    C4_ALWAYS_INLINE auto first_child() RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->first_child(id__)}; }  /**< Forward to @ref Tree::first_child(). Node must be readable. */
232✔
337
    C4_ALWAYS_INLINE ConstImpl first_child() const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->first_child(id_)}; }                  /**< Forward to @ref Tree::first_child(). Node must be readable. */
924,720✔
338

339
    template<class U=Impl>
340
    C4_ALWAYS_INLINE auto last_child() RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->last_child(id__)}; }  /**< Forward to @ref Tree::last_child(). Node must be readable. */
1,848✔
341
    C4_ALWAYS_INLINE ConstImpl last_child () const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->last_child (id_)}; }                /**< Forward to @ref Tree::last_child(). Node must be readable. */
269,808✔
342

343
    template<class U=Impl>
344
    C4_ALWAYS_INLINE auto child(id_type pos) RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->child(id__, pos)}; }  /**< Forward to @ref Tree::child(). Node must be readable. */
216✔
345
    C4_ALWAYS_INLINE ConstImpl child(id_type pos) const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->child(id_, pos)}; }                  /**< Forward to @ref Tree::child(). Node must be readable. */
661,104✔
346

347
    template<class U=Impl>
348
    C4_ALWAYS_INLINE auto find_child(csubstr name)  RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->find_child(id__, name)}; }  /**< Forward to @ref Tree::first_child(). Node must be readable. */
88✔
349
    C4_ALWAYS_INLINE ConstImpl find_child(csubstr name) const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->find_child(id_, name)}; }                   /**< Forward to @ref Tree::first_child(). Node must be readable. */
660,888✔
350

351
    template<class U=Impl>
352
    C4_ALWAYS_INLINE auto prev_sibling() RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->prev_sibling(id__)}; }  /**< Forward to @ref Tree::prev_sibling(). Node must be readable. */
40✔
353
    C4_ALWAYS_INLINE ConstImpl prev_sibling() const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->prev_sibling(id_)}; }                  /**< Forward to @ref Tree::prev_sibling(). Node must be readable. */
65,712✔
354

355
    template<class U=Impl>
356
    C4_ALWAYS_INLINE auto next_sibling() RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->next_sibling(id__)}; }  /**< Forward to @ref Tree::next_sibling(). Node must be readable. */
40✔
357
    C4_ALWAYS_INLINE ConstImpl next_sibling() const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->next_sibling(id_)}; }                  /**< Forward to @ref Tree::next_sibling(). Node must be readable. */
65,768✔
358

359
    template<class U=Impl>
360
    C4_ALWAYS_INLINE auto first_sibling() RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->first_sibling(id__)}; }  /**< Forward to @ref Tree::first_sibling(). Node must be readable. */
40✔
361
    C4_ALWAYS_INLINE ConstImpl first_sibling() const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->first_sibling(id_)}; }                  /**< Forward to @ref Tree::first_sibling(). Node must be readable. */
64✔
362

363
    template<class U=Impl>
364
    C4_ALWAYS_INLINE auto last_sibling() RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->last_sibling(id__)}; }  /**< Forward to @ref Tree::last_sibling(). Node must be readable. */
40✔
365
    C4_ALWAYS_INLINE ConstImpl last_sibling () const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->last_sibling(id_)}; }                 /**< Forward to @ref Tree::last_sibling(). Node must be readable. */
64✔
366

367
    template<class U=Impl>
368
    C4_ALWAYS_INLINE auto sibling(id_type pos) RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->sibling(id__, pos)}; }  /**< Forward to @ref Tree::sibling(). Node must be readable. */
40✔
369
    C4_ALWAYS_INLINE ConstImpl sibling(id_type pos) const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->sibling(id_, pos)}; }                  /**< Forward to @ref Tree::sibling(). Node must be readable. */
48✔
370

371
    template<class U=Impl>
372
    C4_ALWAYS_INLINE auto find_sibling(csubstr name) RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl) { _C4RR(); return {tree__, tree__->find_sibling(id__, name)}; }  /**< Forward to @ref Tree::find_sibling(). Node must be readable. */
96✔
373
    C4_ALWAYS_INLINE ConstImpl find_sibling(csubstr name) const RYML_NOEXCEPT { _C4RR(); return {tree_, tree_->find_sibling(id_, name)}; }                  /**< Forward to @ref Tree::find_sibling(). Node must be readable. */
144✔
374

375
    C4_ALWAYS_INLINE id_type num_children() const RYML_NOEXCEPT { _C4RR(); return tree_->num_children(id_); } /**< O(num_children). Forward to @ref Tree::num_children(). */
8,537,120✔
376
    C4_ALWAYS_INLINE id_type num_siblings() const RYML_NOEXCEPT { _C4RR(); return tree_->num_siblings(id_); } /**< O(num_children). Forward to @ref Tree::num_siblings(). */
851,064✔
377
    C4_ALWAYS_INLINE id_type num_other_siblings() const RYML_NOEXCEPT { _C4RR(); return tree_->num_other_siblings(id_); } /**< O(num_siblings). Forward to @ref Tree::num_other_siblings(). */
851,032✔
378
    C4_ALWAYS_INLINE id_type child_pos(ConstImpl const& n) const RYML_NOEXCEPT { _C4RR(); _RYML_CB_ASSERT(tree_->m_callbacks, n.readable()); return tree_->child_pos(id_, n.m_id); } /**< O(num_children). Forward to @ref Tree::child_pos(). */
200✔
379
    C4_ALWAYS_INLINE id_type sibling_pos(ConstImpl const& n) const RYML_NOEXCEPT { _C4RR(); _RYML_CB_ASSERT(tree_->callbacks(), n.readable()); return tree_->child_pos(tree_->parent(id_), n.m_id); } /**< O(num_siblings). Forward to @ref Tree::sibling_pos(). */
120✔
380

381
    C4_ALWAYS_INLINE id_type depth_asc() const RYML_NOEXCEPT { _C4RR(); return tree_->depth_asc(id_); } /** O(log(num_nodes)). Forward to Tree::depth_asc(). Node must be readable. */
152✔
382
    C4_ALWAYS_INLINE id_type depth_desc() const RYML_NOEXCEPT { _C4RR(); return tree_->depth_desc(id_); } /** O(num_nodes). Forward to Tree::depth_desc(). Node must be readable. */
728✔
383

384
    /** @} */
385

386
public:
387

388
    /** @name square_brackets
389
     * operator[] */
390
    /** @{ */
391

392
    /** Find child by key; complexity is O(num_children).
393
     *
394
     * Returns the requested node, or an object in seed state if no
395
     * such child is found (see @ref NodeRef for an explanation of
396
     * what is seed state). When the object is in seed state, using it
397
     * to read from the tree is UB. The seed node can be used to write
398
     * to the tree provided that its create() method is called prior
399
     * to writing, which happens in most modifying methods in
400
     * NodeRef. It is the caller's responsibility to verify that the
401
     * returned node is readable before subsequently using it to read
402
     * from the tree.
403
     *
404
     * @warning the calling object must be readable. This precondition
405
     * is asserted. The assertion is performed only if @ref
406
     * RYML_USE_ASSERT is set to true. As with the non-const overload,
407
     * it is UB to call this method if the node is not readable.
408
     *
409
     * @see https://github.com/biojppm/rapidyaml/issues/389 */
410
    template<class U=Impl>
411
    C4_ALWAYS_INLINE auto operator[] (csubstr key) RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl)
412
    {
413
        _C4RR();
19,304✔
414
        id_type ch = tree__->find_child(id__, key);
19,272✔
415
        return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, key);
19,272✔
416
    }
417

418
    /** Find child by position; complexity is O(pos).
419
     *
420
     * Returns the requested node, or an object in seed state if no
421
     * such child is found (see @ref NodeRef for an explanation of
422
     * what is seed state). When the object is in seed state, using it
423
     * to read from the tree is UB. The seed node can be used to write
424
     * to the tree provided that its create() method is called prior
425
     * to writing, which happens in most modifying methods in
426
     * NodeRef. It is the caller's responsibility to verify that the
427
     * returned node is readable before subsequently using it to read
428
     * from the tree.
429
     *
430
     * @warning the calling object must be readable. This precondition
431
     * is asserted. The assertion is performed only if @ref
432
     * RYML_USE_ASSERT is set to true. As with the non-const overload,
433
     * it is UB to call this method if the node is not readable.
434
     *
435
     * @see https://github.com/biojppm/rapidyaml/issues/389 */
436
    template<class U=Impl>
437
    C4_ALWAYS_INLINE auto operator[] (id_type pos) RYML_NOEXCEPT -> _C4_IF_MUTABLE(Impl)
438
    {
439
        _C4RR();
11,592✔
440
        id_type ch = tree__->child(id__, pos);
11,568✔
441
        return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, pos);
11,568✔
442
    }
443

444
    /** Find a child by key; complexity is O(num_children).
445
     *
446
     * Behaves similar to the non-const overload, but further asserts
447
     * that the returned node is readable (because it can never be in
448
     * a seed state). The assertion is performed only if @ref
449
     * RYML_USE_ASSERT is set to true. As with the non-const overload,
450
     * it is UB to use the return value if it is not valid.
451
     *
452
     * @see https://github.com/biojppm/rapidyaml/issues/389  */
453
    C4_ALWAYS_INLINE ConstImpl operator[] (csubstr key) const RYML_NOEXCEPT
454
    {
455
        _C4RR();
702,488✔
456
        id_type ch = tree_->find_child(id_, key);
351,328✔
457
        _RYML_CB_ASSERT(tree_->m_callbacks, ch != NONE);
351,320✔
458
        return {tree_, ch};
351,304✔
459
    }
460

461
    /** Find a child by position; complexity is O(pos).
462
     *
463
     * Behaves similar to the non-const overload, but further asserts
464
     * that the returned node is readable (because it can never be in
465
     * a seed state). This assertion is performed only if @ref
466
     * RYML_USE_ASSERT is set to true. As with the non-const overload,
467
     * it is UB to use the return value if it is not valid.
468
     *
469
     * @see https://github.com/biojppm/rapidyaml/issues/389  */
470
    C4_ALWAYS_INLINE ConstImpl operator[] (id_type pos) const RYML_NOEXCEPT
471
    {
472
        _C4RR();
5,511,472✔
473
        id_type ch = tree_->child(id_, pos);
2,755,728✔
474
        _RYML_CB_ASSERT(tree_->m_callbacks, ch != NONE);
2,755,728✔
475
        return {tree_, ch};
2,755,704✔
476
    }
477

478
    /** @} */
479

480
public:
481

482
    /** @name at
483
     *
484
     * These functions are the analogue to operator[], with the
485
     * difference that they emit an error instead of an
486
     * assertion. That is, if any of the pre or post conditions is
487
     * violated, an error is always emitted (resulting in a call to
488
     * the error callback).
489
     *
490
     * @{ */
491

492
    /** Find child by key; complexity is O(num_children).
493
     *
494
     * Returns the requested node, or an object in seed state if no
495
     * such child is found (see @ref NodeRef for an explanation of
496
     * what is seed state). When the object is in seed state, using it
497
     * to read from the tree is UB. The seed node can be subsequently
498
     * used to write to the tree provided that its create() method is
499
     * called prior to writing, which happens inside most mutating
500
     * methods in NodeRef. It is the caller's responsibility to verify
501
     * that the returned node is readable before subsequently using it
502
     * to read from the tree.
503
     *
504
     * @warning This method will call the error callback (regardless
505
     * of build type or of the value of RYML_USE_ASSERT) whenever any
506
     * of the following preconditions is violated: a) the object is
507
     * valid (points at a tree and a node), b) the calling object must
508
     * be readable (must not be in seed state), c) the calling object
509
     * must be pointing at a MAP node. The preconditions are similar
510
     * to the non-const operator[](csubstr), but instead of using
511
     * assertions, this function directly checks those conditions and
512
     * calls the error callback if any of the checks fail.
513
     *
514
     * @note since it is valid behavior for the returned node to be in
515
     * seed state, the error callback is not invoked when this
516
     * happens. */
517
    template<class U=Impl>
518
    C4_ALWAYS_INLINE auto at(csubstr key) -> _C4_IF_MUTABLE(Impl)
519
    {
520
        RYML_CHECK(tree_ != nullptr);
112✔
521
        _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < tree_->capacity()));
104✔
522
        _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
104✔
523
        _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_map(id_));
144✔
524
        id_type ch = tree__->find_child(id__, key);
64✔
525
        return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, key);
64✔
526
    }
527

528
    /** Find child by position; complexity is O(pos).
529
     *
530
     * Returns the requested node, or an object in seed state if no
531
     * such child is found (see @ref NodeRef for an explanation of
532
     * what is seed state). When the object is in seed state, using it
533
     * to read from the tree is UB. The seed node can be used to write
534
     * to the tree provided that its create() method is called prior
535
     * to writing, which happens in most modifying methods in
536
     * NodeRef. It is the caller's responsibility to verify that the
537
     * returned node is readable before subsequently using it to read
538
     * from the tree.
539
     *
540
     * @warning This method will call the error callback (regardless
541
     * of build type or of the value of RYML_USE_ASSERT) whenever any
542
     * of the following preconditions is violated: a) the object is
543
     * valid (points at a tree and a node), b) the calling object must
544
     * be readable (must not be in seed state), c) the calling object
545
     * must be pointing at a MAP node. The preconditions are similar
546
     * to the non-const operator[](id_type), but instead of using
547
     * assertions, this function directly checks those conditions and
548
     * calls the error callback if any of the checks fail.
549
     *
550
     * @note since it is valid behavior for the returned node to be in
551
     * seed state, the error callback is not invoked when this
552
     * happens. */
553
    template<class U=Impl>
554
    C4_ALWAYS_INLINE auto at(id_type pos) -> _C4_IF_MUTABLE(Impl)
555
    {
556
        RYML_CHECK(tree_ != nullptr);
216✔
557
        const id_type cap = tree_->capacity();
208✔
558
        _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < cap));
208✔
559
        _RYML_CB_CHECK(tree_->m_callbacks, (pos >= 0 && pos < cap));
208✔
560
        _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
176✔
561
        _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_container(id_));
320✔
562
        id_type ch = tree__->child(id__, pos);
152✔
563
        return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, pos);
152✔
564
    }
565

566
    /** Get a child by name, with error checking; complexity is
567
     * O(num_children).
568
     *
569
     * Behaves as operator[](csubstr) const, but always raises an
570
     * error (even when RYML_USE_ASSERT is set to false) when the
571
     * returned node does not exist, or when this node is not
572
     * readable, or when it is not a map. This behaviour is similar to
573
     * std::vector::at(), but the error consists in calling the error
574
     * callback instead of directly raising an exception. */
575
    ConstImpl at(csubstr key) const
80✔
576
    {
577
        RYML_CHECK(tree_ != nullptr);
80✔
578
        _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < tree_->capacity()));
72✔
579
        _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
64✔
580
        _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_map(id_));
128✔
581
        id_type ch = tree_->find_child(id_, key);
56✔
582
        _RYML_CB_CHECK(tree_->m_callbacks, ch != NONE);
56✔
583
        return {tree_, ch};
40✔
584
    }
585

586
    /** Get a child by position, with error checking; complexity is
587
     * O(pos).
588
     *
589
     * Behaves as operator[](id_type) const, but always raises an error
590
     * (even when RYML_USE_ASSERT is set to false) when the returned
591
     * node does not exist, or when this node is not readable, or when
592
     * it is not a container. This behaviour is similar to
593
     * std::vector::at(), but the error consists in calling the error
594
     * callback instead of directly raising an exception. */
595
    ConstImpl at(id_type pos) const
168✔
596
    {
597
        RYML_CHECK(tree_ != nullptr);
168✔
598
        const id_type cap = tree_->capacity();
160✔
599
        _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < cap));
160✔
600
        _RYML_CB_CHECK(tree_->m_callbacks, (pos >= 0 && pos < cap));
152✔
601
        _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
120✔
602
        _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_container(id_));
240✔
603
        const id_type ch = tree_->child(id_, pos);
112✔
604
        _RYML_CB_CHECK(tree_->m_callbacks, ch != NONE);
112✔
605
        return {tree_, ch};
48✔
606
    }
607

608
    /** @} */
609

610
public:
611

612
    /** @name deserialization */
613
    /** @{ */
614

615
    /** deserialize the node's val to the given variable, forwarding
616
     * to the user-overrideable @ref read() function. */
617
    template<class T>
618
    ConstImpl const& operator>> (T &v) const
10,392✔
619
    {
620
        _C4RR();
14,832✔
621
        if( ! read((ConstImpl const&)*this, &v))
10,352✔
622
            _RYML_CB_ERR(tree_->m_callbacks, "could not deserialize value");
928✔
623
        return *((ConstImpl const*)this);
9,424✔
624
    }
625

626
    /** deserialize the node's key to the given variable, forwarding
627
     * to the user-overrideable @ref read() function; use @ref key()
628
     * to disambiguate; for example: `node >> ryml::key(var)` */
629
    template<class T>
630
    ConstImpl const& operator>> (Key<T> v) const
3,056✔
631
    {
632
        _C4RR();
6,072✔
633
        if( ! readkey((ConstImpl const&)*this, &v.k))
3,016✔
634
            _RYML_CB_ERR(tree_->m_callbacks, "could not deserialize key");
832✔
635
        return *((ConstImpl const*)this);
2,184✔
636
    }
637

638
    /** look for a child by name, if it exists assign to var. return
639
     * true if the child existed. */
640
    template<class T>
641
    bool get_if(csubstr name, T *var) const
64✔
642
    {
643
        _C4RR();
88✔
644
        ConstImpl ch = find_child(name);
24✔
645
        if(!ch.readable())
24✔
646
            return false;
8✔
647
        ch >> *var;
16✔
648
        return true;
16✔
649
    }
650

651
    /** look for a child by name, if it exists assign to var,
652
     * otherwise default to fallback. return true if the child
653
     * existed. */
654
    template<class T>
655
    bool get_if(csubstr name, T *var, T const& fallback) const
64✔
656
    {
657
        _C4RR();
88✔
658
        ConstImpl ch = find_child(name);
24✔
659
        if(ch.readable())
24✔
660
        {
661
            ch >> *var;
16✔
662
            return true;
16✔
663
        }
664
        else
665
        {
666
            *var = fallback;
8✔
667
            return false;
8✔
668
        }
669
    }
670

671
    /** @name deserialization_base64 */
672
    /** @{ */
673

674
    /** deserialize the node's key as base64. lightweight wrapper over @ref deserialize_key() */
675
    ConstImpl const& operator>> (Key<fmt::base64_wrapper> w) const
72✔
676
    {
677
        deserialize_key(w.wrapper);
72✔
678
        return *((ConstImpl const*)this);
32✔
679
    }
680

681
    /** deserialize the node's val as base64. lightweight wrapper over @ref deserialize_val() */
682
    ConstImpl const& operator>> (fmt::base64_wrapper w) const
72✔
683
    {
684
        deserialize_val(w);
72✔
685
        return *((ConstImpl const*)this);
32✔
686
    }
687

688
    /** decode the base64-encoded key and assign the
689
     * decoded blob to the given buffer/
690
     * @return the size of base64-decoded blob */
691
    size_t deserialize_key(fmt::base64_wrapper v) const
272✔
692
    {
693
        _C4RR();
272✔
694
        return from_chars(key(), &v);
192✔
695
    }
696
    /** decode the base64-encoded key and assign the
697
     * decoded blob to the given buffer/
698
     * @return the size of base64-decoded blob */
699
    size_t deserialize_val(fmt::base64_wrapper v) const
288✔
700
    {
701
        _C4RR();
288✔
702
        return from_chars(val(), &v);
208✔
703
    };
704

705
    /** @} */
706

707
    /** @} */
708

709
public:
710

711
    #if defined(__clang__)
712
    #   pragma clang diagnostic push
713
    #   pragma clang diagnostic ignored "-Wnull-dereference"
714
    #elif defined(__GNUC__)
715
    #   pragma GCC diagnostic push
716
    #   if __GNUC__ >= 6
717
    #       pragma GCC diagnostic ignored "-Wnull-dereference"
718
    #   endif
719
    #endif
720

721
    /** @name iteration */
722
    /** @{ */
723

724
    using iterator = detail::child_iterator<Impl>;
725
    using const_iterator = detail::child_iterator<ConstImpl>;
726
    using children_view = detail::children_view_<Impl>;
727
    using const_children_view = detail::children_view_<ConstImpl>;
728

729
    /** get an iterator to the first child */
730
    template<class U=Impl>
731
    C4_ALWAYS_INLINE auto begin() RYML_NOEXCEPT -> _C4_IF_MUTABLE(iterator) { _C4RR(); return iterator(tree__, tree__->first_child(id__)); }
96✔
732
    /** get an iterator to the first child */
733
    C4_ALWAYS_INLINE const_iterator begin() const RYML_NOEXCEPT { _C4RR(); return const_iterator(tree_, tree_->first_child(id_)); }
2,259,976✔
734
    /** get an iterator to the first child */
735
    C4_ALWAYS_INLINE const_iterator cbegin() const RYML_NOEXCEPT { _C4RR(); return const_iterator(tree_, tree_->first_child(id_)); }
128✔
736

737
    /** get an iterator to after the last child */
738
    template<class U=Impl>
739
    C4_ALWAYS_INLINE auto end() RYML_NOEXCEPT -> _C4_IF_MUTABLE(iterator) { _C4RR(); return iterator(tree__, NONE); }
144✔
740
    /** get an iterator to after the last child */
741
    C4_ALWAYS_INLINE const_iterator end() const RYML_NOEXCEPT { _C4RR(); return const_iterator(tree_, NONE); }
2,501,804✔
742
    /** get an iterator to after the last child */
743
    C4_ALWAYS_INLINE const_iterator cend() const RYML_NOEXCEPT { _C4RR(); return const_iterator(tree_, tree_->first_child(id_)); }
48✔
744

745
    /** get an iterable view over children */
746
    template<class U=Impl>
747
    C4_ALWAYS_INLINE auto children() RYML_NOEXCEPT -> _C4_IF_MUTABLE(children_view) { _C4RR(); return children_view(begin(), end()); }
188✔
748
    /** get an iterable view over children */
749
    C4_ALWAYS_INLINE const_children_view children() const RYML_NOEXCEPT { _C4RR(); return const_children_view(begin(), end()); }
3,952,676✔
750
    /** get an iterable view over children */
751
    C4_ALWAYS_INLINE const_children_view cchildren() const RYML_NOEXCEPT { _C4RR(); return const_children_view(begin(), end()); }
796✔
752

753
    /** get an iterable view over all siblings (including the calling node) */
754
    template<class U=Impl>
755
    C4_ALWAYS_INLINE auto siblings() RYML_NOEXCEPT -> _C4_IF_MUTABLE(children_view)
756
    {
757
        _C4RR();
80✔
758
        NodeData const *nd = tree__->get(id__);
32✔
759
        return (nd->m_parent != NONE) ? // does it have a parent?
32✔
760
            children_view(iterator(tree__, tree_->get(nd->m_parent)->m_first_child), iterator(tree__, NONE))
16✔
761
            :
762
            children_view(end(), end());
72✔
763
    }
764
    /** get an iterable view over all siblings (including the calling node) */
765
    C4_ALWAYS_INLINE const_children_view siblings() const RYML_NOEXCEPT
766
    {
767
        _C4RR();
1,131,264✔
768
        NodeData const *nd = tree_->get(id_);
565,600✔
769
        return (nd->m_parent != NONE) ? // does it have a parent?
565,600✔
770
            const_children_view(const_iterator(tree_, tree_->get(nd->m_parent)->m_first_child), const_iterator(tree_, NONE))
496,504✔
771
            :
772
            const_children_view(end(), end());
1,165,748✔
773
    }
774
    /** get an iterable view over all siblings (including the calling node) */
775
    C4_ALWAYS_INLINE const_children_view csiblings() const RYML_NOEXCEPT { return siblings(); }
64✔
776

777
    /** visit every child node calling fn(node) */
778
    template<class Visitor>
779
    bool visit(Visitor fn, id_type indentation_level=0, bool skip_root=true) const RYML_NOEXCEPT
780
    {
781
        _C4RR();
782
        return detail::_visit(*(ConstImpl const*)this, fn, indentation_level, skip_root);
783
    }
784
    /** visit every child node calling fn(node) */
785
    template<class Visitor, class U=Impl>
786
    auto visit(Visitor fn, id_type indentation_level=0, bool skip_root=true) RYML_NOEXCEPT
787
        -> _C4_IF_MUTABLE(bool)
788
    {
789
        _C4RR();
790
        return detail::_visit(*(Impl*)this, fn, indentation_level, skip_root);
791
    }
792

793
    /** visit every child node calling fn(node, level) */
794
    template<class Visitor>
795
    bool visit_stacked(Visitor fn, id_type indentation_level=0, bool skip_root=true) const RYML_NOEXCEPT
796
    {
797
        _C4RR();
798
        return detail::_visit_stacked(*(ConstImpl const*)this, fn, indentation_level, skip_root);
799
    }
800
    /** visit every child node calling fn(node, level) */
801
    template<class Visitor, class U=Impl>
802
    auto visit_stacked(Visitor fn, id_type indentation_level=0, bool skip_root=true) RYML_NOEXCEPT
803
        -> _C4_IF_MUTABLE(bool)
804
    {
805
        _C4RR();
806
        return detail::_visit_stacked(*(Impl*)this, fn, indentation_level, skip_root);
807
    }
808

809
    /** @} */
810

811
    #if defined(__clang__)
812
    #   pragma clang diagnostic pop
813
    #elif defined(__GNUC__)
814
    #   pragma GCC diagnostic pop
815
    #endif
816

817
    #undef _C4_IF_MUTABLE
818
    #undef _C4RR
819
    #undef tree_
820
    #undef tree__
821
    #undef id_
822
    #undef id__
823

824
    C4_SUPPRESS_WARNING_GCC_CLANG_POP
825
};
826
} // detail
827

828

829
//-----------------------------------------------------------------------------
830
//-----------------------------------------------------------------------------
831
//-----------------------------------------------------------------------------
832
/** Holds a pointer to an existing tree, and a node id. It can be used
833
 * only to read from the tree.
834
 *
835
 * @warning The lifetime of the tree must be larger than that of this
836
 * object. It is up to the user to ensure that this happens. */
837
class RYML_EXPORT ConstNodeRef : public detail::RoNodeMethods<ConstNodeRef, ConstNodeRef> // NOLINT
838
{
839
public:
840

841
    using tree_type = Tree const;
842

843
public:
844

845
    Tree const* C4_RESTRICT m_tree;
846
    id_type m_id;
847

848
    friend NodeRef;
849
    friend struct detail::RoNodeMethods<ConstNodeRef, ConstNodeRef>;
850

851
public:
852

853
    /** @name construction */
854
    /** @{ */
855

856
    ConstNodeRef() noexcept : m_tree(nullptr), m_id(NONE) {}
128✔
857
    ConstNodeRef(Tree const &t) noexcept : m_tree(&t), m_id(t .root_id()) {}
368✔
858
    ConstNodeRef(Tree const *t) noexcept : m_tree(t ), m_id(t->root_id()) {}
64✔
859
    ConstNodeRef(Tree const *t, id_type id) noexcept : m_tree(t), m_id(id) {}
16,126,544✔
860
    ConstNodeRef(std::nullptr_t) noexcept : m_tree(nullptr), m_id(NONE) {}
861

862
    ConstNodeRef(ConstNodeRef const&) noexcept = default;
863
    ConstNodeRef(ConstNodeRef     &&) noexcept = default;
864

865
    inline ConstNodeRef(NodeRef const&) noexcept;
866
    inline ConstNodeRef(NodeRef     &&) noexcept;
867

868
    /** @} */
869

870
public:
871

872
    /** @name assignment */
873
    /** @{ */
874

875
    ConstNodeRef& operator= (std::nullptr_t) noexcept { m_tree = nullptr; m_id = NONE; return *this; }
876

877
    ConstNodeRef& operator= (ConstNodeRef const&) noexcept = default;
878
    ConstNodeRef& operator= (ConstNodeRef     &&) noexcept = default;
879

880
    ConstNodeRef& operator= (NodeRef const&) noexcept;
881
    ConstNodeRef& operator= (NodeRef     &&) noexcept;
882

883

884
    /** @} */
885

886
public:
887

888
    /** @name state queries
889
     *
890
     * see @ref NodeRef for an explanation on what these states mean */
891
    /** @{ */
892

893
    C4_ALWAYS_INLINE bool invalid() const noexcept { return (!m_tree) || (m_id == NONE); }
586,592✔
894
    /** because a ConstNodeRef cannot be used to write to the tree,
895
     * readable() has the same meaning as !invalid() */
896
    C4_ALWAYS_INLINE bool readable() const noexcept { return m_tree != nullptr && m_id != NONE; }
61,918,648✔
897
    /** because a ConstNodeRef cannot be used to write to the tree, it can never be a seed.
898
     * This method is provided for API equivalence between ConstNodeRef and NodeRef. */
899
    constexpr static C4_ALWAYS_INLINE bool is_seed() noexcept { return false; }
40✔
900

901
    RYML_DEPRECATED("use one of readable(), is_seed() or !invalid()") bool valid() const noexcept { return m_tree != nullptr && m_id != NONE; }
902

903
    /** @} */
904

905
public:
906

907
    /** @name member getters */
908
    /** @{ */
909

910
    C4_ALWAYS_INLINE Tree const* tree() const noexcept { return m_tree; }
109,824✔
911
    C4_ALWAYS_INLINE id_type id() const noexcept { return m_id; }
4,698,056✔
912

913
    /** @} */
914

915
public:
916

917
    /** @name comparisons */
918
    /** @{ */
919

920
    C4_ALWAYS_INLINE bool operator== (ConstNodeRef const& that) const RYML_NOEXCEPT { return that.m_tree == m_tree && m_id == that.m_id; }
112✔
921
    C4_ALWAYS_INLINE bool operator!= (ConstNodeRef const& that) const RYML_NOEXCEPT { return ! this->operator== (that); }
32✔
922

923
    /** @cond dev */
924
    RYML_DEPRECATED("use invalid()")  bool operator== (std::nullptr_t) const noexcept { return m_tree == nullptr || m_id == NONE; }
925
    RYML_DEPRECATED("use !invalid()") bool operator!= (std::nullptr_t) const noexcept { return !(m_tree == nullptr || m_id == NONE); }
384✔
926

927
    RYML_DEPRECATED("use (this->val() == s)") bool operator== (csubstr s) const RYML_NOEXCEPT { RYML_ASSERT(m_tree); _RYML_CB_ASSERT(m_tree->m_callbacks, m_id != NONE); return m_tree->val(m_id) == s; }
1,520✔
928
    RYML_DEPRECATED("use (this->val() != s)") bool operator!= (csubstr s) const RYML_NOEXCEPT { RYML_ASSERT(m_tree); _RYML_CB_ASSERT(m_tree->m_callbacks, m_id != NONE); return m_tree->val(m_id) != s; }
256✔
929
    /** @endcond */
930

931
    /** @} */
932

933
};
934

935

936
//-----------------------------------------------------------------------------
937
//-----------------------------------------------------------------------------
938
//-----------------------------------------------------------------------------
939

940
// NOLINTBEGIN(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator)
941

942
/** A reference to a node in an existing yaml tree, offering a more
943
 * convenient API than the index-based API used in the tree.
944
 *
945
 * Unlike its imutable ConstNodeRef peer, a NodeRef can be used to
946
 * mutate the tree, both by writing to existing nodes and by creating
947
 * new nodes to subsequently write to. Semantically, a NodeRef
948
 * object can be in one of three states:
949
 *
950
 * ```text
951
 * invalid  := not pointing at anything
952
 * readable := points at an existing tree/node
953
 * seed     := points at an existing tree, and the node
954
 *             may come to exist, if we write to it.
955
 * ```
956
 *
957
 * So both `readable` and `seed` are states where the node is also `valid`.
958
 *
959
 * ```cpp
960
 * Tree t = parse_in_arena("{a: b}");
961
 * NodeRef invalid; // not pointing at anything.
962
 * NodeRef readable = t["a"]; // also valid, because "a" exists
963
 * NodeRef seed = t["none"]; // also valid, but is seed because "none" is not in the map
964
 * ```
965
 *
966
 * When the object is in seed state, using it to read from the tree is
967
 * UB. The seed node can be used to write to the tree, provided that
968
 * its create() method is called prior to writing, which happens in
969
 * most modifying methods in NodeRef.
970
 *
971
 * It is the owners's responsibility to verify that an existing
972
 * node is readable before subsequently using it to read from the
973
 * tree.
974
 *
975
 * @warning The lifetime of the tree must be larger than that of this
976
 * object. It is up to the user to ensure that this happens.
977
 */
978
class RYML_EXPORT NodeRef : public detail::RoNodeMethods<NodeRef, ConstNodeRef> // NOLINT
979
{
980
public:
981

982
    using tree_type = Tree;
983
    using base_type = detail::RoNodeMethods<NodeRef, ConstNodeRef>;
984

985
private:
986

987
    Tree *C4_RESTRICT m_tree;
988
    id_type m_id;
989

990
    /** This member is used to enable lazy operator[] writing. When a child
991
     * with a key or index is not found, m_id is set to the id of the parent
992
     * and the asked-for key or index are stored in this member until a write
993
     * does happen. Then it is given as key or index for creating the child.
994
     * When a key is used, the csubstr stores it (so the csubstr's string is
995
     * non-null and the csubstr's size is different from NONE). When an index is
996
     * used instead, the csubstr's string is set to null, and only the csubstr's
997
     * size is set to a value different from NONE. Otherwise, when operator[]
998
     * does find the child then this member is empty: the string is null and
999
     * the size is NONE. */
1000
    csubstr m_seed;
1001

1002
    friend ConstNodeRef;
1003
    friend struct detail::RoNodeMethods<NodeRef, ConstNodeRef>;
1004

1005
    // require valid: a helper macro, undefined at the end
1006
    #define _C4RR()                                                         \
1007
        RYML_ASSERT(m_tree != nullptr);                                     \
1008
        _RYML_CB_ASSERT(m_tree->m_callbacks, m_id != NONE && !is_seed())
1009
    // require id: a helper macro, undefined at the end
1010
    #define _C4RID()                                                        \
1011
        RYML_ASSERT(m_tree != nullptr);                                     \
1012
        _RYML_CB_ASSERT(m_tree->m_callbacks, m_id != NONE)
1013

1014
public:
1015

1016
    /** @name construction */
1017
    /** @{ */
1018

1019
    NodeRef() noexcept : m_tree(nullptr), m_id(NONE), m_seed() { _clear_seed(); }
48✔
1020
    NodeRef(Tree &t) noexcept : m_tree(&t), m_id(t .root_id()), m_seed() { _clear_seed(); }
120✔
1021
    NodeRef(Tree *t) noexcept : m_tree(t ), m_id(t->root_id()), m_seed() { _clear_seed(); }
176✔
1022
    NodeRef(Tree *t, id_type id) noexcept : m_tree(t), m_id(id), m_seed() { _clear_seed(); }
229,712✔
1023
    NodeRef(Tree *t, id_type id, id_type seed_pos) noexcept : m_tree(t), m_id(id), m_seed() { m_seed.str = nullptr; m_seed.len = (size_t)seed_pos; }
528✔
1024
    NodeRef(Tree *t, id_type id, csubstr  seed_key) noexcept : m_tree(t), m_id(id), m_seed(seed_key) {}
1,496✔
1025
    NodeRef(std::nullptr_t) noexcept : m_tree(nullptr), m_id(NONE), m_seed() {}
1026

1027
    void _clear_seed() noexcept { /*do the following manually or an assert is triggered: */ m_seed.str = nullptr; m_seed.len = npos; }
230,056✔
1028

1029
    /** @} */
1030

1031
public:
1032

1033
    /** @name assignment */
1034
    /** @{ */
1035

1036
    NodeRef(NodeRef const&) noexcept = default;
1037
    NodeRef(NodeRef     &&) noexcept = default;
1038

1039
    NodeRef& operator= (NodeRef const&) noexcept = default;
1040
    NodeRef& operator= (NodeRef     &&) noexcept = default;
1041

1042
    /** @} */
1043

1044
public:
1045

1046
    /** @name state_queries
1047
     * @{ */
1048

1049
    /** true if the object is not referring to any existing or seed node. @see the doc for @ref NodeRef */
1050
    bool invalid() const noexcept { return m_tree == nullptr || m_id == NONE; }
1,648✔
1051
    /** true if the object is not invalid and in seed state. @see the doc for @ref NodeRef */
1052
    bool is_seed() const noexcept { return (m_tree != nullptr && m_id != NONE) && (m_seed.str != nullptr || m_seed.len != (size_t)NONE); }
115,656✔
1053
    /** true if the object is not invalid and not in seed state. @see the doc for @ref NodeRef */
1054
    bool readable() const noexcept { return (m_tree != nullptr && m_id != NONE) && (m_seed.str == nullptr && m_seed.len == (size_t)NONE); }
210,912✔
1055

1056
    RYML_DEPRECATED("use one of readable(), is_seed() or !invalid()") inline bool valid() const { return m_tree != nullptr && m_id != NONE; }
1057

1058
    /** @} */
1059

1060
public:
1061

1062
    /** @name comparisons */
1063
    /** @{ */
1064

1065
    bool operator== (NodeRef const& that) const
256✔
1066
    {
1067
        if(m_tree == that.m_tree && m_id == that.m_id)
256✔
1068
        {
1069
            bool seed = is_seed();
192✔
1070
            if(seed == that.is_seed())
192✔
1071
            {
1072
                if(seed)
192✔
1073
                {
1074
                    return (m_seed.len == that.m_seed.len)
96✔
1075
                        && (m_seed.str == that.m_seed.str
112✔
1076
                            || m_seed == that.m_seed); // do strcmp only in the last resort
128✔
1077
                }
1078
                return true;
96✔
1079
            }
1080
        }
1081
        return false;
64✔
1082
    }
1083
    bool operator!= (NodeRef const& that) const { return ! this->operator==(that); }
128✔
1084

1085
    bool operator== (ConstNodeRef const& that) const { return m_tree == that.m_tree && m_id == that.m_id && !is_seed(); }
232✔
1086
    bool operator!= (ConstNodeRef const& that) const { return ! this->operator==(that); }
8✔
1087

1088
    /** @cond dev */
1089
    RYML_DEPRECATED("use !readable()") bool operator== (std::nullptr_t) const { return m_tree == nullptr || m_id == NONE || is_seed(); }
1090
    RYML_DEPRECATED("use readable()")  bool operator!= (std::nullptr_t) const { return !(m_tree == nullptr || m_id == NONE || is_seed()); }
1091

1092
    RYML_DEPRECATED("use `this->val() == s`") bool operator== (csubstr s) const { _C4RR(); _RYML_CB_ASSERT(m_tree->m_callbacks, has_val()); return m_tree->val(m_id) == s; }
1,584✔
1093
    RYML_DEPRECATED("use `this->val() != s`") bool operator!= (csubstr s) const { _C4RR(); _RYML_CB_ASSERT(m_tree->m_callbacks, has_val()); return m_tree->val(m_id) != s; }
1094
    /** @endcond */
1095

1096
public:
1097

1098
    /** @name node_property_getters
1099
     * @{ */
1100

1101
    C4_ALWAYS_INLINE Tree * tree() noexcept { return m_tree; }
121,072✔
1102
    C4_ALWAYS_INLINE Tree const* tree() const noexcept { return m_tree; }
88✔
1103

1104
    C4_ALWAYS_INLINE id_type id() const noexcept { return m_id; }
168,792✔
1105

1106
    /** @} */
1107

1108
public:
1109

1110
    /** @name node_modifiers */
1111
    /** @{ */
1112

1113
    void create() { _apply_seed(); }
16✔
1114

1115
    void change_type(NodeType t) { _C4RR(); m_tree->change_type(m_id, t); }
192✔
1116

1117
    void set_type(NodeType t) { _apply_seed(); m_tree->_set_flags(m_id, t); }
272✔
1118
    void set_key(csubstr key) { _apply_seed(); m_tree->_set_key(m_id, key); }
48✔
1119
    void set_val(csubstr val) { _apply_seed(); m_tree->_set_val(m_id, val); }
1,416✔
1120
    void set_key_tag(csubstr key_tag) { _apply_seed(); m_tree->set_key_tag(m_id, key_tag); }
1121
    void set_val_tag(csubstr val_tag) { _apply_seed(); m_tree->set_val_tag(m_id, val_tag); }
8✔
1122
    void set_key_anchor(csubstr key_anchor) { _apply_seed(); m_tree->set_key_anchor(m_id, key_anchor); }
32✔
1123
    void set_val_anchor(csubstr val_anchor) { _apply_seed(); m_tree->set_val_anchor(m_id, val_anchor); }
32✔
1124
    void set_key_ref(csubstr key_ref) { _apply_seed(); m_tree->set_key_ref(m_id, key_ref); }
56✔
1125
    void set_val_ref(csubstr val_ref) { _apply_seed(); m_tree->set_val_ref(m_id, val_ref); }
112✔
1126

1127
    void set_container_style(NodeType_e style) { _C4RR(); m_tree->set_container_style(m_id, style); }
320✔
1128
    void set_key_style(NodeType_e style) { _C4RR(); m_tree->set_key_style(m_id, style); }
640✔
1129
    void set_val_style(NodeType_e style) { _C4RR(); m_tree->set_val_style(m_id, style); }
1,232✔
1130

1131
public:
1132

1133
    void clear()
8✔
1134
    {
1135
        if(is_seed())
8✔
1136
            return;
×
1137
        m_tree->remove_children(m_id);
8✔
1138
        m_tree->_clear(m_id);
8✔
1139
    }
1140

1141
    void clear_key()
1142
    {
1143
        if(is_seed())
1144
            return;
1145
        m_tree->_clear_key(m_id);
1146
    }
1147

1148
    void clear_val()
1149
    {
1150
        if(is_seed())
1151
            return;
1152
        m_tree->_clear_val(m_id);
1153
    }
1154

1155
    void clear_children()
1156
    {
1157
        if(is_seed())
1158
            return;
1159
        m_tree->remove_children(m_id);
1160
    }
1161

1162
    void operator= (NodeType_e t)
1163
    {
1164
        _apply_seed();
1165
        m_tree->_add_flags(m_id, t);
1166
    }
1167

1168
    void operator|= (NodeType_e t)
8,384✔
1169
    {
1170
        _apply_seed();
8,384✔
1171
        m_tree->_add_flags(m_id, t);
8,384✔
1172
    }
8,368✔
1173

1174
    void operator= (NodeInit const& v)
16✔
1175
    {
1176
        _apply_seed();
16✔
1177
        _apply(v);
16✔
1178
    }
16✔
1179

1180
    void operator= (NodeScalar const& v)
1181
    {
1182
        _apply_seed();
1183
        _apply(v);
1184
    }
1185

1186
    void operator= (std::nullptr_t)
16✔
1187
    {
1188
        _apply_seed();
16✔
1189
        _apply(csubstr{});
16✔
1190
    }
16✔
1191

1192
    void operator= (csubstr v)
264✔
1193
    {
1194
        _apply_seed();
264✔
1195
        _apply(v);
264✔
1196
    }
264✔
1197

1198
    template<size_t N>
1199
    void operator= (const char (&v)[N])
952✔
1200
    {
1201
        _apply_seed();
952✔
1202
        csubstr sv;
952✔
1203
        sv.assign<N>(v);
1204
        _apply(sv);
952✔
1205
    }
952✔
1206

1207
    /** @} */
1208

1209
public:
1210

1211
    /** @name serialization */
1212
    /** @{ */
1213

1214
    /** serialize a variable to the arena */
1215
    template<class T>
1216
    csubstr to_arena(T const& C4_RESTRICT s)
80✔
1217
    {
1218
        RYML_ASSERT(m_tree); // no need for valid or readable
80✔
1219
        return m_tree->to_arena(s);
80✔
1220
    }
1221

1222
    template<class T>
1223
    size_t set_key_serialized(T const& C4_RESTRICT k)
1,752✔
1224
    {
1225
        _apply_seed();
1,752✔
1226
        csubstr s = m_tree->to_arena(k);
1,752✔
1227
        m_tree->_set_key(m_id, s);
1,752✔
1228
        return s.len;
1,752✔
1229
    }
1230
    size_t set_key_serialized(std::nullptr_t)
1231
    {
1232
        _apply_seed();
1233
        m_tree->_set_key(m_id, csubstr{});
1234
        return 0;
1235
    }
1236

1237
    template<class T>
1238
    size_t set_val_serialized(T const& C4_RESTRICT v)
7,136✔
1239
    {
1240
        _apply_seed();
7,136✔
1241
        csubstr s = m_tree->to_arena(v);
7,136✔
1242
        m_tree->_set_val(m_id, s);
7,136✔
1243
        return s.len;
7,128✔
1244
    }
1245
    size_t set_val_serialized(std::nullptr_t)
16✔
1246
    {
1247
        _apply_seed();
16✔
1248
        m_tree->_set_val(m_id, csubstr{});
16✔
1249
        return 0;
16✔
1250
    }
1251

1252
    /** encode a blob as base64 into the tree's arena, then assign the
1253
     * result to the node's key
1254
     * @return the size of base64-encoded blob */
1255
    size_t set_key_serialized(fmt::const_base64_wrapper w);
1256
    /** encode a blob as base64 into the tree's arena, then assign the
1257
     * result to the node's val
1258
     * @return the size of base64-encoded blob */
1259
    size_t set_val_serialized(fmt::const_base64_wrapper w);
1260

1261
    /** serialize a variable, then assign the result to the node's val */
1262
    NodeRef& operator<< (csubstr s)
272✔
1263
    {
1264
        // this overload is needed to prevent ambiguity (there's also
1265
        // operator<< for writing a substr to a stream)
1266
        _apply_seed();
272✔
1267
        write(this, s);
272✔
1268
        _RYML_CB_ASSERT(m_tree->m_callbacks, val() == s);
408✔
1269
        return *this;
272✔
1270
    }
1271

1272
    template<class T>
1273
    NodeRef& operator<< (T const& C4_RESTRICT v)
7,560✔
1274
    {
1275
        _apply_seed();
7,560✔
1276
        write(this, v);
696✔
1277
        return *this;
7,552✔
1278
    }
1279

1280
    /** serialize a variable, then assign the result to the node's key */
1281
    template<class T>
1282
    NodeRef& operator<< (Key<const T> const& C4_RESTRICT v)
1,736✔
1283
    {
1284
        _apply_seed();
1,736✔
1285
        set_key_serialized(v.k);
1,736✔
1286
        return *this;
1,736✔
1287
    }
1288

1289
    /** serialize a variable, then assign the result to the node's key */
1290
    template<class T>
1291
    NodeRef& operator<< (Key<T> const& C4_RESTRICT v)
16✔
1292
    {
1293
        _apply_seed();
16✔
1294
        set_key_serialized(v.k);
16✔
1295
        return *this;
16✔
1296
    }
1297

1298
    NodeRef& operator<< (Key<fmt::const_base64_wrapper> w)
32✔
1299
    {
1300
        set_key_serialized(w.wrapper);
32✔
1301
        return *this;
32✔
1302
    }
1303

1304
    NodeRef& operator<< (fmt::const_base64_wrapper w)
48✔
1305
    {
1306
        set_val_serialized(w);
48✔
1307
        return *this;
48✔
1308
    }
1309

1310
    /** @} */
1311

1312
private:
1313

1314
    void _apply_seed()
30,056✔
1315
    {
1316
        _C4RID();
30,056✔
1317
        if(m_seed.str) // we have a seed key: use it to create the new child
30,056✔
1318
        {
1319
            m_id = m_tree->append_child(m_id);
1,104✔
1320
            m_tree->_set_key(m_id, m_seed);
1,104✔
1321
            m_seed.str = nullptr;
1,104✔
1322
            m_seed.len = (size_t)NONE;
1,104✔
1323
        }
1324
        else if(m_seed.len != (size_t)NONE) // we have a seed index: create a child at that position
28,952✔
1325
        {
1326
            _RYML_CB_ASSERT(m_tree->m_callbacks, (size_t)m_tree->num_children(m_id) == m_seed.len);
320✔
1327
            m_id = m_tree->append_child(m_id);
320✔
1328
            m_seed.str = nullptr;
320✔
1329
            m_seed.len = (size_t)NONE;
320✔
1330
        }
1331
        else
1332
        {
1333
            _RYML_CB_ASSERT(m_tree->m_callbacks, readable());
28,632✔
1334
        }
1335
    }
30,056✔
1336

1337
    void _apply(csubstr v)
1,232✔
1338
    {
1339
        m_tree->_set_val(m_id, v);
1,232✔
1340
    }
1,232✔
1341

1342
    void _apply(NodeScalar const& v)
1343
    {
1344
        m_tree->_set_val(m_id, v);
1345
    }
1346

1347
    void _apply(NodeInit const& i)
1,032✔
1348
    {
1349
        m_tree->_set(m_id, i);
1,032✔
1350
    }
1,032✔
1351

1352
public:
1353

1354
    /** @name modification of hierarchy */
1355
    /** @{ */
1356

1357
    NodeRef insert_child(NodeRef after)
136✔
1358
    {
1359
        _C4RR();
136✔
1360
        _RYML_CB_ASSERT(m_tree->m_callbacks, after.m_tree == m_tree);
136✔
1361
        NodeRef r(m_tree, m_tree->insert_child(m_id, after.m_id));
272✔
1362
        return r;
136✔
1363
    }
1364

1365
    NodeRef insert_child(NodeInit const& i, NodeRef after)
104✔
1366
    {
1367
        _C4RR();
104✔
1368
        _RYML_CB_ASSERT(m_tree->m_callbacks, after.m_tree == m_tree);
104✔
1369
        NodeRef r(m_tree, m_tree->insert_child(m_id, after.m_id));
208✔
1370
        r._apply(i);
104✔
1371
        return r;
104✔
1372
    }
1373

1374
    NodeRef prepend_child()
64✔
1375
    {
1376
        _C4RR();
64✔
1377
        NodeRef r(m_tree, m_tree->insert_child(m_id, NONE));
128✔
1378
        return r;
64✔
1379
    }
1380

1381
    NodeRef prepend_child(NodeInit const& i)
56✔
1382
    {
1383
        _C4RR();
56✔
1384
        NodeRef r(m_tree, m_tree->insert_child(m_id, NONE));
112✔
1385
        r._apply(i);
56✔
1386
        return r;
56✔
1387
    }
1388

1389
    NodeRef append_child()
7,608✔
1390
    {
1391
        _C4RR();
7,608✔
1392
        NodeRef r(m_tree, m_tree->append_child(m_id));
15,216✔
1393
        return r;
7,608✔
1394
    }
1395

1396
    NodeRef append_child(NodeInit const& i)
720✔
1397
    {
1398
        _C4RR();
720✔
1399
        NodeRef r(m_tree, m_tree->append_child(m_id));
1,440✔
1400
        r._apply(i);
720✔
1401
        return r;
720✔
1402
    }
1403

1404
    NodeRef insert_sibling(ConstNodeRef const& after)
40✔
1405
    {
1406
        _C4RR();
40✔
1407
        _RYML_CB_ASSERT(m_tree->m_callbacks, after.m_tree == m_tree);
40✔
1408
        NodeRef r(m_tree, m_tree->insert_sibling(m_id, after.m_id));
80✔
1409
        return r;
40✔
1410
    }
1411

1412
    NodeRef insert_sibling(NodeInit const& i, ConstNodeRef const& after)
40✔
1413
    {
1414
        _C4RR();
40✔
1415
        _RYML_CB_ASSERT(m_tree->m_callbacks, after.m_tree == m_tree);
40✔
1416
        NodeRef r(m_tree, m_tree->insert_sibling(m_id, after.m_id));
80✔
1417
        r._apply(i);
40✔
1418
        return r;
40✔
1419
    }
1420

1421
    NodeRef prepend_sibling()
40✔
1422
    {
1423
        _C4RR();
40✔
1424
        NodeRef r(m_tree, m_tree->prepend_sibling(m_id));
80✔
1425
        return r;
40✔
1426
    }
1427

1428
    NodeRef prepend_sibling(NodeInit const& i)
40✔
1429
    {
1430
        _C4RR();
40✔
1431
        NodeRef r(m_tree, m_tree->prepend_sibling(m_id));
80✔
1432
        r._apply(i);
40✔
1433
        return r;
40✔
1434
    }
1435

1436
    NodeRef append_sibling()
40✔
1437
    {
1438
        _C4RR();
40✔
1439
        NodeRef r(m_tree, m_tree->append_sibling(m_id));
80✔
1440
        return r;
40✔
1441
    }
1442

1443
    NodeRef append_sibling(NodeInit const& i)
56✔
1444
    {
1445
        _C4RR();
56✔
1446
        NodeRef r(m_tree, m_tree->append_sibling(m_id));
112✔
1447
        r._apply(i);
56✔
1448
        return r;
56✔
1449
    }
1450

1451
public:
1452

1453
    void remove_child(NodeRef & child)
8✔
1454
    {
1455
        _C4RR();
8✔
1456
        _RYML_CB_ASSERT(m_tree->m_callbacks, has_child(child));
16✔
1457
        _RYML_CB_ASSERT(m_tree->m_callbacks, child.parent().id() == id());
16✔
1458
        m_tree->remove(child.id());
16✔
1459
        child.clear();
8✔
1460
    }
8✔
1461

1462
    //! remove the nth child of this node
1463
    void remove_child(id_type pos)
104✔
1464
    {
1465
        _C4RR();
104✔
1466
        _RYML_CB_ASSERT(m_tree->m_callbacks, pos >= 0 && pos < num_children());
104✔
1467
        id_type child = m_tree->child(m_id, pos);
104✔
1468
        _RYML_CB_ASSERT(m_tree->m_callbacks, child != NONE);
104✔
1469
        m_tree->remove(child);
104✔
1470
    }
104✔
1471

1472
    //! remove a child by name
1473
    void remove_child(csubstr key)
24✔
1474
    {
1475
        _C4RR();
24✔
1476
        id_type child = m_tree->find_child(m_id, key);
24✔
1477
        _RYML_CB_ASSERT(m_tree->m_callbacks, child != NONE);
24✔
1478
        m_tree->remove(child);
24✔
1479
    }
24✔
1480

1481
public:
1482

1483
    /** change the node's position within its parent, placing it after
1484
     * @p after. To move to the first position in the parent, simply
1485
     * pass an empty or default-constructed reference like this:
1486
     * `n.move({})`. */
1487
    void move(ConstNodeRef const& after)
32✔
1488
    {
1489
        _C4RR();
32✔
1490
        m_tree->move(m_id, after.m_id);
32✔
1491
    }
32✔
1492

1493
    /** move the node to a different @p parent (which may belong to a
1494
     * different tree), placing it after @p after. When the
1495
     * destination parent is in a new tree, then this node's tree
1496
     * pointer is reset to the tree of the parent node. */
1497
    void move(NodeRef const& parent, ConstNodeRef const& after)
56✔
1498
    {
1499
        _C4RR();
56✔
1500
        if(parent.m_tree == m_tree)
56✔
1501
        {
1502
            m_tree->move(m_id, parent.m_id, after.m_id);
24✔
1503
        }
1504
        else
1505
        {
1506
            parent.m_tree->move(m_tree, m_id, parent.m_id, after.m_id);
32✔
1507
            m_tree = parent.m_tree;
32✔
1508
        }
1509
    }
56✔
1510

1511
    /** duplicate the current node somewhere within its parent, and
1512
     * place it after the node @p after. To place into the first
1513
     * position of the parent, simply pass an empty or
1514
     * default-constructed reference like this: `n.move({})`. */
1515
    NodeRef duplicate(ConstNodeRef const& after) const
24✔
1516
    {
1517
        _C4RR();
24✔
1518
        _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree == after.m_tree || after.m_id == NONE);
24✔
1519
        id_type dup = m_tree->duplicate(m_id, m_tree->parent(m_id), after.m_id);
24✔
1520
        NodeRef r(m_tree, dup);
24✔
1521
        return r;
24✔
1522
    }
1523

1524
    /** duplicate the current node somewhere into a different @p parent
1525
     * (possibly from a different tree), and place it after the node
1526
     * @p after. To place into the first position of the parent,
1527
     * simply pass an empty or default-constructed reference like
1528
     * this: `n.move({})`. */
1529
    NodeRef duplicate(NodeRef const& parent, ConstNodeRef const& after) const
24✔
1530
    {
1531
        _C4RR();
24✔
1532
        _RYML_CB_ASSERT(m_tree->m_callbacks, parent.m_tree == after.m_tree || after.m_id == NONE);
24✔
1533
        if(parent.m_tree == m_tree)
24✔
1534
        {
1535
            id_type dup = m_tree->duplicate(m_id, parent.m_id, after.m_id);
×
1536
            NodeRef r(m_tree, dup);
×
1537
            return r;
×
1538
        }
1539
        else
1540
        {
1541
            id_type dup = parent.m_tree->duplicate(m_tree, m_id, parent.m_id, after.m_id);
24✔
1542
            NodeRef r(parent.m_tree, dup);
24✔
1543
            return r;
24✔
1544
        }
1545
    }
1546

1547
    void duplicate_children(NodeRef const& parent, ConstNodeRef const& after) const
1548
    {
1549
        _C4RR();
1550
        _RYML_CB_ASSERT(m_tree->m_callbacks, parent.m_tree == after.m_tree);
1551
        if(parent.m_tree == m_tree)
1552
        {
1553
            m_tree->duplicate_children(m_id, parent.m_id, after.m_id);
1554
        }
1555
        else
1556
        {
1557
            parent.m_tree->duplicate_children(m_tree, m_id, parent.m_id, after.m_id);
1558
        }
1559
    }
1560

1561
    /** @} */
1562

1563
#undef _C4RR
1564
#undef _C4RID
1565
};
1566

1567
// NOLINTEND(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator)
1568

1569

1570
//-----------------------------------------------------------------------------
1571

1572
inline ConstNodeRef::ConstNodeRef(NodeRef const& that) noexcept
1,344✔
1573
    : m_tree(that.m_tree)
1,344✔
1574
    , m_id(!that.is_seed() ? that.id() : (id_type)NONE)
2,616✔
1575
{
1576
}
1,344✔
1577

1578
inline ConstNodeRef::ConstNodeRef(NodeRef && that) noexcept // NOLINT
102,408✔
1579
    : m_tree(that.m_tree)
102,408✔
1580
    , m_id(!that.is_seed() ? that.id() : (id_type)NONE)
204,800✔
1581
{
1582
}
102,408✔
1583

1584

1585
inline ConstNodeRef& ConstNodeRef::operator= (NodeRef const& that) noexcept
16✔
1586
{
1587
    m_tree = (that.m_tree);
16✔
1588
    m_id = (!that.is_seed() ? that.id() : (id_type)NONE);
32✔
1589
    return *this;
16✔
1590
}
1591

1592
inline ConstNodeRef& ConstNodeRef::operator= (NodeRef && that) noexcept // NOLINT
8✔
1593
{
1594
    m_tree = (that.m_tree);
8✔
1595
    m_id = (!that.is_seed() ? that.id() : (id_type)NONE);
8✔
1596
    return *this;
8✔
1597
}
1598

1599

1600
//-----------------------------------------------------------------------------
1601

1602
/** @addtogroup doc_serialization_helpers
1603
 *
1604
 * @{
1605
 */
1606

1607
template<class T>
1608
C4_ALWAYS_INLINE void write(NodeRef *n, T const& v)
272✔
1609
{
1610
    n->set_val_serialized(v);
7,136✔
1611
}
6,856✔
1612

1613
template<class T>
1614
C4_ALWAYS_INLINE bool read(ConstNodeRef const& C4_RESTRICT n, T *v)
1615
{
1616
    return read(n.m_tree, n.m_id, v);
10,152✔
1617
}
1618

1619
template<class T>
1620
C4_ALWAYS_INLINE bool read(NodeRef const& C4_RESTRICT n, T *v)
1621
{
1622
    return read(n.tree(), n.id(), v);
176✔
1623
}
1624

1625
template<class T>
1626
C4_ALWAYS_INLINE bool readkey(ConstNodeRef const& C4_RESTRICT n, T *v)
1627
{
1628
    return readkey(n.m_tree, n.m_id, v);
3,016✔
1629
}
1630

1631
template<class T>
1632
C4_ALWAYS_INLINE bool readkey(NodeRef const& C4_RESTRICT n, T *v)
1633
{
1634
    return readkey(n.tree(), n.id(), v);
1635
}
1636

1637
/** @} */
1638

1639
/** @} */
1640

1641

1642
} // namespace yml
1643
} // namespace c4
1644

1645

1646

1647
#ifdef __clang__
1648
#   pragma clang diagnostic pop
1649
#elif defined(__GNUC__)
1650
#   pragma GCC diagnostic pop
1651
#elif defined(_MSC_VER)
1652
#   pragma warning(pop)
1653
#endif
1654

1655
#endif /* _C4_YML_NODE_HPP_ */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc