• 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

99.13
/tools/yaml_events.cpp
1
#ifdef RYML_SINGLE_HEADER
2
#include <ryml_all.hpp>
3
#else
4
#include <c4/yml/std/std.hpp>
5
#include <c4/yml/error.def.hpp>
6
#include <c4/yml/parse.hpp>
7
#include <c4/yml/error.def.hpp>
8
#include <c4/yml/event_handler_tree.hpp>
9
#include <c4/yml/parse_engine.def.hpp>
10
#endif
11
#include <c4/yml/extra/string.hpp>
12
#include <c4/yml/extra/scalar.hpp>
13
#include <c4/yml/extra/event_handler_ints.hpp>
14
#include <c4/yml/extra/event_handler_testsuite.hpp>
15
#include <c4/yml/extra/ints_utils.hpp>
16
#include <c4/yml/extra/ints_to_testsuite.hpp>
17
#include <testsuite/testsuite_events.hpp>
18
#include <c4/fs/fs.hpp>
19
#include <cstdio>
20
#include <chrono>
21

22

23
#ifdef C4_EXCEPTIONS
24
#include <stdexcept>
25
#else
26
#include <csetjmp>
27
std::jmp_buf jmp_env = {};
28
c4::csubstr jmp_msg = {};
29
#endif
30

31
C4_SUPPRESS_WARNING_GCC("-Wold-style-cast")
32

33

34
//-----------------------------------------------------------------------------
35

36
using namespace c4;
37
using namespace c4::yml;
38

39
enum class evts_type
40
{
41
    testsuite_src,
42
    testsuite_ints,
43
    testsuite_tree,
44
    ryml_ints,
45
};
46

47
struct Args
48
{
49
    csubstr filename = "-";
50
    evts_type evts = evts_type::testsuite_src;
51
    int ints_size = -1; // estimate by default
52
    bool ints_size_force = false; // do not force the estimated size
53
    static bool parse(Args *args, int argc, const char *argv[], int *errcode);
54
};
55

56
const char usage[] = R"(usage:
57

58
ryml-yaml-events <command> <options> [-]        # read from stdin (default)
59
ryml-yaml-events <command> <options> <file>     # read from file
60

61
The command must be one of the following:
62

63
    testsuite_src,ts_src,tss
64
         emit test suite events directly from source: parse the YAML
65
         source, and directly emit events during the parse (ie, no
66
         ryml tree is created). This is the default behavior when the
67
         option is omitted.
68

69
    testsuite_tree,ts_tree,tst
70
         emit test suite events from tree: parse the YAML source,
71
         creating a ryml tree. Once the tree is completely created,
72
         emit the test suite events by iterating over the nodes of the
73
         created tree.
74

75
    testsuite_ints,ts_ints,tsi
76
         emit test suite events from the ryml int events handler:
77
         parse the YAML source to a container of ryml int events. Once
78
         this is completed, emit the corresponding test suite events.
79

80
    ryml_ints,ri
81
         emit ryml int events: parse the YAML source to a container of
82
         ryml int events. Once this is completed, print those same int
83
         events.
84

85
The following options influence the behavior of the program:
86

87
    --timings,--timing,-t
88
         print task timings and size information (to stderr)
89

90
The following options influence the behavior of testsuite_ints and ryml_ints:
91

92
    --ints-size <int-number>,-is <int-number>
93
         when using int events, set the int event buffer size from this
94
         value. use a negative value to force a conservative estimation
95
         from a first run over the YAML source, and then multiply the
96
         estimation by the symmetrical of that value. For example, -2
97
         will result in 2*estimation. Default is -1.
98

99
    --ints-size-force,-isf
100
         when using int events, force the initial int event buffer size
101
         to prevail: if this size is not large enough to accomodate the
102
         actual number of events required from the YAML source, exit
103
         with a nonzero error code. This is in contrast to the default
104
         behavior, which consists of expanding the buffer as needed,
105
         which requires two parses and two string copies of the
106
         original source buffer.
107

108
EXAMPLES:
109

110
  $ ryml-yaml-events ts_src            # parse stdin to test suite events, then print the events
111
  $ ryml-yaml-events ts_src -          # parse stdin to test suite events, then print the events
112
  $ ryml-yaml-events ts_src <file>     # parse file to test suite events, then print the events
113

114
  $ ryml-yaml-events ts_tree           # parse stdin to ryml tree, emit test suite events from created tree
