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