rapidyaml 0.15.2
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 2396 of file quickstart.cpp.

2397{
2398 const ryml::Tree tree = ryml::parse_in_arena(
2399 "doe: a deer, a female deer" "\n"
2400 "ray: a drop of golden sun" "\n"
2401 "pi: 3.14159" "\n"
2402 "xmas: true" "\n"
2403 "french-hens: 3" "\n"
2404 "calling-birds:" "\n"
2405 " - huey" "\n"
2406 " - dewey" "\n"
2407 " - louie" "\n"
2408 " - fred" "\n"
2409 "xmas-fifth-day:" "\n"
2410 " calling-birds: four" "\n"
2411 " french-hens: 3" "\n"
2412 " golden-rings: 5" "\n"
2413 " partridges:" "\n"
2414 " count: 1" "\n"
2415 " location: a pear tree" "\n"
2416 " turtle-doves: two" "\n"
2417 "cars: GTO" "\n"
2418 "");
2419 ryml::ConstNodeRef root = tree.crootref();
2420
2421 // iterate children
2422 {
2423 std::vector<ryml::csubstr> keys, vals; // to store all the root-level keys, vals
2424 for(ryml::ConstNodeRef n : root.children())
2425 {
2426 keys.emplace_back(n.key());
2427 vals.emplace_back(n.has_val() ? n.val() : ryml::csubstr{});
2428 }
2429 CHECK(keys.size() >= 6);
2430 CHECK(vals.size() >= 6);
2431 if(keys.size() >= 6 && vals.size() >= 6)
2432 {
2433 CHECK(keys[0] == "doe");
2434 CHECK(vals[0] == "a deer, a female deer");
2435 CHECK(keys[1] == "ray");
2436 CHECK(vals[1] == "a drop of golden sun");
2437 CHECK(keys[2] == "pi");
2438 CHECK(vals[2] == "3.14159");
2439 CHECK(keys[3] == "xmas");
2440 CHECK(vals[3] == "true");
2441 CHECK(root[5].has_key());
2442 CHECK(root[5].is_seq());
2443 CHECK(root[5].key() == "calling-birds");
2444 CHECK(!root[5].has_val()); // it is a map, so not a val
2445 //CHECK(root[5].val() == ""); // ERROR! node does not have a val.
2446 CHECK(keys[5] == "calling-birds");
2447 CHECK(vals[5] == "");
2448 }
2449 }
2450
2451 // iterate siblings
2452 {
2453 size_t count = 0;
2454 ryml::csubstr calling_birds[] = {"huey", "dewey", "louie", "fred"};
2455 for(ryml::ConstNodeRef n : root["calling-birds"][2].siblings())
2456 CHECK(n.val() == calling_birds[count++]);
2457 CHECK(count == 4u);
2458 }
2459}
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 2469 of file quickstart.cpp.

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