115
  $ ryml-yaml-events ts_tree -         # parse stdin to ryml tree, emit test suite events from created tree
116
  $ ryml-yaml-events ts_tree <file>    # parse file to ryml tree, emit test suite events from created tree
117

118
  $ ryml-yaml-events ts_ints           # parse stdin to ryml int events, emit test suite events from ryml int events
119
  $ ryml-yaml-events ts_ints -         # parse stdin to ryml int events, emit test suite events from ryml int events
120
  $ ryml-yaml-events ts_ints <file>    # parse file to ryml int events, emit test suite events from ryml int events
121

122
  $ ryml-yaml-events ryml_ints         # parse stdin to ryml int events, emit ryml int events
123
  $ ryml-yaml-events ryml_ints -       # parse stdin to ryml int events, emit ryml int events
124
  $ ryml-yaml-events ryml_ints <file>  # parse file to ryml int events, emit ryml int events
125

126
)";
127

128

129
//-----------------------------------------------------------------------------
130

131
using IntEvents = std::vector<extra::ievt::DataType>;
132

133
std::string load_file(csubstr filename);
134
extra::string emit_testsuite_events(csubstr filename, substr filecontents);
135
std::string emit_testsuite_events_from_tree(csubstr filename, substr filecontents);
136
std::string emit_testsuite_events_from_ints(csubstr filename, substr filecontents, IntEvents &evts, bool fail_size);
137
void emit_ints_events(csubstr filename, substr filecontents, IntEvents &evts, bool fail_size);
138
int estimate_ints_size(csubstr filecontents, int size);
139
Callbacks create_custom_callbacks();
140

141

142
bool timing_enabled = false;
143
double src_size = 0;
144
namespace stdc = std::chrono;
145
struct stopwatch
146
{
147
    using clock_type = stdc::steady_clock;
148
    const char *name;
149
    clock_type::time_point start;
150
    stopwatch(const char *name_)
480✔
151
    {
480✔
152
        if(!timing_enabled) return;
480✔
153
        name = name_;
42✔
154
        stack.emplace_back(this);
42✔
155
        start = clock_type::now();
42✔
156
    }
157
    ~stopwatch()
458✔
158
    {
42✔
159
        if(!timing_enabled) return;
458✔
160
        stdc::duration<double, std::milli> delta = clock_type::now() - start;
42✔
161
        for(stopwatch const* sw : stack)
132✔
162
            fprintf(stderr, "%s:", sw->name);
90✔
163
        fprintf(stderr, " %.6fms (%.3fMB/s)\n", delta.count(), src_size / delta.count() * 1.e-3);
42✔
164
        stack.resize(stack.size()-1);
42✔
165
    }
458✔
166
    static std::vector<stopwatch*> stack;
167

168
};
169
std::vector<stopwatch*> stopwatch::stack;
170
#define STOPWATCH(name) stopwatch C4_XCAT(timer, __LINE__){name}
171

172

173
//-----------------------------------------------------------------------------
174

