rapidyaml 0.15.2
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
tree.hpp
Go to the documentation of this file.
1#ifndef C4_YML_TREE_HPP_
2#define C4_YML_TREE_HPP_
3
4/** @file tree.hpp */
5
6#ifndef C4_ERROR_HPP_
7#include "c4/error.hpp"
8#endif
9#ifndef C4_LANGUAGE_HPP_
10#include "c4/language.hpp"
11#endif
12#ifndef C4_YML_FWD_HPP_
13#include "c4/yml/fwd.hpp"
14#endif
15#ifndef C4_YML_COMMON_HPP_
16#include "c4/yml/common.hpp"
17#endif
18#ifndef C4_YML_NODE_TYPE_HPP_
19#include "c4/yml/node_type.hpp"
20#endif
21#ifndef C4_YML_TAG_HPP_
22#include "c4/yml/tag.hpp"
23#endif
24#ifndef C4_YML_ERROR_HPP_
25#include "c4/yml/error.hpp"
26#endif
27#ifndef C4_YML_SCALAR_CHARCONV_HPP_
29#endif
30
31#include <math.h>
32#include <limits.h>
33
34
35C4_SUPPRESS_WARNING_PUSH
36C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
37C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
38C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
39#if defined(__GNUC__) && (__GNUC__ >= 6)
40C4_SUPPRESS_WARNING_GCC("-Wnull-dereference")
41#endif
42C4_SUPPRESS_WARNING_CLANG("-Wnull-dereference")
43C4_SUPPRESS_WARNING_CLANG("-Wtautological-compare")
44C4_SUPPRESS_WARNING_MSVC(4127) // conditional expression is constant
45C4_SUPPRESS_WARNING_MSVC(4296) // expression is always 'boolean_value'
46C4_SUPPRESS_WARNING_MSVC(4251) // needs to have dll-interface
47// NOLINTBEGIN(modernize-avoid-c-style-cast)
48
49
50namespace c4 {
51namespace yml {
52
53
54/** @cond dev */
55template<class T>
56using _is_string_nocvref = is_string<typename detail::_remove_cvref<T>::type>;
57/** @endcond */
58
59
60/** @addtogroup doc_serialization_tree_write_arena
61 *
62 * @{
63 */
64
65
66/** Serialize a scalar to the tree's arena. This is an implementation
67 * helper for @ref serialize_to_arena(), serializing through
68 * @ref scalar_serialize(). */
69template<class T>
70csubstr serialize_to_arena_scalar(Tree * tree, T const& scalar);
71
72
73/** Serialize a string type (as specified by @ref c4::is_string) to a
74 * tree's arena, ensuring that there is an entry for the string in the
75 * arena even if the string is empty. This is an implementation helper
76 * for @ref serialize_to_arena(), serializing through @ref
77 * scalar_serialize() and then ensuring that the serialized string
78 * will be placed in the arena, even if the string is zero-length. */
79RYML_EXPORT csubstr serialize_to_arena_str(Tree * tree, csubstr scalar);
80
81
82
83#if (C4_CPP >= 17) || defined(__DOXYGEN__)
84/** Serialize a scalar to a tree's arena, dispatching to either @ref
85 * serialize_to_arena_scalar() or @ref serialize_to_arena_str() when
86 * the type is a string according to @ref c4::is_string. This is the
87 * entry point for customizing how a scalar is serialized to a tree's
88 * arena. It is never needed for the user to call this function, and
89 * generally there is no reason for overriding this function for user
90 * types, unless it has specific requirements for the tree's arena, as
91 * happens for example with string types. For user string types,
92 * defining @ref c4::is_string is enough. For example:
93 *
94 * @note When using a standard older than C++17, `if constexpr` is not
95 * available, and the implementation reverts to SFINAE to achieve the
96 * compile-time dispatch.
97 *
98 * @code{c++}
99 * namespace foo {
100 * class MyStringType {...}; // an example of a user-defined string type
101 * // define conversion to/from substrings
102 * c4::yml::csubstr to_csubstr(MyStringType const& s) { return ...; }
103 * c4::yml::substr to_substr(MyStringType & s) { return ...; }
104 * } // namespace foo
105 * // tell ryml to treat this type as a string
106 * template<> struct c4::is_string<foo::MyStringType> : std::true_type {};
107 * @endcode */
108template<class T>
109C4_ALWAYS_INLINE csubstr serialize_to_arena(Tree * tree, T const& scalar)
110{
111 if constexpr (_is_string_nocvref<T>::value)
112 return serialize_to_arena_str(tree, to_csubstr(scalar));
113 else
114 return serialize_to_arena_scalar<T>(tree, scalar);
115}
116
117#else // pre-C++17: need to use SFINAE
118
119template<class T>
120C4_ALWAYS_INLINE auto serialize_to_arena(Tree * tree, T const& scalar)
121 -> typename std::enable_if<_is_string_nocvref<T>::value, csubstr>::type
122{
123 return serialize_to_arena_str(tree, to_csubstr(scalar));
124}
125
126template<class T>
127C4_ALWAYS_INLINE auto serialize_to_arena(Tree * tree, T const& scalar)
128 -> typename std::enable_if< ! _is_string_nocvref<T>::value, csubstr>::type
129{
130 return serialize_to_arena_scalar<T>(tree, scalar);
131}
132
133#endif // pre-C++17: need to use SFINAE
134
135/** implementation for null values */
136C4_ALWAYS_INLINE csubstr serialize_to_arena(Tree * C4_RESTRICT, std::nullptr_t /*scalar*/) noexcept
137{
138 return csubstr{};
139}
140
141/** @} */
142
143
144//-----------------------------------------------------------------------------
145// fwd decl: write/read, write_key/read_key
146
147/** @cond dev */
148
149template<class T> void write(Tree * tree, id_type id, T const& v);
150template<class T> void write_key(Tree *, id_type id, T const& v);
151
152template<class T> C4_NODISCARD C4_ALWAYS_INLINE ReadResult read(Tree const* tree, id_type id, T * v);
153template<class T> C4_NODISCARD C4_ALWAYS_INLINE ReadResult read_key(Tree const* tree, id_type id, T * v);
154template<class Wrapper> C4_NODISCARD C4_ALWAYS_INLINE ReadResult read(Tree const* tree, id_type id, Wrapper const& w);
155template<class Wrapper> C4_NODISCARD C4_ALWAYS_INLINE ReadResult read_key(Tree const* tree, id_type id, Wrapper const& w);
156/** @endcond */
157
158
159//-----------------------------------------------------------------------------
160//-----------------------------------------------------------------------------
161//-----------------------------------------------------------------------------
162
163/** @addtogroup doc_tree
164 *
165 * @{
166 */
167
168/** a node scalar is a csubstr, which may be tagged and anchored. */
170{
174
175public:
176
177 /// initialize as an empty scalar
178 NodeScalar() noexcept : tag(), scalar(), anchor() {} // NOLINT
179
180 /// initialize as an untagged scalar
181 template<size_t N>
182 NodeScalar(const char (&s)[N]) noexcept : tag(), scalar(s), anchor() {}
183 NodeScalar(csubstr s ) noexcept : tag(), scalar(s), anchor() {}
184
185 /// initialize as a tagged scalar
186 template<size_t N, size_t M>
187 NodeScalar(const char (&t)[N], const char (&s)[N]) noexcept : tag(t), scalar(s), anchor() {}
188 NodeScalar(csubstr t , csubstr s ) noexcept : tag(t), scalar(s), anchor() {}
189
190public:
191
192 ~NodeScalar() noexcept = default;
193 NodeScalar(NodeScalar &&) noexcept = default;
194 NodeScalar(NodeScalar const&) noexcept = default;
195 NodeScalar& operator= (NodeScalar &&) noexcept = default;
196 NodeScalar& operator= (NodeScalar const&) noexcept = default;
197
198public:
199
200 bool empty() const noexcept { return tag.empty() && scalar.empty() && anchor.empty(); }
201
202 void clear() noexcept { tag.clear(); scalar.clear(); anchor.clear(); }
203
205 {
206 csubstr trimmed = ref.begins_with('*') ? ref.sub(1) : ref;
207 anchor = trimmed;
208 if((!has_scalar) || !scalar.ends_with(trimmed))
209 scalar = ref;
210 }
211};
212static_assert(std::is_trivially_copyable<NodeScalar>::value, "must be trivially copyable");
213
214
215//-----------------------------------------------------------------------------
216//-----------------------------------------------------------------------------
217//-----------------------------------------------------------------------------
218
219/** @cond dev */ // LCOV_EXCL_START
220struct RYML_DEPRECATED("") NodeInit
221{
222
223 NodeType type;
224 NodeScalar key;
225 NodeScalar val;
226
227public:
228
229 /// initialize as an empty node
230 NodeInit() : type(NOTYPE), key(), val() {}
231 /// initialize as a typed node
232 NodeInit(type_bits t) : type(t), key(), val() {}
233 /// initialize as a sequence member
234 NodeInit(NodeScalar const& v) : type(VAL), key(), val(v) { _add_flags(); }
235 /// initialize as a sequence member with explicit type
236 NodeInit(NodeScalar const& v, type_bits t) : type(t|VAL), key(), val(v) { _add_flags(); }
237 /// initialize as a mapping member
238 NodeInit( NodeScalar const& k, NodeScalar const& v) : type(KEYVAL), key(k), val(v) { _add_flags(); }
239 /// initialize as a mapping member with explicit type
240 NodeInit(type_bits t, NodeScalar const& k, NodeScalar const& v) : type(t), key(k), val(v) { _add_flags(); }
241 /// initialize as a mapping member with explicit type (eg for SEQ or MAP)
242 NodeInit(type_bits t, NodeScalar const& k ) : type(t), key(k), val( ) { _add_flags(KEY); }
243
244public:
245
246 void clear()
247 {
248 type.clear();
249 key.clear();
250 val.clear();
251 }
252
253 void _add_flags(type_bits more_flags=0)
254 {
255 type = (type.m_bits|more_flags);
256 if( ! key.tag.empty())
257 type = (type.m_bits|KEYTAG);
258 if( ! val.tag.empty())
259 type = (type.m_bits|VALTAG);
260 if( ! key.anchor.empty())
261 type = (type.m_bits|KEYANCH);
262 if( ! val.anchor.empty())
263 type = (type.m_bits|VALANCH);
264 }
265
266 bool _check() const
267 {
268 // key cannot be empty
269 RYML_ASSERT_BASIC_(key.scalar.empty() == ((type & KEY) == 0));
270 // key tag cannot be empty
271 RYML_ASSERT_BASIC_(key.tag.empty() == ((type & KEYTAG) == 0));
272 // val may be empty even though VAL is set. But when VAL is not set, val must be empty
273 RYML_ASSERT_BASIC_(((type & VAL) != 0) || val.scalar.empty());
274 // val tag cannot be empty
275 RYML_ASSERT_BASIC_(val.tag.empty() == ((type & VALTAG) == 0));
276 return true;
277 }
278};
279/** @endcond */ // LCOV_EXCL_STOP
280
281
282//-----------------------------------------------------------------------------
283//-----------------------------------------------------------------------------
284//-----------------------------------------------------------------------------
285
286/** contains the data for each YAML node. */
300static_assert(std::is_trivially_copyable<NodeData>::value, "must be trivially copyable");
301
302
303//-----------------------------------------------------------------------------
304//-----------------------------------------------------------------------------
305//-----------------------------------------------------------------------------
306
308{
309public:
310
311 /** @name construction and assignment */
312 /** @{ */
313
315 Tree(Callbacks const& cb);
317 Tree(id_type node_capacity, size_t arena_capacity, Callbacks const& cb);
318
319 ~Tree() noexcept;
320
321 Tree(Tree const& that);
322 Tree(Tree && that) noexcept;
323
324 Tree& operator= (Tree const& that);
325 Tree& operator= (Tree && that) noexcept;
326
327 /** @} */
328
329public:
330
331 /** @name memory and sizing */
332 /** @{ */
333
334 void reserve(id_type node_capacity=RYML_DEFAULT_TREE_CAPACITY);
335
336 /** clear the tree and zero every node
337 * @note does NOT clear the arena
338 * @see clear_arena() */
339 void clear();
340 void clear_arena() { m_arena_pos = 0; }
341
342 /** Query for zero size. The tree can be empty only when constructed with explicitly zero-capacity. */
343 bool empty() const { return m_size == 0; }
344
345 id_type size() const { return m_size; }
346 id_type capacity() const { return m_cap; }
347 id_type slack() const { RYML_ASSERT_BASIC_(m_cap >= m_size); return m_cap - m_size; }
348
349 Callbacks const& callbacks() const { return m_callbacks; }
350 void callbacks(Callbacks const& cb) { m_callbacks = cb; }
351
352 /** @} */
353
354public:
355
356 /** @name node getters */
357 /** @{ */
358
359 //! get a pointer to a node's NodeData.
360 //! node can be NONE, in which case a nullptr is returned
361 NodeData *get(id_type node) // NOLINT(readability-make-member-function-const)
362 {
363 if(node == NONE)
364 return nullptr;
365 RYML_ASSERT_VISIT_CB_(m_callbacks, node >= 0 && node < m_cap, this, node);
366 return m_buf + node;
367 }
368 //! get a pointer to a node's NodeData.
369 //! node can be NONE, in which case a nullptr is returned.
370 NodeData const *get(id_type node) const
371 {
372 if(node == NONE)
373 return nullptr;
374 RYML_ASSERT_VISIT_CB_(m_callbacks, node >= 0 && node < m_cap, this, node);
375 return m_buf + node;
376 }
377
378 //! An if-less form of get() that demands a valid node index.
379 //! This function is implementation only; use at your own risk.
380 NodeData * _p(id_type node) { RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node); return m_buf + node; } // NOLINT(readability-make-member-function-const)
381 //! An if-less form of get() that demands a valid node index.
382 //! This function is implementation only; use at your own risk.
383 NodeData const * _p(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node); return m_buf + node; }
384
385 //! Get the id of the root node. The tree must not be empty. The tree can be empty only when constructed with explicitly zero-capacity.
386 id_type root_id() const { RYML_ASSERT_VISIT_CB_(m_callbacks, m_size > 0, this, id_type(0)); return 0; }
387 //! Get the id of the root node, or NONE if the tree is empty.
388 id_type root_id_maybe() const { return m_size ? 0 : id_type(NONE); }
389 //! get the id of a node belonging to this tree.
390 //! @p n can be nullptr, in which case NONE is returned
391 //! @p n must belong to this tree
392 id_type id(NodeData const* n) const
393 {
394 if( ! n)
395 return NONE;
396 RYML_ASSERT_VISIT_CB_(m_callbacks, n >= m_buf && n < m_buf + m_cap, this, NONE);
397 return static_cast<id_type>(n - m_buf);
398 }
399
400 /** @} */
401
402public:
403
404 /** @name NodeRef helpers */
405 /** @{ */
406
407 //! Get a NodeRef of a node by id
408 NodeRef ref(id_type node);
409 //! Get a NodeRef of a node by id
410 ConstNodeRef ref(id_type node) const;
411 //! Get a NodeRef of a node by id
412 ConstNodeRef cref(id_type node) const;
413
414 //! Get the root as a @ref NodeRef . Note that a non-const Tree implicitly converts to @ref NodeRef.
415 NodeRef rootref();
416 //! Get the root as a @ref ConstNodeRef . Note that Tree implicitly converts to @ref ConstNodeRef.
417 ConstNodeRef rootref() const;
418 //! Get the root as a @ref ConstNodeRef . Note that Tree implicitly converts to @ref ConstNodeRef.
419 ConstNodeRef crootref() const;
420
421 //! get the i-th document of the stream
422 //! @note @p i is NOT the node id, but the doc position within the stream
423 NodeRef docref(id_type i);
424 //! get the i-th document of the stream
425 //! @note @p i is NOT the node id, but the doc position within the stream
426 ConstNodeRef docref(id_type i) const;
427 //! get the i-th document of the stream
428 //! @note @p i is NOT the node id, but the doc position within the stream
429 ConstNodeRef cdocref(id_type i) const;
430
431 //! find a root child (ie child of root) by name, return it as a NodeRef
432 //! @note requires the root to be a map.
433 NodeRef operator[] (csubstr key);
434 //! find a root child (ie child of root) by name, return it as a NodeRef
435 //! @note requires the root to be a map.
436 ConstNodeRef operator[] (csubstr key) const;
437
438 //! find a root child (ie child of root) by index: return the root node's @p i-th child as a NodeRef
439 //! @note @p i is NOT the node id, but the child's position within the parent
440 NodeRef operator[] (id_type i);
441 //! find a root child (ie child of root) by index: return the root node's @p i-th child as a NodeRef
442 //! @note @p i is NOT the node id, but the child's position within the parent
443 ConstNodeRef operator[] (id_type i) const;
444
445 /** @} */
446
447public:
448
449 /** @name node property getters */
450 /** @{ */
451
452 NodeType type(id_type node) const { return _p(node)->m_type; }
453
454 csubstr const& key (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node); return _p(node)->m_key.scalar; }
455 csubstr const& key_tag (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key_tag(node), this, node); return _p(node)->m_key.tag; }
456 csubstr const& key_ref (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, is_key_ref(node), this, node); return _p(node)->m_key.anchor; }
457 csubstr const& key_anchor(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key_anchor(node), this, node); return _p(node)->m_key.anchor; }
458 NodeScalar const& keysc (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node); return _p(node)->m_key; }
459
460 csubstr const& val (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val(node), this, node); return _p(node)->m_val.scalar; }
461 csubstr const& val_tag (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val_tag(node), this, node); return _p(node)->m_val.tag; }
462 csubstr const& val_ref (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, is_val_ref(node), this, node); return _p(node)->m_val.anchor; }
463 csubstr const& val_anchor(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val_anchor(node), this, node); return _p(node)->m_val.anchor; }
464 NodeScalar const& valsc (id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val(node), this, node); return _p(node)->m_val; }
465
466 /** @} */
467
468public:
469
470 /** @name node type predicates */
471 /** @{ */
472
473 C4_ALWAYS_INLINE bool type_has_any(id_type node, type_bits bits) const { return _p(node)->m_type.has_any(bits); }
474 C4_ALWAYS_INLINE bool type_has_all(id_type node, type_bits bits) const { return _p(node)->m_type.has_all(bits); }
475 C4_ALWAYS_INLINE bool type_has_none(id_type node, type_bits bits) const { return _p(node)->m_type.has_none(bits); }
476
477 C4_ALWAYS_INLINE bool is_stream(id_type node) const { return _p(node)->m_type.is_stream(); }
478 C4_ALWAYS_INLINE bool is_doc(id_type node) const { return _p(node)->m_type.is_doc(); }
479 C4_ALWAYS_INLINE bool is_container(id_type node) const { return _p(node)->m_type.is_container(); }
480 C4_ALWAYS_INLINE bool is_map(id_type node) const { return _p(node)->m_type.is_map(); }
481 C4_ALWAYS_INLINE bool is_seq(id_type node) const { return _p(node)->m_type.is_seq(); }
482 C4_ALWAYS_INLINE bool has_key(id_type node) const { return _p(node)->m_type.has_key(); }
483 C4_ALWAYS_INLINE bool has_val(id_type node) const { return _p(node)->m_type.has_val(); }
484 C4_ALWAYS_INLINE bool is_val(id_type node) const { return _p(node)->m_type.is_val(); }
485 C4_ALWAYS_INLINE bool is_keyval(id_type node) const { return _p(node)->m_type.is_keyval(); }
486 C4_ALWAYS_INLINE bool has_key_tag(id_type node) const { return _p(node)->m_type.has_key_tag(); }
487 C4_ALWAYS_INLINE bool has_val_tag(id_type node) const { return _p(node)->m_type.has_val_tag(); }
488 C4_ALWAYS_INLINE bool has_key_anchor(id_type node) const { return _p(node)->m_type.has_key_anchor(); }
489 C4_ALWAYS_INLINE bool has_val_anchor(id_type node) const { return _p(node)->m_type.has_val_anchor(); }
490 C4_ALWAYS_INLINE bool has_anchor(id_type node) const { return _p(node)->m_type.has_anchor(); }
491 C4_ALWAYS_INLINE bool is_key_ref(id_type node) const { return _p(node)->m_type.is_key_ref(); }
492 C4_ALWAYS_INLINE bool is_val_ref(id_type node) const { return _p(node)->m_type.is_val_ref(); }
493 C4_ALWAYS_INLINE bool is_ref(id_type node) const { return _p(node)->m_type.is_ref(); }
494
495 C4_ALWAYS_INLINE bool parent_is_seq(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_parent(node), this, node); return is_seq(_p(node)->m_parent); }
496 C4_ALWAYS_INLINE bool parent_is_map(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_parent(node), this, node); return is_map(_p(node)->m_parent); }
497
498 /** true when the node has an anchor named a */
499 C4_ALWAYS_INLINE bool has_anchor(id_type node, csubstr a) const { return _p(node)->m_key.anchor == a || _p(node)->m_val.anchor == a; }
500
501 /** true if the node key is empty, or its scalar verifies @ref scalar_is_null().
502 * @warning the node must verify @ref Tree::has_key() (asserted) (ie must be a member of a map)
503 * @see https://github.com/biojppm/rapidyaml/issues/413 */
504 C4_ALWAYS_INLINE bool key_is_null(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node); NodeData const* C4_RESTRICT n = _p(node); return !n->m_type.is_key_quoted() && (n->m_type.key_is_null() || scalar_is_null(n->m_key.scalar)); }
505 /** true if the node val is empty, or its scalar verifies @ref scalar_is_null().
506 * @warning the node must verify @ref Tree::has_val() (asserted) (ie must be a scalar / must not be a container)
507 * @see https://github.com/biojppm/rapidyaml/issues/413 */
508 C4_ALWAYS_INLINE bool val_is_null(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val(node), this, node); NodeData const* C4_RESTRICT n = _p(node); return !n->m_type.is_val_quoted() && (n->m_type.val_is_null() || scalar_is_null(n->m_val.scalar)); }
509
510 /// true if the key was a scalar requiring filtering and was left
511 /// unfiltered during the parsing (see ParserOptions)
512 C4_ALWAYS_INLINE bool is_key_unfiltered(id_type node) const { return _p(node)->m_type.is_key_unfiltered(); }
513 /// true if the val was a scalar requiring filtering and was left
514 /// unfiltered during the parsing (see ParserOptions)
515 C4_ALWAYS_INLINE bool is_val_unfiltered(id_type node) const { return _p(node)->m_type.is_val_unfiltered(); }
516
517 RYML_DEPRECATED("use has_key_anchor()") bool is_key_anchor(id_type node) const { return _p(node)->m_type.has_key_anchor(); }
518 RYML_DEPRECATED("use has_val_anchor()") bool is_val_anchor(id_type node) const { return _p(node)->m_type.has_val_anchor(); }
519 RYML_DEPRECATED("use has_anchor()") bool is_anchor(id_type node) const { return _p(node)->m_type.has_anchor(); }
520 RYML_DEPRECATED("use has_anchor_or_ref()") bool is_anchor_or_ref(id_type node) const { return _p(node)->m_type.has_anchor() || _p(node)->m_type.is_ref(); }
521
522 /** @} */
523
524public:
525
526 /** @name hierarchy predicates */
527 /** @{ */
528
529 bool is_root(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, _p(node)->m_parent != NONE || node == 0, this, node); return _p(node)->m_parent == NONE; }
530
531 bool has_parent(id_type node) const { return _p(node)->m_parent != NONE; }
532
533 /** true when ancestor is parent or parent of a parent of node */
534 bool is_ancestor(id_type node, id_type ancestor) const;
535
536 /** true when key and val are empty, and has no children */
537 bool empty(id_type node) const { return ! has_children(node) && _p(node)->m_key.empty() && (( ! (_p(node)->m_type & VAL)) || _p(node)->m_val.empty()); }
538
539 /** true if @p node has a child with id @p ch */
540 bool has_child(id_type node, id_type ch) const { return _p(ch)->m_parent == node; }
541 /** true if @p node has a child with key @p key */
542 bool has_child(id_type node, csubstr key) const { return find_child(node, key) != NONE; }
543 /** true if @p node has any children */
544 bool has_children(id_type node) const { return _p(node)->m_first_child != NONE; }
545
546 /** true if @p node has a sibling with id @p sib */
547 bool has_sibling(id_type node, id_type sib) const { return _p(node)->m_parent == _p(sib)->m_parent; }
548 /** true if one of the node's siblings has the given key */
549 bool has_sibling(id_type node, csubstr key) const { return find_sibling(node, key) != NONE; }
550 /** true if node is not a single child */
552 {
553 NodeData const *n = _p(node);
554 if C4_LIKELY(n->m_parent != NONE)
555 {
556 n = _p(n->m_parent);
557 return n->m_first_child != n->m_last_child;
558 }
559 return false;
560 }
561
562 /** @} */
563
564public:
565
566 /** @name hierarchy getters */
567 /** @{ */
568
569 id_type parent(id_type node) const { return _p(node)->m_parent; }
570
571 id_type prev_sibling(id_type node) const { return _p(node)->m_prev_sibling; }
572 id_type next_sibling(id_type node) const { return _p(node)->m_next_sibling; }
573
574 /** O(#num_children) */
575 id_type num_children(id_type node) const;
576 id_type child_pos(id_type node, id_type ch) const;
577 id_type first_child(id_type node) const { return _p(node)->m_first_child; }
578 id_type last_child(id_type node) const { return _p(node)->m_last_child; }
579 id_type child(id_type node, id_type pos) const; ///< find child by position, or NONE if there are less than pos children posi
580 id_type find_child(id_type node, csubstr const& key) const; ///< find child by name, or NONE if no child is found with this key
581 /// like @ref Tree::child(), but return a @ref ReadResult with the status
582 C4_NODISCARD ReadResult child_r(id_type node, id_type pos, id_type *child_id) const { return ReadResult(node, *child_id = child(node, pos)); }
583 /// like @ref Tree::find_child(), but return a @ref ReadResult with the status
584 C4_NODISCARD ReadResult find_child_r(id_type node, csubstr const& key, id_type *child_id) const { return ReadResult(node, *child_id = find_child(node, key)); }
585
586 /** O(#num_siblings) */
587 /** counts with this */
588 id_type num_siblings(id_type node) const { return is_root(node) ? 1 : num_children(_p(node)->m_parent); }
589 /** does not count with this */
590 id_type num_other_siblings(id_type node) const { id_type ns = num_siblings(node); RYML_ASSERT_VISIT_CB_(m_callbacks, ns > 0, this, node); return ns-1; }
591 id_type sibling_pos(id_type node, id_type sib) const { RYML_ASSERT_VISIT_CB_(m_callbacks, ! is_root(node) || node == root_id(), this, node); return child_pos(_p(node)->m_parent, sib); }
592 id_type first_sibling(id_type node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_first_child; }
593 id_type last_sibling(id_type node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_last_child; }
594 id_type sibling(id_type node, id_type pos) const { return child(_p(node)->m_parent, pos); }
595 id_type find_sibling(id_type node, csubstr const& key) const { return find_child(_p(node)->m_parent, key); }
596 /// like @ref Tree::sibling(), but return a @ref ReadResult with the status
597 C4_NODISCARD ReadResult sibling_r(id_type node, id_type pos, id_type *sibling_id) const { return child_r(_p(node)->m_parent, pos, sibling_id); }
598 /// like @ref Tree::find_sibling(), but return a @ref ReadResult if with the status
599 C4_NODISCARD ReadResult find_sibling_r(id_type node, csubstr const& key, id_type *sibling_id) const { return find_child_r(_p(node)->m_parent, key, sibling_id); }
600
601 id_type depth_asc(id_type node) const; /**< O(log(num_tree_nodes)) get the ascending depth of the node: number of levels between root and node */
602 id_type depth_desc(id_type node) const; /**< O(num_tree_nodes) get the descending depth of the node: number of levels between node and deepest child */
603
604 /** gets the @p i document node index. requires that the root node is a stream. */
605 id_type doc(id_type i) const { id_type rid = root_id(); RYML_ASSERT_VISIT_CB_(m_callbacks, is_stream(rid), this, rid); return child(rid, i); }
606
607 /** get the document which is a parent document of node i, or the root if the tree is not a stream */
609 {
610 NodeData const *nd;
611 do
612 {
613 nd = _p(node);
614 if(nd->m_type.is_doc() || nd->m_parent == NONE)
615 break;
616 node = nd->m_parent;
617 } while(nd->m_parent != NONE);
618 return node;
619 }
620
621 /** @} */
622
623public:
624
625 /** @name node style predicates and modifiers. see the corresponding predicate in NodeType */
626 /** @{ */
627
628 C4_ALWAYS_INLINE bool is_container_styled(id_type node) const { return _p(node)->m_type.is_container_styled(); }
629 C4_ALWAYS_INLINE bool is_block(id_type node) const { return _p(node)->m_type.is_block(); }
630 C4_ALWAYS_INLINE bool is_flow_sl(id_type node) const { return _p(node)->m_type.is_flow_sl(); }
631 RYML_DEPRECATED("use one of .is_flow_ml{1,n,x}()")
632 C4_ALWAYS_INLINE bool is_flow_ml(id_type node) const { return _p(node)->m_type.is_flow_ml1(); }
633 C4_ALWAYS_INLINE bool is_flow_ml1(id_type node) const { return _p(node)->m_type.is_flow_ml1(); }
634 C4_ALWAYS_INLINE bool is_flow_mln(id_type node) const { return _p(node)->m_type.is_flow_mln(); }
635 C4_ALWAYS_INLINE bool is_flow_mlx(id_type node) const { return _p(node)->m_type.is_flow_mlx(); }
636 C4_ALWAYS_INLINE bool is_flow(id_type node) const { return _p(node)->m_type.is_flow(); }
637 C4_ALWAYS_INLINE bool has_flow_space(id_type node) const { return _p(node)->m_type.has_flow_space(); }
638
639 C4_ALWAYS_INLINE bool is_key_styled(id_type node) const { return _p(node)->m_type.is_key_styled(); }
640 C4_ALWAYS_INLINE bool is_val_styled(id_type node) const { return _p(node)->m_type.is_val_styled(); }
641 C4_ALWAYS_INLINE bool is_key_literal(id_type node) const { return _p(node)->m_type.is_key_literal(); }
642 C4_ALWAYS_INLINE bool is_val_literal(id_type node) const { return _p(node)->m_type.is_val_literal(); }
643 C4_ALWAYS_INLINE bool is_key_folded(id_type node) const { return _p(node)->m_type.is_key_folded(); }
644 C4_ALWAYS_INLINE bool is_val_folded(id_type node) const { return _p(node)->m_type.is_val_folded(); }
645 C4_ALWAYS_INLINE bool is_key_squo(id_type node) const { return _p(node)->m_type.is_key_squo(); }
646 C4_ALWAYS_INLINE bool is_val_squo(id_type node) const { return _p(node)->m_type.is_val_squo(); }
647 C4_ALWAYS_INLINE bool is_key_dquo(id_type node) const { return _p(node)->m_type.is_key_dquo(); }
648 C4_ALWAYS_INLINE bool is_val_dquo(id_type node) const { return _p(node)->m_type.is_val_dquo(); }
649 C4_ALWAYS_INLINE bool is_key_plain(id_type node) const { return _p(node)->m_type.is_key_plain(); }
650 C4_ALWAYS_INLINE bool is_val_plain(id_type node) const { return _p(node)->m_type.is_val_plain(); }
651 C4_ALWAYS_INLINE bool is_key_quoted(id_type node) const { return _p(node)->m_type.is_key_quoted(); }
652 C4_ALWAYS_INLINE bool is_val_quoted(id_type node) const { return _p(node)->m_type.is_val_quoted(); }
653 C4_ALWAYS_INLINE bool is_quoted(id_type node) const { return _p(node)->m_type.is_quoted(); }
654
655 C4_ALWAYS_INLINE NodeType key_style(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node); return _p(node)->m_type.key_style(); }
656 C4_ALWAYS_INLINE NodeType val_style(id_type node) const { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val(node) || is_root(node), this, node); return _p(node)->m_type.val_style(); }
657
658 C4_ALWAYS_INLINE void set_container_style(id_type node, type_bits style) { RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(node), this, node); _p(node)->m_type.set_container_style(style); }
659 C4_ALWAYS_INLINE void set_key_style(id_type node, type_bits style) { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node); _p(node)->m_type.set_key_style(style); }
660 C4_ALWAYS_INLINE void set_val_style(id_type node, type_bits style) { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val(node), this, node); _p(node)->m_type.set_val_style(style); }
661
662 void clear_style(id_type node, bool recurse=false);
663 void set_style_conditionally(id_type node,
664 NodeType type_mask,
665 NodeType rem_style_flags,
666 NodeType add_style_flags,
667 bool recurse=false);
668 /** @} */
669
670public:
671
672 /** @name node type modifiers */
673 /** @{ */
674
676 {
677 RYML_ASSERT_VISIT_CB_(m_callbacks, is_root(node), this, node);
678 RYML_ASSERT_VISIT_CB_(m_callbacks, (_p(node)->m_type & (DOC|MAP|VAL)) == 0, this, node);
679 _p(node)->m_type |= STREAM;
680 }
681
682 void set_doc(id_type node)
683 {
684 RYML_ASSERT_VISIT_CB_(m_callbacks, is_root(node) || (is_root(parent(node)) && is_stream(parent(node))), this, node);
685 _p(node)->m_type |= DOC;
686 }
687
689 {
690 RYML_ASSERT_VISIT_CB_(m_callbacks, (_p(node)->m_type & (SEQ|MAP)) == 0, this, node);
691 RYML_ASSERT_VISIT_CB_(m_callbacks, (parent(node) == NONE || (_p(parent(node))->m_type & (SEQ|MAP))), this, node);
692 NodeData *C4_RESTRICT nd = _p(node);
693 nd->m_type |= VAL;
694 nd->m_val.scalar = val;
695 }
697 {
698 RYML_ASSERT_VISIT_CB_(m_callbacks, (_p(node)->m_type & (SEQ|MAP)) == 0, this, node);
699 RYML_ASSERT_VISIT_CB_(m_callbacks, (parent(node) == NONE || (_p(parent(node))->m_type & (SEQ|MAP))), this, node);
700 NodeData *C4_RESTRICT nd = _p(node);
701 nd->m_type |= VAL|more_flags;
702 nd->m_val.scalar = val;
703 }
704
706 {
707 RYML_ASSERT_VISIT_CB_(m_callbacks, (parent(node) != NONE && (_p(parent(node))->m_type & MAP)), this, node);
708 NodeData *C4_RESTRICT nd = _p(node);
709 nd->m_type |= KEY;
710 nd->m_key.scalar = key;
711 }
713 {
714 RYML_ASSERT_VISIT_CB_(m_callbacks, (parent(node) != NONE && (_p(parent(node))->m_type & MAP)), this, node);
715 NodeData *C4_RESTRICT nd = _p(node);
716 nd->m_type |= KEY|more_flags;
717 nd->m_key.scalar = key;
718 }
719
721 {
722 RYML_ASSERT_VISIT_CB_(m_callbacks, (_p(node)->m_type & (VAL|MAP)) == 0, this, node);
723 _p(node)->m_type |= SEQ;
724 }
725 void set_seq(id_type node, NodeType more_flags) RYML_NOEXCEPT
726 {
727 RYML_ASSERT_VISIT_CB_(m_callbacks, ((_p(node)->m_type|more_flags) & (VAL|MAP)) == 0, this, node);
728 _p(node)->m_type |= SEQ|more_flags;
729 }
730
732 {
733 RYML_ASSERT_VISIT_CB_(m_callbacks, (_p(node)->m_type & (VAL|SEQ)) == 0, this, node);
734 _p(node)->m_type |= MAP;
735 }
736 void set_map(id_type node, NodeType more_flags) RYML_NOEXCEPT
737 {
738 RYML_ASSERT_VISIT_CB_(m_callbacks, ((_p(node)->m_type|more_flags) & (VAL|SEQ)) == 0, this, node);
739 _p(node)->m_type |= MAP|more_flags;
740 }
741
742 void set_key_tag(id_type node, csubstr tag) { RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node); _p(node)->m_key.tag = tag; _add_flags(node, KEYTAG); }
743 void set_val_tag(id_type node, csubstr tag) { RYML_ASSERT_VISIT_CB_(m_callbacks, has_val(node) || is_container(node), this, node); _p(node)->m_val.tag = tag; _add_flags(node, VALTAG); }
744
745 void set_key_anchor(id_type node, csubstr anchor) { RYML_ASSERT_VISIT_CB_(m_callbacks, ! is_key_ref(node), this, node); _p(node)->m_key.anchor = anchor.triml('&'); _add_flags(node, KEYANCH); }
746 void set_val_anchor(id_type node, csubstr anchor) { RYML_ASSERT_VISIT_CB_(m_callbacks, ! is_val_ref(node), this, node); _p(node)->m_val.anchor = anchor.triml('&'); _add_flags(node, VALANCH); }
747 void set_key_ref (id_type node, csubstr ref ) { RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_key_anchor(node), this, node); NodeData* C4_RESTRICT n = _p(node); n->m_key.set_ref_maybe_replacing_scalar(ref, n->m_type.has_key()); _add_flags(node, KEY|KEYREF); }
748 void set_val_ref (id_type node, csubstr ref ) { RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_val_anchor(node), this, node); NodeData* C4_RESTRICT n = _p(node); n->m_val.set_ref_maybe_replacing_scalar(ref, n->m_type.has_val()); _add_flags(node, VAL|VALREF); }
749
750 void rem_key_anchor(id_type node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYANCH); }
751 void rem_val_anchor(id_type node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALANCH); }
752 void rem_key_ref (id_type node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYREF); }
753 void rem_val_ref (id_type node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALREF); }
754 void rem_anchor_ref(id_type node) { _p(node)->m_key.anchor.clear(); _p(node)->m_val.anchor.clear(); _rem_flags(node, KEYANCH|VALANCH|KEYREF|VALREF); }
755
756 /** @} */
757
758private:
759
760 C4_NORETURN C4_NO_INLINE C4_COLD
761 void err_visit_(id_type node) const
762 {
763 RYML_ERR_VISIT_CB_(m_callbacks, this, node, "invalid node");
764 }
765
766public:
767
768 /** @name serialization - checked */
769 /** @{ */
770
771 template<class T>
772 C4_ALWAYS_INLINE void save(id_type node, T const& val)
773 {
774 if C4_LIKELY(node != NONE && node < m_cap && node >= 0)
775 {
776 write(this, node, val);
777 return;
778 }
779 err_visit_(node);
780 }
781 template<class T>
782 C4_ALWAYS_INLINE void save(id_type node, T const& val, NodeType more_flags)
783 {
784 if C4_LIKELY(node != NONE && node < m_cap && node >= 0)
785 {
786 write(this, node, val);
787 _p(node)->m_type |= more_flags;
788 return;
789 }
790 err_visit_(node);
791 }
792
793 template<class T>
794 C4_ALWAYS_INLINE void save_key(id_type node, T const& key)
795 {
796 if C4_LIKELY(node != NONE && node < m_cap && node >= 0)
797 {
798 write_key(this, node, key);
799 return;
800 }
801 err_visit_(node);
802 }
803 template<class T>
804 C4_ALWAYS_INLINE void save_key(id_type node, T const& key, NodeType more_flags)
805 {
806 if C4_LIKELY(node != NONE && node < m_cap && node >= 0)
807 {
808 write_key(this, node, key);
809 _p(node)->m_type |= more_flags;
810 return;
811 }
812 err_visit_(node);
813 }
814
815 /** @} */
816
817public:
818
819 /** @name serialization - asserted */
820 /** @{ */
821
822 template<class T>
823 C4_ALWAYS_INLINE void set_serialized(id_type node, T const& val) RYML_NOEXCEPT
824 {
825 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
826 write(this, node, val);
827 }
828 template<class T>
829 C4_ALWAYS_INLINE void set_serialized(id_type node, T const& val, NodeType more_flags) RYML_NOEXCEPT
830 {
831 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
832 write(this, node, val);
833 _p(node)->m_type |= more_flags;
834 }
835
836 template<class T>
837 C4_ALWAYS_INLINE void set_key_serialized(id_type node, T const& key) RYML_NOEXCEPT
838 {
839 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
840 write_key(this, node, key);
841 }
842 template<class T>
843 C4_ALWAYS_INLINE void set_key_serialized(id_type node, T const& key, NodeType more_flags) RYML_NOEXCEPT
844 {
845 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
846 write_key(this, node, key);
847 _p(node)->m_type |= more_flags;
848 }
849
850 /** @} */
851
852public:
853
854 /** @name deserialization - checked
855 *
856 * These methods raise an error if the deserialization failed or
857 * optionally if the node is not readable.
858 */
859 /** @{ */
860
861 static C4_ALWAYS_INLINE ReadResult to_result_(bool, id_type node) noexcept { return ReadResult(node); }
862 static C4_ALWAYS_INLINE ReadResult to_result_(ReadResult notlegacy, id_type) noexcept { return notlegacy; }
863
864 /** (1) deserialize the node's contents (val or container) to the
865 * given variable, forwarding to the user-overrideable @ref read()
866 * function (see @ref doc_serialization_tree_read). This function
867 * differs from @ref Tree::deserialize() in that here the error
868 * callback is called if the deserialization failed, or
869 * (optionally) the node is not readable. */
870 template<class T>
871 C4_ALWAYS_INLINE void load(id_type node, T * v, bool check_readable=true) const
872 {
873 const bool can_read_val = (node != NONE && node < m_cap && node >= 0 && (_p(node)->m_type & (VAL|MAP|SEQ)));
874 RYML_ASSERT_VISIT_CB_(m_callbacks, can_read_val, this, node);
875 if C4_LIKELY(!check_readable || can_read_val)
876 {
877 const ReadResult result(read(this, node, v), node);
878 if C4_LIKELY(result)
879 return;
880 else
881 node = result.node;
882 }
883 err_visit_(node);
884 }
885 /** (2) like (1), but for wrapper tag types such as @ref
886 * c4::fmt::base64() */
887 template<class Wrapper>
888 C4_ALWAYS_INLINE void load(id_type node, Wrapper const& w, bool check_readable=true) const
889 {
890 RYML_CHECK_TYPE_IS_WRAPPER_LIKE_(Wrapper);
891 const bool can_read_val = (node != NONE && node < m_cap && node >= 0 && (_p(node)->m_type & (VAL|MAP|SEQ)));
892 RYML_ASSERT_VISIT_CB_(m_callbacks, can_read_val, this, node);
893 if C4_LIKELY(!check_readable || can_read_val)
894 {
895 const ReadResult result(read(this, node, w), node);
896 if C4_LIKELY(result)
897 return;
898 else
899 node = result.node;
900 }
901 err_visit_(node);
902 }
903
904 /** (1) deserialize the node's key (necessarily a scalar) to the
905 * given variable, forwarding to the user-overrideable @ref
906 * read_key() function (see @ref
907 * doc_serialization_node_read). This function differs from @ref
908 * Tree::deserialize_key() in that here the error callback is
909 * called if the deserialization failed, or (optionally) the node
910 * is not readable. */
911 template<class T>
912 C4_ALWAYS_INLINE void load_key(id_type node, T * k, bool check_readable=true) const
913 {
914 const bool can_read_key = (node != NONE && node < m_cap && node >= 0 && (_p(node)->m_type & KEY));
915 RYML_ASSERT_VISIT_CB_(m_callbacks, can_read_key, this, node);
916 if C4_LIKELY(!check_readable || can_read_key)
917 {
918 const ReadResult result(read_key(this, node, k), node);
919 if C4_LIKELY(result)
920 return;
921 else
922 node = result.node;
923 }
924 err_visit_(node);
925 }
926 /** (2) like (1), but for wrapper tag types such as @ref
927 * c4::fmt::base64() */
928 template<class Wrapper>
929 C4_ALWAYS_INLINE void load_key(id_type node, Wrapper const& w, bool check_readable=true) const
930 {
931 RYML_CHECK_TYPE_IS_WRAPPER_LIKE_(Wrapper);
932 bool can_read_key = (node != NONE && node < m_cap && node >= 0 && (_p(node)->m_type & KEY));
933 RYML_ASSERT_VISIT_CB_(m_callbacks, can_read_key, this, node);
934 if C4_LIKELY(!check_readable || can_read_key)
935 {
936 const ReadResult result(read_key(this, node, w), node);
937 if C4_LIKELY(result)
938 return;
939 else
940 node = result.node;
941 }
942 err_visit_(node);
943 }
944
945 /** @} */
946
947public:
948
949 /** @name deserialization - asserted preconditions */
950 /** @{ */
951
952 /** (1) deserialize a node's contents to a variable */
953 template<class T>
954 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize(id_type node, T * v) const
955 {
956 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
957 RYML_ASSERT_VISIT_CB_(m_callbacks, _p(node)->m_type & (VAL|MAP|SEQ), this, node);
958 // use the adapter ctor to accomodate legacy read() implementations
959 return ReadResult(read(this, node, v), node);
960 }
961 /** (2) like (1), but for a wrapper type like those used in tag
962 * functions */
963 template<class Wrapper>
964 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize(id_type node, Wrapper const& w) const
965 {
966 RYML_CHECK_TYPE_IS_WRAPPER_LIKE_(Wrapper);
967 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
968 RYML_ASSERT_VISIT_CB_(m_callbacks, _p(node)->m_type & (VAL|MAP|SEQ), this, node);
969 // use the adapter ctor to accomodate legacy read() implementations
970 return ReadResult(read(this, node, w), node);
971 }
972
973 /** (1) deserialize a node's key to a variable */
974 template<class T>
975 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_key(id_type node, T * v) const
976 {
977 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
978 RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node);
979 // use the adapter ctor to accomodate legacy read_key() implementations
980 return ReadResult(read_key(this, node, v), node);
981 }
982 /** (2) like (1), but for a wrapper type like those used in tag
983 * functions */
984 template<class Wrapper>
985 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_key(id_type node, Wrapper const& w) const
986 {
987 RYML_CHECK_TYPE_IS_WRAPPER_LIKE_(Wrapper);
988 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node);
989 RYML_ASSERT_VISIT_CB_(m_callbacks, has_key(node), this, node);
990 // use the adapter ctor to accomodate legacy read_key() implementations
991 return ReadResult(read_key(this, node, w), node);
992 }
993
994 /** @} */
995
996public:
997
998 /** @name lookup and deserialize */
999 /** @{ */
1000
1001 /** (1) find a child by name and deserialize its contents to the
1002 * given variable (ie call .deserialize() on the child if it
1003 * exists). Otherwise, the variable is kept unchanged.
1004 *
1005 * @return a @ref ReadResult set with this node's id if no child
1006 * exists, or the ReadResult from the deserialization.
1007 *
1008 * @see see also @ref Tree::find_child_r()
1009 */
1010 template<class T>
1011 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_child(id_type node, csubstr child_key, T * v) const
1012 {
1013 id_type ch;
1014 ReadResult r = find_child_r(node, child_key, &ch);
1015 if(r)
1016 r = deserialize(ch, v);
1017 return r;
1018 }
1019 /** (2) like (1), but assign from fallback if no such child exists. */
1020 template<class T>
1021 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_child(id_type node, csubstr child_key, T * v, T const& fallback) const
1022 {
1023 id_type ch;
1024 if(find_child_r(node, child_key, &ch))
1025 return deserialize(ch, v);
1026 *v = fallback;
1027 return ReadResult();
1028 }
1029 /** (3) like (1), but for wrapper tag types such as @ref c4::fmt::base64() */
1030 template<class Wrapper>
1031 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_child(id_type node, csubstr child_key, Wrapper const& wrapper) const
1032 {
1033 id_type ch;
1034 ReadResult r = find_child_r(node, child_key, &ch);
1035 if(r)
1036 r = deserialize(ch, wrapper);
1037 return r;
1038 }
1039
1040 /** (1) find a child by position and deserialize its contents to
1041 * the given variable (ie call .deserialize() on the child if it
1042 * exists). Otherwise, the variable is kept unchanged.
1043 *
1044 * @return a @ref ReadResult set with this node's id if no child
1045 * exists, or the ReadResult from the deserialization.
1046 *
1047 * @see see also @ref Tree::child_r()
1048 */
1049 template<class T>
1050 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_child(id_type node, id_type child_pos, T * v) const
1051 {
1052 RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(node), this, node); // this assertion is needed because child_r() does not assert, contrary to find_child_r()
1053 id_type ch;
1054 ReadResult r = child_r(node, child_pos, &ch);
1055 if(r)
1056 r = deserialize(ch, v);
1057 return r;
1058 }
1059 /** (2) like (1), but assign from fallback if no such child exists */
1060 template<class T>
1061 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_child(id_type node, id_type child_pos, T * v, T const& fallback) const
1062 {
1063 RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(node), this, node); // this assertion is needed because child_r() does not assert, contrary to find_child_r()
1064 id_type ch;
1065 if(child_r(node, child_pos, &ch))
1066 return deserialize(ch, v);
1067 *v = fallback;
1068 return ReadResult();
1069 }
1070 /** (3) like (1), but for wrapper tag types such as @ref
1071 * c4::fmt::base64() */
1072 template<class Wrapper>
1073 C4_NODISCARD C4_ALWAYS_INLINE ReadResult deserialize_child(id_type node, id_type child_pos, Wrapper const& wrapper) const
1074 {
1075 RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(node), this, node); // this assertion is needed because child_r() does not assert, contrary to find_child_r()
1076 id_type ch;
1077 ReadResult r = child_r(node, child_pos, &ch);
1078 if(r)
1079 r = deserialize(ch, wrapper);
1080 return r;
1081 }
1082
1083 /** @} */
1084
1085public:
1086
1087 /** @name tree modifiers */
1088 /** @{ */
1089
1090 /** reorder the tree in memory so that all the nodes are stored
1091 * in a linear sequence when visited in depth-first order.
1092 * This will invalidate existing ids, since the node id is its
1093 * position in the tree's node array. */
1094 void reorder();
1095
1096 /** @} */
1097
1098public:
1099
1100 /** @name anchors and references/aliases */
1101 /** @{ */
1102
1103 /** Resolve references (aliases <- anchors), by forwarding to @ref
1104 * ReferenceResolver::resolve(); refer to @ref
1105 * ReferenceResolver::resolve() for further details. */
1106 void resolve(ReferenceResolver *C4_RESTRICT rr, bool clear_anchors=true);
1107
1108 /** Resolve references (aliases <- anchors), by forwarding to @ref
1109 * ReferenceResolver::resolve(); refer to @ref
1110 * ReferenceResolver::resolve() for further details. This overload
1111 * uses a throwaway resolver object. */
1112 void resolve(bool clear_anchors=true);
1113
1114 /** @} */
1115
1116public:
1117
1118 /** @name tags and tag directives */
1119 /** @{ */
1120
1121 /** Resolve tags in the tree such as \c "!!str" ->
1122 * \c "<tag:yaml.org,2002:str>", \c "!foo" -> \c "<!foo>" and custom tags
1123 * as well, ie tags of the form \c "!handle!tag" for which there is a
1124 * corresponding \c "%TAG" directive
1125 *
1126 * @param cache an object of type @ref TagCache to minimize memory
1127 * usage by avoiding repeated instantiation of the resolved
1128 * tags in the tree's arena.
1129 *
1130 * @param all if true, resolve all tags; if false resolve only
1131 * custom tags, ie those that have a prefix such as
1132 * \c "!m!tag" with a matching \c "\%TAG" directive */
1133 void resolve_tags(TagCache &cache, bool all=true);
1134 void normalize_tags();
1135 void normalize_tags_long();
1136
1137 id_type num_tag_directives() const;
1138 void add_tag_directive(csubstr handle, csubstr prefix, id_type id);
1139 void clear_tag_directives();
1140
1141 /** resolve the given tag, appearing at node_id. Write the result into output.
1142 * @return the number of characters required for the resolved tag */
1143 size_t resolve_tag(substr output, csubstr tag, id_type node_id) const;
1144 /** Wrapper for @ref Tree::resolve_tag(), returning a substring */
1145 csubstr resolve_tag_sub(substr output, csubstr tag, id_type node_id) const
1146 {
1147 size_t needed = resolve_tag(output, tag, node_id);
1148 return needed <= output.len ? output.first(needed) : output;
1149 }
1150
1152
1153 /** @cond dev */
1154 RYML_DEPRECATED("use c4::yml::tag_directive_const_iterator") typedef TagDirective const* tag_directive_const_iterator;
1155 RYML_DEPRECATED("use c4::yml::TagDirectiveRange") typedef c4::yml::TagDirectiveRange TagDirectiveProxy;
1156 /** @endcond */
1157
1158 /** @} */
1159
1160public:
1161
1162 /** @name modifying hierarchy */
1163 /** @{ */
1164
1165 /** create and insert a new child of @p parent. insert after the (to-be)
1166 * sibling @p after, which must be a child of @p parent. To insert as the
1167 * first child, set after to NONE */
1168 C4_ALWAYS_INLINE id_type insert_child(id_type parent, id_type after)
1169 {
1170 RYML_ASSERT_VISIT_CB_(m_callbacks, parent != NONE, this, parent);
1171 RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(parent) || is_root(parent), this, parent);
1172 RYML_ASSERT_VISIT_CB_(m_callbacks, after == NONE || (_p(after)->m_parent == parent), this, parent);
1173 id_type child = _claim();
1174 _set_hierarchy(child, parent, after);
1175 return child;
1176 }
1177 /** create and insert a node as the first child of @p parent */
1179 /** create and insert a node as the last child of @p parent */
1180 C4_ALWAYS_INLINE id_type append_child(id_type parent) { return insert_child(parent, _p(parent)->m_last_child); }
1182 {
1183 id_type child = _claim();
1184 _set_hierarchy(child, parent, _p(parent)->m_last_child);
1185 return child;
1186 }
1187
1188public:
1189
1190 //! create and insert a new sibling of n. insert after "after"
1191 C4_ALWAYS_INLINE id_type insert_sibling(id_type node, id_type after)
1192 {
1193 return insert_child(_p(node)->m_parent, after);
1194 }
1195 /** create and insert a node as the first node of @p parent */
1196 C4_ALWAYS_INLINE id_type prepend_sibling(id_type node) { return prepend_child(_p(node)->m_parent); }
1197 C4_ALWAYS_INLINE id_type append_sibling(id_type node) { return append_child(_p(node)->m_parent); }
1198
1199public:
1200
1201 /** remove an entire branch at once: ie remove the children and the node itself */
1202 void remove(id_type node)
1203 {
1204 remove_children(node);
1205 _release(node);
1206 }
1207
1208 /** remove all the node's children, but keep the node itself */
1209 void remove_children(id_type node);
1210
1211public:
1212
1213 /** change the node's position in the parent */
1214 void move(id_type node, id_type after);
1215
1216 /** change the node's parent and position */
1217 void move(id_type node, id_type new_parent, id_type after);
1218
1219 /** change the node's parent and position to a different tree
1220 * @return the index of the new node in the destination tree */
1221 id_type move(Tree * src, id_type node, id_type new_parent, id_type after);
1222
1223 /** ensure the first node is a stream. Eg, change this tree
1224 *
1225 * DOCMAP
1226 * MAP
1227 * KEYVAL
1228 * KEYVAL
1229 * SEQ
1230 * VAL
1231 *
1232 * to
1233 *
1234 * STREAM
1235 * DOCMAP
1236 * MAP
1237 * KEYVAL
1238 * KEYVAL
1239 * SEQ
1240 * VAL
1241 *
1242 * If the root is already a stream, this is a no-op.
1243 */
1244 void set_root_as_stream();
1245
1246 bool change_type(id_type node, NodeType type);
1248 {
1249 return change_type(node, (NodeType)type);
1250 }
1251
1252public:
1253
1254 /** recursively duplicate a node from this tree into a new parent,
1255 * placing it after one of its children
1256 * @return the index of the copy */
1257 id_type duplicate(id_type node, id_type new_parent, id_type after);
1258 /** recursively duplicate a node from a different tree into a new parent,
1259 * placing it after one of its children
1260 * @return the index of the copy */
1261 id_type duplicate(Tree const* src, id_type node, id_type new_parent, id_type after);
1262
1263 /** recursively duplicate the node's children (but not the node)
1264 * @return the index of the last duplicated child */
1265 id_type duplicate_children(id_type node, id_type parent, id_type after);
1266 /** recursively duplicate the node's children (but not the node), where
1267 * the node is from a different tree
1268 * @return the index of the last duplicated child */
1269 id_type duplicate_children(Tree const* src, id_type node, id_type parent, id_type after);
1270
1271 /** duplicate the node's children (but not the node) in a new parent, but
1272 * omit repetitions where a duplicated node has the same key (in maps) or
1273 * value (in seqs). If one of the duplicated children has the same key
1274 * (in maps) or value (in seqs) as one of the parent's children, the one
1275 * that is placed closest to the end will prevail. */
1276 id_type duplicate_children_no_rep(id_type node, id_type parent, id_type after);
1277 id_type duplicate_children_no_rep(Tree const* src, id_type node, id_type parent, id_type after);
1278
1279 void duplicate_contents(id_type node, id_type where);
1280 void duplicate_contents(Tree const* src, id_type node, id_type where);
1281
1282public:
1283
1284 void merge_with(Tree const* src, id_type src_node=NONE, id_type dst_root=NONE);
1285
1286 /** @} */
1287
1288public:
1289
1290 /** @name locations */
1291 /** @{ */
1292
1293 /** Get the location of a node from the parse used to parse this tree. */
1294 Location location(Parser const& p, id_type node) const;
1295
1296private:
1297
1298 bool _location_from_node(Parser const& p, id_type node, Location *C4_RESTRICT loc, id_type level) const;
1299 bool _location_from_cont(Parser const& p, id_type node, Location *C4_RESTRICT loc) const;
1300
1301 /** @} */
1302
1303public:
1304
1305 /** @name internal string arena */
1306 /** @{ */
1307
1308 /** get the current size of the tree's internal arena */
1309 size_t arena_size() const { return m_arena_pos; }
1310 /** get the current capacity of the tree's internal arena */
1311 size_t arena_capacity() const { return m_arena.len; }
1312 /** get the current slack of the tree's internal arena */
1313 size_t arena_slack() const { RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.len >= m_arena_pos, this, NONE); return m_arena.len - m_arena_pos; }
1314 RYML_DEPRECATED("use arena_size() instead") size_t arena_pos() const { return m_arena_pos; }
1315
1316 /** get the current arena */
1317 csubstr arena() const { return m_arena.first(m_arena_pos); }
1318 /** get the current arena */
1319 substr arena() { return m_arena.first(m_arena_pos); } // NOLINT(readability-make-member-function-const)
1320
1321 /** get the free space at the end of the arena */
1322 csubstr arena_rem() const { return m_arena.sub(m_arena_pos); }
1323 /** get the free space at the end of the arena */
1324 substr arena_rem() { return m_arena.sub(m_arena_pos); } // NOLINT(readability-make-member-function-const)
1325
1326 /** return true if the given substring is part of the tree's string arena */
1327 C4_ALWAYS_INLINE bool in_arena(csubstr s) const
1328 {
1329 return m_arena.is_super(s);
1330 }
1331
1332 /** serialize the given variable to the tree's
1333 * arena, growing it as needed to accomodate the serialization.
1334 *
1335 * @note To customize how the type gets serialized to a string,
1336 * you can overload c4::to_chars(substr, T const&)
1337 *
1338 * @note To customize how the type gets serialized to the arena,
1339 * you can overload @ref scalar_serialize()
1340 *
1341 * @note Growing the arena may cause relocation of the entire
1342 * existing arena, and thus change the contents of individual
1343 * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
1344 * cost, ensure that the arena is reserved to an appropriate size
1345 * using @ref Tree::reserve_arena().
1346 *
1347 * @see alloc_arena() */
1348 template<class T>
1349 C4_ALWAYS_INLINE csubstr to_arena(T const& C4_RESTRICT a)
1350 {
1351 return serialize_to_arena(this, a);
1352 }
1353
1354 /** copy the given string to the tree's arena, growing the arena
1355 * by the required size.
1356 *
1357 * @note this method differs from @ref to_arena() in that it
1358 * returns a mutable substr, and further it does not deal
1359 * with some corner cases for null/empty strings
1360 *
1361 * @note Growing the arena may cause relocation of the entire
1362 * existing arena, and thus change the contents of individual
1363 * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
1364 * cost, ensure that the arena is reserved to an appropriate size
1365 * before using @ref Tree::reserve_arena()
1366 *
1367 * @see reserve_arena()
1368 * @see alloc_arena()
1369 */
1371 {
1372 RYML_ASSERT_VISIT_CB_(m_callbacks, !s.overlaps(m_arena), this, NONE);
1373 substr cp = alloc_arena(s.len);
1374 RYML_ASSERT_VISIT_CB_(m_callbacks, cp.len == s.len, this, NONE);
1375 RYML_ASSERT_VISIT_CB_(m_callbacks, !s.overlaps(cp), this, NONE);
1376 C4_SUPPRESS_WARNING_GCC_PUSH
1377 #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
1378 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow=") // no need for terminating \0
1379 C4_SUPPRESS_WARNING_GCC("-Wrestrict") // there's an assert to ensure no violation of restrict behavior
1380 #endif
1381 if(s.len)
1382 memcpy(cp.str, s.str, s.len);
1383 C4_SUPPRESS_WARNING_GCC_POP
1384 return cp;
1385 }
1386
1387 /** grow the tree's string arena by the given size and return a substr
1388 * of the added portion
1389 *
1390 * @note Growing the arena may cause relocation of the entire
1391 * existing arena, and thus change the contents of individual
1392 * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
1393 * cost, ensure that the arena is reserved to an appropriate size
1394 * using @ref Tree::reserve_arena().
1395 *
1396 * @see reserve_arena() */
1398 {
1399 if(sz > arena_slack())
1400 _grow_arena(sz - arena_slack());
1401 substr s = _request_span(sz);
1402 return s;
1403 }
1404
1405 /** ensure the tree's internal string arena is at least the given capacity
1406 * @warning This operation may be expensive, with a potential complexity of O(numNodes)+O(arenasize).
1407 * @warning Growing the arena may cause relocation of the entire
1408 * existing arena, and thus change the contents of individual nodes. */
1410 {
1411 if(arena_cap > m_arena.len)
1412 {
1413 substr buf;
1414 buf.str = RYML_CB_ALLOC_(m_callbacks, char, arena_cap);
1415 buf.len = arena_cap;
1416 if(m_arena.str)
1417 {
1418 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.len >= 0, this, NONE);
1419 _relocate(buf); // does a memcpy and changes nodes using the arena
1420 RYML_CB_FREE_(m_callbacks, m_arena.str, char, m_arena.len);
1421 }
1422 m_arena = buf;
1423 }
1424 }
1425
1426 /** @} */
1427
1428public:
1429
1430 /** @cond dev */
1431
1432 substr _grow_arena(size_t more)
1433 {
1434 size_t cap = m_arena.len + more;
1436 cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap;
1437 reserve_arena(cap);
1438 return m_arena.sub(m_arena_pos);
1439 }
1440
1441 substr _request_span(size_t sz)
1442 {
1443 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena_pos + sz <= m_arena.len, this, NONE);
1444 substr s;
1445 s = m_arena.sub(m_arena_pos, sz);
1446 m_arena_pos += sz;
1447 return s;
1448 }
1449
1450 substr _relocated(csubstr s, substr next_arena) const
1451 {
1452 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.is_super(s) || s.len == 0, this, NONE);
1453 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.sub(0, m_arena_pos).is_super(s) || s.len == 0, this, NONE);
1454 auto pos = (s.str - m_arena.str); // this is larger than 0 based on the assertions above
1455 substr r(next_arena.str + pos, s.len);
1456 RYML_ASSERT_VISIT_CB_(m_callbacks, r.str - next_arena.str == pos, this, NONE);
1457 RYML_ASSERT_VISIT_CB_(m_callbacks, next_arena.sub(0, m_arena_pos).is_super(r) || r.len == 0, this, NONE);
1458 return r;
1459 }
1460
1461 /** @endcond */
1462
1463public:
1464
1465 /** @name lookup */
1466 /** @{ */
1467
1469 {
1472 size_t path_pos;
1474
1475 operator bool() const { return target != NONE; }
1476
1478 lookup_result(csubstr path_, id_type start) : target(NONE), closest(start), path_pos(0), path(path_) {}
1479
1480 /** get the part ot the input path that was resolved */
1481 csubstr resolved() const;
1482 /** get the part ot the input path that was unresolved */
1483 csubstr unresolved() const;
1484 };
1485
1486 /** for example foo.bar[0].baz */
1487 lookup_result lookup_path(csubstr path, id_type start=NONE) const;
1488
1489 /** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
1490 * the tree so that the corresponding lookup_path() would return the
1491 * default value.
1492 * @see lookup_path() */
1493 id_type lookup_path_or_modify(csubstr default_value, csubstr path, id_type start=NONE);
1494
1495 /** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
1496 * the tree so that the corresponding lookup_path() would return the
1497 * branch @p src_node (from the tree @p src).
1498 * @see lookup_path() */
1499 id_type lookup_path_or_modify(Tree const *src, id_type src_node, csubstr path, id_type start=NONE);
1500
1501 /** @} */
1502
1503private:
1504
1505 struct _lookup_path_token
1506 {
1507 csubstr value;
1508 NodeType type;
1509 _lookup_path_token() : value(), type() {} // LCOV_EXCL_LINE
1510 _lookup_path_token(csubstr v, NodeType t) : value(v), type(t) {}
1511 operator bool() const { return type != NOTYPE; }
1512 bool is_index() const { return value.begins_with('[') && value.ends_with(']'); }
1513 };
1514
1515 id_type _lookup_path_or_create(csubstr path, id_type start);
1516
1517 void _lookup_path (lookup_result *r) const;
1518 void _lookup_path_modify(lookup_result *r);
1519
1520 id_type _next_node (lookup_result *r, _lookup_path_token *parent) const;
1521 id_type _next_node_modify(lookup_result *r, _lookup_path_token *parent);
1522
1523 static void _advance(lookup_result *r, size_t more);
1524
1525 _lookup_path_token _next_token(lookup_result *r, _lookup_path_token const& parent) const;
1526
1527private:
1528
1529 void _clear();
1530 void _free();
1531 void _copy(Tree const& that);
1532 void _move(Tree & that) noexcept;
1533
1534 void _relocate(substr next_arena);
1535
1536public:
1537
1538 /** @cond dev*/
1539
1540 #if ! RYML_USE_ASSERT
1541 C4_ALWAYS_INLINE void _check_next_flags(id_type, type_bits) {}
1542 #else
1543 void _check_next_flags(id_type node, type_bits f)
1544 {
1545 NodeData *n = _p(node);
1546 type_bits o = n->m_type; // old
1547 C4_UNUSED(o);
1548 if(f & MAP)
1549 {
1550 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (f & SEQ) == 0, this, node, "cannot mark simultaneously as map and seq");
1551 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (f & VAL) == 0, this, node, "cannot mark simultaneously as map and val");
1552 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (o & SEQ) == 0, this, node, "cannot turn a seq into a map; clear first");
1553 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (o & VAL) == 0, this, node, "cannot turn a val into a map; clear first");
1554 }
1555 else if(f & SEQ)
1556 {
1557 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (f & MAP) == 0, this, node, "cannot mark simultaneously as seq and map");
1558 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (f & VAL) == 0, this, node, "cannot mark simultaneously as seq and val");
1559 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (o & MAP) == 0, this, node, "cannot turn a map into a seq; clear first");
1560 RYML_ASSERT_VISIT_MSG_CB_(m_callbacks, (o & VAL) == 0, this, node, "cannot turn a val into a seq; clear first");
1561 }
1562 if(f & KEY)
1563 {
1564 RYML_ASSERT_VISIT_CB_(m_callbacks, !is_root(node), this, node);
1565 RYML_ASSERT_VISIT_CB_(m_callbacks, is_map(parent(node)), this, node);
1566 }
1567 if((f & VAL) && !is_root(node))
1568 {
1569 auto pid = parent(node); C4_UNUSED(pid);
1570 RYML_ASSERT_VISIT_CB_(m_callbacks, is_map(pid) || is_seq(pid), this, node);
1571 }
1572 }
1573 #endif
1574
1575 void _add_flags(id_type node, type_bits f) { NodeData *d = _p(node); type_bits fb = f | d->m_type; _check_next_flags(node, fb); d->m_type = fb; }
1576 void _rem_flags(id_type node, type_bits f) { NodeData *d = _p(node); type_bits fb = d->m_type & ~f; _check_next_flags(node, fb); d->m_type = fb; }
1577
1578 id_type _do_reorder(id_type *node, id_type count);
1579
1580 void _swap(id_type n_, id_type m_);
1581 void _swap_props(id_type n_, id_type m_);
1582 void _swap_hierarchy(id_type n_, id_type m_);
1583 void _copy_hierarchy(id_type dst_, id_type src_);
1584
1585 void _copy_props(id_type dst_, id_type src_)
1586 {
1587 _copy_props(dst_, this, src_);
1588 }
1589
1590 void _copy_props_wo_key(id_type dst_, id_type src_)
1591 {
1592 _copy_props_wo_key(dst_, this, src_);
1593 }
1594
1595 void _copy_props(id_type dst_, Tree const* that_tree, id_type src_)
1596 {
1597 NodeData & C4_RESTRICT dst = *_p(dst_);
1598 NodeData const& C4_RESTRICT src = *that_tree->_p(src_);
1599 dst.m_type = src.m_type;
1600 dst.m_key = src.m_key;
1601 dst.m_val = src.m_val;
1602 }
1603
1604 void _copy_props(id_type dst_, Tree const* that_tree, id_type src_, type_bits src_mask)
1605 {
1606 NodeData & C4_RESTRICT dst = *_p(dst_);
1607 NodeData const& C4_RESTRICT src = *that_tree->_p(src_);
1608 dst.m_type = (src.m_type & src_mask) | (dst.m_type & ~src_mask);
1609 dst.m_key = src.m_key;
1610 dst.m_val = src.m_val;
1611 }
1612
1613 void _copy_props_wo_key(id_type dst_, Tree const* that_tree, id_type src_)
1614 {
1615 auto & C4_RESTRICT dst = *_p(dst_);
1616 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1617 dst.m_type = (src.m_type & ~KEYMASK_) | (dst.m_type & KEYMASK_);
1618 dst.m_val = src.m_val;
1619 }
1620
1621 void _copy_props_wo_key(id_type dst_, Tree const* that_tree, id_type src_, type_bits src_mask)
1622 {
1623 auto & C4_RESTRICT dst = *_p(dst_);
1624 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1625 dst.m_type = (src.m_type & ((~KEYMASK_)|src_mask)) | (dst.m_type & (KEYMASK_|~src_mask));
1626 dst.m_val = src.m_val;
1627 }
1628
1629 void _clear_type(id_type node)
1630 {
1631 _p(node)->m_type = NOTYPE;
1632 }
1633
1634 void _clear(id_type node)
1635 {
1636 auto *C4_RESTRICT n = _p(node);
1637 n->m_type = NOTYPE;
1638 n->m_key.clear();
1639 n->m_val.clear();
1640 n->m_parent = NONE;
1641 n->m_first_child = NONE;
1642 n->m_last_child = NONE;
1643 }
1644
1645 void _clear_key(id_type node)
1646 {
1647 _p(node)->m_key.clear();
1648 _rem_flags(node, KEY);
1649 }
1650
1651 void _clear_val(id_type node)
1652 {
1653 _p(node)->m_val.clear();
1654 _rem_flags(node, VAL);
1655 }
1656
1657 /** @endcond */
1658
1659private:
1660
1661 void _clear_range(id_type first, id_type num);
1662
1663 id_type _claim();
1664 void _claim_root();
1665 void _release(id_type node);
1666 void _free_list_add(id_type node);
1667 void _free_list_rem(id_type node);
1668
1669 void _set_hierarchy(id_type node, id_type parent, id_type after_sibling);
1670 void _rem_hierarchy(id_type node);
1671
1672public:
1673
1674 // members are exposed, but you should NOT access them directly
1675
1679
1682
1685
1687
1689
1690public:
1691
1692 /** @cond dev */ // LCOV_EXCL_START
1693 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
1694 C4_SUPPRESS_WARNING_MSVC_PUSH
1695 C4_SUPPRESS_WARNING_GCC_CLANG("-Wdeprecated")
1696 C4_SUPPRESS_WARNING_GCC_CLANG("-Wdeprecated-declarations")
1697 C4_SUPPRESS_WARNING_MSVC(4996) // deprecated
1698 RYML_DEPRECATED("use .type().type_str(buf)") const char* type_str(id_type node) const { return NodeType::type_str(_p(node)->m_type); }
1699 RYML_DEPRECATED("use has_other_siblings()") static bool has_siblings(id_type /*node*/) { return true; }
1700 RYML_DEPRECATED("use set_key()+set_val()") void to_keyval(id_type node, csubstr key, csubstr val, type_bits more_flags=0);
1701 RYML_DEPRECATED("use set_key()+set_map()") void to_map(id_type node, csubstr key, type_bits more_flags=0);
1702 RYML_DEPRECATED("use set_key()+set_seq()") void to_seq(id_type node, csubstr key, type_bits more_flags=0);
1703 RYML_DEPRECATED("use set_val()") void to_val(id_type node, csubstr val, type_bits more_flags=0);
1704 RYML_DEPRECATED("use set_map()") void to_map(id_type node, type_bits more_flags=0);
1705 RYML_DEPRECATED("use set_seq()") void to_seq(id_type node, type_bits more_flags=0);
1706 RYML_DEPRECATED("use set_doc()") void to_doc(id_type node, type_bits more_flags=0);
1707 RYML_DEPRECATED("use set_stream()") void to_stream(id_type node, type_bits more_flags=0);
1708 RYML_DEPRECATED("use resolve_tags(TagCache&)") void resolve_tags() { TagCache cache; resolve_tags(cache); }
1709 RYML_DEPRECATED("") void _set(id_type node, NodeInit const& i)
1710 {
1711 RYML_ASSERT_VISIT_CB_(m_callbacks, i._check(), this, node);
1712 NodeData *n = _p(node);
1713 RYML_ASSERT_VISIT_CB_(m_callbacks, n->m_key.scalar.empty() || i.key.scalar.empty() || i.key.scalar == n->m_key.scalar, this, node);
1714 _add_flags(node, i.type);
1715 if(n->m_key.scalar.empty())
1716 {
1717 if( ! i.key.scalar.empty())
1718 {
1719 set_key(node, i.key.scalar);
1720 }
1721 }
1722 n->m_key.tag = i.key.tag;
1723 n->m_val = i.val;
1724 }
1725 RYML_DEPRECATED("") void _set_key(id_type node, NodeScalar const& key, NodeType more_flags=0)
1726 {
1727 _p(node)->m_key = key;
1728 _add_flags(node, KEY|more_flags);
1729 }
1730 RYML_DEPRECATED("") void _set_val(id_type node, NodeScalar const& val, NodeType more_flags=0)
1731 {
1732 _p(node)->m_val = val;
1733 _add_flags(node, VAL|more_flags);
1734 }
1735 C4_SUPPRESS_WARNING_MSVC_POP
1736 C4_SUPPRESS_WARNING_GCC_CLANG_POP
1737 /** @endcond */ // LCOV_EXCL_STOP
1738};
1739
1740
1741//-----------------------------------------------------------------------------
1742//-----------------------------------------------------------------------------
1743//-----------------------------------------------------------------------------
1744
1745
1746/** @addtogroup doc_serialization_tree_write_arena
1747 *
1748 * @{
1749 */
1750
1751/** Forward to @ref scalar_serialize(), giving it the tree's arena and
1752 * resizing the arena as needed to fit the result. */
1753template<class T>
1755{
1756 substr buf = tree->arena_rem(); // buffer: the free part of the tree's arenra.
1757 again:
1758 size_t num = scalar_serialize(buf, a); // try to write into it
1759 if C4_LIKELY(num <= buf.len) // was it enough?
1760 {
1761 buf = buf.first(num); // fit the payload
1762 }
1763 else
1764 {
1765 buf = tree->_grow_arena(num); // does not advance pos
1766 goto again; // NOLINT
1767 }
1768 tree->m_arena_pos += num;
1769 return buf;
1770}
1771
1772/** @} */
1773
1774
1775/** @addtogroup doc_serialization_tree_write
1776 *
1777 * @{
1778 */
1779
1780#if (C4_CPP >= 17) || defined(__DOXYGEN__)
1781
1782/** Return extra style flags to use when setting a scalar as val.
1783 * Defaults to VAL_PLAIN for arithmetic types, or NOTYPE otherwise */
1784template<class T>
1785C4_ALWAYS_INLINE type_bits scalar_flags_val(T const&) noexcept
1786{
1787 if constexpr (std::is_arithmetic_v<T>)
1788 return VAL_PLAIN;
1789 else
1790 return NOTYPE;
1791}
1792/** Return extra style flags to use when setting a scalar as key.
1793 * Defaults to KEY_PLAIN for arithmetic types, or NOTYPE otherwise */
1794template<class T>
1795C4_ALWAYS_INLINE type_bits scalar_flags_key(T const&) noexcept
1796{
1797 if constexpr (std::is_arithmetic_v<T>)
1798 return KEY_PLAIN;
1799 else
1800 return NOTYPE;
1801}
1802
1803#else // pre-C++17 implementation: need to use SFINAE
1804
1805template<class T>
1806C4_ALWAYS_INLINE auto scalar_flags_val(T const&) noexcept
1807 -> typename std::enable_if<std::is_arithmetic<T>::value, type_bits>::type
1808{
1809 return VAL_PLAIN;
1810}
1811template<class T>
1812C4_ALWAYS_INLINE auto scalar_flags_key(T const&) noexcept
1813 -> typename std::enable_if<std::is_arithmetic<T>::value, type_bits>::type
1814{
1815 return KEY_PLAIN;
1816}
1817
1818template<class T>
1819C4_ALWAYS_INLINE auto scalar_flags_val(T const&) noexcept
1820 -> typename std::enable_if< ! std::is_arithmetic<T>::value, type_bits>::type
1821{
1822 return NOTYPE;
1823}
1824template<class T>
1825C4_ALWAYS_INLINE auto scalar_flags_key(T const&) noexcept
1826 -> typename std::enable_if< ! std::is_arithmetic<T>::value, type_bits>::type
1827{
1828 return NOTYPE;
1829}
1830
1831#endif // pre-C++17 implementation
1832
1833
1834//-----------------------------------------------------------------------------
1835
1836/** Serialize a variable to the tree's arena, and set it as the node's val. */
1837template<class T>
1838C4_ALWAYS_INLINE void write(Tree * tree, id_type id, T const& v)
1839{
1840 tree->set_val(id, serialize_to_arena(tree, v), scalar_flags_val(v));
1841}
1842
1843/** Serialize a variable to the tree's arena, and set it as the node's key.
1844 *
1845 * @warning The key MUST be a scalar value. The tree cannot handle
1846 * container keys. */
1847template<class T>
1848C4_ALWAYS_INLINE void write_key(Tree * tree, id_type id, T const& v)
1849{
1850 tree->set_key(id, serialize_to_arena(tree, v), scalar_flags_key(v));
1851}
1852
1853/** @} */
1854
1855
1856//-----------------------------------------------------------------------------
1857//-----------------------------------------------------------------------------
1858//-----------------------------------------------------------------------------
1859
1860/** @addtogroup doc_serialization_tree_read
1861 * @{
1862 */
1863
1864/** Deserialize a scalar node's val from a tree object, returning
1865 * false if the conversion failed.
1866 *
1867 * @return false if the conversion failed
1868 * @see @ref scalar_deserialize()
1869 * @see the counterpart function @ref read_key() */
1870template<class T>
1871C4_NODISCARD C4_ALWAYS_INLINE ReadResult read(Tree const* tree, id_type id, T * v)
1872{
1873 // caller only checks that type is one of VAL|MAP|SEQ (because it
1874 // can't be more concrete than that). Here, we expect a VAL so now
1875 // we can check for that:
1876 NodeData const* C4_RESTRICT nd = tree->_p(id);
1877 return ReadResult((nd->m_type & VAL) && scalar_deserialize(nd->m_val.scalar, v), id);
1878}
1879/** overload to enable use of wrapper tag-types like eg @ref
1880 * c4::fmt::base64() */
1881template<class Wrapper>
1882C4_NODISCARD C4_ALWAYS_INLINE ReadResult read(Tree const* tree, id_type id, Wrapper const& w)
1883{
1884 // caller only checks that type is one of VAL|MAP|SEQ (because it
1885 // can't be more concrete than that). Here, we expect a VAL so now
1886 // we can check for that:
1887 NodeData const* C4_RESTRICT nd = tree->_p(id);
1888 return ReadResult((nd->m_type & VAL) && from_chars(nd->m_val.scalar, w), id);
1889}
1890
1891
1892/** Deserialize a node's key from a tree object, returning false if
1893 * the conversion failed
1894 *
1895 * @return false if the conversion failed
1896 * @see the counterpart function @ref read(Tree const*, id_type, T*) */
1897template<class T>
1898C4_NODISCARD C4_ALWAYS_INLINE ReadResult read_key(Tree const* tree, id_type id, T * v)
1899{
1900 // caller already checks availability of key
1901 return ReadResult(scalar_deserialize(tree->_p(id)->m_key.scalar, v), id);
1902}
1903/** overload to enable use of wrapper tag-types like eg @ref
1904 * c4::fmt::base64() */
1905template<class Wrapper>
1906C4_NODISCARD C4_ALWAYS_INLINE ReadResult read_key(Tree const* tree, id_type id, Wrapper const& w)
1907{
1908 // caller already checks availability of key
1909 return ReadResult(from_chars(tree->_p(id)->m_key.scalar, w), id);
1910}
1911
1912/** @} */
1913
1914/** @} */
1915
1916
1917} // namespace yml
1918} // namespace c4
1919
1920// NOLINTEND(modernize-avoid-c-style-cast)
1921C4_SUPPRESS_WARNING_POP
1922
1923#endif /* C4_YML_TREE_HPP_ */
Holds a pointer to an existing tree, and a node id.
Definition node.hpp:737
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition node.hpp:1063
id_type num_other_siblings(id_type node) const
does not count with this
Definition tree.hpp:590
NodeData * m_buf
Definition tree.hpp:1676
NodeData * _p(id_type node)
An if-less form of get() that demands a valid node index. This function is implementation only; use a...
Definition tree.hpp:380
id_type m_free_head
Definition tree.hpp:1680
void set_val_style(id_type node, type_bits style)
Definition tree.hpp:660
bool has_sibling(id_type node, csubstr key) const
true if one of the node's siblings has the given key
Definition tree.hpp:549
csubstr resolve_tag_sub(substr output, csubstr tag, id_type node_id) const
Wrapper for Tree::resolve_tag(), returning a substring.
Definition tree.hpp:1145
csubstr const & key_ref(id_type node) const
Definition tree.hpp:456
csubstr const & key(id_type node) const
Definition tree.hpp:454
bool is_key_plain(id_type node) const
Definition tree.hpp:649
id_type num_siblings(id_type node) const
O(num_siblings).
Definition tree.hpp:588
id_type first_child(id_type node) const
Definition tree.hpp:577
bool is_stream(id_type node) const
Definition tree.hpp:477
void save_key(id_type node, T const &key, NodeType more_flags)
Definition tree.hpp:804
NodeType type(id_type node) const
Definition tree.hpp:452
id_type append_sibling(id_type node)
Definition tree.hpp:1197
void set_val_ref(id_type node, csubstr ref)
Definition tree.hpp:748
csubstr const & val_ref(id_type node) const
Definition tree.hpp:462
id_type root_id() const
Get the id of the root node. The tree must not be empty. The tree can be empty only when constructed ...
Definition tree.hpp:386
size_t arena_slack() const
get the current slack of the tree's internal arena
Definition tree.hpp:1313
bool has_key_tag(id_type node) const
Definition tree.hpp:486
void save(id_type node, T const &val)
Definition tree.hpp:772
id_type prev_sibling(id_type node) const
Definition tree.hpp:571
void reserve_arena(size_t arena_cap=RYML_DEFAULT_TREE_ARENA_CAPACITY)
ensure the tree's internal string arena is at least the given capacity
Definition tree.hpp:1409
id_type prepend_sibling(id_type node)
create and insert a node as the first node of parent
Definition tree.hpp:1196
size_t m_arena_pos
Definition tree.hpp:1684
void set_val(id_type node, csubstr val) RYML_NOEXCEPT
Definition tree.hpp:688
ReadResult deserialize_key(id_type node, Wrapper const &w) const
(2) like (1), but for a wrapper type like those used in tag functions
Definition tree.hpp:985
bool is_map(id_type node) const
Definition tree.hpp:480
NodeData const * _p(id_type node) const
An if-less form of get() that demands a valid node index. This function is implementation only; use a...
Definition tree.hpp:383
void set_serialized(id_type node, T const &val) RYML_NOEXCEPT
Definition tree.hpp:823
void callbacks(Callbacks const &cb)
Definition tree.hpp:350
bool is_val_anchor(id_type node) const
Definition tree.hpp:518
void set_key_serialized(id_type node, T const &key) RYML_NOEXCEPT
Definition tree.hpp:837
ReadResult deserialize_child(id_type node, csubstr child_key, T *v, T const &fallback) const
(2) like (1), but assign from fallback if no such child exists.
Definition tree.hpp:1021
id_type sibling(id_type node, id_type pos) const
Definition tree.hpp:594
void set_key_serialized(id_type node, T const &key, NodeType more_flags) RYML_NOEXCEPT
Definition tree.hpp:843
void load(id_type node, T *v, bool check_readable=true) const
(1) deserialize the node's contents (val or container) to the given variable, forwarding to the user-...
Definition tree.hpp:871
NodeScalar const & keysc(id_type node) const
Definition tree.hpp:458
c4::yml::TagDirectiveRange tag_directives() const
Definition tree.hpp:1151
bool is_key_quoted(id_type node) const
Definition tree.hpp:651
csubstr const & val(id_type node) const
Definition tree.hpp:460
bool has_val_anchor(id_type node) const
Definition tree.hpp:489
void clear_arena()
Definition tree.hpp:340
id_type insert_child(id_type parent, id_type after)
create and insert a new child of parent.
Definition tree.hpp:1168
TagDirectives m_tag_directives
Definition tree.hpp:1688
bool is_root(id_type node) const
Definition tree.hpp:529
bool key_is_null(id_type node) const
true if the node key is empty, or its scalar verifies scalar_is_null().
Definition tree.hpp:504
NodeRef ref(id_type node)
Get a NodeRef of a node by id.
Definition tree.cpp:70
substr alloc_arena(size_t sz)
grow the tree's string arena by the given size and return a substr of the added portion
Definition tree.hpp:1397
bool is_keyval(id_type node) const
Definition tree.hpp:485
bool is_val_folded(id_type node) const
Definition tree.hpp:644
void set_key(id_type node, csubstr key, NodeType more_flags) RYML_NOEXCEPT
Definition tree.hpp:712
void set_serialized(id_type node, T const &val, NodeType more_flags) RYML_NOEXCEPT
Definition tree.hpp:829
id_type m_size
Definition tree.hpp:1678
id_type last_sibling(id_type node) const
Definition tree.hpp:593
static ReadResult to_result_(ReadResult notlegacy, id_type) noexcept
Definition tree.hpp:862
bool has_key(id_type node) const
Definition tree.hpp:482
bool has_other_siblings(id_type node) const
true if node is not a single child
Definition tree.hpp:551
csubstr const & val_tag(id_type node) const
Definition tree.hpp:461
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
Definition tree.hpp:1327
ReadResult deserialize_child(id_type node, id_type child_pos, Wrapper const &wrapper) const
(3) like (1), but for wrapper tag types such as c4::fmt::base64()
Definition tree.hpp:1073
id_type parent(id_type node) const
Definition tree.hpp:569
bool is_key_unfiltered(id_type node) const
true if the key was a scalar requiring filtering and was left unfiltered during the parsing (see Pars...
Definition tree.hpp:512
void rem_key_anchor(id_type node)
Definition tree.hpp:750
size_t resolve_tag(substr output, csubstr tag, id_type node_id) const
resolve the given tag, appearing at node_id.
Definition tree.cpp:1456
bool is_key_literal(id_type node) const
Definition tree.hpp:641
NodeType key_style(id_type node) const
Definition tree.hpp:655
bool is_block(id_type node) const
Definition tree.hpp:629
id_type prepend_child(id_type parent)
create and insert a node as the first child of parent
Definition tree.hpp:1178
bool is_val(id_type node) const
Definition tree.hpp:484
bool is_container_styled(id_type node) const
Definition tree.hpp:628
bool empty() const
Query for zero size.
Definition tree.hpp:343
ReadResult find_sibling_r(id_type node, csubstr const &key, id_type *sibling_id) const
like Tree::find_sibling(), but return a ReadResult if with the status
Definition tree.hpp:599
void load_key(id_type node, T *k, bool check_readable=true) const
(1) deserialize the node's key (necessarily a scalar) to the given variable, forwarding to the user-o...
Definition tree.hpp:912
void set_seq(id_type node) RYML_NOEXCEPT
Definition tree.hpp:720
void set_stream(id_type node)
Definition tree.hpp:675
id_type sibling_pos(id_type node, id_type sib) const
Definition tree.hpp:591
id_type child_pos(id_type node, id_type ch) const
Definition tree.cpp:1224
id_type append_child(id_type parent)
create and insert a node as the last child of parent
Definition tree.hpp:1180
ReadResult find_child_r(id_type node, csubstr const &key, id_type *child_id) const
like Tree::find_child(), but return a ReadResult with the status
Definition tree.hpp:584
bool has_sibling(id_type node, id_type sib) const
true if node has a sibling with id sib
Definition tree.hpp:547
ReadResult deserialize(id_type node, T *v) const
(1) deserialize a node's contents to a variable
Definition tree.hpp:954
id_type next_sibling(id_type node) const
Definition tree.hpp:572
bool is_flow_mln(id_type node) const
Definition tree.hpp:634
bool is_key_squo(id_type node) const
Definition tree.hpp:645
bool parent_is_seq(id_type node) const
Definition tree.hpp:495
void save_key(id_type node, T const &key)
Definition tree.hpp:794
bool has_anchor(id_type node) const
Definition tree.hpp:490
id_type insert_sibling(id_type node, id_type after)
create and insert a new sibling of n. insert after "after"
Definition tree.hpp:1191
bool has_val_tag(id_type node) const
Definition tree.hpp:487
NodeData const * get(id_type node) const
get a pointer to a node's NodeData. node can be NONE, in which case a nullptr is returned.
Definition tree.hpp:370
ReadResult sibling_r(id_type node, id_type pos, id_type *sibling_id) const
like Tree::sibling(), but return a ReadResult with the status
Definition tree.hpp:597
bool is_flow_ml1(id_type node) const
Definition tree.hpp:633
bool is_key_dquo(id_type node) const
Definition tree.hpp:647
void set_key_tag(id_type node, csubstr tag)
Definition tree.hpp:742
void set_container_style(id_type node, type_bits style)
Definition tree.hpp:658
id_type m_cap
Definition tree.hpp:1677
void rem_anchor_ref(id_type node)
Definition tree.hpp:754
id_type last_child(id_type node) const
Definition tree.hpp:578
csubstr const & key_tag(id_type node) const
Definition tree.hpp:455
id_type id(NodeData const *n) const
get the id of a node belonging to this tree. n can be nullptr, in which case NONE is returned n must ...
Definition tree.hpp:392
bool type_has_any(id_type node, type_bits bits) const
Definition tree.hpp:473
substr arena_rem()
get the free space at the end of the arena
Definition tree.hpp:1324
substr m_arena
Definition tree.hpp:1683
void set_map(id_type node, NodeType more_flags) RYML_NOEXCEPT
Definition tree.hpp:736
csubstr to_arena(T const &a)
serialize the given variable to the tree's arena, growing it as needed to accomodate the serializatio...
Definition tree.hpp:1349
void set_val_anchor(id_type node, csubstr anchor)
Definition tree.hpp:746
bool is_val_plain(id_type node) const
Definition tree.hpp:650
bool is_doc(id_type node) const
Definition tree.hpp:478
bool has_child(id_type node, csubstr key) const
true if node has a child with key key
Definition tree.hpp:542
void set_val(id_type node, csubstr val, NodeType more_flags) RYML_NOEXCEPT
Definition tree.hpp:696
ReadResult deserialize(id_type node, Wrapper const &w) const
(2) like (1), but for a wrapper type like those used in tag functions
Definition tree.hpp:964
ReadResult deserialize_child(id_type node, id_type child_pos, T *v, T const &fallback) const
(2) like (1), but assign from fallback if no such child exists
Definition tree.hpp:1061
void set_seq(id_type node, NodeType more_flags) RYML_NOEXCEPT
Definition tree.hpp:725
id_type root_id_maybe() const
Get the id of the root node, or NONE if the tree is empty.
Definition tree.hpp:388
bool is_key_ref(id_type node) const
Definition tree.hpp:491
bool is_key_anchor(id_type node) const
Definition tree.hpp:517
void remove_children(id_type node)
remove all the node's children, but keep the node itself
Definition tree.cpp:917
bool type_has_all(id_type node, type_bits bits) const
Definition tree.hpp:474
bool has_val(id_type node) const
Definition tree.hpp:483
bool is_val_dquo(id_type node) const
Definition tree.hpp:648
Callbacks const & callbacks() const
Definition tree.hpp:349
size_t arena_capacity() const
get the current capacity of the tree's internal arena
Definition tree.hpp:1311
id_type doc(id_type i) const
gets the i document node index.
Definition tree.hpp:605
bool change_type(id_type node, NodeType type)
Definition tree.cpp:940
bool is_flow_ml(id_type node) const
Definition tree.hpp:632
NodeType val_style(id_type node) const
Definition tree.hpp:656
bool is_key_folded(id_type node) const
Definition tree.hpp:643
size_t arena_pos() const
Definition tree.hpp:1314
csubstr arena_rem() const
get the free space at the end of the arena
Definition tree.hpp:1322
Tree(id_type node_capacity, size_t arena_capacity=RYML_DEFAULT_TREE_ARENA_CAPACITY)
Definition tree.hpp:316
id_type m_free_tail
Definition tree.hpp:1681
bool is_anchor_or_ref(id_type node) const
Definition tree.hpp:520
id_type slack() const
Definition tree.hpp:347
bool is_anchor(id_type node) const
Definition tree.hpp:519
id_type find_sibling(id_type node, csubstr const &key) const
Definition tree.hpp:595
bool is_seq(id_type node) const
Definition tree.hpp:481
bool is_val_styled(id_type node) const
Definition tree.hpp:640
bool parent_is_map(id_type node) const
Definition tree.hpp:496
void set_map(id_type node) RYML_NOEXCEPT
Definition tree.hpp:731
bool has_key_anchor(id_type node) const
Definition tree.hpp:488
void remove(id_type node)
remove an entire branch at once: ie remove the children and the node itself
Definition tree.hpp:1202
ReadResult deserialize_key(id_type node, T *v) const
(1) deserialize a node's key to a variable
Definition tree.hpp:975
id_type find_child(id_type node, csubstr const &key) const
find child by name, or NONE if no child is found with this key like Tree::child(),...
Definition tree.cpp:1249
void save(id_type node, T const &val, NodeType more_flags)
Definition tree.hpp:782
bool type_has_none(id_type node, type_bits bits) const
Definition tree.hpp:475
bool is_quoted(id_type node) const
Definition tree.hpp:653
csubstr const & val_anchor(id_type node) const
Definition tree.hpp:463
bool change_type(id_type node, type_bits type)
Definition tree.hpp:1247
NodeData * get(id_type node)
get a pointer to a node's NodeData. node can be NONE, in which case a nullptr is returned
Definition tree.hpp:361
substr arena()
get the current arena
Definition tree.hpp:1319
void rem_val_ref(id_type node)
Definition tree.hpp:753
bool has_parent(id_type node) const
Definition tree.hpp:531
bool val_is_null(id_type node) const
true if the node val is empty, or its scalar verifies scalar_is_null().
Definition tree.hpp:508
id_type first_sibling(id_type node) const
Definition tree.hpp:592
void set_key_style(id_type node, type_bits style)
Definition tree.hpp:659
bool is_flow(id_type node) const
Definition tree.hpp:636
bool is_val_quoted(id_type node) const
Definition tree.hpp:652
bool empty(id_type node) const
true when key and val are empty, and has no children
Definition tree.hpp:537
csubstr const & key_anchor(id_type node) const
Definition tree.hpp:457
bool is_val_squo(id_type node) const
Definition tree.hpp:646
substr copy_to_arena(csubstr s)
copy the given string to the tree's arena, growing the arena by the required size.
Definition tree.hpp:1370
ReadResult deserialize_child(id_type node, csubstr child_key, Wrapper const &wrapper) const
(3) like (1), but for wrapper tag types such as c4::fmt::base64()
Definition tree.hpp:1031
void set_key(id_type node, csubstr key) RYML_NOEXCEPT
Definition tree.hpp:705
bool has_flow_space(id_type node) const
Definition tree.hpp:637
id_type num_children(id_type node) const
O(num_children).
Definition tree.cpp:1216
csubstr arena() const
get the current arena
Definition tree.hpp:1317
id_type size() const
Definition tree.hpp:345
bool is_container(id_type node) const
Definition tree.hpp:479
void set_doc(id_type node)
Definition tree.hpp:682
bool is_flow_mlx(id_type node) const
Definition tree.hpp:635
ReadResult deserialize_child(id_type node, id_type child_pos, T *v) const
(1) find a child by position and deserialize its contents to the given variable (ie call ....
Definition tree.hpp:1050
size_t arena_size() const
get the current size of the tree's internal arena
Definition tree.hpp:1309
id_type child(id_type node, id_type pos) const
find child by position, or NONE if there are less than pos children posi
Definition tree.cpp:1237
bool is_ref(id_type node) const
Definition tree.hpp:493
void set_val_tag(id_type node, csubstr tag)
Definition tree.hpp:743
bool is_key_styled(id_type node) const
Definition tree.hpp:639
void set_key_anchor(id_type node, csubstr anchor)
Definition tree.hpp:745
bool is_val_ref(id_type node) const
Definition tree.hpp:492
void set_key_ref(id_type node, csubstr ref)
Definition tree.hpp:747
void load(id_type node, Wrapper const &w, bool check_readable=true) const
(2) like (1), but for wrapper tag types such as c4::fmt::base64()
Definition tree.hpp:888
id_type _append_child__unprotected(id_type parent)
Definition tree.hpp:1181
bool has_child(id_type node, id_type ch) const
true if node has a child with id ch
Definition tree.hpp:540
bool is_val_literal(id_type node) const
Definition tree.hpp:642
void rem_val_anchor(id_type node)
Definition tree.hpp:751
NodeScalar const & valsc(id_type node) const
Definition tree.hpp:464
Callbacks m_callbacks
Definition tree.hpp:1686
bool is_val_unfiltered(id_type node) const
true if the val was a scalar requiring filtering and was left unfiltered during the parsing (see Pars...
Definition tree.hpp:515
ReadResult deserialize_child(id_type node, csubstr child_key, T *v) const
(1) find a child by name and deserialize its contents to the given variable (ie call ....
Definition tree.hpp:1011
void load_key(id_type node, Wrapper const &w, bool check_readable=true) const
(2) like (1), but for wrapper tag types such as c4::fmt::base64()
Definition tree.hpp:929
void rem_key_ref(id_type node)
Definition tree.hpp:752
ReadResult child_r(id_type node, id_type pos, id_type *child_id) const
Definition tree.hpp:582
id_type capacity() const
Definition tree.hpp:346
bool is_flow_sl(id_type node) const
Definition tree.hpp:630
id_type ancestor_doc(id_type node) const
get the document which is a parent document of node i, or the root if the tree is not a stream
Definition tree.hpp:608
bool has_children(id_type node) const
true if node has any children
Definition tree.hpp:544
static ReadResult to_result_(bool, id_type node) noexcept
Definition tree.hpp:861
bool has_anchor(id_type node, csubstr a) const
true when the node has an anchor named a
Definition tree.hpp:499
Common utilities and infrastructure used by ryml.
#define RYML_DEFAULT_TREE_CAPACITY
default capacity for the tree when not set explicitly
Definition common.hpp:21
#define RYML_DEFAULT_TREE_ARENA_CAPACITY
default capacity for the tree's arena when not set explicitly
Definition common.hpp:26
#define RYML_DEFAULT_TREE_ARENA_CAPACITY_START
default starting capacity for the tree's arena when it is first allocated. should be larger than RYML...
Definition common.hpp:32
#define RYML_NOEXCEPT
Conditionally expands to noexcept when RYML_USE_ASSERT is 0 and is empty otherwise.
Error utilities used by ryml.
#define RYML_EXPORT
Definition export.hpp:18
forward declarations
Callbacks const & get_callbacks()
get the global callbacks
Definition common.cpp:94
bool from_chars(csubstr buf, uint8_t *v) noexcept
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition node_type.hpp:26
@ VALANCH
the val has an &anchor
Definition node_type.hpp:42
@ NOTYPE
no node type or style is set
Definition node_type.hpp:32
@ VALREF
a *reference: the val references an &anchor
Definition node_type.hpp:40
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition node_type.hpp:35
@ STREAM
a stream: a seq of docs
Definition node_type.hpp:38
@ KEY
the scalar to the left of : in a map's member
Definition node_type.hpp:33
@ KEYTAG
the key has a tag
Definition node_type.hpp:43
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
Definition node_type.hpp:34
@ VALTAG
the val has a tag
Definition node_type.hpp:44
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
Definition node_type.hpp:36
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
@ KEYREF
a *reference: the key references an &anchor
Definition node_type.hpp:39
@ KEYVAL
mask of KEY|VAL
@ KEYANCH
the key has an &anchor
Definition node_type.hpp:41
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
@ DOC
a document
Definition node_type.hpp:37
ParseEngine< EventHandlerTree > Parser
This is the main ryml parser, where the parser events are handled to create a ryml tree (see Event Ha...
Definition fwd.hpp:19
ryml::ReadResult read(ryml::Tree const *tree, ryml::id_type id, my_seq_type< T > *seq)
void write(ryml::Tree *tree, ryml::id_type id, my_seq_type< T > const &seq)
bool from_chars(ryml::csubstr buf, vec2< T > *v)
size_t scalar_serialize(substr buf, T const &a)
Serialize a scalar to the buffer, dispatching to to_chars() or to_chars_float() as appropriate.
bool scalar_is_null(csubstr s) noexcept
YAML-sense query of nullity.
bool scalar_deserialize(csubstr str, T *val)
Deserialize a scalar from its string representation, dispatching to one of from_chars(),...
ReadResult read_key(ConstNodeRef const &n, T *v)
Definition node.hpp:2088
void write_key(NodeRef *n, T const &v)
Definition node.hpp:2119
csubstr serialize_to_arena(Tree *tree, T const &scalar)
Serialize a scalar to a tree's arena, dispatching to either serialize_to_arena_scalar() or serialize_...
Definition tree.hpp:109
csubstr serialize_to_arena_str(Tree *tree, csubstr scalar)
Serialize a string type (as specified by c4::is_string) to a tree's arena, ensuring that there is an ...
Definition tree.cpp:27
csubstr serialize_to_arena_scalar(Tree *tree, T const &scalar)
Serialize a scalar to the tree's arena.
Definition tree.hpp:1754
type_bits scalar_flags_val(T const &) noexcept
Return extra style flags to use when setting a scalar as val.
Definition tree.hpp:1785
type_bits scalar_flags_key(T const &) noexcept
Return extra style flags to use when setting a scalar as key.
Definition tree.hpp:1795
csubstr to_csubstr(const char(&s)[N]) noexcept
Definition substr.hpp:2380
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2355
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2356
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
Definition common.hpp:124
@ NONE
an index to none
Definition common.hpp:131
bool begins_with(const C c) const noexcept
true if the first character of the string is c
Definition substr.hpp:850
basic_substring triml(const C c) const
trim left
Definition substr.hpp:629
size_t len
the length of the substring
Definition substr.hpp:218
bool ends_with(const C c) const noexcept
true if the last character of the string is c
Definition substr.hpp:894
bool overlaps(ro_substr const that) const noexcept
true if there is overlap of at least one element between that and *this
Definition substr.hpp:493
basic_substring first(size_t num) const noexcept
return the first num elements: [0,num[
Definition substr.hpp:529
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition substr.hpp:502
C * str
a restricted pointer to the first character of the substring
Definition substr.hpp:216
A c-style callbacks class to customize behavior on errors or allocation.
Definition common.hpp:374
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition common.hpp:229
contains the data for each YAML node.
Definition tree.hpp:288
NodeType m_type
Definition tree.hpp:289
id_type m_next_sibling
Definition tree.hpp:297
id_type m_parent
Definition tree.hpp:294
NodeScalar m_key
Definition tree.hpp:291
id_type m_prev_sibling
Definition tree.hpp:298
id_type m_first_child
Definition tree.hpp:295
NodeScalar m_val
Definition tree.hpp:292
id_type m_last_child
Definition tree.hpp:296
a node scalar is a csubstr, which may be tagged and anchored.
Definition tree.hpp:170
NodeScalar(const char(&t)[N], const char(&s)[N]) noexcept
initialize as a tagged scalar
Definition tree.hpp:187
bool empty() const noexcept
Definition tree.hpp:200
NodeScalar() noexcept
initialize as an empty scalar
Definition tree.hpp:178
NodeScalar(csubstr s) noexcept
Definition tree.hpp:183
NodeScalar(csubstr t, csubstr s) noexcept
Definition tree.hpp:188
void clear() noexcept
Definition tree.hpp:202
NodeScalar(const char(&s)[N]) noexcept
initialize as an untagged scalar
Definition tree.hpp:182
~NodeScalar() noexcept=default
void set_ref_maybe_replacing_scalar(csubstr ref, bool has_scalar) RYML_NOEXCEPT
Definition tree.hpp:204
Wraps a type_bits mask of NodeTypeBits flags with some syntactic sugar and predicates.
bool is_doc() const noexcept
const char * type_str() const noexcept
return a preset string based on the node type
A lightweight truthy type, used to enable reporting the offending node when a deserializing error hap...
Definition common.hpp:162
Reusable object to resolve references/aliases in a Tree.
Accelerator structure to reduce memory requirements by enabling reuse of resolved tags.
Definition tag.hpp:71
lookup_result(csubstr path_, id_type start)
Definition tree.hpp:1478