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

biojppm / rapidyaml / 14071101254

25 Mar 2025 10:12PM UTC coverage: 97.53% (-0.06%) from 97.586%
14071101254

Pull #508

github

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

11568 of 11861 relevant lines covered (97.53%)

763005.52 hits per line

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

98.09
/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)
3,896✔
18
{
19
    id_type c = 0;
3,896✔
20
    c += m_tree->has_key_anchor(n);
3,896✔
21
    c += m_tree->has_val_anchor(n);
3,896✔
22
    c += m_tree->is_key_ref(n);
3,896✔
23
    c += m_tree->is_val_ref(n);
3,896✔
24
    c += m_tree->has_key(n) && m_tree->key(n) == "<<";
10,696✔
25
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
7,440✔
26
        c += count_anchors_and_refs_(ch);
3,544✔
27
    return c;
3,896✔
28
}
29

30
void ReferenceResolver::gather_anchors_and_refs__(id_type n)
3,736✔
31
{
32
    // insert key refs BEFORE inserting val refs
33
    if(m_tree->has_key(n))
7,472✔
34
    {
35
        if(m_tree->key(n) == "<<")
5,808✔
36
        {
37
            _c4dbgpf("node[{}]: key is <<", n);
104✔
38
            if(m_tree->has_val(n))
416✔
39
            {
40
                if(m_tree->is_val_ref(n))
336✔
41
                {
42
                    _c4dbgpf("node[{}]: val ref, inheriting!", n);
72✔
43
                    m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
144✔
44
                    //m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
45
                }
46
                else
47
                {
48
                    _c4dbgpf("node[{}]: not ref!", n);
12✔
49
                }
50
            }
51
            else if(m_tree->is_seq(n))
80✔
52
            {
53
                // for merging multiple inheritance targets
54
                //   <<: [ *CENTER, *BIG ]
55
                _c4dbgpf("node[{}]: is seq!", n);
20✔
56
                for(id_type ich = m_tree->first_child(n); ich != NONE; ich = m_tree->next_sibling(ich))
144✔
57
                {
58
                    _c4dbgpf("node[{}]: val ref, inheriting multiple: {}", n, ich);
52✔
59
                    if(m_tree->is_container(ich))
208✔
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)});
104✔
65
                }
66
                return; // don't descend into the seq
40✔
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))
5,392✔
75
        {
76
            _c4dbgpf("node[{}]: key ref: '{}'", n, m_tree->key_ref(n));
192✔
77
            _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->key(n) != "<<");
768✔
78
            _RYML_CB_CHECK(m_tree->m_callbacks, (!m_tree->has_key(n)) || m_tree->key(n).ends_with(m_tree->key_ref(n)));
768✔
79
            m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
384✔
80
        }
81
    }
82
    // val ref
83
    if(m_tree->is_val_ref(n) && (!m_tree->has_key(n) || m_tree->key(n) != "<<"))
8,528✔
84
    {
85
        _c4dbgpf("node[{}]: val ref: '{}'", n, m_tree->val_ref(n));
240✔
86
        RYML_CHECK((!m_tree->has_val(n)) || m_tree->val(n).ends_with(m_tree->val_ref(n)));
960✔
87
        m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
480✔
88
    }
89
    // anchors
90
    if(m_tree->has_key_anchor(n))
7,392✔
91
    {
92
        _c4dbgpf("node[{}]: key anchor: '{}'", n, m_tree->key_anchor(n));
168✔
93
        RYML_CHECK(m_tree->has_key(n));
672✔
94
        m_refs.push({KEYANCH, n, NONE, NONE, NONE, NONE});
336✔
95
    }
96
    if(m_tree->has_val_anchor(n))
7,392✔
97
    {
98
        _c4dbgpf("node[{}]: val anchor: '{}'", n, m_tree->val_anchor(n));
348✔
99
        RYML_CHECK(m_tree->has_val(n) || m_tree->is_container(n));
1,792✔
100
        m_refs.push({VALANCH, n, NONE, NONE, NONE, NONE});
696✔
101
    }
102
    // recurse
103
    for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
7,088✔
104
        gather_anchors_and_refs__(ch);
3,392✔
105
}
106

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

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

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

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

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

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

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

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

174
    reset_(t_);
352✔
175

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

178
    gather_anchors_and_refs_();
352✔
179
    if(m_refs.empty())
352✔
180
        return;
8✔
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...");
172✔
188
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
2,440✔
189
    {
190
        RefData &C4_RESTRICT refdata = m_refs.top(i);
2,144✔
191
        if( ! refdata.type.is_ref())
4,288✔
192
            continue;
1,032✔
193
        refdata.target = lookup_(&refdata);
1,112✔
194
    }
195
    _c4dbgp("matching anchors/refs: finished");
148✔
196

197
    // insert the resolved references
198
    _c4dbgp("modifying tree...");
148✔
199
    id_type prev_parent_ref = NONE;