175
int main(int argc, const char *argv[])
120✔
176
{
177
    Args args = {};
120✔
178
    {
179
        int errcode = 0;
120✔
180
        if(!Args::parse(&args, argc, argv, &errcode))
120✔
181
            return errcode;
36✔
182
    }
183
    set_callbacks(create_custom_callbacks());
84✔
184
    C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
14✔
185
    {
186
        std::string src;
84✔
187
        {
188
            STOPWATCH("load_file");
84✔
189
            src = load_file(args.filename);
84✔
190
            src_size = (double)src.size();
78✔
191
            if(timing_enabled) fprintf(stderr, "src_size=%zuB\n", src.size());
78✔
192
        }
83✔
193
        STOPWATCH("process");
78✔
194
        switch(args.evts)
78✔
195
        {
196
        case evts_type::testsuite_src:
24✔
197
        {
198
            extra::string evts;
24✔
199
            {
200
                STOPWATCH("testsuite_src");
24✔
201
                evts = emit_testsuite_events(args.filename, to_substr(src));
24✔
202
            }
21✔
203
            {
204
                STOPWATCH("print");
6✔
205
                std::fwrite(evts.data(), 1, evts.size(), stdout);
6✔
206
            }
6✔
207
            break;
6✔
208
        }
21✔
209
        case evts_type::testsuite_tree:
30✔
210
        {
211
            std::string evts;
30✔
212
            {
213
                STOPWATCH("testsuite_tree");
30✔
214
                evts = emit_testsuite_events_from_tree(args.filename, to_substr(src));
30✔
215
            }
27✔
216
            {
217
                STOPWATCH("print");
12✔
218
                std::fwrite(evts.data(), 1, evts.size(), stdout);
12✔
219
            }
12✔
220
            break;
12✔
221
        }
27✔
222
        case evts_type::testsuite_ints:
18✔
223
        {
224
            substr ssrc = to_substr(src);
12✔
225
            int estimated_size = estimate_ints_size(ssrc, args.ints_size);
36✔
226
            IntEvents int_evts((size_t)estimated_size);
18✔
227
            std::string ts_evts;
18✔
228
            {
229
                STOPWATCH("testsuite_ints");
18✔
230
                ts_evts = emit_testsuite_events_from_ints(args.filename, to_substr(src), int_evts, args.ints_size_force);
36✔
231
            }
17✔
232
            {
233
                STOPWATCH("print");
12✔
234
                std::fwrite(ts_evts.data(), 1, ts_evts.size(), stdout);
12✔
235
            }
12✔
236
            break;
12✔
237
        }
22✔
238
        case evts_type::ryml_ints:
6✔
239
        {
240
            substr ssrc = to_substr(src);
4✔
241
            int estimated_size = estimate_ints_size(ssrc, args.ints_size);
12✔
242
            IntEvents int_evts((size_t)estimated_size);
6✔
243
            {
244
                STOPWATCH("ryml_ints");
6✔
245
                emit_ints_events(args.filename, to_substr(src), int_evts, args.ints_size_force);
12✔
246
            }
6✔
247
            break;
6✔
248
        }
6✔
249
        }
250
    }
111✔
251
    C4_IF_EXCEPTIONS_(catch(std::exception const&), else)
40✔
252
    {
253
        return 1;
48✔
254
    }
40✔
255
    return 0;
36✔
256
}
257

258

259
//-----------------------------------------------------------------------------
260

261
std::string emit_testsuite_events_from_tree(csubstr filename, substr filecontents)
30✔
262
{
263
    Tree tree(create_custom_callbacks());
30✔
264
    {
265
        STOPWATCH("tree_reserve");
30✔
266
        tree.reserve(estimate_tree_capacity(filecontents));
30✔
267
    }
30✔
268
    {
269
        STOPWATCH("parse");
30✔
270
        parse_in_place(filename, filecontents, &tree);
30✔
271
    }
27✔
272
    {
273
        STOPWATCH("emit_events");
12✔
274
        std::string result = emit_events_from_tree<std::string>(tree);
12✔
275
        return result;
22✔
276
    }
12✔
277
}
27✔
278

279
extra::string emit_testsuite_events(csubstr filename, substr filecontents)
24✔
280
{
281
    extra::EventHandlerTestSuite::EventSink sink = {};
24✔
282
    extra::EventHandlerTestSuite handler(&sink, create_custom_callbacks());
24✔
283
    ParseEngine<extra::EventHandlerTestSuite> parser(&handler);
24✔
284
    {
285
        STOPWATCH("parse");
24✔
286
        parser.parse_in_place_ev(filename, filecontents);
24✔
287
    }
21✔
288
    return sink;
11✔
289
}
51✔
290

291
csubstr parse_events_ints(csubstr filename, substr filecontents, std::string &parsed, std::string &arena, IntEvents &evts, bool fail_size)
24✔
292
{
293
    using I = extra::ievt::DataType;
294
    using Handler = extra::EventHandlerInts;
295
    Handler handler(create_custom_callbacks());
24✔
296
    ParseEngine<Handler> parser(&handler);
24✔
297
    substr src = filecontents;
24✔
298
    if(!fail_size)
24✔
299
    {
300
        STOPWATCH("copy_src");
18✔
301
        parsed.assign(filecontents.str, filecontents.len);
18✔
302
        src = to_substr(parsed);
18✔
303
    }
18✔
304
    arena.resize(src.len);
24✔
305
    handler.reset(src, to_substr(arena), evts.data(), (I)evts.size());
48✔
306
    {
307
        STOPWATCH("parse");
24✔
308
        parser.parse_in_place_ev(filename, src);
24✔
309
    }
24✔
310
    size_t sz = (size_t)handler.required_size_events();
24✔
311
    if(timing_enabled) fprintf(stderr, "current_size=%zu vs needed_size=%zu. arena_size=%zu\n", evts.size(), sz, arena.size());
24✔
312
    if (!handler.fits_buffers())
24✔
313
    {
314
        if(fail_size)
12✔
315
        {
316
            _RYML_ERR_BASIC("buffers too small");
6✔
317
        }
318
        {
319
            STOPWATCH("resize");
6✔
320
            evts.resize(sz);
6✔
321
            arena.resize(handler.required_size_arena());
6✔
322
        }
6✔
323
        {
324
            STOPWATCH("redo_copy_src");
6✔
325
            parsed.assign(filecontents.str, filecontents.len);
6✔
326
            src = to_substr(parsed);
6✔
327
        }
6✔
328
        handler.reset(src, to_substr(arena), evts.data(), (I)evts.size());
12✔
329
        {
330
            STOPWATCH("redo_parse");
6✔
331
            parser.parse_in_place_ev(filename, src);
6✔
332
        }
6✔
333
        _RYML_CHECK_BASIC((size_t)handler.m_evt_pos == sz);
6✔
334
    }
335
    evts.resize(sz);
18✔
336
    return src;
33✔
337
}
28✔
338

