• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

biojppm / rapidyaml / 18640477834

20 Oct 2025 02:44AM UTC coverage: 97.759% (+0.1%) from 97.65%
18640477834

Pull #550

github

web-flow
Merge 5a981cd8d into 48acea949
Pull Request #550: Implement FLOW_ML style

823 of 856 new or added lines in 12 files covered. (96.14%)

160 existing lines in 15 files now uncovered.

13830 of 14147 relevant lines covered (97.76%)

548857.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.32
/src/c4/yml/reference_resolver.cpp
1
#include "c4/yml/reference_resolver.hpp"
2
#include "c4/yml/common.hpp"
3
#include "c4/yml/detail/dbgprint.hpp"
4
#ifdef RYML_DBG
5
#include "c4/yml/detail/print.hpp"
6
#else
7
#define _c4dbg_tree(...)
8
#define _c4dbg_node(...)
9
#endif
10

11
namespace c4 {
12
namespace yml {
13

14
/** @cond dev */
15

16
id_type ReferenceResolver::count_anchors_and_refs_(id_type n)
7,626✔
17
{
18
    id_type c = 0;
7,626✔
19
    c += m_tree->has_key_anchor(n);
7,626✔
20
    c += m_tree->has_val_anchor(n);
7,626✔
21
    c += m_tree->is_key_ref(n);
7,626✔
22
    c += m_tree->is_val_ref(n);
7,626✔
23
    c += m_tree->has_key(n) && m_tree->key(n) == "<<";
21,210✔
24
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
14,646✔
25
        c += count_anchors_and_refs_(ch);
7,020✔
26
    return c;
7,626✔
27
}
28

29
void ReferenceResolver::gather_anchors_and_refs__(id_type n)
4,548✔
30
{
31
    // insert key refs BEFORE inserting val refs
32
    if(m_tree->has_key(n))
9,096✔
33
    {
34
        if(!m_tree->is_key_quoted(n) && m_tree->key(n) == "<<")
10,422✔
35
        {
36
            _c4dbgpf("node[{}]: key is <<", n);
92✔
37
            if(m_tree->has_val(n))
552✔
38
            {
39
                if(m_tree->is_val_ref(n))
492✔
40
                {
41
                    _c4dbgpf("node[{}]: instance[{}]: val ref, inheriting! '{}'", n, m_refs.size(), m_tree->val_ref(n));
74✔
42
                    m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
222✔
43
                    //m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
44
                }
45
                else
46
                {
47
                    _c4dbgpf("node[{}]: not ref!", n);
8✔
48
                }
49
            }
50
            else if(m_tree->is_seq(n))
60✔
51
            {
52
                // for merging multiple inheritance targets
53
                //   <<: [ *CENTER, *BIG ]
54
                _c4dbgpf("node[{}]: is seq!", n);
10✔
55
                for(id_type ich = m_tree->first_child(n); ich != NONE; ich = m_tree->next_sibling(ich))
108✔
56
                {
57
                    _c4dbgpf("node[{}]: instance [{}]: val ref, inheriting multiple: {} '{}'", n, m_refs.size(), ich, m_tree->val_ref(ich));
26✔
58
                    if(C4_UNLIKELY(m_tree->is_container(ich)))
156✔
UNCOV
59
                        _RYML_ERR_VISIT_(m_tree->m_callbacks, m_tree, n, "child={}: refs for << cannot be containers.", ich);
×
60
                    m_refs.push({VALREF, ich, NONE, NONE, n, m_tree->next_sibling(n)});
78✔
61
                }
62
                return; // don't descend into the seq
30✔
63
            }
64
            else
65
            {
UNCOV
66
                _RYML_ERR_VISIT_(m_tree->m_callbacks, m_tree, n, "refs for << must be either val or seq");
×
67
            }
68
        }
69
        else if(m_tree->is_key_ref(n))
6,432✔
70
        {
71
            _c4dbgpf("node[{}]: instance[{}]: key ref: '{}', key='{}'", n, m_refs.size(), m_tree->key_ref(n), m_tree->has_key(n) ? m_tree->key(n) : csubstr{"-"});
192✔
72
            _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->key(n) != "<<", m_tree, n);
576✔
73
            _RYML_CHECK_VISIT_(m_tree->m_callbacks, (!m_tree->has_key(n)) || m_tree->key(n).ends_with(m_tree->key_ref(n)), m_tree, n);
576✔
74
            m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
288✔
75
        }
76
    }
77
    // val ref
78
    if(m_tree->is_val_ref(n) && (!m_tree->has_key(n) || m_tree->key(n) != "<<"))
10,308✔
79
    {
80
        _c4dbgpf("node[{}]: instance[{}]: val ref: '{}'", n, m_refs.size(), m_tree->val_ref(n));
160✔
81
        _RYML_CHECK_VISIT((!m_tree->has_val(n)) || m_tree->val(n).ends_with(m_tree->val_ref(n)), m_tree, n);
960✔
82
        m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
480✔
83
    }
84
    // anchors
85
    if(m_tree->has_key_anchor(n))
9,036✔
86
    {
87
        _c4dbgpf("node[{}]: instance[{}]: key anchor: '{}'", n, m_refs.size(), m_tree->key_anchor(n));
100✔
88
        _RYML_CHECK_VISIT(m_tree->has_key(n), m_tree, n);
600✔
89
        m_refs.push({KEYANCH, n, NONE, NONE, NONE, NONE});
300✔
90
    }
91
    if(m_tree->has_val_anchor(n))
9,036✔
92
    {
93
        _c4dbgpf("node[{}]: instance[{}]: val anchor: '{}'", n, m_refs.size(), m_tree->val_anchor(n));
294✔
94
        _RYML_CHECK_VISIT(m_tree->has_val(n) || m_tree->is_container(n), m_tree, n);
2,382✔
95
        m_refs.push({VALANCH, n, NONE, NONE, NONE, NONE});
882✔
96
    }
97
    // recurse
98
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
8,688✔
99
        gather_anchors_and_refs__(ch);
4,170✔
100
}
101

102
void ReferenceResolver::gather_anchors_and_refs_()
606✔
103
{
104
    _c4dbgp("gathering anchors and refs...");
202✔
105

106
    // minimize (re-)allocations by counting first
107
    id_type num_anchors_and_refs = count_anchors_and_refs_(m_tree->root_id());
606✔
108
    if(!num_anchors_and_refs)
606✔
109
        return;
228✔
110
    m_refs.reserve(num_anchors_and_refs);
378✔
111
    m_refs.clear();
378✔
112

113
    // now descend through the hierarchy
114
    gather_anchors_and_refs__(m_tree->root_id());
378✔
115

116
    _c4dbgpf("found {} anchors/refs", m_refs.size());
126✔
117

118
    // finally connect the reference list
119
    id_type prev_anchor = NONE;
378✔
120
    id_type count = 0;
378✔
121
    for(auto &rd : m_refs)
2,628✔
122
    {
123
        rd.prev_anchor = prev_anchor;
2,250✔
124
        if(rd.type.has_anchor())
4,500✔
125
            prev_anchor = count;
1,182✔
126
        ++count;
2,250✔
127
    }
128
    _c4dbgp("gathering anchors and refs: finished");
126✔
129
}
130

131
id_type ReferenceResolver::lookup_(RefData const* C4_RESTRICT ra)
1,068✔
132
{
133
    #ifdef RYML_DBG
134
    id_type instance = static_cast<id_type>(ra-m_refs.m_stack);
356✔
135
    id_type node = ra->node;
356✔
136
    #endif
137
    _RYML_ASSERT_VISIT_(m_tree->m_callbacks, ra->type.is_key_ref() || ra->type.is_val_ref(), m_tree, ra->node);
2,916✔
138
    _RYML_ASSERT_VISIT_(m_tree->m_callbacks, ra->type.is_key_ref() != ra->type.is_val_ref(), m_tree, ra->node);
3,204✔
139
    csubstr refname;
1,068✔
140
    _c4dbgpf("instance[{}:node{}]: lookup from node={}...", instance, node, ra->node);
356✔
141
    if(ra->type.is_val_ref())
2,136✔
142
    {
143
        refname = m_tree->val_ref(ra->node);
780✔
144
        _c4dbgpf("instance[{}:node{}]: valref: '{}'", instance, node, refname);
260✔
145
    }
146
    else
147
    {
148
        _RYML_ASSERT_VISIT_(m_tree->m_callbacks, ra->type.is_key_ref(), m_tree, ra->node);
576✔
149
        refname = m_tree->key_ref(ra->node);
288✔
150
        _c4dbgpf("instance[{}:node{}]: keyref: '{}'", instance, node, refname);
96✔
151
    }
152
    while(ra->prev_anchor != NONE)
2,304✔
153
    {
154
        ra = &m_refs[ra->prev_anchor];
4,536✔
155
        _c4dbgpf("instance[{}:node{}]: lookup '{}' at [{}:node{}]: keyref='{}' valref='{}'", instance, node, refname, ra-m_refs.m_stack, ra->node,
2,910✔
156
                 (m_tree->has_key_anchor(ra->node) ? m_tree->key_anchor(ra->node) : csubstr("~")),
157
                 (m_tree->has_val_anchor(ra->node) ? m_tree->val_anchor(ra->node) : csubstr("~")));
158
        if(m_tree->has_anchor(ra->node, refname))
4,536✔
159
        {
160
            _c4dbgpf("instance[{}:node{}]: got it at [{}:node{}]!", instance, node, ra-m_refs.m_stack, ra->node);
344✔
161
            return ra->node;
1,032✔
162
        }
163
    }
164
    _RYML_ERR_VISIT_(m_tree->m_callbacks, m_tree, ra->node, "anchor not found: '{}'", refname);
36✔
165
    C4_UNREACHABLE_AFTER_ERR();
166
}
167

168
void ReferenceResolver::reset_(Tree *t_)
336✔
169
{
170
    if(t_->callbacks() != m_refs.m_callbacks)
336✔
171
    {
UNCOV
172
        m_refs.m_callbacks = t_->callbacks();
×
173
    }
174
    m_tree = t_;
336✔
175
    m_refs.clear();
336✔
176
}
336✔
177

178
void ReferenceResolver::resolve_()
330✔
179
{
180
    /* from the specs: "an alias node refers to the most recent
181
     * node in the serialization having the specified anchor". So
182
     * we need to start looking upward from ref nodes.
183
     *
184
     * @see http://yaml.org/spec/1.2/spec.html#id2765878 */
185
    _c4dbgp("matching anchors/refs...");
110✔
186
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
2,346✔
187
    {
188
        RefData &C4_RESTRICT refdata = m_refs.top(i);
2,052✔
189
        if( ! refdata.type.is_ref())
4,104✔
190
            continue;
984✔
191
        refdata.target = lookup_(&refdata);
1,068✔
192
    }
193
    _c4dbgp("matching anchors/refs: finished");
98✔
194

195
    // insert the resolved references
196
    _c4dbgp("modifying tree...");
98✔
197
    id_type prev_parent_ref = NONE;
294✔
198
    id_type prev_parent_ref_after = NONE;
294✔
199
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
2,298✔
200
    {
201
        RefData const& C4_RESTRICT refdata = m_refs[i];
2,016✔
202
        _c4dbgpf("instance[{}:node{}]: {}/{}...", i, refdata.node, i+1, e);
672✔
203
        if( ! refdata.type.is_ref())
4,032✔
204
            continue;
984✔
205
        _c4dbgpf("instance[{}:node{}]: is reference!", i, refdata.node);
344✔
206
        if(refdata.parent_ref != NONE)
1,032✔
207
        {
208
            _c4dbgpf("instance[{}:node{}] has parent: {}", i, refdata.node, refdata.parent_ref);
26✔
209
            _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->is_seq(refdata.parent_ref), m_tree, refdata.node);
156✔
210
            const id_type p = m_tree->parent(refdata.parent_ref);
78✔
211
            const id_type after = (prev_parent_ref != refdata.parent_ref) ?
78✔
212
                refdata.parent_ref//prev_sibling(rd.parent_ref_sibling)
213
                :
214
                prev_parent_ref_after;
48✔
215
            prev_parent_ref = refdata.parent_ref;
78✔
216
            prev_parent_ref_after = m_tree->duplicate_children_no_rep(refdata.target, p, after);
78✔
217
            m_tree->remove(refdata.node);
78✔
218
        }
219
        else
220
        {
221
            _c4dbgpf("instance[{}:node{}] has no parent", i, refdata.node, refdata.parent_ref);
318✔
222
            if(m_tree->has_key(refdata.node) && m_tree->key(refdata.node) == "<<")
2,754✔
223
            {
224
                _c4dbgpf("instance[{}:node{}] is inheriting", i, refdata.node);
74✔
225
                _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->is_keyval(refdata.node), m_tree, refdata.node);
444✔
226
                const id_type p = m_tree->parent(refdata.node);
222✔
227
                const id_type after = m_tree->prev_sibling(refdata.node);
222✔
228
                _c4dbgpf("instance[{}:node{}] p={} after={}", i, refdata.node, p, after);
74✔
229
                m_tree->duplicate_children_no_rep(refdata.target, p, after);
222✔
230
                m_tree->remove(refdata.node);
210✔
231
            }
232
            else if(refdata.type.is_key_ref())
1,464✔
233
            {
234
                _c4dbgpf("instance[{}:node{}] is key ref", i, refdata.node);
96✔
235
                _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->is_key_ref(refdata.node), m_tree, refdata.node);
576✔
236
                _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->has_key_anchor(refdata.target) || m_tree->has_val_anchor(refdata.target), m_tree, refdata.node);
642✔
237
                if(m_tree->has_val_anchor(refdata.target) && m_tree->val_anchor(refdata.target) == m_tree->key_ref(refdata.node))
726✔
238
                {
239
                    _c4dbgpf("instance[{}:node{}] target.anchor==val.anchor=={}", i, refdata.node, m_tree->val_anchor(refdata.target));
50✔
240
                    _RYML_CHECK_VISIT_(m_tree->m_callbacks, !m_tree->is_container(refdata.target), m_tree, refdata.target);
300✔
241
                    _RYML_CHECK_VISIT_(m_tree->m_callbacks, m_tree->has_val(refdata.target), m_tree, refdata.target);
300✔
242
                    const type_bits existing_style_flags = VAL_STYLE & m_tree->_p(refdata.target)->m_type.type;
150✔
243
                    static_assert((VAL_STYLE >> 1u) == (KEY_STYLE), "bad flags");
244
                    m_tree->_p(refdata.node)->m_key.scalar = m_tree->val(refdata.target);
150✔
245
                    m_tree->_add_flags(refdata.node, KEY | (existing_style_flags >> 1u));
150✔
246
                }
247
                else
248
                {
249
                    _c4dbgpf("instance[{}:node{}] don't inherit container flags", i, refdata.node);
46✔
250
                    _RYML_CHECK_BASIC_(m_tree->m_callbacks, m_tree->key_anchor(refdata.target) == m_tree->key_ref(refdata.node));
276✔
251
                    m_tree->_p(refdata.node)->m_key.scalar = m_tree->key(refdata.target);
138✔
252
                    // keys cannot be containers, so don't inherit container flags
253
                    const type_bits existing_style_flags = KEY_STYLE & m_tree->_p(refdata.target)->m_type.type;
138✔
254
                    m_tree->_add_flags(refdata.node, KEY | existing_style_flags);
138✔
255
                }
256
            }
