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

biojppm / rapidyaml / 18640334796

20 Oct 2025 02:34AM UTC coverage: 97.752% (+0.1%) from 97.65%
18640334796

Pull #550

github

web-flow
Merge 138b7f86b 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.

13784 of 14101 relevant lines covered (97.75%)

543110.94 hits per line

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

97.81
/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)
5,084✔
17
{
18
    id_type c = 0;
5,084✔
19
    c += m_tree->has_key_anchor(n);
5,084✔
20
    c += m_tree->has_val_anchor(n);
5,084✔
21
    c += m_tree->is_key_ref(n);
5,084✔
22
    c += m_tree->is_val_ref(n);
5,084✔
23
    c += m_tree->has_key(n) && m_tree->key(n) == "<<";
14,140✔
24
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
9,764✔
25
        c += count_anchors_and_refs_(ch);
4,680✔
26
    return c;
5,084✔
27
}
28

29
void ReferenceResolver::gather_anchors_and_refs__(id_type n)
3,032✔
30
{
31
    // insert key refs BEFORE inserting val refs
32
    if(m_tree->has_key(n))
6,064✔
33
    {
34
        if(!m_tree->is_key_quoted(n) && m_tree->key(n) == "<<")
6,948✔
35
        {
36
            _c4dbgpf("node[{}]: key is <<", n);
37
            if(m_tree->has_val(n))
368✔
38
            {
39
                if(m_tree->is_val_ref(n))
328✔
40
                {
41
                    _c4dbgpf("node[{}]: instance[{}]: val ref, inheriting! '{}'", n, m_refs.size(), m_tree->val_ref(n));
42
                    m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
148✔
43
                    //m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
44
                }
45
                else
46
                {
47
                    _c4dbgpf("node[{}]: not ref!", n);
48
                }
49
            }
50
            else if(m_tree->is_seq(n))
40✔
51
            {
52
                // for merging multiple inheritance targets
53
                //   <<: [ *CENTER, *BIG ]
54
                _c4dbgpf("node[{}]: is seq!", n);
55
                for(id_type ich = m_tree->first_child(n); ich != NONE; ich = m_tree->next_sibling(ich))
72✔
56
                {
57
                    _c4dbgpf("node[{}]: instance [{}]: val ref, inheriting multiple: {} '{}'", n, m_refs.size(), ich, m_tree->val_ref(ich));
58
                    if(C4_UNLIKELY(m_tree->is_container(ich)))
104✔
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)});
52✔
61
                }
62
                return; // don't descend into the seq
20✔
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))
4,288✔
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{"-"});
72
            _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->key(n) != "<<", m_tree, n);
384✔
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);
384✔
74
            m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
192✔
75
        }
76
    }
77
    // val ref
78
    if(m_tree->is_val_ref(n) && (!m_tree->has_key(n) || m_tree->key(n) != "<<"))
6,872✔
79
    {
80
        _c4dbgpf("node[{}]: instance[{}]: val ref: '{}'", n, m_refs.size(), m_tree->val_ref(n));
81
        _RYML_CHECK_VISIT((!m_tree->has_val(n)) || m_tree->val(n).ends_with(m_tree->val_ref(n)), m_tree, n);
640✔
82
        m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
320✔
83
    }
84
    // anchors
85
    if(m_tree->has_key_anchor(n))
6,024✔
86
    {
87
        _c4dbgpf("node[{}]: instance[{}]: key anchor: '{}'", n, m_refs.size(), m_tree->key_anchor(n));
88
        _RYML_CHECK_VISIT(m_tree->has_key(n), m_tree, n);
400✔
89
        m_refs.push({KEYANCH, n, NONE, NONE, NONE, NONE});
200✔
90
    }
91
    if(m_tree->has_val_anchor(n))
6,024✔
92
    {
93
        _c4dbgpf("node[{}]: instance[{}]: val anchor: '{}'", n, m_refs.size(), m_tree->val_anchor(n));
94
        _RYML_CHECK_VISIT(m_tree->has_val(n) || m_tree->is_container(n), m_tree, n);
1,588✔
95
        m_refs.push({VALANCH, n, NONE, NONE, NONE, NONE});
588✔
96
    }
97
    // recurse
98
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
5,792✔
99
        gather_anchors_and_refs__(ch);
2,780✔
100
}
101

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

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

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

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

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