339
std::string emit_testsuite_events_from_ints(csubstr filename, substr filecontents, IntEvents &evts, bool fail_size)
18✔
340
{
341
    using I = extra::ievt::DataType;
342
    std::string buf;
18✔
343
    std::string arena;
18✔
344
    csubstr parsed;
18✔
345
    {
346
        STOPWATCH("events");
18✔
347
        parsed = parse_events_ints(filename, filecontents, buf, arena, evts, fail_size);
18✔
348
    }
17✔
349
    std::string result;
12✔
350
    {
351
        STOPWATCH("emit");
12✔
352
        extra::events_ints_to_testsuite(parsed, to_substr(arena), evts.data(), (I)evts.size(), &result);
32✔
353
    }
12✔
354
    return result;
22✔
355
}
22✔
356

357
void emit_ints_events(csubstr filename, substr filecontents, IntEvents &evts, bool fail_size)
6✔
358
{
359
    using I = extra::ievt::DataType;
360
    std::string buf;
6✔
361
    std::string arena;
6✔
362
    csubstr parsed;
6✔
363
    {
364
        STOPWATCH("events");
6✔
365
        parsed = parse_events_ints(filename, filecontents, buf, arena, evts, fail_size);
6✔
366
    }
6✔
367
    {
368
        STOPWATCH("emit");
6✔
369
        extra::events_ints_print(parsed, to_substr(arena), evts.data(), (I)evts.size());
16✔
370
    }
6✔
371
}
6✔
372

373
int estimate_ints_size(csubstr filecontents, int size)
24✔
374
{
375
    if(size < 0)
24✔
376
    {
377
        STOPWATCH("estimate_size");
12✔
378
        int est = extra::estimate_events_ints_size(filecontents);
12✔
379
        if(timing_enabled) fprintf(stderr, "estimated_size=%d*%d=%d\n", -size, est, -size * est);
12✔
380
        size = -size * est;
12✔
381
    }
12✔
382
    return size;
24✔
383
}
384

385

386
//-----------------------------------------------------------------------------
387

