rapidyaml  0.8.0
parse and emit YAML, and do it fast
quickstart.cpp
Go to the documentation of this file.
1 // ryml: quickstart
2 
3 
4 //-----------------------------------------------------------------------------
5 
6 // ryml can be used as a single header, or as a simple library:
7 #if defined(RYML_SINGLE_HEADER) // using the single header directly in the executable
8  #define RYML_SINGLE_HDR_DEFINE_NOW
9  #include <ryml_all.hpp>
10 #elif defined(RYML_SINGLE_HEADER_LIB) // using the single header from a library
11  #include <ryml_all.hpp>
12 #else
13  #include <ryml.hpp>
14  // <ryml_std.hpp> is needed if interop with std containers is
15  // desired; ryml itself does not use any STL container.
16  // For this sample, we will be using std interop, so...
17  #include <ryml_std.hpp> // optional header, provided for std:: interop
18  #include <c4/format.hpp> // needed for the examples below
19 #endif
20 
21 // these are needed for the examples below
22 #include <iostream>
23 #include <sstream>
24 #include <vector>
25 #include <map>
26 #ifdef C4_EXCEPTIONS
27 #include <stdexcept>
28 #else
29 #include <csetjmp>
30 #endif
31 
32 
33 //-----------------------------------------------------------------------------
34 
35 /** @cond dev */
36 // CONTENTS:
37 //
38 // (Each function addresses a topic and is fully self-contained. Jump
39 // to the function to find out about its topic.)
40 namespace sample {
41 void sample_lightning_overview(); ///< lightning overview of most common features
42 void sample_quick_overview(); ///< quick overview of most common features
43 void sample_substr(); ///< about ryml's string views (from c4core)
44 void sample_parse_file(); ///< ready-to-go example of parsing a file from disk
45 void sample_parse_in_place(); ///< parse a mutable YAML source buffer
46 void sample_parse_in_arena(); ///< parse a read-only YAML source buffer
47 void sample_parse_reuse_tree(); ///< parse into an existing tree, maybe into a node
48 void sample_parse_reuse_parser(); ///< reuse an existing parser
49 void sample_parse_reuse_tree_and_parser(); ///< how to reuse existing trees and parsers
50 void sample_iterate_trees(); ///< visit individual nodes and iterate through trees
51 void sample_create_trees(); ///< programatically create trees
52 void sample_tree_arena(); ///< interact with the tree's serialization arena
53 void sample_fundamental_types(); ///< serialize/deserialize fundamental types
54 void sample_empty_null_values(); ///< serialize/deserialize/query empty or null values
55 void sample_formatting(); ///< control formatting when serializing/deserializing
56 void sample_base64(); ///< encode/decode base64
57 void sample_user_scalar_types(); ///< serialize/deserialize scalar (leaf/string) types
58 void sample_user_container_types(); ///< serialize/deserialize container (map or seq) types
59 void sample_std_types(); ///< serialize/deserialize STL containers
60 void sample_float_precision(); ///< control precision of serialized floats
61 void sample_emit_to_container(); ///< emit to memory, eg a string or vector-like container
62 void sample_emit_to_stream(); ///< emit to a stream, eg std::ostream
63 void sample_emit_to_file(); ///< emit to a FILE*
64 void sample_emit_nested_node(); ///< pick a nested node as the root when emitting
65 void sample_emit_style(); ///< set the nodes to FLOW/BLOCK format
66 void sample_json(); ///< JSON parsing and emitting
67 void sample_anchors_and_aliases(); ///< deal with YAML anchors and aliases
68 void sample_tags(); ///< deal with YAML type tags
69 void sample_tag_directives(); ///< deal with YAML tag namespace directives
70 void sample_docs(); ///< deal with YAML docs
71 void sample_error_handler(); ///< set a custom error handler
72 void sample_global_allocator(); ///< set a global allocator for ryml
73 void sample_per_tree_allocator(); ///< set per-tree allocators
74 void sample_static_trees(); ///< how to use static trees in ryml
75 void sample_location_tracking(); ///< track node locations in the parsed source tree
76 int report_checks();
77 } /* namespace sample */
78 
79 int main()
80 {
116  return sample::report_checks();
117 }
118 
119 /** @endcond */
120 
121 
122 //-----------------------------------------------------------------------------
123 //-----------------------------------------------------------------------------
124 //-----------------------------------------------------------------------------
125 
126 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
127 C4_SUPPRESS_WARNING_GCC_CLANG("-Wcast-qual")
128 C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
129 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
130 
131 namespace sample {
132 
133 /** @addtogroup doc_quickstart
134  *
135  * This file does a quick tour of ryml. It has multiple self-contained
136  * and well-commented samples that illustrate how to use ryml, and how
137  * it works.
138  *
139  * Although this is not a unit test, the samples are written as a
140  * sequence of actions and predicate checks to better convey what is
141  * the expected result at any stage. And to ensure the code here is
142  * correct and up to date, it's also run as part of the CI tests.
143  *
144  * If something is unclear, please open an issue or send a pull
145  * request at https://github.com/biojppm/rapidyaml . If you have an
146  * issue while using ryml, it is also encouraged to try to reproduce
147  * the issue here, or look first through the relevant section.
148  *
149  * Happy ryml'ing!
150  *
151  * ### Some guidance on building
152  *
153  * The directories that exist side-by-side with this file contain
154  * several examples on how to build this with cmake, such that you can
155  * hit the ground running. See [the relevant section of the main
156  * README](https://github.com/biojppm/rapidyaml/tree/v0.5.0?tab=readme-ov-file#quickstart-samples)
157  * for an overview of the different choices. I suggest starting first
158  * with the `add_subdirectory` example, treating it just like any
159  * other self-contained cmake project.
160  *
161  * Or very quickly, to build and run this sample on your PC, start by
162  * creating this `CMakeLists.txt`:
163  * ```cmake
164  * cmake_minimum_required(VERSION 3.13)
165  * project(ryml-quickstart LANGUAGES CXX)
166  * include(FetchContent)
167  * FetchContent_Declare(ryml
168  * GIT_REPOSITORY https://github.com/biojppm/rapidyaml.git
169  * GIT_TAG v0.8.0
170  * GIT_SHALLOW FALSE # ensure submodules are checked out
171  * )
172  * FetchContent_MakeAvailable(ryml)
173  * add_executable(ryml-quickstart ${ryml_SOURCE_DIR}/samples/quickstart.cpp)
174  * target_link_libraries(ryml-quickstart ryml::ryml)
175  * add_custom_target(run ryml-quickstart
176  * COMMAND $<TARGET_FILE:ryml-quickstart>
177  * DEPENDS ryml-quickstart
178  * COMMENT "running: $<TARGET_FILE:ryml-quickstart>")
179  * ```
180  * Now run the following commands in the same folder:
181  * ```bash
182  * # configure the project
183  * cmake -S . -B build
184  * # build and run
185  * cmake --build build --target ryml-quickstart -j
186  * # optionally, open in your IDE
187  * cmake --open build
188  * ```
189  *
190  * @{ */
191 
192 //-----------------------------------------------------------------------------
193 // first, some helpers used in this quickstart
194 
195 /** @defgroup doc_sample_helpers Sample helpers
196  * @brief Functions and classes used in the examples of this sample.
197  * @addtogroup doc_sample_helpers
198  * @{ */
199 
200 
201 bool report_check(int line, const char *predicate, bool result);
202 
203 
204 // GCC 4.8 has a problem with the CHECK() macro
205 #ifndef _DOXYGEN_
206 #if (defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
207 /// a quick'n'dirty assertion to verify a predicate
208 #define CHECK CheckPredicate{__FILE__, __LINE__}
209 struct CheckPredicate
210 {
211  const char *file;
212  const int line;
213  void operator() (bool predicate) const
214  {
215  if (!report_check(line, nullptr, predicate))
216  {
217 #ifdef RYML_DBG
218  RYML_DEBUG_BREAK();
219 #endif
220  }
221  }
222 };
223 #else
224 /** a quick'n'dirty assertion to verify a predicate */
225 #define CHECK(predicate) do { if(!report_check(__LINE__, #predicate, (predicate))) { RYML_DEBUG_BREAK(); } } while(0)
226 #endif
227 #else
228 // enable doxygen to link to the functions called inside CHECK()
229 #define CHECK(predicate) assert(predicate)
230 #endif
231 
232 
233 // helper functions for sample_parse_file()
234 template<class CharContainer> CharContainer file_get_contents(const char *filename);
235 template<class CharContainer> size_t file_get_contents(const char *filename, CharContainer *v);
236 template<class CharContainer> void file_put_contents(const char *filename, CharContainer const& v, const char* access="wb");
237 void file_put_contents(const char *filename, const char *buf, size_t sz, const char* access);
238 
239 
240 /** this is an example error handler, required for some of the
241  * quickstart examples. */
243 {
244  ryml::Callbacks callbacks();
245  C4_NORETURN void on_error(const char* msg, size_t len, ryml::Location loc);
246  C4_NORETURN static void s_error(const char* msg, size_t len, ryml::Location loc, void *this_);
247  template<class Fn> C4_NODISCARD bool check_error_occurs(Fn &&fn) const;
248  template<class Fn> C4_NODISCARD bool check_assertion_occurs(Fn &&fn) const;
249  void check_effect(bool committed) const;
252 };
253 /** Shows how to easily create a scoped error handler. */
255 {
256  ScopedErrorHandlerExample() : ErrorHandlerExample() { ryml::set_callbacks(callbacks()); check_effect(true); }
257  ~ScopedErrorHandlerExample() { ryml::set_callbacks(defaults); check_effect(false); }
258 };
259 
260 /** @} */ // doc_sample_helpers
261 
262 
263 //-----------------------------------------------------------------------------
264 //-----------------------------------------------------------------------------
265 //-----------------------------------------------------------------------------
266 
267 /** a lightning tour over most features
268  * see @ref sample_quick_overview */
270 {
271  // Parse YAML code in place, potentially mutating the buffer:
272  char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}";
273  ryml::Tree tree = ryml::parse_in_place(yml_buf);
274 
275  // read from the tree:
276  ryml::NodeRef bar = tree["bar"];
277  CHECK(bar[0].val() == "2");
278  CHECK(bar[1].val() == "3");
279  CHECK(bar[0].val().str == yml_buf + 15); // points at the source buffer
280  CHECK(bar[1].val().str == yml_buf + 18);
281 
282  // deserializing:
283  int bar0 = 0, bar1 = 0;
284  bar[0] >> bar0;
285  bar[1] >> bar1;
286  CHECK(bar0 == 2);
287  CHECK(bar1 == 3);
288 
289  // serializing:
290  bar[0] << 10; // creates a string in the tree's arena
291  bar[1] << 11;
292  CHECK(bar[0].val() == "10");
293  CHECK(bar[1].val() == "11");
294 
295  // add nodes
296  bar.append_child() << 12; // see also operator= (explanation below)
297  CHECK(bar[2].val() == "12");
298 
299  // emit tree
300  // to std::string
301  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"({foo: 1,bar: [10,11,12],john: doe})");
302  std::cout << tree; // emit to ostream
303  ryml::emit_yaml(tree, stdout); // emit to FILE*
304 
305  // emit node
306  ryml::ConstNodeRef foo = tree["foo"];
307  // to std::string
308  CHECK(ryml::emitrs_yaml<std::string>(foo) == "foo: 1\n");
309  std::cout << foo; // emit node to ostream
310  ryml::emit_yaml(foo, stdout); // emit node to FILE*
311 }
312 
313 
314 //-----------------------------------------------------------------------------
315 
316 /** a brief tour over most features */
318 {
319  // Parse YAML code in place, potentially mutating the buffer:
320  char yml_buf[] = R"(
321 foo: 1
322 bar: [2, 3]
323 john: doe)";
324  ryml::Tree tree = ryml::parse_in_place(yml_buf);
325 
326  // The resulting tree contains only views to the parsed string. If
327  // the string was parsed in place, then the string must outlive
328  // the tree! This works in this case because `yml_buf` and `tree`
329  // live on the same scope, so have the same lifetime.
330 
331  // It is also possible to:
332  //
333  // - parse a read-only buffer using parse_in_arena(). This
334  // copies the YAML buffer to the tree's arena, and spares the
335  // headache of the string's lifetime.
336  //
337  // - reuse an existing tree (advised)
338  //
339  // - reuse an existing parser (advised)
340  //
341  // - parse into an existing node deep in a tree
342  //
343  // Note: it will always be significantly faster to parse in place
344  // and reuse tree+parser.
345  //
346  // Below you will find samples that show how to achieve reuse; but
347  // please note that for brevity and clarity, many of the examples
348  // here are parsing in the arena, and not reusing tree or parser.
349 
350 
351  //------------------------------------------------------------------
352  // API overview
353 
354  // ryml has a two-level API:
355  //
356  // The lower level index API is based on the indices of nodes,
357  // where the node's id is the node's position in the tree's data
358  // array. This API is very efficient, but somewhat difficult to use:
359  ryml::id_type root_id = tree.root_id();
360  ryml::id_type bar_id = tree.find_child(root_id, "bar"); // need to get the index right
361  CHECK(tree.is_map(root_id)); // all of the index methods are in the tree
362  CHECK(tree.is_seq(bar_id)); // ... and receive the subject index
363 
364  // The node API is a lightweight abstraction sitting on top of the
365  // index API, but offering a much more convenient interaction:
366  ryml::ConstNodeRef root = tree.rootref(); // a const node reference
367  ryml::ConstNodeRef bar = tree["bar"];
368  CHECK(root.is_map());
369  CHECK(bar.is_seq());
370 
371  // A node ref is a lightweight handle to the tree and associated id:
372  CHECK(root.tree() == &tree); // a node ref points at its tree, WITHOUT refcount
373  CHECK(root.id() == root_id); // a node ref's id is the index of the node
374  CHECK(bar.id() == bar_id); // a node ref's id is the index of the node
375 
376  // The node API translates very cleanly to the index API, so most
377  // of the code examples below are using the node API.
378 
379  // WARNING. A node ref holds a raw pointer to the tree. Care must
380  // be taken to ensure the lifetimes match, so that a node will
381  // never access the tree after the goes out of scope.
382 
383 
384  //------------------------------------------------------------------
385  // To read the parsed tree
386 
387  // ConstNodeRef::operator[] does a lookup, is O(num_children[node]).
388  CHECK(tree["foo"].is_keyval());
389  CHECK(tree["foo"].val() == "1"); // get the val of a node (must be leaf node, otherwise it is a container and has no val)
390  CHECK(tree["foo"].key() == "foo"); // get the key of a node (must be child of a map, otherwise it has no key)
391  CHECK(tree["bar"].is_seq());
392  CHECK(tree["bar"].has_key());
393  CHECK(tree["bar"].key() == "bar");
394  // maps use string keys, seqs use index keys:
395  CHECK(tree["bar"][0].val() == "2");
396  CHECK(tree["bar"][1].val() == "3");
397  CHECK(tree["john"].val() == "doe");
398  // An index key is the position of the child within its parent,
399  // so even maps can also use int keys, if the key position is
400  // known.
401  CHECK(tree[0].id() == tree["foo"].id());
402  CHECK(tree[1].id() == tree["bar"].id());
403  CHECK(tree[2].id() == tree["john"].id());
404  // Tree::operator[](int) searches a ***root*** child by its position.
405  CHECK(tree[0].id() == tree["foo"].id()); // 0: first child of root
406  CHECK(tree[1].id() == tree["bar"].id()); // 1: second child of root
407  CHECK(tree[2].id() == tree["john"].id()); // 2: third child of root
408  // NodeRef::operator[](int) searches a ***node*** child by its position:
409  CHECK(bar[0].val() == "2"); // 0 means first child of bar
410  CHECK(bar[1].val() == "3"); // 1 means second child of bar
411  // NodeRef::operator[](string):
412  // A string key is the key of the node: lookup is by name. So it
413  // is only available for maps, and it is NOT available for seqs,
414  // since seq members do not have keys.
415  CHECK(tree["foo"].key() == "foo");
416  CHECK(tree["bar"].key() == "bar");
417  CHECK(tree["john"].key() == "john");
418  CHECK(bar.is_seq());
419  // CHECK(bar["BOOM!"].is_seed()); // error, seqs do not have key lookup
420 
421  // Note that maps can also use index keys as well as string keys:
422  CHECK(root["foo"].id() == root[0].id());
423  CHECK(root["bar"].id() == root[1].id());
424  CHECK(root["john"].id() == root[2].id());
425 
426  // IMPORTANT. The ryml tree uses an index-based linked list for
427  // storing children, so the complexity of
428  // `Tree::operator[csubstr]` and `Tree::operator[id_type]` is O(n),
429  // linear on the number of root children. If you use
430  // `Tree::operator[]` with a large tree where the root has many
431  // children, you will see a performance hit.
432  //
433  // To avoid this hit, you can create your own accelerator
434  // structure. For example, before doing a lookup, do a single
435  // traverse at the root level to fill an `map<csubstr,id_type>`
436  // mapping key names to node indices; with a node index, a lookup
437  // (via `Tree::get()`) is O(1), so this way you can get O(log n)
438  // lookup from a key. (But please do not use `std::map` if you
439  // care about performance; use something else like a flat map or
440  // sorted vector).
441  //
442  // As for node refs, the difference from `NodeRef::operator[]` and
443  // `ConstNodeRef::operator[]` to `Tree::operator[]` is that the
444  // latter refers to the root node, whereas the former are invoked
445  // on their target node. But the lookup process works the same for
446  // both and their algorithmic complexity is the same: they are
447  // both linear in the number of direct children. But of course,
448  // depending on the data, that number may be very different from
449  // one to another.
450 
451 
452  //------------------------------------------------------------------
453  // Hierarchy:
454 
455  {
456  ryml::ConstNodeRef foo = root.first_child();
457  ryml::ConstNodeRef john = root.last_child();
458  CHECK(tree.size() == 6); // O(1) number of nodes in the tree
459  CHECK(root.num_children() == 3); // O(num_children[root])
460  CHECK(foo.num_siblings() == 3); // O(num_children[parent(foo)])
461  CHECK(foo.parent().id() == root.id()); // parent() is O(1)
462  CHECK(root.first_child().id() == root["foo"].id()); // first_child() is O(1)
463  CHECK(root.last_child().id() == root["john"].id()); // last_child() is O(1)
464  CHECK(john.first_sibling().id() == foo.id());
465  CHECK(foo.last_sibling().id() == john.id());
466  // prev_sibling(), next_sibling(): (both are O(1))
467  CHECK(foo.num_siblings() == root.num_children());
468  CHECK(foo.prev_sibling().id() == ryml::NONE); // foo is the first_child()
469  CHECK(foo.next_sibling().key() == "bar");
470  CHECK(foo.next_sibling().next_sibling().key() == "john");
471  CHECK(foo.next_sibling().next_sibling().next_sibling().id() == ryml::NONE); // john is the last_child()
472  }
473 
474 
475  //------------------------------------------------------------------
476  // Iterating:
477  {
478  ryml::csubstr expected_keys[] = {"foo", "bar", "john"};
479  // iterate children using the high-level node API:
480  {
481  ryml::id_type count = 0;
482  for(ryml::ConstNodeRef const& child : root.children())
483  CHECK(child.key() == expected_keys[count++]);
484  }
485  // iterate siblings using the high-level node API:
486  {
487  ryml::id_type count = 0;
488  for(ryml::ConstNodeRef const& child : root["foo"].siblings())
489  CHECK(child.key() == expected_keys[count++]);
490  }
491  // iterate children using the lower-level tree index API:
492  {
493  ryml::id_type count = 0;
494  for(ryml::id_type child_id = tree.first_child(root_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
495  CHECK(tree.key(child_id) == expected_keys[count++]);
496  }
497  // iterate siblings using the lower-level tree index API:
498  // (notice the only difference from above is in the loop
499  // preamble, which calls tree.first_sibling(bar_id) instead of
500  // tree.first_child(root_id))
501  {
502  ryml::id_type count = 0;
503  for(ryml::id_type child_id = tree.first_sibling(bar_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
504  CHECK(tree.key(child_id) == expected_keys[count++]);
505  }
506  }
507 
508 
509  //------------------------------------------------------------------
510  // Gotchas:
511 
512  // ryml uses assertions to prevent you from trying to obtain
513  // things that do not exist. For example:
514 
515  {
516  ryml::ConstNodeRef seq_node = tree["bar"];
517  ryml::ConstNodeRef val_node = seq_node[0];
518 
519  CHECK(seq_node.is_seq()); // seq is a container
520  CHECK(!seq_node.has_val()); // ... so it has no val
521  //CHECK(seq_node.val() == BOOM!); // ... so attempting to get a val is undefined behavior
522 
523  CHECK(val_node.parent() == seq_node); // belongs to a seq
524  CHECK(!val_node.has_key()); // ... so it has no key
525  //CHECK(val_node.key() == BOOM!); // ... so attempting to get a key is undefined behavior
526 
527  CHECK(val_node.is_val()); // this node is a val
528  //CHECK(val_node.first_child() == BOOM!); // ... so attempting to get a child is undefined behavior
529 
530  // assertions are also present in methods that /may/ read the val:
531  CHECK(seq_node.is_seq()); // seq is a container
532  //CHECK(seq_node.val_is_null() BOOM!); // so cannot get the val to check
533  }
534 
535  // By default, assertions are enabled unless the NDEBUG macro is
536  // defined (which happens in release builds).
537  //
538  // This adheres to the pay-only-for-what-you-use philosophy: if
539  // you are sure that your intent is correct, why would you need to
540  // pay the runtime cost for the assertions?
541  //
542  // The downside, of course, is that when you are not sure, release
543  // builds may be doing something crazy.
544  //
545  // So in that case, you can either use the appropriate ryml
546  // predicates to check your intent (as in the examples above), or
547  // you can override this behavior and enable/disable assertions,
548  // by defining the macro RYML_USE_ASSERT to a proper value (see
549  // c4/yml/common.hpp).
550  //
551  // Also, to be clear, this does not apply to parse errors
552  // occurring when the YAML is parsed. Checking for these errors is
553  // always enabled and cannot be switched off.
554 
555 
556  //------------------------------------------------------------------
557  // Deserializing: use operator>>
558  {
559  int foo = 0, bar0 = 0, bar1 = 0;
560  std::string john_str;
561  std::string bar_str;
562  root["foo"] >> foo;
563  root["bar"][0] >> bar0;
564  root["bar"][1] >> bar1;
565  root["john"] >> john_str; // requires from_chars(std::string). see serialization samples below.
566  root["bar"] >> ryml::key(bar_str); // to deserialize the key, use the tag function ryml::key()
567  CHECK(foo == 1);
568  CHECK(bar0 == 2);
569  CHECK(bar1 == 3);
570  CHECK(john_str == "doe");
571  CHECK(bar_str == "bar");
572  }
573 
574 
575  //------------------------------------------------------------------
576  // Modifying existing nodes: operator= vs operator<<
577 
578  // As implied by its name, ConstNodeRef is a reference to a const
579  // node. It can be used to read from the node, but not write to it
580  // or modify the hierarchy of the node. If any modification is
581  // desired then a NodeRef must be used instead:
582  ryml::NodeRef wroot = tree.rootref(); // writeable root
583 
584  // operator= assigns an existing string to the receiving node.
585  // The contents are NOT copied, and the string pointer will be in
586  // effect until the tree goes out of scope! So BEWARE to only
587  // assign from strings outliving the tree.
588  wroot["foo"] = "says you";
589  wroot["bar"][0] = "-2";
590  wroot["bar"][1] = "-3";
591  wroot["john"] = "ron";
592  // Now the tree is _pointing_ at the memory of the strings above.
593  // In this case it is OK because those are static strings, located
594  // in the executable's static section, and will outlive the tree.
595  CHECK(root["foo"].val() == "says you");
596  CHECK(root["bar"][0].val() == "-2");
597  CHECK(root["bar"][1].val() == "-3");
598  CHECK(root["john"].val() == "ron");
599  // But WATCHOUT: do not assign from temporary objects:
600  // {
601  // std::string crash("will dangle");
602  // root["john"] = ryml::to_csubstr(crash);
603  // }
604  // CHECK(root["john"] == "dangling"); // CRASH! the string was deallocated
605 
606  // operator<<: for cases where the lifetime of the string is
607  // problematic WRT the tree, you can create and save a string in
608  // the tree using operator<<. It first serializes values to a
609  // string arena owned by the tree, then assigns the serialized
610  // string to the receiving node. This avoids constraints with the
611  // lifetime, since the arena lives with the tree.
612  CHECK(tree.arena().empty());
613  wroot["foo"] << "says who"; // requires to_chars(). see serialization samples below.
614  wroot["bar"][0] << 20;
615  wroot["bar"][1] << 30;
616  wroot["john"] << "deere";
617  CHECK(root["foo"].val() == "says who");
618  CHECK(root["bar"][0].val() == "20");
619  CHECK(root["bar"][1].val() == "30");
620  CHECK(root["john"].val() == "deere");
621  CHECK(tree.arena() == "says who2030deere"); // the result of serializations to the tree arena
622  // using operator<< instead of operator=, the crash above is avoided:
623  {
624  std::string ok("in_scope");
625  // root["john"] = ryml::to_csubstr(ok); // don't, will dangle
626  wroot["john"] << ryml::to_csubstr(ok); // OK, copy to the tree's arena
627  }
628  CHECK(root["john"].val() == "in_scope"); // OK!
629  // serializing floating points:
630  wroot["float"] << 2.4;
631  // to force a particular precision or float format:
632  // (see sample_float_precision() and sample_formatting())
633  wroot["digits"] << ryml::fmt::real(2.4, /*num_digits*/6, ryml::FTOA_FLOAT);
634  CHECK(tree.arena() == "says who2030deerein_scope2.42.400000"); // the result of serializations to the tree arena
635 
636 
637  //------------------------------------------------------------------
638  // Adding new nodes:
639 
640  // adding a keyval node to a map:
641  CHECK(root.num_children() == 5);
642  wroot["newkeyval"] = "shiny and new"; // using these strings
643  wroot.append_child() << ryml::key("newkeyval (serialized)") << "shiny and new (serialized)"; // serializes and assigns the serialization
644  CHECK(root.num_children() == 7);
645  CHECK(root["newkeyval"].key() == "newkeyval");
646  CHECK(root["newkeyval"].val() == "shiny and new");
647  CHECK(root["newkeyval (serialized)"].key() == "newkeyval (serialized)");
648  CHECK(root["newkeyval (serialized)"].val() == "shiny and new (serialized)");
649  CHECK( ! tree.in_arena(root["newkeyval"].key())); // it's using directly the static string above
650  CHECK( ! tree.in_arena(root["newkeyval"].val())); // it's using directly the static string above
651  CHECK( tree.in_arena(root["newkeyval (serialized)"].key())); // it's using a serialization of the string above
652  CHECK( tree.in_arena(root["newkeyval (serialized)"].val())); // it's using a serialization of the string above
653  // adding a val node to a seq:
654  CHECK(root["bar"].num_children() == 2);
655  wroot["bar"][2] = "oh so nice";
656  wroot["bar"][3] << "oh so nice (serialized)";
657  CHECK(root["bar"].num_children() == 4);
658  CHECK(root["bar"][2].val() == "oh so nice");
659  CHECK(root["bar"][3].val() == "oh so nice (serialized)");
660  // adding a seq node:
661  CHECK(root.num_children() == 7);
662  wroot["newseq"] |= ryml::SEQ;
663  wroot.append_child() << ryml::key("newseq (serialized)") |= ryml::SEQ;
664  CHECK(root.num_children() == 9);
665  CHECK(root["newseq"].num_children() == 0);
666  CHECK(root["newseq"].is_seq());
667  CHECK(root["newseq (serialized)"].num_children() == 0);
668  CHECK(root["newseq (serialized)"].is_seq());
669  // adding a map node:
670  CHECK(root.num_children() == 9);
671  wroot["newmap"] |= ryml::MAP;
672  wroot.append_child() << ryml::key("newmap (serialized)") |= ryml::MAP;
673  CHECK(root.num_children() == 11);
674  CHECK(root["newmap"].num_children() == 0);
675  CHECK(root["newmap"].is_map());
676  CHECK(root["newmap (serialized)"].num_children() == 0);
677  CHECK(root["newmap (serialized)"].is_map());
678  //
679  // When the tree is mutable, operator[] first searches the tree
680  // for the does not mutate the tree until the returned node is
681  // written to.
682  //
683  // Until such time, the NodeRef object keeps in itself the required
684  // information to write to the proper place in the tree. This is
685  // called being in a "seed" state.
686  //
687  // This means that passing a key/index which does not exist will
688  // not mutate the tree, but will instead store (in the node) the
689  // proper place of the tree to be able to do so, if and when it is
690  // required. This is why the node is said to be in "seed" state -
691  // it allows creating the entry in the tree in the future.
692  //
693  // This is a significant difference from eg, the behavior of
694  // std::map, which mutates the map immediately within the call to
695  // operator[].
696  //
697  // All of the points above apply only if the tree is mutable. If
698  // the tree is const, then a NodeRef cannot be obtained from it;
699  // only a ConstNodeRef, which can never be used to mutate the
700  // tree.
701  //
702  CHECK(!root.has_child("I am not nothing"));
703  ryml::NodeRef nothing;
704  CHECK(nothing.invalid()); // invalid because it points at nothing
705  nothing = wroot["I am nothing"];
706  CHECK(!nothing.invalid()); // points at the tree, and a specific place in the tree
707  CHECK(nothing.is_seed()); // ... but nothing is there yet.
708  CHECK(!root.has_child("I am nothing")); // same as above
709  CHECK(!nothing.readable()); // ... and this node cannot be used to
710  // read anything from the tree
711  ryml::NodeRef something = wroot["I am something"];
712  ryml::ConstNodeRef constsomething = wroot["I am something"];
713  CHECK(!root.has_child("I am something")); // same as above
714  CHECK(!something.invalid());
715  CHECK(something.is_seed()); // same as above
716  CHECK(!something.readable()); // same as above
717  CHECK(constsomething.invalid()); // NOTE: because a ConstNodeRef cannot be
718  // used to mutate a tree, it is only valid()
719  // if it is pointing at an existing node.
720  something = "indeed"; // this will commit the seed to the tree, mutating at the proper place
721  CHECK(root.has_child("I am something"));
722  CHECK(root["I am something"].val() == "indeed");
723  CHECK(!something.invalid()); // it was already valid
724  CHECK(!something.is_seed()); // now the tree has this node, so the
725  // ref is no longer a seed
726  CHECK(something.readable()); // and it is now readable
727  //
728  // now the constref is also valid (but it needs to be reassigned):
729  ryml::ConstNodeRef constsomethingnew = wroot["I am something"];
730  CHECK(!constsomethingnew.invalid());
731  CHECK(constsomethingnew.readable());
732  // note that the old constref is now stale, because it only keeps
733  // the state at creation:
734  CHECK(constsomething.invalid());
735  CHECK(!constsomething.readable());
736  //
737  // -----------------------------------------------------------
738  // Remember: a seed node cannot be used to read from the tree!
739  // -----------------------------------------------------------
740  //
741  // The seed node needs to be created and become readable first.
742  //
743  // Trying to invoke any tree-reading method on a node that is not
744  // readable will cause an assertion (see RYML_USE_ASSERT).
745  //
746  // It is your responsibility to verify that the preconditions are
747  // met. If you are not sure about the structure of your data,
748  // write your code defensively to signify your full intent:
749  //
750  ryml::NodeRef wbar = wroot["bar"];
751  if(wbar.readable() && wbar.is_seq()) // .is_seq() requires .readable()
752  {
753  CHECK(wbar[0].readable() && wbar[0].val() == "20");
754  CHECK( ! wbar[100].readable());
755  CHECK( ! wbar[100].readable() || wbar[100].val() == "100"); // <- no crash because it is not .readable(), so never tries to call .val()
756  // this would work as well:
757  CHECK( ! wbar[0].is_seed() && wbar[0].val() == "20");
758  CHECK(wbar[100].is_seed() || wbar[100].val() == "100");
759  }
760 
761 
762  //------------------------------------------------------------------
763  // .operator[]() vs .at()
764 
765  // (Const)NodeRef::operator[]() is an analogue to std::vector::operator[].
766  // (Const)NodeRef::at() is an analogue to std::vector::at()
767  //
768  // at() will always check the subject node is .readable().
769  //
770  // [] is meant for the happy path, and unverified in Release
771  // builds.
772  {
773  // in this example we will be checking errors, so set up a
774  // temporary error handler to catch them:
776  // instantiate the tree after errh
777  ryml::Tree err_tree = ryml::parse_in_arena("{foo: bar}");
778  // ... so that the tree uses the current callbacks:
779  CHECK(err_tree.callbacks() == errh.callbacks());
780  // node does not exist, only a seed node
781  ryml::NodeRef seed_node = err_tree["this"];
782  // ... therefore not .readable()
783  CHECK(!seed_node.readable());
784  // using .at() reliably produces an error:
785  CHECK(errh.check_error_occurs([&]{
786  return seed_node.at("is").at("an").at("invalid").at("operation");
787  // ^
788  // error occurs here because it is unreadable
789  }));
790  // ... but using [] fails only when RYML_USE_ASSERT is
791  // defined. otherwise, it's the dreaded Undefined Behavior:
792  CHECK(errh.check_assertion_occurs([&]{
793  return seed_node["is"]["an"]["invalid"]["operation"];
794  // ^
795  // assertion occurs here because it is unreadable
796  }));
797  }
798 
799 
800  //------------------------------------------------------------------
801  // Emitting:
802 
803  ryml::csubstr expected_result = R"(foo: says who
804 bar: [20,30,oh so nice,oh so nice (serialized)]
805 john: in_scope
806 float: 2.4
807 digits: 2.400000
808 newkeyval: shiny and new
809 newkeyval (serialized): shiny and new (serialized)
810 newseq: []
811 newseq (serialized): []
812 newmap: {}
813 newmap (serialized): {}
814 I am something: indeed
815 )";
816 
817  // emit to a FILE*
818  ryml::emit_yaml(tree, stdout);
819  // emit to a stream
820  std::stringstream ss;
821  ss << tree;
822  std::string stream_result = ss.str();
823  // emit to a buffer:
824  std::string str_result = ryml::emitrs_yaml<std::string>(tree);
825  // can emit to any given buffer:
826  char buf[1024];
827  ryml::csubstr buf_result = ryml::emit_yaml(tree, buf);
828  // now check
829  CHECK(buf_result == expected_result);
830  CHECK(str_result == expected_result);
831  CHECK(stream_result == expected_result);
832  // There are many possibilities to emit to buffer;
833  // please look at the emit sample functions below.
834 
835 
836  //------------------------------------------------------------------
837  // ConstNodeRef vs NodeRef
838 
839  ryml::NodeRef noderef = tree["bar"][0];
840  ryml::ConstNodeRef constnoderef = tree["bar"][0];
841 
842  // ConstNodeRef cannot be used to mutate the tree:
843  //constnoderef = "21"; // compile error
844  //constnoderef << "22"; // compile error
845  // ... but a NodeRef can:
846  noderef = "21"; // ok, can assign because it's not const
847  CHECK(tree["bar"][0].val() == "21");
848  noderef << "22"; // ok, can serialize and assign because it's not const
849  CHECK(tree["bar"][0].val() == "22");
850 
851  // it is not possible to obtain a NodeRef from a ConstNodeRef:
852  // noderef = constnoderef; // compile error
853 
854  // it is always possible to obtain a ConstNodeRef from a NodeRef:
855  constnoderef = noderef; // ok can assign const <- nonconst
856 
857  // If a tree is const, then only ConstNodeRef's can be
858  // obtained from that tree:
859  ryml::Tree const& consttree = tree;
860  //noderef = consttree["bar"][0]; // compile error
861  noderef = tree["bar"][0]; // ok
862  constnoderef = consttree["bar"][0]; // ok
863 
864  // ConstNodeRef and NodeRef can be compared for equality.
865  // Equality means they point at the same node.
866  CHECK(constnoderef == noderef);
867  CHECK(!(constnoderef != noderef));
868 
869 
870  //------------------------------------------------------------------
871  // Getting the location of nodes in the source:
872  //
873  // Location tracking is opt-in:
874  ryml::EventHandlerTree evt_handler = {};
875  ryml::Parser parser(&evt_handler, ryml::ParserOptions().locations(true));
876  // Now the parser will start by building the accelerator structure:
877  ryml::Tree tree2 = parse_in_arena(&parser, "expected.yml", expected_result);
878  // ... and use it when querying
879  ryml::ConstNodeRef subject_node = tree2["bar"][1];
880  CHECK(subject_node.val() == "30");
881  ryml::Location loc = parser.location(subject_node);
882  CHECK(parser.location_contents(loc).begins_with("30"));
883  CHECK(loc.line == 1u);
884  CHECK(loc.col == 9u);
885  // For further details in location tracking,
886  // refer to the sample function below.
887 
888  //------------------------------------------------------------------
889  // Dealing with UTF8
890  ryml::Tree langs = ryml::parse_in_arena(R"(
891 en: Planet (Gas)
892 fr: Planète (Gazeuse)
893 ru: Планета (Газ)
894 ja: 惑星(ガス)
895 zh: 行星(气体)
896 # UTF8 decoding only happens in double-quoted strings,
897 # as per the YAML standard
898 decode this: "\u263A \xE2\x98\xBA"
899 and this as well: "\u2705 \U0001D11E"
900 not decoded: '\u263A \xE2\x98\xBA'
901 neither this: '\u2705 \U0001D11E'
902 )");
903  // in-place UTF8 just works:
904  CHECK(langs["en"].val() == "Planet (Gas)");
905  CHECK(langs["fr"].val() == "Planète (Gazeuse)");
906  CHECK(langs["ru"].val() == "Планета (Газ)");
907  CHECK(langs["ja"].val() == "惑星(ガス)");
908  CHECK(langs["zh"].val() == "行星(气体)");
909  // and \x \u \U codepoints are decoded, but only when they appear
910  // inside double-quoted strings, as dictated by the YAML
911  // standard:
912  CHECK(langs["decode this"].val() == "☺ ☺");
913  CHECK(langs["and this as well"].val() == "✅ 𝄞");
914  CHECK(langs["not decoded"].val() == "\\u263A \\xE2\\x98\\xBA");
915  CHECK(langs["neither this"].val() == "\\u2705 \\U0001D11E");
916 }
917 
918 
919 //-----------------------------------------------------------------------------
920 
921 /** demonstrate usage of ryml::substr and ryml::csubstr
922  *
923  * These types are imported from the c4core library into the ryml
924  * namespace You may have noticed above the use of a `csubstr`
925  * class. This class is defined in another library,
926  * [c4core](https://github.com/biojppm/c4core), which is imported by
927  * ryml. This is a library I use with my projects consisting of
928  * multiplatform low-level utilities. One of these is `c4::csubstr`
929  * (the name comes from "constant substring") which is a non-owning
930  * read-only string view, with many methods that make it practical to
931  * use (I would certainly argue more practical than `std::string`). In
932  * fact, `c4::csubstr` and its writeable counterpart `c4::substr` are
933  * the workhorses of the ryml parsing and serialization code.
934  *
935  * @see doc_substr */
937 {
938  // substr is a mutable view: pointer and length to a string in memory.
939  // csubstr is a const-substr (immutable).
940 
941  // construct from explicit args
942  {
943  const char foobar_str[] = "foobar";
944  auto s = ryml::csubstr(foobar_str, strlen(foobar_str));
945  CHECK(s == "foobar");
946  CHECK(s.size() == 6);
947  CHECK(s.data() == foobar_str);
948  CHECK(s.size() == s.len);
949  CHECK(s.data() == s.str);
950  }
951 
952  // construct from a string array
953  {
954  const char foobar_str[] = "foobar";
955  ryml::csubstr s = foobar_str;
956  CHECK(s == "foobar");
957  CHECK(s != "foobar0");
958  CHECK(s.size() == 6); // does not include the terminating \0
959  CHECK(s.data() == foobar_str);
960  CHECK(s.size() == s.len);
961  CHECK(s.data() == s.str);
962  }
963  // you can also declare directly in-place from an array:
964  {
965  ryml::csubstr s = "foobar";
966  CHECK(s == "foobar");
967  CHECK(s != "foobar0");
968  CHECK(s.size() == 6);
969  CHECK(s.size() == s.len);
970  CHECK(s.data() == s.str);
971  }
972 
973  // construct from a C-string:
974  //
975  // Since the input is only a pointer, the string length can only
976  // be found with a call to strlen(). To make this cost evident, we
977  // require calling to_csubstr():
978  {
979  const char *foobar_str = "foobar";
980  ryml::csubstr s = ryml::to_csubstr(foobar_str);
981  CHECK(s == "foobar");
982  CHECK(s != "foobar0");
983  CHECK(s.size() == 6);
984  CHECK(s.size() == s.len);
985  CHECK(s.data() == s.str);
986  }
987 
988  // construct from a std::string: same approach as above.
989  // requires inclusion of the <ryml/std/string.hpp> header
990  // or of the umbrella header <ryml_std.hpp>.
991  //
992  // not including <string> in the default header is a deliberate
993  // design choice to avoid including the heavy std:: allocation
994  // machinery
995  {
996  std::string foobar_str = "foobar";
997  ryml::csubstr s = ryml::to_csubstr(foobar_str); // defined in <ryml/std/string.hpp>
998  CHECK(s == "foobar");
999  CHECK(s != "foobar0");
1000  CHECK(s.size() == 6);
1001  CHECK(s.size() == s.len);
1002  CHECK(s.data() == s.str);
1003  }
1004 
1005  // convert substr -> csubstr
1006  {
1007  char buf[] = "foo";
1008  ryml::substr foo = buf;
1009  CHECK(foo.len == 3);
1010  CHECK(foo.data() == buf);
1011  ryml::csubstr cfoo = foo;
1012  CHECK(cfoo.data() == buf);
1013  }
1014  // cannot convert csubstr -> substr:
1015  {
1016  // ryml::substr foo2 = cfoo; // compile error: cannot write to csubstr
1017  }
1018 
1019  // construct from char[]/const char[]: mutable vs immutable memory
1020  {
1021  char const foobar_str_ro[] = "foobar"; // ro := read-only
1022  char foobar_str_rw[] = "foobar"; // rw := read-write
1023  static_assert(std::is_array<decltype(foobar_str_ro)>::value, "this is an array");
1024  static_assert(std::is_array<decltype(foobar_str_rw)>::value, "this is an array");
1025  // csubstr <- read-only memory
1026  {
1027  ryml::csubstr foobar = foobar_str_ro;
1028  CHECK(foobar.data() == foobar_str_ro);
1029  CHECK(foobar.size() == strlen(foobar_str_ro));
1030  CHECK(foobar == "foobar"); // AKA strcmp
1031  }
1032  // csubstr <- read-write memory: you can create an immutable csubstr from mutable memory
1033  {
1034  ryml::csubstr foobar = foobar_str_rw;
1035  CHECK(foobar.data() == foobar_str_rw);
1036  CHECK(foobar.size() == strlen(foobar_str_rw));
1037  CHECK(foobar == "foobar"); // AKA strcmp
1038  }
1039  // substr <- read-write memory.
1040  {
1041  ryml::substr foobar = foobar_str_rw;
1042  CHECK(foobar.data() == foobar_str_rw);
1043  CHECK(foobar.size() == strlen(foobar_str_rw));
1044  CHECK(foobar == "foobar"); // AKA strcmp
1045  }
1046  // substr <- ro is impossible.
1047  {
1048  //ryml::substr foobar = foobar_str_ro; // compile error!
1049  }
1050  }
1051 
1052  // construct from char*/const char*: mutable vs immutable memory.
1053  // use to_substr()/to_csubstr()
1054  {
1055  char const* foobar_str_ro = "foobar"; // ro := read-only
1056  char foobar_str_rw_[] = "foobar"; // rw := read-write
1057  char * foobar_str_rw = foobar_str_rw_; // rw := read-write
1058  static_assert(!std::is_array<decltype(foobar_str_ro)>::value, "this is a decayed pointer");
1059  static_assert(!std::is_array<decltype(foobar_str_rw)>::value, "this is a decayed pointer");
1060  // csubstr <- read-only memory
1061  {
1062  //ryml::csubstr foobar = foobar_str_ro; // compile error: length is not known
1063  ryml::csubstr foobar = ryml::to_csubstr(foobar_str_ro);
1064  CHECK(foobar.data() == foobar_str_ro);
1065  CHECK(foobar.size() == strlen(foobar_str_ro));
1066  CHECK(foobar == "foobar"); // AKA strcmp
1067  }
1068  // csubstr <- read-write memory: you can create an immutable csubstr from mutable memory
1069  {
1070  ryml::csubstr foobar = ryml::to_csubstr(foobar_str_rw);
1071  CHECK(foobar.data() == foobar_str_rw);
1072  CHECK(foobar.size() == strlen(foobar_str_rw));
1073  CHECK(foobar == "foobar"); // AKA strcmp
1074  }
1075  // substr <- read-write memory.
1076  {
1077  ryml::substr foobar = ryml::to_substr(foobar_str_rw);
1078  CHECK(foobar.data() == foobar_str_rw);
1079  CHECK(foobar.size() == strlen(foobar_str_rw));
1080  CHECK(foobar == "foobar"); // AKA strcmp
1081  }
1082  // substr <- read-only is impossible.
1083  {
1084  //ryml::substr foobar = ryml::to_substr(foobar_str_ro); // compile error!
1085  }
1086  }
1087 
1088  // substr is mutable, without changing the size:
1089  {
1090  char buf[] = "foobar";
1091  ryml::substr foobar = buf;
1092  CHECK(foobar == "foobar");
1093  foobar[0] = 'F'; CHECK(foobar == "Foobar");
1094  foobar.back() = 'R'; CHECK(foobar == "FoobaR");
1095  foobar.reverse(); CHECK(foobar == "RabooF");
1096  foobar.reverse(); CHECK(foobar == "FoobaR");
1097  foobar.reverse_sub(1, 4); CHECK(foobar == "FabooR");
1098  foobar.reverse_sub(1, 4); CHECK(foobar == "FoobaR");
1099  foobar.reverse_range(2, 5); CHECK(foobar == "FoaboR");
1100  foobar.reverse_range(2, 5); CHECK(foobar == "FoobaR");
1101  foobar.replace('o', '0'); CHECK(foobar == "F00baR");
1102  foobar.replace('a', '_'); CHECK(foobar == "F00b_R");
1103  foobar.replace("_0b", 'a'); CHECK(foobar == "FaaaaR");
1104  foobar.toupper(); CHECK(foobar == "FAAAAR");
1105  foobar.tolower(); CHECK(foobar == "faaaar");
1106  foobar.fill('.'); CHECK(foobar == "......");
1107  // see also:
1108  // - .erase()
1109  // - .replace_all()
1110  }
1111 
1112  // sub-views
1113  {
1114  ryml::csubstr s = "fooFOObarBAR";
1115  CHECK(s.len == 12u);
1116  // sub(): <- first,[num]
1117  CHECK(s.sub(0) == "fooFOObarBAR");
1118  CHECK(s.sub(0, 12) == "fooFOObarBAR");
1119  CHECK(s.sub(0, 3) == "foo" );
1120  CHECK(s.sub(3) == "FOObarBAR");
1121  CHECK(s.sub(3, 3) == "FOO" );
1122  CHECK(s.sub(6) == "barBAR");
1123  CHECK(s.sub(6, 3) == "bar" );
1124  CHECK(s.sub(9) == "BAR");
1125  CHECK(s.sub(9, 3) == "BAR");
1126  // first(): <- length
1127  CHECK(s.first(0) == "" );
1128  CHECK(s.first(1) == "f" );
1129  CHECK(s.first(2) != "f" );
1130  CHECK(s.first(2) == "fo" );
1131  CHECK(s.first(3) == "foo");
1132  // last(): <- length
1133  CHECK(s.last(0) == "");
1134  CHECK(s.last(1) == "R");
1135  CHECK(s.last(2) == "AR");
1136  CHECK(s.last(3) == "BAR");
1137  // range(): <- first, last
1138  CHECK(s.range(0, 12) == "fooFOObarBAR");
1139  CHECK(s.range(1, 12) == "ooFOObarBAR");
1140  CHECK(s.range(1, 11) == "ooFOObarBA" );
1141  CHECK(s.range(2, 10) == "oFOObarB" );
1142  CHECK(s.range(3, 9) == "FOObar" );
1143  // offs(): offset from beginning, end
1144  CHECK(s.offs(0, 0) == "fooFOObarBAR");
1145  CHECK(s.offs(1, 0) == "ooFOObarBAR");
1146  CHECK(s.offs(1, 1) == "ooFOObarBA" );
1147  CHECK(s.offs(2, 1) == "oFOObarBA" );
1148  CHECK(s.offs(2, 2) == "oFOObarB" );
1149  CHECK(s.offs(3, 3) == "FOObar" );
1150  // right_of(): <- pos, include_pos
1151  CHECK(s.right_of(0, true) == "fooFOObarBAR");
1152  CHECK(s.right_of(0, false) == "ooFOObarBAR");
1153  CHECK(s.right_of(1, true) == "ooFOObarBAR");
1154  CHECK(s.right_of(1, false) == "oFOObarBAR");
1155  CHECK(s.right_of(2, true) == "oFOObarBAR");
1156  CHECK(s.right_of(2, false) == "FOObarBAR");
1157  CHECK(s.right_of(3, true) == "FOObarBAR");
1158  CHECK(s.right_of(3, false) == "OObarBAR");
1159  // left_of() <- pos, include_pos
1160  CHECK(s.left_of(12, false) == "fooFOObarBAR");
1161  CHECK(s.left_of(11, true) == "fooFOObarBAR");
1162  CHECK(s.left_of(11, false) == "fooFOObarBA" );
1163  CHECK(s.left_of(10, true) == "fooFOObarBA" );
1164  CHECK(s.left_of(10, false) == "fooFOObarB" );
1165  CHECK(s.left_of( 9, true) == "fooFOObarB" );
1166  CHECK(s.left_of( 9, false) == "fooFOObar" );
1167  // left_of(),right_of() <- substr
1168  ryml::csubstr FOO = s.sub(3, 3);
1169  CHECK(s.is_super(FOO)); // required for the following
1170  CHECK(s.left_of(FOO) == "foo");
1171  CHECK(s.right_of(FOO) == "barBAR");
1172  }
1173 
1174  // printing a substr/csubstr using printf-like
1175  {
1176  ryml::csubstr s = "some substring";
1177  ryml::csubstr some = s.first(4);
1178  CHECK(some == "some");
1179  CHECK(s == "some substring");
1180  // To print a csubstr using printf(), use the %.*s format specifier:
1181  {
1182  char result[32] = {0};
1183  std::snprintf(result, sizeof(result), "%.*s", (int)some.len, some.str);
1184  printf("~~~%s~~~\n", result);
1185  CHECK(ryml::to_csubstr((const char*)result) == "some");
1186  CHECK(ryml::to_csubstr((const char*)result) == some);
1187  }
1188  // But NOTE: because this is a string view type, in general
1189  // the C-string is NOT zero terminated. So NEVER print it
1190  // directly, or it will overflow past the end of the given
1191  // substr, with a potential unbounded access. For example,
1192  // this is bad:
1193  {
1194  char result[32] = {0};
1195  std::snprintf(result, sizeof(result), "%s", some.str); // ERROR! do not print the c-string directly
1196  CHECK(ryml::to_csubstr((const char*)result) == "some substring");
1197  CHECK(ryml::to_csubstr((const char*)result) == s);
1198  }
1199  }
1200 
1201  // printing a substr/csubstr using ostreams
1202  {
1203  ryml::csubstr s = "some substring";
1204  ryml::csubstr some = s.first(4);
1205  CHECK(some == "some");
1206  CHECK(s == "some substring");
1207  // simple! just use plain operator<<
1208  {
1209  std::stringstream ss;
1210  ss << s;
1211  CHECK(ss.str() == "some substring"); // as expected
1212  CHECK(ss.str() == s); // as expected
1213  }
1214  // But NOTE: because this is a string view type, in general
1215  // the C-string is NOT zero terminated. So NEVER print it
1216  // directly, or it will overflow past the end of the given
1217  // substr, with a potential unbounded access. For example,
1218  // this is bad:
1219  {
1220  std::stringstream ss;
1221  ss << some.str; // ERROR! do not print the c-string directly
1222  CHECK(ss.str() == "some substring"); // NOT "some"
1223  CHECK(ss.str() == s); // NOT some
1224  }
1225  // this is also bad (the same)
1226  {
1227  std::stringstream ss;
1228  ss << some.data(); // ERROR! do not print the c-string directly
1229  CHECK(ss.str() == "some substring"); // NOT "some"
1230  CHECK(ss.str() == s); // NOT some
1231  }
1232  // this is ok:
1233  {
1234  std::stringstream ss;
1235  ss << some;
1236  CHECK(ss.str() == "some"); // ok
1237  CHECK(ss.str() == some); // ok
1238  }
1239  }
1240 
1241  // is_sub(),is_super()
1242  {
1243  ryml::csubstr foobar = "foobar";
1244  ryml::csubstr foo = foobar.first(3);
1245  CHECK(foo.is_sub(foobar));
1246  CHECK(foo.is_sub(foo));
1247  CHECK(!foo.is_super(foobar));
1248  CHECK(!foobar.is_sub(foo));
1249  // identity comparison is true:
1250  CHECK(foo.is_super(foo));
1251  CHECK(foo.is_sub(foo));
1252  CHECK(foobar.is_sub(foobar));
1253  CHECK(foobar.is_super(foobar));
1254  }
1255 
1256  // overlaps()
1257  {
1258  ryml::csubstr foobar = "foobar";
1259  ryml::csubstr foo = foobar.first(3);
1260  ryml::csubstr oba = foobar.offs(2, 1);
1261  ryml::csubstr abc = "abc";
1262  CHECK(foobar.overlaps(foo));
1263  CHECK(foobar.overlaps(oba));
1264  CHECK(foo.overlaps(foobar));
1265  CHECK(foo.overlaps(oba));
1266  CHECK(!foo.overlaps(abc));
1267  CHECK(!abc.overlaps(foo));
1268  }
1269 
1270  // triml(): trim characters from the left
1271  // trimr(): trim characters from the right
1272  // trim(): trim characters from left AND right
1273  {
1274  CHECK(ryml::csubstr(" \t\n\rcontents without whitespace\t \n\r").trim("\t \n\r") == "contents without whitespace");
1275  ryml::csubstr aaabbb = "aaabbb";
1276  ryml::csubstr aaa___bbb = "aaa___bbb";
1277  // trim a character:
1278  CHECK(aaabbb.triml('a') == aaabbb.last(3)); // bbb
1279  CHECK(aaabbb.trimr('a') == aaabbb);
1280  CHECK(aaabbb.trim ('a') == aaabbb.last(3)); // bbb
1281  CHECK(aaabbb.triml('b') == aaabbb);
1282  CHECK(aaabbb.trimr('b') == aaabbb.first(3)); // aaa
1283  CHECK(aaabbb.trim ('b') == aaabbb.first(3)); // aaa
1284  CHECK(aaabbb.triml('c') == aaabbb);
1285  CHECK(aaabbb.trimr('c') == aaabbb);
1286  CHECK(aaabbb.trim ('c') == aaabbb);
1287  CHECK(aaa___bbb.triml('a') == aaa___bbb.last(6)); // ___bbb
1288  CHECK(aaa___bbb.trimr('a') == aaa___bbb);
1289  CHECK(aaa___bbb.trim ('a') == aaa___bbb.last(6)); // ___bbb
1290  CHECK(aaa___bbb.triml('b') == aaa___bbb);
1291  CHECK(aaa___bbb.trimr('b') == aaa___bbb.first(6)); // aaa___
1292  CHECK(aaa___bbb.trim ('b') == aaa___bbb.first(6)); // aaa___
1293  CHECK(aaa___bbb.triml('c') == aaa___bbb);
1294  CHECK(aaa___bbb.trimr('c') == aaa___bbb);
1295  CHECK(aaa___bbb.trim ('c') == aaa___bbb);
1296  // trim ANY of the characters:
1297  CHECK(aaabbb.triml("ab") == "");
1298  CHECK(aaabbb.trimr("ab") == "");
1299  CHECK(aaabbb.trim ("ab") == "");
1300  CHECK(aaabbb.triml("ba") == "");
1301  CHECK(aaabbb.trimr("ba") == "");
1302  CHECK(aaabbb.trim ("ba") == "");
1303  CHECK(aaabbb.triml("cd") == aaabbb);
1304  CHECK(aaabbb.trimr("cd") == aaabbb);
1305  CHECK(aaabbb.trim ("cd") == aaabbb);
1306  CHECK(aaa___bbb.triml("ab") == aaa___bbb.last(6)); // ___bbb
1307  CHECK(aaa___bbb.triml("ba") == aaa___bbb.last(6)); // ___bbb
1308  CHECK(aaa___bbb.triml("cd") == aaa___bbb);
1309  CHECK(aaa___bbb.trimr("ab") == aaa___bbb.first(6)); // aaa___
1310  CHECK(aaa___bbb.trimr("ba") == aaa___bbb.first(6)); // aaa___
1311  CHECK(aaa___bbb.trimr("cd") == aaa___bbb);
1312  CHECK(aaa___bbb.trim ("ab") == aaa___bbb.range(3, 6)); // ___
1313  CHECK(aaa___bbb.trim ("ba") == aaa___bbb.range(3, 6)); // ___
1314  CHECK(aaa___bbb.trim ("cd") == aaa___bbb);
1315  }
1316 
1317  // unquoted():
1318  {
1319  CHECK(ryml::csubstr(R"('this is is single quoted')").unquoted() == "this is is single quoted");
1320  CHECK(ryml::csubstr(R"("this is is double quoted")").unquoted() == "this is is double quoted");
1321  }
1322 
1323  // stripl(): remove pattern from the left
1324  // stripr(): remove pattern from the right
1325  {
1326  ryml::csubstr abc___cba = "abc___cba";
1327  ryml::csubstr abc___abc = "abc___abc";
1328  CHECK(abc___cba.stripl("abc") == abc___cba.last(6)); // ___cba
1329  CHECK(abc___cba.stripr("abc") == abc___cba);
1330  CHECK(abc___cba.stripl("ab") == abc___cba.last(7)); // c___cba
1331  CHECK(abc___cba.stripr("ab") == abc___cba);
1332  CHECK(abc___cba.stripl("a") == abc___cba.last(8)); // bc___cba, same as triml('a')
1333  CHECK(abc___cba.stripr("a") == abc___cba.first(8));
1334  CHECK(abc___abc.stripl("abc") == abc___abc.last(6)); // ___abc
1335  CHECK(abc___abc.stripr("abc") == abc___abc.first(6)); // abc___
1336  CHECK(abc___abc.stripl("ab") == abc___abc.last(7)); // c___cba
1337  CHECK(abc___abc.stripr("ab") == abc___abc);
1338  CHECK(abc___abc.stripl("a") == abc___abc.last(8)); // bc___cba, same as triml('a')
1339  CHECK(abc___abc.stripr("a") == abc___abc);
1340  }
1341 
1342  // begins_with()/ends_with()
1343  // begins_with_any()/ends_with_any()
1344  {
1345  ryml::csubstr s = "foobar123";
1346  // char overloads
1347  CHECK(s.begins_with('f'));
1348  CHECK(s.ends_with('3'));
1349  CHECK(!s.ends_with('2'));
1350  CHECK(!s.ends_with('o'));
1351  // char[] overloads
1352  CHECK(s.begins_with("foobar"));
1353  CHECK(s.begins_with("foo"));
1354  CHECK(s.begins_with_any("foo"));
1355  CHECK(!s.begins_with("oof"));
1356  CHECK(s.begins_with_any("oof"));
1357  CHECK(s.ends_with("23"));
1358  CHECK(s.ends_with("123"));
1359  CHECK(s.ends_with_any("123"));
1360  CHECK(!s.ends_with("321"));
1361  CHECK(s.ends_with_any("231"));
1362  }
1363 
1364  // select()
1365  {
1366  ryml::csubstr s = "0123456789";
1367  CHECK(s.select('0') == s.sub(0, 1));
1368  CHECK(s.select('1') == s.sub(1, 1));
1369  CHECK(s.select('2') == s.sub(2, 1));
1370  CHECK(s.select('8') == s.sub(8, 1));
1371  CHECK(s.select('9') == s.sub(9, 1));
1372  CHECK(s.select("0123") == s.range(0, 4));
1373  CHECK(s.select("012" ) == s.range(0, 3));
1374  CHECK(s.select("01" ) == s.range(0, 2));
1375  CHECK(s.select("0" ) == s.range(0, 1));
1376  CHECK(s.select( "123") == s.range(1, 4));
1377  CHECK(s.select( "23") == s.range(2, 4));
1378  CHECK(s.select( "3") == s.range(3, 4));
1379  }
1380 
1381  // find()
1382  {
1383  ryml::csubstr s012345 = "012345";
1384  // find single characters:
1385  CHECK(s012345.find('a') == ryml::npos);
1386  CHECK(s012345.find('0' ) == 0u);
1387  CHECK(s012345.find('0', 1u) == ryml::npos);
1388  CHECK(s012345.find('1' ) == 1u);
1389  CHECK(s012345.find('1', 2u) == ryml::npos);
1390  CHECK(s012345.find('2' ) == 2u);
1391  CHECK(s012345.find('2', 3u) == ryml::npos);
1392  CHECK(s012345.find('3' ) == 3u);
1393  CHECK(s012345.find('3', 4u) == ryml::npos);
1394  // find patterns
1395  CHECK(s012345.find("ab" ) == ryml::npos);
1396  CHECK(s012345.find("01" ) == 0u);
1397  CHECK(s012345.find("01", 1u) == ryml::npos);
1398  CHECK(s012345.find("12" ) == 1u);
1399  CHECK(s012345.find("12", 2u) == ryml::npos);
1400  CHECK(s012345.find("23" ) == 2u);
1401  CHECK(s012345.find("23", 3u) == ryml::npos);
1402  }
1403 
1404  // count(): count the number of occurrences of a character
1405  {
1406  ryml::csubstr buf = "00110022003300440055";
1407  CHECK(buf.count('1' ) == 2u);
1408  CHECK(buf.count('1', 0u) == 2u);
1409  CHECK(buf.count('1', 1u) == 2u);
1410  CHECK(buf.count('1', 2u) == 2u);
1411  CHECK(buf.count('1', 3u) == 1u);
1412  CHECK(buf.count('1', 4u) == 0u);
1413  CHECK(buf.count('1', 5u) == 0u);
1414  CHECK(buf.count('0' ) == 10u);
1415  CHECK(buf.count('0', 0u) == 10u);
1416  CHECK(buf.count('0', 1u) == 9u);
1417  CHECK(buf.count('0', 2u) == 8u);
1418  CHECK(buf.count('0', 3u) == 8u);
1419  CHECK(buf.count('0', 4u) == 8u);
1420  CHECK(buf.count('0', 5u) == 7u);
1421  CHECK(buf.count('0', 6u) == 6u);
1422  CHECK(buf.count('0', 7u) == 6u);
1423  CHECK(buf.count('0', 8u) == 6u);
1424  CHECK(buf.count('0', 9u) == 5u);
1425  CHECK(buf.count('0', 10u) == 4u);
1426  CHECK(buf.count('0', 11u) == 4u);
1427  CHECK(buf.count('0', 12u) == 4u);
1428  CHECK(buf.count('0', 13u) == 3u);
1429  CHECK(buf.count('0', 14u) == 2u);
1430  CHECK(buf.count('0', 15u) == 2u);
1431  CHECK(buf.count('0', 16u) == 2u);
1432  CHECK(buf.count('0', 17u) == 1u);
1433  CHECK(buf.count('0', 18u) == 0u);
1434  CHECK(buf.count('0', 19u) == 0u);
1435  CHECK(buf.count('0', 20u) == 0u);
1436  }
1437 
1438  // first_of(),last_of()
1439  {
1440  ryml::csubstr s012345 = "012345";
1441  CHECK(s012345.first_of('a') == ryml::npos);
1442  CHECK(s012345.first_of("ab") == ryml::npos);
1443  CHECK(s012345.first_of('0') == 0u);
1444  CHECK(s012345.first_of("0") == 0u);
1445  CHECK(s012345.first_of("01") == 0u);
1446  CHECK(s012345.first_of("10") == 0u);
1447  CHECK(s012345.first_of("012") == 0u);
1448  CHECK(s012345.first_of("210") == 0u);
1449  CHECK(s012345.first_of("0123") == 0u);
1450  CHECK(s012345.first_of("3210") == 0u);
1451  CHECK(s012345.first_of("01234") == 0u);
1452  CHECK(s012345.first_of("43210") == 0u);
1453  CHECK(s012345.first_of("012345") == 0u);
1454  CHECK(s012345.first_of("543210") == 0u);
1455  CHECK(s012345.first_of('5') == 5u);
1456  CHECK(s012345.first_of("5") == 5u);
1457  CHECK(s012345.first_of("45") == 4u);
1458  CHECK(s012345.first_of("54") == 4u);
1459  CHECK(s012345.first_of("345") == 3u);
1460  CHECK(s012345.first_of("543") == 3u);
1461  CHECK(s012345.first_of("2345") == 2u);
1462  CHECK(s012345.first_of("5432") == 2u);
1463  CHECK(s012345.first_of("12345") == 1u);
1464  CHECK(s012345.first_of("54321") == 1u);
1465  CHECK(s012345.first_of("012345") == 0u);
1466  CHECK(s012345.first_of("543210") == 0u);
1467  CHECK(s012345.first_of('0', 6u) == ryml::npos);
1468  CHECK(s012345.first_of('5', 6u) == ryml::npos);
1469  CHECK(s012345.first_of("012345", 6u) == ryml::npos);
1470  //
1471  CHECK(s012345.last_of('a') == ryml::npos);
1472  CHECK(s012345.last_of("ab") == ryml::npos);
1473  CHECK(s012345.last_of('0') == 0u);
1474  CHECK(s012345.last_of("0") == 0u);
1475  CHECK(s012345.last_of("01") == 1u);
1476  CHECK(s012345.last_of("10") == 1u);
1477  CHECK(s012345.last_of("012") == 2u);
1478  CHECK(s012345.last_of("210") == 2u);
1479  CHECK(s012345.last_of("0123") == 3u);
1480  CHECK(s012345.last_of("3210") == 3u);
1481  CHECK(s012345.last_of("01234") == 4u);
1482  CHECK(s012345.last_of("43210") == 4u);
1483  CHECK(s012345.last_of("012345") == 5u);
1484  CHECK(s012345.last_of("543210") == 5u);
1485  CHECK(s012345.last_of('5') == 5u);
1486  CHECK(s012345.last_of("5") == 5u);
1487  CHECK(s012345.last_of("45") == 5u);
1488  CHECK(s012345.last_of("54") == 5u);
1489  CHECK(s012345.last_of("345") == 5u);
1490  CHECK(s012345.last_of("543") == 5u);
1491  CHECK(s012345.last_of("2345") == 5u);
1492  CHECK(s012345.last_of("5432") == 5u);
1493  CHECK(s012345.last_of("12345") == 5u);
1494  CHECK(s012345.last_of("54321") == 5u);
1495  CHECK(s012345.last_of("012345") == 5u);
1496  CHECK(s012345.last_of("543210") == 5u);
1497  CHECK(s012345.last_of('0', 6u) == 0u);
1498  CHECK(s012345.last_of('5', 6u) == 5u);
1499  CHECK(s012345.last_of("012345", 6u) == 5u);
1500  }
1501 
1502  // first_not_of(), last_not_of()
1503  {
1504  ryml::csubstr s012345 = "012345";
1505  CHECK(s012345.first_not_of('a') == 0u);
1506  CHECK(s012345.first_not_of("ab") == 0u);
1507  CHECK(s012345.first_not_of('0') == 1u);
1508  CHECK(s012345.first_not_of("0") == 1u);
1509  CHECK(s012345.first_not_of("01") == 2u);
1510  CHECK(s012345.first_not_of("10") == 2u);
1511  CHECK(s012345.first_not_of("012") == 3u);
1512  CHECK(s012345.first_not_of("210") == 3u);
1513  CHECK(s012345.first_not_of("0123") == 4u);
1514  CHECK(s012345.first_not_of("3210") == 4u);
1515  CHECK(s012345.first_not_of("01234") == 5u);
1516  CHECK(s012345.first_not_of("43210") == 5u);
1517  CHECK(s012345.first_not_of("012345") == ryml::npos);
1518  CHECK(s012345.first_not_of("543210") == ryml::npos);
1519  CHECK(s012345.first_not_of('5') == 0u);
1520  CHECK(s012345.first_not_of("5") == 0u);
1521  CHECK(s012345.first_not_of("45") == 0u);
1522  CHECK(s012345.first_not_of("54") == 0u);
1523  CHECK(s012345.first_not_of("345") == 0u);
1524  CHECK(s012345.first_not_of("543") == 0u);
1525  CHECK(s012345.first_not_of("2345") == 0u);
1526  CHECK(s012345.first_not_of("5432") == 0u);
1527  CHECK(s012345.first_not_of("12345") == 0u);
1528  CHECK(s012345.first_not_of("54321") == 0u);
1529  CHECK(s012345.first_not_of("012345") == ryml::npos);
1530  CHECK(s012345.first_not_of("543210") == ryml::npos);
1531  CHECK(s012345.last_not_of('a') == 5u);
1532  CHECK(s012345.last_not_of("ab") == 5u);
1533  CHECK(s012345.last_not_of('5') == 4u);
1534  CHECK(s012345.last_not_of("5") == 4u);
1535  CHECK(s012345.last_not_of("45") == 3u);
1536  CHECK(s012345.last_not_of("54") == 3u);
1537  CHECK(s012345.last_not_of("345") == 2u);
1538  CHECK(s012345.last_not_of("543") == 2u);
1539  CHECK(s012345.last_not_of("2345") == 1u);
1540  CHECK(s012345.last_not_of("5432") == 1u);
1541  CHECK(s012345.last_not_of("12345") == 0u);
1542  CHECK(s012345.last_not_of("54321") == 0u);
1543  CHECK(s012345.last_not_of("012345") == ryml::npos);
1544  CHECK(s012345.last_not_of("543210") == ryml::npos);
1545  CHECK(s012345.last_not_of('0') == 5u);
1546  CHECK(s012345.last_not_of("0") == 5u);
1547  CHECK(s012345.last_not_of("01") == 5u);
1548  CHECK(s012345.last_not_of("10") == 5u);
1549  CHECK(s012345.last_not_of("012") == 5u);
1550  CHECK(s012345.last_not_of("210") == 5u);
1551  CHECK(s012345.last_not_of("0123") == 5u);
1552  CHECK(s012345.last_not_of("3210") == 5u);
1553  CHECK(s012345.last_not_of("01234") == 5u);
1554  CHECK(s012345.last_not_of("43210") == 5u);
1555  CHECK(s012345.last_not_of("012345") == ryml::npos);
1556  CHECK(s012345.last_not_of("543210") == ryml::npos);
1557  }
1558 
1559  // first_non_empty_span()
1560  {
1561  CHECK(ryml::csubstr("foo bar").first_non_empty_span() == "foo");
1562  CHECK(ryml::csubstr(" foo bar").first_non_empty_span() == "foo");
1563  CHECK(ryml::csubstr("\n \r \t foo bar").first_non_empty_span() == "foo");
1564  CHECK(ryml::csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span() == "foo");
1565  CHECK(ryml::csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span() == "foo");
1566  CHECK(ryml::csubstr(",\n \r \t foo\n\r\t bar").first_non_empty_span() == ",");
1567  }
1568  // first_uint_span()
1569  {
1570  CHECK(ryml::csubstr("1234 asdkjh").first_uint_span() == "1234");
1571  CHECK(ryml::csubstr("1234\rasdkjh").first_uint_span() == "1234");
1572  CHECK(ryml::csubstr("1234\tasdkjh").first_uint_span() == "1234");
1573  CHECK(ryml::csubstr("1234\nasdkjh").first_uint_span() == "1234");
1574  CHECK(ryml::csubstr("1234]asdkjh").first_uint_span() == "1234");
1575  CHECK(ryml::csubstr("1234)asdkjh").first_uint_span() == "1234");
1576  CHECK(ryml::csubstr("1234gasdkjh").first_uint_span() == "");
1577  }
1578  // first_int_span()
1579  {
1580  CHECK(ryml::csubstr("-1234 asdkjh").first_int_span() == "-1234");
1581  CHECK(ryml::csubstr("-1234\rasdkjh").first_int_span() == "-1234");
1582  CHECK(ryml::csubstr("-1234\tasdkjh").first_int_span() == "-1234");
1583  CHECK(ryml::csubstr("-1234\nasdkjh").first_int_span() == "-1234");
1584  CHECK(ryml::csubstr("-1234]asdkjh").first_int_span() == "-1234");
1585  CHECK(ryml::csubstr("-1234)asdkjh").first_int_span() == "-1234");
1586  CHECK(ryml::csubstr("-1234gasdkjh").first_int_span() == "");
1587  }
1588  // first_real_span()
1589  {
1590  CHECK(ryml::csubstr("-1234 asdkjh").first_real_span() == "-1234");
1591  CHECK(ryml::csubstr("-1234\rasdkjh").first_real_span() == "-1234");
1592  CHECK(ryml::csubstr("-1234\tasdkjh").first_real_span() == "-1234");
1593  CHECK(ryml::csubstr("-1234\nasdkjh").first_real_span() == "-1234");
1594  CHECK(ryml::csubstr("-1234]asdkjh").first_real_span() == "-1234");
1595  CHECK(ryml::csubstr("-1234)asdkjh").first_real_span() == "-1234");
1596  CHECK(ryml::csubstr("-1234gasdkjh").first_real_span() == "");
1597  CHECK(ryml::csubstr("1.234 asdkjh").first_real_span() == "1.234");
1598  CHECK(ryml::csubstr("1.234e+5 asdkjh").first_real_span() == "1.234e+5");
1599  CHECK(ryml::csubstr("1.234e-5 asdkjh").first_real_span() == "1.234e-5");
1600  CHECK(ryml::csubstr("1.234 asdkjh").first_real_span() == "1.234");
1601  CHECK(ryml::csubstr("1.234e+5 asdkjh").first_real_span() == "1.234e+5");
1602  CHECK(ryml::csubstr("1.234e-5 asdkjh").first_real_span() == "1.234e-5");
1603  CHECK(ryml::csubstr("-1.234 asdkjh").first_real_span() == "-1.234");
1604  CHECK(ryml::csubstr("-1.234e+5 asdkjh").first_real_span() == "-1.234e+5");
1605  CHECK(ryml::csubstr("-1.234e-5 asdkjh").first_real_span() == "-1.234e-5");
1606  // hexadecimal real numbers
1607  CHECK(ryml::csubstr("0x1.e8480p+19 asdkjh").first_real_span() == "0x1.e8480p+19");
1608  CHECK(ryml::csubstr("0x1.e8480p-19 asdkjh").first_real_span() == "0x1.e8480p-19");
1609  CHECK(ryml::csubstr("-0x1.e8480p+19 asdkjh").first_real_span() == "-0x1.e8480p+19");
1610  CHECK(ryml::csubstr("-0x1.e8480p-19 asdkjh").first_real_span() == "-0x1.e8480p-19");
1611  CHECK(ryml::csubstr("+0x1.e8480p+19 asdkjh").first_real_span() == "+0x1.e8480p+19");
1612  CHECK(ryml::csubstr("+0x1.e8480p-19 asdkjh").first_real_span() == "+0x1.e8480p-19");
1613  // binary real numbers
1614  CHECK(ryml::csubstr("0b101.011p+19 asdkjh").first_real_span() == "0b101.011p+19");
1615  CHECK(ryml::csubstr("0b101.011p-19 asdkjh").first_real_span() == "0b101.011p-19");
1616  CHECK(ryml::csubstr("-0b101.011p+19 asdkjh").first_real_span() == "-0b101.011p+19");
1617  CHECK(ryml::csubstr("-0b101.011p-19 asdkjh").first_real_span() == "-0b101.011p-19");
1618  CHECK(ryml::csubstr("+0b101.011p+19 asdkjh").first_real_span() == "+0b101.011p+19");
1619  CHECK(ryml::csubstr("+0b101.011p-19 asdkjh").first_real_span() == "+0b101.011p-19");
1620  // octal real numbers
1621  CHECK(ryml::csubstr("0o173.045p+19 asdkjh").first_real_span() == "0o173.045p+19");
1622  CHECK(ryml::csubstr("0o173.045p-19 asdkjh").first_real_span() == "0o173.045p-19");
1623  CHECK(ryml::csubstr("-0o173.045p+19 asdkjh").first_real_span() == "-0o173.045p+19");
1624  CHECK(ryml::csubstr("-0o173.045p-19 asdkjh").first_real_span() == "-0o173.045p-19");
1625  CHECK(ryml::csubstr("+0o173.045p+19 asdkjh").first_real_span() == "+0o173.045p+19");
1626  CHECK(ryml::csubstr("+0o173.045p-19 asdkjh").first_real_span() == "+0o173.045p-19");
1627  }
1628  // see also is_number()
1629 
1630  // basename(), dirname(), extshort(), extlong()
1631  {
1632  CHECK(ryml::csubstr("/path/to/file.tar.gz").basename() == "file.tar.gz");
1633  CHECK(ryml::csubstr("/path/to/file.tar.gz").dirname() == "/path/to/");
1634  CHECK(ryml::csubstr("C:\\path\\to\\file.tar.gz").basename('\\') == "file.tar.gz");
1635  CHECK(ryml::csubstr("C:\\path\\to\\file.tar.gz").dirname('\\') == "C:\\path\\to\\");
1636  CHECK(ryml::csubstr("/path/to/file.tar.gz").extshort() == "gz");
1637  CHECK(ryml::csubstr("/path/to/file.tar.gz").extlong() == "tar.gz");
1638  CHECK(ryml::csubstr("/path/to/file.tar.gz").name_wo_extshort() == "/path/to/file.tar");
1639  CHECK(ryml::csubstr("/path/to/file.tar.gz").name_wo_extlong() == "/path/to/file");
1640  }
1641 
1642  // split()
1643  {
1644  using namespace ryml;
1645  csubstr parts[] = {"aa", "bb", "cc", "dd", "ee", "ff"};
1646  {
1647  size_t count = 0;
1648  for(csubstr part : csubstr("aa/bb/cc/dd/ee/ff").split('/'))
1649  CHECK(part == parts[count++]);
1650  CHECK(count == 6u);
1651  }
1652  {
1653  size_t count = 0;
1654  for(csubstr part : csubstr("aa.bb.cc.dd.ee.ff").split('.'))
1655  CHECK(part == parts[count++]);
1656  CHECK(count == 6u);
1657  }
1658  {
1659  size_t count = 0;
1660  for(csubstr part : csubstr("aa-bb-cc-dd-ee-ff").split('-'))
1661  CHECK(part == parts[count++]);
1662  CHECK(count == 6u);
1663  }
1664  // see also next_split()
1665  }
1666 
1667  // pop_left(), pop_right() --- non-greedy version
1668  // gpop_left(), gpop_right() --- greedy version
1669  {
1670  const bool skip_empty = true;
1671  // pop_left(): pop the last element from the left
1672  CHECK(ryml::csubstr( "0/1/2" ). pop_left('/' ) == "0" );
1673  CHECK(ryml::csubstr( "/0/1/2" ). pop_left('/' ) == "" );
1674  CHECK(ryml::csubstr("//0/1/2" ). pop_left('/' ) == "" );
1675  CHECK(ryml::csubstr( "0/1/2" ). pop_left('/', skip_empty) == "0" );
1676  CHECK(ryml::csubstr( "/0/1/2" ). pop_left('/', skip_empty) == "/0" );
1677  CHECK(ryml::csubstr("//0/1/2" ). pop_left('/', skip_empty) == "//0" );
1678  // gpop_left(): pop all but the first element (greedy pop)
1679  CHECK(ryml::csubstr( "0/1/2" ).gpop_left('/' ) == "0/1" );
1680  CHECK(ryml::csubstr( "/0/1/2" ).gpop_left('/' ) == "/0/1" );
1681  CHECK(ryml::csubstr("//0/1/2" ).gpop_left('/' ) == "//0/1" );
1682  CHECK(ryml::csubstr( "0/1/2/" ).gpop_left('/' ) == "0/1/2");
1683  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_left('/' ) == "/0/1/2");
1684  CHECK(ryml::csubstr("//0/1/2/" ).gpop_left('/' ) == "//0/1/2");
1685  CHECK(ryml::csubstr( "0/1/2//" ).gpop_left('/' ) == "0/1/2/");
1686  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_left('/' ) == "/0/1/2/");
1687  CHECK(ryml::csubstr("//0/1/2//" ).gpop_left('/' ) == "//0/1/2/");
1688  CHECK(ryml::csubstr( "0/1/2" ).gpop_left('/', skip_empty) == "0/1" );
1689  CHECK(ryml::csubstr( "/0/1/2" ).gpop_left('/', skip_empty) == "/0/1" );
1690  CHECK(ryml::csubstr("//0/1/2" ).gpop_left('/', skip_empty) == "//0/1" );
1691  CHECK(ryml::csubstr( "0/1/2/" ).gpop_left('/', skip_empty) == "0/1" );
1692  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_left('/', skip_empty) == "/0/1" );
1693  CHECK(ryml::csubstr("//0/1/2/" ).gpop_left('/', skip_empty) == "//0/1" );
1694  CHECK(ryml::csubstr( "0/1/2//" ).gpop_left('/', skip_empty) == "0/1" );
1695  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_left('/', skip_empty) == "/0/1" );
1696  CHECK(ryml::csubstr("//0/1/2//" ).gpop_left('/', skip_empty) == "//0/1" );
1697  // pop_right(): pop the last element from the right
1698  CHECK(ryml::csubstr( "0/1/2" ). pop_right('/' ) == "2" );
1699  CHECK(ryml::csubstr( "0/1/2/" ). pop_right('/' ) == "" );
1700  CHECK(ryml::csubstr( "0/1/2//" ). pop_right('/' ) == "" );
1701  CHECK(ryml::csubstr( "0/1/2" ). pop_right('/', skip_empty) == "2" );
1702  CHECK(ryml::csubstr( "0/1/2/" ). pop_right('/', skip_empty) == "2/" );
1703  CHECK(ryml::csubstr( "0/1/2//" ). pop_right('/', skip_empty) == "2//" );
1704  // gpop_right(): pop all but the first element (greedy pop)
1705  CHECK(ryml::csubstr( "0/1/2" ).gpop_right('/' ) == "1/2");
1706  CHECK(ryml::csubstr( "0/1/2/" ).gpop_right('/' ) == "1/2/" );
1707  CHECK(ryml::csubstr( "0/1/2//" ).gpop_right('/' ) == "1/2//" );
1708  CHECK(ryml::csubstr( "/0/1/2" ).gpop_right('/' ) == "0/1/2");
1709  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_right('/' ) == "0/1/2/" );
1710  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_right('/' ) == "0/1/2//" );
1711  CHECK(ryml::csubstr("//0/1/2" ).gpop_right('/' ) == "/0/1/2");
1712  CHECK(ryml::csubstr("//0/1/2/" ).gpop_right('/' ) == "/0/1/2/" );
1713  CHECK(ryml::csubstr("//0/1/2//" ).gpop_right('/' ) == "/0/1/2//" );
1714  CHECK(ryml::csubstr( "0/1/2" ).gpop_right('/', skip_empty) == "1/2");
1715  CHECK(ryml::csubstr( "0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1716  CHECK(ryml::csubstr( "0/1/2//" ).gpop_right('/', skip_empty) == "1/2//" );
1717  CHECK(ryml::csubstr( "/0/1/2" ).gpop_right('/', skip_empty) == "1/2");
1718  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1719  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_right('/', skip_empty) == "1/2//" );
1720  CHECK(ryml::csubstr("//0/1/2" ).gpop_right('/', skip_empty) == "1/2");
1721  CHECK(ryml::csubstr("//0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1722  CHECK(ryml::csubstr("//0/1/2//" ).gpop_right('/', skip_empty) == "1/2//" );
1723  }
1724 }
1725 
1726 
1727 //-----------------------------------------------------------------------------
1728 
1729 
1730 /** demonstrate how to load a YAML file from disk to parse with ryml.
1731  *
1732  * ryml offers no overload to directly parse files from disk; it only
1733  * parses source buffers (which may be mutable or immutable). It is
1734  * up to the caller to load the file contents into a buffer before
1735  * parsing with ryml.
1736  *
1737  * But that does not mean that loading a file is unimportant. There
1738  * are many ways to achieve this in C++, but for convenience and to
1739  * enable you to quickly get up to speed, here is an example
1740  * implementation loading a file from disk and then parsing the
1741  * resulting buffer with ryml.
1742  * @see doc_parse */
1744 {
1745  const char filename[] = "ryml_example.yml";
1746 
1747  // because this is a minimal sample, it assumes nothing on the
1748  // environment/OS (other than that it can read/write files). So we
1749  // create the file on the fly:
1750  file_put_contents(filename, ryml::csubstr("foo: 1\nbar:\n - 2\n - 3\n"));
1751 
1752  // now we can load it into a std::string (for example):
1753  {
1754  std::string contents = file_get_contents<std::string>(filename);
1755  ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(contents)); // immutable (csubstr) overload
1756  CHECK(tree["foo"].val() == "1");
1757  CHECK(tree["bar"][0].val() == "2");
1758  CHECK(tree["bar"][1].val() == "3");
1759  }
1760 
1761  // or we can use a vector<char> instead:
1762  {
1763  std::vector<char> contents = file_get_contents<std::vector<char>>(filename);
1764  ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(contents)); // mutable (csubstr) overload
1765  CHECK(tree["foo"].val() == "1");
1766  CHECK(tree["bar"][0].val() == "2");
1767  CHECK(tree["bar"][1].val() == "3");
1768  }
1769 
1770  // generally, any contiguous char container can be used with ryml,
1771  // provided that the ryml::substr/ryml::csubstr view can be
1772  // created out of it.
1773  //
1774  // ryml provides the overloads above for these two containers, but
1775  // if you are using another container it should be very easy (only
1776  // requires pointer and length).
1777 }
1778 
1779 
1780 //-----------------------------------------------------------------------------
1781 
1782 /** demonstrate in-place parsing of a mutable YAML source buffer.
1783  * @see doc_parse */
1785 {
1786  // Like the name suggests, parse_in_place() directly mutates the
1787  // source buffer in place
1788  char src[] = "{foo: 1, bar: [2, 3]}"; // ryml can parse in situ
1789  ryml::substr srcview = src; // a mutable view to the source buffer
1790  ryml::Tree tree = ryml::parse_in_place(srcview); // you can also reuse the tree and/or parser
1791  ryml::ConstNodeRef root = tree.crootref(); // get a constant reference to the root
1792 
1793  CHECK(root.is_map());
1794  CHECK(root["foo"].is_keyval());
1795  CHECK(root["foo"].key() == "foo");
1796  CHECK(root["foo"].val() == "1");
1797  CHECK(root["bar"].is_seq());
1798  CHECK(root["bar"].has_key());
1799  CHECK(root["bar"].key() == "bar");
1800  CHECK(root["bar"][0].val() == "2");
1801  CHECK(root["bar"][1].val() == "3");
1802 
1803  // deserializing:
1804  int foo = 0, bar0 = 0, bar1 = 0;
1805  root["foo"] >> foo;
1806  root["bar"][0] >> bar0;
1807  root["bar"][1] >> bar1;
1808  CHECK(foo == 1);
1809  CHECK(bar0 == 2);
1810  CHECK(bar1 == 3);
1811 
1812  // after parsing, the tree holds views to the source buffer:
1813  CHECK(root["foo"].val().data() == src + strlen("{foo: "));
1814  CHECK(root["foo"].val().begin() == src + strlen("{foo: "));
1815  CHECK(root["foo"].val().end() == src + strlen("{foo: 1"));
1816  CHECK(root["foo"].val().is_sub(srcview)); // equivalent to the previous three assertions
1817  CHECK(root["bar"][0].val().data() == src + strlen("{foo: 1, bar: ["));
1818  CHECK(root["bar"][0].val().begin() == src + strlen("{foo: 1, bar: ["));
1819  CHECK(root["bar"][0].val().end() == src + strlen("{foo: 1, bar: [2"));
1820  CHECK(root["bar"][0].val().is_sub(srcview)); // equivalent to the previous three assertions
1821  CHECK(root["bar"][1].val().data() == src + strlen("{foo: 1, bar: [2, "));
1822  CHECK(root["bar"][1].val().begin() == src + strlen("{foo: 1, bar: [2, "));
1823  CHECK(root["bar"][1].val().end() == src + strlen("{foo: 1, bar: [2, 3"));
1824  CHECK(root["bar"][1].val().is_sub(srcview)); // equivalent to the previous three assertions
1825 
1826  // NOTE. parse_in_place() cannot accept ryml::csubstr
1827  // so this will cause a /compile/ error:
1828  ryml::csubstr csrcview = srcview; // ok, can assign from mutable to immutable
1829  //tree = ryml::parse_in_place(csrcview); // compile error, cannot mutate an immutable view
1830  (void)csrcview;
1831 }
1832 
1833 
1834 //-----------------------------------------------------------------------------
1835 
1836 /** demonstrate parsing of a read-only YAML source buffer
1837  * @see doc_parse */
1839 {
1840  // to parse read-only memory, ryml will copy first to the tree's
1841  // arena, and then parse the copied buffer:
1842  ryml::Tree tree = ryml::parse_in_arena("{foo: 1, bar: [2, 3]}");
1843  ryml::ConstNodeRef root = tree.crootref(); // get a const reference to the root
1844 
1845  CHECK(root.is_map());
1846  CHECK(root["foo"].is_keyval());
1847  CHECK(root["foo"].key() == "foo");
1848  CHECK(root["foo"].val() == "1");
1849  CHECK(root["bar"].is_seq());
1850  CHECK(root["bar"].has_key());
1851  CHECK(root["bar"].key() == "bar");
1852  CHECK(root["bar"][0].val() == "2");
1853  CHECK(root["bar"][1].val() == "3");
1854 
1855  // deserializing:
1856  int foo = 0, bar0 = 0, bar1 = 0;
1857  root["foo"] >> foo;
1858  root["bar"][0] >> bar0;
1859  root["bar"][1] >> bar1;
1860  CHECK(foo == 1);
1861  CHECK(bar0 == 2);
1862  CHECK(bar1 == 3);
1863 
1864  // NOTE. parse_in_arena() cannot accept ryml::substr. Overloads
1865  // receiving substr buffers are declared, but intentionally left
1866  // undefined, so this will cause a /linker/ error
1867  char src[] = "{foo: is it really true}";
1868  ryml::substr srcview = src;
1869  //tree = ryml::parse_in_place(srcview); // linker error, overload intentionally undefined
1870 
1871  // If you really intend to parse a mutable buffer in the arena,
1872  // then simply convert it to immutable prior to calling
1873  // parse_in_arena():
1874  ryml::csubstr csrcview = srcview; // assigning from src also works
1875  tree = ryml::parse_in_arena(csrcview); // OK! csrcview is immutable
1876  CHECK(tree["foo"].val() == "is it really true");
1877 }
1878 
1879 
1880 //-----------------------------------------------------------------------------
1881 
1882 /** demonstrate reuse/modification of tree when parsing
1883  * @see doc_parse */
1885 {
1886  ryml::Tree tree;
1887 
1888  // it will always be faster if the tree's size is conveniently reserved:
1889  tree.reserve(30); // reserve 30 nodes (good enough for this sample)
1890  // if you are using the tree's arena to serialize data,
1891  // then reserve also the arena's size:
1892  tree.reserve_arena(256); // reserve 256 characters (good enough for this sample)
1893 
1894  // now parse into the tree:
1895  ryml::csubstr yaml = R"(foo: 1
1896 bar: [2, 3]
1897 )";
1898  ryml::parse_in_arena(yaml, &tree);
1899 
1900  ryml::ConstNodeRef root = tree.crootref();
1901  CHECK(root.num_children() == 2);
1902  CHECK(root.is_map());
1903  CHECK(root["foo"].is_keyval());
1904  CHECK(root["foo"].key() == "foo");
1905  CHECK(root["foo"].val() == "1");
1906  CHECK(root["bar"].is_seq());
1907  CHECK(root["bar"].has_key());
1908  CHECK(root["bar"].key() == "bar");
1909  CHECK(root["bar"][0].val() == "2");
1910  CHECK(root["bar"][1].val() == "3");
1911  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1
1912 bar: [2,3]
1913 )");
1914 
1915  // WATCHOUT: parsing into an existing tree will APPEND to it:
1916  ryml::parse_in_arena("{foo2: 12, bar2: [22, 32]}", &tree);
1917  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1
1918 bar: [2,3]
1919 foo2: 12
1920 bar2: [22,32]
1921 )");
1922  CHECK(root.num_children() == 4);
1923  CHECK(root["foo2"].is_keyval());
1924  CHECK(root["foo2"].key() == "foo2");
1925  CHECK(root["foo2"].val() == "12");
1926  CHECK(root["bar2"].is_seq());
1927  CHECK(root["bar2"].has_key());
1928  CHECK(root["bar2"].key() == "bar2");
1929  CHECK(root["bar2"][0].val() == "22");
1930  CHECK(root["bar2"][1].val() == "32");
1931 
1932  // clear first before parsing into an existing tree.
1933  tree.clear();
1934  tree.clear_arena(); // you may or may not want to clear the arena
1935  ryml::parse_in_arena("- a\n- b\n- {x0: 1, x1: 2}", &tree);
1936  CHECK(ryml::emitrs_yaml<std::string>(tree) == "- a\n- b\n- {x0: 1,x1: 2}\n");
1937  CHECK(root.is_seq());
1938  CHECK(root[0].val() == "a");
1939  CHECK(root[1].val() == "b");
1940  CHECK(root[2].is_map());
1941  CHECK(root[2]["x0"].val() == "1");
1942  CHECK(root[2]["x1"].val() == "2");
1943 
1944  // we can parse directly into a node nested deep in an existing tree:
1945  ryml::NodeRef mroot = tree.rootref(); // modifiable root
1946  ryml::parse_in_arena("champagne: Dom Perignon\ncoffee: Arabica", mroot.append_child());
1947  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
1948 - b
1949 - {x0: 1,x1: 2}
1950 - champagne: Dom Perignon
1951  coffee: Arabica
1952 )");
1953  CHECK(root.is_seq());
1954  CHECK(root[0].val() == "a");
1955  CHECK(root[1].val() == "b");
1956  CHECK(root[2].is_map());
1957  CHECK(root[2]["x0"].val() == "1");
1958  CHECK(root[2]["x1"].val() == "2");
1959  CHECK(root[3].is_map());
1960  CHECK(root[3]["champagne"].val() == "Dom Perignon");
1961  CHECK(root[3]["coffee"].val() == "Arabica");
1962 
1963  // watchout: to add to an existing node within a map, the node's
1964  // key must be separately set first:
1965  ryml::NodeRef more = mroot[3].append_child({ryml::KEYMAP, "more"});
1966  ryml::NodeRef beer = mroot[3].append_child({ryml::KEYSEQ, "beer"});
1967  ryml::NodeRef always = mroot[3].append_child({ryml::KEY, "always"});
1968  ryml::parse_in_arena("{vinho verde: Soalheiro, vinho tinto: Redoma 2017}", more);
1969  ryml::parse_in_arena("- Rochefort 10\n- Busch\n- Leffe Rituel", beer);
1970  ryml::parse_in_arena("lots\nof\nwater", always);
1971  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
1972 - b
1973 - {x0: 1,x1: 2}
1974 - champagne: Dom Perignon
1975  coffee: Arabica
1976  more:
1977  vinho verde: Soalheiro
1978  vinho tinto: Redoma 2017
1979  beer:
1980  - Rochefort 10
1981  - Busch
1982  - Leffe Rituel
1983  always: lots of water
1984 )");
1985 
1986  // can append at the top:
1987  ryml::parse_in_arena("- foo\n- bar\n- baz\n- bat", mroot);
1988  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
1989 - b
1990 - {x0: 1,x1: 2}
1991 - champagne: Dom Perignon
1992  coffee: Arabica
1993  more:
1994  vinho verde: Soalheiro
1995  vinho tinto: Redoma 2017
1996  beer:
1997  - Rochefort 10
1998  - Busch
1999  - Leffe Rituel
2000  always: lots of water
2001 - foo
2002 - bar
2003 - baz
2004 - bat
2005 )");
2006 
2007  // or nested:
2008  ryml::parse_in_arena("[Kasteel Donker]", beer);
2009  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
2010 - b
2011 - {x0: 1,x1: 2}
2012 - champagne: Dom Perignon
2013  coffee: Arabica
2014  more:
2015  vinho verde: Soalheiro
2016  vinho tinto: Redoma 2017
2017  beer:
2018  - Rochefort 10
2019  - Busch
2020  - Leffe Rituel
2021  - Kasteel Donker
2022  always: lots of water
2023 - foo
2024 - bar
2025 - baz
2026 - bat
2027 )");
2028 }
2029 
2030 
2031 //-----------------------------------------------------------------------------
2032 
2033 /** Demonstrates reuse of an existing parser. Doing this is
2034  * recommended when multiple files are parsed.
2035  * @see doc_parse */
2037 {
2038  ryml::EventHandlerTree evt_handler = {};
2039  ryml::Parser parser(&evt_handler);
2040 
2041  // it is also advised to reserve the parser depth
2042  // to the expected depth of the data tree:
2043  parser.reserve_stack(10); // uses small storage optimization
2044  // defaulting to 16 depth, so this
2045  // instruction is a no-op, and the stack
2046  // will located in the parser object.
2047  parser.reserve_stack(20); // But this will cause an allocation
2048  // because it is above 16.
2049 
2050  ryml::Tree champagnes = parse_in_arena(&parser, "champagnes.yml", "[Dom Perignon, Gosset Grande Reserve, Jacquesson 742]");
2051  CHECK(ryml::emitrs_yaml<std::string>(champagnes) == "[Dom Perignon,Gosset Grande Reserve,Jacquesson 742]");
2052 
2053  ryml::Tree beers = parse_in_arena(&parser, "beers.yml", "[Rochefort 10, Busch, Leffe Rituel, Kasteel Donker]");
2054  CHECK(ryml::emitrs_yaml<std::string>(beers) == "[Rochefort 10,Busch,Leffe Rituel,Kasteel Donker]");
2055 }
2056 
2057 
2058 //-----------------------------------------------------------------------------
2059 
2060 /** for ultimate speed when parsing multiple times, reuse both the
2061  * tree and parser
2062  * @see doc_parse */
2064 {
2065  ryml::Tree tree;
2066  // it will always be faster if the tree's size is conveniently reserved:
2067  tree.reserve(30); // reserve 30 nodes (good enough for this sample)
2068  // if you are using the tree's arena to serialize data,
2069  // then reserve also the arena's size:
2070  tree.reserve(256); // reserve 256 characters (good enough for this sample)
2071 
2072  ryml::EventHandlerTree evt_handler;
2073  ryml::Parser parser(&evt_handler);
2074  // it is also advised to reserve the parser depth
2075  // to the expected depth of the data tree:
2076  parser.reserve_stack(10); // the parser uses small storage
2077  // optimization defaulting to 16 depth,
2078  // so this instruction is a no-op, and
2079  // the stack will be located in the
2080  // parser object.
2081  parser.reserve_stack(20); // But this will cause an allocation
2082  // because it is above 16.
2083 
2084  ryml::csubstr champagnes = "- Dom Perignon\n- Gosset Grande Reserve\n- Jacquesson 742";
2085  ryml::csubstr beers = "- Rochefort 10\n- Busch\n- Leffe Rituel\n- Kasteel Donker";
2086  ryml::csubstr wines = "- Soalheiro\n- Niepoort Redoma 2017\n- Vina Esmeralda";
2087 
2088  parse_in_arena(&parser, "champagnes.yml", champagnes, &tree);
2089  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Dom Perignon
2090 - Gosset Grande Reserve
2091 - Jacquesson 742
2092 )");
2093 
2094  // watchout: this will APPEND to the given tree:
2095  parse_in_arena(&parser, "beers.yml", beers, &tree);
2096  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Dom Perignon
2097 - Gosset Grande Reserve
2098 - Jacquesson 742
2099 - Rochefort 10
2100 - Busch
2101 - Leffe Rituel
2102 - Kasteel Donker
2103 )");
2104 
2105  // if you don't wish to append, clear the tree first:
2106  tree.clear();
2107  parse_in_arena(&parser, "wines.yml", wines, &tree);
2108  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Soalheiro
2109 - Niepoort Redoma 2017
2110 - Vina Esmeralda
2111 )");
2112 }
2113 
2114 
2115 //-----------------------------------------------------------------------------
2116 
2117 /** shows how to programatically iterate through trees
2118  * @see doc_tree
2119  * @see doc_node_classes
2120  */
2122 {
2123  const ryml::Tree tree = ryml::parse_in_arena(R"(doe: "a deer, a female deer"
2124 ray: "a drop of golden sun"
2125 pi: 3.14159
2126 xmas: true
2127 french-hens: 3
2128 calling-birds:
2129  - huey
2130  - dewey
2131  - louie
2132  - fred
2133 xmas-fifth-day:
2134  calling-birds: four
2135  french-hens: 3
2136  golden-rings: 5
2137  partridges:
2138  count: 1
2139  location: a pear tree
2140  turtle-doves: two
2141 cars: GTO
2142 )");
2143  ryml::ConstNodeRef root = tree.crootref();
2144 
2145  // iterate children
2146  {
2147  std::vector<ryml::csubstr> keys, vals; // to store all the root-level keys, vals
2148  for(ryml::ConstNodeRef n : root.children())
2149  {
2150  keys.emplace_back(n.key());
2151  vals.emplace_back(n.has_val() ? n.val() : ryml::csubstr{});
2152  }
2153  CHECK(keys[0] == "doe");
2154  CHECK(vals[0] == "a deer, a female deer");
2155  CHECK(keys[1] == "ray");
2156  CHECK(vals[1] == "a drop of golden sun");
2157  CHECK(keys[2] == "pi");
2158  CHECK(vals[2] == "3.14159");
2159  CHECK(keys[3] == "xmas");
2160  CHECK(vals[3] == "true");
2161  CHECK(root[5].has_key());
2162  CHECK(root[5].is_seq());
2163  CHECK(root[5].key() == "calling-birds");
2164  CHECK(!root[5].has_val()); // it is a map, so not a val
2165  //CHECK(root[5].val() == ""); // ERROR! node does not have a val.
2166  CHECK(keys[5] == "calling-birds");
2167  CHECK(vals[5] == "");
2168  }
2169 
2170  // iterate siblings
2171  {
2172  size_t count = 0;
2173  ryml::csubstr calling_birds[] = {"huey", "dewey", "louie", "fred"};
2174  for(ryml::ConstNodeRef n : root["calling-birds"][2].siblings())
2175  CHECK(n.val() == calling_birds[count++]);
2176  CHECK(count == 4u);
2177  }
2178 }
2179 
2180 
2181 //-----------------------------------------------------------------------------
2182 
2183 /** shows how to programatically create trees
2184  * @see doc_tree
2185  * @see doc_node_classes
2186  * */
2188 {
2189  ryml::NodeRef doe;
2190  CHECK(doe.invalid()); // it's pointing at nowhere
2191 
2192  ryml::Tree tree;
2193  ryml::NodeRef root = tree.rootref();
2194  root |= ryml::MAP; // mark root as a map
2195  doe = root["doe"];
2196  CHECK(!doe.invalid()); // it's now pointing at the tree
2197  CHECK(doe.is_seed()); // but the tree has nothing there, so this is only a seed
2198 
2199  // set the value of the node
2200  const char a_deer[] = "a deer, a female deer";
2201  doe = a_deer;
2202  // now the node really exists in the tree, and this ref is no
2203  // longer a seed:
2204  CHECK(!doe.is_seed());
2205  // WATCHOUT for lifetimes:
2206  CHECK(doe.val().str == a_deer); // it is pointing at the initial string
2207  // If you need to avoid lifetime dependency, serialize the data:
2208  {
2209  std::string a_drop = "a drop of golden sun";
2210  // this will copy the string to the tree's arena:
2211  // (see the serialization samples below)
2212  root["ray"] << a_drop;
2213  // and now you can modify the original string without changing
2214  // the tree:
2215  a_drop[0] = 'Z';
2216  a_drop[1] = 'Z';
2217  }
2218  CHECK(root["ray"].val() == "a drop of golden sun");
2219 
2220  // etc.
2221  root["pi"] << ryml::fmt::real(3.141592654, 5);
2222  root["xmas"] << ryml::fmt::boolalpha(true);
2223  root["french-hens"] << 3;
2224  ryml::NodeRef calling_birds = root["calling-birds"];
2225  calling_birds |= ryml::SEQ;
2226  calling_birds.append_child() = "huey";
2227  calling_birds.append_child() = "dewey";
2228  calling_birds.append_child() = "louie";
2229  calling_birds.append_child() = "fred";
2230  ryml::NodeRef xmas5 = root["xmas-fifth-day"];
2231  xmas5 |= ryml::MAP;
2232  xmas5["calling-birds"] = "four";
2233  xmas5["french-hens"] << 3;
2234  xmas5["golden-rings"] << 5;
2235  xmas5["partridges"] |= ryml::MAP;
2236  xmas5["partridges"]["count"] << 1;
2237  xmas5["partridges"]["location"] = "a pear tree";
2238  xmas5["turtle-doves"] = "two";
2239  root["cars"] = "GTO";
2240 
2241  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(doe: 'a deer, a female deer'
2242 ray: a drop of golden sun
2243 pi: 3.14159
2244 xmas: true
2245 french-hens: 3
2246 calling-birds:
2247  - huey
2248  - dewey
2249  - louie
2250  - fred
2251 xmas-fifth-day:
2252  calling-birds: four
2253  french-hens: 3
2254  golden-rings: 5
2255  partridges:
2256  count: 1
2257  location: a pear tree
2258  turtle-doves: two
2259 cars: GTO
2260 )");
2261 }
2262 
2263 
2264 //-----------------------------------------------------------------------------
2265 
2266 /** demonstrates explicit and implicit interaction with the tree's string arena.
2267  * Notice that ryml only holds strings in the tree's nodes. */
2269 {
2270  // mutable buffers are parsed in situ:
2271  {
2272  char buf[] = "[a, b, c, d]";
2273  ryml::substr yml = buf;
2274  ryml::Tree tree = ryml::parse_in_place(yml);
2275  // notice the arena is empty:
2276  CHECK(tree.arena().empty());
2277  // and the tree is pointing at the original buffer:
2278  ryml::NodeRef root = tree.rootref();
2279  CHECK(root[0].val().is_sub(yml));
2280  CHECK(root[1].val().is_sub(yml));
2281  CHECK(root[2].val().is_sub(yml));
2282  CHECK(root[3].val().is_sub(yml));
2283  CHECK(yml.is_super(root[0].val()));
2284  CHECK(yml.is_super(root[1].val()));
2285  CHECK(yml.is_super(root[2].val()));
2286  CHECK(yml.is_super(root[3].val()));
2287  }
2288 
2289  // when parsing immutable buffers, the buffer is first copied to the
2290  // tree's arena; the copy in the arena is then the buffer which is
2291  // actually parsed
2292  {
2293  ryml::csubstr yml = "[a, b, c, d]";
2294  ryml::Tree tree = ryml::parse_in_arena(yml);
2295  // notice the buffer was copied to the arena:
2296  CHECK(tree.arena().data() != yml.data());
2297  CHECK(tree.arena() == yml);
2298  // and the tree is pointing at the arena instead of to the
2299  // original buffer:
2300  ryml::NodeRef root = tree.rootref();
2301  ryml::csubstr arena = tree.arena();
2302  CHECK(root[0].val().is_sub(arena));
2303  CHECK(root[1].val().is_sub(arena));
2304  CHECK(root[2].val().is_sub(arena));
2305  CHECK(root[3].val().is_sub(arena));
2306  CHECK(arena.is_super(root[0].val()));
2307  CHECK(arena.is_super(root[1].val()));
2308  CHECK(arena.is_super(root[2].val()));
2309  CHECK(arena.is_super(root[3].val()));
2310  }
2311 
2312  // the arena is also used when the data is serialized to string
2313  // with NodeRef::operator<<(): mutable buffer
2314  {
2315  char buf[] = "[a, b, c, d]"; // mutable
2316  ryml::substr yml = buf;
2317  ryml::Tree tree = ryml::parse_in_place(yml);
2318  // notice the arena is empty:
2319  CHECK(tree.arena().empty());
2320  ryml::NodeRef root = tree.rootref();
2321 
2322  // serialize an integer, and mutate the tree
2323  CHECK(root[2].val() == "c");
2324  CHECK(root[2].val().is_sub(yml)); // val is first pointing at the buffer
2325  root[2] << 12345;
2326  CHECK(root[2].val() == "12345");
2327  CHECK(root[2].val().is_sub(tree.arena())); // now val is pointing at the arena
2328  // notice the serialized string was appended to the tree's arena:
2329  CHECK(tree.arena() == "12345");
2330 
2331  // serialize an integer, and mutate the tree
2332  CHECK(root[3].val() == "d");
2333  CHECK(root[3].val().is_sub(yml)); // val is first pointing at the buffer
2334  root[3] << 67890;
2335  CHECK(root[3].val() == "67890");
2336  CHECK(root[3].val().is_sub(tree.arena())); // now val is pointing at the arena
2337  // notice the serialized string was appended to the tree's arena:
2338  CHECK(tree.arena() == "1234567890");
2339  }
2340  // the arena is also used when the data is serialized to string
2341  // with NodeRef::operator<<(): immutable buffer
2342  {
2343  ryml::csubstr yml = "[a, b, c, d]"; // immutable
2344  ryml::Tree tree = ryml::parse_in_arena(yml);
2345  // notice the buffer was copied to the arena:
2346  CHECK(tree.arena().data() != yml.data());
2347  CHECK(tree.arena() == yml);
2348  ryml::NodeRef root = tree.rootref();
2349 
2350  // serialize an integer, and mutate the tree
2351  CHECK(root[2].val() == "c");
2352  root[2] << 12345; // serialize an integer
2353  CHECK(root[2].val() == "12345");
2354  // notice the serialized string was appended to the tree's arena:
2355  // notice also the previous values remain there.
2356  // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2357  CHECK(tree.arena() == "[a, b, c, d]12345");
2358  // old values: --------------^
2359 
2360  // serialize an integer, and mutate the tree
2361  root[3] << 67890;
2362  CHECK(root[3].val() == "67890");
2363  // notice the serialized string was appended to the tree's arena:
2364  // notice also the previous values remain there.
2365  // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2366  CHECK(tree.arena() == "[a, b, c, d]1234567890");
2367  // old values: --------------^ ---^^^^^
2368  }
2369 
2370  // to_arena(): directly serialize values to the arena:
2371  {
2372  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2373  ryml::csubstr c10 = tree.to_arena(10101010);
2374  CHECK(c10 == "10101010");
2375  CHECK(c10.is_sub(tree.arena()));
2376  CHECK(tree.arena() == "{a: b}10101010");
2377  CHECK(tree.key(1) == "a");
2378  CHECK(tree.val(1) == "b");
2379  tree.set_val(1, c10);
2380  CHECK(tree.val(1) == c10);
2381  // and you can also do it through a node:
2382  ryml::NodeRef root = tree.rootref();
2383  root["a"].set_val_serialized(2222);
2384  CHECK(root["a"].val() == "2222");
2385  CHECK(tree.arena() == "{a: b}101010102222");
2386  }
2387 
2388  // copy_to_arena(): manually copy a string to the arena:
2389  {
2390  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2391  ryml::csubstr mystr = "Gosset Grande Reserve";
2392  ryml::csubstr copied = tree.copy_to_arena(mystr);
2393  CHECK(!copied.overlaps(mystr));
2394  CHECK(copied == mystr);
2395  CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2396  }
2397 
2398  // alloc_arena(): allocate a buffer from the arena:
2399  {
2400  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2401  ryml::csubstr mystr = "Gosset Grande Reserve";
2402  ryml::substr copied = tree.alloc_arena(mystr.size());
2403  CHECK(!copied.overlaps(mystr));
2404  memcpy(copied.str, mystr.str, mystr.len);
2405  CHECK(copied == mystr);
2406  CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2407  }
2408 
2409  // reserve_arena(): ensure the arena has a certain size to avoid reallocations
2410  {
2411  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2412  CHECK(tree.arena().size() == strlen("{a: b}"));
2413  tree.reserve_arena(100);
2414  CHECK(tree.arena_capacity() >= 100);
2415  CHECK(tree.arena().size() == strlen("{a: b}"));
2416  tree.to_arena(123456);
2417  CHECK(tree.arena().first(12) == "{a: b}123456");
2418  }
2419 }
2420 
2421 
2422 //-----------------------------------------------------------------------------
2423 
2424 /** ryml provides facilities for serializing and deserializing the C++
2425  fundamental types, including boolean and null values; this is
2426  provided by the several overloads in @ref doc_to_chars and @ref
2427  doc_from_chars. To add serialization for user scalar types (ie,
2428  those types that should be serialized as strings in leaf nodes),
2429  you just need to define the appropriate overloads of to_chars and
2430  from_chars for those types; see @ref sample_user_scalar_types for
2431  an example on how to achieve this, and see @ref doc_serialization
2432  for more information on serialization. */
2434 {
2435  ryml::Tree tree;
2436  CHECK(tree.arena().empty());
2437  CHECK(tree.to_arena('a') == "a"); CHECK(tree.arena() == "a");
2438  CHECK(tree.to_arena("bcde") == "bcde"); CHECK(tree.arena() == "abcde");
2439  CHECK(tree.to_arena(unsigned(0)) == "0"); CHECK(tree.arena() == "abcde0");
2440  CHECK(tree.to_arena(int(1)) == "1"); CHECK(tree.arena() == "abcde01");
2441  CHECK(tree.to_arena(uint8_t(0)) == "0"); CHECK(tree.arena() == "abcde010");
2442  CHECK(tree.to_arena(uint16_t(1)) == "1"); CHECK(tree.arena() == "abcde0101");
2443  CHECK(tree.to_arena(uint32_t(2)) == "2"); CHECK(tree.arena() == "abcde01012");
2444  CHECK(tree.to_arena(uint64_t(3)) == "3"); CHECK(tree.arena() == "abcde010123");
2445  CHECK(tree.to_arena(int8_t( 4)) == "4"); CHECK(tree.arena() == "abcde0101234");
2446  CHECK(tree.to_arena(int8_t(-4)) == "-4"); CHECK(tree.arena() == "abcde0101234-4");
2447  CHECK(tree.to_arena(int16_t( 5)) == "5"); CHECK(tree.arena() == "abcde0101234-45");
2448  CHECK(tree.to_arena(int16_t(-5)) == "-5"); CHECK(tree.arena() == "abcde0101234-45-5");
2449  CHECK(tree.to_arena(int32_t( 6)) == "6"); CHECK(tree.arena() == "abcde0101234-45-56");
2450  CHECK(tree.to_arena(int32_t(-6)) == "-6"); CHECK(tree.arena() == "abcde0101234-45-56-6");
2451  CHECK(tree.to_arena(int64_t( 7)) == "7"); CHECK(tree.arena() == "abcde0101234-45-56-67");
2452  CHECK(tree.to_arena(int64_t(-7)) == "-7"); CHECK(tree.arena() == "abcde0101234-45-56-67-7");
2453  CHECK(tree.to_arena((void*)1) == "0x1"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x1");
2454  CHECK(tree.to_arena(float(0.124)) == "0.124"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.124");
2455  CHECK(tree.to_arena(double(0.234)) == "0.234"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.234");
2456 
2457  // write boolean values - see also sample_formatting()
2458  CHECK(tree.to_arena(bool(true)) == "1"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.2341");
2459  CHECK(tree.to_arena(bool(false)) == "0"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410");
2460  CHECK(tree.to_arena(c4::fmt::boolalpha(true)) == "true"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410true");
2461  CHECK(tree.to_arena(c4::fmt::boolalpha(false)) == "false"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse");
2462 
2463  // write special float values
2464  // see also sample_float_precision()
2465  const float fnan = std::numeric_limits<float >::quiet_NaN();
2466  const double dnan = std::numeric_limits<double>::quiet_NaN();
2467  const float finf = std::numeric_limits<float >::infinity();
2468  const double dinf = std::numeric_limits<double>::infinity();
2469  CHECK(tree.to_arena( finf) == ".inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf");
2470  CHECK(tree.to_arena( dinf) == ".inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf");
2471  CHECK(tree.to_arena(-finf) == "-.inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf");
2472  CHECK(tree.to_arena(-dinf) == "-.inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf");
2473  CHECK(tree.to_arena( fnan) == ".nan"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf.nan");
2474  CHECK(tree.to_arena( dnan) == ".nan"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf.nan.nan");
2475 
2476  // read special float values
2477  // see also sample_float_precision()
2478  C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wfloat-equal");
2479  tree = ryml::parse_in_arena(R"({ninf: -.inf, pinf: .inf, nan: .nan})");
2480  float f = 0.f;
2481  double d = 0.;
2482  CHECK(f == 0.f);
2483  CHECK(d == 0.);
2484  tree["ninf"] >> f; CHECK(f == -finf);
2485  tree["ninf"] >> d; CHECK(d == -dinf);
2486  tree["pinf"] >> f; CHECK(f == finf);
2487  tree["pinf"] >> d; CHECK(d == dinf);
2488  tree["nan" ] >> f; CHECK(std::isnan(f));
2489  tree["nan" ] >> d; CHECK(std::isnan(d));
2490  C4_SUPPRESS_WARNING_GCC_CLANG_POP
2491 
2492  // value overflow detection:
2493  // (for integral types only)
2494  {
2495  // we will be detecting errors below, so we use this sample helper
2496  ScopedErrorHandlerExample err = {};
2497  ryml::Tree t(err.callbacks()); // instantiate with the error-detecting callbacks
2498  // create a simple tree with an int value
2499  ryml::parse_in_arena(R"({val: 258})", &t);
2500  // by default, overflow is not detected:
2501  uint8_t valu8 = 0;
2502  int8_t vali8 = 0;
2503  t["val"] >> valu8; CHECK(valu8 == 2); // not 257; it wrapped around
2504  t["val"] >> vali8; CHECK(vali8 == 2); // not 257; it wrapped around
2505  // ...but there are facilities to detect overflow
2506  CHECK(ryml::overflows<uint8_t>(t["val"].val()));
2507  CHECK(ryml::overflows<int8_t>(t["val"].val()));
2508  CHECK( ! ryml::overflows<int16_t>(t["val"].val()));
2509  // and there is a format helper
2510  CHECK(err.check_error_occurs([&]{
2511  auto checku8 = ryml::fmt::overflow_checked(valu8); // need to declare the wrapper type before using it with >>
2512  t["val"] >> checku8; // this will cause an error
2513  }));
2514  CHECK(err.check_error_occurs([&]{
2515  auto checki8 = ryml::fmt::overflow_checked(vali8); // need to declare the wrapper type before using it with >>
2516  t["val"] >> checki8; // this will cause an error
2517  }));
2518  }
2519 }
2520 
2521 
2522 //-----------------------------------------------------------------------------
2523 
2524 /** Shows how to deal with empty/null values. See also @ref
2525  * c4::yml::Tree::val_is_null */
2527 {
2528  // reading empty/null values - see also sample_formatting()
2529  ryml::Tree tree = ryml::parse_in_arena(R"(
2530 plain:
2531 squoted: ''
2532 dquoted: ""
2533 literal: |
2534 folded: >
2535 all_null: [~, null, Null, NULL]
2536 non_null: [nULL, non_null, non null, null it is not]
2537 )");
2538  // first, remember that .has_val() is a structural predicate
2539  // indicating the node is a leaf, and not a container.
2540  CHECK(tree["plain"].has_val()); // has a val, even if it's empty!
2541  CHECK(tree["squoted"].has_val());
2542  CHECK(tree["dquoted"].has_val());
2543  CHECK(tree["literal"].has_val());
2544  CHECK(tree["folded"].has_val());
2545  CHECK( ! tree["all_null"].has_val());
2546  CHECK( ! tree["non_null"].has_val());
2547  // In essence, has_val() is the logical opposite of is_container()
2548  CHECK( ! tree["plain"].is_container());
2549  CHECK( ! tree["squoted"].is_container());
2550  CHECK( ! tree["dquoted"].is_container());
2551  CHECK( ! tree["literal"].is_container());
2552  CHECK( ! tree["folded"].is_container());
2553  CHECK(tree["all_null"].is_container());
2554  CHECK(tree["non_null"].is_container());
2555  //
2556  // Right. How about the contents of each val?
2557  //
2558  // all of these scalars have zero-length:
2559  CHECK(tree["plain"].val().len == 0);
2560  CHECK(tree["squoted"].val().len == 0);
2561  CHECK(tree["dquoted"].val().len == 0);
2562  CHECK(tree["literal"].val().len == 0);
2563  CHECK(tree["folded"].val().len == 0);
2564  // but only the empty scalar has null string:
2565  CHECK(tree["plain"].val().str == nullptr);
2566  CHECK(tree["squoted"].val().str != nullptr);
2567  CHECK(tree["dquoted"].val().str != nullptr);
2568  CHECK(tree["literal"].val().str != nullptr);
2569  CHECK(tree["folded"].val().str != nullptr);
2570  // likewise, scalar comparison to nullptr has the same results:
2571  // (remember that .val() gives you the scalar value, node must
2572  // have a val, ie must be a leaf node, not a container)
2573  CHECK(tree["plain"].val() == nullptr);
2574  CHECK(tree["squoted"].val() != nullptr);
2575  CHECK(tree["dquoted"].val() != nullptr);
2576  CHECK(tree["literal"].val() != nullptr);
2577  CHECK(tree["folded"].val() != nullptr);
2578  // the tree and node classes provide the corresponding predicate
2579  // functions .key_is_null() and .val_is_null().
2580  // (note that these functions have the same preconditions as .val(),
2581  // because they need get the val to look into its contents)
2582  CHECK(tree["plain"].val_is_null());
2583  CHECK( ! tree["squoted"].val_is_null());
2584  CHECK( ! tree["dquoted"].val_is_null());
2585  CHECK( ! tree["literal"].val_is_null());
2586  CHECK( ! tree["folded"].val_is_null());
2587  // matching to null is case-sensitive. only the cases shown here
2588  // match to null:
2589  for(ryml::ConstNodeRef child : tree["all_null"].children())
2590  {
2591  CHECK(child.val() != nullptr); // it is pointing at a string, so it is not nullptr!
2592  CHECK(child.val_is_null());
2593  }
2594  for(ryml::ConstNodeRef child : tree["non_null"].children())
2595  {
2596  CHECK(child.val() != nullptr);
2597  CHECK( ! child.val_is_null());
2598  }
2599  //
2600  //
2601  // Because the meaning of null/~/empty will vary from application
2602  // to application, ryml makes no assumption on what should be
2603  // serialized as null. It leaves this decision to the user. But
2604  // it also provides the proper toolbox for the user to implement
2605  // its intended solution.
2606  //
2607  // writing/disambiguating null values:
2608  ryml::csubstr null = {};
2609  ryml::csubstr nonnull = "";
2610  ryml::csubstr strnull = "null";
2611  ryml::csubstr tilde = "~";
2612  CHECK(null .len == 0); CHECK(null .str == nullptr); CHECK(null == nullptr);
2613  CHECK(nonnull.len == 0); CHECK(nonnull.str != nullptr); CHECK(nonnull != nullptr);
2614  CHECK(strnull.len != 0); CHECK(strnull.str != nullptr); CHECK(strnull != nullptr);
2615  CHECK(tilde .len != 0); CHECK(tilde .str != nullptr); CHECK(tilde != nullptr);
2616  tree.clear();
2617  tree.clear_arena();
2618  tree.rootref() |= ryml::MAP;
2619  // serializes as an empty plain scalar:
2620  tree["empty_null"] << null; CHECK(tree.arena() == "");
2621  // serializes as an empty quoted scalar:
2622  tree["empty_nonnull"] << nonnull; CHECK(tree.arena() == "");
2623  // serializes as the normal 'null' string:
2624  tree["str_null"] << strnull; CHECK(tree.arena() == "null");
2625  // serializes as the normal '~' string:
2626  tree["str_tilde"] << tilde; CHECK(tree.arena() == "null~");
2627  // this is the resulting yaml:
2628  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(empty_null:
2629 empty_nonnull: ''
2630 str_null: null
2631 str_tilde: ~
2632 )");
2633  // To enforce a particular concept of what is a null string, you
2634  // can use the appropriate condition based on pointer nulity or
2635  // other appropriate criteria.
2636  //
2637  // As an example, proper comparison to nullptr:
2638  auto null_if_nullptr = [](ryml::csubstr s) {
2639  return s.str == nullptr ? "null" : s;
2640  };
2641  tree["empty_null"] << null_if_nullptr(null);
2642  tree["empty_nonnull"] << null_if_nullptr(nonnull);
2643  tree["str_null"] << null_if_nullptr(strnull);
2644  tree["str_tilde"] << null_if_nullptr(tilde);
2645  // this is the resulting yaml:
2646  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(empty_null: null
2647 empty_nonnull: ''
2648 str_null: null
2649 str_tilde: ~
2650 )");
2651  //
2652  // As another example, nulity check based on the YAML nulity
2653  // predicate:
2654  auto null_if_predicate = [](ryml::csubstr s) {
2655  return ryml::scalar_is_null(s) ? "null" : s;
2656  };
2657  tree["empty_null"] << null_if_predicate(null);
2658  tree["empty_nonnull"] << null_if_predicate(nonnull);
2659  tree["str_null"] << null_if_predicate(strnull);
2660  tree["str_tilde"] << null_if_predicate(tilde);
2661  // this is the resulting yaml:
2662  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(empty_null: null
2663 empty_nonnull: ''
2664 str_null: null
2665 str_tilde: null
2666 )");
2667  //
2668  // As another example, nulity check based on the YAML nulity
2669  // predicate, but returning "~" to simbolize nulity:
2670  auto tilde_if_predicate = [](ryml::csubstr s) {
2671  return ryml::scalar_is_null(s) ? "~" : s;
2672  };
2673  tree["empty_null"] << tilde_if_predicate(null);
2674  tree["empty_nonnull"] << tilde_if_predicate(nonnull);
2675  tree["str_null"] << tilde_if_predicate(strnull);
2676  tree["str_tilde"] << tilde_if_predicate(tilde);
2677  // this is the resulting yaml:
2678  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(empty_null: ~
2679 empty_nonnull: ''
2680 str_null: ~
2681 str_tilde: ~
2682 )");
2683 }
2684 
2685 
2686 //-----------------------------------------------------------------------------
2687 
2688 /** ryml provides facilities for formatting/deformatting (imported
2689  * from c4core into the ryml namespace). See @ref doc_format_utils
2690  * . These functions are very useful to serialize and deserialize
2691  * scalar types; see @ref doc_serialization .
2692  */
2694 {
2695  // format(), format_sub(), formatrs(): format arguments
2696  {
2697  char buf_[256] = {};
2698  ryml::substr buf = buf_;
2699  size_t size = ryml::format(buf, "a={} foo {} {} bar {}", 0.1, 10, 11, 12);
2700  CHECK(size == strlen("a=0.1 foo 10 11 bar 12"));
2701  CHECK(buf.first(size) == "a=0.1 foo 10 11 bar 12");
2702  // it is safe to call on an empty buffer:
2703  // returns the size needed for the result, and no overflow occurs:
2704  size = ryml::format({} , "a={} foo {} {} bar {}", "this_is_a", 10, 11, 12);
2705  CHECK(size == ryml::format(buf, "a={} foo {} {} bar {}", "this_is_a", 10, 11, 12));
2706  CHECK(size == strlen("a=this_is_a foo 10 11 bar 12"));
2707  // it is also safe to call on an insufficient buffer:
2708  char smallbuf[8] = {};
2709  size = ryml::format(smallbuf, "{} is too large {}", "this", "for the buffer");
2710  CHECK(size == strlen("this is too large for the buffer"));
2711  // ... and the result is truncated at the buffer size:
2712  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "this is\0");
2713 
2714  // format_sub() directly returns the written string:
2715  ryml::csubstr result = ryml::format_sub(buf, "b={}, damn it.", 1);
2716  CHECK(result == "b=1, damn it.");
2717  CHECK(result.is_sub(buf));
2718 
2719  // formatrs() means FORMAT & ReSize:
2720  //
2721  // Instead of a substr, it receives any owning linear char container
2722  // for which to_substr() is defined (using ADL).
2723  // <ryml_std.hpp> has to_substr() definitions for std::string and
2724  // std::vector<char>.
2725  //
2726  // formatrs() starts by calling format(), and if needed, resizes the container
2727  // and calls format() again.
2728  //
2729  // Note that unless the container is previously sized, this
2730  // may cause an allocation, which will make your code slower.
2731  // Make sure to call .reserve() on the container for real
2732  // production code.
2733  std::string sbuf;
2734  ryml::formatrs(&sbuf, "and c={} seems about right", 2);
2735  CHECK(sbuf == "and c=2 seems about right");
2736  std::vector<char> vbuf; // works with any linear char container
2737  ryml::formatrs(&vbuf, "and c={} seems about right", 2);
2738  CHECK(sbuf == "and c=2 seems about right");
2739  // with formatrs() it is also possible to append:
2740  ryml::formatrs_append(&sbuf, ", and finally d={} - done", 3);
2741  CHECK(sbuf == "and c=2 seems about right, and finally d=3 - done");
2742  }
2743 
2744  // unformat(): read arguments - opposite of format()
2745  {
2746  char buf_[256];
2747 
2748  int a = 0, b = 1, c = 2;
2749  ryml::csubstr result = ryml::format_sub(buf_, "{} and {} and {}", a, b, c);
2750  CHECK(result == "0 and 1 and 2");
2751  int aa = -1, bb = -2, cc = -3;
2752  size_t num_characters = ryml::unformat(result, "{} and {} and {}", aa, bb, cc);
2753  CHECK(num_characters != ryml::csubstr::npos); // if a conversion fails, returns ryml::csubstr::npos
2754  CHECK(num_characters == result.size());
2755  CHECK(aa == a);
2756  CHECK(bb == b);
2757  CHECK(cc == c);
2758 
2759  result = ryml::format_sub(buf_, "{} and {} and {}", 10, 20, 30);
2760  CHECK(result == "10 and 20 and 30");
2761  num_characters = ryml::unformat(result, "{} and {} and {}", aa, bb, cc);
2762  CHECK(num_characters != ryml::csubstr::npos); // if a conversion fails, returns ryml::csubstr::npos
2763  CHECK(num_characters == result.size());
2764  CHECK(aa == 10);
2765  CHECK(bb == 20);
2766  CHECK(cc == 30);
2767  }
2768 
2769  // cat(), cat_sub(), catrs(): concatenate arguments
2770  {
2771  char buf_[256] = {};
2772  ryml::substr buf = buf_;
2773  size_t size = ryml::cat(buf, "a=", 0.1, "foo", 10, 11, "bar", 12);
2774  CHECK(size == strlen("a=0.1foo1011bar12"));
2775  CHECK(buf.first(size) == "a=0.1foo1011bar12");
2776  // it is safe to call on an empty buffer:
2777  // returns the size needed for the result, and no overflow occurs:
2778  CHECK(ryml::cat({}, "a=", 0) == 3);
2779  // it is also safe to call on an insufficient buffer:
2780  char smallbuf[8] = {};
2781  size = ryml::cat(smallbuf, "this", " is too large ", "for the buffer");
2782  CHECK(size == strlen("this is too large for the buffer"));
2783  // ... and the result is truncated at the buffer size:
2784  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "this is\0");
2785 
2786  // cat_sub() directly returns the written string:
2787  ryml::csubstr result = ryml::cat_sub(buf, "b=", 1, ", damn it.");
2788  CHECK(result == "b=1, damn it.");
2789  CHECK(result.is_sub(buf));
2790 
2791  // catrs() means CAT & ReSize:
2792  //
2793  // Instead of a substr, it receives any owning linear char container
2794  // for which to_substr() is defined (using ADL).
2795  // <ryml_std.hpp> has to_substr() definitions for std::string and
2796  // std::vector<char>.
2797  //
2798  // catrs() starts by calling cat(), and if needed, resizes the container
2799  // and calls cat() again.
2800  //
2801  // Note that unless the container is previously sized, this
2802  // may cause an allocation, which will make your code slower.
2803  // Make sure to call .reserve() on the container for real
2804  // production code.
2805  std::string sbuf;
2806  ryml::catrs(&sbuf, "and c=", 2, " seems about right");
2807  CHECK(sbuf == "and c=2 seems about right");
2808  std::vector<char> vbuf; // works with any linear char container
2809  ryml::catrs(&vbuf, "and c=", 2, " seems about right");
2810  CHECK(sbuf == "and c=2 seems about right");
2811  // with catrs() it is also possible to append:
2812  ryml::catrs_append(&sbuf, ", and finally d=", 3, " - done");
2813  CHECK(sbuf == "and c=2 seems about right, and finally d=3 - done");
2814  }
2815 
2816  // uncat(): read arguments - opposite of cat()
2817  {
2818  char buf_[256];
2819 
2820  int a = 0, b = 1, c = 2;
2821  ryml::csubstr result = ryml::cat_sub(buf_, a, ' ', b, ' ', c);
2822  CHECK(result == "0 1 2");
2823  int aa = -1, bb = -2, cc = -3;
2824  char sep1 = 'a', sep2 = 'b';
2825  size_t num_characters = ryml::uncat(result, aa, sep1, bb, sep2, cc);
2826  CHECK(num_characters == result.size());
2827  CHECK(aa == a);
2828  CHECK(bb == b);
2829  CHECK(cc == c);
2830  CHECK(sep1 == ' ');
2831  CHECK(sep2 == ' ');
2832 
2833  result = ryml::cat_sub(buf_, 10, ' ', 20, ' ', 30);
2834  CHECK(result == "10 20 30");
2835  num_characters = ryml::uncat(result, aa, sep1, bb, sep2, cc);
2836  CHECK(num_characters == result.size());
2837  CHECK(aa == 10);
2838  CHECK(bb == 20);
2839  CHECK(cc == 30);
2840  CHECK(sep1 == ' ');
2841  CHECK(sep2 == ' ');
2842  }
2843 
2844  // catsep(), catsep_sub(), catseprs(): concatenate arguments, with a separator
2845  {
2846  char buf_[256] = {};
2847  ryml::substr buf = buf_;
2848  // use ' ' as a separator
2849  size_t size = ryml::catsep(buf, ' ', "a=", 0, "b=", 1, "c=", 2, 45, 67);
2850  CHECK(buf.first(size) == "a= 0 b= 1 c= 2 45 67");
2851  // any separator may be used
2852  // use " and " as a separator
2853  size = ryml::catsep(buf, " and ", "a=0", "b=1", "c=2", 45, 67);
2854  CHECK(buf.first(size) == "a=0 and b=1 and c=2 and 45 and 67");
2855  // use " ... " as a separator
2856  size = ryml::catsep(buf, " ... ", "a=0", "b=1", "c=2", 45, 67);
2857  CHECK(buf.first(size) == "a=0 ... b=1 ... c=2 ... 45 ... 67");
2858  // use '/' as a separator
2859  size = ryml::catsep(buf, '/', "a=", 0, "b=", 1, "c=", 2, 45, 67);
2860  CHECK(buf.first(size) == "a=/0/b=/1/c=/2/45/67");
2861  // use 888 as a separator
2862  size = ryml::catsep(buf, 888, "a=0", "b=1", "c=2", 45, 67);
2863  CHECK(buf.first(size) == "a=0888b=1888c=28884588867");
2864 
2865  // it is safe to call on an empty buffer:
2866  // returns the size needed for the result, and no overflow occurs:
2867  CHECK(size == ryml::catsep({}, 888, "a=0", "b=1", "c=2", 45, 67));
2868  // it is also safe to call on an insufficient buffer:
2869  char smallbuf[8] = {};
2870  CHECK(size == ryml::catsep(smallbuf, 888, "a=0", "b=1", "c=2", 45, 67));
2871  CHECK(size == strlen("a=0888b=1888c=28884588867"));
2872  // ... and the result is truncated:
2873  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "a=0888b\0");
2874 
2875  // catsep_sub() directly returns the written substr:
2876  ryml::csubstr result = ryml::catsep_sub(buf, " and ", "a=0", "b=1", "c=2", 45, 67);
2877  CHECK(result == "a=0 and b=1 and c=2 and 45 and 67");
2878  CHECK(result.is_sub(buf));
2879 
2880  // catseprs() means CATSEP & ReSize:
2881  //
2882  // Instead of a substr, it receives any owning linear char container
2883  // for which to_substr() is defined (using ADL).
2884  // <ryml_std.hpp> has to_substr() definitions for std::string and
2885  // std::vector<char>.
2886  //
2887  // catseprs() starts by calling catsep(), and if needed, resizes the container
2888  // and calls catsep() again.
2889  //
2890  // Note that unless the container is previously sized, this
2891  // may cause an allocation, which will make your code slower.
2892  // Make sure to call .reserve() on the container for real
2893  // production code.
2894  std::string sbuf;
2895  ryml::catseprs(&sbuf, " and ", "a=0", "b=1", "c=2", 45, 67);
2896  CHECK(sbuf == "a=0 and b=1 and c=2 and 45 and 67");
2897  std::vector<char> vbuf; // works with any linear char container
2898  ryml::catseprs(&vbuf, " and ", "a=0", "b=1", "c=2", 45, 67);
2899  CHECK(ryml::to_csubstr(vbuf) == "a=0 and b=1 and c=2 and 45 and 67");
2900 
2901  // with catseprs() it is also possible to append:
2902  ryml::catseprs_append(&sbuf, " well ", " --- a=0", "b=11", "c=12", 145, 167);
2903  CHECK(sbuf == "a=0 and b=1 and c=2 and 45 and 67 --- a=0 well b=11 well c=12 well 145 well 167");
2904  }
2905 
2906  // uncatsep(): read arguments with a separator - opposite of catsep()
2907  {
2908  char buf_[256] = {};
2909 
2910  int a = 0, b = 1, c = 2;
2911  ryml::csubstr result = ryml::catsep_sub(buf_, ' ', a, b, c);
2912  CHECK(result == "0 1 2");
2913  int aa = -1, bb = -2, cc = -3;
2914  char sep = 'b';
2915  size_t num_characters = ryml::uncatsep(result, sep, aa, bb, cc);
2916  CHECK(num_characters == result.size());
2917  CHECK(aa == a);
2918  CHECK(bb == b);
2919  CHECK(cc == c);
2920  CHECK(sep == ' ');
2921 
2922  sep = '_';
2923  result = ryml::catsep_sub(buf_, ' ', 10, 20, 30);
2924  CHECK(result == "10 20 30");
2925  num_characters = ryml::uncatsep(result, sep, aa, bb, cc);
2926  CHECK(num_characters == result.size());
2927  CHECK(aa == 10);
2928  CHECK(bb == 20);
2929  CHECK(cc == 30);
2930  CHECK(sep == ' ');
2931  }
2932 
2933  // formatting individual arguments
2934  {
2935  using namespace ryml; // all the symbols below are in the ryml namespace.
2936  char buf_[256] = {}; // all the results below are written in this buffer
2937  substr buf = buf_;
2938  // --------------------------------------
2939  // fmt::boolalpha(): format as true/false
2940  // --------------------------------------
2941  // just as with std streams, printing a bool will output the integer value:
2942  CHECK("0" == cat_sub(buf, false));
2943  CHECK("1" == cat_sub(buf, true));
2944  // to force a "true"/"false", use fmt::boolalpha:
2945  CHECK("false" == cat_sub(buf, fmt::boolalpha(false)));
2946  CHECK("true" == cat_sub(buf, fmt::boolalpha(true)));
2947 
2948  // ---------------------------------
2949  // fmt::hex(): format as hexadecimal
2950  // ---------------------------------
2951  CHECK("0xff" == cat_sub(buf, fmt::hex(255)));
2952  CHECK("0x100" == cat_sub(buf, fmt::hex(256)));
2953  CHECK("-0xff" == cat_sub(buf, fmt::hex(-255)));
2954  CHECK("-0x100" == cat_sub(buf, fmt::hex(-256)));
2955  CHECK("3735928559" == cat_sub(buf, UINT32_C(0xdeadbeef)));
2956  CHECK("0xdeadbeef" == cat_sub(buf, fmt::hex(UINT32_C(0xdeadbeef))));
2957  // ----------------------------
2958  // fmt::bin(): format as binary
2959  // ----------------------------
2960  CHECK("0b1000" == cat_sub(buf, fmt::bin(8)));
2961  CHECK("0b1001" == cat_sub(buf, fmt::bin(9)));
2962  CHECK("0b10001" == cat_sub(buf, fmt::bin(17)));
2963  CHECK("0b11001" == cat_sub(buf, fmt::bin(25)));
2964  CHECK("-0b1000" == cat_sub(buf, fmt::bin(-8)));
2965  CHECK("-0b1001" == cat_sub(buf, fmt::bin(-9)));
2966  CHECK("-0b10001" == cat_sub(buf, fmt::bin(-17)));
2967  CHECK("-0b11001" == cat_sub(buf, fmt::bin(-25)));
2968  // ---------------------------
2969  // fmt::bin(): format as octal
2970  // ---------------------------
2971  CHECK("0o77" == cat_sub(buf, fmt::oct(63)));
2972  CHECK("0o100" == cat_sub(buf, fmt::oct(64)));
2973  CHECK("0o377" == cat_sub(buf, fmt::oct(255)));
2974  CHECK("0o400" == cat_sub(buf, fmt::oct(256)));
2975  CHECK("0o1000" == cat_sub(buf, fmt::oct(512)));
2976  CHECK("-0o77" == cat_sub(buf, fmt::oct(-63)));
2977  CHECK("-0o100" == cat_sub(buf, fmt::oct(-64)));
2978  CHECK("-0o377" == cat_sub(buf, fmt::oct(-255)));
2979  CHECK("-0o400" == cat_sub(buf, fmt::oct(-256)));
2980  CHECK("-0o1000" == cat_sub(buf, fmt::oct(-512)));
2981  // ---------------------------
2982  // fmt::zpad(): pad with zeros
2983  // ---------------------------
2984  CHECK("000063" == cat_sub(buf, fmt::zpad(63, 6)));
2985  CHECK( "00063" == cat_sub(buf, fmt::zpad(63, 5)));
2986  CHECK( "0063" == cat_sub(buf, fmt::zpad(63, 4)));
2987  CHECK( "063" == cat_sub(buf, fmt::zpad(63, 3)));
2988  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 2)));
2989  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 1))); // will never trim the result
2990  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 0))); // will never trim the result
2991  CHECK("0x00003f" == cat_sub(buf, fmt::zpad(fmt::hex(63), 6)));
2992  CHECK("0o000077" == cat_sub(buf, fmt::zpad(fmt::oct(63), 6)));
2993  CHECK("0b00011001" == cat_sub(buf, fmt::zpad(fmt::bin(25), 8)));
2994  // ------------------------------------------------
2995  // fmt::left(): align left with a given field width
2996  // ------------------------------------------------
2997  CHECK("63 " == cat_sub(buf, fmt::left(63, 6)));
2998  CHECK("63 " == cat_sub(buf, fmt::left(63, 5)));
2999  CHECK("63 " == cat_sub(buf, fmt::left(63, 4)));
3000  CHECK("63 " == cat_sub(buf, fmt::left(63, 3)));
3001  CHECK("63" == cat_sub(buf, fmt::left(63, 2)));
3002  CHECK("63" == cat_sub(buf, fmt::left(63, 1))); // will never trim the result
3003  CHECK("63" == cat_sub(buf, fmt::left(63, 0))); // will never trim the result
3004  // the fill character can be specified (defaults to ' '):
3005  CHECK("63----" == cat_sub(buf, fmt::left(63, 6, '-')));
3006  CHECK("63++++" == cat_sub(buf, fmt::left(63, 6, '+')));
3007  CHECK("63////" == cat_sub(buf, fmt::left(63, 6, '/')));
3008  CHECK("630000" == cat_sub(buf, fmt::left(63, 6, '0')));
3009  CHECK("63@@@@" == cat_sub(buf, fmt::left(63, 6, '@')));
3010  CHECK("0x003f " == cat_sub(buf, fmt::left(fmt::zpad(fmt::hex(63), 4), 10)));
3011  // --------------------------------------------------
3012  // fmt::right(): align right with a given field width
3013  // --------------------------------------------------
3014  CHECK(" 63" == cat_sub(buf, fmt::right(63, 6)));
3015  CHECK(" 63" == cat_sub(buf, fmt::right(63, 5)));
3016  CHECK(" 63" == cat_sub(buf, fmt::right(63, 4)));
3017  CHECK(" 63" == cat_sub(buf, fmt::right(63, 3)));
3018  CHECK("63" == cat_sub(buf, fmt::right(63, 2)));
3019  CHECK("63" == cat_sub(buf, fmt::right(63, 1))); // will never trim the result
3020  CHECK("63" == cat_sub(buf, fmt::right(63, 0))); // will never trim the result
3021  // the fill character can be specified (defaults to ' '):
3022  CHECK("----63" == cat_sub(buf, fmt::right(63, 6, '-')));
3023  CHECK("++++63" == cat_sub(buf, fmt::right(63, 6, '+')));
3024  CHECK("////63" == cat_sub(buf, fmt::right(63, 6, '/')));
3025  CHECK("000063" == cat_sub(buf, fmt::right(63, 6, '0')));
3026  CHECK("@@@@63" == cat_sub(buf, fmt::right(63, 6, '@')));
3027  CHECK(" 0x003f" == cat_sub(buf, fmt::right(fmt::zpad(fmt::hex(63), 4), 10)));
3028 
3029  // ------------------------------------------
3030  // fmt::real(): format floating point numbers
3031  // ------------------------------------------
3032  // see also sample_float_precision()
3033  CHECK("0" == cat_sub(buf, fmt::real(0.01f, 0)));
3034  CHECK("0.0" == cat_sub(buf, fmt::real(0.01f, 1)));
3035  CHECK("0.01" == cat_sub(buf, fmt::real(0.01f, 2)));
3036  CHECK("0.010" == cat_sub(buf, fmt::real(0.01f, 3)));
3037  CHECK("0.0100" == cat_sub(buf, fmt::real(0.01f, 4)));
3038  CHECK("0.01000" == cat_sub(buf, fmt::real(0.01f, 5)));
3039  CHECK("1" == cat_sub(buf, fmt::real(1.01f, 0)));
3040  CHECK("1.0" == cat_sub(buf, fmt::real(1.01f, 1)));
3041  CHECK("1.01" == cat_sub(buf, fmt::real(1.01f, 2)));
3042  CHECK("1.010" == cat_sub(buf, fmt::real(1.01f, 3)));
3043  CHECK("1.0100" == cat_sub(buf, fmt::real(1.01f, 4)));
3044  CHECK("1.01000" == cat_sub(buf, fmt::real(1.01f, 5)));
3045  CHECK("1" == cat_sub(buf, fmt::real(1.234234234, 0)));
3046  CHECK("1.2" == cat_sub(buf, fmt::real(1.234234234, 1)));
3047  CHECK("1.23" == cat_sub(buf, fmt::real(1.234234234, 2)));
3048  CHECK("1.234" == cat_sub(buf, fmt::real(1.234234234, 3)));
3049  CHECK("1.2342" == cat_sub(buf, fmt::real(1.234234234, 4)));
3050  CHECK("1.23423" == cat_sub(buf, fmt::real(1.234234234, 5)));
3051  CHECK("1000000.00000" == cat_sub(buf, fmt::real(1000000.000000000, 5)));
3052  CHECK("1234234.23423" == cat_sub(buf, fmt::real(1234234.234234234, 5)));
3053  // AKA %f
3054  CHECK("1000000.00000" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_FLOAT))); // AKA %f, same as above
3055  CHECK("1234234.23423" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_FLOAT))); // AKA %f
3056  CHECK("1234234.2342" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_FLOAT))); // AKA %f
3057  CHECK("1234234.234" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_FLOAT))); // AKA %f
3058  CHECK("1234234.23" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_FLOAT))); // AKA %f
3059  // AKA %e
3060  CHECK("1.00000e+06" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_SCIENT))); // AKA %e
3061  CHECK("1.23423e+06" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_SCIENT))); // AKA %e
3062  CHECK("1.2342e+06" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_SCIENT))); // AKA %e
3063  CHECK("1.234e+06" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_SCIENT))); // AKA %e
3064  CHECK("1.23e+06" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_SCIENT))); // AKA %e
3065  // AKA %g
3066  CHECK("1e+06" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_FLEX))); // AKA %g
3067  CHECK("1.2342e+06" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_FLEX))); // AKA %g
3068  CHECK("1.234e+06" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_FLEX))); // AKA %g
3069  CHECK("1.23e+06" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_FLEX))); // AKA %g
3070  CHECK("1.2e+06" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_FLEX))); // AKA %g
3071  // FTOA_HEXA: AKA %a (hexadecimal formatting of floats)
3072  CHECK("0x1.e8480p+19" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_HEXA))); // AKA %a
3073  CHECK("0x1.2d53ap+20" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_HEXA))); // AKA %a
3074 
3075  // --------------------------------------------------------------
3076  // fmt::raw(): dump data in machine format (respecting alignment)
3077  // --------------------------------------------------------------
3078  {
3079  C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wcast-align") // we're casting the values directly, so alignment is strictly respected.
3080  const uint32_t payload[] = {10, 20, 30, 40, UINT32_C(0xdeadbeef)};
3081  // (package payload as a substr, for comparison only)
3082  csubstr expected = csubstr((const char *)payload, sizeof(payload));
3083  csubstr actual = cat_sub(buf, fmt::raw(payload));
3084  CHECK(!actual.overlaps(expected));
3085  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3086  // also possible with variables:
3087  for(const uint32_t value : payload)
3088  {
3089  // (package payload as a substr, for comparison only)
3090  expected = csubstr((const char *)&value, sizeof(value));
3091  actual = cat_sub(buf, fmt::raw(value));
3092  CHECK(actual.size() == sizeof(uint32_t));
3093  CHECK(!actual.overlaps(expected));
3094  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3095  // with non-const data, fmt::craw() may be needed for disambiguation:
3096  actual = cat_sub(buf, fmt::craw(value));
3097  CHECK(actual.size() == sizeof(uint32_t));
3098  CHECK(!actual.overlaps(expected));
3099  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3100  //
3101  // read back:
3102  uint32_t result;
3103  auto reader = fmt::raw(result); // keeps a reference to result
3104  CHECK(&result == (uint32_t*)reader.buf);
3105  CHECK(reader.len == sizeof(uint32_t));
3106  uncat(actual, reader);
3107  // and compare:
3108  // (vs2017/release/32bit does not reload result from cache, so force it)
3109  result = *(uint32_t*)reader.buf;
3110  CHECK(result == value); // roundtrip completed successfully
3111  }
3112  C4_SUPPRESS_WARNING_GCC_CLANG_POP
3113  }
3114 
3115  // -------------------------
3116  // fmt::base64(): see below!
3117  // -------------------------
3118  }
3119 }
3120 
3121 
3122 //-----------------------------------------------------------------------------
3123 
3124 /** demonstrates how to read and write base64-encoded blobs.
3125  @see @ref doc_base64
3126  */
3128 {
3129  ryml::Tree tree;
3130  tree.rootref() |= ryml::MAP;
3131  struct text_and_base64 { ryml::csubstr text, base64; };
3132  text_and_base64 cases[] = {
3133  {{"Love all, trust a few, do wrong to none."}, {"TG92ZSBhbGwsIHRydXN0IGEgZmV3LCBkbyB3cm9uZyB0byBub25lLg=="}},
3134  {{"The fool doth think he is wise, but the wise man knows himself to be a fool."}, {"VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg=="}},
3135  {{"Brevity is the soul of wit."}, {"QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu"}},
3136  {{"All that glitters is not gold."}, {"QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu"}},
3137  };
3138  // to encode base64 and write the result to val:
3139  for(text_and_base64 c : cases)
3140  {
3141  tree[c.text] << ryml::fmt::base64(c.text);
3142  CHECK(tree[c.text].val() == c.base64);
3143  }
3144  // to encode base64 and write the result to key:
3145  for(text_and_base64 c : cases)
3146  {
3147  tree.rootref().append_child() << ryml::key(ryml::fmt::base64(c.text)) << c.text;
3148  CHECK(tree[c.base64].val() == c.text);
3149  }
3150  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"('Love all, trust a few, do wrong to none.': TG92ZSBhbGwsIHRydXN0IGEgZmV3LCBkbyB3cm9uZyB0byBub25lLg==
3151 'The fool doth think he is wise, but the wise man knows himself to be a fool.': VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg==
3152 Brevity is the soul of wit.: QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu
3153 All that glitters is not gold.: QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu
3154 TG92ZSBhbGwsIHRydXN0IGEgZmV3LCBkbyB3cm9uZyB0byBub25lLg==: 'Love all, trust a few, do wrong to none.'
3155 VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg==: 'The fool doth think he is wise, but the wise man knows himself to be a fool.'
3156 QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu: Brevity is the soul of wit.
3157 QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu: All that glitters is not gold.
3158 )");
3159  char buf1_[128], buf2_[128];
3160  ryml::substr buf1 = buf1_; // this is where we will write the result (using >>)
3161  ryml::substr buf2 = buf2_; // this is where we will write the result (using deserialize_val()/deserialize_key())
3162  std::string result = {}; // show also how to decode to a std::string
3163  // to decode the val base64 and write the result to buf:
3164  for(const text_and_base64 c : cases)
3165  {
3166  // write the decoded result into the given buffer
3167  tree[c.text] >> ryml::fmt::base64(buf1); // cannot know the needed size
3168  size_t len = tree[c.text].deserialize_val(ryml::fmt::base64(buf2)); // returns the needed size
3169  CHECK(len <= buf1.len);
3170  CHECK(len <= buf2.len);
3171  CHECK(c.text.len == len);
3172  CHECK(buf1.first(len) == c.text);
3173  CHECK(buf2.first(len) == c.text);
3174  //
3175  // interop with std::string: using substr
3176  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3177  len = tree[c.text].deserialize_val(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3178  if(len > result.size()) // the size was not enough; resize and call again
3179  {
3180  result.resize(len);
3181  len = tree[c.text].deserialize_val(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3182  }
3183  result.resize(len); // trim to the length of the decoded buffer
3184  CHECK(result == c.text);
3185  //
3186  // interop with std::string: using blob
3187  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3188  ryml::blob strblob(&result[0], result.size());
3189  CHECK(strblob.buf == result.data());
3190  CHECK(strblob.len == result.size());
3191  len = tree[c.text].deserialize_val(ryml::fmt::base64(strblob)); // returns the needed size
3192  if(len > result.size()) // the size was not enough; resize and call again
3193  {
3194  result.resize(len);
3195  strblob = {&result[0], result.size()};
3196  CHECK(strblob.buf == result.data());
3197  CHECK(strblob.len == result.size());
3198  len = tree[c.text].deserialize_val(ryml::fmt::base64(strblob)); // returns the needed size
3199  }
3200  result.resize(len); // trim to the length of the decoded buffer
3201  CHECK(result == c.text);
3202  //
3203  // Note also these are just syntatic wrappers to simplify client code.
3204  // You can call into the lower level functions without much effort:
3205  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3206  ryml::csubstr encoded = tree[c.text].val();
3207  CHECK(encoded == c.base64);
3208  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3209  if(len > result.size()) // the size was not enough; resize and call again
3210  {
3211  result.resize(len);
3212  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3213  }
3214  result.resize(len); // trim to the length of the decoded buffer
3215  CHECK(result == c.text);
3216  }
3217  // to decode the key base64 and write the result to buf:
3218  for(const text_and_base64 c : cases)
3219  {
3220  // write the decoded result into the given buffer
3221  tree[c.base64] >> ryml::key(ryml::fmt::base64(buf1)); // cannot know the needed size
3222  size_t len = tree[c.base64].deserialize_key(ryml::fmt::base64(buf2)); // returns the needed size
3223  CHECK(len <= buf1.len);
3224  CHECK(len <= buf2.len);
3225  CHECK(c.text.len == len);
3226  CHECK(buf1.first(len) == c.text);
3227  CHECK(buf2.first(len) == c.text);
3228  // interop with std::string: using substr
3229  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3230  len = tree[c.base64].deserialize_key(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3231  if(len > result.size()) // the size was not enough; resize and call again
3232  {
3233  result.resize(len);
3234  len = tree[c.base64].deserialize_key(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3235  }
3236  result.resize(len); // trim to the length of the decoded buffer
3237  CHECK(result == c.text);
3238  //
3239  // interop with std::string: using blob
3240  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3241  ryml::blob strblob = {&result[0], result.size()};
3242  CHECK(strblob.buf == result.data());
3243  CHECK(strblob.len == result.size());
3244  len = tree[c.base64].deserialize_key(ryml::fmt::base64(strblob)); // returns the needed size
3245  if(len > result.size()) // the size was not enough; resize and call again
3246  {
3247  result.resize(len);
3248  strblob = {&result[0], result.size()};
3249  CHECK(strblob.buf == result.data());
3250  CHECK(strblob.len == result.size());
3251  len = tree[c.base64].deserialize_key(ryml::fmt::base64(strblob)); // returns the needed size
3252  }
3253  result.resize(len); // trim to the length of the decoded buffer
3254  CHECK(result == c.text);
3255  //
3256  // Note also these are just syntactic wrappers to simplify client code.
3257  // You can call into the lower level functions without much effort:
3258  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3259  ryml::csubstr encoded = tree[c.base64].key();
3260  CHECK(encoded == c.base64);
3261  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3262  if(len > result.size()) // the size was not enough; resize and call again
3263  {
3264  result.resize(len);
3265  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3266  }
3267  result.resize(len); // trim to the length of the decoded buffer
3268  CHECK(result == c.text);
3269  }
3270  // directly encode variables
3271  {
3272  const uint64_t valin = UINT64_C(0xdeadbeef);
3273  uint64_t valout = 0;
3274  tree["deadbeef"] << c4::fmt::base64(valin); // sometimes cbase64() is needed to avoid ambiguity
3275  size_t len = tree["deadbeef"].deserialize_val(ryml::fmt::base64(valout));
3276  CHECK(len <= sizeof(valout));
3277  CHECK(valout == UINT64_C(0xdeadbeef)); // base64 roundtrip is bit-accurate
3278  }
3279  // directly encode memory ranges
3280  {
3281  const uint32_t data_in[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xdeadbeef};
3282  uint32_t data_out[11] = {};
3283  CHECK(memcmp(data_in, data_out, sizeof(data_in)) != 0); // before the roundtrip
3284  tree["int_data"] << c4::fmt::base64(data_in);
3285  size_t len = tree["int_data"].deserialize_val(ryml::fmt::base64(data_out));
3286  CHECK(len <= sizeof(data_out));
3287  CHECK(memcmp(data_in, data_out, sizeof(data_in)) == 0); // after the roundtrip
3288  }
3289 }
3290 
3291 
3292 //-----------------------------------------------------------------------------
3293 // Serialization info
3294 
3295 } // namespace sample // because we want the doxygen document above to show up in the proper place
3296 /** @addtogroup doc_serialization
3297  *
3298  * ## Fundamental types
3299  *
3300  * ryml provides serialization/deserialization utilities for all
3301  * fundamental data types in @ref doc_charconv .
3302  *
3303  * - See @ref sample::sample_fundamental_types() for basic examples
3304  * of serialization of fundamental types.
3305  * - See @ref sample::sample_empty_null_values() for different ways
3306  * to serialize and deserialize empty and null values/
3307  * - When serializing floating point values in C++ earlier than
3308  * 17, be aware that there may be a truncation of the precision
3309  * with the default float/double implementations of @ref
3310  * doc_to_chars. To enforce a particular precision, use for
3311  * example @ref c4::fmt::real, or call directly @ref c4::ftoa() or
3312  * @ref c4::dtoa(), or any other method (remember that ryml only
3313  * stores the final string in the tree, so nothing prevents you from
3314  * creating it in whatever way is most suitable). See the relevant
3315  * sample: @ref sample::sample_float_precision().
3316  * - You can also serialize and deserialize base64: see @ref
3317  * doc_base64 and @ref sample::sample_base64
3318  *
3319  * To serialize/deserialize any non-fundamental type will require
3320  * that you instruct ryml on how to achieve this. That will differ
3321  * based on whether the type is scalar or container.
3322  *
3323  *
3324  * ## User scalar types
3325  *
3326  * See @ref doc_sample_scalar_types for serializing user scalar types
3327  * (ie leaf nodes in the YAML tree, containing a string
3328  * representation):
3329  *
3330  * - See examples on how to @ref doc_sample_to_chars_scalar
3331  * - See examples on how to @ref doc_sample_from_chars_scalar
3332  * - See the sample @ref sample::sample_user_scalar_types
3333  * - See the sample @ref sample::sample_formatting for examples
3334  * of functions from @ref doc_format_utils that will be very
3335  * helpful in implementing custom `to_chars()`/`from_chars()`
3336  * functions.
3337  * - See @ref doc_charconv for the implementations of
3338  * `to_chars()`/`from_chars()` for the fundamental types.
3339  * - See @ref doc_substr and @ref sample::sample_substr() for the
3340  * many useful utilities in the substring class.
3341  *
3342  *
3343  * ## User container types
3344  *
3345  * - See @ref doc_sample_container_types for when the type is a
3346  * container (ie, a node which has children, which may themselves be
3347  * containers).
3348  *
3349  * - See the sample @ref sample::sample_user_container_types
3350  *
3351  * - See the sample @ref sample::sample_std_types, and also...
3352  *
3353  *
3354  * ## STL types
3355  *
3356  * ryml does not use any STL containers internally, but it can be
3357  * used to serialize and deserialize these containers. See @ref
3358  * sample::sample_std_types() for an example. See the header @ref
3359  * ryml_std.hpp and also the headers it includes:
3360  *
3361  * - scalar types:
3362  * - for `std::string`: @ref ext/c4core/src/c4/std/string.hpp
3363  * - for `std::string_view`: @ref ext/c4core/src/c4/std/string_view.hpp
3364  * - for `std::vector<char>`: @ref ext/c4core/src/c4/std/vector.hpp
3365  * - container types:
3366  * - for `std::vector<T>`: @ref src/c4/yml/std/vector.hpp
3367  * - for `std::map<K,V>`: @ref src/c4/yml/std/map.hpp
3368  *
3369  */
3370 namespace sample { // because we want the doxygen document above to show up in the proper place
3371 
3372 
3373 //-----------------------------------------------------------------------------
3374 // user scalar types: implemented in ryml through to_chars() + from_chars()
3375 
3376 /** @addtogroup doc_sample_helpers
3377  * @{ */
3378 
3379 /** @defgroup doc_sample_scalar_types Serialize/deserialize scalar types
3380  * @{ */
3381 
3382 template<class T> struct vec2 { T x, y; }; ///< example scalar type, serialized and deserialized
3383 template<class T> struct vec3 { T x, y, z; }; ///< example scalar type, serialized and deserialized
3384 template<class T> struct vec4 { T x, y, z, w; }; ///< example scalar type, serialized and deserialized
3385 
3386 template<class T> struct parse_only_vec2 { T x, y; }; ///< example scalar type, deserialized only
3387 template<class T> struct parse_only_vec3 { T x, y, z; }; ///< example scalar type, deserialized only
3388 template<class T> struct parse_only_vec4 { T x, y, z, w; }; ///< example scalar type, deserialized only
3389 
3390 template<class T> struct emit_only_vec2 { T x, y; }; ///< example scalar type, serialized only
3391 template<class T> struct emit_only_vec3 { T x, y, z; }; ///< example scalar type, serialized only
3392 template<class T> struct emit_only_vec4 { T x, y, z, w; }; ///< example scalar type, serialized only
3393 
3394 /** @defgroup doc_sample_to_chars_scalar Define to_chars to write scalar types
3395  *
3396  * @brief To serialize user scalar types, implement the appropriate
3397  * function to_chars (see also @ref doc_to_chars):
3398  *
3399  * ```cpp
3400  * // any of these can be used:
3401  * size_t to_chars(substr buf, T const& v);
3402  * size_t to_chars(substr buf, T v); // this also works, and is good when the type is small
3403  * ```
3404  *
3405  * See the sample @ref sample_user_scalar_types() for an example usage.
3406  *
3407  * Your implementation of to_chars must format v to the given string
3408  * view + return the number of characters written into it. The view
3409  * size (buf.len) must be strictly respected. Return the number of
3410  * characters that need to be written for the value to be completely
3411  * serialized in the string. So if the return value is larger than
3412  * buf.len, ryml will know that the buffer resize the buffer and call
3413  * this again with a larger buffer of the correct size.
3414  *
3415  * In your implementation, you may be interested in using the
3416  * formatting facilities in @ref doc_format_utils and @ref doc_charconv;
3417  * refer to their documentation for further details. But this is not
3418  * mandatory, and anything can be used, provided that the implemented
3419  * `to_chars()` fulfills its contract, described above.
3420  *
3421  * @warning Because of [C++'s ADL
3422  * rules](http://en.cppreference.com/w/cpp/language/adl), **it is
3423  * required to overload these functions in the namespace of the type**
3424  * you're serializing (or in the c4 namespace, or in the c4::yml
3425  * namespace). [Here's an example of an issue where failing to do this
3426  * was causing problems in some
3427  * platforms](https://github.com/biojppm/rapidyaml/issues/424)
3428  *
3429  * @note Please take note of the following pitfall when using
3430  * serialization functions: you may have to include the header with
3431  * your `to_chars()` implementation before any other headers that use
3432  * functions from it. See the include order at the top of this source
3433  * file. This constraint also applies to the conversion functions for
3434  * your types; just like with the STL's headers, they should be
3435  * included prior to ryml's headers. Lately, some effort was directed
3436  * to provide forward declarations to alleviate this problem, but it
3437  * may still occur.
3438  *
3439  * @see string.hpp
3440  * @see string_view.hpp
3441  * @{
3442  */
3443 template<class T> size_t to_chars(ryml::substr buf, vec2<T> v) { return ryml::format(buf, "({},{})", v.x, v.y); }
3444 template<class T> size_t to_chars(ryml::substr buf, vec3<T> v) { return ryml::format(buf, "({},{},{})", v.x, v.y, v.z); }
3445 template<class T> size_t to_chars(ryml::substr buf, vec4<T> v) { return ryml::format(buf, "({},{},{},{})", v.x, v.y, v.z, v.w); }
3446 
3447 template<class T> size_t to_chars(ryml::substr buf, emit_only_vec2<T> v) { return ryml::format(buf, "({},{})", v.x, v.y); }
3448 template<class T> size_t to_chars(ryml::substr buf, emit_only_vec3<T> v) { return ryml::format(buf, "({},{},{})", v.x, v.y, v.z); }
3449 template<class T> size_t to_chars(ryml::substr buf, emit_only_vec4<T> v) { return ryml::format(buf, "({},{},{},{})", v.x, v.y, v.z, v.w); }
3450 /** @} */
3451 
3452 
3453 /** @defgroup doc_sample_from_chars_scalar Define from_chars to read scalar types
3454  *
3455  * @brief To deserialize user scalar types, implement the
3456  * function `bool from_chars(csubstr buf, T *val)`; see @ref
3457  * doc_from_chars.
3458  *
3459  * The implementation of from_chars must never read beyond the limit
3460  * of the given buffer, and must return true/false to indicate
3461  * success/failure in the deserialization. On failure, it is up to you
3462  * whether the value is left unchanged; ryml itself does not care
3463  * about the value when the deserialization failed.
3464  *
3465  * In your implementation, you may be interested in using the
3466  * reading facilities in @ref doc_format_utils and @ref doc_charconv;
3467  * refer to their documentation for further details. But this is not
3468  * mandatory, and anything can be used, provided that the implemented
3469  * from_chars fulfills its contract, described above.
3470  *
3471  * @warning Because of [C++'s ADL
3472  * rules](http://en.cppreference.com/w/cpp/language/adl), **it is
3473  * required to overload these functions in the namespace of the type**
3474  * you're serializing (or in the c4 namespace, or in the c4::yml
3475  * namespace). [Here's an example of an issue where failing to do this
3476  * was causing problems in some
3477  * platforms](https://github.com/biojppm/rapidyaml/issues/424)
3478  *
3479  * @note Please take note of the following pitfall when using
3480  * serialization functions: you may have to include the header with
3481  * your `from_chars()` implementation before any other headers that use
3482  * functions from it. See the include order at the top of this source
3483  * file. This constraint also applies to the conversion functions for
3484  * your types; just like with the STL's headers, they should be
3485  * included prior to ryml's headers. Lately, some effort was directed
3486  * to provide forward declarations to alleviate this problem, but it
3487  * may still occur.
3488  *
3489  * @{
3490  */
3491 template<class T> bool from_chars(ryml::csubstr buf, vec2<T> *v) { size_t ret = ryml::unformat(buf, "({},{})", v->x, v->y); return ret != ryml::yml::npos; }
3492 template<class T> bool from_chars(ryml::csubstr buf, vec3<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{})", v->x, v->y, v->z); return ret != ryml::yml::npos; }
3493 template<class T> bool from_chars(ryml::csubstr buf, vec4<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{},{})", v->x, v->y, v->z, v->w); return ret != ryml::yml::npos; }
3494 
3495 template<class T> bool from_chars(ryml::csubstr buf, parse_only_vec2<T> *v) { size_t ret = ryml::unformat(buf, "({},{})", v->x, v->y); return ret != ryml::yml::npos; }
3496 template<class T> bool from_chars(ryml::csubstr buf, parse_only_vec3<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{})", v->x, v->y, v->z); return ret != ryml::yml::npos; }
3497 template<class T> bool from_chars(ryml::csubstr buf, parse_only_vec4<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{},{})", v->x, v->y, v->z, v->w); return ret != ryml::yml::npos; }
3498 /** @} */ // doc_sample_from_chars_scalar
3499 
3500 /** @} */ // doc_sample_scalar_types
3501 /** @} */ // doc_sample_helpers
3502 
3503 
3504 /** to add scalar types (ie leaf types converting to/from string),
3505  * define the functions above for those types. See @ref
3506  * doc_sample_scalar_types. */
3508 {
3509  ryml::Tree t;
3510 
3511  auto r = t.rootref();
3512  r |= ryml::MAP;
3513 
3514  vec2<int> v2in{10, 11};
3515  vec2<int> v2out{1, 2};
3516  r["v2"] << v2in; // serializes to the tree's arena, and then sets the keyval
3517  r["v2"] >> v2out;
3518  CHECK(v2in.x == v2out.x);
3519  CHECK(v2in.y == v2out.y);
3520  vec3<int> v3in{100, 101, 102};
3521  vec3<int> v3out{1, 2, 3};
3522  r["v3"] << v3in; // serializes to the tree's arena, and then sets the keyval
3523  r["v3"] >> v3out;
3524  CHECK(v3in.x == v3out.x);
3525  CHECK(v3in.y == v3out.y);
3526  CHECK(v3in.z == v3out.z);
3527  vec4<int> v4in{1000, 1001, 1002, 1003};
3528  vec4<int> v4out{1, 2, 3, 4};
3529  r["v4"] << v4in; // serializes to the tree's arena, and then sets the keyval
3530  r["v4"] >> v4out;
3531  CHECK(v4in.x == v4out.x);
3532  CHECK(v4in.y == v4out.y);
3533  CHECK(v4in.z == v4out.z);
3534  CHECK(v4in.w == v4out.w);
3535  CHECK(ryml::emitrs_yaml<std::string>(t) == R"(v2: '(10,11)'
3536 v3: '(100,101,102)'
3537 v4: '(1000,1001,1002,1003)'
3538 )");
3539 
3540  // note that only the used functions are needed:
3541  // - if a type is only parsed, then only from_chars() is needed
3542  // - if a type is only emitted, then only to_chars() is needed
3543  emit_only_vec2<int> eov2in{20, 21}; // only has to_chars()
3544  parse_only_vec2<int> pov2out{1, 2}; // only has from_chars()
3545  r["v2"] << eov2in; // serializes to the tree's arena, and then sets the keyval
3546  r["v2"] >> pov2out;
3547  CHECK(eov2in.x == pov2out.x);
3548  CHECK(eov2in.y == pov2out.y);
3549  emit_only_vec3<int> eov3in{30, 31, 32}; // only has to_chars()
3550  parse_only_vec3<int> pov3out{1, 2, 3}; // only has from_chars()
3551  r["v3"] << eov3in; // serializes to the tree's arena, and then sets the keyval
3552  r["v3"] >> pov3out;
3553  CHECK(eov3in.x == pov3out.x);
3554  CHECK(eov3in.y == pov3out.y);
3555  CHECK(eov3in.z == pov3out.z);
3556  emit_only_vec4<int> eov4in{40, 41, 42, 43}; // only has to_chars()
3557  parse_only_vec4<int> pov4out{1, 2, 3, 4}; // only has from_chars()
3558  r["v4"] << eov4in; // serializes to the tree's arena, and then sets the keyval
3559  r["v4"] >> pov4out;
3560  CHECK(eov4in.x == pov4out.x);
3561  CHECK(eov4in.y == pov4out.y);
3562  CHECK(eov4in.z == pov4out.z);
3563  CHECK(ryml::emitrs_yaml<std::string>(t) == R"(v2: '(20,21)'
3564 v3: '(30,31,32)'
3565 v4: '(40,41,42,43)'
3566 )");
3567 }
3568 
3569 
3570 //-----------------------------------------------------------------------------
3571 // user container types: implemented in ryml through write() + read()
3572 
3573 /** @addtogroup doc_sample_helpers
3574  * @{ */
3575 
3576 /** @defgroup doc_sample_container_types Serialize/deserialize container types
3577  *
3578  * To serialize/deserialize container types to a tree, implement the
3579  * appropriate functions:
3580  *
3581  * ```cpp
3582  * void write(ryml::NodeRef *n, T const& seq);
3583  * bool read(ryml::ConstNodeRef const& n, T *seq);
3584  * ```
3585  *
3586  * @warning Because of [C++'s ADL
3587  * rules](http://en.cppreference.com/w/cpp/language/adl), **it is
3588  * required to overload these functions in the namespace of the type**
3589  * you're serializing (or in the c4 namespace, or in the c4::yml
3590  * namespace). [Here's an example of an issue where failing to do this
3591  * was causing problems in some
3592  * platforms](https://github.com/biojppm/rapidyaml/issues/424)
3593  *
3594  * @note Please take note of the following pitfall when using
3595  * serialization functions: you may have to include the header with
3596  * your `write()` or `read()` implementation before any other headers
3597  * that use functions from it. See the include order at the top of
3598  * this source file. This constraint also applies to the conversion
3599  * functions for your types; just like with the STL's headers, they
3600  * should be included prior to ryml's headers. Lately, some effort was
3601  * directed to provide forward declarations to alleviate this problem,
3602  * but it may still occur.
3603  *
3604  * @see sample::sample_container_types
3605  * @see sample::sample_std_types
3606  *
3607  * @{ */
3608 
3609 
3610 /** example user container type: seq-like */
3611 template<class T>
3613 {
3614  std::vector<T> seq_member;
3615 };
3616 /** example user container type: map-like */
3617 template<class K, class V>
3619 {
3620  std::map<K, V> map_member;
3621 };
3622 /** example user container type with nested container members.
3623  * notice all the members have user-defined serialization methods. */
3624 struct my_type
3625 {
3626  // these are leaf nodes:
3630  // these are container nodes:
3633 };
3634 
3635 template<class T>
3636 void write(ryml::NodeRef *n, my_seq_type<T> const& seq)
3637 {
3638  *n |= ryml::SEQ;
3639  for(auto const& v : seq.seq_member)
3640  n->append_child() << v;
3641 }
3642 template<class K, class V>
3644 {
3645  *n |= ryml::MAP;
3646  for(auto const& v : map.map_member)
3647  n->append_child() << ryml::key(v.first) << v.second;
3648 }
3649 void write(ryml::NodeRef *n, my_type const& val)
3650 {
3651  *n |= ryml::MAP;
3652  // these are leaf nodes:
3653  n->append_child() << ryml::key("v2") << val.v2;
3654  n->append_child() << ryml::key("v3") << val.v3;
3655  n->append_child() << ryml::key("v4") << val.v4;
3656  // these are container nodes:
3657  n->append_child() << ryml::key("seq") << val.seq;
3658  n->append_child() << ryml::key("map") << val.map;
3659 }
3660 
3661 template<class T>
3663 {
3664  seq->seq_member.resize(static_cast<size_t>(n.num_children())); // num_children() is O(N)
3665  size_t pos = 0;
3666  for(auto const ch : n.children())
3667  ch >> seq->seq_member[pos++];
3668  return true;
3669 }
3670 template<class K, class V>
3672 {
3673  K k{};
3674  V v{};
3675  for(auto const ch : n)
3676  {
3677  ch >> c4::yml::key(k) >> v;
3678  map->map_member.emplace(std::make_pair(std::move(k), std::move(v)));
3679  }
3680  return true;
3681 }
3682 bool read(ryml::ConstNodeRef const& n, my_type *val)
3683 {
3684  // these are leaf nodes:
3685  n["v2"] >> val->v2;
3686  n["v3"] >> val->v3;
3687  n["v4"] >> val->v4;
3688  // these are container nodes:
3689  n["seq"] >> val->seq;
3690  n["map"] >> val->map;
3691  return true;
3692 }
3693 
3694 /** @} */ // doc_sample_container_types
3695 
3696 /** @} */ // sample_helpers
3697 
3698 
3699 /** shows how to serialize/deserialize container types.
3700  * @see doc_sample_container_types
3701  * @see sample_std_types
3702  * */
3704 {
3705  my_type mt_in{
3706  {20, 21},
3707  {30, 31, 32},
3708  {40, 41, 42, 43},
3709  {{101, 102, 103, 104, 105, 106, 107}},
3710  {{{1001, 2001}, {1002, 2002}, {1003, 2003}}},
3711  };
3712  my_type mt_out;
3713 
3714  ryml::Tree t;
3715  t.rootref() << mt_in; // read from this
3716  t.crootref() >> mt_out; // assign here
3717  CHECK(mt_out.v2.x == mt_in.v2.x);
3718  CHECK(mt_out.v2.y == mt_in.v2.y);
3719  CHECK(mt_out.v3.x == mt_in.v3.x);
3720  CHECK(mt_out.v3.y == mt_in.v3.y);
3721  CHECK(mt_out.v3.z == mt_in.v3.z);
3722  CHECK(mt_out.v4.x == mt_in.v4.x);
3723  CHECK(mt_out.v4.y == mt_in.v4.y);
3724  CHECK(mt_out.v4.z == mt_in.v4.z);
3725  CHECK(mt_out.v4.w == mt_in.v4.w);
3726  CHECK(mt_in.seq.seq_member.size() > 0);
3727  CHECK(mt_out.seq.seq_member.size() == mt_in.seq.seq_member.size());
3728  for(size_t i = 0; i < mt_in.seq.seq_member.size(); ++i)
3729  {
3730  CHECK(mt_out.seq.seq_member[i] == mt_in.seq.seq_member[i]);
3731  }
3732  CHECK(mt_in.map.map_member.size() > 0);
3733  CHECK(mt_out.map.map_member.size() == mt_in.map.map_member.size());
3734  for(auto const& kv : mt_in.map.map_member)
3735  {
3736  CHECK(mt_out.map.map_member.find(kv.first) != mt_out.map.map_member.end());
3737  CHECK(mt_out.map.map_member[kv.first] == kv.second);
3738  }
3739  CHECK(ryml::emitrs_yaml<std::string>(t) == R"(v2: '(20,21)'
3740 v3: '(30,31,32)'
3741 v4: '(40,41,42,43)'
3742 seq:
3743  - 101
3744  - 102
3745  - 103
3746  - 104
3747  - 105
3748  - 106
3749  - 107
3750 map:
3751  1001: 2001
3752  1002: 2002
3753  1003: 2003
3754 )");
3755 }
3756 
3757 
3758 //-----------------------------------------------------------------------------
3759 
3760 /** demonstrates usage with the std implementations provided by ryml
3761  in the ryml_std.hpp header
3762  @see @ref doc_sample_container_types
3763  @see also the STL section in @ref doc_serialization */
3765 {
3766  std::string yml_std_string = R"(- v2: '(20,21)'
3767  v3: '(30,31,32)'
3768  v4: '(40,41,42,43)'
3769  seq:
3770  - 101
3771  - 102
3772  - 103
3773  - 104
3774  - 105
3775  - 106
3776  - 107
3777  map:
3778  1001: 2001
3779  1002: 2002
3780  1003: 2003
3781 - v2: '(120,121)'
3782  v3: '(130,131,132)'
3783  v4: '(140,141,142,143)'
3784  seq:
3785  - 1101
3786  - 1102
3787  - 1103
3788  - 1104
3789  - 1105
3790  - 1106
3791  - 1107
3792  map:
3793  11001: 12001
3794  11002: 12002
3795  11003: 12003
3796 - v2: '(220,221)'
3797  v3: '(230,231,232)'
3798  v4: '(240,241,242,243)'
3799  seq:
3800  - 2101
3801  - 2102
3802  - 2103
3803  - 2104
3804  - 2105
3805  - 2106
3806  - 2107
3807  map:
3808  21001: 22001
3809  21002: 22002
3810  21003: 22003
3811 )";
3812  // parse in-place using the std::string above
3813  ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(yml_std_string));
3814  // my_type is a container-of-containers type. see above its
3815  // definition implementation for ryml.
3816  std::vector<my_type> vmt;
3817  tree.rootref() >> vmt;
3818  CHECK(vmt.size() == 3);
3819  ryml::Tree tree_out;
3820  tree_out.rootref() << vmt;
3821  CHECK(ryml::emitrs_yaml<std::string>(tree_out) == yml_std_string);
3822 }
3823 
3824 
3825 //-----------------------------------------------------------------------------
3826 
3827 /** control precision of serialized floats */
3829 {
3830  std::vector<double> reference{1.23234412342131234, 2.12323123143434237, 3.67847983572591234};
3831  // A safe precision for comparing doubles. May vary depending on
3832  // compiler flags. Double goes to about 15 digits, so 14 should be
3833  // safe enough for this test to succeed.
3834  const double precision_safe = 1.e-14;
3835  const size_t num_digits_safe = 14;
3836  const size_t num_digits_original = 17;
3837  auto get_num_digits = [](ryml::csubstr number){ return number.sub(2).len; };
3838  //
3839  // no significant precision is lost when reading
3840  // floating point numbers:
3841  {
3842  ryml::Tree tree = ryml::parse_in_arena(R"([1.23234412342131234, 2.12323123143434237, 3.67847983572591234])");
3843  std::vector<double> output;
3844  tree.rootref() >> output;
3845  CHECK(output.size() == reference.size());
3846  for(size_t i = 0; i < reference.size(); ++i)
3847  {
3848  CHECK(get_num_digits(tree[(ryml::id_type)i].val()) == num_digits_original);
3849  CHECK(fabs(output[i] - reference[i]) < precision_safe);
3850  }
3851  }
3852  //
3853  // However, depending on the compilation settings, there may be a
3854  // significant precision loss when serializing with the default
3855  // approach, operator<<(double):
3856  {
3857  ryml::Tree serialized;
3858  serialized.rootref() << reference;
3859  std::cout << serialized;
3860  // Without std::to_chars() there is a loss of precision:
3861  #if (!C4CORE_HAVE_STD_TOCHARS) // This macro is defined when std::to_chars() is available.
3862  CHECK(ryml::emitrs_yaml<std::string>(serialized) == R"(- 1.23234
3863 - 2.12323
3864 - 3.67848
3865 )" || (bool)"this is indicative; the exact results will vary from platform to platform.");
3866  C4_UNUSED(num_digits_safe);
3867  #else // ... but when using C++17 and above, the results are eminently equal:
3868  CHECK((ryml::emitrs_yaml<std::string>(serialized) == R"(- 1.2323441234213124
3869 - 2.1232312314343424
3870 - 3.6784798357259123
3871 )") || (bool)"this is indicative; the exact results will vary from platform to platform.");
3872  size_t pos = 0;
3873  for(ryml::ConstNodeRef child : serialized.rootref().children())
3874  {
3875  CHECK(get_num_digits(child.val()) >= num_digits_safe);
3876  double out = {};
3877  child >> out;
3878  CHECK(fabs(out - reference[pos++]) < precision_safe);
3879  }
3880  #endif
3881  }
3882  //
3883  // The difference is explained by the availability of
3884  // fastfloat::from_chars(), std::from_chars() and std::to_chars().
3885  //
3886  // ryml prefers the fastfloat::from_chars() version. Unfortunately
3887  // fastfloat does not have to_chars() (see
3888  // https://github.com/fastfloat/fast_float/issues/23).
3889  //
3890  // When C++17 is used, ryml uses std::to_chars(), which produces
3891  // good defaults.
3892  //
3893  // However, with earlier standards, or in some library
3894  // implementations, there's only snprintf() available. Every other
3895  // std library function will either disrespect the string limits,
3896  // or more precisely, accept no string size limits. So the
3897  // implementation of c4core (which ryml uses) falls back to
3898  // snprintf("%g"), and that picks by default a (low) number of
3899  // digits.
3900  //
3901  // But all is not lost for C++11/C++14 users!
3902  //
3903  // To force a particular precision when serializing, you can use
3904  // c4::fmt::real() (brought into the ryml:: namespace). Or you can
3905  // serialize the number yourself! The small downside is that you
3906  // have to build the container.
3907  //
3908  // First a function to check the result:
3909  auto check_precision = [&](ryml::Tree serialized){
3910  std::cout << serialized;
3911  // now it works!
3912  CHECK((ryml::emitrs_yaml<std::string>(serialized) == R"(- 1.23234412342131239
3913 - 2.12323123143434245
3914 - 3.67847983572591231
3915 )") || (bool)"this is indicative; the exact results will vary from platform to platform.");
3916  size_t pos = 0;
3917  for(ryml::ConstNodeRef child : serialized.rootref().children())
3918  {
3919  CHECK(get_num_digits(child.val()) == num_digits_original);
3920  double out = {};
3921  child >> out;
3922  CHECK(fabs(out - reference[pos++]) < precision_safe);
3923  }
3924  };
3925  //
3926  // Serialization example using fmt::real()
3927  {
3928  ryml::Tree serialized;
3929  ryml::NodeRef root = serialized.rootref();
3930  root |= ryml::SEQ;
3931  for(const double v : reference)
3932  root.append_child() << ryml::fmt::real(v, num_digits_original, ryml::FTOA_FLOAT);
3933  check_precision(serialized); // OK - now within bounds!
3934  }
3935  //
3936  // Serialization example using snprintf
3937  {
3938  ryml::Tree serialized;
3939  ryml::NodeRef root = serialized.rootref();
3940  root |= ryml::SEQ;
3941  char tmp[64];
3942  for(const double v : reference)
3943  {
3944  // reuse a buffer to serialize.
3945  // add 1 to the significant digits because the %g
3946  // specifier counts the integral digits.
3947  (void)snprintf(tmp, sizeof(tmp), "%.18g", v);
3948  // copy the serialized string to the tree (operator<<
3949  // copies to the arena, operator= just assigns the string
3950  // pointer and would be wrong in this case):
3951  root.append_child() << ryml::to_csubstr((const char*)tmp);
3952  }
3953  check_precision(serialized); // OK - now within bounds!
3954  }
3955 }
3956 
3957 
3958 //-----------------------------------------------------------------------------
3959 
3960 /** demonstrates how to emit to a linear container of char */
3962 {
3963  // it is possible to emit to any linear container of char.
3964 
3965  ryml::csubstr ymla = "- 1\n- 2\n";
3966  ryml::csubstr ymlb = R"(- a
3967 - b
3968 - x0: 1
3969  x1: 2
3970 - champagne: Dom Perignon
3971  coffee: Arabica
3972  more:
3973  vinho verde: Soalheiro
3974  vinho tinto: Redoma 2017
3975  beer:
3976  - Rochefort 10
3977  - Busch
3978  - Leffe Rituel
3979 - foo
3980 - bar
3981 - baz
3982 - bat
3983 )";
3984  auto treea = ryml::parse_in_arena(ymla);
3985  auto treeb = ryml::parse_in_arena(ymlb);
3986 
3987  // eg, std::vector<char>
3988  {
3989  // do a blank call on an empty buffer to find the required size.
3990  // no overflow will occur, and returns a substr with the size
3991  // required to output
3992  ryml::csubstr output = ryml::emit_yaml(treea, treea.root_id(), ryml::substr{}, /*error_on_excess*/false);
3993  CHECK(output.str == nullptr);
3994  CHECK(output.len > 0);
3995  size_t num_needed_chars = output.len;
3996  std::vector<char> buf(num_needed_chars);
3997  // now try again with the proper buffer
3998  output = ryml::emit_yaml(treea, treea.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
3999  CHECK(output == ymla);
4000 
4001  // it is possible to reuse the buffer and grow it as needed.
4002  // first do a blank run to find the size:
4003  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::substr{}, /*error_on_excess*/false);
4004  CHECK(output.str == nullptr);
4005  CHECK(output.len > 0);
4006  CHECK(output.len == ymlb.len);
4007  num_needed_chars = output.len;
4008  buf.resize(num_needed_chars);
4009  // now try again with the proper buffer
4010  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4011  CHECK(output == ymlb);
4012 
4013  // there is a convenience wrapper performing the same as above:
4014  // provided to_substr() is defined for that container.
4015  output = ryml::emitrs_yaml(treeb, &buf);
4016  CHECK(output == ymlb);
4017 
4018  // or you can just output a new container:
4019  // provided to_substr() is defined for that container.
4020  std::vector<char> another = ryml::emitrs_yaml<std::vector<char>>(treeb);
4021  CHECK(ryml::to_csubstr(another) == ymlb);
4022 
4023  // you can also emit nested nodes:
4024  another = ryml::emitrs_yaml<std::vector<char>>(treeb[3][2]);
4025  CHECK(ryml::to_csubstr(another) == R"(more:
4026  vinho verde: Soalheiro
4027  vinho tinto: Redoma 2017
4028 )");
4029  }
4030 
4031 
4032  // eg, std::string. notice this is the same code as above
4033  {
4034  // do a blank call on an empty buffer to find the required size.
4035  // no overflow will occur, and returns a substr with the size
4036  // required to output
4037  ryml::csubstr output = ryml::emit_yaml(treea, treea.root_id(), ryml::substr{}, /*error_on_excess*/false);
4038  CHECK(output.str == nullptr);
4039  CHECK(output.len > 0);
4040  size_t num_needed_chars = output.len;
4041  std::string buf;
4042  buf.resize(num_needed_chars);
4043  // now try again with the proper buffer
4044  output = ryml::emit_yaml(treea, treea.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4045  CHECK(output == ymla);
4046 
4047  // it is possible to reuse the buffer and grow it as needed.
4048  // first do a blank run to find the size:
4049  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::substr{}, /*error_on_excess*/false);
4050  CHECK(output.str == nullptr);
4051  CHECK(output.len > 0);
4052  CHECK(output.len == ymlb.len);
4053  num_needed_chars = output.len;
4054  buf.resize(num_needed_chars);
4055  // now try again with the proper buffer
4056  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4057  CHECK(output == ymlb);
4058 
4059  // there is a convenience wrapper performing the above instructions:
4060  // provided to_substr() is defined for that container
4061  output = ryml::emitrs_yaml(treeb, &buf);
4062  CHECK(output == ymlb);
4063 
4064  // or you can just output a new container:
4065  // provided to_substr() is defined for that container.
4066  std::string another = ryml::emitrs_yaml<std::string>(treeb);
4067  CHECK(ryml::to_csubstr(another) == ymlb);
4068 
4069  // you can also emit nested nodes:
4070  another = ryml::emitrs_yaml<std::string>(treeb[3][2]);
4071  CHECK(ryml::to_csubstr(another) == R"(more:
4072  vinho verde: Soalheiro
4073  vinho tinto: Redoma 2017
4074 )");
4075  }
4076 }
4077 
4078 
4079 //-----------------------------------------------------------------------------
4080 
4081 /** demonstrates how to emit to a stream-like structure */
4083 {
4084  ryml::csubstr ymlb = R"(- a
4085 - b
4086 - x0: 1
4087  x1: 2
4088 - champagne: Dom Perignon
4089  coffee: Arabica
4090  more:
4091  vinho verde: Soalheiro
4092  vinho tinto: Redoma 2017
4093  beer:
4094  - Rochefort 10
4095  - Busch
4096  - Leffe Rituel
4097 - foo
4098 - bar
4099 - baz
4100 - bat
4101 )";
4102  auto tree = ryml::parse_in_arena(ymlb);
4103 
4104  std::string s;
4105 
4106  // emit a full tree
4107  {
4108  std::stringstream ss;
4109  ss << tree; // works with any stream having .operator<<() and .write()
4110  s = ss.str();
4111  CHECK(ryml::to_csubstr(s) == ymlb);
4112  }
4113 
4114  // emit a full tree as json
4115  {
4116  std::stringstream ss;
4117  ss << ryml::as_json(tree); // works with any stream having .operator<<() and .write()
4118  s = ss.str();
4119  CHECK(ryml::to_csubstr(s) == R"(["a","b",{"x0": 1,"x1": 2},{"champagne": "Dom Perignon","coffee": "Arabica","more": {"vinho verde": "Soalheiro","vinho tinto": "Redoma 2017"},"beer": ["Rochefort 10","Busch","Leffe Rituel"]},"foo","bar","baz","bat"])");
4120  }
4121 
4122  // emit a nested node
4123  {
4124  std::stringstream ss;
4125  ss << tree[3][2]; // works with any stream having .operator<<() and .write()
4126  s = ss.str();
4127  CHECK(ryml::to_csubstr(s) == R"(more:
4128  vinho verde: Soalheiro
4129  vinho tinto: Redoma 2017
4130 )");
4131  }
4132 
4133  // emit a nested node as json
4134  {
4135  std::stringstream ss;
4136  ss << ryml::as_json(tree[3][2]); // works with any stream having .operator<<() and .write()
4137  s = ss.str();
4138  CHECK(ryml::to_csubstr(s) == R"("more": {"vinho verde": "Soalheiro","vinho tinto": "Redoma 2017"})");
4139  }
4140 }
4141 
4142 
4143 //-----------------------------------------------------------------------------
4144 
4145 /** demonstrates how to emit to a FILE* */
4147 {
4148  ryml::csubstr yml = R"(- a
4149 - b
4150 - x0: 1
4151  x1: 2
4152 - champagne: Dom Perignon
4153  coffee: Arabica
4154  more:
4155  vinho verde: Soalheiro
4156  vinho tinto: Redoma 2017
4157  beer:
4158  - Rochefort 10
4159  - Busch
4160  - Leffe Rituel
4161 - foo
4162 - bar
4163 - baz
4164 - bat
4165 )";
4166  auto tree = ryml::parse_in_arena(yml);
4167  // this is emitting to stdout, but of course you can pass in any
4168  // FILE* obtained from fopen()
4169  size_t len = ryml::emit_yaml(tree, tree.root_id(), stdout);
4170  // the return value is the number of characters that were written
4171  // to the file
4172  CHECK(len == yml.len);
4173 }
4174 
4175 
4176 //-----------------------------------------------------------------------------
4177 
4178 /** just like parsing into a nested node, you can also emit from a nested node. */
4180 {
4181  const ryml::Tree tree = ryml::parse_in_arena(R"(- a
4182 - b
4183 - x0: 1
4184  x1: 2
4185 - champagne: Dom Perignon
4186  coffee: Arabica
4187  more:
4188  vinho verde: Soalheiro
4189  vinho tinto: Redoma 2017
4190  beer:
4191  - Rochefort 10
4192  - Busch
4193  - Leffe Rituel
4194  - - and so
4195  - many other
4196  - wonderful beers
4197 - more
4198 - seq
4199 - members
4200 - here
4201 )");
4202  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"]) == R"(beer:
4203  - Rochefort 10
4204  - Busch
4205  - Leffe Rituel
4206  - - and so
4207  - many other
4208  - wonderful beers
4209 )");
4210  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][0]) == "Rochefort 10\n");
4211  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][3]) == R"(- and so
4212 - many other
4213 - wonderful beers
4214 )");
4215 }
4216 
4217 
4218 //-----------------------------------------------------------------------------
4219 
4220 /** [experimental] pick flow/block style for certain nodes. */
4222 {
4223  ryml::Tree tree = ryml::parse_in_arena(R"(
4224 NodeOne:
4225  - key: a
4226  desc: b
4227  class: c
4228  number: d
4229  - key: e
4230  desc: f
4231  class: g
4232  number: h
4233  - key: i
4234  desc: j
4235  class: k
4236  number: l
4237 )");
4238  // ryml uses block style by default:
4239  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(NodeOne:
4240  - key: a
4241  desc: b
4242  class: c
4243  number: d
4244  - key: e
4245  desc: f
4246  class: g
4247  number: h
4248  - key: i
4249  desc: j
4250  class: k
4251  number: l
4252 )");
4253  // you can override the emit style of individual nodes:
4254  for(ryml::NodeRef child : tree["NodeOne"].children())
4255  child |= ryml::FLOW_SL; // flow, single-line
4256  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(NodeOne:
4257  - {key: a,desc: b,class: c,number: d}
4258  - {key: e,desc: f,class: g,number: h}
4259  - {key: i,desc: j,class: k,number: l}
4260 )");
4261  tree["NodeOne"] |= ryml::FLOW_SL;
4262  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(NodeOne: [{key: a,desc: b,class: c,number: d},{key: e,desc: f,class: g,number: h},{key: i,desc: j,class: k,number: l}]
4263 )");
4264  tree.rootref() |= ryml::FLOW_SL;
4265  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"({NodeOne: [{key: a,desc: b,class: c,number: d},{key: e,desc: f,class: g,number: h},{key: i,desc: j,class: k,number: l}]})");
4266 }
4267 
4268 
4269 //-----------------------------------------------------------------------------
4270 
4271 
4272 /** shows how to parse and emit JSON. */
4274 {
4275  ryml::csubstr json = R"({
4276  "doe":"a deer, a female deer",
4277  "ray":"a drop of golden sun",
4278  "me":"a name, I call myself",
4279  "far":"a long long way to go"
4280 })";
4281  // Since JSON is a subset of YAML, parsing JSON is just the
4282  // same as YAML:
4283  ryml::Tree tree = ryml::parse_in_arena(json);
4284  // If you are sure the source is valid json, you can use the
4285  // appropriate parse_json overload, which is faster because json
4286  // has a smaller grammar:
4287  ryml::Tree json_tree = ryml::parse_json_in_arena(json);
4288  // to emit JSON, use the proper overload:
4289  CHECK(ryml::emitrs_json<std::string>(tree) == R"({"doe": "a deer, a female deer","ray": "a drop of golden sun","me": "a name, I call myself","far": "a long long way to go"})");
4290  CHECK(ryml::emitrs_json<std::string>(json_tree) == R"({"doe": "a deer, a female deer","ray": "a drop of golden sun","me": "a name, I call myself","far": "a long long way to go"})");
4291  // to emit JSON to a stream:
4292  std::stringstream ss;
4293  ss << ryml::as_json(tree); // <- mark it like this
4294  CHECK(ss.str() == R"({"doe": "a deer, a female deer","ray": "a drop of golden sun","me": "a name, I call myself","far": "a long long way to go"})");
4295  // Note the following limitations:
4296  //
4297  // - YAML streams cannot be emitted as json, and are not
4298  // allowed. But you can work around this by emitting the
4299  // individual documents separately; see the sample_docs()
4300  // below for such an example.
4301  //
4302  // - tags cannot be emitted as json, and are not allowed.
4303  //
4304  // - anchors and references cannot be emitted as json and
4305  // are not allowed.
4306 }
4307 
4308 
4309 //-----------------------------------------------------------------------------
4310 
4311 /** demonstrates usage with anchors and alias references.
4312 
4313 Note that dereferencing is opt-in; after parsing, you have to call
4314 `Tree::resolve()` explicitly if you want resolved references in the
4315 tree. This method will resolve all references and substitute the anchored
4316 values in place of the reference.
4317 
4318 The `Tree::resolve()` method first does a full traversal of the tree to
4319 gather all anchors and references in a separate collection, then it goes
4320 through that collection to locate the names, which it does by obeying the
4321 YAML standard diktat that
4322 
4323  an alias node refers to the most recent node in
4324  the serialization having the specified anchor
4325 
4326 So, depending on the number of anchor/alias nodes, this is a potentially
4327 expensive operation, with a best-case linear complexity (from the initial
4328 traversal) and a worst-case quadratic complexity (if every node has an
4329 alias/anchor). This potential cost is the reason for requiring an explicit
4330 call to `Tree::resolve()`. */
4332 {
4333  std::string unresolved = R"(base: &base
4334  name: Everyone has same name
4335 foo: &foo
4336  <<: *base
4337  age: 10
4338 bar: &bar
4339  <<: *base
4340  age: 20
4341 bill_to: &id001
4342  street: |-
4343  123 Tornado Alley
4344  Suite 16
4345  city: East Centerville
4346  state: KS
4347 ship_to: *id001
4348 &keyref key: &valref val
4349 *valref : *keyref
4350 )";
4351  std::string resolved = R"(base:
4352  name: Everyone has same name
4353 foo:
4354  name: Everyone has same name
4355  age: 10
4356 bar:
4357  name: Everyone has same name
4358  age: 20
4359 bill_to:
4360  street: |-
4361  123 Tornado Alley
4362  Suite 16
4363  city: East Centerville
4364  state: KS
4365 ship_to:
4366  street: |-
4367  123 Tornado Alley
4368  Suite 16
4369  city: East Centerville
4370  state: KS
4371 key: val
4372 val: key
4373 )";
4374 
4375  ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(unresolved));
4376  // by default, references are not resolved when parsing:
4377  CHECK( ! tree["base"].has_key_anchor());
4378  CHECK( tree["base"].has_val_anchor());
4379  CHECK( tree["base"].val_anchor() == "base");
4380  CHECK( tree["key"].key_anchor() == "keyref");
4381  CHECK( tree["key"].val_anchor() == "valref");
4382  CHECK( tree["*valref"].is_key_ref());
4383  CHECK( tree["*valref"].is_val_ref());
4384  CHECK( tree["*valref"].key_ref() == "valref");
4385  CHECK( tree["*valref"].val_ref() == "keyref");
4386 
4387  // to resolve references, simply call tree.resolve(),
4388  // which will perform the reference instantiations:
4389  tree.resolve();
4390 
4391  // all the anchors and references are substistuted and then removed:
4392  CHECK( ! tree["base"].has_key_anchor());
4393  CHECK( ! tree["base"].has_val_anchor());
4394  CHECK( ! tree["base"].has_val_anchor());
4395  CHECK( ! tree["key"].has_key_anchor());
4396  CHECK( ! tree["key"].has_val_anchor());
4397  CHECK( ! tree["val"].is_key_ref()); // notice *valref is now turned to val
4398  CHECK( ! tree["val"].is_val_ref()); // notice *valref is now turned to val
4399 
4400  CHECK(tree["ship_to"]["city"].val() == "East Centerville");
4401  CHECK(tree["ship_to"]["state"].val() == "KS");
4402 }
4403 
4404 
4405 //-----------------------------------------------------------------------------
4406 
4408 {
4409  const std::string yaml = R"(--- !!map
4410 a: 0
4411 b: 1
4412 --- !map
4413 a: b
4414 --- !!seq
4415 - a
4416 - b
4417 --- !!str a b
4418 --- !!str 'a: b'
4419 ---
4420 !!str a: b
4421 --- !!set
4422 ? a
4423 ? b
4424 --- !!set
4425 a:
4426 --- !!seq
4427 - !!int 0
4428 - !!str 1
4429 )";
4430  const ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(yaml));
4431  const ryml::ConstNodeRef root = tree.rootref();
4432  CHECK(root.is_stream());
4433  CHECK(root.num_children() == 9);
4434  for(ryml::ConstNodeRef doc : root.children())
4435  CHECK(doc.is_doc());
4436  // tags are kept verbatim from the source:
4437  CHECK(root[0].has_val_tag());
4438  CHECK(root[0].val_tag() == "!!map"); // valid only if the node has a val tag
4439  CHECK(root[1].val_tag() == "!map");
4440  CHECK(root[2].val_tag() == "!!seq");
4441  CHECK(root[3].val_tag() == "!!str");
4442  CHECK(root[4].val_tag() == "!!str");
4443  CHECK(root[5]["a"].has_key_tag());
4444  CHECK(root[5]["a"].key_tag() == "!!str"); // valid only if the node has a key tag
4445  CHECK(root[6].val_tag() == "!!set");
4446  CHECK(root[7].val_tag() == "!!set");
4447  CHECK(root[8].val_tag() == "!!seq");
4448  CHECK(root[8][0].val_tag() == "!!int");
4449  CHECK(root[8][1].val_tag() == "!!str");
4450  // ryml also provides a complete toolbox to deal with tags.
4451  // there is an enumeration for the standard YAML tags:
4452  CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
4453  CHECK(ryml::to_tag("!!map") == ryml::TAG_MAP);
4454  CHECK(ryml::to_tag("!!seq") == ryml::TAG_SEQ);
4455  CHECK(ryml::to_tag("!!str") == ryml::TAG_STR);
4456  CHECK(ryml::to_tag("!!int") == ryml::TAG_INT);
4457  CHECK(ryml::to_tag("!!set") == ryml::TAG_SET);
4458  // given a tag enum, you can fetch the short tag string:
4460  CHECK(ryml::from_tag(ryml::TAG_MAP) == "!!map");
4461  CHECK(ryml::from_tag(ryml::TAG_SEQ) == "!!seq");
4462  CHECK(ryml::from_tag(ryml::TAG_STR) == "!!str");
4463  CHECK(ryml::from_tag(ryml::TAG_INT) == "!!int");
4464  CHECK(ryml::from_tag(ryml::TAG_SET) == "!!set");
4465  // you can also fetch the long tag string:
4467  CHECK(ryml::from_tag_long(ryml::TAG_MAP) == "<tag:yaml.org,2002:map>");
4468  CHECK(ryml::from_tag_long(ryml::TAG_SEQ) == "<tag:yaml.org,2002:seq>");
4469  CHECK(ryml::from_tag_long(ryml::TAG_STR) == "<tag:yaml.org,2002:str>");
4470  CHECK(ryml::from_tag_long(ryml::TAG_INT) == "<tag:yaml.org,2002:int>");
4471  CHECK(ryml::from_tag_long(ryml::TAG_SET) == "<tag:yaml.org,2002:set>");
4472  // and likewise:
4473  CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
4474  CHECK(ryml::to_tag("<tag:yaml.org,2002:map>") == ryml::TAG_MAP);
4475  CHECK(ryml::to_tag("<tag:yaml.org,2002:seq>") == ryml::TAG_SEQ);
4476  CHECK(ryml::to_tag("<tag:yaml.org,2002:str>") == ryml::TAG_STR);
4477  CHECK(ryml::to_tag("<tag:yaml.org,2002:int>") == ryml::TAG_INT);
4478  CHECK(ryml::to_tag("<tag:yaml.org,2002:set>") == ryml::TAG_SET);
4479  // to normalize a tag as much as possible, use normalize_tag():
4480  CHECK(ryml::normalize_tag("!!map") == "!!map");
4481  CHECK(ryml::normalize_tag("!<tag:yaml.org,2002:map>") == "!!map");
4482  CHECK(ryml::normalize_tag("<tag:yaml.org,2002:map>") == "!!map");
4483  CHECK(ryml::normalize_tag("tag:yaml.org,2002:map") == "!!map");
4484  CHECK(ryml::normalize_tag("!<!!map>") == "<!!map>");
4485  CHECK(ryml::normalize_tag("!map") == "!map");
4486  CHECK(ryml::normalize_tag("!my!foo") == "!my!foo");
4487  // and also for the long form:
4488  CHECK(ryml::normalize_tag_long("!!map") == "<tag:yaml.org,2002:map>");
4489  CHECK(ryml::normalize_tag_long("!<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
4490  CHECK(ryml::normalize_tag_long("<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
4491  CHECK(ryml::normalize_tag_long("tag:yaml.org,2002:map") == "<tag:yaml.org,2002:map>");
4492  CHECK(ryml::normalize_tag_long("!<!!map>") == "<!!map>");
4493  CHECK(ryml::normalize_tag_long("!map") == "!map");
4494  // The tree provides the following methods applying to every node
4495  // with a key and/or val tag:
4496  ryml::Tree normalized_tree = tree;
4497  normalized_tree.normalize_tags(); // normalize all tags in short form
4498  CHECK(ryml::emitrs_yaml<std::string>(normalized_tree) == R"(--- !!map
4499 a: 0
4500 b: 1
4501 --- !map
4502 a: b
4503 --- !!seq
4504 - a
4505 - b
4506 --- !!str a b
4507 --- !!str 'a: b'
4508 ---
4509 !!str a: b
4510 --- !!set
4511 a:
4512 b:
4513 --- !!set
4514 a:
4515 --- !!seq
4516 - !!int 0
4517 - !!str 1
4518 )");
4519  ryml::Tree normalized_tree_long = tree;
4520  normalized_tree_long.normalize_tags_long(); // normalize all tags in short form
4521  CHECK(ryml::emitrs_yaml<std::string>(normalized_tree_long) == R"(--- !<tag:yaml.org,2002:map>
4522 a: 0
4523 b: 1
4524 --- !map
4525 a: b
4526 --- !<tag:yaml.org,2002:seq>
4527 - a
4528 - b
4529 --- !<tag:yaml.org,2002:str> a b
4530 --- !<tag:yaml.org,2002:str> 'a: b'
4531 ---
4532 !<tag:yaml.org,2002:str> a: b
4533 --- !<tag:yaml.org,2002:set>
4534 a:
4535 b:
4536 --- !<tag:yaml.org,2002:set>
4537 a:
4538 --- !<tag:yaml.org,2002:seq>
4539 - !<tag:yaml.org,2002:int> 0
4540 - !<tag:yaml.org,2002:str> 1
4541 )");
4542 }
4543 
4544 
4545 //-----------------------------------------------------------------------------
4546 
4548 {
4549  const std::string yaml = R"(
4550 %TAG !m! !my-
4551 --- # Bulb here
4552 !m!light fluorescent
4553 ...
4554 %TAG !m! !meta-
4555 --- # Color here
4556 !m!light green
4557 )";
4559  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(%TAG !m! !my-
4560 --- !m!light fluorescent
4561 ...
4562 %TAG !m! !meta-
4563 --- !m!light green
4564 )");
4565  // tags are not resolved by default. Use .resolve_tags() to
4566  // accomplish this:
4567  tree.resolve_tags();
4568  CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(%TAG !m! !my-
4569 --- !<!my-light> fluorescent
4570 ...
4571 %TAG !m! !meta-
4572 --- !<!meta-light> green
4573 )");
4574  // see also tree.normalize_tags()
4575  // see also tree.normalize_tags_long()
4576 }
4577 
4578 
4579 //-----------------------------------------------------------------------------
4580 
4582 {
4583  std::string yml = R"(---
4584 a: 0
4585 b: 1
4586 ---
4587 c: 2
4588 d: 3
4589 ---
4590 - 4
4591 - 5
4592 - 6
4593 - 7
4594 )";
4596  CHECK(ryml::emitrs_yaml<std::string>(tree) == yml);
4597 
4598  // iteration through docs
4599  {
4600  // using the node API
4601  const ryml::ConstNodeRef stream = tree.rootref();
4602  CHECK(stream.is_root());
4603  CHECK(stream.is_stream());
4604  CHECK(!stream.is_doc());
4605  CHECK(stream.num_children() == 3);
4606  for(const ryml::ConstNodeRef doc : stream.children())
4607  CHECK(doc.is_doc());
4608  CHECK(tree.docref(0).id() == stream.child(0).id());
4609  CHECK(tree.docref(1).id() == stream.child(1).id());
4610  CHECK(tree.docref(2).id() == stream.child(2).id());
4611  // equivalent: using the lower level index API
4612  const ryml::id_type stream_id = tree.root_id();
4613  CHECK(tree.is_root(stream_id));
4614  CHECK(tree.is_stream(stream_id));
4615  CHECK(!tree.is_doc(stream_id));
4616  CHECK(tree.num_children(stream_id) == 3);
4617  for(ryml::id_type doc_id = tree.first_child(stream_id); doc_id != ryml::NONE; doc_id = tree.next_sibling(stream_id))
4618  CHECK(tree.is_doc(doc_id));
4619  CHECK(tree.doc(0) == tree.child(stream_id, 0));
4620  CHECK(tree.doc(1) == tree.child(stream_id, 1));
4621  CHECK(tree.doc(2) == tree.child(stream_id, 2));
4622 
4623  // using the node API
4624  CHECK(stream[0].is_doc());
4625  CHECK(stream[0].is_map());
4626  CHECK(stream[0]["a"].val() == "0");
4627  CHECK(stream[0]["b"].val() == "1");
4628  // equivalent: using the index API
4629  const ryml::id_type doc0_id = tree.first_child(stream_id);
4630  CHECK(tree.is_doc(doc0_id));
4631  CHECK(tree.is_map(doc0_id));
4632  CHECK(tree.val(tree.find_child(doc0_id, "a")) == "0");
4633  CHECK(tree.val(tree.find_child(doc0_id, "b")) == "1");
4634 
4635  // using the node API
4636  CHECK(stream[1].is_doc());
4637  CHECK(stream[1].is_map());
4638  CHECK(stream[1]["c"].val() == "2");
4639  CHECK(stream[1]["d"].val() == "3");
4640  // equivalent: using the index API
4641  const ryml::id_type doc1_id = tree.next_sibling(doc0_id);
4642  CHECK(tree.is_doc(doc1_id));
4643  CHECK(tree.is_map(doc1_id));
4644  CHECK(tree.val(tree.find_child(doc1_id, "c")) == "2");
4645  CHECK(tree.val(tree.find_child(doc1_id, "d")) == "3");
4646 
4647  // using the node API
4648  CHECK(stream[2].is_doc());
4649  CHECK(stream[2].is_seq());
4650  CHECK(stream[2][0].val() == "4");
4651  CHECK(stream[2][1].val() == "5");
4652  CHECK(stream[2][2].val() == "6");
4653  CHECK(stream[2][3].val() == "7");
4654  // equivalent: using the index API
4655  const ryml::id_type doc2_id = tree.next_sibling(doc1_id);
4656  CHECK(tree.is_doc(doc2_id));
4657  CHECK(tree.is_seq(doc2_id));
4658  CHECK(tree.val(tree.child(doc2_id, 0)) == "4");
4659  CHECK(tree.val(tree.child(doc2_id, 1)) == "5");
4660  CHECK(tree.val(tree.child(doc2_id, 2)) == "6");
4661  CHECK(tree.val(tree.child(doc2_id, 3)) == "7");
4662  }
4663 
4664  // Note: since json does not have streams, you cannot emit the above
4665  // tree as json when you start from the root:
4666  //CHECK(ryml::emitrs_json<std::string>(tree) == yml); // RUNTIME ERROR!
4667 
4668  // emitting streams as json is not possible, but
4669  // you can iterate through individual documents and emit
4670  // them separately:
4671  {
4672  const std::string expected_json[] = {
4673  R"({"a": 0,"b": 1})",
4674  R"({"c": 2,"d": 3})",
4675  R"([4,5,6,7])",
4676  };
4677  // using the node API
4678  {
4679  ryml::id_type count = 0;
4680  const ryml::ConstNodeRef stream = tree.rootref();
4681  CHECK(stream.num_children() == (ryml::id_type)C4_COUNTOF(expected_json));
4682  for(ryml::ConstNodeRef doc : stream.children())
4683  CHECK(ryml::emitrs_json<std::string>(doc) == expected_json[count++]);
4684  }
4685  // equivalent: using the index API
4686  {
4687  ryml::id_type count = 0;
4688  const ryml::id_type stream_id = tree.root_id();
4689  CHECK(tree.num_children(stream_id) == (ryml::id_type)C4_COUNTOF(expected_json));
4690  for(ryml::id_type doc_id = tree.first_child(stream_id); doc_id != ryml::NONE; doc_id = tree.next_sibling(doc_id))
4691  CHECK(ryml::emitrs_json<std::string>(tree, doc_id) == expected_json[count++]);
4692  }
4693  }
4694 }
4695 
4696 
4697 //-----------------------------------------------------------------------------
4698 
4699 // To avoid imposing a particular type of error handling, ryml uses an
4700 // error handler callback. This enables users to use exceptions, or
4701 // setjmp()/longjmp(), or plain calls to abort(), as they see fit.
4702 //
4703 // However, it is important to note that the error callback must never
4704 // return to the caller! Otherwise, an infinite loop or program crash
4705 // will likely occur.
4706 //
4707 // For this reason, to recover from an error when exceptions are disabled,
4708 // then a non-local jump must be performed using setjmp()/longjmp().
4709 // The code below demonstrates both flows.
4710 //
4711 // ryml provides a default error handler, which calls
4712 // std::abort(). You can use the cmake option and the macro
4713 // RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS to have the default error
4714 // handler throw a std::runtime_error instead.
4715 
4716 /** demonstrates how to set a custom error handler for ryml */
4718 {
4719  ErrorHandlerExample errh;
4720 
4721  // set a global error handler. Note the error callback must never
4722  // return: it must either throw an exception, use setjmp() and
4723  // longjmp(), or abort. Otherwise, the parser will enter into an
4724  // infinite loop, or the program may crash.
4726  errh.check_effect(/*committed*/true);
4727  CHECK(errh.check_error_occurs([&]{
4728  ryml::Tree tree = ryml::parse_in_arena("errorhandler.yml", "[a: b\n}");
4729  }));
4730  ryml::set_callbacks(errh.defaults); // restore defaults.
4731  errh.check_effect(/*committed*/false);
4732 }
4733 
4734 
4735 //-----------------------------------------------------------------------------
4736 
4737 // Please note the following about the use of custom allocators with
4738 // ryml. Due to [the static initialization order
4739 // fiasco](https://en.cppreference.com/w/cpp/language/siof), if you
4740 // use static ryml trees or parsers, you need to make sure that their
4741 // callbacks have the same lifetime. So you can't use ryml's default
4742 // callbacks structure, as it is declared in a ryml file, and the standard
4743 // provides no guarantee on the relative initialization order, such
4744 // that it is constructed before and destroyed after your
4745 // variables (in fact you are pretty much guaranteed to see this
4746 // fail). So please carefully consider your choices, and ponder
4747 // whether you really need to use ryml static trees and parsers. If
4748 // you do need this, then you will need to declare and use a ryml
4749 // callbacks structure that outlives the tree and/or parser.
4750 //
4751 // See also sample_static_trees() for an example on how to use
4752 // trees with static lifetime.
4753 
4754 /** @addtogroup doc_sample_helpers
4755  * @{ */
4757 {
4758  std::vector<char> memory_pool = std::vector<char>(10u * 1024u); // 10KB
4759  size_t num_allocs = 0, alloc_size = 0, corr_size = 0;
4760  size_t num_deallocs = 0, dealloc_size = 0;
4761 
4762  void *allocate(size_t len)
4763  {
4764  void *ptr = &memory_pool[alloc_size];
4765  alloc_size += len;
4766  ++num_allocs;
4767  // ensure the ptr is aligned
4768  uintptr_t uptr = (uintptr_t)ptr;
4769  const uintptr_t align = alignof(max_align_t);
4770  if (uptr % align)
4771  {
4772  uintptr_t prev = uptr - (uptr % align);
4773  uintptr_t next = prev + align;
4774  uintptr_t corr = next - uptr;
4775  ptr = (void*)(((char*)ptr) + corr);
4776  corr_size += corr;
4777  }
4778  C4_CHECK_MSG(alloc_size + corr_size <= memory_pool.size(),
4779  "out of memory! requested=%zu+%zu available=%zu\n",
4780  alloc_size, corr_size, memory_pool.size());
4781  return ptr;
4782  }
4783 
4784  void free(void *mem, size_t len)
4785  {
4786  CHECK((char*)mem >= &memory_pool.front() && (char*)mem < &memory_pool.back());
4787  CHECK((char*)mem+len >= &memory_pool.front() && (char*)mem+len <= &memory_pool.back());
4788  dealloc_size += len;
4789  ++num_deallocs;
4790  // no need to free here
4791  }
4792 
4793  // bridge
4795  {
4797  }
4798  static void* s_allocate(size_t len, void* /*hint*/, void *this_)
4799  {
4800  return ((GlobalAllocatorExample*)this_)->allocate(len);
4801  }
4802  static void s_free(void *mem, size_t len, void *this_)
4803  {
4804  return ((GlobalAllocatorExample*)this_)->free(mem, len);
4805  }
4806 
4807  // checking
4809  {
4810  check_and_reset();
4811  }
4813  {
4814  std::cout << "size: alloc=" << alloc_size << " dealloc=" << dealloc_size << std::endl;
4815  std::cout << "count: #allocs=" << num_allocs << " #deallocs=" << num_deallocs << std::endl;
4817  CHECK(alloc_size >= dealloc_size); // failure here means a double free
4818  CHECK(alloc_size == dealloc_size); // failure here means a leak
4819  num_allocs = 0;
4820  num_deallocs = 0;
4821  alloc_size = 0;
4822  dealloc_size = 0;
4823  }
4824 };
4825 /** @} */
4826 
4827 
4828 /** demonstrates how to set the global allocator for ryml */
4830 {
4832 
4833  // save the existing callbacks for restoring
4834  ryml::Callbacks defaults = ryml::get_callbacks();
4835 
4836  // set to our callbacks
4838 
4839  // verify that the allocator is in effect
4840  ryml::Callbacks const& current = ryml::get_callbacks();
4841  CHECK(current.m_allocate == &mem.s_allocate);
4842  CHECK(current.m_free == &mem.s_free);
4843 
4844  // so far nothing was allocated
4845  CHECK(mem.alloc_size == 0);
4846 
4847  // parse one tree and check
4848  (void)ryml::parse_in_arena(R"({foo: bar})");
4849  mem.check_and_reset();
4850 
4851  // parse another tree and check
4852  (void)ryml::parse_in_arena(R"([a, b, c, d, {foo: bar, money: pennys}])");
4853  mem.check_and_reset();
4854 
4855  // verify that by reserving we save allocations
4856  {
4857  ryml::EventHandlerTree evt_handler;
4858  ryml::Parser parser(&evt_handler); // reuse a parser
4859  ryml::Tree tree; // reuse a tree
4860 
4861  tree.reserve(10); // reserve the number of nodes
4862  tree.reserve_arena(100); // reserve the arena size
4863  parser.reserve_stack(10); // reserve the parser depth.
4864 
4865  // since the parser stack uses Small Storage Optimization,
4866  // allocations will only happen with capacities higher than 16.
4867  CHECK(mem.num_allocs == 2); // tree, tree_arena and NOT the parser
4868 
4869  parser.reserve_stack(20); // reserve the parser depth.
4870  CHECK(mem.num_allocs == 3); // tree, tree_arena and now the parser as well
4871 
4872  // verify that no other allocations occur when parsing
4873  size_t size_before = mem.alloc_size;
4874  parse_in_arena(&parser, "", R"([a, b, c, d, {foo: bar, money: pennys}])", &tree);
4875  CHECK(mem.alloc_size == size_before);
4876  CHECK(mem.num_allocs == 3);
4877  }
4878  mem.check_and_reset();
4879 
4880  // restore defaults.
4881  ryml::set_callbacks(defaults);
4882 }
4883 
4884 
4885 //-----------------------------------------------------------------------------
4886 
4887 /** @addtogroup doc_sample_helpers
4888  * @{ */
4889 
4890 /** an example for a per-tree memory allocator */
4892 {
4893  std::vector<char> memory_pool = std::vector<char>(10u * 1024u); // 10KB
4894  size_t num_allocs = 0, alloc_size = 0;
4895  size_t num_deallocs = 0, dealloc_size = 0;
4896 
4898  {
4899  // Above we used static functions to bridge to our methods.
4900  // To show a different approach, we employ lambdas here.
4901  // Note that there can be no captures in the lambdas
4902  // because these are C-style function pointers.
4903  ryml::Callbacks cb;
4904  cb.m_user_data = (void*) this;
4905  cb.m_allocate = [](size_t len, void *, void *data){ return ((PerTreeMemoryExample*) data)->allocate(len); };
4906  cb.m_free = [](void *mem, size_t len, void *data){ return ((PerTreeMemoryExample*) data)->free(mem, len); };
4907  return cb;
4908  }
4909 
4910  void *allocate(size_t len)
4911  {
4912  void *ptr = &memory_pool[alloc_size];
4913  alloc_size += len;
4914  ++num_allocs;
4915  if(C4_UNLIKELY(alloc_size > memory_pool.size()))
4916  {
4917  std::cerr << "out of memory! requested=" << alloc_size << " vs " << memory_pool.size() << " available" << std::endl;
4918  std::abort();
4919  }
4920  return ptr;
4921  }
4922 
4923  void free(void *mem, size_t len)
4924  {
4925  CHECK((char*)mem >= &memory_pool.front() && (char*)mem < &memory_pool.back());
4926  CHECK((char*)mem+len >= &memory_pool.front() && (char*)mem+len <= &memory_pool.back());
4927  dealloc_size += len;
4928  ++num_deallocs;
4929  // no need to free here
4930  }
4931 
4932  // checking
4934  {
4935  check_and_reset();
4936  }
4938  {
4939  std::cout << "size: alloc=" << alloc_size << " dealloc=" << dealloc_size << std::endl;
4940  std::cout << "count: #allocs=" << num_allocs << " #deallocs=" << num_deallocs << std::endl;
4942  CHECK(alloc_size >= dealloc_size); // failure here means a double free
4943  CHECK(alloc_size == dealloc_size); // failure here means a leak
4944  num_allocs = 0;
4945  num_deallocs = 0;
4946  alloc_size = 0;
4947  dealloc_size = 0;
4948  }
4949 };
4950 /** @} */
4951 
4953 {
4957 
4958  // the trees will use the memory in the resources above,
4959  // with each tree using a separate resource
4960  {
4961  // Watchout: ensure that the lifetime of the callbacks target
4962  // exceeds the lifetime of the tree.
4963  ryml::EventHandlerTree evt_handler(mrp.callbacks());
4964  ryml::Parser parser(&evt_handler);
4965  ryml::Tree tree1(mr1.callbacks());
4966  ryml::Tree tree2(mr2.callbacks());
4967 
4968  ryml::csubstr yml1 = "{a: b}";
4969  ryml::csubstr yml2 = "{c: d, e: f, g: [h, i, 0, 1, 2, 3]}";
4970 
4971  parse_in_arena(&parser, "file1.yml", yml1, &tree1);
4972  parse_in_arena(&parser, "file2.yml", yml2, &tree2);
4973  }
4974 
4975  CHECK(mrp.num_allocs == 0); // YAML depth not large enough to warrant a parser allocation
4976  CHECK(mr1.alloc_size <= mr2.alloc_size); // because yml2 has more nodes
4977 }
4978 
4979 
4980 //-----------------------------------------------------------------------------
4981 
4982 
4983 /** shows how to work around the static initialization order fiasco
4984  * when using a static-duration ryml tree
4985  * @see https://en.cppreference.com/w/cpp/language/siof */
4987 {
4988  // Using static trees incurs may incur a static initialization
4989  // order problem. This happens because a default-constructed tree will
4990  // obtain the callbacks from the current global setting, which may
4991  // not have been initialized due to undefined static initialization
4992  // order:
4993  //
4994  //static ryml::Tree tree; // ERROR! depends on ryml::get_callbacks() which may not have been initialized.
4995  //
4996  // To work around the issue, declare static callbacks
4997  // to explicitly initialize the static tree:
4998  static ryml::Callbacks callbacks = {}; // use default callback members
4999  static ryml::Tree tree(callbacks); // OK
5000  // now you can use the tree as normal:
5001  ryml::parse_in_arena(R"(doe: "a deer, a female deer")", &tree);
5002  CHECK(tree["doe"].val() == "a deer, a female deer");
5003 }
5004 
5005 
5006 //-----------------------------------------------------------------------------
5007 //-----------------------------------------------------------------------------
5008 //-----------------------------------------------------------------------------
5009 /** demonstrates how to obtain the (zero-based) location of a node
5010  * from a recently parsed tree */
5012 {
5013  // NOTE: locations are zero-based. If you intend to show the
5014  // location to a human user, you may want to pre-increment the line
5015  // and column by 1.
5016  ryml::csubstr yaml = R"({
5017 aa: contents,
5018 foo: [one, [two, three]]
5019 })";
5020  // A parser is needed to track locations, and it has to be
5021  // explicitly set to do it. Location tracking is disabled by
5022  // default.
5023  ryml::ParserOptions opts = {};
5024  opts.locations(true); // enable locations, default is false
5025  ryml::EventHandlerTree evt_handler = {};
5026  ryml::Parser parser(&evt_handler, opts);
5027  CHECK(parser.options().locations());
5028  // When locations are enabled, the first task while parsing will
5029  // consist of building and caching (in the parser) a
5030  // source-to-node lookup structure to accelerate location lookups.
5031  //
5032  // The cost of building the location accelerator is linear in the
5033  // size of the source buffer. This increased cost is the reason
5034  // for the opt-in requirement. When locations are disabled there
5035  // is no cost.
5036  //
5037  // Building the location accelerator may trigger an allocation,
5038  // but this can and should be avoided by reserving prior to
5039  // parsing:
5040  parser.reserve_locations(50u); // reserve for 50 lines
5041  // Now the structure will be built during parsing:
5042  ryml::Tree tree = parse_in_arena(&parser, "source.yml", yaml);
5043  // After this, we are ready to query the location from the parser:
5044  ryml::Location loc = parser.location(tree.rootref());
5045  // As for the complexity of the query: for large buffers it is
5046  // O(log(numlines)). For short source buffers (30 lines and less),
5047  // it is O(numlines), as a plain linear search is faster in this
5048  // case.
5049  CHECK(parser.location_contents(loc).begins_with("{"));
5050  CHECK(loc.offset == 0u);
5051  CHECK(loc.line == 0u);
5052  CHECK(loc.col == 0u);
5053  // on the next call, we only pay O(log(numlines)) because the
5054  // rebuild is already available:
5055  loc = parser.location(tree["aa"]);
5056  CHECK(parser.location_contents(loc).begins_with("aa"));
5057  CHECK(loc.offset == 2u);
5058  CHECK(loc.line == 1u);
5059  CHECK(loc.col == 0u);
5060  // KEYSEQ in flow style: points at the key
5061  loc = parser.location(tree["foo"]);
5062  CHECK(parser.location_contents(loc).begins_with("foo"));
5063  CHECK(loc.offset == 16u);
5064  CHECK(loc.line == 2u);
5065  CHECK(loc.col == 0u);
5066  loc = parser.location(tree["foo"][0]);
5067  CHECK(parser.location_contents(loc).begins_with("one"));
5068  CHECK(loc.line == 2u);
5069  CHECK(loc.col == 6u);
5070  // SEQ in flow style: location points at the opening '[' (there's no key)
5071  loc = parser.location(tree["foo"][1]);
5072  CHECK(parser.location_contents(loc).begins_with("["));
5073  CHECK(loc.line == 2u);
5074  CHECK(loc.col == 11u);
5075  loc = parser.location(tree["foo"][1][0]);
5076  CHECK(parser.location_contents(loc).begins_with("two"));
5077  CHECK(loc.line == 2u);
5078  CHECK(loc.col == 12u);
5079  loc = parser.location(tree["foo"][1][1]);
5080  CHECK(parser.location_contents(loc).begins_with("three"));
5081  CHECK(loc.line == 2u);
5082  CHECK(loc.col == 17u);
5083  // NOTE. The parser locations always point at the latest buffer to
5084  // be parsed with the parser object, so they must be queried using
5085  // the corresponding latest tree to be parsed. This means that if
5086  // the parser is reused, earlier trees will loose the possibility
5087  // of querying for location. It is undefined behavior to query the
5088  // parser for the location of a node from an earlier tree:
5089  ryml::Tree docval = parse_in_arena(&parser, "docval.yaml", "this is a docval");
5090  // From now on, none of the locations from the previous tree can
5091  // be queried:
5092  //loc = parser.location(tree.rootref()); // ERROR, undefined behavior
5093  loc = parser.location(docval.rootref()); // OK. this is the latest tree from this parser
5094  CHECK(parser.location_contents(loc).begins_with("this is a docval"));
5095  CHECK(loc.line == 0u);
5096  CHECK(loc.col == 0u);
5097 
5098  // NOTES ABOUT CONTAINER LOCATIONS
5099  ryml::Tree tree2 = parse_in_arena(&parser, "containers.yaml", R"(
5100 a new: buffer
5101 to: be parsed
5102 map with key:
5103  first: value
5104  second: value
5105 seq with key:
5106  - first value
5107  - second value
5108  -
5109  - nested first value
5110  - nested second value
5111  -
5112  nested first: value
5113  nested second: value
5114 )");
5115  // (Likewise, the docval tree can no longer be used to query.)
5116  //
5117  // For key-less block-style maps, the location of the container
5118  // points at the first child's key. For example, in this case
5119  // the root does not have a key, so its location is taken
5120  // to be at the first child:
5121  loc = parser.location(tree2.rootref());
5122  CHECK(parser.location_contents(loc).begins_with("a new"));
5123  CHECK(loc.offset == 1u);
5124  CHECK(loc.line == 1u);
5125  CHECK(loc.col == 0u);
5126  // note the first child points exactly at the same place:
5127  loc = parser.location(tree2["a new"]);
5128  CHECK(parser.location_contents(loc).begins_with("a new"));
5129  CHECK(loc.offset == 1u);
5130  CHECK(loc.line == 1u);
5131  CHECK(loc.col == 0u);
5132  loc = parser.location(tree2["to"]);
5133  CHECK(parser.location_contents(loc).begins_with("to"));
5134  CHECK(loc.line == 2u);
5135  CHECK(loc.col == 0u);
5136  // but of course, if the block-style map is a KEYMAP, then the
5137  // location is the map's key, and not the first child's key:
5138  loc = parser.location(tree2["map with key"]);
5139  CHECK(parser.location_contents(loc).begins_with("map with key"));
5140  CHECK(loc.line == 3u);
5141  CHECK(loc.col == 0u);
5142  loc = parser.location(tree2["map with key"]["first"]);
5143  CHECK(parser.location_contents(loc).begins_with("first"));
5144  CHECK(loc.line == 4u);
5145  CHECK(loc.col == 2u);
5146  loc = parser.location(tree2["map with key"]["second"]);
5147  CHECK(parser.location_contents(loc).begins_with("second"));
5148  CHECK(loc.line == 5u);
5149  CHECK(loc.col == 2u);
5150  // same thing for KEYSEQ:
5151  loc = parser.location(tree2["seq with key"]);
5152  CHECK(parser.location_contents(loc).begins_with("seq with key"));
5153  CHECK(loc.line == 6u);
5154  CHECK(loc.col == 0u);
5155  loc = parser.location(tree2["seq with key"][0]);
5156  CHECK(parser.location_contents(loc).begins_with("first value"));
5157  CHECK(loc.line == 7u);
5158  CHECK(loc.col == 4u);
5159  loc = parser.location(tree2["seq with key"][1]);
5160  CHECK(parser.location_contents(loc).begins_with("second value"));
5161  CHECK(loc.line == 8u);
5162  CHECK(loc.col == 4u);
5163  // SEQ nested in SEQ: container location points at the first child's "- " dash
5164  loc = parser.location(tree2["seq with key"][2]);
5165  CHECK(parser.location_contents(loc).begins_with("- nested first value"));
5166  CHECK(loc.line == 10u);
5167  CHECK(loc.col == 4u);
5168  loc = parser.location(tree2["seq with key"][2][0]);
5169  CHECK(parser.location_contents(loc).begins_with("nested first value"));
5170  CHECK(loc.line == 10u);
5171  CHECK(loc.col == 6u);
5172  // MAP nested in SEQ: same as above: point to key
5173  loc = parser.location(tree2["seq with key"][3]);
5174  CHECK(parser.location_contents(loc).begins_with("nested first: "));
5175  CHECK(loc.line == 13u);
5176  CHECK(loc.col == 4u);
5177  loc = parser.location(tree2["seq with key"][3][0]);
5178  CHECK(parser.location_contents(loc).begins_with("nested first: "));
5179  CHECK(loc.line == 13u);
5180  CHECK(loc.col == 4u);
5181 }
5182 
5183 
5184 //-----------------------------------------------------------------------------
5185 //-----------------------------------------------------------------------------
5186 //-----------------------------------------------------------------------------
5187 
5188 
5189 /** @addtogroup doc_sample_helpers
5190  * @{ */
5191 
5192 namespace /*anon*/ {
5193 static int num_checks = 0;
5194 static int num_failed_checks = 0;
5195 } // namespace /*anon*/
5196 
5197 
5198 bool report_check(int line, const char *predicate, bool result)
5199 {
5200  ++num_checks;
5201  const char *msg = predicate ? "OK! " : "OK!";
5202  if(!result)
5203  {
5204  ++num_failed_checks;
5205  msg = predicate ? "ERROR: " : "ERROR";
5206  }
5207  std::cout << __FILE__ << ':' << line << ": " << msg << (predicate ? predicate : "") << std::endl;
5208  return result;
5209 }
5210 
5211 
5213 {
5214  std::cout << "Completed " << num_checks << " checks." << std::endl;
5215  if(num_failed_checks)
5216  std::cout << "ERROR: " << num_failed_checks << '/' << num_checks << " checks failed." << std::endl;
5217  else
5218  std::cout << "SUCCESS!" << std::endl;
5219  return num_failed_checks;
5220 }
5221 
5222 // methods for the example error handler
5223 
5224 // this macro selects code for when exceptions are enabled/disabled
5225 C4_IF_EXCEPTIONS_( /*nothing for exceptions*/ ,
5226  /*environment for setjmp*/
5227  static std::jmp_buf s_jmp_env;
5228  static std::string s_jmp_msg;
5229  )
5230 
5231 // checking
5232 template<class Fn>
5233 C4_NODISCARD bool ErrorHandlerExample::check_error_occurs(Fn &&fn) const
5234 {
5235  bool expected_error_occurred = false;
5236  C4_IF_EXCEPTIONS_(try, if(setjmp(s_jmp_env) == 0)) // selectively picks based on availability of exceptions
5237  {
5238  fn();
5239  }
5240  C4_IF_EXCEPTIONS_(catch(...), else)
5241  {
5242  expected_error_occurred = true;
5243  }
5244  return expected_error_occurred;
5245 }
5246 template<class Fn>
5247 C4_NODISCARD bool ErrorHandlerExample::check_assertion_occurs(Fn &&fn) const
5248 {
5249 #if RYML_USE_ASSERT
5250  return check_error_occurs(fn);
5251 #else
5252  (void)fn; // do nothing otherwise, as there would be undefined behavior
5253  return true;
5254 #endif
5255 }
5256 
5257 /** this C-style callback is the one stored and used by ryml. It is a
5258  * trampoline function calling on_error() */
5259 C4_NORETURN void ErrorHandlerExample::s_error(const char* msg, size_t len, ryml::Location loc, void *this_)
5260 {
5261  ((ErrorHandlerExample*)this_)->on_error(msg, len, loc);
5262 }
5263 /** this is the where the callback implementation goes. Remember that it must not return. */
5264 C4_NORETURN void ErrorHandlerExample::on_error(const char* msg, size_t len, ryml::Location loc)
5265 {
5266  std::string full_msg = ryml::formatrs<std::string>(
5267  "{}:{}:{} ({}B): ERROR: {}",
5268  loc.name, loc.line, loc.col, loc.offset, ryml::csubstr(msg, len));
5269  C4_IF_EXCEPTIONS(
5270  // this will execute if exceptions are enabled.
5271  throw std::runtime_error(full_msg);
5272  ,
5273  // this will execute if exceptions are disabled. It will
5274  // jump to the function calling the corresponding setjmp().
5275  s_jmp_msg = full_msg;
5276  std::longjmp(s_jmp_env, 1);
5277  );
5278 }
5279 
5280 /** a helper to create the Callbacks object with the custom error handler */
5282 {
5283  return ryml::Callbacks(this, nullptr, nullptr, ErrorHandlerExample::s_error);
5284 }
5285 
5286 void ErrorHandlerExample::check_effect(bool committed) const
5287 {
5288  ryml::Callbacks const& current = ryml::get_callbacks();
5289  if(committed)
5290  {
5291  CHECK((ryml::pfn_error)current.m_error == &s_error);
5292  }
5293  else
5294  {
5295  CHECK((ryml::pfn_error)current.m_error != &s_error);
5296  }
5297  CHECK(current.m_allocate == defaults.m_allocate);
5298  CHECK(current.m_free == defaults.m_free);
5299 }
5300 
5301 
5302 // helper functions for sample_parse_file()
5303 
5304 C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4996) // fopen: this function may be unsafe
5305 /** load a file from disk into an existing CharContainer */
5306 template<class CharContainer>
5307 size_t file_get_contents(const char *filename, CharContainer *v)
5308 {
5309  std::FILE *fp = std::fopen(filename, "rb");
5310  RYML_CHECK_MSG(fp != nullptr, "could not open file");
5311  std::fseek(fp, 0, SEEK_END);
5312  long sz = std::ftell(fp);
5313  v->resize(static_cast<typename CharContainer::size_type>(sz));
5314  if(sz)
5315  {
5316  std::rewind(fp);
5317  size_t ret = std::fread(&(*v)[0], 1, v->size(), fp);
5318  RYML_CHECK(ret == (size_t)sz);
5319  }
5320  std::fclose(fp);
5321  return v->size();
5322 }
5323 
5324 /** load a file from disk and return a newly created CharContainer */
5325 template<class CharContainer>
5326 CharContainer file_get_contents(const char *filename)
5327 {
5328  CharContainer cc;
5329  file_get_contents(filename, &cc);
5330  return cc;
5331 }
5332 
5333 /** save a buffer into a file */
5334 template<class CharContainer>
5335 void file_put_contents(const char *filename, CharContainer const& v, const char* access)
5336 {
5337  file_put_contents(filename, v.empty() ? "" : &v[0], v.size(), access);
5338 }
5339 
5340 /** save a buffer into a file */
5341 void file_put_contents(const char *filename, const char *buf, size_t sz, const char* access)
5342 {
5343  std::FILE *fp = std::fopen(filename, access);
5344  RYML_CHECK_MSG(fp != nullptr, "could not open file");
5345  std::fwrite(buf, 1, sz, fp);
5346  std::fclose(fp);
5347 }
5348 C4_SUPPRESS_WARNING_MSVC_POP
5349 
5350 /** @} */ // doc_sample_helpers
5351 
5352 /** @} */ // doc_quickstart
5353 
5354 } // namespace sample
5355 
5356 C4_SUPPRESS_WARNING_GCC_CLANG_POP
Holds a pointer to an existing tree, and a node id.
Definition: node.hpp:838
Tree const * tree() const noexcept
Definition: node.hpp:910
id_type id() const noexcept
Definition: node.hpp:911
bool invalid() const noexcept
Definition: node.hpp:893
bool readable() const noexcept
because a ConstNodeRef cannot be used to write to the tree, readable() has the same meaning as !...
Definition: node.hpp:896
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition: node.hpp:979
id_type id() const noexcept
Definition: node.hpp:1104
bool invalid() const noexcept
true if the object is not referring to any existing or seed node.
Definition: node.hpp:1050
bool readable() const noexcept
true if the object is not invalid and not in seed state.
Definition: node.hpp:1054
NodeRef append_child()
Definition: node.hpp:1389
size_t set_val_serialized(T const &v)
Definition: node.hpp:1238
bool is_seed() const noexcept
true if the object is not invalid and in seed state.
Definition: node.hpp:1052
This is the main driver of parsing logic: it scans the YAML or JSON source for tokens,...
Location location(Tree const &tree, id_type node_id) const
Get the location of a node of the last tree to be parsed by this parser.
void reserve_stack(id_type capacity)
Reserve a certain capacity for the parsing stack.
csubstr location_contents(Location const &loc) const
Get the string starting at a particular location, to the end of the parsed source buffer.
ParserOptions const & options() const
Get the options used to build this parser object.
void reserve_locations(size_t num_source_lines)
Reserve a certain capacity for the array used to track node locations in the source buffer.
void reserve_arena(size_t arena_cap)
ensure the tree's internal string arena is at least the given capacity
Definition: tree.hpp:955
void clear()
clear the tree and zero every node
Definition: tree.cpp:292
id_type first_child(id_type node) const
Definition: tree.hpp:462
bool is_stream(id_type node) const
Definition: tree.hpp:363
NodeRef rootref()
Get the root as a NodeRef.
Definition: tree.cpp:21
auto to_arena(T const &a) -> typename std::enable_if< std::is_floating_point< T >::value, csubstr >::type
serialize the given floating-point variable to the tree's arena, growing it as needed to accomodate t...
Definition: tree.hpp:812
bool is_map(id_type node) const
Definition: tree.hpp:366
void clear_arena()
Definition: tree.hpp:233
bool is_root(id_type node) const
Definition: tree.hpp:415
substr alloc_arena(size_t sz)
grow the tree's string arena by the given size and return a substr of the added portion
Definition: tree.hpp:943
void reserve(id_type node_capacity)
Definition: tree.cpp:254
void resolve(ReferenceResolver *rr)
Resolve references (aliases <- anchors) in the tree.
Definition: tree.cpp:1121
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
Definition: tree.hpp:796
Callbacks const & callbacks() const
Definition: tree.hpp:241
void resolve_tags()
Definition: tree.cpp:1439
id_type next_sibling(id_type node) const
Definition: tree.hpp:457
ConstNodeRef crootref() const
Get the root as a ConstNodeRef.
Definition: tree.cpp:30
csubstr const & key(id_type node) const
Definition: tree.hpp:340
void normalize_tags()
Definition: tree.cpp:1449
NodeRef docref(id_type i)
get the i-th document of the stream
Definition: tree.cpp:69
bool is_doc(id_type node) const
Definition: tree.hpp:364
size_t arena_capacity() const
get the current capacity of the tree's internal arena
Definition: tree.hpp:786
id_type doc(id_type i) const
gets the i document node index.
Definition: tree.hpp:478
void set_val(id_type node, csubstr val)
Definition: tree.hpp:533
void normalize_tags_long()
Definition: tree.cpp:1456
csubstr const & val(id_type node) const
Definition: tree.hpp:346
bool is_seq(id_type node) const
Definition: tree.hpp:367
id_type find_child(id_type node, csubstr const &key) const
Definition: tree.cpp:1177
id_type root_id()
Get the id of the root node.
Definition: tree.hpp:288
id_type first_sibling(id_type node) const
Definition: tree.hpp:473
substr copy_to_arena(csubstr s)
copy the given substr to the tree's arena, growing it by the required size
Definition: tree.hpp:915
id_type num_children(id_type node) const
O(num_children)
Definition: tree.cpp:1131
csubstr arena() const
get the current arena
Definition: tree.hpp:791
id_type size() const
Definition: tree.hpp:237
id_type child(id_type node, id_type pos) const
Definition: tree.cpp:1139
provides type-safe facilities for formatting arguments to string buffers
left_< T > left(T val, size_t width, char padchar=' ')
mark an argument to be aligned left
Definition: format.hpp:525
right_< T > right(T val, size_t width, char padchar=' ')
mark an argument to be aligned right
Definition: format.hpp:532
const_base64_wrapper base64(Args const &...args)
mark a variable to be written in base64 format
Definition: base64.hpp:96
size_t base64_decode(csubstr encoded, blob data)
decode the base64 encoding in the given buffer
boolalpha_< T > boolalpha(T const &val, bool strict_read=false)
Definition: format.hpp:73
void(*)(const char *msg, size_t msg_len, Location location, void *user_data) pfn_error
the type of the function used to report errors
Definition: common.hpp:360
void set_callbacks(Callbacks const &c)
set the global callbacks for the library; after a call to this function, these callbacks will be used...
Definition: common.cpp:113
Callbacks const & get_callbacks()
get the global callbacks
Definition: common.cpp:118
substr cat_sub(substr buf, Args &&...args)
like c4::cat() but return a substr instead of a size
Definition: format.hpp:604
csubstr catrs_append(CharOwningContainer *cont, Args const &...args)
cat+resize+append: like c4::cat(), but receives a container, and appends to it instead of overwriting...
Definition: format.hpp:929
size_t cat(substr buf, Arg const &a, Args const &...more)
serialize the arguments, concatenating them to the given fixed-size buffer.
Definition: format.hpp:594
void catrs(CharOwningContainer *cont, Args const &...args)
cat+resize: like c4::cat(), but receives a container, and resizes it as needed to contain the result.
Definition: format.hpp:898
csubstr catseprs_append(CharOwningContainer *cont, Sep const &sep, Args const &...args)
catsep+resize+append: like catsep(), but receives a container, and appends the arguments,...
Definition: format.hpp:982
void catseprs(CharOwningContainer *cont, Sep const &sep, Args const &...args)
catsep+resize: like c4::catsep(), but receives a container, and resizes it as needed to contain the r...
Definition: format.hpp:951
size_t catsep(substr buf, Sep const &sep, Arg const &a, Args const &...more)
serialize the arguments, concatenating them to the given fixed-size buffer, using a separator between...
Definition: format.hpp:727
substr catsep_sub(substr buf, Args &&...args)
like c4::catsep() but return a substr instead of a size
Definition: format.hpp:738
@ FTOA_FLEX
print the real number in flexible format (like g)
Definition: charconv.hpp:199
@ FTOA_SCIENT
print the real number in scientific format (like e)
Definition: charconv.hpp:197
@ FTOA_FLOAT
print the real number in floating point format (like f)
Definition: charconv.hpp:195
@ FTOA_HEXA
print the real number in hexadecimal format (like a)
Definition: charconv.hpp:201
substr emitrs_yaml(Tree const &t, id_type id, EmitOptions const &opts, CharOwningContainer *cont, bool append=false)
(1) emit+resize: emit YAML to the given std::string/std::vector-like container, resizing it as needed...
Definition: emit.hpp:619
size_t emit_yaml(Tree const &t, id_type id, EmitOptions const &opts, FILE *f)
(1) emit YAML to the given file, starting at the given node.
Definition: emit.hpp:270
csubstr formatrs_append(CharOwningContainer *cont, csubstr fmt, Args const &...args)
format+resize+append: like format(), but receives a container, and appends the arguments,...
Definition: format.hpp:1033
substr format_sub(substr buf, csubstr fmt, Args const &...args)
like c4::format() but return a substr instead of a size
Definition: format.hpp:836
size_t format(substr buf, csubstr fmt, Arg const &a, Args const &...more)
using a format string, serialize the arguments into the given fixed-size buffer.
Definition: format.hpp:816
void formatrs(CharOwningContainer *cont, csubstr fmt, Args const &...args)
format+resize: like c4::format(), but receives a container, and resizes it as needed to contain the r...
Definition: format.hpp:1004
integral_< intptr_t > hex(std::nullptr_t)
format null as an hexadecimal value
Definition: format.hpp:164
integral_< intptr_t > bin(std::nullptr_t)
format null as a binary 0-1 value
Definition: format.hpp:216
integral_< intptr_t > oct(std::nullptr_t)
format null as an octal value
Definition: format.hpp:189
bool scalar_is_null(csubstr s) noexcept
YAML-sense query of nullity.
Definition: node_type.hpp:255
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition: node_type.hpp:35
@ KEY
is member of a map
Definition: node_type.hpp:33
@ FLOW_SL
mark container with single-line flow style (seqs as '[val1,val2], maps as '{key: val,...
Definition: node_type.hpp:56
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
Definition: node_type.hpp:36
void parse_in_arena(Parser *parser, csubstr filename, csubstr yaml, Tree *t, id_type node_id)
(1) parse YAML into an existing tree node. The filename will be used in any error messages arising du...
Definition: parse.cpp:91
void parse_json_in_arena(Parser *parser, csubstr filename, csubstr json, Tree *t, id_type node_id)
(1) parse JSON into an existing tree node. The filename will be used in any error messages arising du...
Definition: parse.cpp:112
void parse_in_place(Parser *parser, csubstr filename, substr yaml, Tree *t, id_type node_id)
(1) parse YAML into an existing tree node.
Definition: parse.cpp:37
void sample_fundamental_types()
ryml provides facilities for serializing and deserializing the C++ fundamental types,...
void sample_parse_in_place()
demonstrate in-place parsing of a mutable YAML source buffer.
void sample_quick_overview()
a brief tour over most features
Definition: quickstart.cpp:317
void sample_per_tree_allocator()
void sample_docs()
void sample_lightning_overview()
a lightning tour over most features see sample_quick_overview
Definition: quickstart.cpp:269
void sample_anchors_and_aliases()
demonstrates usage with anchors and alias references.
void sample_location_tracking()
demonstrates how to obtain the (zero-based) location of a node from a recently parsed tree
void sample_parse_reuse_tree()
demonstrate reuse/modification of tree when parsing
void check_effect(bool committed) const
void sample_substr()
demonstrate usage of ryml::substr and ryml::csubstr
Definition: quickstart.cpp:936
void sample_tag_directives()
void sample_emit_nested_node()
just like parsing into a nested node, you can also emit from a nested node.
void sample_float_precision()
control precision of serialized floats
bool check_assertion_occurs(Fn &&fn) const
void sample_create_trees()
shows how to programatically create trees
static void s_error(const char *msg, size_t len, ryml::Location loc, void *this_)
void sample_user_scalar_types()
to add scalar types (ie leaf types converting to/from string), define the functions above for those t...
void sample_user_container_types()
shows how to serialize/deserialize container types.
bool check_error_occurs(Fn &&fn) const
void sample_error_handler()
demonstrates how to set a custom error handler for ryml
void sample_parse_in_arena()
demonstrate parsing of a read-only YAML source buffer
void sample_emit_to_file()
demonstrates how to emit to a FILE*
void sample_emit_style()
[experimental] pick flow/block style for certain nodes.
void sample_empty_null_values()
Shows how to deal with empty/null values.
void sample_std_types()
demonstrates usage with the std implementations provided by ryml in the ryml_std.hpp header
void sample_tags()
ryml::Callbacks callbacks()
a helper to create the Callbacks object with the custom error handler
void sample_emit_to_stream()
demonstrates how to emit to a stream-like structure
void sample_global_allocator()
demonstrates how to set the global allocator for ryml
void sample_formatting()
ryml provides facilities for formatting/deformatting (imported from c4core into the ryml namespace).
void sample_parse_file()
demonstrate how to load a YAML file from disk to parse with ryml.
void sample_json()
shows how to parse and emit JSON.
void sample_emit_to_container()
demonstrates how to emit to a linear container of char
void sample_parse_reuse_tree_and_parser()
for ultimate speed when parsing multiple times, reuse both the tree and parser
void sample_base64()
demonstrates how to read and write base64-encoded blobs.
void sample_static_trees()
shows how to work around the static initialization order fiasco when using a static-duration ryml tre...
void sample_tree_arena()
demonstrates explicit and implicit interaction with the tree's string arena.
void sample_parse_reuse_parser()
Demonstrates reuse of an existing parser.
void sample_iterate_trees()
shows how to programatically iterate through trees
const_raw_wrapper raw(cblob data, size_t alignment=alignof(max_align_t))
mark a variable to be written in raw binary format, using memcpy
Definition: format.hpp:426
const_raw_wrapper craw(cblob data, size_t alignment=alignof(max_align_t))
mark a variable to be written in raw binary format, using memcpy
Definition: format.hpp:420
real_< T > real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)
Definition: format.hpp:370
void write(ryml::NodeRef *n, my_seq_type< T > const &seq)
bool read(ryml::ConstNodeRef const &n, my_seq_type< T > *seq)
bool from_chars(ryml::csubstr buf, vec2< T > *v)
size_t file_get_contents(const char *filename, CharContainer *v)
load a file from disk into an existing CharContainer
void file_put_contents(const char *filename, CharContainer const &v, const char *access="wb")
save a buffer into a file
C4_IF_EXCEPTIONS_(, static std::jmp_buf s_jmp_env;static std::string s_jmp_msg;) template< class Fn > bool ErrorHandlerExample
this C-style callback is the one stored and used by ryml.
void file_put_contents(const char *filename, const char *buf, size_t sz, const char *access)
save a buffer into a file
int report_checks()
bool report_check(int line, const char *predicate, bool result)
CharContainer file_get_contents(const char *filename)
load a file from disk and return a newly created CharContainer
#define CHECK(predicate)
Definition: quickstart.cpp:229
void on_error(const char *msg, size_t len, ryml::Location loc)
this is the where the callback implementation goes.
size_t to_chars(ryml::substr buf, vec2< T > v)
Key< K > key(K &k)
Definition: node.hpp:43
csubstr to_csubstr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2186
substr to_substr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2184
csubstr from_tag_long(YamlTag_e tag)
Definition: tag.cpp:123
csubstr normalize_tag_long(csubstr tag)
Definition: tag.cpp:31
csubstr normalize_tag(csubstr tag)
Definition: tag.cpp:19
csubstr from_tag(YamlTag_e tag)
Definition: tag.cpp:163
YamlTag_e to_tag(csubstr tag)
Definition: tag.cpp:67
@ TAG_SET
!!set Unordered set of non-equal values.
Definition: tag.hpp:32
@ TAG_INT
!!float Mathematical integers.
Definition: tag.hpp:38
@ TAG_SEQ
!!seq Sequence of arbitrary values.
Definition: tag.hpp:33
@ TAG_NONE
Definition: tag.hpp:27
@ TAG_STR
!!str A sequence of zero or more Unicode characters.
Definition: tag.hpp:41
@ TAG_MAP
!!map Unordered set of key: value pairs without duplicates.
Definition: tag.hpp:29
size_t uncat(csubstr buf, Arg &a, Args &...more)
deserialize the arguments from the given buffer.
Definition: format.hpp:635
size_t uncatsep(csubstr buf, Sep &sep, Arg &a, Args &...more)
deserialize the arguments from the given buffer.
Definition: format.hpp:768
size_t unformat(csubstr buf, csubstr fmt, Arg &a, Args &...more)
using a format string, deserialize the arguments from the given buffer.
Definition: format.hpp:865
integral_padded_< T > zpad(T val, size_t num_digits)
pad the argument with zeroes on the left, with decimal radix
Definition: format.hpp:236
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
Definition: common.hpp:253
@ npos
a null string position
Definition: common.hpp:267
@ NONE
an index to none
Definition: common.hpp:260
Definition: ryml.hpp:6
a c-style callbacks class.
Definition: common.hpp:376
void * m_user_data
Definition: common.hpp:377
pfn_allocate m_allocate
Definition: common.hpp:378
pfn_free m_free
Definition: common.hpp:379
pfn_error m_error
Definition: common.hpp:380
The event handler to create a ryml Tree.
a source file position
Definition: common.hpp:297
size_t col
column
Definition: common.hpp:303
size_t line
line
Definition: common.hpp:301
size_t offset
number of bytes from the beginning of the source buffer
Definition: common.hpp:299
csubstr name
file name
Definition: common.hpp:305
Options to give to the parser to control its behavior.
ParserOptions & locations(bool enabled) noexcept
enable/disable source location tracking
mark a tree or node to be emitted as yaml when using operator<<, with options.
Definition: emit.hpp:406
auto first_child() RYML_NOEXCEPT -> Impl
Forward to Tree::first_child().
Definition: node.hpp:336
auto children() RYML_NOEXCEPT -> children_view
get an iterable view over children
Definition: node.hpp:747
bool is_val() const RYML_NOEXCEPT
Forward to Tree::is_val().
Definition: node.hpp:241
bool is_root() const RYML_NOEXCEPT
Forward to Tree::is_root().
Definition: node.hpp:303
auto last_child() RYML_NOEXCEPT -> Impl
Forward to Tree::last_child().
Definition: node.hpp:340
bool is_stream() const RYML_NOEXCEPT
Forward to Tree::is_stream().
Definition: node.hpp:234
auto parent() RYML_NOEXCEPT -> Impl
Forward to Tree::parent().
Definition: node.hpp:332
bool has_child(ConstImpl const &n) const RYML_NOEXCEPT
Forward to Tree::has_child().
Definition: node.hpp:306
auto next_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::next_sibling().
Definition: node.hpp:356
auto last_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::last_sibling().
Definition: node.hpp:364
bool is_map() const RYML_NOEXCEPT
Forward to Tree::is_map().
Definition: node.hpp:237
id_type num_siblings() const RYML_NOEXCEPT
O(num_children).
Definition: node.hpp:376
bool has_key() const RYML_NOEXCEPT
Forward to Tree::has_key().
Definition: node.hpp:240
csubstr key() const RYML_NOEXCEPT
Forward to Tree::key().
Definition: node.hpp:207
auto first_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::first_sibling().
Definition: node.hpp:360
csubstr val() const RYML_NOEXCEPT
Forward to Tree::val().
Definition: node.hpp:212
bool is_doc() const RYML_NOEXCEPT
Forward to Tree::is_doc().
Definition: node.hpp:235
id_type num_children() const RYML_NOEXCEPT
O(num_children).
Definition: node.hpp:375
auto child(id_type pos) RYML_NOEXCEPT -> Impl
Forward to Tree::child().
Definition: node.hpp:344
bool is_seq() const RYML_NOEXCEPT
Forward to Tree::is_seq().
Definition: node.hpp:238
bool has_val() const RYML_NOEXCEPT
Forward to Tree::has_val().
Definition: node.hpp:239
auto prev_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::prev_sibling().
Definition: node.hpp:352
this is an example error handler, required for some of the quickstart examples.
Definition: quickstart.cpp:243
static void * s_allocate(size_t len, void *, void *this_)
void free(void *mem, size_t len)
std::vector< char > memory_pool
static void s_free(void *mem, size_t len, void *this_)
an example for a per-tree memory allocator
std::vector< char > memory_pool
ryml::Callbacks callbacks() const
void free(void *mem, size_t len)
void * allocate(size_t len)
Shows how to easily create a scoped error handler.
Definition: quickstart.cpp:255
example scalar type, serialized only
example scalar type, serialized only
example scalar type, serialized only
example user container type: map-like
std::map< K, V > map_member
example user container type: seq-like
std::vector< T > seq_member
example user container type with nested container members.
vec3< int > v3
my_seq_type< int > seq
vec4< int > v4
vec2< int > v2
my_map_type< int, int > map
example scalar type, deserialized only
example scalar type, deserialized only
example scalar type, deserialized only
example scalar type, serialized and deserialized
example scalar type, serialized and deserialized
example scalar type, serialized and deserialized