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

biojppm / rapidyaml / 18162524607

01 Oct 2025 12:42PM UTC coverage: 97.638% (-0.01%) from 97.65%
18162524607

Pull #503

github

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

1795 of 1842 new or added lines in 32 files covered. (97.45%)

38 existing lines in 4 files now uncovered.

13597 of 13926 relevant lines covered (97.64%)

534874.49 hits per line

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

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

5

6
namespace c4 {
7
namespace yml {
8

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

19
csubstr normalize_tag(csubstr tag)
1,662✔
20
{
21
    YamlTag_e t = to_tag(tag);
1,662✔
22
    if(t != TAG_NONE)
1,662✔
23
        return from_tag(t);
1,236✔
24
    if(tag.begins_with("!<"))
426✔
25
        tag = tag.sub(1);
54✔
26
    if(tag.begins_with("<!"))
426✔
27
        return tag;
48✔
28
    return tag;
378✔
29
}
30

31
csubstr normalize_tag_long(csubstr tag)
95,136✔
32
{
33
    YamlTag_e t = to_tag(tag);
95,136✔
34
    if(t != TAG_NONE)
95,136✔
35
        return from_tag_long(t);
62,880✔
36
    if(tag.begins_with("!<"))
32,256✔
37
        tag = tag.sub(1);
6,762✔
38
    if(tag.begins_with("<!"))
32,256✔
39
        return tag;
2,550✔
40
    return tag;
29,706✔
41
}
42

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

67
YamlTag_e to_tag(csubstr tag)
97,458✔
68
{
69
    if(tag.begins_with("!<"))
97,458✔
70
        tag = tag.sub(1);
49,092✔
71
    if(tag.begins_with("!!"))
97,458✔
72
        tag = tag.sub(2);
15,180✔
73
    else if(tag.begins_with('!'))
82,278✔
74
    {
75
        return TAG_NONE;
18,684✔
76
    }
77
    else
78
    {
79
        csubstr pfx = "<tag:yaml.org,2002:";
63,594✔
80
        csubstr pfx2 = pfx.sub(1);
42,396✔
81
        if(tag.begins_with(pfx2))
63,594✔
82
        {
83
            tag = tag.sub(pfx2.len);
408✔
84
        }
85
        else if(tag.begins_with(pfx))
63,390✔
86
        {
87
            tag = tag.sub(pfx.len);
49,872✔
88
            if(!tag.len)
49,872✔
89
                return TAG_NONE;
18✔
90
            tag = tag.offs(0, 1);
49,854✔
91
        }
92
    }
93

94
    if(tag == "map")
78,756✔
95
        return TAG_MAP;
10,260✔
96
    else if(tag == "omap")
68,496✔
97
        return TAG_OMAP;
1,122✔
98
    else if(tag == "pairs")
67,374✔
99
        return TAG_PAIRS;
54✔
100
    else if(tag == "set")
67,320✔
101
        return TAG_SET;
1,176✔
102
    else if(tag == "seq")
66,144✔
103
        return TAG_SEQ;
5,574✔
104
    else if(tag == "binary")
60,570✔
105
        return TAG_BINARY;
1,746✔
106
    else if(tag == "bool")
58,824✔
107
        return TAG_BOOL;
1,128✔
108
    else if(tag == "float")
57,696✔
109
        return TAG_FLOAT;
54✔
110
    else if(tag == "int")
57,642✔
111
        return TAG_INT;
8,628✔
112
    else if(tag == "merge")
49,014✔
113
        return TAG_MERGE;
54✔
114
    else if(tag == "null")
48,960✔
115
        return TAG_NULL;
2,190✔
116
    else if(tag == "str")
46,770✔
117
        return TAG_STR;
32,298✔
118
    else if(tag == "timestamp")
14,472✔
119
        return TAG_TIMESTAMP;
54✔
120
    else if(tag == "value")
14,418✔
121
        return TAG_VALUE;
54✔
122
    else if(tag == "yaml")
14,364✔
123
        return TAG_YAML;
48✔
124

125
    return TAG_NONE;
14,316✔
126
}
127

128
csubstr from_tag_long(YamlTag_e tag)
63,012✔
129
{
130
    switch(tag)
63,012✔
131
    {
132
    case TAG_MAP:
9,702✔
133
        return {"<tag:yaml.org,2002:map>"};
9,702✔
134
    case TAG_OMAP:
1,038✔
135
        return {"<tag:yaml.org,2002:omap>"};
1,038✔
136
    case TAG_PAIRS:
24✔
137
        return {"<tag:yaml.org,2002:pairs>"};
24✔
138
    case TAG_SET:
1,074✔
139
        return {"<tag:yaml.org,2002:set>"};
1,074✔
140
    case TAG_SEQ:
5,250✔
141
        return {"<tag:yaml.org,2002:seq>"};
5,250✔
142
    case TAG_BINARY:
1,716✔
143
        return {"<tag:yaml.org,2002:binary>"};
1,716✔
144
    case TAG_BOOL:
1,098✔
145
        return {"<tag:yaml.org,2002:bool>"};
1,098✔
146
    case TAG_FLOAT:
24✔
147
        return {"<tag:yaml.org,2002:float>"};
24✔
148
    case TAG_INT:
8,586✔
149
        return {"<tag:yaml.org,2002:int>"};
8,586✔
150
    case TAG_MERGE:
24✔
151
        return {"<tag:yaml.org,2002:merge>"};
24✔
152
    case TAG_NULL:
2,160✔
153
        return {"<tag:yaml.org,2002:null>"};
2,160✔
154
    case TAG_STR:
32,232✔
155
        return {"<tag:yaml.org,2002:str>"};
32,232✔
156
    case TAG_TIMESTAMP:
24✔
157
        return {"<tag:yaml.org,2002:timestamp>"};
24✔
158
    case TAG_VALUE:
24✔
159
        return {"<tag:yaml.org,2002:value>"};
24✔
160
    case TAG_YAML:
24✔
161
        return {"<tag:yaml.org,2002:yaml>"};
24✔
162
    case TAG_NONE:
12✔
163
    default:
164
        return {""};
12✔
165
    }
166
}
167

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

208

209
bool TagDirective::create_from_str(csubstr directive)
7,669✔
210
{
211
    _RYML_CHECK_BASIC(directive.begins_with("%TAG "));
7,669✔
212
    directive = directive.sub(4);
7,662✔
213
    if(!directive.begins_with(' '))
7,662✔
214
        return false;
×
215
    directive = directive.triml(' ');
7,662✔
216
    size_t pos = directive.find(' ');
7,662✔
217
    if(pos == npos)
7,662✔
218
        return false;
18✔
219
    handle = directive.first(pos);
7,644✔
220
    directive = directive.sub(handle.len).triml(' ');
15,288✔
221
    pos = directive.find(' ');
7,644✔
222
    if(pos != npos)
7,644✔
223
        directive = directive.first(pos);
×
224
    prefix = directive;
7,644✔
225
    next_node_id = NONE;
7,644✔
226
    _c4dbgpf("%TAG: handle={} prefix={}", handle, prefix);
2,548✔
227
    return true;
7,644✔
228
}
229

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