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