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