2653{
2654 ryml::NodeRef doe;
2655 CHECK(doe.invalid()); // it's pointing at nowhere
2656
2657 ryml::Tree tree;
2658 ryml::NodeRef root = tree.rootref();
2659 root.set_map(); // mark root as a map
2660 doe = root["doe"];
2661 CHECK(!doe.invalid()); // it's now pointing at the tree
2662 CHECK(doe.is_seed()); // but the tree has nothing there, so this is only a seed
2663
2664 // set the value of the node
2665 const char a_deer[] = "a deer, a female deer";
2666 doe.set_val(a_deer);
2667 // now the node really exists in the tree, and this ref is no
2668 // longer a seed:
2669 CHECK(!doe.is_seed());
2670 // WATCHOUT for lifetimes:
2671 CHECK(doe.val().str == a_deer); // it is pointing at the initial string
2672 // If you need to avoid lifetime dependency, serialize the data:
2673 {
2674 std::string a_drop = "a drop of golden sun";
2675 // this will copy the string to the tree's arena:
2676 // (see the serialization samples below)
2677 root["ray"].set_serialized(a_drop);
2678 // and now you can modify the original string without changing
2679 // the tree:
2680 a_drop[0] = 'Z';
2681 a_drop[1] = 'Z';
2682 }
2683 CHECK(root["ray"].val() == "a drop of golden sun");
2684
2685 // etc.
2686 root["pi"].set_serialized(ryml::fmt::real(3.141592654, 5));
2687 root["xmas"].set_serialized(ryml::fmt::boolalpha(true));
2688 root["french-hens"].set_serialized(3);
2689 ryml::NodeRef calling_birds = root["calling-birds"];
2690 calling_birds.set_seq();
2691 calling_birds.append_child().set_val("huey");
2692 calling_birds.append_child().set_val("dewey");
2693 calling_birds.append_child().set_val("louie");
2694 calling_birds.append_child().set_val("fred");
2695 ryml::NodeRef xmas5 = root["xmas-fifth-day"];
2696 xmas5.set_map();
2697 xmas5["calling-birds"].set_val("four");
2698 xmas5["french-hens"].set_serialized(3);
2699 xmas5["golden-rings"].set_serialized(5);
2700 xmas5["partridges"].set_map();
2701 xmas5["partridges"]["count"].set_serialized(1);
2702 xmas5["partridges"]["location"].set_val("a pear tree");
2703 xmas5["turtle-doves"].set_val("two");
2704 root["cars"].set_val("GTO");
2705
2707 "doe: a deer, a female deer" "\n"
2708 "ray: a drop of golden sun" "\n"
2709 "pi: 3.14159" "\n"
2710 "xmas: true" "\n"
2711 "french-hens: 3" "\n"
2712 "calling-birds:" "\n"
2713 " - huey" "\n"
2714 " - dewey" "\n"
2715 " - louie" "\n"
2716 " - fred" "\n"
2717 "xmas-fifth-day:" "\n"
2718 " calling-birds: four" "\n"
2719 " french-hens: 3" "\n"
2720 " golden-rings: 5" "\n"
2721 " partridges:" "\n"
2722 " count: 1" "\n"
2723 " location: a pear tree" "\n"
2724 " turtle-doves: two" "\n"
2725 "cars: GTO" "\n"
2726 "");
2727
2728 // NOTE: it is good practice to set scalar styles when building
2729 // the tree. See the sample_create_tree_style() below.
2730}
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 2762 of file quickstart.cpp.

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

