rapidyaml 0.16.0
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches

Functions

void sample_iterate_tree ()
 visit individual nodes and iterate through trees
void sample_location_tracking ()
 track node YAML source locations in the parsed tree
void sample_create_tree ()
 programatically create trees
void sample_create_tree_style ()
 set node styles while creating trees
void sample_tree_arena ()
 interact with the tree's serialization arena

Detailed Description

Function Documentation

◆ sample_iterate_tree()

void sample_iterate_tree ( )

visit individual nodes and iterate through trees

shows how to programatically iterate through trees

See also
Tree utilities
Node classes

Definition at line 2398 of file quickstart.cpp.

2399{
2400 const ryml::Tree tree = ryml::parse_in_arena(
2401 "doe: a deer, a female deer" "\n"
2402 "ray: a drop of golden sun" "\n"
2403 "pi: 3.14159" "\n"
2404 "xmas: true" "\n"
2405 "french-hens: 3" "\n"
2406 "calling-birds:" "\n"
2407 " - huey" "\n"
2408 " - dewey" "\n"
2409 " - louie" "\n"
2410 " - fred" "\n"
2411 "xmas-fifth-day:" "\n"
2412 " calling-birds: four" "\n"
2413 " french-hens: 3" "\n"
2414 " golden-rings: 5" "\n"
2415 " partridges:" "\n"
2416 " count: 1" "\n"
2417 " location: a pear tree" "\n"
2418 " turtle-doves: two" "\n"
2419 "cars: GTO" "\n"
2420 "");
2421 ryml::ConstNodeRef root = tree.crootref();
2422
2423 // iterate children
2424 {
2425 std::vector<ryml::csubstr> keys, vals; // to store all the root-level keys, vals
2426 for(ryml::ConstNodeRef n : root.children())
2427 {
2428 keys.emplace_back(n.key());
2429 vals.emplace_back(n.has_val() ? n.val() : ryml::csubstr{});
2430 }
2431 CHECK(keys.size() >= 6);
2432 CHECK(vals.size() >= 6);
2433 if(keys.size() >= 6 && vals.size() >= 6)
2434 {
2435 CHECK(keys[0] == "doe");
2436 CHECK(vals[0] == "a deer, a female deer");
2437 CHECK(keys[1] == "ray");
2438 CHECK(vals[1] == "a drop of golden sun");
2439 CHECK(keys[2] == "pi");
2440 CHECK(vals[2] == "3.14159");
2441 CHECK(keys[3] == "xmas");
2442 CHECK(vals[3] == "true");
2443 CHECK(root[5].has_key());
2444 CHECK(root[5].is_seq());
2445 CHECK(root[5].key() == "calling-birds");
2446 CHECK(!root[5].has_val()); // it is a map, so not a val
2447 //CHECK(root[5].val() == ""); // ERROR! node does not have a val.
2448 CHECK(keys[5] == "calling-birds");
2449 CHECK(vals[5] == "");
2450 }
2451 }
2452
2453 // iterate siblings
2454 {
2455 size_t count = 0;
2456 ryml::csubstr calling_birds[] = {"huey", "dewey", "louie", "fred"};
2457 for(ryml::ConstNodeRef n : root["calling-birds"][2].siblings())
2458 CHECK(n.val() == calling_birds[count++]);
2459 CHECK(count == 4u);
2460 }
2461}
Holds a pointer to an existing tree, and a node id.
Definition node.hpp:737
const_children_view children() const RYML_NOEXCEPT
get an iterable view over children
Definition node.hpp:987
ConstNodeRef crootref() const
Get the root as a ConstNodeRef . Note that Tree implicitly converts to ConstNodeRef.
Definition tree.cpp:65
void parse_in_arena(Parser *parser, csubstr filename, csubstr yaml, Tree *tree, 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:209
#define CHECK(predicate)
a testing assertion, used only in this quickstart
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2356

Referenced by main().

◆ sample_location_tracking()

void sample_location_tracking ( )

track node YAML source locations in the parsed tree

demonstrates how to obtain the (zero-based) location of a node from a recently parsed tree

See also
RYML_LOCATIONS_SMALL_THRESHOLD

Definition at line 2471 of file quickstart.cpp.

2472{
2473 // NOTE: locations are zero-based. If you intend to show the
2474 // location to a human user, you may want to pre-increment the line
2475 // and column by 1.
2476 ryml::csubstr yaml = ""
2477 "{" "\n"
2478 "aa: contents," "\n"
2479 "foo: [one, [two, three]]" "\n"
2480 "}" "\n"
2481 "";
2482 // A parser is needed to track locations, and it has to be
2483 // explicitly set to do it. Location tracking is disabled by
2484 // default.
2485 ryml::ParserOptions opts = {};
2486 opts.locations(true); // enable locations, default is false
2487 ryml::EventHandlerTree evt_handler = {};
2488 ryml::Parser parser(&evt_handler, opts);
2489 CHECK(parser.options().locations());
2490 // When locations are enabled, the first task while parsing will
2491 // consist of building and caching (in the parser) a
2492 // source-to-node lookup structure to accelerate location lookups.
2493 //
2494 // The cost of building the location accelerator is linear in the
2495 // size of the source buffer. This increased cost is the reason
2496 // for the opt-in requirement. When locations are disabled there
2497 // is no cost.
2498 //
2499 // Building the location accelerator may trigger an allocation,
2500 // but this can and should be avoided by reserving prior to
2501 // parsing:
2502 parser.reserve_locations(50u); // reserve for 50 lines of YAML code
2503 // Now the structure will be built during parsing:
2504 ryml::Tree tree = parse_in_arena(&parser, "source.yml", yaml);
2505 // After this, we are ready to query the location from the parser:
2506 ryml::Location loc = tree.rootref().location(parser);
2507 // As for the complexity of the query: for large buffers it is
2508 // O(log(numlines)). For short source buffers
2509 // (RYML_LOCATIONS_SMALL_THRESHOLD lines and less), it is
2510 // O(numlines), as a plain linear search is faster in this case.
2511 CHECK(parser.location_contents(loc).begins_with("{"));
2512 CHECK(loc.offset == 0u);
2513 CHECK(loc.line == 0u);
2514 CHECK(loc.col == 0u);
2515 // on the next call, we only pay O(log(numlines)) because the
2516 // rebuild is already available:
2517 loc = tree["aa"].location(parser);
2518 CHECK(parser.location_contents(loc).begins_with("aa"));
2519 CHECK(loc.offset == 2u);
2520 CHECK(loc.line == 1u);
2521 CHECK(loc.col == 0u);
2522 // KEYSEQ in flow style: points at the key
2523 loc = tree["foo"].location(parser);
2524 CHECK(parser.location_contents(loc).begins_with("foo"));
2525 CHECK(loc.offset == 16u);
2526 CHECK(loc.line == 2u);
2527 CHECK(loc.col == 0u);
2528 loc = tree["foo"][0].location(parser);
2529 CHECK(parser.location_contents(loc).begins_with("one"));
2530 CHECK(loc.line == 2u);
2531 CHECK(loc.col == 6u);
2532 // SEQ in flow style: location points at the opening '[' (there's no key)
2533 loc = tree["foo"][1].location(parser);
2534 CHECK(parser.location_contents(loc).begins_with("["));
2535 CHECK(loc.line == 2u);
2536 CHECK(loc.col == 11u);
2537 loc = tree["foo"][1][0].location(parser);
2538 CHECK(parser.location_contents(loc).begins_with("two"));
2539 CHECK(loc.line == 2u);
2540 CHECK(loc.col == 12u);
2541 loc = tree["foo"][1][1].location(parser);
2542 CHECK(parser.location_contents(loc).begins_with("three"));
2543 CHECK(loc.line == 2u);
2544 CHECK(loc.col == 17u);
2545 // NOTE. The parser locations always point at the latest buffer to
2546 // be parsed with the parser object, so they must be queried using
2547 // the corresponding latest tree to be parsed. This means that if
2548 // the parser is reused, earlier trees will loose the possibility
2549 // of querying for location. It is undefined behavior to query the
2550 // parser for the location of a node from an earlier tree:
2551 ryml::Tree docval = parse_in_arena(&parser, "docval.yaml", "this is a docval");
2552 // From now on, none of the locations from the previous tree can
2553 // be queried:
2554 //loc = tree.rootref().location(parser); // ERROR, undefined behavior
2555 loc = docval.rootref().location(parser); // OK. this is the latest tree from this parser
2556 CHECK(parser.location_contents(loc).begins_with("this is a docval"));
2557 CHECK(loc.line == 0u);
2558 CHECK(loc.col == 0u);
2559
2560 // NOTES ABOUT CONTAINER LOCATIONS
2561 ryml::Tree tree2 = parse_in_arena(&parser, "containers.yaml",
2562 "" "\n"
2563 "a new: buffer" "\n"
2564 "to: be parsed" "\n"
2565 "map with key:" "\n"
2566 " first: value" "\n"
2567 " second: value" "\n"
2568 "seq with key:" "\n"
2569 " - first value" "\n"
2570 " - second value" "\n"
2571 " -" "\n"
2572 " - nested first value" "\n"
2573 " - nested second value" "\n"
2574 " -" "\n"
2575 " nested first: value" "\n"
2576 " nested second: value" "\n"
2577 "");
2578 // (Likewise, the docval tree can no longer be used to query.)
2579 //
2580 // For key-less block-style maps, the location of the container
2581 // points at the first child's key. For example, in this case
2582 // the root does not have a key, so its location is taken
2583 // to be at the first child:
2584 loc = tree2.rootref().location(parser);
2585 CHECK(parser.location_contents(loc).begins_with("a new"));
2586 CHECK(loc.offset == 1u);
2587 CHECK(loc.line == 1u);
2588 CHECK(loc.col == 0u);
2589 // note the first child points exactly at the same place:
2590 loc = tree2["a new"].location(parser);
2591 CHECK(parser.location_contents(loc).begins_with("a new"));
2592 CHECK(loc.offset == 1u);
2593 CHECK(loc.line == 1u);
2594 CHECK(loc.col == 0u);
2595 loc = tree2["to"].location(parser);
2596 CHECK(parser.location_contents(loc).begins_with("to"));
2597 CHECK(loc.line == 2u);
2598 CHECK(loc.col == 0u);
2599 // but of course, if the block-style map is a KEYMAP, then the
2600 // location is the map's key, and not the first child's key:
2601 loc = tree2["map with key"].location(parser);
2602 CHECK(parser.location_contents(loc).begins_with("map with key"));
2603 CHECK(loc.line == 3u);
2604 CHECK(loc.col == 0u);
2605 loc = tree2["map with key"]["first"].location(parser);
2606 CHECK(parser.location_contents(loc).begins_with("first"));
2607 CHECK(loc.line == 4u);
2608 CHECK(loc.col == 2u);
2609 loc = tree2["map with key"]["second"].location(parser);
2610 CHECK(parser.location_contents(loc).begins_with("second"));
2611 CHECK(loc.line == 5u);
2612 CHECK(loc.col == 2u);
2613 // same thing for KEYSEQ:
2614 loc = tree2["seq with key"].location(parser);
2615 CHECK(parser.location_contents(loc).begins_with("seq with key"));
2616 CHECK(loc.line == 6u);
2617 CHECK(loc.col == 0u);
2618 loc = tree2["seq with key"][0].location(parser);
2619 CHECK(parser.location_contents(loc).begins_with("first value"));
2620 CHECK(loc.line == 7u);
2621 CHECK(loc.col == 4u);
2622 loc = tree2["seq with key"][1].location(parser);
2623 CHECK(parser.location_contents(loc).begins_with("second value"));
2624 CHECK(loc.line == 8u);
2625 CHECK(loc.col == 4u);
2626 // SEQ nested in SEQ: container location points at the first child's "- " dash
2627 loc = tree2["seq with key"][2].location(parser);
2628 CHECK(parser.location_contents(loc).begins_with("- nested first value"));
2629 CHECK(loc.line == 10u);
2630 CHECK(loc.col == 4u);
2631 loc = tree2["seq with key"][2][0].location(parser);
2632 CHECK(parser.location_contents(loc).begins_with("nested first value"));
2633 CHECK(loc.line == 10u);
2634 CHECK(loc.col == 6u);
2635 // MAP nested in SEQ: same as above: point to key
2636 loc = tree2["seq with key"][3].location(parser);
2637 CHECK(parser.location_contents(loc).begins_with("nested first: "));
2638 CHECK(loc.line == 13u);
2639 CHECK(loc.col == 4u);
2640 loc = tree2["seq with key"][3][0].location(parser);
2641 CHECK(parser.location_contents(loc).begins_with("nested first: "));
2642 CHECK(loc.line == 13u);
2643 CHECK(loc.col == 4u);
2644}
NodeRef rootref()
Get the root as a NodeRef . Note that a non-const Tree implicitly converts to NodeRef.
Definition tree.cpp:56
Location location(Parser const &p, id_type node) const
Get the location of a node from the parse used to parse this tree.
Definition tree.cpp:1913
ParseEngine< EventHandlerTree > Parser
This is the main ryml parser, where the parser events are handled to create a ryml tree (see Event Ha...
Definition fwd.hpp:19
The event handler to create a ryml Tree.
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition common.hpp:229
size_t col
column
Definition common.hpp:232
size_t line
line
Definition common.hpp:231
size_t offset
number of bytes from the beginning of the source buffer
Definition common.hpp:230
Options to give to the ParseEngine to control its behavior.
ParserOptions & locations(bool enabled) noexcept
enable/disable source location tracking.
Location location(Parser const &parser) const
Definition node.hpp:318

Referenced by main().

◆ sample_create_tree()

void sample_create_tree ( )

programatically create trees

shows how to programatically create trees

See also
Tree utilities
Node classes
sample_create_tree_style()

Definition at line 2654 of file quickstart.cpp.

2655{
2656 ryml::NodeRef doe;
2657 CHECK(doe.invalid()); // it's pointing at nowhere
2658
2659 ryml::Tree tree;
2660 ryml::NodeRef root = tree.rootref();
2661 root.set_map(); // mark root as a map
2662 doe = root["doe"];
2663 CHECK(!doe.invalid()); // it's now pointing at the tree
2664 CHECK(doe.is_seed()); // but the tree has nothing there, so this is only a seed
2665
2666 // set the value of the node
2667 const char a_deer[] = "a deer, a female deer";
2668 doe.set_val(a_deer);
2669 // now the node really exists in the tree, and this ref is no
2670 // longer a seed:
2671 CHECK(!doe.is_seed());
2672 // WATCHOUT for lifetimes:
2673 CHECK(doe.val().str == a_deer); // it is pointing at the initial string
2674 // If you need to avoid lifetime dependency, serialize the data:
2675 {
2676 std::string a_drop = "a drop of golden sun";
2677 // this will copy the string to the tree's arena:
2678 // (see the serialization samples below)
2679 root["ray"].set_serialized(a_drop);
2680 // and now you can modify the original string without changing
2681 // the tree:
2682 a_drop[0] = 'Z';
2683 a_drop[1] = 'Z';
2684 }
2685 CHECK(root["ray"].val() == "a drop of golden sun");
2686
2687 // etc.
2688 root["pi"].set_serialized(ryml::fmt::real(3.141592654, 5));
2689 root["xmas"].set_serialized(ryml::fmt::boolalpha(true));
2690 root["french-hens"].set_serialized(3);
2691 ryml::NodeRef calling_birds = root["calling-birds"];
2692 calling_birds.set_seq();
2693 calling_birds.append_child().set_val("huey");
2694 calling_birds.append_child().set_val("dewey");
2695 calling_birds.append_child().set_val("louie");
2696 calling_birds.append_child().set_val("fred");
2697 ryml::NodeRef xmas5 = root["xmas-fifth-day"];
2698 xmas5.set_map();
2699 xmas5["calling-birds"].set_val("four");
2700 xmas5["french-hens"].set_serialized(3);
2701 xmas5["golden-rings"].set_serialized(5);
2702 xmas5["partridges"].set_map();
2703 xmas5["partridges"]["count"].set_serialized(1);
2704 xmas5["partridges"]["location"].set_val("a pear tree");
2705 xmas5["turtle-doves"].set_val("two");
2706 root["cars"].set_val("GTO");
2707
2709 "doe: a deer, a female deer" "\n"
2710 "ray: a drop of golden sun" "\n"
2711 "pi: 3.14159" "\n"
2712 "xmas: true" "\n"
2713 "french-hens: 3" "\n"
2714 "calling-birds:" "\n"
2715 " - huey" "\n"
2716 " - dewey" "\n"
2717 " - louie" "\n"
2718 " - fred" "\n"
2719 "xmas-fifth-day:" "\n"
2720 " calling-birds: four" "\n"
2721 " french-hens: 3" "\n"
2722 " golden-rings: 5" "\n"
2723 " partridges:" "\n"
2724 " count: 1" "\n"
2725 " location: a pear tree" "\n"
2726 " turtle-doves: two" "\n"
2727 "cars: GTO" "\n"
2728 "");
2729
2730 // NOTE: it is good practice to set scalar styles when building
2731 // the tree. See the sample_create_tree_style() below.
2732}
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition node.hpp:1063
void set_serialized(T const &v)
serialize a variable to this node.
Definition node.hpp:1375
void set_val(csubstr val)
Definition node.hpp:1251
bool invalid() const noexcept
true if the object is not referring to any existing or seed node.
Definition node.hpp:1134
NodeRef append_child()
Definition node.hpp:1715
bool is_seed() const noexcept
true if the object is not invalid and in seed state.
Definition node.hpp:1136
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<char>-like container,...
C * str
a restricted pointer to the first character of the substring
Definition substr.hpp:216
csubstr val() const RYML_NOEXCEPT
Forward to Tree::val().
Definition node.hpp:179

Referenced by main().

◆ sample_create_tree_style()

void sample_create_tree_style ( )

set node styles while creating trees

Shows how to set styles when building a tree: this may be needed because you want to control the emitted YAML, but for scalars there is also a performance reason to do this.

Note
You can and should set the style when creating a tree for emitting later. This is because YAML has constraints on which styles can be used for a particular scalar (eg, a plain scalar have leading or trailing whitespace, or if in flow mode it cannot have comma followed by space).
When the scalar is not marked with an explicit style, the ryml emitter adheres to these constraints by scanning each scalar to choose a style for it. On the other hand, if the scalar is marked with an explicit style, the emitter will honor that style, and not do the scan.
So explicitly setting the style for scalars saves the emitter from having to scan each scalar while emitting.
For containers, setting the style does not offer an emit performance improvement like with scalars. Nevertheless, you may still find it useful to control the emitted YAML.

Following this recommendation, ryml also explicitly sets the styles while parsing.

Definition at line 2764 of file quickstart.cpp.

2765{
2766 ryml::Tree tree;
2767 ryml::NodeRef root = tree;
2768 root.set_map(ryml::BLOCK);
2769 // with .set_*() we should use explicit styles (because .set_*()
2770 // only takes csubstr arguments)
2771 root["not plain"].set_val(" with whitespace "); // no style set
2772 root["doe"].set_val("a deer, a female deer", ryml::VAL_PLAIN);
2773 root["ray"].set_val("a drop of golden sun", ryml::VAL_SQUO);
2774 root["me"].set_val("a name I call myself", ryml::VAL_DQUO);
2775 root["far"].set_val("a long long way to go", ryml::VAL_LITERAL);
2776 root["sow"].set_val("a needle pulling thread", ryml::VAL_FOLDED);
2777 root["seq"].set_seq(ryml::FLOW_SL|ryml::FLOW_SPC); // flow, single-line, with spaces after commas
2778 root["map1"].set_map(ryml::FLOW_ML1); // flow, multiline, 1 value per line
2779 root["mapn"].set_map(ryml::FLOW_MLN); // flow, multiline, N values per line, wrapped
2780 // likewise for all of set_serialized(), set_key(),
2781 // set_key_serialized(), save(), save_key(): all these accept the
2782 // style as an extra argument. But when serializing there's a
2783 // nice feature: ryml will automatically set the scalar style to
2784 // VAL_PLAIN / KEY_PLAIN when its type verifies std::is_arithmetic<T>.
2785 for(int i : {0, 1, 2, 3})
2786 {
2787 ryml::NodeRef childseq = root["seq"].append_child();
2788 ryml::NodeRef childmap1 = root["map1"].append_child();
2789 ryml::NodeRef childmapn = root["mapn"].append_child();
2790 // Note how we're NOT setting the style:
2791 childseq.set_serialized(i);
2792 childmap1.set_key_serialized(i + 1);
2793 childmapn.set_key_serialized(i + 1);
2794 childmap1.set_serialized((i + 1) * 10);
2795 childmapn.set_serialized((i + 1) * 10);
2796 // ... and yet ryml has set it to plain:
2797 CHECK(childseq.is_val_plain());
2798 CHECK(childmap1.is_key_plain());
2799 CHECK(childmapn.is_key_plain());
2800 CHECK(childmap1.is_val_plain());
2801 CHECK(childmapn.is_val_plain());
2802 }
2803 // let's see the styles now:
2805 // note how this is quoted, without having explicit style
2806 // set. That is because plain scalars cannot have
2807 // leading/trailing whitespace characters. This scalar did
2808 // not have a style set, so during emitting it was scanned
2809 // to determine its style, and so VAL_SQUO was chosen:
2810 "not plain: ' with whitespace '" "\n"
2811 // as for the rest, the style is honored, and the scalars
2812 // are not scanned by the emitter, resulting in a
2813 // performance increase:
2814 "doe: a deer, a female deer" "\n"
2815 "ray: 'a drop of golden sun'" "\n"
2816 "me: \"a name I call myself\"" "\n"
2817 "far: |-" "\n"
2818 " a long long way to go" "\n"
2819 "sow: >-" "\n"
2820 " a needle pulling thread" "\n"
2821 "seq: [0, 1, 2, 3]" "\n"
2822 "map1: {" "\n"
2823 " 1: 10," "\n"
2824 " 2: 20," "\n"
2825 " 3: 30," "\n"
2826 " 4: 40" "\n"
2827 " }" "\n"
2828 "mapn: {" "\n"
2829 " 1: 10,2: 20,3: 30,4: 40" "\n"
2830 " }" "\n"
2831 "");
2832 // Note that it would be an error to set a scalar style
2833 // incompatible with the scalar contents. ryml always honor the
2834 // node style, so it will blindly emit roundtrip-unstable YAML if
2835 // the style is incompatible.
2836 //
2837 // For example, this is incorrect because a plain scalar cannot
2838 // have leading or trailing whitespace:
2839 root["plain"].set_val(" with whitespace ", ryml::VAL_PLAIN); // incorrect.
2840 // note how the whitespace will be lost when parsed:
2841 CHECK(ryml::emitrs_yaml<std::string>(root["plain"]) ==
2842 "plain: with whitespace " "\n"
2843 "");
2844}
void set_key_serialized(T const &k)
serialize a variable, then assign the result to the node's key
Definition node.hpp:1396
@ FLOW_ML1
mark container with multi-line flow style, 1 element per line
Definition node_type.hpp:75
@ VAL_FOLDED
mark val scalar as multiline, block folded >
@ FLOW_SL
mark container with single-line flow style
Definition node_type.hpp:56
@ FLOW_MLN
mark container with multi-line flow style, n elements per line, wrapped (as set by EmitOptions::max_c...
Definition node_type.hpp:90
@ VAL_SQUO
mark val scalar as single quoted '
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
@ BLOCK
mark container with block style
@ FLOW_SPC
mark container with spaces after comma when in flow mode. Applies to both FLOW_SL and FLOW_MLN (but n...
@ VAL_DQUO
mark val scalar as double quoted "
@ VAL_LITERAL
mark val scalar as multiline, block literal |
bool is_key_plain() const RYML_NOEXCEPT
Forward to Tree::is_key_plain().
Definition node.hpp:262
bool is_val_plain() const RYML_NOEXCEPT
Forward to Tree::is_val_plain().
Definition node.hpp:263

Referenced by main().

◆ sample_tree_arena()

void sample_tree_arena ( )

interact with the tree's serialization arena

demonstrates explicit and implicit interaction with the tree's string arena.

Note that ryml only holds strings in the tree's nodes.

Definition at line 2852 of file quickstart.cpp.

2853{
2854 // mutable buffers are parsed in place:
2855 {
2856 char buf[] = "[a, b, c, d]";
2857 ryml::substr yml = buf;
2858 ryml::Tree tree = ryml::parse_in_place(yml);
2859 // notice the arena is empty:
2860 CHECK(tree.arena().empty());
2861 // and the tree is pointing at the original buffer:
2862 ryml::NodeRef root = tree.rootref();
2863 CHECK(root[0].val().is_sub(yml));
2864 CHECK(root[1].val().is_sub(yml));
2865 CHECK(root[2].val().is_sub(yml));
2866 CHECK(root[3].val().is_sub(yml));
2867 CHECK(yml.is_super(root[0].val()));
2868 CHECK(yml.is_super(root[1].val()));
2869 CHECK(yml.is_super(root[2].val()));
2870 CHECK(yml.is_super(root[3].val()));
2871 }
2872
2873 // when parsing immutable buffers, the buffer is first copied to the
2874 // tree's arena; the copy in the arena is then the buffer which is
2875 // actually parsed
2876 {
2877 ryml::csubstr yml = "[a, b, c, d]";
2878 ryml::Tree tree = ryml::parse_in_arena(yml);
2879 // notice the buffer was copied to the arena:
2880 CHECK(tree.arena().data() != yml.data());
2881 CHECK(tree.arena() == yml);
2882 // and the tree is pointing at the arena instead of to the
2883 // original buffer:
2884 ryml::NodeRef root = tree.rootref();
2885 ryml::csubstr arena = tree.arena();
2886 CHECK(root[0].val().is_sub(arena));
2887 CHECK(root[1].val().is_sub(arena));
2888 CHECK(root[2].val().is_sub(arena));
2889 CHECK(root[3].val().is_sub(arena));
2890 CHECK(arena.is_super(root[0].val()));
2891 CHECK(arena.is_super(root[1].val()));
2892 CHECK(arena.is_super(root[2].val()));
2893 CHECK(arena.is_super(root[3].val()));
2894 }
2895
2896 // the arena is also used when the data is serialized to the tree.
2897 // first example: parse in place
2898 {
2899 char buf[] = "[a, b, c, d]"; // mutable
2900 ryml::substr yml = buf;
2901 ryml::Tree tree = ryml::parse_in_place(yml);
2902 // notice the arena is empty:
2903 CHECK(tree.arena().empty());
2904 ryml::NodeRef root = tree.rootref();
2905
2906 // serialize an integer, and mutate the tree
2907 CHECK(root[2].val() == "c");
2908 CHECK(root[2].val().is_sub(yml)); // val is first pointing at the buffer
2909 root[2].set_serialized(12345);
2910 CHECK(root[2].val() == "12345");
2911 CHECK(root[2].val().is_sub(tree.arena())); // now val is pointing at the arena
2912 // notice the serialized string was appended to the tree's arena:
2913 CHECK(tree.arena() == "12345");
2914
2915 // serialize an integer, and mutate the tree
2916 CHECK(root[3].val() == "d");
2917 CHECK(root[3].val().is_sub(yml)); // val is first pointing at the buffer
2918 root[3].set_serialized(67890);
2919 CHECK(root[3].val() == "67890");
2920 CHECK(root[3].val().is_sub(tree.arena())); // now val is pointing at the arena
2921 // notice the serialized string was appended to the tree's arena:
2922 CHECK(tree.arena() == "1234567890");
2923 }
2924 // the arena is also used when the data is serialized to string
2925 // second example: parse in arena
2926 {
2927 ryml::csubstr yml = "[a, b, c, d]"; // immutable
2928 ryml::Tree tree = ryml::parse_in_arena(yml);
2929 // notice the buffer was copied to the arena:
2930 CHECK(tree.arena().data() != yml.data());
2931 CHECK(tree.arena() == yml);
2932 ryml::NodeRef root = tree.rootref();
2933
2934 // serialize an integer, and mutate the tree
2935 CHECK(root[2].val() == "c");
2936 root[2].set_serialized(12345); // serialize an integer
2937 CHECK(root[2].val() == "12345");
2938 // notice the serialized string was appended to the tree's arena:
2939 // notice also the previous values remain there.
2940 // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2941 CHECK(tree.arena() == "[a, b, c, d]12345");
2942 // old values: --------------^
2943
2944 // serialize an integer, and mutate the tree
2945 root[3].set_serialized(67890);
2946 CHECK(root[3].val() == "67890");
2947 // notice the serialized string was appended to the tree's arena:
2948 // notice also the previous values remain there.
2949 // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2950 CHECK(tree.arena() == "[a, b, c, d]1234567890");
2951 // old values: --------------^ ---^^^^^
2952 }
2953
2954 // to_arena(): directly serialize values to the arena:
2955 {
2956 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2957 ryml::csubstr c10 = tree.to_arena(10101010);
2958 CHECK(c10 == "10101010");
2959 CHECK(c10.is_sub(tree.arena()));
2960 CHECK(tree.arena() == "{a: b}10101010");
2961 CHECK(tree.key(1) == "a");
2962 CHECK(tree.val(1) == "b");
2963 tree.set_val(1, c10);
2964 CHECK(tree.val(1) == c10);
2965 // and you can also do it through a node:
2966 ryml::NodeRef root = tree.rootref();
2967 root["a"].set_serialized(2222);
2968 CHECK(root["a"].val() == "2222");
2969 CHECK(tree.arena() == "{a: b}101010102222");
2970 }
2971
2972 // copy_to_arena(): manually copy a string to the arena:
2973 {
2974 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2975 ryml::csubstr mystr = "Gosset Grande Reserve";
2976 ryml::csubstr copied = tree.copy_to_arena(mystr);
2977 CHECK(!copied.overlaps(mystr));
2978 CHECK(copied == mystr);
2979 CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2980 }
2981
2982 // alloc_arena(): allocate a buffer from the arena:
2983 {
2984 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2985 ryml::csubstr mystr = "Gosset Grande Reserve";
2986 ryml::substr copied = tree.alloc_arena(mystr.size());
2987 CHECK(!copied.overlaps(mystr));
2988 memcpy(copied.str, mystr.str, mystr.len);
2989 CHECK(copied == mystr);
2990 CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2991 }
2992
2993 // reserve_arena(): ensure the arena has a certain size to avoid reallocations
2994 {
2995 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2996 CHECK(tree.arena().size() == strlen("{a: b}"));
2997 tree.reserve_arena(100);
2998 CHECK(tree.arena_capacity() >= 100);
2999 CHECK(tree.arena().size() == strlen("{a: b}"));
3000 tree.to_arena(123456);
3001 CHECK(tree.arena().first(12) == "{a: b}123456");
3002 }
3003}
csubstr const & key(id_type node) const
Definition tree.hpp:454
void reserve_arena(size_t arena_cap=RYML_DEFAULT_TREE_ARENA_CAPACITY)
ensure the tree's internal string arena is at least the given capacity
Definition tree.hpp:1409
void set_val(id_type node, csubstr val) RYML_NOEXCEPT
Definition tree.hpp:688
csubstr const & val(id_type node) const
Definition tree.hpp:460
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:1397
csubstr to_arena(T const &a)
serialize the given variable to the tree's arena, growing it as needed to accomodate the serializatio...
Definition tree.hpp:1349
size_t arena_capacity() const
get the current capacity of the tree's internal arena
Definition tree.hpp:1311
substr copy_to_arena(csubstr s)
copy the given string to the tree's arena, growing the arena by the required size.
Definition tree.hpp:1370
csubstr arena() const
get the current arena
Definition tree.hpp:1317
void parse_in_place(Parser *parser, csubstr filename, substr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node.
Definition parse.cpp:165
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2355
size_t len
the length of the substring
Definition substr.hpp:218
size_t size() const noexcept
Definition substr.hpp:358
bool overlaps(ro_substr const that) const noexcept
true if there is overlap of at least one element between that and *this
Definition substr.hpp:493
basic_substring first(size_t num) const noexcept
return the first num elements: [0,num[
Definition substr.hpp:529
C * data() noexcept
Definition substr.hpp:366
bool empty() const noexcept
Definition substr.hpp:356
bool is_super(ro_substr const that) const noexcept
true if that is a substring of *this (ie, from the same buffer)
Definition substr.hpp:484
bool is_sub(ro_substr const that) const noexcept
true if *this is a substring of that (ie, from the same buffer)
Definition substr.hpp:478

Referenced by main().