296✔
200
    id_type prev_parent_ref_after = NONE;
296✔
201
    for(id_type i = 0, e = m_refs.size(); i < e; ++i)
2,392✔
202
    {
203
        RefData const& C4_RESTRICT refdata = m_refs[i];
2,096✔
204
        _c4dbgpf("instance {}/{}...", i, e);
1,048✔
205
        if( ! refdata.type.is_ref())
4,192✔
206
            continue;
1,032✔
207
        _c4dbgpf("instance {} is reference!", i);
532✔
208
        if(refdata.parent_ref != NONE)
1,064✔
209
        {
210
            _c4dbgpf("ref {} has parent: {}", i, refdata.parent_ref);
52✔
211
            _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_seq(refdata.parent_ref));
208✔
212
            const id_type p = m_tree->parent(refdata.parent_ref);
104✔
213
            const id_type after = (prev_parent_ref != refdata.parent_ref) ?
104✔
214
                refdata.parent_ref//prev_sibling(rd.parent_ref_sibling)
215
                :
216
                prev_parent_ref_after;
64✔
217
            prev_parent_ref = refdata.parent_ref;
104✔
218
            prev_parent_ref_after = m_tree->duplicate_children_no_rep(refdata.target, p, after);
104✔
219
            m_tree->remove(refdata.node);
104✔
220
        }
221
        else
222
        {
223
            _c4dbgpf("ref {} has no parent", i, refdata.parent_ref);
480✔
224
            if(m_tree->has_key(refdata.node) && m_tree->key(refdata.node) == "<<")
2,800✔
225
            {
226
                _c4dbgpf("ref {} is inheriting", i);
72✔
227
                _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_keyval(refdata.node));
288✔
228
                const id_type p = m_tree->parent(refdata.node);
144✔
229
                const id_type after = m_tree->prev_sibling(refdata.node);
144✔
230
                m_tree->duplicate_children_no_rep(refdata.target, p, after);
144✔
231
                m_tree->remove(refdata.node);
144✔
232
            }
233
            else if(refdata.type.is_key_ref())
1,632✔
234
            {
235
                _c4dbgpf("ref {} is key ref", i);
192✔
236
                _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_key_ref(refdata.node));
768✔
237
                _RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->has_key_anchor(refdata.target) || m_tree->has_val_anchor(refdata.target));
856✔
238
                if(m_tree->has_val_anchor(refdata.target) && m_tree->val_anchor(refdata.target) == m_tree->key_ref(refdata.node))
968✔
239
                {
240
                    _RYML_CB_CHECK(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
400✔
241
                    _RYML_CB_CHECK(m_tree->m_callbacks, m_tree->has_val(refdata.target));
400✔
242
                    const type_bits existing_style_flags = VAL_STYLE & m_tree->_p(refdata.target)->m_type.type;
200✔
243
                    static_assert((VAL_STYLE >> 1u) == (KEY_STYLE), "bad flags");
244
                    m_tree->_p(refdata.node)->m_key.scalar = m_tree->val(refdata.target);
200✔
245
                    m_tree->_add_flags(refdata.node, KEY | (existing_style_flags >> 1u));
200✔
246
                }
247
                else
248
                {
249
                    _RYML_CB_CHECK(m_tree->m_callbacks, m_tree->key_anchor(refdata.target) == m_tree->key_ref(refdata.node));
368✔
250
                    m_tree->_p(refdata.node)->m_key.scalar = m_tree->key(refdata.target);
184✔
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;
184✔
253
                    m_tree->_add_flags(refdata.node, KEY | existing_style_flags);
184✔
254
                }
255
            }
256
            else // val ref
257
            {
258
                _c4dbgpf("ref {} is val ref", i);
216✔
259
                _RYML_CB_ASSERT(m_tree->m_callbacks, refdata.type.is_val_ref());
864✔
260
                if(m_tree->has_key_anchor(refdata.target) && m_tree->key_anchor(refdata.target) == m_tree->val_ref(refdata.node))
1,048✔
261
                {
262
                    _RYML_CB_CHECK(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
336✔
263
                    _RYML_CB_CHECK(m_tree->m_callbacks, m_tree->has_val(refdata.target));
336✔
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;
168✔
266
                    static_assert((KEY_STYLE << 1u) == (VAL_STYLE), "bad flags");
267
                    m_tree->_p(refdata.node)->m_val.scalar = m_tree->key(refdata.target);
168✔
268
                    m_tree->_add_flags(refdata.node, VAL | (existing_style_flags << 1u));
168✔
269
                }
270
                else
271
                {
272
                    m_tree->duplicate_contents(refdata.target, refdata.node);
264✔
273
                }
274
            }
275
        }
276
    }
277
    _c4dbgp("modifying tree: finished");
148✔
278

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

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

292
    m_tree = nullptr;
296✔
293
    _c4dbgp("resolving references: finished");
148✔
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