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