rapidyaml 0.15.2
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
quickstart-ints.cpp
Go to the documentation of this file.
1// This file shows a quick example of parsing YAML to an int events
2// buffer. Since this functionality is meant to implement in other
3// programming languages, the code is kept very simple, and using only
4// C-like idioms (other than std::vector).
5
6
7// ryml can be used as a single header, or as a simple library:
8#if defined(RYML_SINGLE_HEADER) // using the single header directly in the executable
9 #define RYML_SINGLE_HDR_DEFINE_NOW
10 #include <ryml_ints.hpp>
11#elif defined(RYML_SINGLE_HEADER_LIB) // using the single header from a library
12 #include <ryml_ints.hpp>
13#else
16#include <c4/yml/extra/ints_utils.hpp> // estimate size, print
17#include <c4/std/vector.hpp>
18#include <c4/yml/file.hpp>
19#endif
20
21#include <vector>
22
23using namespace c4::yml::extra::ievt;
24
25
26// holds the buffers necessary for getting the int events
28{
29 std::vector<char> yaml = {}; ///< source buffer
30 std::vector<char> arena = {}; ///< arena to place out-of-source filtered scalars or tags
31 std::vector<evt_bits> events = {}; ///< result buffer with events
33 {
34 yaml.assign(yaml_.begin(), yaml_.end());
35 }
36 void resize_buffers(c4::yml::extra::evt_size esz, size_t arena_size)
37 {
38 events.resize(static_cast<size_t>(esz));
39 arena.resize(arena_size);
40 }
45};
46
47
48// a helper to parse the YAML
50{
53
54 IntsParser() noexcept: handler(), parser(&handler) {}
55
56 bool parse_in_place(const char *filename, c4::substr yaml, IntsResult *result)
57 {
58 handler.reset(yaml, c4::to_substr(result->arena),
59 result->events.data(), static_cast<c4::yml::extra::evt_size>(result->events.size()));
60 parser.parse_in_place_ev(filename, yaml); // we're parsing in place on the original YAML
61 // the YAML was successfully parsed, but it may happen that it
62 // requires more events than may fit in the buffers. so we
63 // need to check that it actually fits (this is mandatory):
64 if(handler.fits_buffers())
65 result->resize_buffers(handler); // trim the events buffer to the required size
66 return handler.fits_buffers();
67 }
68
69 bool parse_or_resize_and_then_parse(const char *filename, c4::csubstr yaml, IntsResult *result)
70 {
71 // the buffer is not mutable. we'll need to parse a copy
72 result->reset_yaml(yaml);
73 // attempt the parse
74 if(this->parse_in_place(filename, c4::to_substr(result->yaml), result))
75 return true;
76 // events or arena buffers were too small. need to resize and parse again.
77 result->resize_buffers(handler);
78 // copy the source again! we need to do this because the
79 // buffer was parsed in place, and may have been modified
80 // during the parse.
81 result->reset_yaml(yaml);
82 // parse again!
83 return this->parse_in_place(filename, c4::to_substr(result->yaml), result);
84 }
85};
86
87
88struct Args
89{
90 const char *filename = nullptr;
91 int events_size = -1;
92 int arena_size = -1;
93 bool retry = true;
94 bool quiet = false;
95};
97{
98 Args defaults = {};
99 printf(R"(usage: %.*s [options] [<filename>]
100
101 if filename is missing, we default to a preset example.
102
103options:
104
105 -e <size>,--events-size <size> set size of events buffer. -1 means
106 estimate size. default=%d
107 -a <size>,--arena-size <size> set size of arena buffer. -1 means
108 take the source size as the arena size.
109 default=%d
110 -n,--no-retry do not retry parsing if any buffer size
111 was insufficient. default=%s
112 -q,--quiet do not print events. default=%s
113)",
114 static_cast<int>(basename.len), basename.str,
115 defaults.events_size,
116 defaults.arena_size,
117 defaults.retry ? "retry" : "do not retry",
118 defaults.quiet ? "quiet" : "print events");
119}
120bool parse_args(int argc, const char **argv, Args *args, int &status)
121{
122 c4::csubstr basename = argv[0];
123 // read left-to-right until we find an arg that is not an option
124 int remaining = argc - 1;
125 ++argv;
126 auto advance = [&](int num) { argv += num; remaining -= num; };
127 auto parse_arg = [&](const char *name, int *dst) {
128 if(remaining > 1)
129 {
130 c4::csubstr val = argv[1];
131 if(!c4::atoi(val, dst))
132 {
133 status = -1;
134 printf("error: could not parse %s value: %.*s\n", name, static_cast<int>(val.len), val.str);
135 }
136 advance(2);
137 }
138 else
139 {
140 status = -1;
141 printf("error: missing value: %s\n", name);
142 }
143 return true;
144 };
145 while(remaining > 0 && status == 0)
146 {
147 c4::csubstr arg = argv[0];
148 if(arg == "-h" || arg == "--help")
149 {
150 print_usage(basename);
151 status = 0;
152 return false;
153 }
154 else if(arg == "-e" || arg == "--events-size")
155 {
156 parse_arg("--events-size", &args->events_size);
157 }
158 else if(arg == "-a" || arg == "--arena-size")
159 {
160 parse_arg("--arena-size", &args->arena_size);
161 }
162 else if(arg == "-n" || arg == "--no-retry")
163 {
164 args->retry = false;
165 advance(1);
166 }
167 else if(arg == "-q" || arg == "--quiet")
168 {
169 args->quiet = false;
170 advance(1);
171 }
172 else
173 break;
174 }
175 if(remaining == 1)
176 {
177 if(status == 0)
178 args->filename = argv[0];
179 }
180 else if(remaining > 1)
181 {
182 print_usage(basename);
183 printf("error: too many arguments\n");
184 status = -1;
185 }
186 return status == 0;
187}
188
189
190//-----------------------------------------------------------------------------
191
192// NOLINTBEGIN(hicpp-signed-bitwise)
193
194int main(int argc, const char *argv[])
195{
196 Args args = {};
197 int status = 0;
198 if(!parse_args(argc, argv, &args, status))
199 return status;
200
201 // if no filename is given, we run a demo,
202 // and show the result is as expected.
203 bool is_demo = args.filename == nullptr;
204
205 // YAML code to be parsed in place
206 char demo_yaml[] = ""
207 "doe: a deer, a female deer\n"
208 "ray: a drop of golden sun\n"
209 "me : a name I call myself\n"
210 "far: a long long way to run\n"
211 "";
212
213 // these are the events we expect from parsing the demo YAML
214 auto STR = c4::yml::extra::ievt::PSTR; // PSTR does not work in windows
215 const int demo_expected[] = {
216 BSTR,
217 BDOC,
218 VAL_|BMAP|BLCK,
219 KEY_|SCLR|PLAI, 0, 3, // "doe"
220 VAL_|SCLR|PLAI|STR, 5, 21, // "a deer, a female deer"
221 KEY_|SCLR|PLAI|STR, 27, 3, // "ray"
222 VAL_|SCLR|PLAI|STR, 32, 20, // "a drop of golden sun"
223 KEY_|SCLR|PLAI|STR, 53, 2, // "me"
224 VAL_|SCLR|PLAI|STR, 58, 20, // "a name I call myself"
225 KEY_|SCLR|PLAI|STR, 79, 3, // "far"
226 VAL_|SCLR|PLAI|STR, 84, 22, // "a long long way to run"
227 EMAP|STR,
228 EDOC,
229 ESTR,
230 };
231
232 /* the output should be this:
233 *
234 * pos=0 event[0]: BSTR = 0x00000001
235 * pos=1 event[1]: BDOC = 0x00000004
236 * pos=2 event[2]: VAL_|BMAP|BLCK = 0x00140010
237 * pos=3 event[3]: KEY_|SCLR|PLAI = 0x00081100 str=(0,3) 'doe'
238 * pos=6 event[4]: VAL_|SCLR|PLAI|PSTR = 0x04101100 str=(5,21) 'a deer, a female deer'
239 * pos=9 event[5]: KEY_|SCLR|PLAI|PSTR = 0x04081100 str=(27,3) 'ray'
240 * pos=12 event[6]: VAL_|SCLR|PLAI|PSTR = 0x04101100 str=(32,20) 'a drop of golden sun'
241 * pos=15 event[7]: KEY_|SCLR|PLAI|PSTR = 0x04081100 str=(53,2) 'me'
242 * pos=18 event[8]: VAL_|SCLR|PLAI|PSTR = 0x04101100 str=(58,20) 'a name I call myself'
243 * pos=21 event[9]: KEY_|SCLR|PLAI|PSTR = 0x04081100 str=(79,3) 'far'
244 * pos=24 event[10]: VAL_|SCLR|PLAI|PSTR = 0x04101100 str=(84,22) 'a long long way to run'
245 * pos=27 event[11]: EMAP|PSTR = 0x04000020
246 * pos=28 event[12]: EDOC = 0x00000008
247 * pos=29 event[13]: ESTR = 0x00000002
248 */
249
250 IntsParser parser;
251 IntsResult result;
252
253 const char *filename = args.filename;
254 std::vector<char> yaml_file;
255 c4::substr yaml;
256
257 if(is_demo)
258 {
259 yaml = demo_yaml;
260 filename = "demo";
261 }
262 else
263 {
264 c4::csubstr filename_ = c4::to_csubstr(filename);
265 if(filename_ == "-" || filename_ == "stdin")
266 c4::yml::stdin_get_contents(&yaml_file); // LCOV_EXCL_LINE lcov fail
267 else
268 c4::yml::file_get_contents(&yaml_file, args.filename);
269 yaml = c4::to_substr(yaml_file);
270 }
271
272 if(args.events_size == -1)
274 if(args.arena_size == -1)
275 args.arena_size = static_cast<int>(yaml.size());
276
277 result.resize_buffers(args.events_size, static_cast<size_t>(args.arena_size));
278
279 // parse now. since we did not set up the error callbacks, if
280 // there is a parse error, we will get the default behavior, which
281 // is abort on error
282 if( ! args.retry)
283 {
284 if( ! parser.parse_in_place(filename, yaml, &result))
285 return 1;
286 }
287 else
288 {
289 if( ! parser.parse_or_resize_and_then_parse(filename, yaml, &result))
290 return 1; // this is really not expected LCOV_EXCL_LINE
291 }
292
293 if( ! is_demo)
294 {
295 if( ! args.quiet)
296 {
297 c4::csubstr actual_buffer = args.retry ? yaml : c4::to_csubstr(result.yaml);
299 result.events.data(),
300 static_cast<c4::yml::extra::evt_size>(result.events.size()));
301 }
302 return 0;
303 }
304
305 // we have the demo. ensure the result is as expected
306 bool success = true;
307
308 // example iterating through the events array: compare and print
309 // the result
310 char flags[100]; // buffer for converting event flags to string
311 for (size_t pos = 0, evt = 0, sz = static_cast<size_t>(parser.handler.required_size_events());
312 pos < sz;
313 ++pos, ++evt)
314 {
315 bool ok = (result.events[pos] == demo_expected[pos]);
316 // print the event
317 if( ! args.quiet)
318 {
319 // let's format the event flags to print them as string.
320 size_t len = c4::yml::extra::ievt::to_str(flags, result.events[pos]);
321 printf("pos=%zu\tevent[%zu]:\t%.*s = 0x%08x", pos, evt, static_cast<int>(len), flags, result.events[pos]);
322 }
323 if(result.events[pos] & WSTR) // the event has a string following it
324 {
325 int offset = result.events[pos + 1];
326 int length = result.events[pos + 2];
327 bool in_arena = (result.events[pos] & AREN);
328 // WATCHOUT! the string is NOT ZERO TERMINATED!
329 const char *ptr = in_arena ? result.arena.data() : yaml.str;
330 const char *str = ptr + offset;
331 if( ! args.quiet)
332 printf("\tstr=(%d,%d)\t'%.*s'", offset, length, length, str);
333 ok = ok && (offset == demo_expected[pos + 1]);
334 ok = ok && (length == demo_expected[pos + 2]);
335 pos += 2; // advance the two ints from the string
336 }
337 if(!ok)
338 {
339 if( ! args.quiet) // LCOV_EXCL_LINE
340 printf(" ... fail!"); // LCOV_EXCL_LINE
341 success = false; // LCOV_EXCL_LINE
342 }
343 if( ! args.quiet) // LCOV_EXCL_LINE
344 printf("\n");
345 }
346
347 return success ? 0 : 1;
348}
349
350// NOLINTEND(hicpp-signed-bitwise)
This is the main driver of parsing logic: it scans the YAML or JSON source for tokens,...
An event handler that creates an integer buffer with a very compact representation of the YAML tree i...
provides conversion and comparison facilities from/between std::vector<char> to c4::substr and c4::cs...
bool atoi(csubstr str, T *v) noexcept
Convert a trimmed string to a signed integral value.
int32_t evt_size
data type for integer events size.
void events_ints_print(csubstr parsed_yaml, csubstr arena, ievt::evt_bits const *evts, ievt::evt_bits evts_sz)
Print integer events to stdout.
evt_size estimate_events_ints_size(csubstr src)
Read YAML source and, without undergoing a full parse, estimate the size of the integer buffer requir...
void file_get_contents(const char *filename, FILE *fp, size_t filesz, void *buf, size_t bufsz)
load a file of specified size from disk into an existing contiguous buffer.
Definition file.hpp:106
void stdin_get_contents(ContiguousContainer *cont, FILE *f=stdin)
load a file from stdin (or similar stream-like file) and return a newly created ContiguousContainer w...
Definition file.hpp:193
substr to_substr(char(&s)[N]) noexcept
Definition substr.hpp:2376
csubstr to_csubstr(const char(&s)[N]) noexcept
Definition substr.hpp:2380
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2355
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2356
size_t to_str(substr buf, ievt::evt_bits flags) noexcept
Convert bit mask of ievt::EventBits to text.
@ PSTR
Special flag to enable look-back in the event array. It signifies that the previous event has a strin...
@ SCLR
scalar (=VAL in test suite events)
@ EMAP
end map (-MAP in test suite events)
@ BMAP
begin map (+MAP in test suite events)
@ ESTR
end stream (-STR in test suite events)
@ BSTR
begin stream (+STR in test suite events)
@ WSTR
WithSTRing: mask of all events that encode a string following the event. For such events,...
@ BDOC
begin doc (+DOC in test suite events)
@ AREN
Special flag to mark events whose string was placed in the arena. This happens when the filtered stri...
@ EDOC
end doc (-DOC in test suite events)
void print_usage(c4::csubstr basename)
bool parse_args(int argc, const char **argv, Args *args, int &status)
int main(int argc, const char *argv[])
int events_size
const char * filename
IntsParser() noexcept
c4::yml::ParseEngine< c4::yml::extra::EventHandlerInts > parser
bool parse_in_place(const char *filename, c4::substr yaml, IntsResult *result)
c4::yml::extra::EventHandlerInts handler
bool parse_or_resize_and_then_parse(const char *filename, c4::csubstr yaml, IntsResult *result)
void resize_buffers(c4::yml::extra::EventHandlerInts const &handler)
std::vector< char > yaml
source buffer
std::vector< evt_bits > events
result buffer with events
void reset_yaml(c4::csubstr yaml_)
std::vector< char > arena
arena to place out-of-source filtered scalars or tags
void resize_buffers(c4::yml::extra::evt_size esz, size_t arena_size)
size_t len
the length of the substring
Definition substr.hpp:218
iterator begin() noexcept
Definition substr.hpp:360
size_t size() const noexcept
Definition substr.hpp:358
iterator end() noexcept
Definition substr.hpp:361
C * str
a restricted pointer to the first character of the substring
Definition substr.hpp:216
A parser event handler that creates a compact representation of the YAML tree in a contiguous buffer ...
evt_size required_size_events() const
get the size needed for the event buffer from the previous parse
size_t required_size_arena() const
get the size needed for the arena from the previous parse