388
// return true if the program should continue (eg -h should exit)
389
bool Args::parse(Args *args, int argc, const char *argv[], int *errcode)
120✔
390
{
391
    *errcode = 0;
120✔
392
    auto argerr = [&](const char *msg){
24✔
393
        std::printf(usage);
24✔
394
        std::printf("ERROR: %s\n", msg);
24✔
395
        *errcode = 1;
24✔
396
        return false;
24✔
397
    };
120✔
398
    // set defaults
399
    *args = {};
120✔
400
    // parse the command argument
401
    if(argc < 2)
120✔
402
        return argerr("must provide a command");  // LCOV_EXCL_LINE
403
    bool has_cmd = false;
120✔
404
    {
405
        csubstr s = to_csubstr(argv[1]);
200✔
406
        if(s == "testsuite_src" || s == "ts_src" || s == "tss")
360✔
407
        {
408
            args->evts = evts_type::testsuite_src;
24✔
409
            has_cmd = true;
24✔
410
        }
411
        else if(s == "testsuite_tree" || s == "ts_tree" || s == "tst")
288✔
412
        {
413
            args->evts = evts_type::testsuite_tree;
36✔
414
            has_cmd = true;
36✔
415
        }
416
        else if(s == "testsuite_ints" || s == "ts_ints" || s == "tsi")
180✔
417
        {
418
            args->evts = evts_type::testsuite_ints;
36✔
419
            has_cmd = true;
36✔
420
        }
421
        else if(s == "ryml_ints" || s == "ri")
48✔
422
        {
423
            args->evts = evts_type::ryml_ints;
6✔
424
            has_cmd = true;
6✔
425
        }
426
        else if(s == "--help" || s == "-h")
30✔
427
        {
428
            std::printf(usage);
6✔
429
            return false;
6✔
430
        }
431
    }
432
    if(!has_cmd)
114✔
433
        return argerr("unknown command");
12✔
434
    // parse other args
435
    int i = 2; // cmd is mandatory at i=1, so start after that at i=2
102✔
436
    for(; i < argc; ++i)
210✔
437
    {
438
        csubstr arg = to_csubstr(argv[i]);
210✔
439
        if(arg == "--help" || arg == "-h")
246✔
440
        {
441
            std::printf(usage);
6✔
442
            return false;
18✔
443
        }
444
        else if(arg == "--timings" || arg == "--timing" || arg == "-t")
354✔
445
        {
446
            timing_enabled = true;
6✔
447
        }
448
        else if(arg == "--ints-size-force" || arg == "-isf")
222✔
449
        {
450
            args->ints_size_force = true;
6✔
451
        }
452
        else if(arg == "--ints-size" || arg == "-is")
192✔
453
        {
454
            if(i + 1 >= argc)
24✔
455
                return argerr("ints-size value not given");  // LCOV_EXCL_LINE
456
            else if(!atoi(to_csubstr(argv[i+1]), &args->ints_size))
48✔
457
                return argerr("ints-size value fails to parse");  // LCOV_EXCL_LINE
458
            ++i; // shift 1
12✔
459
        }
460
        else
461
        {
462
            args->filename = arg;
84✔
463
        }
464
    }
465
    return true;
84✔
466
}
467

468
std::string load_file(csubstr filename)
84✔
469
{
470
    if(filename == "-") // read from stdin
84✔
471
    {   // LCOV_EXCL_START
472
        std::string buf;
473
        buf.reserve(128);
474
        for(int c = std::getchar(); c != EOF; c = std::getchar())
475
            buf.push_back(static_cast<char>(c));
UNCOV
476
        return buf;    // LCOV_EXCL_STOP
×
UNCOV
477
    }
×
478
    else if(!fs::path_exists(filename.str))
84✔
479
    {
480
        std::fprintf(stderr, "%s: file not found (cwd=%s)\n", filename.str, fs::cwd<std::string>().c_str()); // LCOV_EXCL_LINE
481
        err_basic(RYML_LOC_HERE(), "file not found"); // LCOV_EXCL_LINE
482
    }
483
    return fs::file_get_contents<std::string>(filename.str);
78✔
484
}
485

486
[[noreturn]] C4_NO_INLINE void throwerr(csubstr msg)
48✔
487
{
488
    C4_IF_EXCEPTIONS(
128✔
489
        throw std::runtime_error({msg.str, msg.len});
490
        ,
491
        jmp_msg.assign(msg.str, msg.len);
492
        std::longjmp(jmp_env, 1);
493
        );
494
    C4_UNREACHABLE_AFTER_ERR();
495
}
496

497
void dump2stderr(csubstr s)
804✔
498
{
499
    if(s.len)
804✔
500
    {
501
        fwrite(s.str, 1, s.len, stderr); // NOLINT
804✔
502
        fflush(stderr); // NOLINT
804✔
503
    }
504
}
804✔
505

506
Callbacks create_custom_callbacks()
162✔
507
{
508
    return Callbacks{}
162✔
509
        .set_error_basic([](csubstr msg, yml::ErrorDataBasic const& errdata, void *){
162✔
510
            yml::err_basic_format(dump2stderr, msg, errdata); // LCOV_EXCL_LINE
511
            throwerr(msg); // LCOV_EXCL_LINE
512
        })
513
        .set_error_parse([](csubstr msg, yml::ErrorDataParse const& errdata, void *){
162✔
514
            yml::err_parse_format(dump2stderr, msg, errdata); // LCOV_EXCL_LINE
515
            throwerr(msg); // LCOV_EXCL_LINE
516
        })
517
        .set_error_visit([](csubstr msg, yml::ErrorDataVisit const& errdata, void *){
162✔
518
            yml::err_visit_format(dump2stderr, msg, errdata); // LCOV_EXCL_LINE
519
            throwerr(msg); // LCOV_EXCL_LINE
520
        });
162✔
521
}
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