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

biojppm / rapidyaml / 18160471768

01 Oct 2025 11:19AM UTC coverage: 97.276% (-0.4%) from 97.65%
18160471768

Pull #503

github

web-flow
Merge 8e72e99dd into 653eac974
Pull Request #503: Improve error model, callbacks

1735 of 1782 new or added lines in 31 files covered. (97.36%)

64 existing lines in 7 files now uncovered.

12677 of 13032 relevant lines covered (97.28%)

187443.13 hits per line

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

98.7
/src/c4/yml/tag.cpp
1
#include "c4/yml/tag.hpp"
2
#include "c4/yml/detail/dbgprint.hpp"
3
#include "c4/yml/error.hpp"
4

5

6
namespace c4 {
7
namespace yml {
8

9
bool is_custom_tag(csubstr tag)
39,748✔
10
{
11
    if((tag.len > 2) && (tag.str[0] == '!'))
39,748✔
12
    {
13
        size_t pos = tag.find('!', 1);
29,748✔
14
        return pos != npos && pos > 1 && tag.str[1] != '<';
29,748✔
15
    }
16
    return false;
10,000✔
17
}
18

19
csubstr normalize_tag(csubstr tag)
1,108✔
20
{
21
    YamlTag_e t = to_tag(tag);
1,108✔
22
    if(t != TAG_NONE)
1,108✔
23
        return from_tag(t);
824✔
24
    if(tag.begins_with("!<"))
284✔
25
        tag = tag.sub(1);
36✔
26
    if(tag.begins_with("<!"))
284✔
27
        return tag;
32✔
28
    return tag;
252✔
29
}
30

31
csubstr normalize_tag_long(csubstr tag)
63,424✔
32
{
33
    YamlTag_e t = to_tag(tag);
63,424✔
34
    if(t != TAG_NONE)
63,424✔
35
        return from_tag_long(t);
41,920✔
36
    if(tag.begins_with("!<"))
21,504✔
37
        tag = tag.sub(1);
4,508✔
38
    if(tag.begins_with("<!"))
21,504✔
39
        return tag;
1,700✔
40
    return tag;
19,804✔
41
}
42

43
csubstr normalize_tag_long(csubstr tag, substr output)
18,796✔
44
{
45
    csubstr result = normalize_tag_long(tag);
18,796✔
46
    if(result.begins_with("!!"))
18,796✔
47
    {
48
        tag = tag.sub(2);
312✔
49
        const csubstr pfx = "<tag:yaml.org,2002:";
50
        const size_t len = pfx.len + tag.len + 1;
312✔
51
        if(len <= output.len)
312✔
52
        {
53
            memcpy(output.str          , pfx.str, pfx.len);
304✔
54
            memcpy(output.str + pfx.len, tag.str, tag.len);
304✔
55
            output[pfx.len + tag.len] = '>';
608✔
56
            result = output.first(len);
608✔
57
        }
58
        else
59
        {
60
            result.str = nullptr;
8✔
61
            result.len = len;
8✔
62
        }
63
    }
64
    return result;
18,796✔
65
}
66

67
YamlTag_e to_tag(csubstr tag)
64,972✔
68
{
69
    if(tag.begins_with("!<"))
64,972✔
70
        tag = tag.sub(1);
32,728✔
71
    if(tag.begins_with("!!"))
64,972✔
72
        tag = tag.sub(2);
10,120✔
73
    else if(tag.begins_with('!'))
54,852✔
74
    {
75
        return TAG_NONE;
12,456✔
76
    }
77
    else
78
    {
79
        csubstr pfx = "<tag:yaml.org,2002:";
42,396✔
80
        csubstr pfx2 = pfx.sub(1);
31,797✔
81
        if(tag.begins_with(pfx2))
42,396✔
82
        {
83
            tag = tag.sub(pfx2.len);
272✔
84
        }
85
        else if(tag.begins_with(pfx))
42,260✔
86
        {
87
            tag = tag.sub(pfx.len);
33,248✔
88
            if(!tag.len)
33,248✔
89
                return TAG_NONE;
12✔
90
            tag = tag.offs(0, 1);
33,236✔
91
        }
92
    }
93

94
    if(tag == "map")
52,504✔
95
        return TAG_MAP;
6,840✔
96
    else if(tag == "omap")
45,664✔
97
        return TAG_OMAP;
748✔
98
    else if(tag == "pairs")
44,916✔
99
        return TAG_PAIRS;
36✔
100
    else if(tag == "set")
44,880✔
101
        return TAG_SET;
784✔
102
    else if(tag == "seq")
44,096✔
103
        return TAG_SEQ;
3,716✔
104
    else if(tag == "binary")
40,380✔
105
        return TAG_BINARY;
1,164✔
106
    else if(tag == "bool")
39,216✔
107
        return TAG_BOOL;
752✔
108
    else if(tag == "float")
38,464✔
109
        return TAG_FLOAT;
36✔
110
    else if(tag == "int")
38,428✔
111
        return TAG_INT;
5,752✔
112
    else if(tag == "merge")
32,676✔
113
        return TAG_MERGE;
36✔
114
    else if(tag == "null")
32,640✔
115
        return TAG_NULL;
1,460✔
116
    else if(tag == "str")
31,180✔
117
        return TAG_STR;
21,532✔
118
    else if(tag == "timestamp")
9,648✔
119
        return TAG_TIMESTAMP;
36✔
120
    else if(tag == "value")
9,612✔
121
        return TAG_VALUE;
36✔
122
    else if(tag == "yaml")
9,576✔
123
        return TAG_YAML;
32✔
124

125
    return TAG_NONE;
9,544✔
126
}
127

128
csubstr from_tag_long(YamlTag_e tag)
42,008✔
129
{
130
    switch(tag)
42,008✔
131
    {
132
    case TAG_MAP:
6,468✔
133
        return {"<tag:yaml.org,2002:map>"};
6,468✔
134
    case TAG_OMAP:
692✔
135
        return {"<tag:yaml.org,2002:omap>"};
692✔
136
    case TAG_PAIRS:
16✔
137
        return {"<tag:yaml.org,2002:pairs>"};
16✔
138
    case TAG_SET:
716✔
139
        return {"<tag:yaml.org,2002:set>"};
716✔
140
    case TAG_SEQ:
3,500✔
141
        return {"<tag:yaml.org,2002:seq>"};
3,500✔
142
    case TAG_BINARY:
1,144✔
143
        return {"<tag:yaml.org,2002:binary>"};
1,144✔
144
    case TAG_BOOL:
732✔
145
        return {"<tag:yaml.org,2002:bool>"};
732✔
146
    case TAG_FLOAT:
16✔
147
        return {"<tag:yaml.org,2002:float>"};
16✔
148
    case TAG_INT:
5,724✔
149
        return {"<tag:yaml.org,2002:int>"};
5,724✔
150
    case TAG_MERGE:
16✔
151
        return {"<tag:yaml.org,2002:merge>"};
16✔
152
    case TAG_NULL:
1,440✔
153
        return {"<tag:yaml.org,2002:null>"};
1,440✔
154
    case TAG_STR:
21,488✔
155
        return {"<tag:yaml.org,2002:str>"};
21,488✔
156
    case TAG_TIMESTAMP:
16✔
157
        return {"<tag:yaml.org,2002:timestamp>"};
16✔
158
    case TAG_VALUE:
16✔
159
        return {"<tag:yaml.org,2002:value>"};
16✔
160
    case TAG_YAML:
16✔
161
        return {"<tag:yaml.org,2002:yaml>"};
16✔
162
    case TAG_NONE:
8✔
163
    default:
164
        return {""};
8✔
165
    }
166
}
167

168
csubstr from_tag(YamlTag_e tag)
912✔
169
{
170
    switch(tag)
912✔
171
    {
172
    case TAG_MAP:
368✔
173
        return {"!!map"};
368✔
174
    case TAG_OMAP:
52✔
175
        return {"!!omap"};
52✔
176
    case TAG_PAIRS:
16✔
177
        return {"!!pairs"};
16✔
178
    case TAG_SET:
64✔
179
        return {"!!set"};
64✔
180
    case TAG_SEQ:
212✔
181
        return {"!!seq"};
212✔
182
    case TAG_BINARY:
16✔
183
        return {"!!binary"};
16✔
184
    case TAG_BOOL:
16✔
185
        return {"!!bool"};
16✔
186
    case TAG_FLOAT:
16✔
187
        return {"!!float"};
16✔
188
    case TAG_INT:
24✔
189
        return {"!!int"};
24✔
190
    case TAG_MERGE:
16✔
191
        return {"!!merge"};
16✔
192
    case TAG_NULL:
16✔
193
        return {"!!null"};
16✔
194
    case TAG_STR:
40✔
195
        return {"!!str"};
40✔
196
    case TAG_TIMESTAMP:
16✔
197
        return {"!!timestamp"};
16✔
198
    case TAG_VALUE:
16✔
199
        return {"!!value"};
16✔
200
    case TAG_YAML:
16✔
201
        return {"!!yaml"};
16✔
202
    case TAG_NONE:
8✔
203
    default:
204
        return {""};
8✔
205
    }
206
}
207

208

209
bool TagDirective::create_from_str(csubstr directive)
5,113✔
210
{
211
    _RYML_CHECK_BASIC(directive.begins_with("%TAG "));
5,113✔
212
    directive = directive.sub(4);
5,108✔
213
    if(!directive.begins_with(' '))
5,108✔
214
        return false;
×
215
    directive = directive.triml(' ');
5,108✔
216
    size_t pos = directive.find(' ');
5,108✔
217
    if(pos == npos)
5,108✔
218
        return false;
12✔
219
    handle = directive.first(pos);
5,096✔
220
    directive = directive.sub(handle.len).triml(' ');
10,192✔
221
    pos = directive.find(' ');
5,096✔
222
    if(pos != npos)
5,096✔
223
        directive = directive.first(pos);
×
224
    prefix = directive;
5,096✔
225
    next_node_id = NONE;
5,096✔
226
    _c4dbgpf("%TAG: handle={} prefix={}", handle, prefix);
227
    return true;
5,096✔
228
}
229

230
size_t TagDirective::transform(csubstr tag, substr output, Callbacks const& callbacks, bool with_brackets) const
7,280✔
231
{
232
    _c4dbgpf("%TAG: handle={} prefix={} next_node={}. tag={}", handle, prefix, next_node_id, tag);
233
    _RYML_ASSERT_BASIC_(callbacks, tag.len >= handle.len);
7,280✔
234
    csubstr rest = tag.sub(handle.len);
7,280✔
235
    _c4dbgpf("%TAG: rest={}", rest);
236
    if(rest.begins_with('<'))
7,280✔
237
    {
238
        _c4dbgpf("%TAG: begins with <. rest={}", rest);
239
        if(C4_UNLIKELY(!rest.ends_with('>')))
3,024✔
240
            _RYML_ERR_BASIC_(callbacks, "malformed tag");
16✔
241
        rest = rest.offs(1, 1);
3,008✔
242
        if(rest.begins_with(prefix))
3,008✔
243
        {
244
            _c4dbgpf("%TAG: already transformed! actual={}", rest.sub(prefix.len));
245
            return 0; // return 0 to signal that the tag is local and cannot be resolved
3,008✔
246
        }
247
    }
248
    size_t len = prefix.len + rest.len;
4,256✔
249
    if(with_brackets)
4,256✔
250
        len += 2;
3,272✔
251
    size_t numpc = rest.count('%');
4,256✔
252
    if(numpc == 0)
4,256✔
253
    {
254
        if(len <= output.len)
3,912✔
255
        {
256
            if(with_brackets)
1,888✔
257
            {
258
                output.str[0] = '<';
1,360✔
259
                memcpy(1u + output.str, prefix.str, prefix.len);
1,360✔
260
                memcpy(1u + output.str + prefix.len, rest.str, rest.len);
1,360✔
261
                output.str[1u + prefix.len + rest.len] = '>';
1,360✔
262
            }
263
            else
264
            {
265
                memcpy(output.str, prefix.str, prefix.len);
528✔
266
                memcpy(output.str + prefix.len, rest.str, rest.len);
528✔
267
            }
268
        }
269
    }
270
    else
271
    {
272
        // need to decode URI % sequences
273
        size_t pos = rest.find('%');
344✔
274
        _RYML_ASSERT_BASIC_(callbacks, pos != npos);
344✔
275
        do {
276
            size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
404✔
277
            if(next == npos)
404✔
278
                next = rest.len;
324✔
279
            _RYML_CHECK_BASIC_(callbacks, pos+1 < next);
404✔
280
            _RYML_CHECK_BASIC_(callbacks, pos+1 + 2 <= next);
380✔
281
            size_t delta = next - (pos+1);
380✔
282
            len -= delta;
380✔
283
            pos = rest.find('%', pos+1);
380✔
284
        } while(pos != npos);
380✔
285
        if(len <= output.len)
320✔
286
        {
287
            size_t prev = 0, wpos = 0;
144✔
288
            auto appendstr = [&](csubstr s) { memcpy(output.str + wpos, s.str, s.len); wpos += s.len; };
588✔
289
            auto appendchar = [&](char c) { output.str[wpos++] = c; };
524✔
290
            if(with_brackets)
144✔
291
                appendchar('<');
112✔
292
            appendstr(prefix);
144✔
293
            pos = rest.find('%');
144✔
294
            _RYML_ASSERT_BASIC_(callbacks, pos != npos);
144✔
295
            do {
296
                size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
156✔
297
                if(next == npos)
156✔
298
                    next = rest.len;
144✔
299
                _RYML_CHECK_BASIC_(callbacks, pos+1 < next);
156✔
300
                _RYML_CHECK_BASIC_(callbacks, pos+1 + 2 <= next);
156✔
301
                uint8_t val;
302
                if(C4_UNLIKELY(!read_hex(rest.range(pos+1, next), &val) || val > 127))
468✔
NEW
303
                    _RYML_ERR_BASIC_(callbacks, "invalid URI character");
×
304
                appendstr(rest.range(prev, pos));
156✔
305
                appendchar(static_cast<char>(val));
156✔
306
                prev = next;
156✔
307
                pos = rest.find('%', pos+1);
156✔
308
            } while(pos != npos);
156✔
309
            _RYML_ASSERT_BASIC_(callbacks, pos == npos);
144✔
310
            _RYML_ASSERT_BASIC_(callbacks, prev > 0);
144✔
311
            _RYML_ASSERT_BASIC_(callbacks, rest.len >= prev);
144✔
312
            appendstr(rest.sub(prev));
144✔
313
            if(with_brackets)
144✔
314
                appendchar('>');
112✔
315
            _RYML_ASSERT_BASIC_(callbacks, wpos == len);
144✔
316
        }
317
    }
318
    return len;
4,232✔
319
}
320

321
} // namespace yml
322
} // 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