257
            else // val ref
258
            {
259
                _c4dbgpf("instance[{}:node{}] is val ref", i, refdata.node);
148✔
260
                _RYML_ASSERT_VISIT_(m_tree->m_callbacks, refdata.type.is_val_ref(), m_tree, refdata.node);
888✔
261
                if(m_tree->has_key_anchor(refdata.target) && m_tree->key_anchor(refdata.target) == m_tree->val_ref(refdata.node))
1,026✔
262
                {
263
                    _c4dbgpf("instance[{}:node{}] target.anchor==key.anchor=={}", i, refdata.node, m_tree->key_anchor(refdata.target));
42✔
264
                    _RYML_CHECK_BASIC_(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
252✔
265
                    _RYML_CHECK_BASIC_(m_tree->m_callbacks, m_tree->has_val(refdata.target));
252✔
266
                    // keys cannot be containers, so don't inherit container flags
267
                    const type_bits existing_style_flags = (KEY_STYLE) & m_tree->_p(refdata.target)->m_type.type;
126✔
268
                    static_assert((KEY_STYLE << 1u) == (VAL_STYLE), "bad flags");
269
                    m_tree->_p(refdata.node)->m_val.scalar = m_tree->key(refdata.target);
126✔
270
                    m_tree->_add_flags(refdata.node, VAL | (existing_style_flags << 1u));
126✔
271
                }
272
                else
273
                {
274
                    _c4dbgpf("instance[{}:node{}] duplicate contents", i, refdata.node);
106✔
275
                    m_tree->duplicate_contents(refdata.target, refdata.node);
318✔
276
                }
277
            }
278
        }
