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