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