131
id_type ReferenceResolver::lookup_(RefData const* C4_RESTRICT ra)
712✔
132
{
133
    #ifdef RYML_DBG
134
    id_type instance = static_cast<id_type>(ra-m_refs.m_stack);
135
    id_type node = ra->node;
136
    #endif
137
    _RYML_ASSERT_VISIT_(m_tree->m_callbacks, ra->type.is_key_ref() || ra->type.is_val_ref(), m_tree, ra->node);
1,944✔
138
    _RYML_ASSERT_VISIT_(m_tree->m_callbacks, ra->type.is_key_ref() != ra->type.is_val_ref(), m_tree, ra->node);
2,136✔
139
    csubstr refname;
712✔
140
    _c4dbgpf("instance[{}:node{}]: lookup from node={}...", instance, node, ra->node);
141
    if(ra->type.is_val_ref())
1,424✔
142
    {
143
        refname = m_tree->val_ref(ra->node);
520✔
144
        _c4dbgpf("instance[{}:node{}]: valref: '{}'", instance, node, refname);
145
    }
146
    else
147
    {
148
        _RYML_ASSERT_VISIT_(m_tree->m_callbacks, ra->type.is_key_ref(), m_tree, ra->node);
384✔
149
        refname = m_tree->key_ref(ra->node);
192✔
150
        _c4dbgpf("instance[{}:node{}]: keyref: '{}'", instance, node, refname);
151
    }
152
    while(ra->prev_anchor != NONE)
1,536✔
153
    {
154
        ra = &m_refs[ra->prev_anchor];
3,024✔
155
        _c4dbgpf("instance[{}:node{}]: lookup '{}' at [{}:node{}]: keyref='{}' valref='{}'", instance, node, refname, ra-m_refs.m_stack, ra->node,
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))
3,024✔
159
        {
160
            _c4dbgpf("instance[{}:node{}]: got it at [{}:node{}]!", instance, node, ra-m_refs.m_stack, ra->node);
161
            return ra->node;
688✔
162
        }
163
    }
164
    _RYML_ERR_VISIT_(m_tree->m_callbacks, m_tree, ra->node, "anchor not found: '{}'", refname);
24✔
165
    C4_UNREACHABLE_AFTER_ERR();
166
}
167

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

178
void ReferenceResolver::resolve_()
220✔
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...");
186
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
1,564✔
187
    {
188
        RefData &C4_RESTRICT refdata = m_refs.top(i);
1,368✔
189
        if( ! refdata.type.is_ref())
2,736✔
190
            continue;
656✔
191
        refdata.target = lookup_(&refdata);
712✔
192
    }
193
    _c4dbgp("matching anchors/refs: finished");
194

195
    // insert the resolved references
196
    _c4dbgp("modifying tree...");
197
    id_type prev_parent_ref = NONE;
196✔
198
    id_type prev_parent_ref_after = NONE;
196✔
199
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
1,532✔
200
    {
201
        RefData const& C4_RESTRICT refdata = m_refs[i];
1,344✔
202
        _c4dbgpf("instance[{}:node{}]: {}/{}...", i, refdata.node, i+1, e);
203
        if( ! refdata.type.is_ref())
2,688✔
204
            continue;
656✔
205
        _c4dbgpf("instance[{}:node{}]: is reference!", i, refdata.node);
206
        if(refdata.parent_ref != NONE)
688✔
207
        {
208
            _c4dbgpf("instance[{}:node{}] has parent: {}", i, refdata.node, refdata.parent_ref);
209
            _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->is_seq(refdata.parent_ref), m_tree, refdata.node);
104✔
210
            const id_type p = m_tree->parent(refdata.parent_ref);
52✔
211
            const id_type after = (prev_parent_ref != refdata.parent_ref) ?
52✔
212
                refdata.parent_ref//prev_sibling(rd.parent_ref_sibling)
213
                :
214
                prev_parent_ref_after;
32✔
215
            prev_parent_ref = refdata.parent_ref;
52✔
216
            prev_parent_ref_after = m_tree->duplicate_children_no_rep(refdata.target, p, after);
52✔
217
            m_tree->remove(refdata.node);
52✔
218
        }
219
        else
220
        {
221
            _c4dbgpf("instance[{}:node{}] has no parent", i, refdata.node, refdata.parent_ref);
222
            if(m_tree->has_key(refdata.node) && m_tree->key(refdata.node) == "<<")
1,836✔
223
            {
224
                _c4dbgpf("instance[{}:node{}] is inheriting", i, refdata.node);
225
                _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->is_keyval(refdata.node), m_tree, refdata.node);
296✔
226
                const id_type p = m_tree->parent(refdata.node);
148✔
227
                const id_type after = m_tree->prev_sibling(refdata.node);
148✔
228
                _c4dbgpf("instance[{}:node{}] p={} after={}", i, refdata.node, p, after);
229
                m_tree->duplicate_children_no_rep(refdata.target, p, after);
148✔
230
                m_tree->remove(refdata.node);
140✔
231
            }
232
            else if(refdata.type.is_key_ref())
976✔
233
            {
234
                _c4dbgpf("instance[{}:node{}] is key ref", i, refdata.node);
235
                _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_tree->is_key_ref(refdata.node), m_tree, refdata.node);
384✔
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);
428✔
237
                if(m_tree->has_val_anchor(refdata.target) && m_tree->val_anchor(refdata.target) == m_tree->key_ref(refdata.node))
484✔
238
                {
239
                    _c4dbgpf("instance[{}:node{}] target.anchor==val.anchor=={}", i, refdata.node, m_tree->val_anchor(refdata.target));
240
                    _RYML_CHECK_VISIT_(m_tree->m_callbacks, !m_tree->is_container(refdata.target), m_tree, refdata.target);
200✔
241
                    _RYML_CHECK_VISIT_(m_tree->m_callbacks, m_tree->has_val(refdata.target), m_tree, refdata.target);
200✔
242
                    const type_bits existing_style_flags = VAL_STYLE & m_tree->_p(refdata.target)->m_type.type;
100✔
243
                    static_assert((VAL_STYLE >> 1u) == (KEY_STYLE), "bad flags");
244
                    m_tree->_p(refdata.node)->m_key.scalar = m_tree->val(refdata.target);
100✔
245
                    m_tree->_add_flags(refdata.node, KEY | (existing_style_flags >> 1u));
100✔
246
                }
247
                else
248
                {
249
                    _c4dbgpf("instance[{}:node{}] don't inherit container flags", i, refdata.node);
250
                    _RYML_CHECK_BASIC_(m_tree->m_callbacks, m_tree->key_anchor(refdata.target) == m_tree->key_ref(refdata.node));
184✔
251
                    m_tree->_p(refdata.node)->m_key.scalar = m_tree->key(refdata.target);
92✔
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;
92✔
254
                    m_tree->_add_flags(refdata.node, KEY | existing_style_flags);
92✔
255
                }
256
            }
257
            else // val ref
258
            {
259
                _c4dbgpf("instance[{}:node{}] is val ref", i, refdata.node);
260
                _RYML_ASSERT_VISIT_(m_tree->m_callbacks, refdata.type.is_val_ref(), m_tree, refdata.node);
592✔
261
                if(m_tree->has_key_anchor(refdata.target) && m_tree->key_anchor(refdata.target) == m_tree->val_ref(refdata.node))
684✔
262
                {
263
                    _c4dbgpf("instance[{}:node{}] target.anchor==key.anchor=={}", i, refdata.node, m_tree->key_anchor(refdata.target));
264
                    _RYML_CHECK_BASIC_(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
168✔
265
                    _RYML_CHECK_BASIC_(m_tree->m_callbacks, m_tree->has_val(refdata.target));
168✔
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;
84✔
268
                    static_assert((KEY_STYLE << 1u) == (VAL_STYLE), "bad flags");
269
                    m_tree->_p(refdata.node)->m_val.scalar = m_tree->key(refdata.target);
84✔
270
                    m_tree->_add_flags(refdata.node, VAL | (existing_style_flags << 1u));
84✔
271
                }
272
                else
273
                {
274
                    _c4dbgpf("instance[{}:node{}] duplicate contents", i, refdata.node);
275
                    m_tree->duplicate_contents(refdata.target, refdata.node);
212✔
276
                }
277
            }
278
        }
279
        _c4dbg_tree("after insertion", *m_tree);
280
    }
281
}
188✔
282

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

287
    reset_(t_);
224✔
288

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

291
    gather_anchors_and_refs_();
224✔
292
    if(m_refs.empty())
224✔
293
        return;
4✔
294
    resolve_();
220✔
295
    _c4dbg_tree("resolved tree", *m_tree);
296

297
    // clear anchors and refs
298
    if(clear_anchors)
188✔
299
    {
300
        _c4dbgp("clearing anchors/refs");
301
        auto clear_ = [this]{
360✔
302
            for(auto const& C4_RESTRICT ar : m_refs)
2,692✔
303
            {
304
                m_tree->rem_anchor_ref(ar.node);
2,332✔
305
                if(ar.parent_ref != NONE)
2,332✔
306
                    if(m_tree->type(ar.parent_ref) != NOTYPE)
208✔
307
                        m_tree->remove(ar.parent_ref);
20✔
308
            }
309
        };
405✔
310
        clear_();
180✔
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_();
180✔
316
        clear_();
180✔
317
        _c4dbgp("clearing anchors/refs: finished");
318
    }
319

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

322
    m_tree = nullptr;
188✔
323
    _c4dbgp("resolving references: finished");
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