rapidyaml 0.14.0
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_TYPES_HPP_
10#include "c4/types.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_CHARCONV_HPP_
28#include <c4/charconv.hpp>
29#endif
30
31#include <cmath>
32#include <limits>
33
34
35C4_SUPPRESS_WARNING_MSVC_PUSH
36C4_SUPPRESS_WARNING_MSVC(4251) // needs to have dll-interface to be used by clients of struct
37C4_SUPPRESS_WARNING_MSVC(4296) // expression is always 'boolean_value'
38C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
39C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
40C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
41C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
42
43// NOLINTBEGIN(modernize-avoid-c-style-cast)
44
45namespace c4 {
46namespace yml {
47
48/** @cond dev */
49template<class T> inline auto read(Tree const* C4_RESTRICT tree, id_type id, T *v) -> typename std::enable_if<!std::is_arithmetic<T>::value, bool>::type;
50template<class T> inline auto read(Tree const* C4_RESTRICT tree, id_type id, T *v) -> typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value, bool>::type;
51template<class T> inline auto read(Tree const* C4_RESTRICT tree, id_type id, T *v) -> typename std::enable_if<std::is_floating_point<T>::value, bool>::type;
52template<class T> bool read(Tree const* C4_RESTRICT tree, id_type id, T const& wrapper);
53
54template<class T> inline auto readkey(Tree const* C4_RESTRICT tree, id_type id, T *v) -> typename std::enable_if<!std::is_arithmetic<T>::value, bool>::type;
55template<class T> inline auto readkey(Tree const* C4_RESTRICT tree, id_type id, T *v) -> typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value, bool>::type;
56template<class T> inline auto readkey(Tree const* C4_RESTRICT tree, id_type id, T *v) -> typename std::enable_if<std::is_floating_point<T>::value, bool>::type;
57template<class T> bool readkey(Tree const* C4_RESTRICT tree, id_type id, T const& wrapper);
58/** @endcond */
59
60
61template<class T> size_t to_chars_float(substr buf, T val);
62template<class T> bool from_chars_float(csubstr buf, T *C4_RESTRICT val);
63
64
65template<class T>
66C4_ALWAYS_INLINE auto serialize_scalar(substr buf, T const& C4_RESTRICT a)
67 -> typename std::enable_if<std::is_floating_point<T>::value, size_t>::type
68{
69 return to_chars_float(buf, a);
70}
71template<class T>
72C4_ALWAYS_INLINE auto serialize_scalar(substr buf, T const& C4_RESTRICT a)
73 -> typename std::enable_if< ! std::is_floating_point<T>::value, size_t>::type
74{
75 return to_chars(buf, a);
76}
77
78
79template<class T>
80csubstr serialize_to_arena(Tree * C4_RESTRICT tree, T const& C4_RESTRICT a);
81
82RYML_EXPORT csubstr serialize_to_arena(Tree * C4_RESTRICT tree, csubstr a);
83
84// these overloads are needed to ensure that these types are not
85// dispatched to the general template overload
86C4_ALWAYS_INLINE csubstr serialize_to_arena(Tree * C4_RESTRICT tree, substr a)
87{
88 return serialize_to_arena(tree, csubstr(a));
89}
90C4_ALWAYS_INLINE csubstr serialize_to_arena(Tree * C4_RESTRICT tree, const char *a)
91{
92 return serialize_to_arena(tree, to_csubstr(a));
93}
94C4_ALWAYS_INLINE csubstr serialize_to_arena(Tree * C4_RESTRICT, std::nullptr_t)
95{
96 return csubstr{};
97}
98
99
100
101//-----------------------------------------------------------------------------
102//-----------------------------------------------------------------------------
103//-----------------------------------------------------------------------------
104
105
106/** @addtogroup doc_tree
107 *
108 * @{
109 */
110
111/** a node scalar is a csubstr, which may be tagged and anchored. */
113{
117
118public:
119
120 /// initialize as an empty scalar
121 NodeScalar() noexcept : tag(), scalar(), anchor() {} // NOLINT
122
123 /// initialize as an untagged scalar
124 template<size_t N>
125 NodeScalar(const char (&s)[N]) noexcept : tag(), scalar(s), anchor() {}
126 NodeScalar(csubstr s ) noexcept : tag(), scalar(s), anchor() {}
127
128 /// initialize as a tagged scalar
129 template<size_t N, size_t M>
130 NodeScalar(const char (&t)[N], const char (&s)[N]) noexcept : tag(t), scalar(s), anchor() {}
131 NodeScalar(csubstr t , csubstr s ) noexcept : tag(t), scalar(s), anchor() {}
132
133public:
134
135 ~NodeScalar() noexcept = default;
136 NodeScalar(NodeScalar &&) noexcept = default;
137 NodeScalar(NodeScalar const&) noexcept = default;
138 NodeScalar& operator= (NodeScalar &&) noexcept = default;
139 NodeScalar& operator= (NodeScalar const&) noexcept = default;
140
141public:
142
143 bool empty() const noexcept { return tag.empty() && scalar.empty() && anchor.empty(); }
144
145 void clear() noexcept { tag.clear(); scalar.clear(); anchor.clear(); }
146
148 {
149 csubstr trimmed = ref.begins_with('*') ? ref.sub(1) : ref;
150 anchor = trimmed;
151 if((!has_scalar) || !scalar.ends_with(trimmed))
152 scalar = ref;
153 }
154};
155C4_MUST_BE_TRIVIAL_COPY(NodeScalar);
156
157
158//-----------------------------------------------------------------------------
159//-----------------------------------------------------------------------------
160//-----------------------------------------------------------------------------
161
162/** convenience class to initialize nodes */
164{
165
169
170public:
171
172 /// initialize as an empty node
173 NodeInit() : type(NOTYPE), key(), val() {}
174 /// initialize as a typed node
175 NodeInit(NodeType_e t) : type(t), key(), val() {}
176 /// initialize as a sequence member
177 NodeInit(NodeScalar const& v) : type(VAL), key(), val(v) { _add_flags(); }
178 /// initialize as a sequence member with explicit type
179 NodeInit(NodeScalar const& v, NodeType_e t) : type(t|VAL), key(), val(v) { _add_flags(); }
180 /// initialize as a mapping member
181 NodeInit( NodeScalar const& k, NodeScalar const& v) : type(KEYVAL), key(k), val(v) { _add_flags(); }
182 /// initialize as a mapping member with explicit type
183 NodeInit(NodeType_e t, NodeScalar const& k, NodeScalar const& v) : type(t), key(k), val(v) { _add_flags(); }
184 /// initialize as a mapping member with explicit type (eg for SEQ or MAP)
185 NodeInit(NodeType_e t, NodeScalar const& k ) : type(t), key(k), val( ) { _add_flags(KEY); }
186
187public:
188
189 void clear()
190 {
191 type.clear();
192 key.clear();
193 val.clear();
194 }
195
196 void _add_flags(type_bits more_flags=0)
197 {
198 type = (type|more_flags);
199 if( ! key.tag.empty())
200 type = (type|KEYTAG);
201 if( ! val.tag.empty())
202 type = (type|VALTAG);
203 if( ! key.anchor.empty())
204 type = (type|KEYANCH);
205 if( ! val.anchor.empty())
206 type = (type|VALANCH);
207 }
208
209 bool _check() const
210 {
211 // key cannot be empty
212 _RYML_ASSERT_BASIC(key.scalar.empty() == ((type & KEY) == 0));
213 // key tag cannot be empty
214 _RYML_ASSERT_BASIC(key.tag.empty() == ((type & KEYTAG) == 0));
215 // val may be empty even though VAL is set. But when VAL is not set, val must be empty
216 _RYML_ASSERT_BASIC(((type & VAL) != 0) || val.scalar.empty());
217 // val tag cannot be empty
218 _RYML_ASSERT_BASIC(val.tag.empty() == ((type & VALTAG) == 0));
219 return true;
220 }
221};
222
223
224//-----------------------------------------------------------------------------
225//-----------------------------------------------------------------------------
226//-----------------------------------------------------------------------------
227
228/** contains the data for each YAML node. */
242C4_MUST_BE_TRIVIAL_COPY(NodeData);
243
244
245//-----------------------------------------------------------------------------
246//-----------------------------------------------------------------------------
247//-----------------------------------------------------------------------------
248
250{
251public:
252
253 /** @name construction and assignment */
254 /** @{ */
255
257 Tree(Callbacks const& cb);
259 Tree(id_type node_capacity, size_t arena_capacity, Callbacks const& cb);
260
261 ~Tree();
262
263 Tree(Tree const& that);
264 Tree(Tree && that) noexcept;
265
266 Tree& operator= (Tree const& that);
267 Tree& operator= (Tree && that) noexcept;
268
269 /** @} */
270
271public:
272
273 /** @name memory and sizing */
274 /** @{ */
275
276 void reserve(id_type node_capacity=RYML_DEFAULT_TREE_CAPACITY);
277
278 /** clear the tree and zero every node
279 * @note does NOT clear the arena
280 * @see clear_arena() */
281 void clear();
282 void clear_arena() { m_arena_pos = 0; }
283
284 bool empty() const { return m_size == 0; }
285
286 id_type size() const { return m_size; }
287 id_type capacity() const { return m_cap; }
288 id_type slack() const { _RYML_ASSERT_BASIC(m_cap >= m_size); return m_cap - m_size; }
289
290 Callbacks const& callbacks() const { return m_callbacks; }
291 void callbacks(Callbacks const& cb) { m_callbacks = cb; }
292
293 /** @} */
294
295public:
296
297 /** @name node getters */
298 /** @{ */
299
300 //! get the index of a node belonging to this tree.
301 //! @p n can be nullptr, in which case NONE is returned
302 id_type id(NodeData const* n) const
303 {
304 if( ! n)
305 return NONE;
306 _RYML_ASSERT_VISIT_(m_callbacks, n >= m_buf && n < m_buf + m_cap, this, NONE);
307 return static_cast<id_type>(n - m_buf);
308 }
309
310 //! get a pointer to a node's NodeData.
311 //! i can be NONE, in which case a nullptr is returned
312 NodeData *get(id_type node) // NOLINT(readability-make-member-function-const)
313 {
314 if(node == NONE)
315 return nullptr;
316 _RYML_ASSERT_VISIT_(m_callbacks, node >= 0 && node < m_cap, this, node);
317 return m_buf + node;
318 }
319 //! get a pointer to a node's NodeData.
320 //! i can be NONE, in which case a nullptr is returned.
321 NodeData const *get(id_type node) const
322 {
323 if(node == NONE)
324 return nullptr;
325 _RYML_ASSERT_VISIT_(m_callbacks, node >= 0 && node < m_cap, this, node);
326 return m_buf + node;
327 }
328
329 //! An if-less form of get() that demands a valid node index.
330 //! This function is implementation only; use at your own risk.
331 NodeData * _p(id_type node) { _RYML_ASSERT_VISIT_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node); return m_buf + node; } // NOLINT(readability-make-member-function-const)
332 //! An if-less form of get() that demands a valid node index.
333 //! This function is implementation only; use at your own risk.
334 NodeData const * _p(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, node != NONE && node >= 0 && node < m_cap, this, node); return m_buf + node; }
335
336 //! Get the id of the root node. The tree must not be empty.
337 id_type root_id() const { _RYML_ASSERT_VISIT_(m_callbacks, m_cap > 0 && m_size > 0, this, id_type(0)); return 0; }
338
339 //! Get a NodeRef of a node by id
340 NodeRef ref(id_type node);
341 //! Get a NodeRef of a node by id
342 ConstNodeRef ref(id_type node) const;
343 //! Get a NodeRef of a node by id
344 ConstNodeRef cref(id_type node) const;
345
346 //! Get the root as a NodeRef
347 NodeRef rootref();
348 //! Get the root as a ConstNodeRef
349 ConstNodeRef rootref() const;
350 //! Get the root as a ConstNodeRef
351 ConstNodeRef crootref() const;
352
353 //! get the i-th document of the stream
354 //! @note @p i is NOT the node id, but the doc position within the stream
355 NodeRef docref(id_type i);
356 //! get the i-th document of the stream
357 //! @note @p i is NOT the node id, but the doc position within the stream
358 ConstNodeRef docref(id_type i) const;
359 //! get the i-th document of the stream
360 //! @note @p i is NOT the node id, but the doc position within the stream
361 ConstNodeRef cdocref(id_type i) const;
362
363 //! find a root child (ie child of root) by name, return it as a NodeRef
364 //! @note requires the root to be a map.
365 NodeRef operator[] (csubstr key);
366 //! find a root child (ie child of root) by name, return it as a NodeRef
367 //! @note requires the root to be a map.
368 ConstNodeRef operator[] (csubstr key) const;
369
370 //! find a root child (ie child of root) by index: return the root node's @p i-th child as a NodeRef
371 //! @note @p i is NOT the node id, but the child's position
372 NodeRef operator[] (id_type i);
373 //! find a root child (ie child of root) by index: return the root node's @p i-th child as a NodeRef
374 //! @note @p i is NOT the node id, but the child's position
375 ConstNodeRef operator[] (id_type i) const;
376
377 /** @} */
378
379public:
380
381 /** @name node property getters */
382 /** @{ */
383
384 NodeType type(id_type node) const { return _p(node)->m_type; }
385 const char* type_str(id_type node) const { return NodeType::type_str(_p(node)->m_type); }
386
387 csubstr const& key (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_key(node), this, node); return _p(node)->m_key.scalar; }
388 csubstr const& key_tag (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_key_tag(node), this, node); return _p(node)->m_key.tag; }
389 csubstr const& key_ref (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, is_key_ref(node), this, node); return _p(node)->m_key.anchor; }
390 csubstr const& key_anchor(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_key_anchor(node), this, node); return _p(node)->m_key.anchor; }
391 NodeScalar const& keysc (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_key(node), this, node); return _p(node)->m_key; }
392
393 csubstr const& val (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_val(node), this, node); return _p(node)->m_val.scalar; }
394 csubstr const& val_tag (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_val_tag(node), this, node); return _p(node)->m_val.tag; }
395 csubstr const& val_ref (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, is_val_ref(node), this, node); return _p(node)->m_val.anchor; }
396 csubstr const& val_anchor(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_val_anchor(node), this, node); return _p(node)->m_val.anchor; }
397 NodeScalar const& valsc (id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_val(node), this, node); return _p(node)->m_val; }
398
399 /** @} */
400
401public:
402
403 /** @name node type predicates */
404 /** @{ */
405
406 C4_ALWAYS_INLINE bool type_has_any(id_type node, NodeType_e bits) const { return _p(node)->m_type.has_any(bits); }
407 C4_ALWAYS_INLINE bool type_has_all(id_type node, NodeType_e bits) const { return _p(node)->m_type.has_all(bits); }
408 C4_ALWAYS_INLINE bool type_has_none(id_type node, NodeType_e bits) const { return _p(node)->m_type.has_none(bits); }
409
410 C4_ALWAYS_INLINE bool is_stream(id_type node) const { return _p(node)->m_type.is_stream(); }
411 C4_ALWAYS_INLINE bool is_doc(id_type node) const { return _p(node)->m_type.is_doc(); }
412 C4_ALWAYS_INLINE bool is_container(id_type node) const { return _p(node)->m_type.is_container(); }
413 C4_ALWAYS_INLINE bool is_map(id_type node) const { return _p(node)->m_type.is_map(); }
414 C4_ALWAYS_INLINE bool is_seq(id_type node) const { return _p(node)->m_type.is_seq(); }
415 C4_ALWAYS_INLINE bool has_key(id_type node) const { return _p(node)->m_type.has_key(); }
416 C4_ALWAYS_INLINE bool has_val(id_type node) const { return _p(node)->m_type.has_val(); }
417 C4_ALWAYS_INLINE bool is_val(id_type node) const { return _p(node)->m_type.is_val(); }
418 C4_ALWAYS_INLINE bool is_keyval(id_type node) const { return _p(node)->m_type.is_keyval(); }
419 C4_ALWAYS_INLINE bool has_key_tag(id_type node) const { return _p(node)->m_type.has_key_tag(); }
420 C4_ALWAYS_INLINE bool has_val_tag(id_type node) const { return _p(node)->m_type.has_val_tag(); }
421 C4_ALWAYS_INLINE bool has_key_anchor(id_type node) const { return _p(node)->m_type.has_key_anchor(); }
422 C4_ALWAYS_INLINE bool has_val_anchor(id_type node) const { return _p(node)->m_type.has_val_anchor(); }
423 C4_ALWAYS_INLINE bool has_anchor(id_type node) const { return _p(node)->m_type.has_anchor(); }
424 C4_ALWAYS_INLINE bool is_key_ref(id_type node) const { return _p(node)->m_type.is_key_ref(); }
425 C4_ALWAYS_INLINE bool is_val_ref(id_type node) const { return _p(node)->m_type.is_val_ref(); }
426 C4_ALWAYS_INLINE bool is_ref(id_type node) const { return _p(node)->m_type.is_ref(); }
427
428 C4_ALWAYS_INLINE bool parent_is_seq(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_parent(node), this, node); return is_seq(_p(node)->m_parent); }
429 C4_ALWAYS_INLINE bool parent_is_map(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_parent(node), this, node); return is_map(_p(node)->m_parent); }
430
431 /** true when the node has an anchor named a */
432 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; }
433
434 /** true if the node key is empty, or its scalar verifies @ref scalar_is_null().
435 * @warning the node must verify @ref Tree::has_key() (asserted) (ie must be a member of a map)
436 * @see https://github.com/biojppm/rapidyaml/issues/413 */
437 C4_ALWAYS_INLINE bool key_is_null(id_type node) const { _RYML_ASSERT_VISIT_(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)); }
438 /** true if the node val is empty, or its scalar verifies @ref scalar_is_null().
439 * @warning the node must verify @ref Tree::has_val() (asserted) (ie must be a scalar / must not be a container)
440 * @see https://github.com/biojppm/rapidyaml/issues/413 */
441 C4_ALWAYS_INLINE bool val_is_null(id_type node) const { _RYML_ASSERT_VISIT_(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)); }
442
443 /// true if the key was a scalar requiring filtering and was left
444 /// unfiltered during the parsing (see ParserOptions)
445 C4_ALWAYS_INLINE bool is_key_unfiltered(id_type node) const { return _p(node)->m_type.is_key_unfiltered(); }
446 /// true if the val was a scalar requiring filtering and was left
447 /// unfiltered during the parsing (see ParserOptions)
448 C4_ALWAYS_INLINE bool is_val_unfiltered(id_type node) const { return _p(node)->m_type.is_val_unfiltered(); }
449
450 RYML_DEPRECATED("use has_key_anchor()") bool is_key_anchor(id_type node) const { return _p(node)->m_type.has_key_anchor(); }
451 RYML_DEPRECATED("use has_val_anchor()") bool is_val_anchor(id_type node) const { return _p(node)->m_type.has_val_anchor(); }
452 RYML_DEPRECATED("use has_anchor()") bool is_anchor(id_type node) const { return _p(node)->m_type.has_anchor(); }
453 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(); }
454
455 /** @} */
456
457public:
458
459 /** @name hierarchy predicates */
460 /** @{ */
461
462 bool is_root(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, _p(node)->m_parent != NONE || node == 0, this, node); return _p(node)->m_parent == NONE; }
463
464 bool has_parent(id_type node) const { return _p(node)->m_parent != NONE; }
465
466 /** true when ancestor is parent or parent of a parent of node */
467 bool is_ancestor(id_type node, id_type ancestor) const;
468
469 /** true when key and val are empty, and has no children */
470 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()); }
471
472 /** true if @p node has a child with id @p ch */
473 bool has_child(id_type node, id_type ch) const { return _p(ch)->m_parent == node; }
474 /** true if @p node has a child with key @p key */
475 bool has_child(id_type node, csubstr key) const { return find_child(node, key) != NONE; }
476 /** true if @p node has any children key */
477 bool has_children(id_type node) const { return _p(node)->m_first_child != NONE; }
478
479 /** true if @p node has a sibling with id @p sib */
480 bool has_sibling(id_type node, id_type sib) const { return _p(node)->m_parent == _p(sib)->m_parent; }
481 /** true if one of the node's siblings has the given key */
482 bool has_sibling(id_type node, csubstr key) const { return find_sibling(node, key) != NONE; }
483 /** true if node is not a single child */
485 {
486 NodeData const *n = _p(node);
487 if(C4_LIKELY(n->m_parent != NONE))
488 {
489 n = _p(n->m_parent);
490 return n->m_first_child != n->m_last_child;
491 }
492 return false;
493 }
494
495 RYML_DEPRECATED("use has_other_siblings()") static bool has_siblings(id_type /*node*/) { return true; }
496
497 /** @} */
498
499public:
500
501 /** @name hierarchy getters */
502 /** @{ */
503
504 id_type parent(id_type node) const { return _p(node)->m_parent; }
505
506 id_type prev_sibling(id_type node) const { return _p(node)->m_prev_sibling; }
507 id_type next_sibling(id_type node) const { return _p(node)->m_next_sibling; }
508
509 /** O(#num_children) */
510 id_type num_children(id_type node) const;
511 id_type child_pos(id_type node, id_type ch) const;
512 id_type first_child(id_type node) const { return _p(node)->m_first_child; }
513 id_type last_child(id_type node) const { return _p(node)->m_last_child; }
514 id_type child(id_type node, id_type pos) const;
515 id_type find_child(id_type node, csubstr const& key) const;
516
517 /** O(#num_siblings) */
518 /** counts with this */
519 id_type num_siblings(id_type node) const { return is_root(node) ? 1 : num_children(_p(node)->m_parent); }
520 /** does not count with this */
521 id_type num_other_siblings(id_type node) const { id_type ns = num_siblings(node); _RYML_ASSERT_VISIT_(m_callbacks, ns > 0, this, node); return ns-1; }
522 id_type sibling_pos(id_type node, id_type sib) const { _RYML_ASSERT_VISIT_(m_callbacks, ! is_root(node) || node == root_id(), this, node); return child_pos(_p(node)->m_parent, sib); }
523 id_type first_sibling(id_type node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_first_child; }
524 id_type last_sibling(id_type node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_last_child; }
525 id_type sibling(id_type node, id_type pos) const { return child(_p(node)->m_parent, pos); }
526 id_type find_sibling(id_type node, csubstr const& key) const { return find_child(_p(node)->m_parent, key); }
527
528 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 */
529 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 */
530
531 /** gets the @p i document node index. requires that the root node is a stream. */
532 id_type doc(id_type i) const { id_type rid = root_id(); _RYML_ASSERT_VISIT_(m_callbacks, is_stream(rid), this, rid); return child(rid, i); }
533
534 /** get the document which is a parent document of node i, or the root if the tree is not a stream */
536 {
537 NodeData const *nd;
538 do
539 {
540 nd = _p(node);
541 if(nd->m_type.is_doc() || nd->m_parent == NONE)
542 break;
543 node = nd->m_parent;
544 } while(nd->m_parent != NONE);
545 return node;
546 }
547
548 /** @} */
549
550public:
551
552 /** @name node style predicates and modifiers. see the corresponding predicate in NodeType */
553 /** @{ */
554
555 C4_ALWAYS_INLINE bool is_container_styled(id_type node) const { return _p(node)->m_type.is_container_styled(); }
556 C4_ALWAYS_INLINE bool is_block(id_type node) const { return _p(node)->m_type.is_block(); }
557 C4_ALWAYS_INLINE bool is_flow_sl(id_type node) const { return _p(node)->m_type.is_flow_sl(); }
558 C4_ALWAYS_INLINE bool is_flow_ml(id_type node) const { return _p(node)->m_type.is_flow_ml(); }
559 C4_ALWAYS_INLINE bool is_flow(id_type node) const { return _p(node)->m_type.is_flow(); }
560
561 C4_ALWAYS_INLINE bool is_key_styled(id_type node) const { return _p(node)->m_type.is_key_styled(); }
562 C4_ALWAYS_INLINE bool is_val_styled(id_type node) const { return _p(node)->m_type.is_val_styled(); }
563 C4_ALWAYS_INLINE bool is_key_literal(id_type node) const { return _p(node)->m_type.is_key_literal(); }
564 C4_ALWAYS_INLINE bool is_val_literal(id_type node) const { return _p(node)->m_type.is_val_literal(); }
565 C4_ALWAYS_INLINE bool is_key_folded(id_type node) const { return _p(node)->m_type.is_key_folded(); }
566 C4_ALWAYS_INLINE bool is_val_folded(id_type node) const { return _p(node)->m_type.is_val_folded(); }
567 C4_ALWAYS_INLINE bool is_key_squo(id_type node) const { return _p(node)->m_type.is_key_squo(); }
568 C4_ALWAYS_INLINE bool is_val_squo(id_type node) const { return _p(node)->m_type.is_val_squo(); }
569 C4_ALWAYS_INLINE bool is_key_dquo(id_type node) const { return _p(node)->m_type.is_key_dquo(); }
570 C4_ALWAYS_INLINE bool is_val_dquo(id_type node) const { return _p(node)->m_type.is_val_dquo(); }
571 C4_ALWAYS_INLINE bool is_key_plain(id_type node) const { return _p(node)->m_type.is_key_plain(); }
572 C4_ALWAYS_INLINE bool is_val_plain(id_type node) const { return _p(node)->m_type.is_val_plain(); }
573 C4_ALWAYS_INLINE bool is_key_quoted(id_type node) const { return _p(node)->m_type.is_key_quoted(); }
574 C4_ALWAYS_INLINE bool is_val_quoted(id_type node) const { return _p(node)->m_type.is_val_quoted(); }
575 C4_ALWAYS_INLINE bool is_quoted(id_type node) const { return _p(node)->m_type.is_quoted(); }
576
577 C4_ALWAYS_INLINE NodeType key_style(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_key(node), this, node); return _p(node)->m_type.key_style(); }
578 C4_ALWAYS_INLINE NodeType val_style(id_type node) const { _RYML_ASSERT_VISIT_(m_callbacks, has_val(node) || is_root(node), this, node); return _p(node)->m_type.val_style(); }
579
580 C4_ALWAYS_INLINE void set_container_style(id_type node, NodeType_e style) { _RYML_ASSERT_VISIT_(m_callbacks, is_container(node), this, node); _p(node)->m_type.set_container_style(style); }
581 C4_ALWAYS_INLINE void set_key_style(id_type node, NodeType_e style) { _RYML_ASSERT_VISIT_(m_callbacks, has_key(node), this, node); _p(node)->m_type.set_key_style(style); }
582 C4_ALWAYS_INLINE void set_val_style(id_type node, NodeType_e style) { _RYML_ASSERT_VISIT_(m_callbacks, has_val(node), this, node); _p(node)->m_type.set_val_style(style); }
583
584 void clear_style(id_type node, bool recurse=false);
585 void set_style_conditionally(id_type node,
586 NodeType type_mask,
587 NodeType rem_style_flags,
588 NodeType add_style_flags,
589 bool recurse=false);
590 /** @} */
591
592public:
593
594 /** @name node type modifiers */
595 /** @{ */
596
597 void to_keyval(id_type node, csubstr key, csubstr val, type_bits more_flags=0);
598 void to_map(id_type node, csubstr key, type_bits more_flags=0);
599 void to_seq(id_type node, csubstr key, type_bits more_flags=0);
600 void to_val(id_type node, csubstr val, type_bits more_flags=0);
601 void to_map(id_type node, type_bits more_flags=0);
602 void to_seq(id_type node, type_bits more_flags=0);
603 void to_doc(id_type node, type_bits more_flags=0);
604 void to_stream(id_type node, type_bits more_flags=0);
605
606 void set_key(id_type node, csubstr key) { _RYML_ASSERT_VISIT_(m_callbacks, has_key(node), this, node); _p(node)->m_key.scalar = key; }
607 void set_val(id_type node, csubstr val) { _RYML_ASSERT_VISIT_(m_callbacks, has_val(node), this, node); _p(node)->m_val.scalar = val; }
608
609 void set_key_tag(id_type node, csubstr tag) { _RYML_ASSERT_VISIT_(m_callbacks, has_key(node), this, node); _p(node)->m_key.tag = tag; _add_flags(node, KEYTAG); }
610 void set_val_tag(id_type node, csubstr tag) { _RYML_ASSERT_VISIT_(m_callbacks, has_val(node) || is_container(node), this, node); _p(node)->m_val.tag = tag; _add_flags(node, VALTAG); }
611
612 void set_key_anchor(id_type node, csubstr anchor) { _RYML_ASSERT_VISIT_(m_callbacks, ! is_key_ref(node), this, node); _p(node)->m_key.anchor = anchor.triml('&'); _add_flags(node, KEYANCH); }
613 void set_val_anchor(id_type node, csubstr anchor) { _RYML_ASSERT_VISIT_(m_callbacks, ! is_val_ref(node), this, node); _p(node)->m_val.anchor = anchor.triml('&'); _add_flags(node, VALANCH); }
614 void set_key_ref (id_type node, csubstr ref ) { _RYML_ASSERT_VISIT_(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); }
615 void set_val_ref (id_type node, csubstr ref ) { _RYML_ASSERT_VISIT_(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); }
616
617 void rem_key_anchor(id_type node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYANCH); }
618 void rem_val_anchor(id_type node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALANCH); }
619 void rem_key_ref (id_type node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYREF); }
620 void rem_val_ref (id_type node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALREF); }
621 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); }
622
623 /** @} */
624
625public:
626
627 /** @name tree modifiers */
628 /** @{ */
629
630 /** reorder the tree in memory so that all the nodes are stored
631 * in a linear sequence when visited in depth-first order.
632 * This will invalidate existing ids, since the node id is its
633 * position in the tree's node array. */
634 void reorder();
635
636 /** @} */
637
638public:
639
640 /** @name anchors and references/aliases */
641 /** @{ */
642
643 /** Resolve references (aliases <- anchors), by forwarding to @ref
644 * ReferenceResolver::resolve(); refer to @ref
645 * ReferenceResolver::resolve() for further details. */
646 void resolve(ReferenceResolver *C4_RESTRICT rr, bool clear_anchors=true);
647
648 /** Resolve references (aliases <- anchors), by forwarding to @ref
649 * ReferenceResolver::resolve(); refer to @ref
650 * ReferenceResolver::resolve() for further details. This overload
651 * uses a throwaway resolver object. */
652 void resolve(bool clear_anchors=true);
653
654 /** @} */
655
656public:
657
658 /** @name tags and tag directives */
659 /** @{ */
660
661 /** Resolve tags in the tree such as `"!!str"` ->
662 * `"<tag:yaml.org,2002:str>"`, `"!foo"` ->
663 * `"<!foo>"` and custom tags as well, ie tags of the form
664 * `"!handle!tag"` for which there is a corresponding `"%%TAG"`
665 * directive
666 *
667 * @param cache an object of type @ref TagCache to minimize memory
668 * usage by avoiding repeated instantiation of the resolved
669 * tags in the tree's arena.
670 *
671 * @param all if true, resolve all tags; if false resolve only
672 * custom tags, ie those that have a prefix such as
673 * `"!m!tag"` with a matching `"%TAG"` directive */
674 void resolve_tags(TagCache &cache, bool all=true);
675 void normalize_tags();
676 void normalize_tags_long();
677
678 id_type num_tag_directives() const;
679 void add_tag_directive(csubstr handle, csubstr prefix, id_type id);
680 void clear_tag_directives();
681
682 /** resolve the given tag, appearing at node_id. Write the result into output.
683 * @return the number of characters required for the resolved tag */
684 size_t resolve_tag(substr output, csubstr tag, id_type node_id) const;
685 /** Wrapper for @ref Tree::resolve_tag(), returning a substring */
686 csubstr resolve_tag_sub(substr output, csubstr tag, id_type node_id) const
687 {
688 size_t needed = resolve_tag(output, tag, node_id);
689 return needed <= output.len ? output.first(needed) : output;
690 }
691
693
694 /** @cond dev */
695 RYML_DEPRECATED("use c4::yml::tag_directive_const_iterator") typedef TagDirective const* tag_directive_const_iterator;
696 RYML_DEPRECATED("use c4::yml::TagDirectiveRange") typedef c4::yml::TagDirectiveRange TagDirectiveProxy;
697 /** @endcond */
698
699 /** @} */
700
701public:
702
703 /** @name modifying hierarchy */
704 /** @{ */
705
706 /** create and insert a new child of @p parent. insert after the (to-be)
707 * sibling @p after, which must be a child of @p parent. To insert as the
708 * first child, set after to NONE */
709 C4_ALWAYS_INLINE id_type insert_child(id_type parent, id_type after)
710 {
711 _RYML_ASSERT_VISIT_(m_callbacks, parent != NONE, this, parent);
712 _RYML_ASSERT_VISIT_(m_callbacks, is_container(parent) || is_root(parent), this, parent);
713 _RYML_ASSERT_VISIT_(m_callbacks, after == NONE || (_p(after)->m_parent == parent), this, parent);
714 id_type child = _claim();
715 _set_hierarchy(child, parent, after);
716 return child;
717 }
718 /** create and insert a node as the first child of @p parent */
720 /** create and insert a node as the last child of @p parent */
721 C4_ALWAYS_INLINE id_type append_child(id_type parent) { return insert_child(parent, _p(parent)->m_last_child); }
723 {
724 id_type child = _claim();
725 _set_hierarchy(child, parent, _p(parent)->m_last_child);
726 return child;
727 }
728
729public:
730
731 #if defined(__clang__) // NOLINT
732 # pragma clang diagnostic push
733 # pragma clang diagnostic ignored "-Wnull-dereference"
734 #elif defined(__GNUC__)
735 # pragma GCC diagnostic push
736 # if __GNUC__ >= 6
737 # pragma GCC diagnostic ignored "-Wnull-dereference"
738 # endif
739 #endif
740
741 //! create and insert a new sibling of n. insert after "after"
742 C4_ALWAYS_INLINE id_type insert_sibling(id_type node, id_type after)
743 {
744 return insert_child(_p(node)->m_parent, after);
745 }
746 /** create and insert a node as the first node of @p parent */
747 C4_ALWAYS_INLINE id_type prepend_sibling(id_type node) { return prepend_child(_p(node)->m_parent); }
748 C4_ALWAYS_INLINE id_type append_sibling(id_type node) { return append_child(_p(node)->m_parent); }
749
750public:
751
752 /** remove an entire branch at once: ie remove the children and the node itself */
753 void remove(id_type node)
754 {
755 remove_children(node);
756 _release(node);
757 }
758
759 /** remove all the node's children, but keep the node itself */
760 void remove_children(id_type node);
761
762 /** change the @p type of the node to one of MAP, SEQ or VAL. @p
763 * type must have one and only one of MAP,SEQ,VAL; @p type may
764 * possibly have KEY, but if it does, then the @p node must also
765 * have KEY. Changing to the same type is a no-op. Otherwise,
766 * changing to a different type will initialize the node with an
767 * empty value of the desired type: changing to VAL will
768 * initialize with a null scalar (~), changing to MAP will
769 * initialize with an empty map ({}), and changing to SEQ will
770 * initialize with an empty seq ([]). */
771 bool change_type(id_type node, NodeType type);
772
774 {
775 return change_type(node, (NodeType)type);
776 }
777
778 #if defined(__clang__)
779 # pragma clang diagnostic pop
780 #elif defined(__GNUC__)
781 # pragma GCC diagnostic pop
782 #endif
783
784public:
785
786 /** change the node's position in the parent */
787 void move(id_type node, id_type after);
788
789 /** change the node's parent and position */
790 void move(id_type node, id_type new_parent, id_type after);
791
792 /** change the node's parent and position to a different tree
793 * @return the index of the new node in the destination tree */
794 id_type move(Tree * src, id_type node, id_type new_parent, id_type after);
795
796 /** ensure the first node is a stream. Eg, change this tree
797 *
798 * DOCMAP
799 * MAP
800 * KEYVAL
801 * KEYVAL
802 * SEQ
803 * VAL
804 *
805 * to
806 *
807 * STREAM
808 * DOCMAP
809 * MAP
810 * KEYVAL
811 * KEYVAL
812 * SEQ
813 * VAL
814 *
815 * If the root is already a stream, this is a no-op.
816 */
817 void set_root_as_stream();
818
819public:
820
821 /** recursively duplicate a node from this tree into a new parent,
822 * placing it after one of its children
823 * @return the index of the copy */
824 id_type duplicate(id_type node, id_type new_parent, id_type after);
825 /** recursively duplicate a node from a different tree into a new parent,
826 * placing it after one of its children
827 * @return the index of the copy */
828 id_type duplicate(Tree const* src, id_type node, id_type new_parent, id_type after);
829
830 /** recursively duplicate the node's children (but not the node)
831 * @return the index of the last duplicated child */
832 id_type duplicate_children(id_type node, id_type parent, id_type after);
833 /** recursively duplicate the node's children (but not the node), where
834 * the node is from a different tree
835 * @return the index of the last duplicated child */
836 id_type duplicate_children(Tree const* src, id_type node, id_type parent, id_type after);
837
838 /** duplicate the node's children (but not the node) in a new parent, but
839 * omit repetitions where a duplicated node has the same key (in maps) or
840 * value (in seqs). If one of the duplicated children has the same key
841 * (in maps) or value (in seqs) as one of the parent's children, the one
842 * that is placed closest to the end will prevail. */
843 id_type duplicate_children_no_rep(id_type node, id_type parent, id_type after);
844 id_type duplicate_children_no_rep(Tree const* src, id_type node, id_type parent, id_type after);
845
846 void duplicate_contents(id_type node, id_type where);
847 void duplicate_contents(Tree const* src, id_type node, id_type where);
848
849public:
850
851 void merge_with(Tree const* src, id_type src_node=NONE, id_type dst_root=NONE);
852
853 /** @} */
854
855public:
856
857 /** @name locations */
858 /** @{ */
859
860 /** Get the location of a node from the parse used to parse this tree. */
861 Location location(Parser const& p, id_type node) const;
862
863private:
864
865 bool _location_from_node(Parser const& p, id_type node, Location *C4_RESTRICT loc, id_type level) const;
866 bool _location_from_cont(Parser const& p, id_type node, Location *C4_RESTRICT loc) const;
867
868 /** @} */
869
870public:
871
872 /** @name internal string arena */
873 /** @{ */
874
875 /** get the current size of the tree's internal arena */
876 size_t arena_size() const { return m_arena_pos; }
877 /** get the current capacity of the tree's internal arena */
878 size_t arena_capacity() const { return m_arena.len; }
879 /** get the current slack of the tree's internal arena */
880 size_t arena_slack() const { _RYML_ASSERT_VISIT_(m_callbacks, m_arena.len >= m_arena_pos, this, NONE); return m_arena.len - m_arena_pos; }
881 RYML_DEPRECATED("use arena_size() instead") size_t arena_pos() const { return m_arena_pos; }
882
883 /** get the current arena */
884 csubstr arena() const { return m_arena.first(m_arena_pos); }
885 /** get the current arena */
886 substr arena() { return m_arena.first(m_arena_pos); } // NOLINT(readability-make-member-function-const)
887
888 /** return true if the given substring is part of the tree's string arena */
889 C4_ALWAYS_INLINE bool in_arena(csubstr s) const
890 {
891 return m_arena.is_super(s);
892 }
893
894 /** serialize the given variable to the tree's
895 * arena, growing it as needed to accomodate the serialization.
896 *
897 * @note To customize how the type gets serialized to a string,
898 * you can overload c4::to_chars(substr, T const&)
899 *
900 * @note To customize how the type gets serialized to the arena,
901 * you can overload @ref serialize_scalar()
902 *
903 * @note Growing the arena may cause relocation of the entire
904 * existing arena, and thus change the contents of individual
905 * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
906 * cost, ensure that the arena is reserved to an appropriate size
907 * using @ref Tree::reserve_arena().
908 *
909 * @see alloc_arena() */
910 template<class T>
911 C4_ALWAYS_INLINE csubstr to_arena(T const& C4_RESTRICT a)
912 {
913 return serialize_to_arena(this, a);
914 }
915
916 /** copy the given string to the tree's arena, growing the arena
917 * by the required size.
918 *
919 * @note this method differs from @ref to_arena() in that it
920 * returns a mutable substr, and further it does not deal
921 * with some corner cases for null/empty strings
922 *
923 * @note Growing the arena may cause relocation of the entire
924 * existing arena, and thus change the contents of individual
925 * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
926 * cost, ensure that the arena is reserved to an appropriate size
927 * before using @ref Tree::reserve_arena()
928 *
929 * @see reserve_arena()
930 * @see alloc_arena()
931 */
933 {
934 _RYML_ASSERT_VISIT_(m_callbacks, !s.overlaps(m_arena), this, NONE);
935 substr cp = alloc_arena(s.len);
936 _RYML_ASSERT_VISIT_(m_callbacks, cp.len == s.len, this, NONE);
937 _RYML_ASSERT_VISIT_(m_callbacks, !s.overlaps(cp), this, NONE);
938 #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
939 C4_SUPPRESS_WARNING_GCC_PUSH
940 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow=") // no need for terminating \0
941 C4_SUPPRESS_WARNING_GCC("-Wrestrict") // there's an assert to ensure no violation of restrict behavior
942 #endif
943 if(s.len)
944 memcpy(cp.str, s.str, s.len);
945 #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
946 C4_SUPPRESS_WARNING_GCC_POP
947 #endif
948 return cp;
949 }
950
951 /** grow the tree's string arena by the given size and return a substr
952 * of the added portion
953 *
954 * @note Growing the arena may cause relocation of the entire
955 * existing arena, and thus change the contents of individual
956 * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
957 * cost, ensure that the arena is reserved to an appropriate size
958 * using .reserve_arena().
959 *
960 * @see reserve_arena() */
962 {
963 if(sz > arena_slack())
964 _grow_arena(sz - arena_slack());
965 substr s = _request_span(sz);
966 return s;
967 }
968
969 /** ensure the tree's internal string arena is at least the given capacity
970 * @warning This operation may be expensive, with a potential complexity of O(numNodes)+O(arenasize).
971 * @warning Growing the arena may cause relocation of the entire
972 * existing arena, and thus change the contents of individual nodes. */
974 {
975 if(arena_cap > m_arena.len)
976 {
977 substr buf;
978 buf.str = _RYML_CB_ALLOC(m_callbacks, char, arena_cap);
979 buf.len = arena_cap;
980 if(m_arena.str)
981 {
982 _RYML_ASSERT_VISIT_(m_callbacks, m_arena.len >= 0, this, NONE);
983 _relocate(buf); // does a memcpy and changes nodes using the arena
984 _RYML_CB_FREE(m_callbacks, m_arena.str, char, m_arena.len);
985 }
986 m_arena = buf;
987 }
988 }
989
990 /** @} */
991
992public:
993
994 /** @cond dev */
995
996 substr _grow_arena(size_t more)
997 {
998 size_t cap = m_arena.len + more;
999 cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap;
1000 cap = cap < 64 ? 64 : cap;
1001 reserve_arena(cap);
1002 return m_arena.sub(m_arena_pos);
1003 }
1004
1005 substr _request_span(size_t sz)
1006 {
1007 _RYML_ASSERT_VISIT_(m_callbacks, m_arena_pos + sz <= m_arena.len, this, NONE);
1008 substr s;
1009 s = m_arena.sub(m_arena_pos, sz);
1010 m_arena_pos += sz;
1011 return s;
1012 }
1013
1014 substr _relocated(csubstr s, substr next_arena) const
1015 {
1016 _RYML_ASSERT_VISIT_(m_callbacks, m_arena.is_super(s) || s.len == 0, this, NONE);
1017 _RYML_ASSERT_VISIT_(m_callbacks, m_arena.sub(0, m_arena_pos).is_super(s) || s.len == 0, this, NONE);
1018 auto pos = (s.str - m_arena.str); // this is larger than 0 based on the assertions above
1019 substr r(next_arena.str + pos, s.len);
1020 _RYML_ASSERT_VISIT_(m_callbacks, r.str - next_arena.str == pos, this, NONE);
1021 _RYML_ASSERT_VISIT_(m_callbacks, next_arena.sub(0, m_arena_pos).is_super(r) || r.len == 0, this, NONE);
1022 return r;
1023 }
1024
1025 /** @endcond */
1026
1027public:
1028
1029 /** @name lookup */
1030 /** @{ */
1031
1033 {
1036 size_t path_pos;
1038
1039 operator bool() const { return target != NONE; }
1040
1042 lookup_result(csubstr path_, id_type start) : target(NONE), closest(start), path_pos(0), path(path_) {}
1043
1044 /** get the part ot the input path that was resolved */
1045 csubstr resolved() const;
1046 /** get the part ot the input path that was unresolved */
1047 csubstr unresolved() const;
1048 };
1049
1050 /** for example foo.bar[0].baz */
1051 lookup_result lookup_path(csubstr path, id_type start=NONE) const;
1052
1053 /** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
1054 * the tree so that the corresponding lookup_path() would return the
1055 * default value.
1056 * @see lookup_path() */
1057 id_type lookup_path_or_modify(csubstr default_value, csubstr path, id_type start=NONE);
1058
1059 /** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
1060 * the tree so that the corresponding lookup_path() would return the
1061 * branch @p src_node (from the tree @p src).
1062 * @see lookup_path() */
1063 id_type lookup_path_or_modify(Tree const *src, id_type src_node, csubstr path, id_type start=NONE);
1064
1065 /** @} */
1066
1067private:
1068
1069 struct _lookup_path_token
1070 {
1071 csubstr value;
1072 NodeType type;
1073 _lookup_path_token() : value(), type() {}
1074 _lookup_path_token(csubstr v, NodeType t) : value(v), type(t) {}
1075 operator bool() const { return type != NOTYPE; }
1076 bool is_index() const { return value.begins_with('[') && value.ends_with(']'); }
1077 };
1078
1079 id_type _lookup_path_or_create(csubstr path, id_type start);
1080
1081 void _lookup_path (lookup_result *r) const;
1082 void _lookup_path_modify(lookup_result *r);
1083
1084 id_type _next_node (lookup_result *r, _lookup_path_token *parent) const;
1085 id_type _next_node_modify(lookup_result *r, _lookup_path_token *parent);
1086
1087 static void _advance(lookup_result *r, size_t more);
1088
1089 _lookup_path_token _next_token(lookup_result *r, _lookup_path_token const& parent) const;
1090
1091private:
1092
1093 void _clear();
1094 void _free();
1095 void _copy(Tree const& that);
1096 void _move(Tree & that) noexcept;
1097
1098 void _relocate(substr next_arena);
1099
1100public:
1101
1102 /** @cond dev*/
1103
1104 #if ! RYML_USE_ASSERT
1105 C4_ALWAYS_INLINE void _check_next_flags(id_type, type_bits) {}
1106 #else
1107 void _check_next_flags(id_type node, type_bits f)
1108 {
1109 NodeData *n = _p(node);
1110 type_bits o = n->m_type; // old
1111 C4_UNUSED(o);
1112 if(f & MAP)
1113 {
1114 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (f & SEQ) == 0, this, node, "cannot mark simultaneously as map and seq");
1115 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (f & VAL) == 0, this, node, "cannot mark simultaneously as map and val");
1116 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (o & SEQ) == 0, this, node, "cannot turn a seq into a map; clear first");
1117 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (o & VAL) == 0, this, node, "cannot turn a val into a map; clear first");
1118 }
1119 else if(f & SEQ)
1120 {
1121 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (f & MAP) == 0, this, node, "cannot mark simultaneously as seq and map");
1122 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (f & VAL) == 0, this, node, "cannot mark simultaneously as seq and val");
1123 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (o & MAP) == 0, this, node, "cannot turn a map into a seq; clear first");
1124 _RYML_ASSERT_VISIT_MSG_(m_callbacks, (o & VAL) == 0, this, node, "cannot turn a val into a seq; clear first");
1125 }
1126 if(f & KEY)
1127 {
1128 _RYML_ASSERT_VISIT_(m_callbacks, !is_root(node), this, node);
1129 auto pid = parent(node); C4_UNUSED(pid);
1130 _RYML_ASSERT_VISIT_(m_callbacks, is_map(pid), this, node);
1131 }
1132 if((f & VAL) && !is_root(node))
1133 {
1134 auto pid = parent(node); C4_UNUSED(pid);
1135 _RYML_ASSERT_VISIT_(m_callbacks, is_map(pid) || is_seq(pid), this, node);
1136 }
1137 }
1138 #endif
1139
1140 void _set_flags(id_type node, NodeType_e f) { _check_next_flags(node, f); _p(node)->m_type = f; }
1141 void _set_flags(id_type node, type_bits f) { _check_next_flags(node, f); _p(node)->m_type = f; }
1142
1143 void _add_flags(id_type node, NodeType_e f) { NodeData *d = _p(node); type_bits fb = f | d->m_type; _check_next_flags(node, fb); d->m_type = (NodeType_e) fb; }
1144 void _add_flags(id_type node, type_bits f) { NodeData *d = _p(node); f |= d->m_type; _check_next_flags(node, f); d->m_type = f; }
1145
1146 void _rem_flags(id_type node, NodeType_e f) { NodeData *d = _p(node); type_bits fb = d->m_type & ~f; _check_next_flags(node, fb); d->m_type = (NodeType_e) fb; }
1147 void _rem_flags(id_type node, type_bits f) { NodeData *d = _p(node); f = d->m_type & ~f; _check_next_flags(node, f); d->m_type = f; }
1148
1149 void _set_key(id_type node, csubstr key, type_bits more_flags=0)
1150 {
1151 _p(node)->m_key.scalar = key;
1152 _add_flags(node, KEY|more_flags);
1153 }
1154 void _set_key(id_type node, NodeScalar const& key, type_bits more_flags=0)
1155 {
1156 _p(node)->m_key = key;
1157 _add_flags(node, KEY|more_flags);
1158 }
1159
1160 void _set_val(id_type node, csubstr val, type_bits more_flags=0)
1161 {
1162 _RYML_ASSERT_VISIT_(m_callbacks, num_children(node) == 0, this, node);
1163 _RYML_ASSERT_VISIT_(m_callbacks, !is_seq(node) && !is_map(node), this, node);
1164 _p(node)->m_val.scalar = val;
1165 _add_flags(node, VAL|more_flags);
1166 }
1167 void _set_val(id_type node, NodeScalar const& val, type_bits more_flags=0)
1168 {
1169 _RYML_ASSERT_VISIT_(m_callbacks, num_children(node) == 0, this, node);
1170 _RYML_ASSERT_VISIT_(m_callbacks, ! is_container(node), this, node);
1171 _p(node)->m_val = val;
1172 _add_flags(node, VAL|more_flags);
1173 }
1174
1175 void _set(id_type node, NodeInit const& i)
1176 {
1177 _RYML_ASSERT_VISIT_(m_callbacks, i._check(), this, node);
1178 NodeData *n = _p(node);
1179 _RYML_ASSERT_VISIT_(m_callbacks, n->m_key.scalar.empty() || i.key.scalar.empty() || i.key.scalar == n->m_key.scalar, this, node);
1180 _add_flags(node, i.type);
1181 if(n->m_key.scalar.empty())
1182 {
1183 if( ! i.key.scalar.empty())
1184 {
1185 _set_key(node, i.key.scalar);
1186 }
1187 }
1188 n->m_key.tag = i.key.tag;
1189 n->m_val = i.val;
1190 }
1191
1192 void _set_parent_as_container_if_needed(id_type in)
1193 {
1194 NodeData const* n = _p(in);
1195 id_type ip = parent(in);
1196 if(ip != NONE)
1197 {
1198 if( ! (is_seq(ip) || is_map(ip)))
1199 {
1200 if((in == first_child(ip)) && (in == last_child(ip)))
1201 {
1202 if( ! n->m_key.empty() || has_key(in))
1203 {
1204 _add_flags(ip, MAP);
1205 }
1206 else
1207 {
1208 _add_flags(ip, SEQ);
1209 }
1210 }
1211 }
1212 }
1213 }
1214
1215 void _seq2map(id_type node)
1216 {
1217 _RYML_ASSERT_VISIT_(m_callbacks, is_seq(node), this, node);
1218 for(id_type i = first_child(node); i != NONE; i = next_sibling(i))
1219 {
1220 NodeData *C4_RESTRICT ch = _p(i);
1221 if(ch->m_type.is_keyval())
1222 continue;
1223 ch->m_type.add(KEY);
1224 ch->m_key = ch->m_val;
1225 }
1226 auto *C4_RESTRICT n = _p(node);
1227 n->m_type.rem(SEQ);
1228 n->m_type.add(MAP);
1229 }
1230
1231 id_type _do_reorder(id_type *node, id_type count);
1232
1233 void _swap(id_type n_, id_type m_);
1234 void _swap_props(id_type n_, id_type m_);
1235 void _swap_hierarchy(id_type n_, id_type m_);
1236 void _copy_hierarchy(id_type dst_, id_type src_);
1237
1238 void _copy_props(id_type dst_, id_type src_)
1239 {
1240 _copy_props(dst_, this, src_);
1241 }
1242
1243 void _copy_props_wo_key(id_type dst_, id_type src_)
1244 {
1245 _copy_props_wo_key(dst_, this, src_);
1246 }
1247
1248 void _copy_props(id_type dst_, Tree const* that_tree, id_type src_)
1249 {
1250 NodeData & C4_RESTRICT dst = *_p(dst_);
1251 NodeData const& C4_RESTRICT src = *that_tree->_p(src_);
1252 dst.m_type = src.m_type;
1253 dst.m_key = src.m_key;
1254 dst.m_val = src.m_val;
1255 }
1256
1257 void _copy_props(id_type dst_, Tree const* that_tree, id_type src_, type_bits src_mask)
1258 {
1259 NodeData & C4_RESTRICT dst = *_p(dst_);
1260 NodeData const& C4_RESTRICT src = *that_tree->_p(src_);
1261 dst.m_type = (src.m_type & src_mask) | (dst.m_type & ~src_mask);
1262 dst.m_key = src.m_key;
1263 dst.m_val = src.m_val;
1264 }
1265
1266 void _copy_props_wo_key(id_type dst_, Tree const* that_tree, id_type src_)
1267 {
1268 auto & C4_RESTRICT dst = *_p(dst_);
1269 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1270 dst.m_type = (src.m_type & ~_KEYMASK) | (dst.m_type & _KEYMASK);
1271 dst.m_val = src.m_val;
1272 }
1273
1274 void _copy_props_wo_key(id_type dst_, Tree const* that_tree, id_type src_, type_bits src_mask)
1275 {
1276 auto & C4_RESTRICT dst = *_p(dst_);
1277 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1278 dst.m_type = (src.m_type & ((~_KEYMASK)|src_mask)) | (dst.m_type & (_KEYMASK|~src_mask));
1279 dst.m_val = src.m_val;
1280 }
1281
1282 void _clear_type(id_type node)
1283 {
1284 _p(node)->m_type = NOTYPE;
1285 }
1286
1287 void _clear(id_type node)
1288 {
1289 auto *C4_RESTRICT n = _p(node);
1290 n->m_type = NOTYPE;
1291 n->m_key.clear();
1292 n->m_val.clear();
1293 n->m_parent = NONE;
1294 n->m_first_child = NONE;
1295 n->m_last_child = NONE;
1296 }
1297
1298 void _clear_key(id_type node)
1299 {
1300 _p(node)->m_key.clear();
1301 _rem_flags(node, KEY);
1302 }
1303
1304 void _clear_val(id_type node)
1305 {
1306 _p(node)->m_val.clear();
1307 _rem_flags(node, VAL);
1308 }
1309
1310 /** @endcond */
1311
1312private:
1313
1314 void _clear_range(id_type first, id_type num);
1315
1316public:
1317 id_type _claim();
1318private:
1319 void _claim_root();
1320 void _release(id_type node);
1321 void _free_list_add(id_type node);
1322 void _free_list_rem(id_type node);
1323
1324 void _set_hierarchy(id_type node, id_type parent, id_type after_sibling);
1325 void _rem_hierarchy(id_type node);
1326
1327public:
1328
1329 // members are exposed, but you should NOT access them directly
1330
1334
1337
1340
1342
1344
1345public:
1346 /** @cond dev */
1347 RYML_DEPRECATED("use Tree::resolve_tags(TagCache&)") void resolve_tags() { TagCache cache; resolve_tags(cache); }
1348 /** @endcond */
1349};
1350
1351
1352//-----------------------------------------------------------------------------
1353//-----------------------------------------------------------------------------
1354//-----------------------------------------------------------------------------
1355
1356/** @defgroup doc_serialization_helpers Serialization helpers
1357 *
1358 * @{
1359 */
1360
1361template<class T>
1362bool read(Tree const* C4_RESTRICT tree, id_type id, T const& wrapper)
1363{
1364 return C4_LIKELY(!(tree->type(id) & VALNIL)) ? from_chars(tree->val(id), wrapper) : false;
1365}
1366template<class T>
1367bool readkey(Tree const* C4_RESTRICT tree, id_type id, T const& wrapper)
1368{
1369 return C4_LIKELY(!(tree->type(id) & KEYNIL)) ? from_chars(tree->key(id), wrapper) : false;
1370}
1371
1372
1373
1374// NON-ARITHMETIC -------------------------------------------------------------
1375
1376/** convert the val of a scalar node to a particular non-arithmetic
1377 * non-float type, by forwarding its val to @ref from_chars<T>(). The
1378 * full string is used.
1379 * @return false if the conversion failed, or if the key was empty and unquoted */
1380template<class T>
1381inline auto read(Tree const* C4_RESTRICT tree, id_type id, T *v)
1382 -> typename std::enable_if<!std::is_arithmetic<T>::value, bool>::type
1383{
1384 return C4_LIKELY(!(tree->type(id) & VALNIL)) ? from_chars(tree->val(id), v) : false;
1385}
1386
1387/** convert the key of a node to a particular non-arithmetic
1388 * non-float type, by forwarding its key to @ref from_chars<T>(). The
1389 * full string is used.
1390 * @return false if the conversion failed, or if the key was empty and unquoted */
1391template<class T>
1392inline auto readkey(Tree const* C4_RESTRICT tree, id_type id, T *v)
1393 -> typename std::enable_if<!std::is_arithmetic<T>::value, bool>::type
1394{
1395 return C4_LIKELY(!(tree->type(id) & KEYNIL)) ? from_chars(tree->key(id), v) : false;
1396}
1397
1398
1399// INTEGRAL, NOT FLOATING -------------------------------------------------------------
1400
1401/** convert the val of a scalar node to a particular arithmetic
1402 * integral non-float type, by forwarding its val to @ref
1403 * from_chars<T>(). The full string is used.
1404 *
1405 * @return false if the conversion failed */
1406template<class T>
1407inline auto read(Tree const* C4_RESTRICT tree, id_type id, T *v)
1408 -> typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value, bool>::type
1409{
1410 using U = typename std::remove_cv<T>::type;
1411 enum { ischar = std::is_same<char, U>::value || std::is_same<signed char, U>::value || std::is_same<unsigned char, U>::value }; // NOLINT
1412 csubstr val = tree->val(id);
1413 NodeType ty = tree->type(id);
1414 if(C4_UNLIKELY((ty & VALNIL) || val.empty()))
1415 return false;
1416 // quote integral numbers if they have a leading 0
1417 // https://github.com/biojppm/rapidyaml/issues/291
1418 char first = val.str[0];
1419 if(ty.is_val_quoted() && (first != '0' && !ischar))
1420 return false;
1421 else if(first == '+')
1422 val = val.sub(1);
1423 return from_chars(val, v);
1424}
1425
1426/** convert the key of a node to a particular arithmetic
1427 * integral non-float type, by forwarding its val to @ref
1428 * from_chars<T>(). The full string is used.
1429 *
1430 * @return false if the conversion failed */
1431template<class T>
1432inline auto readkey(Tree const* C4_RESTRICT tree, id_type id, T *v)
1433 -> typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value, bool>::type
1434{
1435 using U = typename std::remove_cv<T>::type;
1436 enum { ischar = std::is_same<char, U>::value || std::is_same<signed char, U>::value || std::is_same<unsigned char, U>::value }; // NOLINT
1437 csubstr key = tree->key(id);
1438 NodeType ty = tree->type(id);
1439 if((ty & KEYNIL) || key.empty())
1440 return false;
1441 // quote integral numbers if they have a leading 0
1442 // https://github.com/biojppm/rapidyaml/issues/291
1443 char first = key.str[0];
1444 if(ty.is_key_quoted() && (first != '0' && !ischar))
1445 return false;
1446 else if(first == '+')
1447 key = key.sub(1);
1448 return from_chars(key, v);
1449}
1450
1451
1452// FLOATING -------------------------------------------------------------
1453
1454/** encode a floating point value to a string. */
1455template<class T>
1456size_t to_chars_float(substr buf, T val)
1457{
1458 static_assert(std::is_floating_point<T>::value, "must be floating point");
1459 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wfloat-equal")
1460 if(C4_UNLIKELY(std::isnan(val)))
1461 return to_chars(buf, csubstr(".nan"));
1462 else if(C4_UNLIKELY(val == std::numeric_limits<T>::infinity()))
1463 return to_chars(buf, csubstr(".inf"));
1464 else if(C4_UNLIKELY(val == -std::numeric_limits<T>::infinity()))
1465 return to_chars(buf, csubstr("-.inf"));
1466 return to_chars(buf, val);
1467 C4_SUPPRESS_WARNING_GCC_CLANG_POP
1468}
1469
1470
1471/** decode a floating point from string. Accepts special values: .nan,
1472 * .inf, -.inf */
1473template<class T>
1474bool from_chars_float(csubstr buf, T *C4_RESTRICT val)
1475{
1476 static_assert(std::is_floating_point<T>::value, "must be floating point");
1477 if(buf.begins_with('+'))
1478 {
1479 buf = buf.sub(1);
1480 }
1481 if(C4_LIKELY(from_chars(buf, val)))
1482 {
1483 return true;
1484 }
1485 else if(C4_UNLIKELY(buf == ".nan" || buf == ".NaN" || buf == ".NAN"))
1486 {
1487 *val = std::numeric_limits<T>::quiet_NaN();
1488 return true;
1489 }
1490 else if(C4_UNLIKELY(buf == ".inf" || buf == ".Inf" || buf == ".INF"))
1491 {
1492 *val = std::numeric_limits<T>::infinity();
1493 return true;
1494 }
1495 else if(C4_UNLIKELY(buf == "-.inf" || buf == "-.Inf" || buf == "-.INF"))
1496 {
1497 *val = -std::numeric_limits<T>::infinity();
1498 return true;
1499 }
1500 else
1501 {
1502 return false;
1503 }
1504}
1505
1506/** convert the val of a scalar node to a floating point type, by
1507 * forwarding its val to @ref from_chars_float<T>().
1508 *
1509 * @return false if the conversion failed
1510 *
1511 * @warning Unlike non-floating types, only the leading part of the
1512 * string that may constitute a number is processed. This happens
1513 * because the float parsing is delegated to fast_float, which is
1514 * implemented that way. Consequently, for example, all of `"34"`,
1515 * `"34 "` `"34hg"` `"34 gh"` will be read as 34. If you are not sure
1516 * about the contents of the data, you can use
1517 * csubstr::first_real_span() to check before calling `>>`, for
1518 * example like this:
1519 *
1520 * ```cpp
1521 * csubstr val = node.val();
1522 * if(val.first_real_span() == val)
1523 * node >> v;
1524 * else
1525 * ERROR("not a real")
1526 * ```
1527 */
1528template<class T>
1529typename std::enable_if<std::is_floating_point<T>::value, bool>::type
1530inline read(Tree const* C4_RESTRICT tree, id_type id, T *v)
1531{
1532 csubstr val = tree->val(id);
1533 return C4_LIKELY(!val.empty()) ? from_chars_float(val, v) : false;
1534}
1535
1536/** convert the key of a scalar node to a floating point type, by
1537 * forwarding its key to @ref from_chars_float<T>().
1538 *
1539 * @return false if the conversion failed
1540 *
1541 * @warning Unlike non-floating types, only the leading part of the
1542 * string that may constitute a number is processed. This happens
1543 * because the float parsing is delegated to fast_float, which is
1544 * implemented that way. Consequently, for example, all of `"34"`,
1545 * `"34 "` `"34hg"` `"34 gh"` will be read as 34. If you are not sure
1546 * about the contents of the data, you can use
1547 * csubstr::first_real_span() to check before calling `>>`, for
1548 * example like this:
1549 *
1550 * ```cpp
1551 * csubstr key = node.key();
1552 * if(key.first_real_span() == key)
1553 * node >> v;
1554 * else
1555 * ERROR("not a real")
1556 * ```
1557 */
1558template<class T>
1559typename std::enable_if<std::is_floating_point<T>::value, bool>::type
1560inline readkey(Tree const* C4_RESTRICT tree, id_type id, T *v)
1561{
1562 csubstr key = tree->key(id);
1563 return C4_LIKELY(!key.empty()) ? from_chars_float(key, v) : false;
1564}
1565
1566
1567//-----------------------------------------------------------------------------
1568
1569template<class T>
1570csubstr serialize_to_arena(Tree * C4_RESTRICT tree, T const& C4_RESTRICT a)
1571{
1572 substr rem(tree->m_arena.sub(tree->m_arena_pos));
1573 size_t num = serialize_scalar(rem, a);
1574 if(num > rem.len)
1575 {
1576 rem = tree->_grow_arena(num);
1577 num = serialize_scalar(rem, a);
1578 _RYML_ASSERT_VISIT_(tree->m_callbacks, num <= rem.len, tree, NONE);
1579 }
1580 rem = tree->_request_span(num);
1581 return rem;
1582}
1583
1584
1585
1586/** @} */
1587
1588/** @} */
1589
1590
1591} // namespace yml
1592} // namespace c4
1593
1594// NOLINTEND(modernize-avoid-c-style-cast)
1595
1596C4_SUPPRESS_WARNING_MSVC_POP
1597C4_SUPPRESS_WARNING_GCC_CLANG_POP
1598
1599
1600#endif /* _C4_YML_TREE_HPP_ */
Lightweight generic type-safe wrappers for converting individual values to/from strings.
Holds a pointer to an existing tree, and a node id.
Definition node.hpp:827
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition node.hpp:967
id_type num_other_siblings(id_type node) const
does not count with this
Definition tree.hpp:521
NodeData * m_buf
Definition tree.hpp:1331
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:331
id_type m_free_head
Definition tree.hpp:1335
bool has_sibling(id_type node, csubstr key) const
true if one of the node's siblings has the given key
Definition tree.hpp:482
csubstr resolve_tag_sub(substr output, csubstr tag, id_type node_id) const
Wrapper for Tree::resolve_tag(), returning a substring.
Definition tree.hpp:686
csubstr const & key_ref(id_type node) const
Definition tree.hpp:389
csubstr const & key(id_type node) const
Definition tree.hpp:387
bool is_key_plain(id_type node) const
Definition tree.hpp:571
id_type num_siblings(id_type node) const
O(num_siblings).
Definition tree.hpp:519
id_type first_child(id_type node) const
Definition tree.hpp:512
bool is_stream(id_type node) const
Definition tree.hpp:410
NodeType type(id_type node) const
Definition tree.hpp:384
id_type append_sibling(id_type node)
Definition tree.hpp:748
void set_val_ref(id_type node, csubstr ref)
Definition tree.hpp:615
csubstr const & val_ref(id_type node) const
Definition tree.hpp:395
id_type root_id() const
Get the id of the root node. The tree must not be empty.
Definition tree.hpp:337
size_t arena_slack() const
get the current slack of the tree's internal arena
Definition tree.hpp:880
bool has_key_tag(id_type node) const
Definition tree.hpp:419
void resolve_tags(TagCache &cache, bool all=true)
Resolve tags in the tree such as "!!str" -> "<tag:yaml.org,2002:str>", "!foo" -> "<!...
Definition tree.cpp:1555
id_type prev_sibling(id_type node) const
Definition tree.hpp:506
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:973
id_type prepend_sibling(id_type node)
create and insert a node as the first node of parent
Definition tree.hpp:747
size_t m_arena_pos
Definition tree.hpp:1339
bool is_map(id_type node) const
Definition tree.hpp:413
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:334
void callbacks(Callbacks const &cb)
Definition tree.hpp:291
bool is_val_anchor(id_type node) const
Definition tree.hpp:451
id_type sibling(id_type node, id_type pos) const
Definition tree.hpp:525
NodeScalar const & keysc(id_type node) const
Definition tree.hpp:391
c4::yml::TagDirectiveRange tag_directives() const
Definition tree.hpp:692
bool is_key_quoted(id_type node) const
Definition tree.hpp:573
csubstr const & val(id_type node) const
Definition tree.hpp:393
bool has_val_anchor(id_type node) const
Definition tree.hpp:422
void clear_arena()
Definition tree.hpp:282
id_type insert_child(id_type parent, id_type after)
create and insert a new child of parent.
Definition tree.hpp:709
TagDirectives m_tag_directives
Definition tree.hpp:1343
bool is_root(id_type node) const
Definition tree.hpp:462
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:437
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:961
bool is_keyval(id_type node) const
Definition tree.hpp:418
bool is_val_folded(id_type node) const
Definition tree.hpp:566
id_type m_size
Definition tree.hpp:1333
id_type last_sibling(id_type node) const
Definition tree.hpp:524
bool has_key(id_type node) const
Definition tree.hpp:415
bool has_other_siblings(id_type node) const
true if node is not a single child
Definition tree.hpp:484
csubstr const & val_tag(id_type node) const
Definition tree.hpp:394
const char * type_str(id_type node) const
Definition tree.hpp:385
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
Definition tree.hpp:889
id_type parent(id_type node) const
Definition tree.hpp:504
void set_key_style(id_type node, NodeType_e style)
Definition tree.hpp:581
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:445
void rem_key_anchor(id_type node)
Definition tree.hpp:617
size_t resolve_tag(substr output, csubstr tag, id_type node_id) const
resolve the given tag, appearing at node_id.
Definition tree.cpp:1458
bool is_key_literal(id_type node) const
Definition tree.hpp:563
NodeType key_style(id_type node) const
Definition tree.hpp:577
bool is_block(id_type node) const
Definition tree.hpp:556
id_type prepend_child(id_type parent)
create and insert a node as the first child of parent
Definition tree.hpp:719
bool is_val(id_type node) const
Definition tree.hpp:417
bool type_has_any(id_type node, NodeType_e bits) const
Definition tree.hpp:406
bool is_container_styled(id_type node) const
Definition tree.hpp:555
bool empty() const
Definition tree.hpp:284
id_type sibling_pos(id_type node, id_type sib) const
Definition tree.hpp:522
id_type child_pos(id_type node, id_type ch) const
Definition tree.cpp:1232
id_type append_child(id_type parent)
create and insert a node as the last child of parent
Definition tree.hpp:721
bool has_sibling(id_type node, id_type sib) const
true if node has a sibling with id sib
Definition tree.hpp:480
id_type next_sibling(id_type node) const
Definition tree.hpp:507
bool is_key_squo(id_type node) const
Definition tree.hpp:567
bool parent_is_seq(id_type node) const
Definition tree.hpp:428
bool has_anchor(id_type node) const
Definition tree.hpp:423
id_type insert_sibling(id_type node, id_type after)
create and insert a new sibling of n. insert after "after"
Definition tree.hpp:742
bool has_val_tag(id_type node) const
Definition tree.hpp:420
NodeData const * get(id_type node) const
get a pointer to a node's NodeData. i can be NONE, in which case a nullptr is returned.
Definition tree.hpp:321
bool is_key_dquo(id_type node) const
Definition tree.hpp:569
void set_key_tag(id_type node, csubstr tag)
Definition tree.hpp:609
id_type m_cap
Definition tree.hpp:1332
void rem_anchor_ref(id_type node)
Definition tree.hpp:621
id_type last_child(id_type node) const
Definition tree.hpp:513
csubstr const & key_tag(id_type node) const
Definition tree.hpp:388
id_type id(NodeData const *n) const
get the index of a node belonging to this tree. n can be nullptr, in which case NONE is returned
Definition tree.hpp:302
substr m_arena
Definition tree.hpp:1338
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:911
void set_val_anchor(id_type node, csubstr anchor)
Definition tree.hpp:613
bool is_val_plain(id_type node) const
Definition tree.hpp:572
static bool has_siblings(id_type)
Definition tree.hpp:495
bool is_doc(id_type node) const
Definition tree.hpp:411
bool has_child(id_type node, csubstr key) const
true if node has a child with key key
Definition tree.hpp:475
void set_container_style(id_type node, NodeType_e style)
Definition tree.hpp:580
bool is_key_ref(id_type node) const
Definition tree.hpp:424
bool is_key_anchor(id_type node) const
Definition tree.hpp:450
void remove_children(id_type node)
remove all the node's children, but keep the node itself
Definition tree.cpp:918
void set_key(id_type node, csubstr key)
Definition tree.hpp:606
bool has_val(id_type node) const
Definition tree.hpp:416
bool is_val_dquo(id_type node) const
Definition tree.hpp:570
Callbacks const & callbacks() const
Definition tree.hpp:290
size_t arena_capacity() const
get the current capacity of the tree's internal arena
Definition tree.hpp:878
id_type doc(id_type i) const
gets the i document node index.
Definition tree.hpp:532
bool change_type(id_type node, NodeType type)
change the type of the node to one of MAP, SEQ or VAL.
Definition tree.cpp:939
bool is_flow_ml(id_type node) const
Definition tree.hpp:558
NodeType val_style(id_type node) const
Definition tree.hpp:578
bool is_key_folded(id_type node) const
Definition tree.hpp:565
size_t arena_pos() const
Definition tree.hpp:881
Tree(id_type node_capacity, size_t arena_capacity=RYML_DEFAULT_TREE_ARENA_CAPACITY)
Definition tree.hpp:258
bool type_has_all(id_type node, NodeType_e bits) const
Definition tree.hpp:407
id_type m_free_tail
Definition tree.hpp:1336
bool is_anchor_or_ref(id_type node) const
Definition tree.hpp:453
id_type slack() const
Definition tree.hpp:288
void set_val(id_type node, csubstr val)
Definition tree.hpp:607
bool is_anchor(id_type node) const
Definition tree.hpp:452
id_type find_sibling(id_type node, csubstr const &key) const
Definition tree.hpp:526
bool is_seq(id_type node) const
Definition tree.hpp:414
bool is_val_styled(id_type node) const
Definition tree.hpp:562
bool parent_is_map(id_type node) const
Definition tree.hpp:429
bool has_key_anchor(id_type node) const
Definition tree.hpp:421
void remove(id_type node)
remove an entire branch at once: ie remove the children and the node itself
Definition tree.hpp:753
id_type find_child(id_type node, csubstr const &key) const
Definition tree.cpp:1257
bool is_quoted(id_type node) const
Definition tree.hpp:575
csubstr const & val_anchor(id_type node) const
Definition tree.hpp:396
bool change_type(id_type node, type_bits type)
Definition tree.hpp:773
NodeData * get(id_type node)
get a pointer to a node's NodeData. i can be NONE, in which case a nullptr is returned
Definition tree.hpp:312
void set_val_style(id_type node, NodeType_e style)
Definition tree.hpp:582
substr arena()
get the current arena
Definition tree.hpp:886
void rem_val_ref(id_type node)
Definition tree.hpp:620
bool has_parent(id_type node) const
Definition tree.hpp:464
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:441
id_type first_sibling(id_type node) const
Definition tree.hpp:523
bool is_flow(id_type node) const
Definition tree.hpp:559
bool is_val_quoted(id_type node) const
Definition tree.hpp:574
bool type_has_none(id_type node, NodeType_e bits) const
Definition tree.hpp:408
bool empty(id_type node) const
true when key and val are empty, and has no children
Definition tree.hpp:470
csubstr const & key_anchor(id_type node) const
Definition tree.hpp:390
bool is_val_squo(id_type node) const
Definition tree.hpp:568
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:932
id_type num_children(id_type node) const
O(num_children).
Definition tree.cpp:1212
csubstr arena() const
get the current arena
Definition tree.hpp:884
id_type size() const
Definition tree.hpp:286
id_type _claim()
Definition tree.cpp:414
bool is_container(id_type node) const
Definition tree.hpp:412
size_t arena_size() const
get the current size of the tree's internal arena
Definition tree.hpp:876
id_type child(id_type node, id_type pos) const
Definition tree.cpp:1220
bool is_ref(id_type node) const
Definition tree.hpp:426
void set_val_tag(id_type node, csubstr tag)
Definition tree.hpp:610
bool is_key_styled(id_type node) const
Definition tree.hpp:561
void set_key_anchor(id_type node, csubstr anchor)
Definition tree.hpp:612
bool is_val_ref(id_type node) const
Definition tree.hpp:425
void set_key_ref(id_type node, csubstr ref)
Definition tree.hpp:614
id_type _append_child__unprotected(id_type parent)
Definition tree.hpp:722
bool has_child(id_type node, id_type ch) const
true if node has a child with id ch
Definition tree.hpp:473
bool is_val_literal(id_type node) const
Definition tree.hpp:564
void rem_val_anchor(id_type node)
Definition tree.hpp:618
NodeScalar const & valsc(id_type node) const
Definition tree.hpp:397
Callbacks m_callbacks
Definition tree.hpp:1341
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:448
void rem_key_ref(id_type node)
Definition tree.hpp:619
id_type capacity() const
Definition tree.hpp:287
bool is_flow_sl(id_type node) const
Definition tree.hpp:557
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:535
bool has_children(id_type node) const
true if node has any children key
Definition tree.hpp:477
bool has_anchor(id_type node, csubstr a) const
true when the node has an anchor named a
Definition tree.hpp:432
Common utilities and infrastructure used by ryml.
#define RYML_DEFAULT_TREE_CAPACITY
default capacity for the tree when not set explicitly
Definition common.hpp:16
#define RYML_DEFAULT_TREE_ARENA_CAPACITY
default capacity for the tree's arena when not set explicitly
Definition common.hpp:21
#define RYML_NOEXCEPT
Conditionally expands to noexcept when RYML_USE_ASSERT is 0 and is empty otherwise.
Definition common.hpp:197
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 scalar_is_null(csubstr s) noexcept
YAML-sense query of nullity.
NodeType_e
a bit mask for marking node types and styles
Definition node_type.hpp:34
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition node_type.hpp:30
@ VALANCH
the val has an &anchor
Definition node_type.hpp:46
@ NOTYPE
no node type or style is set
Definition node_type.hpp:36
@ VALREF
a *reference: the val references an &anchor
Definition node_type.hpp:44
@ VALNIL
the val is null (eg {a : } results in a null val)
Definition node_type.hpp:50
@ KEY
is member of a map
Definition node_type.hpp:37
@ KEYTAG
the key has a tag
Definition node_type.hpp:47
@ 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:38
@ VALTAG
the val has a tag
Definition node_type.hpp:48
@ KEYREF
a *reference: the key references an &anchor
Definition node_type.hpp:43
@ KEYANCH
the key has an &anchor
Definition node_type.hpp:45
@ KEYNIL
the key is null (eg { : b} results in a null key)
Definition node_type.hpp:49
ParseEngine< EventHandlerTree > Parser
This is the main ryml parser, where the parser events are handled to create a ryml tree.
Definition fwd.hpp:19
bool read(ryml::ConstNodeRef const &n, my_seq_type< T > *seq)
bool from_chars(ryml::csubstr buf, vec2< T > *v)
size_t to_chars(ryml::substr buf, vec2< T > v)
bool readkey(ConstNodeRef const &n, T *v)
Definition node.hpp:1611
size_t to_chars_float(substr buf, T val)
encode a floating point value to a string.
Definition tree.hpp:1456
Key< K > key(K &&k)
Definition node.hpp:43
bool from_chars_float(csubstr buf, T *val)
decode a floating point from string.
Definition tree.hpp:1474
csubstr to_csubstr(const char(&s)[N]) noexcept
Definition substr.hpp:2381
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2356
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2357
csubstr serialize_to_arena(Tree *tree, csubstr a)
Definition tree.cpp:19
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:249
auto serialize_scalar(substr buf, T const &a) -> typename std::enable_if< std::is_floating_point< T >::value, size_t >::type
Definition tree.hpp:66
@ NONE
an index to none
Definition common.hpp:256
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition common.cpp:14
bool begins_with(const C c) const noexcept
true if the first character of the string is c
Definition substr.hpp:851
basic_substring triml(const C c) const
trim left
Definition substr.hpp:630
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:895
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:494
basic_substring first(size_t num) const noexcept
return the first num elements: [0,num[
Definition substr.hpp:530
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition substr.hpp:503
bool empty() const noexcept
Definition substr.hpp:356
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:546
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition common.hpp:289
contains the data for each YAML node.
Definition tree.hpp:230
NodeType m_type
Definition tree.hpp:231
id_type m_next_sibling
Definition tree.hpp:239
id_type m_parent
Definition tree.hpp:236
NodeScalar m_key
Definition tree.hpp:233
id_type m_prev_sibling
Definition tree.hpp:240
id_type m_first_child
Definition tree.hpp:237
NodeScalar m_val
Definition tree.hpp:234
id_type m_last_child
Definition tree.hpp:238
NodeInit(NodeScalar const &k, NodeScalar const &v)
initialize as a mapping member
Definition tree.hpp:181
bool _check() const
Definition tree.hpp:209
NodeInit(NodeType_e t, NodeScalar const &k, NodeScalar const &v)
initialize as a mapping member with explicit type
Definition tree.hpp:183
NodeType type
Definition tree.hpp:166
void _add_flags(type_bits more_flags=0)
Definition tree.hpp:196
NodeScalar key
Definition tree.hpp:167
NodeInit(NodeScalar const &v, NodeType_e t)
initialize as a sequence member with explicit type
Definition tree.hpp:179
NodeInit(NodeScalar const &v)
initialize as a sequence member
Definition tree.hpp:177
NodeScalar val
Definition tree.hpp:168
NodeInit(NodeType_e t)
initialize as a typed node
Definition tree.hpp:175
NodeInit(NodeType_e t, NodeScalar const &k)
initialize as a mapping member with explicit type (eg for SEQ or MAP)
Definition tree.hpp:185
NodeInit()
initialize as an empty node
Definition tree.hpp:173
a node scalar is a csubstr, which may be tagged and anchored.
Definition tree.hpp:113
NodeScalar(const char(&t)[N], const char(&s)[N]) noexcept
initialize as a tagged scalar
Definition tree.hpp:130
bool empty() const noexcept
Definition tree.hpp:143
NodeScalar() noexcept
initialize as an empty scalar
Definition tree.hpp:121
NodeScalar(csubstr s) noexcept
Definition tree.hpp:126
NodeScalar(csubstr t, csubstr s) noexcept
Definition tree.hpp:131
void clear() noexcept
Definition tree.hpp:145
NodeScalar(const char(&s)[N]) noexcept
initialize as an untagged scalar
Definition tree.hpp:125
~NodeScalar() noexcept=default
void set_ref_maybe_replacing_scalar(csubstr ref, bool has_scalar) RYML_NOEXCEPT
Definition tree.hpp:147
wraps a NodeType_e element with some syntactic sugar and predicates
bool is_doc() const noexcept
bool is_key_quoted() const noexcept
const char * type_str() const noexcept
return a preset string based on the node type
bool is_val_quoted() const noexcept
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:1042