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

biojppm / rapidyaml / 14071541066

25 Mar 2025 10:45PM UTC coverage: 97.524% (-0.06%) from 97.586%
14071541066

Pull #508

github

web-flow
Merge d27170bc2 into d3132a25e
Pull Request #508: fix build with cmake 4

11539 of 11832 relevant lines covered (97.52%)

764866.73 hits per line

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

97.66
/src/c4/yml/reference_resolver.cpp
1
#include "c4/yml/reference_resolver.hpp"
2
#include "c4/dump.hpp" // this is needed to resolve a function in the next header
3
#include "c4/yml/common.hpp"
4
#include "c4/yml/detail/parser_dbg.hpp"
5
#ifdef RYML_DBG
6
#include "c4/yml/detail/print.hpp"
7
#else
8
#define _c4dbg_tree(...)
9
#define _c4dbg_node(...)
10
#endif
11

12
namespace c4 {
13
namespace yml {
14

15
/** @cond dev */
16

17
id_type ReferenceResolver::count_anchors_and_refs_(id_type n)
1,948✔
18
{
19
    id_type c = 0;
1,948✔
20
    c += m_tree->has_key_anchor(n);
1,948✔
21
    c += m_tree->has_val_anchor(n);
1,948✔
22
    c += m_tree->is_key_ref(n);
1,948✔
23
    c += m_tree->is_val_ref(n);
1,948✔
24
    c += m_tree->has_key(n) && m_tree->key(n) == "<<";
5,348✔
25
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
3,720✔
26
        c += count_anchors_and_refs_(ch);
1,772✔
27
    return c;
1,948✔
28
}
29

30
void ReferenceResolver::gather_anchors_and_refs__(id_type n)
1,868✔
31
{
32
    // insert key refs BEFORE inserting val refs
33
    if(m_tree->has_key(n))
3,736✔
34
    {
35
        if(m_tree->key(n) == "<<")
2,904✔
36
        {
37
            _c4dbgpf("node[{}]: key is <<", n);
38
            if(m_tree->has_val(n))
208✔
39
            {
40
                if(m_tree->is_val_ref(n))
168✔
41
                {
42
                    _c4dbgpf("node[{}]: val ref, inheriting!", n);
43
                    m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
72✔
44
                    //m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
45
                }
46
                else
47
                {
48
                    _c4dbgpf("node[{}]: not ref!", n);
49
                }
50
            }
51
            else if(m_tree->is_seq(n))
40✔
52
            {
53
                // for merging multiple inheritance targets
54
                //   <<: [ *CENTER, *BIG ]
55
                _c4dbgpf("node[{}]: is seq!", n);
56
                for(id_type ich = m_tree->first_child(n); ich != NONE; ich = m_tree->next_sibling(ich))
72✔
57
                {
58
                    _c4dbgpf("node[{}]: val ref, inheriting multiple: {}", n, ich);
59
                    if(m_tree->is_container(ich))
104✔
60
                    {
61
                        detail::_report_err(m_tree->m_callbacks, "ERROR: node {} child {}: refs for << cannot be containers.'", n, ich);
×
62
                        C4_UNREACHABLE_AFTER_ERR();
63
                    }
64
                    m_refs.push({VALREF, ich, NONE, NONE, n, m_tree->next_sibling(n)});
52✔
65
                }
66
                return; // don't descend into the seq
20✔
67
            }
68
            else
69
            {
70
                detail::_report_err(m_tree->m_callbacks, "ERROR: node {}: refs for << must be either val or seq", n);
×
71
                C4_UNREACHABLE_AFTER_ERR();
72
            }
73
        }
74
        else if(m_tree->is_key_ref(n))
2,696✔
75
        {
76
            _c4dbgpf("node[{}]: key ref: '{}'", n, m_tree->key_ref(n));
77
            _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->key(n) != "<<");
384✔
78
            _RYML_CB_CHECK(m_tree->m_callbacks, (!m_tree->has_key(n)) || m_tree->key(n).ends_with(m_tree->key_ref(n)));
384✔
79
            m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
192✔
80
        }
81
    }
82
    // val ref
83
    if(m_tree->is_val_ref(n) && (!m_tree->has_key(n) || m_tree->key(n) != "<<"))
4,264✔
84
    {
85
        _c4dbgpf("node[{}]: val ref: '{}'", n, m_tree->val_ref(n));
86
        RYML_CHECK((!m_tree->has_val(n)) || m_tree->val(n).ends_with(m_tree->val_ref(n)));
480✔
87
        m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
240✔
88
    }
89
    // anchors
90
    if(m_tree->has_key_anchor(n))
3,696✔
91
    {
92
        _c4dbgpf("node[{}]: key anchor: '{}'", n, m_tree->key_anchor(n));
93
        RYML_CHECK(m_tree->has_key(n));
336✔
94
        m_refs.push({KEYANCH, n, NONE, NONE, NONE, NONE});
168✔
95
    }
96
    if(m_tree->has_val_anchor(n))
3,696✔
97
    {
98
        _c4dbgpf("node[{}]: val anchor: '{}'", n, m_tree->val_anchor(n));
99
        RYML_CHECK(m_tree->has_val(n) || m_tree->is_container(n));
896✔
100
        m_refs.push({VALANCH, n, NONE, NONE, NONE, NONE});
348✔
101
    }
102
    // recurse
103
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
3,544✔
104
        gather_anchors_and_refs__(ch);
1,696✔
105
}
106

107
void ReferenceResolver::gather_anchors_and_refs_()
176✔
108
{
109
    _c4dbgp("gathering anchors and refs...");
110

111
    // minimize (re-)allocations by counting first
112
    id_type num_anchors_and_refs = count_anchors_and_refs_(m_tree->root_id());
176✔
113
    if(!num_anchors_and_refs)
176✔
114
        return;
4✔
115
    m_refs.reserve(num_anchors_and_refs);
172✔
116
    m_refs.clear();
172✔
117

118
    // now descend through the hierarchy
119
    gather_anchors_and_refs__(m_tree->root_id());
172✔
120

121
    _c4dbgpf("found {} anchors/refs", m_refs.size());
122

123
    // finally connect the reference list
124
    id_type prev_anchor = NONE;
172✔
125
    id_type count = 0;
172✔
126
    for(auto &rd : m_refs)
1,244✔
127
    {
128
        rd.prev_anchor = prev_anchor;
1,072✔
129
        if(rd.type.has_anchor())
2,144✔
130
            prev_anchor = count;
516✔
131
        ++count;
1,072✔
132
    }
133
    _c4dbgp("gathering anchors and refs: finished");
134
}
135

136
id_type ReferenceResolver::lookup_(RefData *C4_RESTRICT ra)
556✔
137
{
138
    RYML_ASSERT(ra->type.is_key_ref() || ra->type.is_val_ref());
1,476✔
139
    RYML_ASSERT(ra->type.is_key_ref() != ra->type.is_val_ref());
1,668✔
140
    csubstr refname;
556✔
141
    if(ra->type.is_val_ref())
1,112✔
142
    {
143
        refname = m_tree->val_ref(ra->node);
364✔
144
    }
145
    else
146
    {
147
        RYML_ASSERT(ra->type.is_key_ref());
384✔
148
        refname = m_tree->key_ref(ra->node);
192✔
149
    }
150
    while(ra->prev_anchor != NONE)
1,256✔
151
    {
152
        ra = &m_refs[ra->prev_anchor];
1,232✔
153
        if(m_tree->has_anchor(ra->node, refname))
2,464✔
154
            return ra->node;
532✔
155
    }
156
    detail::_report_err(m_tree->m_callbacks, "ERROR: anchor not found: '{}'", refname);
24✔
157
    C4_UNREACHABLE_AFTER_ERR();
158
}
159

160
void ReferenceResolver::reset_(Tree *t_)
176✔
161
{
162
    if(t_->callbacks() != m_refs.m_callbacks)
176✔
163
    {
164
        m_refs.m_callbacks = t_->callbacks();
×
165
    }
166
    m_refs.clear();
176✔
167
    m_tree = t_;
176✔
168
}
176✔
169

170
void ReferenceResolver::resolve(Tree *t_)
176✔
171
{
172
    _c4dbgp("resolving references...");
173

174
    reset_(t_);
176✔
175

176
    _c4dbg_tree("unresolved tree", *m_tree);
177

178
    gather_anchors_and_refs_();
176✔
179
    if(m_refs.empty())
176✔
180
        return;
4✔
181

182
    /* from the specs: "an alias node refers to the most recent
183
     * node in the serialization having the specified anchor". So
184
     * we need to start looking upward from ref nodes.
185
     *
186
     * @see http://yaml.org/spec/1.2/spec.html#id2765878 */
187
    _c4dbgp("matching anchors/refs...");
188
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
1,220✔
189
    {
190
        RefData &C4_RESTRICT refdata = m_refs.top(i);
1,072✔
191
        if( ! refdata.type.is_ref())
2,144✔
192
            continue;
516✔
193
        refdata.target = lookup_(&refdata);
556✔
194
    }
195
    _c4dbgp("matching anchors/refs: finished");
196

197
    // insert the resolved references
198
    _c4dbgp("modifying tree...");
199
    id_type prev_parent_ref = NONE;
148✔
200
    id_type prev_parent_ref_after = NONE;
148✔
201
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
1,196✔
202
    {
203
        RefData const& C4_RESTRICT refdata = m_refs[i];
1,048✔
204
        _c4dbgpf("instance {}/{}...", i, e);
205
        if( ! refdata.type.is_ref())
2,096✔
206
            continue;
516✔
207
        _c4dbgpf("instance {} is reference!", i);
208
        if(refdata.parent_ref != NONE)
532✔
209
        {
210
            _c4dbgpf("ref {} has parent: {}", i, refdata.parent_ref);
211
            _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_seq(refdata.parent_ref));
104✔
212
            const id_type p = m_tree->parent(refdata.parent_ref);
52✔
213
            const id_type after = (prev_parent_ref != refdata.parent_ref) ?
52✔
214
                refdata.parent_ref//prev_sibling(rd.parent_ref_sibling)
215
                :
216
                prev_parent_ref_after;
32✔
217
            prev_parent_ref = refdata.parent_ref;
52✔
218
            prev_parent_ref_after = m_tree->duplicate_children_no_rep(refdata.target, p, after);
52✔
219
            m_tree->remove(refdata.node);
52✔
220
        }
221
        else
222
        {
223
            _c4dbgpf("ref {} has no parent", i, refdata.parent_ref);
224
            if(m_tree->has_key(refdata.node) && m_tree->key(refdata.node) == "<<")
1,400✔
225
            {
226
                _c4dbgpf("ref {} is inheriting", i);
227
                _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_keyval(refdata.node));
144✔
228
                const id_type p = m_tree->parent(refdata.node);
72✔
229
                const id_type after = m_tree->prev_sibling(refdata.node);
72✔
230
                m_tree->duplicate_children_no_rep(refdata.target, p, after);
72✔
231
                m_tree->remove(refdata.node);
72✔
232
            }
233
            else if(refdata.type.is_key_ref())
816✔
234
            {
235
                _c4dbgpf("ref {} is key ref", i);
236
                _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_key_ref(refdata.node));
384✔
237
                _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->has_key_anchor(refdata.target) || m_tree->has_val_anchor(refdata.target));
428✔
238
                if(m_tree->has_val_anchor(refdata.target) && m_tree->val_anchor(refdata.target) == m_tree->key_ref(refdata.node))
484✔
239
                {
240
                    _RYML_CB_CHECK(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
200✔
241
                    _RYML_CB_CHECK(m_tree->m_callbacks, m_tree->has_val(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
                    _RYML_CB_CHECK(m_tree->m_callbacks, m_tree->key_anchor(refdata.target) == m_tree->key_ref(refdata.node));
184✔
250
                    m_tree->_p(refdata.node)->m_key.scalar = m_tree->key(refdata.target);
92✔
251
                    // keys cannot be containers, so don't inherit container flags
252
                    const type_bits existing_style_flags = KEY_STYLE & m_tree->_p(refdata.target)->m_type.type;
92✔
253
                    m_tree->_add_flags(refdata.node, KEY | existing_style_flags);
92✔
254
                }
255
            }
256
            else // val ref
257
            {
258
                _c4dbgpf("ref {} is val ref", i);
259
                _RYML_CB_ASSERT(m_tree->m_callbacks, refdata.type.is_val_ref());
432✔
260
                if(m_tree->has_key_anchor(refdata.target) && m_tree->key_anchor(refdata.target) == m_tree->val_ref(refdata.node))
524✔
261
                {
262
                    _RYML_CB_CHECK(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
168✔
263
                    _RYML_CB_CHECK(m_tree->m_callbacks, m_tree->has_val(refdata.target));
168✔
264
                    // keys cannot be containers, so don't inherit container flags
265
                    const type_bits existing_style_flags = (KEY_STYLE) & m_tree->_p(refdata.target)->m_type.type;
84✔
266
                    static_assert((KEY_STYLE << 1u) == (VAL_STYLE), "bad flags");
267
                    m_tree->_p(refdata.node)->m_val.scalar = m_tree->key(refdata.target);
84✔
268
                    m_tree->_add_flags(refdata.node, VAL | (existing_style_flags << 1u));
84✔
269
                }
270
                else
271
                {
272
                    m_tree->duplicate_contents(refdata.target, refdata.node);
132✔
273
                }
274
            }
275
        }
276
    }
277
    _c4dbgp("modifying tree: finished");
278

279
    // clear anchors and refs
280
    _c4dbgp("clearing anchors/refs");
281
    for(auto const& C4_RESTRICT ar : m_refs)
1,196✔
282
    {
283
        m_tree->rem_anchor_ref(ar.node);
1,048✔
284
        if(ar.parent_ref != NONE)
1,048✔
285
            if(m_tree->type(ar.parent_ref) != NOTYPE)
104✔
286
                m_tree->remove(ar.parent_ref);
20✔
287
    }
288
    _c4dbgp("clearing anchors/refs: finished");
289

290
    _c4dbg_tree("resolved tree", *m_tree);
291

292
    m_tree = nullptr;
148✔
293
    _c4dbgp("resolving references: finished");
294
}
295

296
/** @endcond */
297

298
} // namespace ryml
299
} // 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

© 2026 Coveralls, Inc