279
        _c4dbg_tree("after insertion", *m_tree);
340✔
280
    }
281
}
282✔
282

283
void ReferenceResolver::resolve(Tree *t_, bool clear_anchors)
336✔
284
{
285
    _c4dbgp("resolving references...");
112✔
286

287
    reset_(t_);
336✔
288

289
    _c4dbg_tree("unresolved tree", *m_tree);
112✔
290

291
    gather_anchors_and_refs_();
336✔
292
    if(m_refs.empty())
336✔
293
        return;
6✔
294
    resolve_();
330✔
295
    _c4dbg_tree("resolved tree", *m_tree);
94✔
296

297
    // clear anchors and refs
298
    if(clear_anchors)
282✔
299
    {
300
        _c4dbgp("clearing anchors/refs");
90✔
301
        auto clear_ = [this]{
540✔
302
            for(auto const& C4_RESTRICT ar : m_refs)
4,038✔
303
            {
304
                m_tree->rem_anchor_ref(ar.node);
3,498✔
305
                if(ar.parent_ref != NONE)
3,498✔
306
                    if(m_tree->type(ar.parent_ref) != NOTYPE)
312✔
307
                        m_tree->remove(ar.parent_ref);
30✔
308
            }
309
        };
585✔
310
        clear_();
270✔
311
        // some of the elements injected during the resolution may
312
        // have nested anchors; these anchors will have been newly
313
        // injected during the resolution; collect again, and clear
314
        // again, to ensure those are also cleared:
315
        gather_anchors_and_refs_();
270✔
316
        clear_();
270✔
317
        _c4dbgp("clearing anchors/refs: finished");
90✔
318
    }
319

320
    _c4dbg_tree("final resolved tree", *m_tree);
94✔
321

322
    m_tree = nullptr;
282✔
323
    _c4dbgp("resolving references: finished");
94✔
324
}
325

326
/** @endcond */
327

328
} // namespace ryml
329
} // namespace c4
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc