rapidyaml 0.15.2
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
tree.cpp
Go to the documentation of this file.
1#ifndef C4_YML_TREE_HPP_
2#include "c4/yml/tree.hpp"
3#endif
4#ifndef C4_YML_DETAIL_DBGPRINT_HPP_
5#include "c4/yml/detail/dbgprint.hpp"
6#endif
7#ifndef C4_YML_NODE_HPP_
8#include "c4/yml/node.hpp"
9#endif
10#ifndef C4_YML_REFERENCE_RESOLVERS_HPP_
12#endif
13
14
15C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4296/*expression is always 'boolean_value'*/)
16C4_SUPPRESS_WARNING_MSVC(4702/*unreachable code*/)
17C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
18C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
19C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
20// NOLINTBEGIN(modernize-avoid-c-style-cast)
21
22
23namespace c4 {
24namespace yml {
25
26
28{
29 if(scalar.len > 0)
30 {
31 return serialize_to_arena_scalar<csubstr>(tree, scalar);
32 }
33 else
34 {
35 if(scalar.str == nullptr)
36 {
37 return csubstr{};
38 }
39 else if(tree->m_arena.str == nullptr)
40 {
41 // Arena is empty and we want to store a non-null
42 // zero-length string.
43 // Even though the string has zero length, we need
44 // some "memory" to store a non-nullptr string
45 tree->_grow_arena(1);
46 }
47 return tree->_request_span(0);
48 }
49}
50
51
52//-----------------------------------------------------------------------------
53//-----------------------------------------------------------------------------
54//-----------------------------------------------------------------------------
55
57{
58 return NodeRef(this, root_id());
59}
61{
62 return ConstNodeRef(this, root_id());
63}
64
66{
67 return ConstNodeRef(this, root_id());
68}
69
71{
72 RYML_ASSERT_VISIT_CB_(m_callbacks, id != NONE && id >= 0 && id < m_cap, this, id);
73 return NodeRef(this, id);
74}
76{
77 RYML_ASSERT_VISIT_CB_(m_callbacks, id != NONE && id >= 0 && id < m_cap, this, id);
78 return ConstNodeRef(this, id);
79}
81{
82 RYML_ASSERT_VISIT_CB_(m_callbacks, id != NONE && id >= 0 && id < m_cap, this, id);
83 return ConstNodeRef(this, id);
84}
85
86NodeRef Tree::operator[] (csubstr key)
87{
88 return rootref()[key];
89}
90ConstNodeRef Tree::operator[] (csubstr key) const
91{
92 return crootref()[key];
93}
94
95NodeRef Tree::operator[] (id_type i)
96{
97 return rootref()[i];
98}
99ConstNodeRef Tree::operator[] (id_type i) const
100{
101 return crootref()[i];
102}
103
105{
106 return ref(doc(i));
107}
109{
110 return ConstNodeRef(this, doc(i));
111}
113{
114 return ConstNodeRef(this, doc(i));
115}
116
117
118//-----------------------------------------------------------------------------
123
124Tree::Tree(id_type node_capacity, size_t arena_capacity, Callbacks const& cb)
125 : m_buf(nullptr)
126 , m_cap(0)
127 , m_size(0)
130 , m_arena()
131 , m_arena_pos(0)
132 , m_callbacks(cb)
134{
135 if(node_capacity)
136 reserve(node_capacity);
139}
140
141Tree::~Tree() noexcept
142{
143 _free();
144}
145
146
148{
149 _clear();
150 _copy(that);
151}
152
153Tree::Tree(Tree && that) noexcept : m_callbacks(that.m_callbacks)
154{
155 _clear();
156 _move(that);
157}
158
160{
161 if(&that != this)
162 {
163 _free();
165 _copy(that);
166 }
167 return *this;
168}
169
170Tree& Tree::operator= (Tree && that) noexcept
171{
172 if(&that != this)
173 {
174 _free();
175 m_callbacks = that.m_callbacks;
176 _move(that);
177 }
178 return *this;
179}
180
181void Tree::_free()
182{
183 if(m_buf)
184 {
185 RYML_ASSERT_VISIT_CB_(m_callbacks, m_cap > 0, this, NONE);
186 RYML_CB_FREE_(m_callbacks, m_buf, NodeData, (size_t)m_cap);
187 }
188 if(m_arena.str)
189 {
190 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.len > 0, this, NONE);
191 RYML_CB_FREE_(m_callbacks, m_arena.str, char, m_arena.len);
192 }
193 _clear();
194}
195
196
197C4_SUPPRESS_WARNING_GCC_PUSH
198#if defined(__GNUC__) && __GNUC__>= 8
199 C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wclass-memaccess") // error: ‘void* memset(void*, int, size_t)’ clearing an object of type ‘class c4::yml::Tree’ with no trivial copy-assignment; use assignment or value-initialization instead
200#endif
201
202void Tree::_clear()
203{
204 m_buf = nullptr;
205 m_cap = 0;
206 m_size = 0;
207 m_free_head = 0;
208 m_free_tail = 0;
209 m_arena = {};
210 m_arena_pos = 0;
211 m_tag_directives.clear();
212}
213
214void Tree::_copy(Tree const& that)
215{
216 RYML_ASSERT_VISIT_CB_(m_callbacks, m_buf == nullptr, this, NONE);
217 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.str == nullptr, this, NONE);
218 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.len == 0, this, NONE);
219 if(that.m_cap)
220 {
221 m_buf = RYML_CB_ALLOC_HINT_(m_callbacks, NodeData, (size_t)that.m_cap, that.m_buf);
222 memcpy(m_buf, that.m_buf, (size_t)that.m_cap * sizeof(NodeData));
223 }
224 m_cap = that.m_cap;
225 m_size = that.m_size;
226 m_free_head = that.m_free_head;
227 m_free_tail = that.m_free_tail;
228 m_arena_pos = that.m_arena_pos;
229 m_arena = that.m_arena;
230 m_tag_directives = that.m_tag_directives;
231 if(that.m_arena.str)
232 {
233 RYML_ASSERT_VISIT_CB_(m_callbacks, that.m_arena.len > 0, this, NONE);
235 arena.str = RYML_CB_ALLOC_HINT_(m_callbacks, char, that.m_arena.len, that.m_arena.str);
236 arena.len = that.m_arena.len;
237 _relocate(arena); // does a memcpy of the arena and updates nodes using the old arena
238 m_arena = arena;
239 }
240}
241
242void Tree::_move(Tree & that) noexcept
243{
244 RYML_ASSERT_VISIT_CB_(m_callbacks, m_buf == nullptr, this, NONE);
245 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.str == nullptr, this, NONE);
246 RYML_ASSERT_VISIT_CB_(m_callbacks, m_arena.len == 0, this, NONE);
247 m_buf = that.m_buf;
248 m_cap = that.m_cap;
249 m_size = that.m_size;
250 m_free_head = that.m_free_head;
251 m_free_tail = that.m_free_tail;
252 m_arena = that.m_arena;
253 m_arena_pos = that.m_arena_pos;
254 m_tag_directives = that.m_tag_directives;
255 that._clear();
256}
257
258void Tree::_relocate(substr next_arena)
259{
260 RYML_ASSERT_VISIT_CB_(m_callbacks, next_arena.not_empty(), this, NONE);
261 RYML_ASSERT_VISIT_CB_(m_callbacks, next_arena.len >= m_arena.len, this, NONE);
262 if(m_arena_pos)
263 {
264 memcpy(next_arena.str, m_arena.str, m_arena_pos);
265 }
266 for(NodeData *C4_RESTRICT n = m_buf, *e = m_buf + m_cap; n != e; ++n)
267 {
268 if(in_arena(n->m_key.scalar))
269 n->m_key.scalar = _relocated(n->m_key.scalar, next_arena);
270 if(in_arena(n->m_key.tag))
271 n->m_key.tag = _relocated(n->m_key.tag, next_arena);
272 if(in_arena(n->m_key.anchor))
273 n->m_key.anchor = _relocated(n->m_key.anchor, next_arena);
274 if(in_arena(n->m_val.scalar))
275 n->m_val.scalar = _relocated(n->m_val.scalar, next_arena);
276 if(in_arena(n->m_val.tag))
277 n->m_val.tag = _relocated(n->m_val.tag, next_arena);
278 if(in_arena(n->m_val.anchor))
279 n->m_val.anchor = _relocated(n->m_val.anchor, next_arena);
280 }
281 for(TagDirective &C4_RESTRICT td : m_tag_directives)
282 {
283 if(in_arena(td.prefix))
284 td.prefix = _relocated(td.prefix, next_arena);
285 if(in_arena(td.handle))
286 td.handle = _relocated(td.handle, next_arena);
287 }
288}
289
290
291//-----------------------------------------------------------------------------
293{
294 if(cap > m_cap)
295 {
296 NodeData *buf = RYML_CB_ALLOC_HINT_(m_callbacks, NodeData, (size_t)cap, m_buf);
297 if(m_buf)
298 {
299 memcpy(buf, m_buf, (size_t)m_cap * sizeof(NodeData));
300 RYML_CB_FREE_(m_callbacks, m_buf, NodeData, (size_t)m_cap);
301 }
302 id_type first = m_cap, del = cap - m_cap;
303 m_cap = cap;
304 m_buf = buf;
305 _clear_range(first, del);
306 if(m_free_head != NONE)
307 {
308 RYML_ASSERT_VISIT_CB_(m_callbacks, m_buf != nullptr, this, NONE);
309 RYML_ASSERT_VISIT_CB_(m_callbacks, m_free_tail != NONE, this, NONE);
310 m_buf[m_free_tail].m_next_sibling = first;
311 m_buf[first].m_prev_sibling = m_free_tail;
312 m_free_tail = cap-1;
313 }
314 else
315 {
316 RYML_ASSERT_VISIT_CB_(m_callbacks, m_free_tail == NONE, this, NONE);
317 m_free_head = first;
318 m_free_tail = cap-1;
319 }
320 RYML_ASSERT_VISIT_CB_(m_callbacks, m_free_head == NONE || (m_free_head >= 0 && m_free_head < cap), this, NONE);
321 RYML_ASSERT_VISIT_CB_(m_callbacks, m_free_tail == NONE || (m_free_tail >= 0 && m_free_tail < cap), this, NONE);
322
323 if( ! m_size)
324 _claim_root();
325 }
326}
327
328
329//-----------------------------------------------------------------------------
331{
332 _clear_range(0, m_cap);
333 m_size = 0;
334 if(m_buf)
335 {
336 RYML_ASSERT_VISIT_CB_(m_callbacks, m_cap >= 0, this, NONE);
337 m_free_head = 0;
338 m_free_tail = m_cap-1;
339 _claim_root();
340 }
341 else
342 {
345 }
346 m_tag_directives.clear();
347}
348
349void Tree::_claim_root()
350{
351 id_type r = _claim();
352 RYML_ASSERT_VISIT_CB_(m_callbacks, r == 0, this, r);
353 _set_hierarchy(r, NONE, NONE);
354}
355
356
357//-----------------------------------------------------------------------------
358void Tree::_clear_range(id_type first, id_type num)
359{
360 if(num == 0)
361 return; // prevent overflow when subtracting
362 RYML_ASSERT_VISIT_CB_(m_callbacks, first >= 0 && first + num <= m_cap, this, first);
363 memset(m_buf + first, 0, (size_t)num * sizeof(NodeData)); // TODO we should not need this
364 for(id_type i = first, e = first + num; i < e; ++i)
365 {
366 _clear(i);
367 NodeData *n = m_buf + i;
368 n->m_prev_sibling = i - 1;
369 n->m_next_sibling = i + 1;
370 }
371 m_buf[first + num - 1].m_next_sibling = NONE;
372}
373
374C4_SUPPRESS_WARNING_GCC_POP
375
376
377//-----------------------------------------------------------------------------
378void Tree::_release(id_type i)
379{
380 RYML_ASSERT_VISIT_CB_(m_callbacks, i >= 0 && i < m_cap, this, i);
381
382 _rem_hierarchy(i);
383 _free_list_add(i);
384 _clear(i);
385
386 --m_size;
387}
388
389//-----------------------------------------------------------------------------
390// add to the front of the free list
391void Tree::_free_list_add(id_type i)
392{
393 RYML_ASSERT_VISIT_CB_(m_callbacks, i >= 0 && i < m_cap, this, i);
394 NodeData &C4_RESTRICT w = m_buf[i];
395
396 w.m_parent = NONE;
397 w.m_next_sibling = m_free_head;
398 w.m_prev_sibling = NONE;
399 if(m_free_head != NONE)
400 m_buf[m_free_head].m_prev_sibling = i;
401 m_free_head = i;
402 if(m_free_tail == NONE)
404}
405
406void Tree::_free_list_rem(id_type i)
407{
408 if(m_free_head == i)
410 _rem_hierarchy(i);
411}
412
413//-----------------------------------------------------------------------------
414id_type Tree::_claim()
415{
416 if(m_free_head == NONE || m_buf == nullptr)
417 {
418 id_type sz = 2 * m_cap;
419 sz = sz ? sz : 16;
420 reserve(sz);
421 RYML_ASSERT_VISIT_CB_(m_callbacks, m_free_head != NONE, this, NONE);
422 }
423
424 RYML_ASSERT_VISIT_CB_(m_callbacks, m_size < m_cap, this, NONE);
425 RYML_ASSERT_VISIT_CB_(m_callbacks, m_free_head >= 0 && m_free_head < m_cap, this, NONE);
426
427 id_type ichild = m_free_head;
428 NodeData *child = m_buf + ichild;
429
430 ++m_size;
431 m_free_head = child->m_next_sibling;
432 if(m_free_head == NONE)
433 {
435 RYML_ASSERT_VISIT_CB_(m_callbacks, m_size == m_cap, this, NONE);
436 }
437
438 _clear(ichild);
439
440 return ichild;
441}
442
443//-----------------------------------------------------------------------------
444
445void Tree::_set_hierarchy(id_type ichild, id_type iparent, id_type iprev_sibling)
446{
447 C4_SUPPRESS_WARNING_PUSH
448 C4_SUPPRESS_WARNING_CLANG("-Wnull-dereference")
449 #if defined(__GNUC__)
450 #if (__GNUC__ >= 6)
451 C4_SUPPRESS_WARNING_GCC("-Wnull-dereference")
452 #endif
453 #if (__GNUC__ > 9)
454 C4_SUPPRESS_WARNING_GCC("-Wanalyzer-fd-leak")
455 #endif
456 #endif
457 RYML_ASSERT_VISIT_CB_(m_callbacks, ichild >= 0 && ichild < m_cap, this, ichild);
458 RYML_ASSERT_VISIT_CB_(m_callbacks, iparent == NONE || (iparent >= 0 && iparent < m_cap), this, iparent);
459 RYML_ASSERT_VISIT_CB_(m_callbacks, iprev_sibling == NONE || (iprev_sibling >= 0 && iprev_sibling < m_cap), this, iprev_sibling);
460
461 NodeData *C4_RESTRICT child = _p(ichild);
462
463 child->m_parent = iparent;
464 child->m_prev_sibling = NONE;
465 child->m_next_sibling = NONE;
466
467 if(iparent == NONE)
468 {
469 RYML_ASSERT_VISIT_CB_(m_callbacks, ichild == 0, this, ichild);
470 RYML_ASSERT_VISIT_CB_(m_callbacks, iprev_sibling == NONE, this, iprev_sibling);
471 }
472
473 if(iparent == NONE)
474 return;
475
476 id_type inext_sibling = iprev_sibling != NONE ? next_sibling(iprev_sibling) : first_child(iparent);
477 NodeData *C4_RESTRICT parent = get(iparent);
478 NodeData *C4_RESTRICT psib = get(iprev_sibling);
479 NodeData *C4_RESTRICT nsib = get(inext_sibling);
480
481 if(psib)
482 {
483 RYML_ASSERT_VISIT_CB_(m_callbacks, next_sibling(iprev_sibling) == id(nsib), this, iprev_sibling);
484 child->m_prev_sibling = id(psib);
485 psib->m_next_sibling = id(child);
486 RYML_ASSERT_VISIT_CB_(m_callbacks, psib->m_prev_sibling != psib->m_next_sibling || psib->m_prev_sibling == NONE, this, iprev_sibling);
487 }
488
489 if(nsib)
490 {
491 RYML_ASSERT_VISIT_CB_(m_callbacks, prev_sibling(inext_sibling) == id(psib), this, inext_sibling);
492 child->m_next_sibling = id(nsib);
493 nsib->m_prev_sibling = id(child);
494 RYML_ASSERT_VISIT_CB_(m_callbacks, nsib->m_prev_sibling != nsib->m_next_sibling || nsib->m_prev_sibling == NONE, this, inext_sibling);
495 }
496
497 if(parent->m_first_child == NONE)
498 {
499 RYML_ASSERT_VISIT_CB_(m_callbacks, parent->m_last_child == NONE, this, parent->m_last_child);
500 parent->m_first_child = id(child);
501 parent->m_last_child = id(child);
502 }
503 else
504 {
505 if(child->m_next_sibling == parent->m_first_child)
506 parent->m_first_child = id(child);
507
508 if(child->m_prev_sibling == parent->m_last_child)
509 parent->m_last_child = id(child);
510 }
511 C4_SUPPRESS_WARNING_POP
512}
513
514
515
516//-----------------------------------------------------------------------------
517void Tree::_rem_hierarchy(id_type i)
518{
519 RYML_ASSERT_VISIT_CB_(m_callbacks, i >= 0 && i < m_cap, this, i);
520
521 NodeData &C4_RESTRICT w = m_buf[i];
522
523 // remove from the parent
524 if(w.m_parent != NONE)
525 {
526 NodeData &C4_RESTRICT p = m_buf[w.m_parent];
527 if(p.m_first_child == i)
528 {
529 p.m_first_child = w.m_next_sibling;
530 }
531 if(p.m_last_child == i)
532 {
533 p.m_last_child = w.m_prev_sibling;
534 }
535 }
536
537 // remove from the used list
538 if(w.m_prev_sibling != NONE)
539 {
540 NodeData *C4_RESTRICT prev = get(w.m_prev_sibling);
541 prev->m_next_sibling = w.m_next_sibling;
542 }
543 if(w.m_next_sibling != NONE)
544 {
545 NodeData *C4_RESTRICT next = get(w.m_next_sibling);
546 next->m_prev_sibling = w.m_prev_sibling;
547 }
548}
549
550//-----------------------------------------------------------------------------
551/** @cond dev */
552id_type Tree::_do_reorder(id_type *node, id_type count)
553{
554 // swap this node if it's not in place
555 if(*node != count)
556 {
557 _swap(*node, count);
558 *node = count;
559 }
560 ++count; // bump the count from this node
561
562 // now descend in the hierarchy
563 for(id_type i = first_child(*node); i != NONE; i = next_sibling(i))
564 {
565 // this child may have been relocated to a different index,
566 // so get an updated version
567 count = _do_reorder(&i, count);
568 }
569 return count;
570}
571/** @endcond */
572
574{
575 id_type r = root_id();
576 _do_reorder(&r, 0);
577}
578
579
580//-----------------------------------------------------------------------------
581/** @cond dev */
582void Tree::_swap(id_type n_, id_type m_)
583{
584 RYML_ASSERT_VISIT_CB_(m_callbacks, (parent(n_) != NONE) || type(n_) == NOTYPE, this, n_);
585 RYML_ASSERT_VISIT_CB_(m_callbacks, (parent(m_) != NONE) || type(m_) == NOTYPE, this, m_);
586 NodeType tn = type(n_);
587 NodeType tm = type(m_);
588 if(tn != NOTYPE && tm != NOTYPE)
589 {
590 _swap_props(n_, m_);
591 _swap_hierarchy(n_, m_);
592 }
593 else if(tn == NOTYPE && tm != NOTYPE)
594 {
595 _copy_props(n_, m_);
596 _free_list_rem(n_);
597 _copy_hierarchy(n_, m_);
598 _clear(m_);
599 _free_list_add(m_);
600 }
601 else if(tn != NOTYPE && tm == NOTYPE)
602 {
603 _copy_props(m_, n_);
604 _free_list_rem(m_);
605 _copy_hierarchy(m_, n_);
606 _clear(n_);
607 _free_list_add(n_);
608 }
609 else
610 {
611 C4_NEVER_REACH();
612 }
613}
614
615//-----------------------------------------------------------------------------
616void Tree::_swap_hierarchy(id_type ia, id_type ib)
617{
618 if(ia == ib) return;
619
620 for(id_type i = first_child(ia); i != NONE; i = next_sibling(i))
621 {
622 if(i == ib || i == ia)
623 continue;
624 _p(i)->m_parent = ib;
625 }
626
627 for(id_type i = first_child(ib); i != NONE; i = next_sibling(i))
628 {
629 if(i == ib || i == ia)
630 continue;
631 _p(i)->m_parent = ia;
632 }
633
634 auto & C4_RESTRICT a = *_p(ia);
635 auto & C4_RESTRICT b = *_p(ib);
636 auto & C4_RESTRICT pa = *_p(a.m_parent);
637 auto & C4_RESTRICT pb = *_p(b.m_parent);
638
639 if(&pa == &pb)
640 {
641 if((pa.m_first_child == ib && pa.m_last_child == ia)
642 ||
643 (pa.m_first_child == ia && pa.m_last_child == ib))
644 {
645 std::swap(pa.m_first_child, pa.m_last_child);
646 }
647 else
648 {
649 bool changed = false;
650 if(pa.m_first_child == ia)
651 {
652 pa.m_first_child = ib;
653 changed = true;
654 }
655 if(pa.m_last_child == ia)
656 {
657 pa.m_last_child = ib;
658 changed = true;
659 }
660 if(pb.m_first_child == ib && !changed)
661 {
662 pb.m_first_child = ia;
663 }
664 if(pb.m_last_child == ib && !changed)
665 {
666 pb.m_last_child = ia;
667 }
668 }
669 }
670 else
671 {
672 if(pa.m_first_child == ia)
673 pa.m_first_child = ib;
674 if(pa.m_last_child == ia)
675 pa.m_last_child = ib;
676 if(pb.m_first_child == ib)
677 pb.m_first_child = ia;
678 if(pb.m_last_child == ib)
679 pb.m_last_child = ia;
680 }
681 std::swap(a.m_first_child , b.m_first_child);
682 std::swap(a.m_last_child , b.m_last_child);
683
684 if(a.m_prev_sibling != ib && b.m_prev_sibling != ia &&
685 a.m_next_sibling != ib && b.m_next_sibling != ia)
686 {
687 if(a.m_prev_sibling != NONE && a.m_prev_sibling != ib)
688 _p(a.m_prev_sibling)->m_next_sibling = ib;
689 if(a.m_next_sibling != NONE && a.m_next_sibling != ib)
690 _p(a.m_next_sibling)->m_prev_sibling = ib;
691 if(b.m_prev_sibling != NONE && b.m_prev_sibling != ia)
692 _p(b.m_prev_sibling)->m_next_sibling = ia;
693 if(b.m_next_sibling != NONE && b.m_next_sibling != ia)
694 _p(b.m_next_sibling)->m_prev_sibling = ia;
695 std::swap(a.m_prev_sibling, b.m_prev_sibling);
696 std::swap(a.m_next_sibling, b.m_next_sibling);
697 }
698 else
699 {
700 if(a.m_next_sibling == ib) // n will go after m
701 {
702 RYML_ASSERT_VISIT_CB_(m_callbacks, b.m_prev_sibling == ia, this, ia);
703 if(a.m_prev_sibling != NONE)
704 {
705 RYML_ASSERT_VISIT_CB_(m_callbacks, a.m_prev_sibling != ib, this, ib);
706 _p(a.m_prev_sibling)->m_next_sibling = ib;
707 }
708 if(b.m_next_sibling != NONE)
709 {
710 RYML_ASSERT_VISIT_CB_(m_callbacks, b.m_next_sibling != ia, this, ia);
711 _p(b.m_next_sibling)->m_prev_sibling = ia;
712 }
713 id_type ns = b.m_next_sibling;
714 b.m_prev_sibling = a.m_prev_sibling;
715 b.m_next_sibling = ia;
716 a.m_prev_sibling = ib;
717 a.m_next_sibling = ns;
718 }
719 else if(a.m_prev_sibling == ib) // m will go after n
720 {
721 RYML_ASSERT_VISIT_CB_(m_callbacks, b.m_next_sibling == ia, this, ia);
722 if(b.m_prev_sibling != NONE)
723 {
724 RYML_ASSERT_VISIT_CB_(m_callbacks, b.m_prev_sibling != ia, this, ia);
725 _p(b.m_prev_sibling)->m_next_sibling = ia;
726 }
727 if(a.m_next_sibling != NONE)
728 {
729 RYML_ASSERT_VISIT_CB_(m_callbacks, a.m_next_sibling != ib, this, ib);
730 _p(a.m_next_sibling)->m_prev_sibling = ib;
731 }
732 id_type ns = b.m_prev_sibling;
733 a.m_prev_sibling = b.m_prev_sibling;
734 a.m_next_sibling = ib;
735 b.m_prev_sibling = ia;
736 b.m_next_sibling = ns;
737 }
738 else
739 {
740 C4_NEVER_REACH();
741 }
742 }
743 RYML_ASSERT_VISIT_CB_(m_callbacks, a.m_next_sibling != ia, this, ia);
744 RYML_ASSERT_VISIT_CB_(m_callbacks, a.m_prev_sibling != ia, this, ia);
745 RYML_ASSERT_VISIT_CB_(m_callbacks, b.m_next_sibling != ib, this, ib);
746 RYML_ASSERT_VISIT_CB_(m_callbacks, b.m_prev_sibling != ib, this, ib);
747
748 if(a.m_parent != ib && b.m_parent != ia)
749 {
750 std::swap(a.m_parent, b.m_parent);
751 }
752 else
753 {
754 if(a.m_parent == ib && b.m_parent != ia)
755 {
756 a.m_parent = b.m_parent;
757 b.m_parent = ia;
758 }
759 else if(a.m_parent != ib && b.m_parent == ia)
760 {
761 b.m_parent = a.m_parent;
762 a.m_parent = ib;
763 }
764 else
765 {
766 C4_NEVER_REACH();
767 }
768 }
769}
770
771//-----------------------------------------------------------------------------
772void Tree::_copy_hierarchy(id_type dst_, id_type src_)
773{
774 auto const& C4_RESTRICT src = *_p(src_);
775 auto & C4_RESTRICT dst = *_p(dst_);
776 auto & C4_RESTRICT prt = *_p(src.m_parent);
777 for(id_type i = src.m_first_child; i != NONE; i = next_sibling(i))
778 {
779 _p(i)->m_parent = dst_;
780 }
781 if(src.m_prev_sibling != NONE)
782 {
783 _p(src.m_prev_sibling)->m_next_sibling = dst_;
784 }
785 if(src.m_next_sibling != NONE)
786 {
787 _p(src.m_next_sibling)->m_prev_sibling = dst_;
788 }
789 if(prt.m_first_child == src_)
790 {
791 prt.m_first_child = dst_;
792 }
793 if(prt.m_last_child == src_)
794 {
795 prt.m_last_child = dst_;
796 }
797 dst.m_parent = src.m_parent;
798 dst.m_first_child = src.m_first_child;
799 dst.m_last_child = src.m_last_child;
800 dst.m_prev_sibling = src.m_prev_sibling;
801 dst.m_next_sibling = src.m_next_sibling;
802}
803
804//-----------------------------------------------------------------------------
805void Tree::_swap_props(id_type n_, id_type m_)
806{
807 NodeData &C4_RESTRICT n = *_p(n_);
808 NodeData &C4_RESTRICT m = *_p(m_);
809 std::swap(n.m_type, m.m_type);
810 std::swap(n.m_key, m.m_key);
811 std::swap(n.m_val, m.m_val);
812}
813/** @endcond */
814
815
816//-----------------------------------------------------------------------------
817void Tree::move(id_type node, id_type after)
818{
819 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
820 RYML_ASSERT_VISIT_CB_(m_callbacks, node != after, this, node);
821 RYML_ASSERT_VISIT_CB_(m_callbacks, ! is_root(node), this, node);
822 RYML_ASSERT_VISIT_CB_(m_callbacks, (after == NONE) || (has_sibling(node, after) && has_sibling(after, node)), this, node);
823
824 _rem_hierarchy(node);
825 _set_hierarchy(node, parent(node), after);
826}
827
828//-----------------------------------------------------------------------------
829
830void Tree::move(id_type node, id_type new_parent, id_type after)
831{
832 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
833 RYML_ASSERT_VISIT_CB_(m_callbacks, node != after, this, node);
834 RYML_ASSERT_VISIT_CB_(m_callbacks, new_parent != NONE, this, new_parent);
835 RYML_ASSERT_VISIT_CB_(m_callbacks, new_parent != node, this, new_parent);
836 RYML_ASSERT_VISIT_CB_(m_callbacks, new_parent != after, this, new_parent);
837 RYML_ASSERT_VISIT_CB_(m_callbacks, ! is_root(node), this, node);
838
839 _rem_hierarchy(node);
840 _set_hierarchy(node, new_parent, after);
841}
842
843id_type Tree::move(Tree *src, id_type node, id_type new_parent, id_type after)
844{
845 RYML_ASSERT_VISIT_CB_(m_callbacks, src != nullptr, this, new_parent);
846 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, new_parent);
847 RYML_ASSERT_VISIT_CB_(m_callbacks, new_parent != NONE, this, new_parent);
848 RYML_ASSERT_VISIT_CB_(m_callbacks, new_parent != after, this, new_parent);
849
850 id_type dup = duplicate(src, node, new_parent, after);
851 src->remove(node);
852 return dup;
853}
854
856{
857 id_type root = root_id();
858 NodeType ty = type(root);
859 if(ty.is_stream())
860 return;
861 _c4dbgpf("set_root_as_stream. rootty={}", type(root).m_bits);
862 bool empty_root = ((type(root) & (SEQ|MAP|VAL)) == 0);
863 for(TagDirective &C4_RESTRICT td : m_tag_directives)
864 {
865 if(td.doc_id >= m_cap || _p(td.doc_id)->m_parent == NONE)
866 {
867 _c4dbgpf("tagd[{}]: id={}->NONE", &td-m_tag_directives.m_directives, td.doc_id);
868 td.doc_id = NONE;
869 }
870 }
871 // don't use _add_flags() because it's checked and will fail
872 id_type next_doc;
873 if(!has_children(root))
874 {
875 if(ty.is_container())
876 {
877 next_doc = append_child(root);
878 _copy_props_wo_key(next_doc, root);
879 _p(next_doc)->m_type.add(DOC);
880 }
881 else
882 {
883 _p(root)->m_type.add(SEQ);
884 next_doc = append_child(root);
885 _copy_props_wo_key(next_doc, root);
886 _p(next_doc)->m_type.add(DOC);
887 _p(next_doc)->m_type.rem(SEQ);
888 }
889 }
890 else
891 {
892 RYML_ASSERT_VISIT_CB_(m_callbacks, !ty.has_key(), this, root);
893 next_doc = append_child(root);
894 _copy_props_wo_key(next_doc, root);
895 _add_flags(next_doc, DOC);
896 for(id_type prev = NONE, ch = first_child(root), next = next_sibling(ch); ch != NONE; )
897 {
898 if(ch == next_doc)
899 break;
900 move(ch, next_doc, prev);
901 prev = ch;
902 ch = next;
903 next = next_sibling(next);
904 }
905 }
906 _p(root)->m_type = STREAM;
907 for(TagDirective &C4_RESTRICT td : m_tag_directives)
908 {
909 id_type id = (td.doc_id != NONE) ? next_doc : (empty_root ? first_child(root) : m_free_head);
910 _c4dbgpf("tagd[{}]: id={}->{}", &td-m_tag_directives.m_directives, td.doc_id, id);
911 td.doc_id = id;
912 }
913}
914
915
916//-----------------------------------------------------------------------------
918{
919 RYML_ASSERT_VISIT_CB_(m_callbacks, get(node) != nullptr, this, node);
920 C4_SUPPRESS_WARNING_GCC_PUSH
921 #if defined(__GNUC__) && __GNUC__ >= 6
922 C4_SUPPRESS_WARNING_GCC("-Wnull-dereference")
923 #endif
924 id_type ich = get(node)->m_first_child;
925 while(ich != NONE)
926 {
927 remove_children(ich);
928 RYML_ASSERT_VISIT_CB_(m_callbacks, get(ich) != nullptr, this, node);
929 id_type next = get(ich)->m_next_sibling;
930 _release(ich);
931 if(ich == get(node)->m_last_child)
932 break;
933 ich = next;
934 }
935 C4_SUPPRESS_WARNING_GCC_POP
936}
937
938
939//-----------------------------------------------------------------------------
941{
942 NodeType curr = this->type(node);
943 RYML_ASSERT_VISIT_CB_(m_callbacks, next.is_val() || next.is_map() || next.is_seq(), this, node);
944 RYML_ASSERT_VISIT_CB_(m_callbacks, next.is_val() + next.is_map() + next.is_seq() == 1, this, node);
945 RYML_ASSERT_VISIT_CB_(m_callbacks, next.has_key() == curr.has_key() || (curr.has_key() && !next.has_key()), this, node);
946 NodeData *d = _p(node);
947 if(next.is_map() && curr.is_map())
948 return false;
949 else if(next.is_seq() && curr.is_seq())
950 return false;
951 else if(next.is_val() && curr.is_val())
952 return false;
954 remove_children(node);
955 return true;
956}
957
958
959//-----------------------------------------------------------------------------
961{
962 return duplicate(this, node, parent, after);
963}
964
966{
967 RYML_ASSERT_VISIT_CB_(m_callbacks, src != nullptr, src, node);
968 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, src, node);
969 RYML_ASSERT_VISIT_CB_(m_callbacks, parent != NONE, this, parent);
970 RYML_ASSERT_VISIT_CB_(m_callbacks, ! src->is_root(node), src, node);
971
972 id_type copy = _claim();
973
974 _copy_props(copy, src, node);
975 _set_hierarchy(copy, parent, after);
976 duplicate_children(src, node, copy, NONE);
977
978 return copy;
979}
980
981
982//-----------------------------------------------------------------------------
984{
985 return duplicate_children(this, node, parent, after);
986}
987
989{
990 RYML_ASSERT_VISIT_CB_(m_callbacks, src != nullptr, src, node);
991 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, src, node);
992 RYML_ASSERT_VISIT_CB_(m_callbacks, parent != NONE, this, parent);
993 RYML_ASSERT_VISIT_CB_(m_callbacks, after == NONE || has_child(parent, after), this, parent);
994
995 id_type prev = after;
996 for(id_type i = src->first_child(node); i != NONE; i = src->next_sibling(i))
997 {
998 prev = duplicate(src, i, parent, prev);
999 }
1000
1001 return prev;
1002}
1003
1004//-----------------------------------------------------------------------------
1006{
1007 duplicate_contents(this, node, where);
1008}
1009
1010void Tree::duplicate_contents(Tree const *src, id_type node, id_type where)
1011{
1012 RYML_ASSERT_VISIT_CB_(m_callbacks, src != nullptr, src, node);
1013 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, src, node);
1014 RYML_ASSERT_VISIT_CB_(m_callbacks, where != NONE, this, where);
1015 _copy_props_wo_key(where, src, node);
1016 duplicate_children(src, node, where, last_child(where));
1017}
1018
1019//-----------------------------------------------------------------------------
1024
1026{
1027 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, src, node);
1028 RYML_ASSERT_VISIT_CB_(m_callbacks, parent != NONE, this, parent);
1029 RYML_ASSERT_VISIT_CB_(m_callbacks, after == NONE || has_child(parent, after), this, parent);
1030
1031 // don't loop using pointers as there may be a relocation
1032
1033 // find the position where "after" is
1034 id_type after_pos = NONE;
1035 if(after != NONE)
1036 {
1037 for(id_type i = first_child(parent), icount = 0; i != NONE; ++icount, i = next_sibling(i))
1038 {
1039 if(i == after)
1040 {
1041 after_pos = icount;
1042 break;
1043 }
1044 }
1045 RYML_ASSERT_VISIT_CB_(m_callbacks, after_pos != NONE, this, node);
1046 }
1047
1048 // for each child to be duplicated...
1049 id_type prev = after;
1050 NodeType pty = type(parent);
1051 for(id_type i = src->first_child(node); i != NONE; i = src->next_sibling(i))
1052 {
1053 _c4dbgpf("duplicate_no_rep: {} -> {}/{}", i, parent, prev);
1054 RYML_CHECK_VISIT_CB_(m_callbacks, this != src || (parent != i && !is_ancestor(parent, i)), this, parent);
1055 if(pty.is_seq())
1056 {
1057 _c4dbgpf("duplicate_no_rep: {} is seq", parent);
1058 prev = duplicate(src, i, parent, prev);
1059 }
1060 else
1061 {
1062 _c4dbgpf("duplicate_no_rep: {} is map", parent);
1063 RYML_ASSERT_VISIT_CB_(m_callbacks, pty.is_map(), this, parent);
1064 // does the parent already have a node with key equal to that of the current duplicate?
1065 id_type dstnode_dup = NONE, dstnode_dup_pos = NONE;
1066 {
1067 csubstr srckey = src->key(i);
1068 for(id_type j = first_child(parent), jcount = 0; j != NONE; ++jcount, j = next_sibling(j))
1069 {
1070 if(key(j) == srckey)
1071 {
1072 _c4dbgpf("duplicate_no_rep: found matching key '{}' src={}/{} dst={}/{}", srckey, node, i, parent, j);
1073 dstnode_dup = j;
1074 dstnode_dup_pos = jcount;
1075 break;
1076 }
1077 }
1078 }
1079 _c4dbgpf("duplicate_no_rep: dstnode_dup={} dstnode_dup_pos={} after_pos={}", dstnode_dup, dstnode_dup_pos, after_pos);
1080 if(dstnode_dup == NONE) // there is no repetition; just duplicate
1081 {
1082 _c4dbgpf("duplicate_no_rep: no repetition, just duplicate i={} parent={} prev={}", i, parent, prev);
1083 prev = duplicate(src, i, parent, prev);
1084 }
1085 else // yes, there is a repetition
1086 {
1087 if(after_pos != NONE && dstnode_dup_pos <= after_pos)
1088 {
1089 // the dst duplicate is located before the node which will be inserted,
1090 // and will be overridden by the duplicate. So replace it.
1091 _c4dbgpf("duplicate_no_dstnode_dup: replace {}/{} with {}/{}", parent, dstnode_dup, node, i);
1092 if(prev == dstnode_dup)
1093 prev = prev_sibling(dstnode_dup);
1094 remove(dstnode_dup);
1095 prev = duplicate(src, i, parent, prev);
1096 }
1097 else if(prev == NONE)
1098 {
1099 _c4dbgpf("duplicate_no_dstnode_dup: {}=prev <- {}", prev, dstnode_dup);
1100 // first iteration with prev = after = NONE and dstnode_dupetition
1101 prev = dstnode_dup;
1102 }
1103 else if(dstnode_dup != prev)
1104 {
1105 // dstnode_dup is located after the node which will be inserted
1106 // and overrides it. So move the dstnode_dup into this node's place.
1107 _c4dbgpf("duplicate_no_dstnode_dup: move({}, {})", dstnode_dup, prev);
1108 move(dstnode_dup, prev);
1109 prev = dstnode_dup;
1110 }
1111 } // there's a dstnode_dupetition
1112 }
1113 }
1114
1115 return prev;
1116}
1117
1118
1119//-----------------------------------------------------------------------------
1120
1121void Tree::merge_with(Tree const *src, id_type src_node, id_type dst_node)
1122{
1123 RYML_ASSERT_VISIT_CB_(m_callbacks, src != nullptr, src, src_node);
1124 if(src_node == NONE)
1125 src_node = src->root_id();
1126 if(dst_node == NONE)
1127 dst_node = root_id();
1128 NodeType srcty = src->type(src_node);
1129 NodeType dstty = type(dst_node);
1130 RYML_ASSERT_VISIT_CB_(m_callbacks, srcty.has_val() || srcty.is_seq() || srcty.is_map(), src, src_node);
1131 if(srcty.has_val())
1132 {
1133 type_bits mask_src = ~STYLE; // keep the existing style if it is already a val
1134 if( ! dstty.has_val())
1135 {
1136 if(has_children(dst_node))
1137 remove_children(dst_node);
1138 mask_src |= VAL_STYLE; // copy the src style
1139 }
1140 if(srcty.is_keyval())
1141 {
1142 _copy_props(dst_node, src, src_node, mask_src);
1143 }
1144 else
1145 {
1146 RYML_ASSERT_VISIT_CB_(m_callbacks, srcty.is_val(), src, src_node);
1147 _copy_props_wo_key(dst_node, src, src_node, mask_src);
1148 }
1149 }
1150 else if(srcty.is_seq())
1151 {
1152 if( ! dstty.is_seq())
1153 {
1154 if(has_children(dst_node))
1155 remove_children(dst_node);
1156 _clear_type(dst_node);
1157 if(src->has_key(src_node))
1158 set_key(dst_node, src->key(src_node));
1159 set_seq(dst_node);
1160 _p(dst_node)->m_type = src->_p(src_node)->m_type;
1161 }
1162 for(id_type sch = src->first_child(src_node); sch != NONE; sch = src->next_sibling(sch))
1163 {
1164 id_type dch = append_child(dst_node);
1165 _copy_props_wo_key(dch, src, sch);
1166 merge_with(src, sch, dch);
1167 }
1168 }
1169 else
1170 {
1171 RYML_ASSERT_VISIT_CB_(m_callbacks, srcty.is_map(), src, src_node);
1172 if( ! dstty.is_map())
1173 {
1174 if(has_children(dst_node))
1175 remove_children(dst_node);
1176 _clear_type(dst_node);
1177 if(src->has_key(src_node))
1178 set_key(dst_node, src->key(src_node));
1179 set_map(dst_node);
1180 _p(dst_node)->m_type = src->_p(src_node)->m_type;
1181 }
1182 for(id_type sch = src->first_child(src_node); sch != NONE; sch = src->next_sibling(sch))
1183 {
1184 id_type dch = find_child(dst_node, src->key(sch));
1185 if(dch == NONE)
1186 {
1187 dch = append_child(dst_node);
1188 _copy_props(dch, src, sch);
1189 }
1190 merge_with(src, sch, dch);
1191 }
1192 }
1193}
1194
1195
1196//-----------------------------------------------------------------------------
1197
1198void Tree::resolve(bool clear_anchors)
1199{
1200 if(m_size == 0)
1201 return;
1203 resolve(&rr, clear_anchors);
1204}
1205
1206void Tree::resolve(ReferenceResolver *C4_RESTRICT rr, bool clear_anchors)
1207{
1208 if(m_size == 0)
1209 return;
1210 rr->resolve(this, clear_anchors);
1211}
1212
1213
1214//-----------------------------------------------------------------------------
1215
1217{
1218 id_type count = 0;
1219 for(id_type i = first_child(node); i != NONE; i = next_sibling(i))
1220 ++count;
1221 return count;
1222}
1223
1225{
1226 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
1227 id_type count = 0;
1228 for(id_type i = first_child(node); i != NONE; i = next_sibling(i))
1229 {
1230 if(i == ch)
1231 return count;
1232 ++count;
1233 }
1234 return NONE;
1235}
1236
1238{
1239 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
1240 id_type count = 0;
1241 for(id_type i = first_child(node); i != NONE; i = next_sibling(i))
1242 {
1243 if(count++ == pos)
1244 return i;
1245 }
1246 return NONE;
1247}
1248
1249id_type Tree::find_child(id_type node, csubstr const& name) const
1250{
1251 C4_SUPPRESS_WARNING_PUSH
1252 #if defined(__clang__)
1253 #elif defined(__GNUC__)
1254 # if __GNUC__ >= 6
1255 C4_SUPPRESS_WARNING_GCC("-Wnull-dereference")
1256 # endif
1257 # if __GNUC__ > 9
1258 C4_SUPPRESS_WARNING_GCC("-Wanalyzer-null-dereference")
1259 # endif
1260 #endif
1261 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
1262 RYML_ASSERT_VISIT_CB_(m_callbacks, _p(node)->m_type.m_bits & MAP, this, node);
1263 for(id_type i = first_child(node); i != NONE; i = next_sibling(i))
1264 {
1265 if(_p(i)->m_key.scalar == name)
1266 return i;
1267 }
1268 return NONE;
1269 C4_SUPPRESS_WARNING_POP
1270}
1271
1272
1273namespace {
1274id_type depth_desc_(Tree const& C4_RESTRICT t, id_type id, id_type currdepth=0, id_type maxdepth=0)
1275{
1276 maxdepth = currdepth > maxdepth ? currdepth : maxdepth;
1277 for(id_type child = t.first_child(id); child != NONE; child = t.next_sibling(child))
1278 {
1279 const id_type d = depth_desc_(t, child, currdepth+1, maxdepth);
1280 maxdepth = d > maxdepth ? d : maxdepth;
1281 }
1282 return maxdepth;
1283}
1284}
1285
1287{
1288 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
1289 return depth_desc_(*this, node);
1290}
1291
1293{
1294 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
1295 id_type depth = 0;
1296 while(!is_root(node))
1297 {
1298 ++depth;
1299 node = parent(node);
1300 }
1301 return depth;
1302}
1303
1304bool Tree::is_ancestor(id_type node, id_type ancestor) const
1305{
1306 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, node);
1307 id_type p = parent(node);
1308 while(p != NONE)
1309 {
1310 if(p == ancestor)
1311 return true;
1312 p = parent(p);
1313 }
1314 return false;
1315}
1316
1317
1318//-----------------------------------------------------------------------------
1319
1320/** @cond dev */ // LCOV_EXCL_START
1321void Tree::to_val(id_type node, csubstr val, type_bits more_flags)
1322{
1323 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1324 RYML_ASSERT_VISIT_CB_(m_callbacks, parent(node) == NONE || ! parent_is_map(node), this, node);
1325 RYML_ASSERT_VISIT_CB_(m_callbacks, !is_seq(node) && !is_map(node), this, node);
1326 NodeData* C4_RESTRICT nd = _p(node);
1327 nd->m_type = VAL|more_flags;
1328 nd->m_key.clear();
1329 nd->m_val = val;
1330}
1331
1332void Tree::to_keyval(id_type node, csubstr key, csubstr val, type_bits more_flags)
1333{
1334 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1335 RYML_ASSERT_VISIT_CB_(m_callbacks, parent(node) == NONE || parent_is_map(node), this, node);
1336 RYML_ASSERT_VISIT_CB_(m_callbacks, !is_seq(node) && !is_map(node), this, node);
1337 NodeData* C4_RESTRICT nd = _p(node);
1338 nd->m_type = KEYVAL|more_flags;
1339 nd->m_key = key;
1340 nd->m_val = val;
1341}
1342
1343void Tree::to_map(id_type node, type_bits more_flags)
1344{
1345 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1346 NodeData* C4_RESTRICT nd = _p(node);
1347 nd->m_type = MAP|more_flags;
1348 nd->m_key.clear();
1349 nd->m_val.clear();
1350}
1351
1352void Tree::to_map(id_type node, csubstr key, type_bits more_flags)
1353{
1354 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1355 RYML_ASSERT_VISIT_CB_(m_callbacks, parent(node) == NONE || parent_is_map(node), this, node);
1356 NodeData* C4_RESTRICT nd = _p(node);
1357 nd->m_type = KEY|MAP|more_flags;
1358 nd->m_key = key;
1359 nd->m_val.clear();
1360}
1361
1362void Tree::to_seq(id_type node, type_bits more_flags)
1363{
1364 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1365 RYML_ASSERT_VISIT_CB_(m_callbacks, parent(node) == NONE || parent_is_seq(node), this, node);
1366 NodeData* C4_RESTRICT nd = _p(node);
1367 nd->m_type = SEQ|more_flags;
1368 nd->m_key.clear();
1369 nd->m_val.clear();
1370}
1371
1372void Tree::to_seq(id_type node, csubstr key, type_bits more_flags)
1373{
1374 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1375 RYML_ASSERT_VISIT_CB_(m_callbacks, parent(node) == NONE || parent_is_map(node), this, node);
1376 NodeData* C4_RESTRICT nd = _p(node);
1377 nd->m_type = KEY|SEQ|more_flags;
1378 nd->m_key = key;
1379 nd->m_val.clear();
1380}
1381
1382void Tree::to_doc(id_type node, type_bits more_flags)
1383{
1384 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1385 NodeData* C4_RESTRICT nd = _p(node);
1386 nd->m_type = DOC|more_flags;
1387 nd->m_key.clear();
1388 nd->m_val.clear();
1389}
1390
1391void Tree::to_stream(id_type node, type_bits more_flags)
1392{
1393 RYML_ASSERT_VISIT_CB_(m_callbacks, ! has_children(node), this, node);
1394 NodeData* C4_RESTRICT nd = _p(node);
1395 nd->m_type = STREAM|more_flags;
1396 nd->m_key.clear();
1397 nd->m_val.clear();
1398}
1399/** @endcond */ // LCOV_EXCL_STOP
1400
1401
1402//-----------------------------------------------------------------------------
1403
1404void Tree::clear_style(id_type node, bool recurse)
1405{
1406 NodeData *C4_RESTRICT d = _p(node);
1407 d->m_type.clear_style();
1408 if(!recurse)
1409 return;
1410 for(id_type child = d->m_first_child; child != NONE; child = next_sibling(child))
1411 clear_style(child, recurse);
1412}
1413
1415 NodeType type_mask,
1416 NodeType rem_style_flags,
1417 NodeType add_style_flags,
1418 bool recurse)
1419{
1420 NodeData *C4_RESTRICT d = _p(node);
1421 if((d->m_type & type_mask) == type_mask)
1422 {
1423 d->m_type &= ~(NodeType)rem_style_flags;
1424 d->m_type |= (NodeType)add_style_flags;
1425 }
1426 if(!recurse)
1427 return;
1428 for(id_type child = d->m_first_child; child != NONE; child = next_sibling(child))
1429 set_style_conditionally(child, type_mask, rem_style_flags, add_style_flags, recurse);
1430}
1431
1432
1433//-----------------------------------------------------------------------------
1435{
1436 return m_tag_directives.size();
1437}
1438
1440{
1441 m_tag_directives.clear();
1442}
1443
1445{
1446 RYML_CHECK_BASIC_CB_(m_callbacks,
1447 !handle.empty()
1448 &&
1449 !prefix.empty()
1450 &&
1451 is_valid_tag_handle(handle)
1452 &&
1453 m_tag_directives.add(handle, prefix, id));
1454}
1455
1456size_t Tree::resolve_tag(substr output, csubstr tag, id_type node_id) const
1457{
1458 size_t reqsz = 0;
1459 m_tag_directives.resolve(output, &reqsz, tag, node_id, Location{}, callbacks());
1460 return reqsz;
1461}
1462
1463namespace {
1464// return the extra size needed for the arena to accomodate the resolved tag
1465size_t _transform_tag(Tree *t, id_type node_id, id_type doc_id, TagCache &cache, csubstr tag, csubstr *resolved)
1466{
1467 _c4dbgpf("tag: doc={} node={} resolving tag ~~~{}~~~", doc_id, node_id, tag);
1468 (void)node_id;
1469 size_t reqsize = 0;
1470 if(tag.begins_with('<'))
1471 {
1472 *resolved = tag;
1473 }
1474 else
1475 {
1476 RYML_ASSERT_VISIT_CB_(t->callbacks(), !tag.begins_with("!<"), t, node_id); // this should have been handled elsewhere
1477 TagCache::LookupResult ret = cache.find(tag, doc_id);
1478 if(ret)
1479 {
1480 _c4dbgpf("tag: doc={} node={} resolving tag: found in cache[{}]: {}", doc_id, node_id, ret.pos, prs_(ret.resolved));
1481 *resolved = ret.resolved;
1482 }
1483 else
1484 {
1485 _c4dbgpf("tag: doc={} node={} tag not in cache ~~~{}~~~", doc_id, node_id, tag);
1486 substr buf = t->m_arena.sub(t->m_arena_pos);
1487 reqsize = t->resolve_tag(buf, tag, doc_id);
1488 if(!reqsize)
1489 {
1490 *resolved = tag;
1491 }
1492 else if(reqsize <= buf.len)
1493 {
1494 t->m_arena_pos += reqsize;
1495 *resolved = buf.first(reqsize);
1496 cache.add(tag, *resolved, doc_id, ret.pos);
1497 reqsize = 0;
1498 }
1499 else
1500 {
1501 _c4dbgpf("tag: doc={} node={} extra size needed: {}", doc_id, node_id, reqsize);
1502 }
1503 _c4dbgpf("tag: doc={} node={} resolved tag: ~~~{}~~~", doc_id, node_id, *resolved);
1504 }
1505 }
1506 return reqsize;
1507}
1508size_t _resolve_tags(Tree *t, id_type node, id_type doc_id, TagCache &cache, bool all=true)
1509{
1510 NodeData *C4_RESTRICT d = t->_p(node);
1511 size_t extra_size = 0;
1512 if((d->m_type & KEYTAG) && (all || is_custom_tag(d->m_key.tag)))
1513 extra_size += _transform_tag(t, node, doc_id, cache, d->m_key.tag, &d->m_key.tag);
1514 if((d->m_type & VALTAG) && (all || is_custom_tag(d->m_val.tag)))
1515 extra_size += _transform_tag(t, node, doc_id, cache, d->m_val.tag, &d->m_val.tag);
1516 for(id_type child = t->first_child(node); child != NONE; child = t->next_sibling(child))
1517 extra_size += _resolve_tags(t, child, doc_id, cache);
1518 return extra_size;
1519}
1520size_t _resolve_tags(Tree *t, TagCache &cache, bool all)
1521{
1522 id_type r = t->root_id();
1523 size_t extra_size = 0;
1524 if(!t->is_stream(r))
1525 extra_size += _resolve_tags(t, r, r, cache, all);
1526 else
1527 for(id_type doc_id = t->first_child(r); doc_id != NONE; doc_id = t->next_sibling(doc_id))
1528 extra_size += _resolve_tags(t, doc_id, doc_id, cache, all);
1529 return extra_size;
1530}
1531void _normalize_tags(Tree *t, id_type node)
1532{
1533 NodeData *C4_RESTRICT d = t->_p(node);
1534 if(d->m_type & KEYTAG)
1535 d->m_key.tag = normalize_tag(d->m_key.tag);
1536 if(d->m_type & VALTAG)
1537 d->m_val.tag = normalize_tag(d->m_val.tag);
1538 for(id_type child = t->first_child(node); child != NONE; child = t->next_sibling(child))
1539 _normalize_tags(t, child);
1540}
1541void _normalize_tags_long(Tree *t, id_type node)
1542{
1543 NodeData *C4_RESTRICT d = t->_p(node);
1544 if(d->m_type & KEYTAG)
1545 d->m_key.tag = normalize_tag_long(d->m_key.tag);
1546 if(d->m_type & VALTAG)
1547 d->m_val.tag = normalize_tag_long(d->m_val.tag);
1548 for(id_type child = t->first_child(node); child != NONE; child = t->next_sibling(child))
1549 _normalize_tags_long(t, child);
1550}
1551} // namespace
1552
1553void Tree::resolve_tags(TagCache &cache, bool all)
1554{
1555 if(empty())
1556 return;
1557 // try to resolve. While doing so, get the extra size needed for
1558 // the arena, if the arena is currently too small.
1559 size_t extra_size = _resolve_tags(this, cache, all);
1560 // if the arena requires extra size, grow it and then resolve the
1561 // missing entries
1562 if(extra_size)
1563 {
1564 _c4dbgpf("tag: extrasize={} -- retry! {}->{}", extra_size, m_arena.len, m_arena.len + extra_size);
1565 _grow_arena(extra_size);
1566 extra_size = _resolve_tags(this, cache, all);
1567 RYML_ASSERT_BASIC_CB_(callbacks(), extra_size == 0);
1568 }
1569}
1570
1572{
1573 if(empty())
1574 return;
1575 _normalize_tags(this, root_id());
1576}
1577
1579{
1580 if(empty())
1581 return;
1582 _normalize_tags_long(this, root_id());
1583}
1584
1585
1586//-----------------------------------------------------------------------------
1587
1589{
1590 csubstr p = path.first(path_pos);
1591 if(p.ends_with('.'))
1592 p = p.first(p.len-1);
1593 return p;
1594}
1595
1597{
1598 return path.sub(path_pos);
1599}
1600
1601void Tree::_advance(lookup_result *r, size_t more)
1602{
1603 r->path_pos += more;
1604 if(r->path.sub(r->path_pos).begins_with('.'))
1605 ++r->path_pos;
1606}
1607
1609{
1610 if(start == NONE)
1611 start = root_id();
1612 lookup_result r(path, start);
1613 if(path.empty())
1614 return r;
1615 _lookup_path(&r);
1616 if(r.target == NONE && r.closest == start)
1617 r.closest = NONE;
1618 return r;
1619}
1620
1622{
1623 id_type target = _lookup_path_or_create(path, start);
1624 set_val(target, default_value);
1625 return target;
1626}
1627
1629{
1630 id_type target = _lookup_path_or_create(path, start);
1631 merge_with(src, src_node, target);
1632 return target;
1633}
1634
1635id_type Tree::_lookup_path_or_create(csubstr path, id_type start)
1636{
1637 if(start == NONE)
1638 start = root_id();
1639 lookup_result r(path, start);
1640 _lookup_path(&r);
1641 if(r.target != NONE)
1642 {
1643 C4_ASSERT(r.unresolved().empty());
1644 return r.target;
1645 }
1646 _lookup_path_modify(&r);
1647 return r.target;
1648}
1649
1650void Tree::_lookup_path(lookup_result *r) const
1651{
1652 C4_ASSERT( ! r->unresolved().empty());
1653 _lookup_path_token parent{"", type(r->closest)};
1654 id_type node;
1655 do
1656 {
1657 node = _next_node(r, &parent);
1658 if(node != NONE)
1659 r->closest = node;
1660 if(r->unresolved().empty())
1661 {
1662 r->target = node;
1663 return;
1664 }
1665 } while(node != NONE);
1666}
1667
1668void Tree::_lookup_path_modify(lookup_result *r)
1669{
1670 C4_ASSERT( ! r->unresolved().empty());
1671 _lookup_path_token parent{"", type(r->closest)};
1672 id_type node;
1673 do
1674 {
1675 node = _next_node_modify(r, &parent);
1676 if(node != NONE)
1677 r->closest = node;
1678 if(r->unresolved().empty())
1679 {
1680 r->target = node;
1681 return;
1682 }
1683 } while(node != NONE);
1684}
1685
1686id_type Tree::_next_node(lookup_result * r, _lookup_path_token *parent) const
1687{
1688 _lookup_path_token token = _next_token(r, *parent);
1689 if( ! token)
1690 return NONE;
1691
1692 id_type node = NONE;
1693 csubstr prev = token.value;
1694 if(token.type == MAP || token.type == SEQ)
1695 {
1696 RYML_ASSERT_VISIT_CB_(m_callbacks, !token.value.begins_with('['), this, r->closest);
1697 //RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(r->closest) || r->closest == NONE);
1698 RYML_ASSERT_VISIT_CB_(m_callbacks, is_map(r->closest), this, r->closest);
1699 node = find_child(r->closest, token.value);
1700 }
1701 else if(token.type == KEYVAL)
1702 {
1703 RYML_ASSERT_VISIT_CB_(m_callbacks, r->unresolved().empty(), this, r->closest);
1704 if(is_map(r->closest))
1705 node = find_child(r->closest, token.value);
1706 }
1707 else if(token.type == KEY)
1708 {
1709 RYML_ASSERT_VISIT_CB_(m_callbacks, token.value.begins_with('[') && token.value.ends_with(']'), this, r->closest);
1710 token.value = token.value.offs(1, 1).trim(' ');
1711 id_type idx = 0;
1712 RYML_CHECK_BASIC_CB_(m_callbacks, from_chars(token.value, &idx));
1713 node = child(r->closest, idx);
1714 }
1715 else
1716 {
1717 C4_NEVER_REACH();
1718 }
1719
1720 if(node != NONE)
1721 {
1722 *parent = token;
1723 }
1724 else
1725 {
1726 csubstr p = r->path.sub(r->path_pos > 0 ? r->path_pos - 1 : r->path_pos);
1727 r->path_pos -= prev.len;
1728 if(p.begins_with('.'))
1729 r->path_pos -= 1u;
1730 }
1731
1732 return node;
1733}
1734
1735id_type Tree::_next_node_modify(lookup_result * r, _lookup_path_token *parent)
1736{
1737 _lookup_path_token token = _next_token(r, *parent);
1738 if( ! token)
1739 return NONE;
1740
1741 id_type node = NONE;
1742 NodeType ty = type(r->closest);
1743 if(token.type == MAP || token.type == SEQ)
1744 {
1745 RYML_ASSERT_VISIT_CB_(m_callbacks, !token.value.begins_with('['), this, r->closest);
1746 //RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(r->closest) || r->closest == NONE);
1747 if( ! ty.is_container())
1748 {
1749 set_map(r->closest);
1750 }
1751 else
1752 {
1753 if(ty.is_map())
1754 {
1755 node = find_child(r->closest, token.value);
1756 }
1757 else
1758 {
1759 id_type pos = NONE;
1760 RYML_CHECK_BASIC_CB_(m_callbacks, c4::atox(token.value, &pos));
1761 RYML_ASSERT_VISIT_CB_(m_callbacks, pos != NONE, this, r->closest);
1762 node = child(r->closest, pos);
1763 }
1764 }
1765 if(node == NONE)
1766 {
1767 RYML_ASSERT_VISIT_CB_(m_callbacks, is_map(r->closest), this, r->closest);
1768 node = append_child(r->closest);
1769 NodeData *n = _p(node);
1770 n->m_key.scalar = token.value;
1771 n->m_type.add(KEY);
1772 }
1773 }
1774 else if(token.type == KEYVAL)
1775 {
1776 RYML_ASSERT_VISIT_CB_(m_callbacks, r->unresolved().empty(), this, r->closest);
1777 if(ty.is_map())
1778 {
1779 node = find_child(r->closest, token.value);
1780 if(node == NONE)
1781 node = append_child(r->closest);
1782 }
1783 else
1784 {
1785 RYML_ASSERT_VISIT_CB_(m_callbacks, !ty.is_seq(), this, r->closest);
1786 _add_flags(r->closest, MAP);
1787 node = append_child(r->closest);
1788 }
1789 NodeData *n = _p(node);
1790 n->m_key.scalar = token.value;
1791 n->m_val.scalar = "";
1792 n->m_type.add(KEYVAL);
1793 }
1794 else if(token.type == KEY)
1795 {
1796 RYML_ASSERT_VISIT_CB_(m_callbacks, token.value.begins_with('[') && token.value.ends_with(']'), this, r->closest);
1797 token.value = token.value.offs(1, 1).trim(' ');
1798 id_type idx;
1799 if( ! from_chars(token.value, &idx))
1800 {
1801 return NONE;
1802 }
1803 if( ! is_container(r->closest))
1804 {
1805 set_seq(r->closest);
1806 }
1807 RYML_ASSERT_VISIT_CB_(m_callbacks, is_container(r->closest), this, r->closest);
1808 node = child(r->closest, idx);
1809 if(node == NONE)
1810 {
1811 RYML_ASSERT_VISIT_CB_(m_callbacks, num_children(r->closest) <= idx, this, r->closest);
1812 for(id_type i = num_children(r->closest); i <= idx; ++i)
1813 {
1814 node = append_child(r->closest);
1815 if(i < idx)
1816 {
1817 if(is_map(r->closest))
1818 {
1819 _clear_type(node);
1820 set_key(node, {});
1821 set_val(node, {});
1822 }
1823 else
1824 {
1825 RYML_ASSERT_VISIT_CB_(m_callbacks, is_seq(r->closest), this, r->closest);
1826 _clear_type(node);
1827 set_val(node, {});
1828 }
1829 }
1830 }
1831 }
1832 }
1833 else
1834 {
1835 C4_NEVER_REACH();
1836 }
1837
1838 RYML_ASSERT_VISIT_CB_(m_callbacks, node != NONE, this, r->closest);
1839 *parent = token;
1840 return node;
1841}
1842
1843/* types of tokens:
1844 * - seeing "map." ---> "map"/MAP
1845 * - finishing "scalar" ---> "scalar"/KEYVAL
1846 * - seeing "seq[n]" ---> "seq"/SEQ (--> "[n]"/KEY)
1847 * - seeing "[n]" ---> "[n]"/KEY
1848 */
1849Tree::_lookup_path_token Tree::_next_token(lookup_result *r, _lookup_path_token const& parent) const
1850{
1851 csubstr unres = r->unresolved();
1852 if(unres.empty())
1853 return {}; // LCOV_EXCL_LINE
1854
1855 // is it an indexation like [0], [1], etc?
1856 if(unres.begins_with('['))
1857 {
1858 size_t pos = unres.find(']');
1859 if(pos == csubstr::npos)
1860 return {}; // LCOV_EXCL_LINE
1861 csubstr idx = unres.first(pos + 1);
1862 _advance(r, pos + 1);
1863 return {idx, KEY};
1864 }
1865
1866 // no. so it must be a name
1867 size_t pos = unres.first_of(".[");
1868 if(pos == csubstr::npos)
1869 {
1870 _advance(r, unres.len);
1871 NodeType t;
1872 if(( ! parent) || parent.type.is_seq())
1873 return {unres, VAL};
1874 return {unres, KEYVAL};
1875 }
1876
1877 // it's either a map or a seq
1878 RYML_ASSERT_VISIT_CB_(m_callbacks, unres[pos] == '.' || unres[pos] == '[', this, r->closest);
1879 if(unres[pos] == '.')
1880 {
1881 RYML_ASSERT_VISIT_CB_(m_callbacks, pos != 0, this, r->closest);
1882 _advance(r, pos + 1);
1883 return {unres.first(pos), MAP};
1884 }
1885
1886 RYML_ASSERT_VISIT_CB_(m_callbacks, unres[pos] == '[', this, r->closest);
1887 _advance(r, pos);
1888 return {unres.first(pos), SEQ};
1889}
1890
1891
1892} // namespace yml
1893} // namespace c4
1894
1895
1896//-----------------------------------------------------------------------------
1897//-----------------------------------------------------------------------------
1898//-----------------------------------------------------------------------------
1899
1900#ifndef C4_YML_EVENT_HANDLER_TREE_HPP_
1902#endif
1903#ifndef C4_YML_PARSE_ENGINE_DEF_HPP_
1905#endif
1906#ifndef C4_YML_PARSE_HPP_
1907#include "c4/yml/parse.hpp"
1908#endif
1909
1910namespace c4 {
1911namespace yml {
1912
1913Location Tree::location(Parser const& parser, id_type node) const
1914{
1915 // try hard to avoid getting the location from a null string.
1916 Location loc;
1917 if(_location_from_node(parser, node, &loc, 0))
1918 return loc;
1919 return parser.val_location(parser.source().str);
1920}
1921
1922bool Tree::_location_from_node(Parser const& parser, id_type node, Location *C4_RESTRICT loc, id_type level) const
1923{
1924 NodeType ty = type(node);
1925 if(ty.has_key())
1926 {
1927 csubstr k = key(node);
1928 if C4_LIKELY(k.str != nullptr)
1929 {
1930 RYML_ASSERT_BASIC_CB_(m_callbacks, k.is_sub(parser.source()));
1931 RYML_ASSERT_BASIC_CB_(m_callbacks, parser.source().is_super(k));
1932 *loc = parser.val_location(k.str);
1933 return true;
1934 }
1935 }
1936
1937 if(ty.has_val())
1938 {
1939 csubstr v = val(node);
1940 if C4_LIKELY(v.str != nullptr)
1941 {
1942 RYML_ASSERT_BASIC_CB_(m_callbacks, v.is_sub(parser.source()));
1943 RYML_ASSERT_BASIC_CB_(m_callbacks, parser.source().is_super(v));
1944 *loc = parser.val_location(v.str);
1945 return true;
1946 }
1947 }
1948
1949 if(ty.is_container())
1950 {
1951 if(_location_from_cont(parser, node, loc))
1952 return true;
1953 }
1954
1955 if(type(node) != NOTYPE && level == 0)
1956 {
1957 // try the prev sibling
1958 {
1959 const id_type prev = prev_sibling(node);
1960 if(prev != NONE)
1961 {
1962 if(_location_from_node(parser, prev, loc, level+1))
1963 return true;
1964 }
1965 }
1966 // try the next sibling
1967 {
1968 const id_type next = next_sibling(node);
1969 if(next != NONE)
1970 {
1971 if(_location_from_node(parser, next, loc, level+1))
1972 return true;
1973 }
1974 }
1975 // try the parent
1976 {
1977 const id_type parent = this->parent(node);
1978 if(parent != NONE)
1979 {
1980 if(_location_from_node(parser, parent, loc, level+1))
1981 return true;
1982 }
1983 }
1984 }
1985 return false;
1986}
1987
1988bool Tree::_location_from_cont(Parser const& parser, id_type node, Location *C4_RESTRICT loc) const
1989{
1990 RYML_ASSERT_BASIC_CB_(m_callbacks, type(node).is_container());
1991 if(!type(node).is_stream())
1992 {
1993 const char *node_start = _p(node)->m_val.scalar.str; // this was stored in the container
1994 if(has_children(node))
1995 {
1996 id_type child = first_child(node);
1997 if(type(child).has_key())
1998 {
1999 // when a map starts, the container was set after the key
2000 csubstr k = key(child);
2001 if(k.str && node_start > k.str)
2002 node_start = k.str;
2003 }
2004 }
2005 *loc = parser.val_location(node_start);
2006 return true;
2007 }
2008 else // it's a stream
2009 {
2010 *loc = parser.val_location(parser.source().str); // just return the front of the buffer
2011 }
2012 return true;
2013}
2014
2015} // namespace yml
2016} // namespace c4
2017
2018
2019// NOLINTEND(modernize-avoid-c-style-cast)
2020C4_SUPPRESS_WARNING_GCC_CLANG_POP
2021C4_SUPPRESS_WARNING_MSVC_POP
Holds a pointer to an existing tree, and a node id.
Definition node.hpp:737
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition node.hpp:1063
Location val_location(const char *val) const
Given a pointer to a buffer position, get the location.
csubstr source() const
Get the latest YAML buffer parsed by this object.
NodeData * m_buf
Definition tree.hpp:1676
void move(id_type node, id_type after)
change the node's position in the parent
Definition tree.cpp:817
NodeData * _p(id_type node)
An if-less form of get() that demands a valid node index. This function is implementation only; use a...
Definition tree.hpp:380
id_type duplicate(id_type node, id_type new_parent, id_type after)
recursively duplicate a node from this tree into a new parent, placing it after one of its children
Definition tree.cpp:960
void clear()
clear the tree and zero every node
Definition tree.cpp:330
id_type m_free_head
Definition tree.hpp:1680
lookup_result lookup_path(csubstr path, id_type start=NONE) const
for example foo.bar[0].baz
Definition tree.cpp:1608
csubstr const & key(id_type node) const
Definition tree.hpp:454
id_type lookup_path_or_modify(csubstr default_value, csubstr path, id_type start=NONE)
defaulted lookup: lookup path; if the lookup fails, recursively modify the tree so that the correspon...
Definition tree.cpp:1621
id_type first_child(id_type node) const
Definition tree.hpp:577
bool is_stream(id_type node) const
Definition tree.hpp:477
NodeType type(id_type node) const
Definition tree.hpp:452
void set_root_as_stream()
ensure the first node is a stream.
Definition tree.cpp:855
id_type root_id() const
Get the id of the root node. The tree must not be empty. The tree can be empty only when constructed ...
Definition tree.hpp:386
NodeRef rootref()
Get the root as a NodeRef . Note that a non-const Tree implicitly converts to NodeRef.
Definition tree.cpp:56
void resolve_tags(TagCache &cache, bool all=true)
Resolve tags in the tree such as "!!str" -> "<tag:yaml.org,2002:str>", "!foo" -> "<!...
Definition tree.cpp:1553
id_type prev_sibling(id_type node) const
Definition tree.hpp:571
void reserve_arena(size_t arena_cap=RYML_DEFAULT_TREE_ARENA_CAPACITY)
ensure the tree's internal string arena is at least the given capacity
Definition tree.hpp:1409
size_t m_arena_pos
Definition tree.hpp:1684
void set_val(id_type node, csubstr val) RYML_NOEXCEPT
Definition tree.hpp:688
bool is_map(id_type node) const
Definition tree.hpp:480
void clear_tag_directives()
Definition tree.cpp:1439
void reserve(id_type node_capacity=RYML_DEFAULT_TREE_CAPACITY)
Definition tree.cpp:292
csubstr const & val(id_type node) const
Definition tree.hpp:460
Tree & operator=(Tree const &that)
Definition tree.cpp:159
TagDirectives m_tag_directives
Definition tree.hpp:1688
bool is_root(id_type node) const
Definition tree.hpp:529
NodeRef ref(id_type node)
Get a NodeRef of a node by id.
Definition tree.cpp:70
id_type m_size
Definition tree.hpp:1678
bool has_key(id_type node) const
Definition tree.hpp:482
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.
Definition tree.cpp:1292
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
Definition tree.hpp:1327
id_type parent(id_type node) const
Definition tree.hpp:569
size_t resolve_tag(substr output, csubstr tag, id_type node_id) const
resolve the given tag, appearing at node_id.
Definition tree.cpp:1456
bool empty() const
Query for zero size.
Definition tree.hpp:343
void set_seq(id_type node) RYML_NOEXCEPT
Definition tree.hpp:720
id_type child_pos(id_type node, id_type ch) const
Definition tree.cpp:1224
id_type append_child(id_type parent)
create and insert a node as the last child of parent
Definition tree.hpp:1180
void clear_style(id_type node, bool recurse=false)
Definition tree.cpp:1404
bool has_sibling(id_type node, id_type sib) const
true if node has a sibling with id sib
Definition tree.hpp:547
id_type next_sibling(id_type node) const
Definition tree.hpp:572
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 chi...
Definition tree.cpp:1286
id_type num_tag_directives() const
Definition tree.cpp:1434
bool parent_is_seq(id_type node) const
Definition tree.hpp:495
ConstNodeRef crootref() const
Get the root as a ConstNodeRef . Note that Tree implicitly converts to ConstNodeRef.
Definition tree.cpp:65
id_type duplicate_children_no_rep(id_type node, id_type parent, id_type after)
duplicate the node's children (but not the node) in a new parent, but omit repetitions where a duplic...
Definition tree.cpp:1020
id_type m_cap
Definition tree.hpp:1677
id_type last_child(id_type node) const
Definition tree.hpp:578
id_type id(NodeData const *n) const
get the id of a node belonging to this tree. n can be nullptr, in which case NONE is returned n must ...
Definition tree.hpp:392
ConstNodeRef cdocref(id_type i) const
get the i-th document of the stream
Definition tree.cpp:112
void set_style_conditionally(id_type node, NodeType type_mask, NodeType rem_style_flags, NodeType add_style_flags, bool recurse=false)
Definition tree.cpp:1414
substr m_arena
Definition tree.hpp:1683
void normalize_tags()
Definition tree.cpp:1571
NodeRef docref(id_type i)
get the i-th document of the stream
Definition tree.cpp:104
void reorder()
reorder the tree in memory so that all the nodes are stored in a linear sequence when visited in dept...
Definition tree.cpp:573
void remove_children(id_type node)
remove all the node's children, but keep the node itself
Definition tree.cpp:917
bool is_ancestor(id_type node, id_type ancestor) const
true when ancestor is parent or parent of a parent of node
Definition tree.cpp:1304
Callbacks const & callbacks() const
Definition tree.hpp:349
size_t arena_capacity() const
get the current capacity of the tree's internal arena
Definition tree.hpp:1311
id_type doc(id_type i) const
gets the i document node index.
Definition tree.hpp:605
bool change_type(id_type node, NodeType type)
Definition tree.cpp:940
id_type m_free_tail
Definition tree.hpp:1681
void normalize_tags_long()
Definition tree.cpp:1578
void resolve(ReferenceResolver *rr, bool clear_anchors=true)
Resolve references (aliases <- anchors), by forwarding to ReferenceResolver::resolve(); refer to Refe...
Definition tree.cpp:1206
bool is_seq(id_type node) const
Definition tree.hpp:481
bool parent_is_map(id_type node) const
Definition tree.hpp:496
void set_map(id_type node) RYML_NOEXCEPT
Definition tree.hpp:731
void remove(id_type node)
remove an entire branch at once: ie remove the children and the node itself
Definition tree.hpp:1202
id_type find_child(id_type node, csubstr const &key) const
find child by name, or NONE if no child is found with this key like Tree::child(),...
Definition tree.cpp:1249
Location location(Parser const &p, id_type node) const
Get the location of a node from the parse used to parse this tree.
Definition tree.cpp:1913
void duplicate_contents(id_type node, id_type where)
Definition tree.cpp:1005
NodeData * get(id_type node)
get a pointer to a node's NodeData. node can be NONE, in which case a nullptr is returned
Definition tree.hpp:361
void add_tag_directive(csubstr handle, csubstr prefix, id_type id)
Definition tree.cpp:1444
~Tree() noexcept
Definition tree.cpp:141
id_type duplicate_children(id_type node, id_type parent, id_type after)
recursively duplicate the node's children (but not the node)
Definition tree.cpp:983
void set_key(id_type node, csubstr key) RYML_NOEXCEPT
Definition tree.hpp:705
id_type num_children(id_type node) const
O(num_children).
Definition tree.cpp:1216
csubstr arena() const
get the current arena
Definition tree.hpp:1317
void merge_with(Tree const *src, id_type src_node=NONE, id_type dst_root=NONE)
Definition tree.cpp:1121
bool is_container(id_type node) const
Definition tree.hpp:479
id_type child(id_type node, id_type pos) const
find child by position, or NONE if there are less than pos children posi
Definition tree.cpp:1237
bool has_child(id_type node, id_type ch) const
true if node has a child with id ch
Definition tree.hpp:540
ConstNodeRef cref(id_type node) const
Get a NodeRef of a node by id.
Definition tree.cpp:80
Callbacks m_callbacks
Definition tree.hpp:1686
bool has_children(id_type node) const
true if node has any children
Definition tree.hpp:544
#define RYML_DEFAULT_TREE_CAPACITY
default capacity for the tree when not set explicitly
Definition common.hpp:21
#define RYML_DEFAULT_TREE_ARENA_CAPACITY
default capacity for the tree's arena when not set explicitly
Definition common.hpp:26
bool atox(csubstr s, uint8_t *v) noexcept
bool from_chars(csubstr buf, uint8_t *v) noexcept
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition node_type.hpp:26
@ NOTYPE
no node type or style is set
Definition node_type.hpp:32
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition node_type.hpp:35
@ STREAM
a stream: a seq of docs
Definition node_type.hpp:38
@ KEY
the scalar to the left of : in a map's member
Definition node_type.hpp:33
@ VAL_STYLE
mask of VALQUO|VAL_PLAIN : all the val scalar styles for val (not container styles!...
@ KEYTAG
the key has a tag
Definition node_type.hpp:43
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
Definition node_type.hpp:34
@ VALTAG
the val has a tag
Definition node_type.hpp:44
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
Definition node_type.hpp:36
@ KEY_STYLE
mask of KEYQUO|KEY_PLAIN : all the key scalar styles for key (not container styles!...
@ STYLE
mask of SCALAR_STYLE | CONTAINER_STYLE : all style flags
@ KEYVAL
mask of KEY|VAL
@ CONTAINER_STYLE
mask of CONTAINER_STYLE_FLOW|CONTAINER_STYLE_BLOCK : all container style flags
@ DOC
a document
Definition node_type.hpp:37
ParseEngine< EventHandlerTree > Parser
This is the main ryml parser, where the parser events are handled to create a ryml tree (see Event Ha...
Definition fwd.hpp:19
csubstr serialize_to_arena_str(Tree *tree, csubstr scalar)
Serialize a string type (as specified by c4::is_string) to a tree's arena, ensuring that there is an ...
Definition tree.cpp:27
csubstr serialize_to_arena_scalar(Tree *tree, T const &scalar)
Serialize a scalar to the tree's arena.
Definition tree.hpp:1754
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2355
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2356
bool is_valid_tag_handle(csubstr handle)
Definition tag.cpp:210
bool is_custom_tag(csubstr tag)
is a tag of the form !handle!tag?
Definition tag.cpp:9
csubstr normalize_tag_long(csubstr tag)
Definition tag.cpp:31
csubstr normalize_tag(csubstr tag)
Definition tag.cpp: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:124
@ NONE
an index to none
Definition common.hpp:131
Node classes.
bool begins_with(const C c) const noexcept
true if the first character of the string is c
Definition substr.hpp:850
size_t len
the length of the substring
Definition substr.hpp:218
bool ends_with(const C c) const noexcept
true if the last character of the string is c
Definition substr.hpp:894
size_t first_of(const C c, size_t start=0) const
Definition substr.hpp:934
basic_substring first(size_t num) const noexcept
return the first num elements: [0,num[
Definition substr.hpp:529
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition substr.hpp:502
bool empty() const noexcept
Definition substr.hpp:356
bool is_super(ro_substr const that) const noexcept
true if that is a substring of *this (ie, from the same buffer)
Definition substr.hpp:484
C * str
a restricted pointer to the first character of the substring
Definition substr.hpp:216
bool is_sub(ro_substr const that) const noexcept
true if *this is a substring of that (ie, from the same buffer)
Definition substr.hpp:478
A c-style callbacks class to customize behavior on errors or allocation.
Definition common.hpp:374
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition common.hpp:229
contains the data for each YAML node.
Definition tree.hpp:288
NodeType m_type
Definition tree.hpp:289
id_type m_next_sibling
Definition tree.hpp:297
id_type m_parent
Definition tree.hpp:294
NodeScalar m_key
Definition tree.hpp:291
id_type m_prev_sibling
Definition tree.hpp:298
id_type m_first_child
Definition tree.hpp:295
NodeScalar m_val
Definition tree.hpp:292
Wraps a type_bits mask of NodeTypeBits flags with some syntactic sugar and predicates.
bool has_key() const noexcept
bool is_seq() const noexcept
void rem(type_bits t) noexcept
bool has_val() const noexcept
bool is_map() const noexcept
bool is_container() const noexcept
void add(type_bits t) noexcept
bool is_keyval() const noexcept
bool is_val() const noexcept
bool is_stream() const noexcept
Reusable object to resolve references/aliases in a Tree.
Accelerator structure to reduce memory requirements by enabling reuse of resolved tags.
Definition tag.hpp:71
LookupResult find(csubstr tag, id_type doc_id, id_type linear_threshold=Entries::sso_size) const noexcept
Definition tag.cpp:539
void add(csubstr tag, csubstr resolved, id_type doc_id, const_iterator pos) RYML_NOEXCEPT
Definition tag.cpp:596
csubstr unresolved() const
get the part ot the input path that was unresolved
Definition tree.cpp:1596
csubstr resolved() const
get the part ot the input path that was resolved
Definition tree.cpp:1588