2851{
2852 // mutable buffers are parsed in place:
2853 {
2854 char buf[] = "[a, b, c, d]";
2855 ryml::substr yml = buf;
2856 ryml::Tree tree = ryml::parse_in_place(yml);
2857 // notice the arena is empty:
2858 CHECK(tree.arena().empty());
2859 // and the tree is pointing at the original buffer:
2860 ryml::NodeRef root = tree.rootref();
2861 CHECK(root[0].val().is_sub(yml));
2862 CHECK(root[1].val().is_sub(yml));
2863 CHECK(root[2].val().is_sub(yml));
2864 CHECK(root[3].val().is_sub(yml));
2865 CHECK(yml.is_super(root[0].val()));
2866 CHECK(yml.is_super(root[1].val()));
2867 CHECK(yml.is_super(root[2].val()));
2868 CHECK(yml.is_super(root[3].val()));
2869 }
2870
2871 // when parsing immutable buffers, the buffer is first copied to the
2872 // tree's arena; the copy in the arena is then the buffer which is
2873 // actually parsed
2874 {
2875 ryml::csubstr yml = "[a, b, c, d]";
2876 ryml::Tree tree = ryml::parse_in_arena(yml);
2877 // notice the buffer was copied to the arena:
2878 CHECK(tree.arena().data() != yml.data());
2879 CHECK(tree.arena() == yml);
2880 // and the tree is pointing at the arena instead of to the
2881 // original buffer:
2882 ryml::NodeRef root = tree.rootref();
2883 ryml::csubstr arena = tree.arena();
2884 CHECK(root[0].val().is_sub(arena));
2885 CHECK(root[1].val().is_sub(arena));
2886 CHECK(root[2].val().is_sub(arena));
2887 CHECK(root[3].val().is_sub(arena));
2888 CHECK(arena.is_super(root[0].val()));
2889 CHECK(arena.is_super(root[1].val()));
2890 CHECK(arena.is_super(root[2].val()));
2891 CHECK(arena.is_super(root[3].val()));
2892 }
2893
2894 // the arena is also used when the data is serialized to the tree.
2895 // first example: parse in place
2896 {
2897 char buf[] = "[a, b, c, d]"; // mutable
2898 ryml::substr yml = buf;
2899 ryml::Tree tree = ryml::parse_in_place(yml);
2900 // notice the arena is empty:
2901 CHECK(tree.arena().empty());
2902 ryml::NodeRef root = tree.rootref();
2903
2904 // serialize an integer, and mutate the tree
2905 CHECK(root[2].val() == "c");
2906 CHECK(root[2].val().is_sub(yml)); // val is first pointing at the buffer
2907 root[2].set_serialized(12345);
2908 CHECK(root[2].val() == "12345");
2909 CHECK(root[2].val().is_sub(tree.arena())); // now val is pointing at the arena
2910 // notice the serialized string was appended to the tree's arena:
2911 CHECK(tree.arena() == "12345");
2912
2913 // serialize an integer, and mutate the tree
2914 CHECK(root[3].val() == "d");
2915 CHECK(root[3].val().is_sub(yml)); // val is first pointing at the buffer
2916 root[3].set_serialized(67890);
2917 CHECK(root[3].val() == "67890");
2918 CHECK(root[3].val().is_sub(tree.arena())); // now val is pointing at the arena
2919 // notice the serialized string was appended to the tree's arena:
2920 CHECK(tree.arena() == "1234567890");
2921 }
2922 // the arena is also used when the data is serialized to string
2923 // second example: parse in arena
2924 {
2925 ryml::csubstr yml = "[a, b, c, d]"; // immutable
2926 ryml::Tree tree = ryml::parse_in_arena(yml);
2927 // notice the buffer was copied to the arena:
2928 CHECK(tree.arena().data() != yml.data());
2929 CHECK(tree.arena() == yml);
2930 ryml::NodeRef root = tree.rootref();
2931
2932 // serialize an integer, and mutate the tree
2933 CHECK(root[2].val() == "c");
2934 root[2].set_serialized(12345); // serialize an integer
2935 CHECK(root[2].val() == "12345");
2936 // notice the serialized string was appended to the tree's arena:
2937 // notice also the previous values remain there.
2938 // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2939 CHECK(tree.arena() == "[a, b, c, d]12345");
2940 // old values: --------------^
2941
2942 // serialize an integer, and mutate the tree
2943 root[3].set_serialized(67890);
2944 CHECK(root[3].val() == "67890");
2945 // notice the serialized string was appended to the tree's arena:
2946 // notice also the previous values remain there.
2947 // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2948 CHECK(tree.arena() == "[a, b, c, d]1234567890");
2949 // old values: --------------^ ---^^^^^
2950 }
2951
2952 // to_arena(): directly serialize values to the arena:
2953 {
2954 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2955 ryml::csubstr c10 = tree.to_arena(10101010);
2956 CHECK(c10 == "10101010");
2957 CHECK(c10.is_sub(tree.arena()));
2958 CHECK(tree.arena() == "{a: b}10101010");
2959 CHECK(tree.key(1) == "a");
2960 CHECK(tree.val(1) == "b");
2961 tree.set_val(1, c10);
2962 CHECK(tree.val(1) == c10);
2963 // and you can also do it through a node:
2964 ryml::NodeRef root = tree.rootref();
2965 root["a"].set_serialized(2222);
2966 CHECK(root["a"].val() == "2222");
2967 CHECK(tree.arena() == "{a: b}101010102222");
2968 }
2969
2970 // copy_to_arena(): manually copy a string to the arena:
2971 {
2972 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2973 ryml::csubstr mystr = "Gosset Grande Reserve";
2974 ryml::csubstr copied = tree.copy_to_arena(mystr);
2975 CHECK(!copied.overlaps(mystr));
2976 CHECK(copied == mystr);
2977 CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2978 }
2979
2980 // alloc_arena(): allocate a buffer from the arena:
2981 {
2982 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2983 ryml::csubstr mystr = "Gosset Grande Reserve";
2984 ryml::substr copied = tree.alloc_arena(mystr.size());
2985 CHECK(!copied.overlaps(mystr));
2986 memcpy(copied.str, mystr.str, mystr.len);
2987 CHECK(copied == mystr);
2988 CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2989 }
2990
2991 // reserve_arena(): ensure the arena has a certain size to avoid reallocations
2992 {
2993 ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2994 CHECK(tree.arena().size() == strlen("{a: b}"));
2995 tree.reserve_arena(100);
2996 CHECK(tree.arena_capacity() >= 100);
2997 CHECK(tree.arena().size() == strlen("{a: b}"));
2998 tree.to_arena(123456);
2999 CHECK(tree.arena().first(12) == "{a: b}123456");
3000 }
3001}
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().