duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

tree.hpp (60220B)


      1 #ifndef _C4_YML_TREE_HPP_
      2 #define _C4_YML_TREE_HPP_
      3 
      4 
      5 #include "c4/error.hpp"
      6 #include "c4/types.hpp"
      7 #ifndef _C4_YML_COMMON_HPP_
      8 #include "c4/yml/common.hpp"
      9 #endif
     10 
     11 #include <c4/charconv.hpp>
     12 #include <cmath>
     13 #include <limits>
     14 
     15 
     16 C4_SUPPRESS_WARNING_MSVC_PUSH
     17 C4_SUPPRESS_WARNING_MSVC(4251) // needs to have dll-interface to be used by clients of struct
     18 C4_SUPPRESS_WARNING_MSVC(4296) // expression is always 'boolean_value'
     19 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
     20 C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
     21 C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
     22 
     23 
     24 namespace c4 {
     25 namespace yml {
     26 
     27 struct NodeScalar;
     28 struct NodeInit;
     29 struct NodeData;
     30 class NodeRef;
     31 class ConstNodeRef;
     32 class Tree;
     33 
     34 
     35 /** encode a floating point value to a string. */
     36 template<class T>
     37 size_t to_chars_float(substr buf, T val)
     38 {
     39     C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wfloat-equal");
     40     static_assert(std::is_floating_point<T>::value, "must be floating point");
     41     if(C4_UNLIKELY(std::isnan(val)))
     42         return to_chars(buf, csubstr(".nan"));
     43     else if(C4_UNLIKELY(val == std::numeric_limits<T>::infinity()))
     44         return to_chars(buf, csubstr(".inf"));
     45     else if(C4_UNLIKELY(val == -std::numeric_limits<T>::infinity()))
     46         return to_chars(buf, csubstr("-.inf"));
     47     return to_chars(buf, val);
     48     C4_SUPPRESS_WARNING_GCC_CLANG_POP
     49 }
     50 
     51 
     52 /** decode a floating point from string. Accepts special values: .nan,
     53  * .inf, -.inf */
     54 template<class T>
     55 bool from_chars_float(csubstr buf, T *C4_RESTRICT val)
     56 {
     57     static_assert(std::is_floating_point<T>::value, "must be floating point");
     58     if(C4_LIKELY(from_chars(buf, val)))
     59     {
     60         return true;
     61     }
     62     else if(C4_UNLIKELY(buf == ".nan" || buf == ".NaN" || buf == ".NAN"))
     63     {
     64         *val = std::numeric_limits<T>::quiet_NaN();
     65         return true;
     66     }
     67     else if(C4_UNLIKELY(buf == ".inf" || buf == ".Inf" || buf == ".INF"))
     68     {
     69         *val = std::numeric_limits<T>::infinity();
     70         return true;
     71     }
     72     else if(C4_UNLIKELY(buf == "-.inf" || buf == "-.Inf" || buf == "-.INF"))
     73     {
     74         *val = -std::numeric_limits<T>::infinity();
     75         return true;
     76     }
     77     else
     78     {
     79         return false;
     80     }
     81 }
     82 
     83 
     84 //-----------------------------------------------------------------------------
     85 //-----------------------------------------------------------------------------
     86 //-----------------------------------------------------------------------------
     87 
     88 /** the integral type necessary to cover all the bits marking node tags */
     89 using tag_bits = uint16_t;
     90 
     91 /** a bit mask for marking tags for types */
     92 typedef enum : tag_bits {
     93     // container types
     94     TAG_NONE      =  0,
     95     TAG_MAP       =  1, /**< !!map   Unordered set of key: value pairs without duplicates. @see https://yaml.org/type/map.html */
     96     TAG_OMAP      =  2, /**< !!omap  Ordered sequence of key: value pairs without duplicates. @see https://yaml.org/type/omap.html */
     97     TAG_PAIRS     =  3, /**< !!pairs Ordered sequence of key: value pairs allowing duplicates. @see https://yaml.org/type/pairs.html */
     98     TAG_SET       =  4, /**< !!set   Unordered set of non-equal values. @see https://yaml.org/type/set.html */
     99     TAG_SEQ       =  5, /**< !!seq   Sequence of arbitrary values. @see https://yaml.org/type/seq.html */
    100     // scalar types
    101     TAG_BINARY    =  6, /**< !!binary A sequence of zero or more octets (8 bit values). @see https://yaml.org/type/binary.html */
    102     TAG_BOOL      =  7, /**< !!bool   Mathematical Booleans. @see https://yaml.org/type/bool.html */
    103     TAG_FLOAT     =  8, /**< !!float  Floating-point approximation to real numbers. https://yaml.org/type/float.html */
    104     TAG_INT       =  9, /**< !!float  Mathematical integers. https://yaml.org/type/int.html */
    105     TAG_MERGE     = 10, /**< !!merge  Specify one or more mapping to be merged with the current one. https://yaml.org/type/merge.html */
    106     TAG_NULL      = 11, /**< !!null   Devoid of value. https://yaml.org/type/null.html */
    107     TAG_STR       = 12, /**< !!str    A sequence of zero or more Unicode characters. https://yaml.org/type/str.html */
    108     TAG_TIMESTAMP = 13, /**< !!timestamp A point in time https://yaml.org/type/timestamp.html */
    109     TAG_VALUE     = 14, /**< !!value  Specify the default value of a mapping https://yaml.org/type/value.html */
    110     TAG_YAML      = 15, /**< !!yaml   Specify the default value of a mapping https://yaml.org/type/yaml.html */
    111 } YamlTag_e;
    112 
    113 YamlTag_e to_tag(csubstr tag);
    114 csubstr from_tag(YamlTag_e tag);
    115 csubstr from_tag_long(YamlTag_e tag);
    116 csubstr normalize_tag(csubstr tag);
    117 csubstr normalize_tag_long(csubstr tag);
    118 
    119 struct TagDirective
    120 {
    121     /** Eg `!e!` in `%TAG !e! tag:example.com,2000:app/` */
    122     csubstr handle;
    123     /** Eg `tag:example.com,2000:app/` in `%TAG !e! tag:example.com,2000:app/` */
    124     csubstr prefix;
    125     /** The next node to which this tag directive applies */
    126     size_t next_node_id;
    127 };
    128 
    129 #ifndef RYML_MAX_TAG_DIRECTIVES
    130 /** the maximum number of tag directives in a Tree */
    131 #define RYML_MAX_TAG_DIRECTIVES 4
    132 #endif
    133 
    134 
    135 
    136 //-----------------------------------------------------------------------------
    137 //-----------------------------------------------------------------------------
    138 //-----------------------------------------------------------------------------
    139 
    140 
    141 /** the integral type necessary to cover all the bits marking node types */
    142 using type_bits = uint64_t;
    143 
    144 
    145 /** a bit mask for marking node types */
    146 typedef enum : type_bits {
    147     // a convenience define, undefined below
    148     #define c4bit(v) (type_bits(1) << v)
    149     NOTYPE  = 0,            ///< no node type is set
    150     VAL     = c4bit(0),     ///< a leaf node, has a (possibly empty) value
    151     KEY     = c4bit(1),     ///< is member of a map, must have non-empty key
    152     MAP     = c4bit(2),     ///< a map: a parent of keyvals
    153     SEQ     = c4bit(3),     ///< a seq: a parent of vals
    154     DOC     = c4bit(4),     ///< a document
    155     STREAM  = c4bit(5)|SEQ, ///< a stream: a seq of docs
    156     KEYREF  = c4bit(6),     ///< a *reference: the key references an &anchor
    157     VALREF  = c4bit(7),     ///< a *reference: the val references an &anchor
    158     KEYANCH = c4bit(8),     ///< the key has an &anchor
    159     VALANCH = c4bit(9),     ///< the val has an &anchor
    160     KEYTAG  = c4bit(10),    ///< the key has an explicit tag/type
    161     VALTAG  = c4bit(11),    ///< the val has an explicit tag/type
    162     _TYMASK = c4bit(12)-1,  // all the bits up to here
    163     VALQUO  = c4bit(12),    ///< the val is quoted by '', "", > or |
    164     KEYQUO  = c4bit(13),    ///< the key is quoted by '', "", > or |
    165     KEYVAL  = KEY|VAL,
    166     KEYSEQ  = KEY|SEQ,
    167     KEYMAP  = KEY|MAP,
    168     DOCMAP  = DOC|MAP,
    169     DOCSEQ  = DOC|SEQ,
    170     DOCVAL  = DOC|VAL,
    171     _KEYMASK = KEY | KEYQUO | KEYANCH | KEYREF | KEYTAG,
    172     _VALMASK = VAL | VALQUO | VALANCH | VALREF | VALTAG,
    173     // these flags are from a work in progress and should be used with care
    174     _WIP_STYLE_FLOW_SL = c4bit(14), ///< mark container with single-line flow format (seqs as '[val1,val2], maps as '{key: val, key2: val2}')
    175     _WIP_STYLE_FLOW_ML = c4bit(15), ///< mark container with multi-line flow format (seqs as '[val1,\nval2], maps as '{key: val,\nkey2: val2}')
    176     _WIP_STYLE_BLOCK   = c4bit(16), ///< mark container with block format (seqs as '- val\n', maps as 'key: val')
    177     _WIP_KEY_LITERAL   = c4bit(17), ///< mark key scalar as multiline, block literal |
    178     _WIP_VAL_LITERAL   = c4bit(18), ///< mark val scalar as multiline, block literal |
    179     _WIP_KEY_FOLDED    = c4bit(19), ///< mark key scalar as multiline, block folded >
    180     _WIP_VAL_FOLDED    = c4bit(20), ///< mark val scalar as multiline, block folded >
    181     _WIP_KEY_SQUO      = c4bit(21), ///< mark key scalar as single quoted
    182     _WIP_VAL_SQUO      = c4bit(22), ///< mark val scalar as single quoted
    183     _WIP_KEY_DQUO      = c4bit(23), ///< mark key scalar as double quoted
    184     _WIP_VAL_DQUO      = c4bit(24), ///< mark val scalar as double quoted
    185     _WIP_KEY_PLAIN     = c4bit(25), ///< mark key scalar as plain scalar (unquoted, even when multiline)
    186     _WIP_VAL_PLAIN     = c4bit(26), ///< mark val scalar as plain scalar (unquoted, even when multiline)
    187     _WIP_KEY_STYLE     = _WIP_KEY_LITERAL|_WIP_KEY_FOLDED|_WIP_KEY_SQUO|_WIP_KEY_DQUO|_WIP_KEY_PLAIN,
    188     _WIP_VAL_STYLE     = _WIP_VAL_LITERAL|_WIP_VAL_FOLDED|_WIP_VAL_SQUO|_WIP_VAL_DQUO|_WIP_VAL_PLAIN,
    189     _WIP_KEY_FT_NL     = c4bit(27), ///< features: mark key scalar as having \n in its contents
    190     _WIP_VAL_FT_NL     = c4bit(28), ///< features: mark val scalar as having \n in its contents
    191     _WIP_KEY_FT_SQ     = c4bit(29), ///< features: mark key scalar as having single quotes in its contents
    192     _WIP_VAL_FT_SQ     = c4bit(30), ///< features: mark val scalar as having single quotes in its contents
    193     _WIP_KEY_FT_DQ     = c4bit(31), ///< features: mark key scalar as having double quotes in its contents
    194     _WIP_VAL_FT_DQ     = c4bit(32), ///< features: mark val scalar as having double quotes in its contents
    195     #undef c4bit
    196 } NodeType_e;
    197 
    198 
    199 //-----------------------------------------------------------------------------
    200 //-----------------------------------------------------------------------------
    201 //-----------------------------------------------------------------------------
    202 
    203 /** wraps a NodeType_e element with some syntactic sugar and predicates */
    204 struct NodeType
    205 {
    206 public:
    207 
    208     NodeType_e type;
    209 
    210 public:
    211 
    212     C4_ALWAYS_INLINE NodeType() : type(NOTYPE) {}
    213     C4_ALWAYS_INLINE NodeType(NodeType_e t) : type(t) {}
    214     C4_ALWAYS_INLINE NodeType(type_bits t) : type((NodeType_e)t) {}
    215 
    216     C4_ALWAYS_INLINE const char *type_str() const { return type_str(type); }
    217     static const char* type_str(NodeType_e t);
    218 
    219     C4_ALWAYS_INLINE void set(NodeType_e t) { type = t; }
    220     C4_ALWAYS_INLINE void set(type_bits  t) { type = (NodeType_e)t; }
    221 
    222     C4_ALWAYS_INLINE void add(NodeType_e t) { type = (NodeType_e)(type|t); }
    223     C4_ALWAYS_INLINE void add(type_bits  t) { type = (NodeType_e)(type|t); }
    224 
    225     C4_ALWAYS_INLINE void rem(NodeType_e t) { type = (NodeType_e)(type & ~t); }
    226     C4_ALWAYS_INLINE void rem(type_bits  t) { type = (NodeType_e)(type & ~t); }
    227 
    228     C4_ALWAYS_INLINE void clear() { type = NOTYPE; }
    229 
    230 public:
    231 
    232     C4_ALWAYS_INLINE operator NodeType_e      & C4_RESTRICT ()       { return type; }
    233     C4_ALWAYS_INLINE operator NodeType_e const& C4_RESTRICT () const { return type; }
    234 
    235     C4_ALWAYS_INLINE bool operator== (NodeType_e t) const { return type == t; }
    236     C4_ALWAYS_INLINE bool operator!= (NodeType_e t) const { return type != t; }
    237 
    238 public:
    239 
    240     #if defined(__clang__)
    241     #   pragma clang diagnostic push
    242     #   pragma clang diagnostic ignored "-Wnull-dereference"
    243     #elif defined(__GNUC__)
    244     #   pragma GCC diagnostic push
    245     #   if __GNUC__ >= 6
    246     #       pragma GCC diagnostic ignored "-Wnull-dereference"
    247     #   endif
    248     #endif
    249 
    250     C4_ALWAYS_INLINE bool is_notype() const { return type == NOTYPE; }
    251     C4_ALWAYS_INLINE bool is_stream() const { return ((type & STREAM) == STREAM) != 0; }
    252     C4_ALWAYS_INLINE bool is_doc() const { return (type & DOC) != 0; }
    253     C4_ALWAYS_INLINE bool is_container() const { return (type & (MAP|SEQ|STREAM)) != 0; }
    254     C4_ALWAYS_INLINE bool is_map() const { return (type & MAP) != 0; }
    255     C4_ALWAYS_INLINE bool is_seq() const { return (type & SEQ) != 0; }
    256     C4_ALWAYS_INLINE bool has_key() const { return (type & KEY) != 0; }
    257     C4_ALWAYS_INLINE bool has_val() const { return (type & VAL) != 0; }
    258     C4_ALWAYS_INLINE bool is_val() const { return (type & KEYVAL) == VAL; }
    259     C4_ALWAYS_INLINE bool is_keyval() const { return (type & KEYVAL) == KEYVAL; }
    260     C4_ALWAYS_INLINE bool has_key_tag() const { return (type & (KEY|KEYTAG)) == (KEY|KEYTAG); }
    261     C4_ALWAYS_INLINE bool has_val_tag() const { return ((type & VALTAG) && (type & (VAL|MAP|SEQ))); }
    262     C4_ALWAYS_INLINE bool has_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); }
    263     C4_ALWAYS_INLINE bool is_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); }
    264     C4_ALWAYS_INLINE bool has_val_anchor() const { return (type & VALANCH) != 0 && (type & (VAL|SEQ|MAP)) != 0; }
    265     C4_ALWAYS_INLINE bool is_val_anchor() const { return (type & VALANCH) != 0 && (type & (VAL|SEQ|MAP)) != 0; }
    266     C4_ALWAYS_INLINE bool has_anchor() const { return (type & (KEYANCH|VALANCH)) != 0; }
    267     C4_ALWAYS_INLINE bool is_anchor() const { return (type & (KEYANCH|VALANCH)) != 0; }
    268     C4_ALWAYS_INLINE bool is_key_ref() const { return (type & KEYREF) != 0; }
    269     C4_ALWAYS_INLINE bool is_val_ref() const { return (type & VALREF) != 0; }
    270     C4_ALWAYS_INLINE bool is_ref() const { return (type & (KEYREF|VALREF)) != 0; }
    271     C4_ALWAYS_INLINE bool is_anchor_or_ref() const { return (type & (KEYANCH|VALANCH|KEYREF|VALREF)) != 0; }
    272     C4_ALWAYS_INLINE bool is_key_quoted() const { return (type & (KEY|KEYQUO)) == (KEY|KEYQUO); }
    273     C4_ALWAYS_INLINE bool is_val_quoted() const { return (type & (VAL|VALQUO)) == (VAL|VALQUO); }
    274     C4_ALWAYS_INLINE bool is_quoted() const { return (type & (KEY|KEYQUO)) == (KEY|KEYQUO) || (type & (VAL|VALQUO)) == (VAL|VALQUO); }
    275 
    276     // these predicates are a work in progress and subject to change. Don't use yet.
    277     C4_ALWAYS_INLINE bool default_block() const { return (type & (_WIP_STYLE_BLOCK|_WIP_STYLE_FLOW_ML|_WIP_STYLE_FLOW_SL)) == 0; }
    278     C4_ALWAYS_INLINE bool marked_block() const { return (type & (_WIP_STYLE_BLOCK)) != 0; }
    279     C4_ALWAYS_INLINE bool marked_flow_sl() const { return (type & (_WIP_STYLE_FLOW_SL)) != 0; }
    280     C4_ALWAYS_INLINE bool marked_flow_ml() const { return (type & (_WIP_STYLE_FLOW_ML)) != 0; }
    281     C4_ALWAYS_INLINE bool marked_flow() const { return (type & (_WIP_STYLE_FLOW_ML|_WIP_STYLE_FLOW_SL)) != 0; }
    282     C4_ALWAYS_INLINE bool key_marked_literal() const { return (type & (_WIP_KEY_LITERAL)) != 0; }
    283     C4_ALWAYS_INLINE bool val_marked_literal() const { return (type & (_WIP_VAL_LITERAL)) != 0; }
    284     C4_ALWAYS_INLINE bool key_marked_folded() const { return (type & (_WIP_KEY_FOLDED)) != 0; }
    285     C4_ALWAYS_INLINE bool val_marked_folded() const { return (type & (_WIP_VAL_FOLDED)) != 0; }
    286     C4_ALWAYS_INLINE bool key_marked_squo() const { return (type & (_WIP_KEY_SQUO)) != 0; }
    287     C4_ALWAYS_INLINE bool val_marked_squo() const { return (type & (_WIP_VAL_SQUO)) != 0; }
    288     C4_ALWAYS_INLINE bool key_marked_dquo() const { return (type & (_WIP_KEY_DQUO)) != 0; }
    289     C4_ALWAYS_INLINE bool val_marked_dquo() const { return (type & (_WIP_VAL_DQUO)) != 0; }
    290     C4_ALWAYS_INLINE bool key_marked_plain() const { return (type & (_WIP_KEY_PLAIN)) != 0; }
    291     C4_ALWAYS_INLINE bool val_marked_plain() const { return (type & (_WIP_VAL_PLAIN)) != 0; }
    292 
    293     #if defined(__clang__)
    294     #   pragma clang diagnostic pop
    295     #elif defined(__GNUC__)
    296     #   pragma GCC diagnostic pop
    297     #endif
    298 
    299 };
    300 
    301 
    302 //-----------------------------------------------------------------------------
    303 //-----------------------------------------------------------------------------
    304 //-----------------------------------------------------------------------------
    305 
    306 /** a node scalar is a csubstr, which may be tagged and anchored. */
    307 struct NodeScalar
    308 {
    309     csubstr tag;
    310     csubstr scalar;
    311     csubstr anchor;
    312 
    313 public:
    314 
    315     /// initialize as an empty scalar
    316     inline NodeScalar() noexcept : tag(), scalar(), anchor() {}
    317 
    318     /// initialize as an untagged scalar
    319     template<size_t N>
    320     inline NodeScalar(const char (&s)[N]) noexcept : tag(), scalar(s), anchor() {}
    321     inline NodeScalar(csubstr      s    ) noexcept : tag(), scalar(s), anchor() {}
    322 
    323     /// initialize as a tagged scalar
    324     template<size_t N, size_t M>
    325     inline NodeScalar(const char (&t)[N], const char (&s)[N]) noexcept : tag(t), scalar(s), anchor() {}
    326     inline NodeScalar(csubstr      t    , csubstr      s    ) noexcept : tag(t), scalar(s), anchor() {}
    327 
    328 public:
    329 
    330     ~NodeScalar() noexcept = default;
    331     NodeScalar(NodeScalar &&) noexcept = default;
    332     NodeScalar(NodeScalar const&) noexcept = default;
    333     NodeScalar& operator= (NodeScalar &&) noexcept = default;
    334     NodeScalar& operator= (NodeScalar const&) noexcept = default;
    335 
    336 public:
    337 
    338     bool empty() const noexcept { return tag.empty() && scalar.empty() && anchor.empty(); }
    339 
    340     void clear() noexcept { tag.clear(); scalar.clear(); anchor.clear(); }
    341 
    342     void set_ref_maybe_replacing_scalar(csubstr ref, bool has_scalar) noexcept
    343     {
    344         csubstr trimmed = ref.begins_with('*') ? ref.sub(1) : ref;
    345         anchor = trimmed;
    346         if((!has_scalar) || !scalar.ends_with(trimmed))
    347             scalar = ref;
    348     }
    349 };
    350 C4_MUST_BE_TRIVIAL_COPY(NodeScalar);
    351 
    352 
    353 //-----------------------------------------------------------------------------
    354 //-----------------------------------------------------------------------------
    355 //-----------------------------------------------------------------------------
    356 
    357 /** convenience class to initialize nodes */
    358 struct NodeInit
    359 {
    360 
    361     NodeType   type;
    362     NodeScalar key;
    363     NodeScalar val;
    364 
    365 public:
    366 
    367     /// initialize as an empty node
    368     NodeInit() : type(NOTYPE), key(), val() {}
    369     /// initialize as a typed node
    370     NodeInit(NodeType_e t) : type(t), key(), val() {}
    371     /// initialize as a sequence member
    372     NodeInit(NodeScalar const& v) : type(VAL), key(), val(v) { _add_flags(); }
    373     /// initialize as a mapping member
    374     NodeInit(              NodeScalar const& k, NodeScalar const& v) : type(KEYVAL), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); }
    375     /// initialize as a mapping member with explicit type
    376     NodeInit(NodeType_e t, NodeScalar const& k, NodeScalar const& v) : type(t     ), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); }
    377     /// initialize as a mapping member with explicit type (eg SEQ or MAP)
    378     NodeInit(NodeType_e t, NodeScalar const& k                     ) : type(t     ), key(k.tag, k.scalar), val(               ) { _add_flags(KEY); }
    379 
    380 public:
    381 
    382     void clear()
    383     {
    384         type.clear();
    385         key.clear();
    386         val.clear();
    387     }
    388 
    389     void _add_flags(type_bits more_flags=0)
    390     {
    391         type = (type|more_flags);
    392         if( ! key.tag.empty())
    393             type = (type|KEYTAG);
    394         if( ! val.tag.empty())
    395             type = (type|VALTAG);
    396         if( ! key.anchor.empty())
    397             type = (type|KEYANCH);
    398         if( ! val.anchor.empty())
    399             type = (type|VALANCH);
    400     }
    401 
    402     bool _check() const
    403     {
    404         // key cannot be empty
    405         RYML_ASSERT(key.scalar.empty() == ((type & KEY) == 0));
    406         // key tag cannot be empty
    407         RYML_ASSERT(key.tag.empty() == ((type & KEYTAG) == 0));
    408         // val may be empty even though VAL is set. But when VAL is not set, val must be empty
    409         RYML_ASSERT(((type & VAL) != 0) || val.scalar.empty());
    410         // val tag cannot be empty
    411         RYML_ASSERT(val.tag.empty() == ((type & VALTAG) == 0));
    412         return true;
    413     }
    414 };
    415 
    416 
    417 //-----------------------------------------------------------------------------
    418 //-----------------------------------------------------------------------------
    419 //-----------------------------------------------------------------------------
    420 
    421 /** contains the data for each YAML node. */
    422 struct NodeData
    423 {
    424     NodeType   m_type;
    425 
    426     NodeScalar m_key;
    427     NodeScalar m_val;
    428 
    429     size_t     m_parent;
    430     size_t     m_first_child;
    431     size_t     m_last_child;
    432     size_t     m_next_sibling;
    433     size_t     m_prev_sibling;
    434 };
    435 C4_MUST_BE_TRIVIAL_COPY(NodeData);
    436 
    437 
    438 //-----------------------------------------------------------------------------
    439 //-----------------------------------------------------------------------------
    440 //-----------------------------------------------------------------------------
    441 
    442 class RYML_EXPORT Tree
    443 {
    444 public:
    445 
    446     /** @name construction and assignment */
    447     /** @{ */
    448 
    449     Tree() : Tree(get_callbacks()) {}
    450     Tree(Callbacks const& cb);
    451     Tree(size_t node_capacity, size_t arena_capacity=0) : Tree(node_capacity, arena_capacity, get_callbacks()) {}
    452     Tree(size_t node_capacity, size_t arena_capacity, Callbacks const& cb);
    453 
    454     ~Tree();
    455 
    456     Tree(Tree const& that) noexcept;
    457     Tree(Tree     && that) noexcept;
    458 
    459     Tree& operator= (Tree const& that) noexcept;
    460     Tree& operator= (Tree     && that) noexcept;
    461 
    462     /** @} */
    463 
    464 public:
    465 
    466     /** @name memory and sizing */
    467     /** @{ */
    468 
    469     void reserve(size_t node_capacity);
    470 
    471     /** clear the tree and zero every node
    472      * @note does NOT clear the arena
    473      * @see clear_arena() */
    474     void clear();
    475     inline void clear_arena() { m_arena_pos = 0; }
    476 
    477     inline bool   empty() const { return m_size == 0; }
    478 
    479     inline size_t size() const { return m_size; }
    480     inline size_t capacity() const { return m_cap; }
    481     inline size_t slack() const { RYML_ASSERT(m_cap >= m_size); return m_cap - m_size; }
    482 
    483     Callbacks const& callbacks() const { return m_callbacks; }
    484     void callbacks(Callbacks const& cb) { m_callbacks = cb; }
    485 
    486     /** @} */
    487 
    488 public:
    489 
    490     /** @name node getters */
    491     /** @{ */
    492 
    493     //! get the index of a node belonging to this tree.
    494     //! @p n can be nullptr, in which case a
    495     size_t id(NodeData const* n) const
    496     {
    497         if( ! n)
    498         {
    499             return NONE;
    500         }
    501         RYML_ASSERT(n >= m_buf && n < m_buf + m_cap);
    502         return static_cast<size_t>(n - m_buf);
    503     }
    504 
    505     //! get a pointer to a node's NodeData.
    506     //! i can be NONE, in which case a nullptr is returned
    507     inline NodeData *get(size_t i)
    508     {
    509         if(i == NONE)
    510             return nullptr;
    511         RYML_ASSERT(i >= 0 && i < m_cap);
    512         return m_buf + i;
    513     }
    514     //! get a pointer to a node's NodeData.
    515     //! i can be NONE, in which case a nullptr is returned.
    516     inline NodeData const *get(size_t i) const
    517     {
    518         if(i == NONE)
    519             return nullptr;
    520         RYML_ASSERT(i >= 0 && i < m_cap);
    521         return m_buf + i;
    522     }
    523 
    524     //! An if-less form of get() that demands a valid node index.
    525     //! This function is implementation only; use at your own risk.
    526     inline NodeData       * _p(size_t i)       { RYML_ASSERT(i != NONE && i >= 0 && i < m_cap); return m_buf + i; }
    527     //! An if-less form of get() that demands a valid node index.
    528     //! This function is implementation only; use at your own risk.
    529     inline NodeData const * _p(size_t i) const { RYML_ASSERT(i != NONE && i >= 0 && i < m_cap); return m_buf + i; }
    530 
    531     //! Get the id of the root node
    532     size_t root_id()       { if(m_cap == 0) { reserve(16); } RYML_ASSERT(m_cap > 0 && m_size > 0); return 0; }
    533     //! Get the id of the root node
    534     size_t root_id() const {                                 RYML_ASSERT(m_cap > 0 && m_size > 0); return 0; }
    535 
    536     //! Get a NodeRef of a node by id
    537     NodeRef      ref(size_t id);
    538     //! Get a NodeRef of a node by id
    539     ConstNodeRef ref(size_t id) const;
    540     //! Get a NodeRef of a node by id
    541     ConstNodeRef cref(size_t id);
    542     //! Get a NodeRef of a node by id
    543     ConstNodeRef cref(size_t id) const;
    544 
    545     //! Get the root as a NodeRef
    546     NodeRef      rootref();
    547     //! Get the root as a NodeRef
    548     ConstNodeRef rootref() const;
    549     //! Get the root as a NodeRef
    550     ConstNodeRef crootref();
    551     //! Get the root as a NodeRef
    552     ConstNodeRef crootref() const;
    553 
    554     //! find a root child by name, return it as a NodeRef
    555     //! @note requires the root to be a map.
    556     NodeRef      operator[] (csubstr key);
    557     //! find a root child by name, return it as a NodeRef
    558     //! @note requires the root to be a map.
    559     ConstNodeRef operator[] (csubstr key) const;
    560 
    561     //! find a root child by index: return the root node's @p i-th child as a NodeRef
    562     //! @note @i is NOT the node id, but the child's position
    563     NodeRef      operator[] (size_t i);
    564     //! find a root child by index: return the root node's @p i-th child as a NodeRef
    565     //! @note @i is NOT the node id, but the child's position
    566     ConstNodeRef operator[] (size_t i) const;
    567 
    568     //! get the i-th document of the stream
    569     //! @note @i is NOT the node id, but the doc position within the stream
    570     NodeRef      docref(size_t i);
    571     //! get the i-th document of the stream
    572     //! @note @i is NOT the node id, but the doc position within the stream
    573     ConstNodeRef docref(size_t i) const;
    574 
    575     /** @} */
    576 
    577 public:
    578 
    579     /** @name node property getters */
    580     /** @{ */
    581 
    582     NodeType type(size_t node) const { return _p(node)->m_type; }
    583     const char* type_str(size_t node) const { return NodeType::type_str(_p(node)->m_type); }
    584 
    585     csubstr    const& key       (size_t node) const { RYML_ASSERT(has_key(node)); return _p(node)->m_key.scalar; }
    586     csubstr    const& key_tag   (size_t node) const { RYML_ASSERT(has_key_tag(node)); return _p(node)->m_key.tag; }
    587     csubstr    const& key_ref   (size_t node) const { RYML_ASSERT(is_key_ref(node) && ! has_key_anchor(node)); return _p(node)->m_key.anchor; }
    588     csubstr    const& key_anchor(size_t node) const { RYML_ASSERT( ! is_key_ref(node) && has_key_anchor(node)); return _p(node)->m_key.anchor; }
    589     NodeScalar const& keysc     (size_t node) const { RYML_ASSERT(has_key(node)); return _p(node)->m_key; }
    590 
    591     csubstr    const& val       (size_t node) const { RYML_ASSERT(has_val(node)); return _p(node)->m_val.scalar; }
    592     csubstr    const& val_tag   (size_t node) const { RYML_ASSERT(has_val_tag(node)); return _p(node)->m_val.tag; }
    593     csubstr    const& val_ref   (size_t node) const { RYML_ASSERT(is_val_ref(node) && ! has_val_anchor(node)); return _p(node)->m_val.anchor; }
    594     csubstr    const& val_anchor(size_t node) const { RYML_ASSERT( ! is_val_ref(node) && has_val_anchor(node)); return _p(node)->m_val.anchor; }
    595     NodeScalar const& valsc     (size_t node) const { RYML_ASSERT(has_val(node)); return _p(node)->m_val; }
    596 
    597     /** @} */
    598 
    599 public:
    600 
    601     /** @name node predicates */
    602     /** @{ */
    603 
    604     C4_ALWAYS_INLINE bool is_stream(size_t node) const { return _p(node)->m_type.is_stream(); }
    605     C4_ALWAYS_INLINE bool is_doc(size_t node) const { return _p(node)->m_type.is_doc(); }
    606     C4_ALWAYS_INLINE bool is_container(size_t node) const { return _p(node)->m_type.is_container(); }
    607     C4_ALWAYS_INLINE bool is_map(size_t node) const { return _p(node)->m_type.is_map(); }
    608     C4_ALWAYS_INLINE bool is_seq(size_t node) const { return _p(node)->m_type.is_seq(); }
    609     C4_ALWAYS_INLINE bool has_key(size_t node) const { return _p(node)->m_type.has_key(); }
    610     C4_ALWAYS_INLINE bool has_val(size_t node) const { return _p(node)->m_type.has_val(); }
    611     C4_ALWAYS_INLINE bool is_val(size_t node) const { return _p(node)->m_type.is_val(); }
    612     C4_ALWAYS_INLINE bool is_keyval(size_t node) const { return _p(node)->m_type.is_keyval(); }
    613     C4_ALWAYS_INLINE bool has_key_tag(size_t node) const { return _p(node)->m_type.has_key_tag(); }
    614     C4_ALWAYS_INLINE bool has_val_tag(size_t node) const { return _p(node)->m_type.has_val_tag(); }
    615     C4_ALWAYS_INLINE bool has_key_anchor(size_t node) const { return _p(node)->m_type.has_key_anchor(); }
    616     C4_ALWAYS_INLINE bool is_key_anchor(size_t node) const { return _p(node)->m_type.is_key_anchor(); }
    617     C4_ALWAYS_INLINE bool has_val_anchor(size_t node) const { return _p(node)->m_type.has_val_anchor(); }
    618     C4_ALWAYS_INLINE bool is_val_anchor(size_t node) const { return _p(node)->m_type.is_val_anchor(); }
    619     C4_ALWAYS_INLINE bool has_anchor(size_t node) const { return _p(node)->m_type.has_anchor(); }
    620     C4_ALWAYS_INLINE bool is_anchor(size_t node) const { return _p(node)->m_type.is_anchor(); }
    621     C4_ALWAYS_INLINE bool is_key_ref(size_t node) const { return _p(node)->m_type.is_key_ref(); }
    622     C4_ALWAYS_INLINE bool is_val_ref(size_t node) const { return _p(node)->m_type.is_val_ref(); }
    623     C4_ALWAYS_INLINE bool is_ref(size_t node) const { return _p(node)->m_type.is_ref(); }
    624     C4_ALWAYS_INLINE bool is_anchor_or_ref(size_t node) const { return _p(node)->m_type.is_anchor_or_ref(); }
    625     C4_ALWAYS_INLINE bool is_key_quoted(size_t node) const { return _p(node)->m_type.is_key_quoted(); }
    626     C4_ALWAYS_INLINE bool is_val_quoted(size_t node) const { return _p(node)->m_type.is_val_quoted(); }
    627     C4_ALWAYS_INLINE bool is_quoted(size_t node) const { return _p(node)->m_type.is_quoted(); }
    628 
    629     C4_ALWAYS_INLINE bool parent_is_seq(size_t node) const { RYML_ASSERT(has_parent(node)); return is_seq(_p(node)->m_parent); }
    630     C4_ALWAYS_INLINE bool parent_is_map(size_t node) const { RYML_ASSERT(has_parent(node)); return is_map(_p(node)->m_parent); }
    631 
    632     /** true when key and val are empty, and has no children */
    633     C4_ALWAYS_INLINE bool empty(size_t node) const { return ! has_children(node) && _p(node)->m_key.empty() && (( ! (_p(node)->m_type & VAL)) || _p(node)->m_val.empty()); }
    634     /** true when the node has an anchor named a */
    635     C4_ALWAYS_INLINE bool has_anchor(size_t node, csubstr a) const { return _p(node)->m_key.anchor == a || _p(node)->m_val.anchor == a; }
    636 
    637     C4_ALWAYS_INLINE bool key_is_null(size_t node) const { RYML_ASSERT(has_key(node)); NodeData const* C4_RESTRICT n = _p(node); return !n->m_type.is_key_quoted() && _is_null(n->m_key.scalar); }
    638     C4_ALWAYS_INLINE bool val_is_null(size_t node) const { RYML_ASSERT(has_val(node)); NodeData const* C4_RESTRICT n = _p(node); return !n->m_type.is_val_quoted() && _is_null(n->m_val.scalar); }
    639     static bool _is_null(csubstr s) noexcept
    640     {
    641         return s.str == nullptr ||
    642             s == "~" ||
    643             s == "null" ||
    644             s == "Null" ||
    645             s == "NULL";
    646     }
    647 
    648     /** @} */
    649 
    650 public:
    651 
    652     /** @name hierarchy predicates */
    653     /** @{ */
    654 
    655     bool is_root(size_t node) const { RYML_ASSERT(_p(node)->m_parent != NONE || node == 0); return _p(node)->m_parent == NONE; }
    656 
    657     bool has_parent(size_t node) const { return _p(node)->m_parent != NONE; }
    658 
    659     /** true if @p node has a child with id @p ch */
    660     bool has_child(size_t node, size_t ch) const { return _p(ch)->m_parent == node; }
    661     /** true if @p node has a child with key @p key */
    662     bool has_child(size_t node, csubstr key) const { return find_child(node, key) != npos; }
    663     /** true if @p node has any children key */
    664     bool has_children(size_t node) const { return _p(node)->m_first_child != NONE; }
    665 
    666     /** true if @p node has a sibling with id @p sib */
    667     bool has_sibling(size_t node, size_t sib) const { return _p(node)->m_parent == _p(sib)->m_parent; }
    668     /** true if one of the node's siblings has the given key */
    669     bool has_sibling(size_t node, csubstr key) const { return find_sibling(node, key) != npos; }
    670     /** true if node is not a single child */
    671     bool has_other_siblings(size_t node) const
    672     {
    673         NodeData const *n = _p(node);
    674         if(C4_LIKELY(n->m_parent != NONE))
    675         {
    676             n = _p(n->m_parent);
    677             return n->m_first_child != n->m_last_child;
    678         }
    679         return false;
    680     }
    681 
    682     RYML_DEPRECATED("use has_other_siblings()") bool has_siblings(size_t /*node*/) const { return true; }
    683 
    684     /** @} */
    685 
    686 public:
    687 
    688     /** @name hierarchy getters */
    689     /** @{ */
    690 
    691     size_t parent(size_t node) const { return _p(node)->m_parent; }
    692 
    693     size_t prev_sibling(size_t node) const { return _p(node)->m_prev_sibling; }
    694     size_t next_sibling(size_t node) const { return _p(node)->m_next_sibling; }
    695 
    696     /** O(#num_children) */
    697     size_t num_children(size_t node) const;
    698     size_t child_pos(size_t node, size_t ch) const;
    699     size_t first_child(size_t node) const { return _p(node)->m_first_child; }
    700     size_t last_child(size_t node) const { return _p(node)->m_last_child; }
    701     size_t child(size_t node, size_t pos) const;
    702     size_t find_child(size_t node, csubstr const& key) const;
    703 
    704     /** O(#num_siblings) */
    705     /** counts with this */
    706     size_t num_siblings(size_t node) const { return is_root(node) ? 1 : num_children(_p(node)->m_parent); }
    707     /** does not count with this */
    708     size_t num_other_siblings(size_t node) const { size_t ns = num_siblings(node); RYML_ASSERT(ns > 0); return ns-1; }
    709     size_t sibling_pos(size_t node, size_t sib) const { RYML_ASSERT( ! is_root(node) || node == root_id()); return child_pos(_p(node)->m_parent, sib); }
    710     size_t first_sibling(size_t node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_first_child; }
    711     size_t last_sibling(size_t node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_last_child; }
    712     size_t sibling(size_t node, size_t pos) const { return child(_p(node)->m_parent, pos); }
    713     size_t find_sibling(size_t node, csubstr const& key) const { return find_child(_p(node)->m_parent, key); }
    714 
    715     size_t doc(size_t i) const { size_t rid = root_id(); RYML_ASSERT(is_stream(rid)); return child(rid, i); } //!< gets the @p i document node index. requires that the root node is a stream.
    716 
    717     /** @} */
    718 
    719 public:
    720 
    721     /** @name node modifiers */
    722     /** @{ */
    723 
    724     void to_keyval(size_t node, csubstr key, csubstr val, type_bits more_flags=0);
    725     void to_map(size_t node, csubstr key, type_bits more_flags=0);
    726     void to_seq(size_t node, csubstr key, type_bits more_flags=0);
    727     void to_val(size_t node, csubstr val, type_bits more_flags=0);
    728     void to_map(size_t node, type_bits more_flags=0);
    729     void to_seq(size_t node, type_bits more_flags=0);
    730     void to_doc(size_t node, type_bits more_flags=0);
    731     void to_stream(size_t node, type_bits more_flags=0);
    732 
    733     void set_key(size_t node, csubstr key) { RYML_ASSERT(has_key(node)); _p(node)->m_key.scalar = key; }
    734     void set_val(size_t node, csubstr val) { RYML_ASSERT(has_val(node)); _p(node)->m_val.scalar = val; }
    735 
    736     void set_key_tag(size_t node, csubstr tag) { RYML_ASSERT(has_key(node)); _p(node)->m_key.tag = tag; _add_flags(node, KEYTAG); }
    737     void set_val_tag(size_t node, csubstr tag) { RYML_ASSERT(has_val(node) || is_container(node)); _p(node)->m_val.tag = tag; _add_flags(node, VALTAG); }
    738 
    739     void set_key_anchor(size_t node, csubstr anchor) { RYML_ASSERT( ! is_key_ref(node)); _p(node)->m_key.anchor = anchor.triml('&'); _add_flags(node, KEYANCH); }
    740     void set_val_anchor(size_t node, csubstr anchor) { RYML_ASSERT( ! is_val_ref(node)); _p(node)->m_val.anchor = anchor.triml('&'); _add_flags(node, VALANCH); }
    741     void set_key_ref   (size_t node, csubstr ref   ) { RYML_ASSERT( ! has_key_anchor(node)); NodeData* C4_RESTRICT n = _p(node); n->m_key.set_ref_maybe_replacing_scalar(ref, n->m_type.has_key()); _add_flags(node, KEY|KEYREF); }
    742     void set_val_ref   (size_t node, csubstr ref   ) { RYML_ASSERT( ! has_val_anchor(node)); NodeData* C4_RESTRICT n = _p(node); n->m_val.set_ref_maybe_replacing_scalar(ref, n->m_type.has_val()); _add_flags(node, VAL|VALREF); }
    743 
    744     void rem_key_anchor(size_t node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYANCH); }
    745     void rem_val_anchor(size_t node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALANCH); }
    746     void rem_key_ref   (size_t node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYREF); }
    747     void rem_val_ref   (size_t node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALREF); }
    748     void rem_anchor_ref(size_t node) { _p(node)->m_key.anchor.clear(); _p(node)->m_val.anchor.clear(); _rem_flags(node, KEYANCH|VALANCH|KEYREF|VALREF); }
    749 
    750     /** @} */
    751 
    752 public:
    753 
    754     /** @name tree modifiers */
    755     /** @{ */
    756 
    757     /** reorder the tree in memory so that all the nodes are stored
    758      * in a linear sequence when visited in depth-first order.
    759      * This will invalidate existing ids, since the node id is its
    760      * position in the node array. */
    761     void reorder();
    762 
    763     /** Resolve references (aliases <- anchors) in the tree.
    764      *
    765      * Dereferencing is opt-in; after parsing, Tree::resolve()
    766      * has to be called explicitly for obtaining resolved references in the
    767      * tree. This method will resolve all references and substitute the
    768      * anchored values in place of the reference.
    769      *
    770      * This method first does a full traversal of the tree to gather all
    771      * anchors and references in a separate collection, then it goes through
    772      * that collection to locate the names, which it does by obeying the YAML
    773      * standard diktat that "an alias node refers to the most recent node in
    774      * the serialization having the specified anchor"
    775      *
    776      * So, depending on the number of anchor/alias nodes, this is a
    777      * potentially expensive operation, with a best-case linear complexity
    778      * (from the initial traversal). This potential cost is the reason for
    779      * requiring an explicit call.
    780      */
    781     void resolve();
    782 
    783     /** @} */
    784 
    785 public:
    786 
    787     /** @name tag directives */
    788     /** @{ */
    789 
    790     void resolve_tags();
    791 
    792     size_t num_tag_directives() const;
    793     size_t add_tag_directive(TagDirective const& td);
    794     void clear_tag_directives();
    795 
    796     size_t resolve_tag(substr output, csubstr tag, size_t node_id) const;
    797     csubstr resolve_tag_sub(substr output, csubstr tag, size_t node_id) const
    798     {
    799         size_t needed = resolve_tag(output, tag, node_id);
    800         return needed <= output.len ? output.first(needed) : output;
    801     }
    802 
    803     using tag_directive_const_iterator = TagDirective const*;
    804     tag_directive_const_iterator begin_tag_directives() const { return m_tag_directives; }
    805     tag_directive_const_iterator end_tag_directives() const { return m_tag_directives + num_tag_directives(); }
    806 
    807     struct TagDirectiveProxy
    808     {
    809         tag_directive_const_iterator b, e;
    810         tag_directive_const_iterator begin() const { return b; }
    811         tag_directive_const_iterator end() const { return e; }
    812     };
    813 
    814     TagDirectiveProxy tag_directives() const { return TagDirectiveProxy{begin_tag_directives(), end_tag_directives()}; }
    815 
    816     /** @} */
    817 
    818 public:
    819 
    820     /** @name modifying hierarchy */
    821     /** @{ */
    822 
    823     /** create and insert a new child of @p parent. insert after the (to-be)
    824      * sibling @p after, which must be a child of @p parent. To insert as the
    825      * first child, set after to NONE */
    826     C4_ALWAYS_INLINE size_t insert_child(size_t parent, size_t after)
    827     {
    828         RYML_ASSERT(parent != NONE);
    829         RYML_ASSERT(is_container(parent) || is_root(parent));
    830         RYML_ASSERT(after == NONE || (_p(after)->m_parent == parent));
    831         size_t child = _claim();
    832         _set_hierarchy(child, parent, after);
    833         return child;
    834     }
    835     /** create and insert a node as the first child of @p parent */
    836     C4_ALWAYS_INLINE size_t prepend_child(size_t parent) { return insert_child(parent, NONE); }
    837     /** create and insert a node as the last child of @p parent */
    838     C4_ALWAYS_INLINE size_t  append_child(size_t parent) { return insert_child(parent, _p(parent)->m_last_child); }
    839 
    840 public:
    841 
    842     #if defined(__clang__)
    843     #   pragma clang diagnostic push
    844     #   pragma clang diagnostic ignored "-Wnull-dereference"
    845     #elif defined(__GNUC__)
    846     #   pragma GCC diagnostic push
    847     #   if __GNUC__ >= 6
    848     #       pragma GCC diagnostic ignored "-Wnull-dereference"
    849     #   endif
    850     #endif
    851 
    852     //! create and insert a new sibling of n. insert after "after"
    853     C4_ALWAYS_INLINE size_t insert_sibling(size_t node, size_t after)
    854     {
    855         return insert_child(_p(node)->m_parent, after);
    856     }
    857     /** create and insert a node as the first node of @p parent */
    858     C4_ALWAYS_INLINE size_t prepend_sibling(size_t node) { return prepend_child(_p(node)->m_parent); }
    859     C4_ALWAYS_INLINE size_t  append_sibling(size_t node) { return append_child(_p(node)->m_parent); }
    860 
    861 public:
    862 
    863     /** remove an entire branch at once: ie remove the children and the node itself */
    864     inline void remove(size_t node)
    865     {
    866         remove_children(node);
    867         _release(node);
    868     }
    869 
    870     /** remove all the node's children, but keep the node itself */
    871     void remove_children(size_t node);
    872 
    873     /** change the @p type of the node to one of MAP, SEQ or VAL.  @p
    874      * type must have one and only one of MAP,SEQ,VAL; @p type may
    875      * possibly have KEY, but if it does, then the @p node must also
    876      * have KEY. Changing to the same type is a no-op. Otherwise,
    877      * changing to a different type will initialize the node with an
    878      * empty value of the desired type: changing to VAL will
    879      * initialize with a null scalar (~), changing to MAP will
    880      * initialize with an empty map ({}), and changing to SEQ will
    881      * initialize with an empty seq ([]). */
    882     bool change_type(size_t node, NodeType type);
    883 
    884     bool change_type(size_t node, type_bits type)
    885     {
    886         return change_type(node, (NodeType)type);
    887     }
    888 
    889     #if defined(__clang__)
    890     #   pragma clang diagnostic pop
    891     #elif defined(__GNUC__)
    892     #   pragma GCC diagnostic pop
    893     #endif
    894 
    895 public:
    896 
    897     /** change the node's position in the parent */
    898     void move(size_t node, size_t after);
    899 
    900     /** change the node's parent and position */
    901     void move(size_t node, size_t new_parent, size_t after);
    902 
    903     /** change the node's parent and position to a different tree
    904      * @return the index of the new node in the destination tree */
    905     size_t move(Tree * src, size_t node, size_t new_parent, size_t after);
    906 
    907     /** ensure the first node is a stream. Eg, change this tree
    908      *
    909      *  DOCMAP
    910      *    MAP
    911      *      KEYVAL
    912      *      KEYVAL
    913      *    SEQ
    914      *      VAL
    915      *
    916      * to
    917      *
    918      *  STREAM
    919      *    DOCMAP
    920      *      MAP
    921      *        KEYVAL
    922      *        KEYVAL
    923      *      SEQ
    924      *        VAL
    925      *
    926      * If the root is already a stream, this is a no-op.
    927      */
    928     void set_root_as_stream();
    929 
    930 public:
    931 
    932     /** recursively duplicate a node from this tree into a new parent,
    933      * placing it after one of its children
    934      * @return the index of the copy */
    935     size_t duplicate(size_t node, size_t new_parent, size_t after);
    936     /** recursively duplicate a node from a different tree into a new parent,
    937      * placing it after one of its children
    938      * @return the index of the copy */
    939     size_t duplicate(Tree const* src, size_t node, size_t new_parent, size_t after);
    940 
    941     /** recursively duplicate the node's children (but not the node)
    942      * @return the index of the last duplicated child */
    943     size_t duplicate_children(size_t node, size_t parent, size_t after);
    944     /** recursively duplicate the node's children (but not the node), where
    945      * the node is from a different tree
    946      * @return the index of the last duplicated child */
    947     size_t duplicate_children(Tree const* src, size_t node, size_t parent, size_t after);
    948 
    949     void duplicate_contents(size_t node, size_t where);
    950     void duplicate_contents(Tree const* src, size_t node, size_t where);
    951 
    952     /** duplicate the node's children (but not the node) in a new parent, but
    953      * omit repetitions where a duplicated node has the same key (in maps) or
    954      * value (in seqs). If one of the duplicated children has the same key
    955      * (in maps) or value (in seqs) as one of the parent's children, the one
    956      * that is placed closest to the end will prevail. */
    957     size_t duplicate_children_no_rep(size_t node, size_t parent, size_t after);
    958     size_t duplicate_children_no_rep(Tree const* src, size_t node, size_t parent, size_t after);
    959 
    960 public:
    961 
    962     void merge_with(Tree const* src, size_t src_node=NONE, size_t dst_root=NONE);
    963 
    964     /** @} */
    965 
    966 public:
    967 
    968     /** @name internal string arena */
    969     /** @{ */
    970 
    971     /** get the current size of the tree's internal arena */
    972     RYML_DEPRECATED("use arena_size() instead") size_t arena_pos() const { return m_arena_pos; }
    973     /** get the current size of the tree's internal arena */
    974     inline size_t arena_size() const { return m_arena_pos; }
    975     /** get the current capacity of the tree's internal arena */
    976     inline size_t arena_capacity() const { return m_arena.len; }
    977     /** get the current slack of the tree's internal arena */
    978     inline size_t arena_slack() const { RYML_ASSERT(m_arena.len >= m_arena_pos); return m_arena.len - m_arena_pos; }
    979 
    980     /** get the current arena */
    981     substr arena() const { return m_arena.first(m_arena_pos); }
    982 
    983     /** return true if the given substring is part of the tree's string arena */
    984     bool in_arena(csubstr s) const
    985     {
    986         return m_arena.is_super(s);
    987     }
    988 
    989     /** serialize the given floating-point variable to the tree's
    990      * arena, growing it as needed to accomodate the serialization.
    991      *
    992      * @note Growing the arena may cause relocation of the entire
    993      * existing arena, and thus change the contents of individual
    994      * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
    995      * cost, ensure that the arena is reserved to an appropriate size
    996      * using .reserve_arena()
    997      *
    998      * @see alloc_arena() */
    999     template<class T>
   1000     typename std::enable_if<std::is_floating_point<T>::value, csubstr>::type
   1001     to_arena(T const& C4_RESTRICT a)
   1002     {
   1003         substr rem(m_arena.sub(m_arena_pos));
   1004         size_t num = to_chars_float(rem, a);
   1005         if(num > rem.len)
   1006         {
   1007             rem = _grow_arena(num);
   1008             num = to_chars_float(rem, a);
   1009             RYML_ASSERT(num <= rem.len);
   1010         }
   1011         rem = _request_span(num);
   1012         return rem;
   1013     }
   1014 
   1015     /** serialize the given non-floating-point variable to the tree's
   1016      * arena, growing it as needed to accomodate the serialization.
   1017      *
   1018      * @note Growing the arena may cause relocation of the entire
   1019      * existing arena, and thus change the contents of individual
   1020      * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
   1021      * cost, ensure that the arena is reserved to an appropriate size
   1022      * using .reserve_arena()
   1023      *
   1024      * @see alloc_arena() */
   1025     template<class T>
   1026     typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type
   1027     to_arena(T const& C4_RESTRICT a)
   1028     {
   1029         substr rem(m_arena.sub(m_arena_pos));
   1030         size_t num = to_chars(rem, a);
   1031         if(num > rem.len)
   1032         {
   1033             rem = _grow_arena(num);
   1034             num = to_chars(rem, a);
   1035             RYML_ASSERT(num <= rem.len);
   1036         }
   1037         rem = _request_span(num);
   1038         return rem;
   1039     }
   1040 
   1041     /** serialize the given csubstr to the tree's arena, growing the
   1042      * arena as needed to accomodate the serialization.
   1043      *
   1044      * @note Growing the arena may cause relocation of the entire
   1045      * existing arena, and thus change the contents of individual
   1046      * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
   1047      * cost, ensure that the arena is reserved to an appropriate size
   1048      * using .reserve_arena()
   1049      *
   1050      * @see alloc_arena() */
   1051     csubstr to_arena(csubstr a)
   1052     {
   1053         if(a.len > 0)
   1054         {
   1055             substr rem(m_arena.sub(m_arena_pos));
   1056             size_t num = to_chars(rem, a);
   1057             if(num > rem.len)
   1058             {
   1059                 rem = _grow_arena(num);
   1060                 num = to_chars(rem, a);
   1061                 RYML_ASSERT(num <= rem.len);
   1062             }
   1063             return _request_span(num);
   1064         }
   1065         else
   1066         {
   1067             if(a.str == nullptr)
   1068             {
   1069                 return csubstr{};
   1070             }
   1071             else if(m_arena.str == nullptr)
   1072             {
   1073                 // Arena is empty and we want to store a non-null
   1074                 // zero-length string.
   1075                 // Even though the string has zero length, we need
   1076                 // some "memory" to store a non-nullptr string
   1077                 _grow_arena(1);
   1078             }
   1079             return _request_span(0);
   1080         }
   1081     }
   1082     C4_ALWAYS_INLINE csubstr to_arena(const char *s)
   1083     {
   1084         return to_arena(to_csubstr(s));
   1085     }
   1086     C4_ALWAYS_INLINE csubstr to_arena(std::nullptr_t)
   1087     {
   1088         return csubstr{};
   1089     }
   1090 
   1091     /** copy the given substr to the tree's arena, growing it by the
   1092      * required size
   1093      *
   1094      * @note Growing the arena may cause relocation of the entire
   1095      * existing arena, and thus change the contents of individual
   1096      * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
   1097      * cost, ensure that the arena is reserved to an appropriate size
   1098      * using .reserve_arena()
   1099      *
   1100      * @see alloc_arena() */
   1101     substr copy_to_arena(csubstr s)
   1102     {
   1103         substr cp = alloc_arena(s.len);
   1104         RYML_ASSERT(cp.len == s.len);
   1105         RYML_ASSERT(!s.overlaps(cp));
   1106         #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
   1107         C4_SUPPRESS_WARNING_GCC_PUSH
   1108         C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow=") // no need for terminating \0
   1109         C4_SUPPRESS_WARNING_GCC( "-Wrestrict") // there's an assert to ensure no violation of restrict behavior
   1110         #endif
   1111         if(s.len)
   1112             memcpy(cp.str, s.str, s.len);
   1113         #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
   1114         C4_SUPPRESS_WARNING_GCC_POP
   1115         #endif
   1116         return cp;
   1117     }
   1118 
   1119     /** grow the tree's string arena by the given size and return a substr
   1120      * of the added portion
   1121      *
   1122      * @note Growing the arena may cause relocation of the entire
   1123      * existing arena, and thus change the contents of individual
   1124      * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this
   1125      * cost, ensure that the arena is reserved to an appropriate size
   1126      * using .reserve_arena().
   1127      *
   1128      * @see reserve_arena() */
   1129     substr alloc_arena(size_t sz)
   1130     {
   1131         if(sz > arena_slack())
   1132             _grow_arena(sz - arena_slack());
   1133         substr s = _request_span(sz);
   1134         return s;
   1135     }
   1136 
   1137     /** ensure the tree's internal string arena is at least the given capacity
   1138      * @note This operation has a potential complexity of O(numNodes)+O(arenasize).
   1139      * Growing the arena may cause relocation of the entire
   1140      * existing arena, and thus change the contents of individual nodes. */
   1141     void reserve_arena(size_t arena_cap)
   1142     {
   1143         if(arena_cap > m_arena.len)
   1144         {
   1145             substr buf;
   1146             buf.str = (char*) m_callbacks.m_allocate(arena_cap, m_arena.str, m_callbacks.m_user_data);
   1147             buf.len = arena_cap;
   1148             if(m_arena.str)
   1149             {
   1150                 RYML_ASSERT(m_arena.len >= 0);
   1151                 _relocate(buf); // does a memcpy and changes nodes using the arena
   1152                 m_callbacks.m_free(m_arena.str, m_arena.len, m_callbacks.m_user_data);
   1153             }
   1154             m_arena = buf;
   1155         }
   1156     }
   1157 
   1158     /** @} */
   1159 
   1160 private:
   1161 
   1162     substr _grow_arena(size_t more)
   1163     {
   1164         size_t cap = m_arena.len + more;
   1165         cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap;
   1166         cap = cap < 64 ? 64 : cap;
   1167         reserve_arena(cap);
   1168         return m_arena.sub(m_arena_pos);
   1169     }
   1170 
   1171     substr _request_span(size_t sz)
   1172     {
   1173         substr s;
   1174         s = m_arena.sub(m_arena_pos, sz);
   1175         m_arena_pos += sz;
   1176         return s;
   1177     }
   1178 
   1179     substr _relocated(csubstr s, substr next_arena) const
   1180     {
   1181         RYML_ASSERT(m_arena.is_super(s));
   1182         RYML_ASSERT(m_arena.sub(0, m_arena_pos).is_super(s));
   1183         auto pos = (s.str - m_arena.str);
   1184         substr r(next_arena.str + pos, s.len);
   1185         RYML_ASSERT(r.str - next_arena.str == pos);
   1186         RYML_ASSERT(next_arena.sub(0, m_arena_pos).is_super(r));
   1187         return r;
   1188     }
   1189 
   1190 public:
   1191 
   1192     /** @name lookup */
   1193     /** @{ */
   1194 
   1195     struct lookup_result
   1196     {
   1197         size_t  target;
   1198         size_t  closest;
   1199         size_t  path_pos;
   1200         csubstr path;
   1201 
   1202         inline operator bool() const { return target != NONE; }
   1203 
   1204         lookup_result() : target(NONE), closest(NONE), path_pos(0), path() {}
   1205         lookup_result(csubstr path_, size_t start) : target(NONE), closest(start), path_pos(0), path(path_) {}
   1206 
   1207         /** get the part ot the input path that was resolved */
   1208         csubstr resolved() const;
   1209         /** get the part ot the input path that was unresolved */
   1210         csubstr unresolved() const;
   1211     };
   1212 
   1213     /** for example foo.bar[0].baz */
   1214     lookup_result lookup_path(csubstr path, size_t start=NONE) const;
   1215 
   1216     /** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
   1217      * the tree so that the corresponding lookup_path() would return the
   1218      * default value.
   1219      * @see lookup_path() */
   1220     size_t lookup_path_or_modify(csubstr default_value, csubstr path, size_t start=NONE);
   1221 
   1222     /** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
   1223      * the tree so that the corresponding lookup_path() would return the
   1224      * branch @p src_node (from the tree @p src).
   1225      * @see lookup_path() */
   1226     size_t lookup_path_or_modify(Tree const *src, size_t src_node, csubstr path, size_t start=NONE);
   1227 
   1228     /** @} */
   1229 
   1230 private:
   1231 
   1232     struct _lookup_path_token
   1233     {
   1234         csubstr value;
   1235         NodeType type;
   1236         _lookup_path_token() : value(), type() {}
   1237         _lookup_path_token(csubstr v, NodeType t) : value(v), type(t) {}
   1238         inline operator bool() const { return type != NOTYPE; }
   1239         bool is_index() const { return value.begins_with('[') && value.ends_with(']'); }
   1240     };
   1241 
   1242     size_t _lookup_path_or_create(csubstr path, size_t start);
   1243 
   1244     void   _lookup_path       (lookup_result *r) const;
   1245     void   _lookup_path_modify(lookup_result *r);
   1246 
   1247     size_t _next_node       (lookup_result *r, _lookup_path_token *parent) const;
   1248     size_t _next_node_modify(lookup_result *r, _lookup_path_token *parent);
   1249 
   1250     void   _advance(lookup_result *r, size_t more) const;
   1251 
   1252     _lookup_path_token _next_token(lookup_result *r, _lookup_path_token const& parent) const;
   1253 
   1254 private:
   1255 
   1256     void _clear();
   1257     void _free();
   1258     void _copy(Tree const& that);
   1259     void _move(Tree      & that);
   1260 
   1261     void _relocate(substr next_arena);
   1262 
   1263 public:
   1264 
   1265     #if ! RYML_USE_ASSERT
   1266     C4_ALWAYS_INLINE void _check_next_flags(size_t, type_bits) {}
   1267     #else
   1268     void _check_next_flags(size_t node, type_bits f)
   1269     {
   1270         auto n = _p(node);
   1271         type_bits o = n->m_type; // old
   1272         C4_UNUSED(o);
   1273         if(f & MAP)
   1274         {
   1275             RYML_ASSERT_MSG((f & SEQ) == 0, "cannot mark simultaneously as map and seq");
   1276             RYML_ASSERT_MSG((f & VAL) == 0, "cannot mark simultaneously as map and val");
   1277             RYML_ASSERT_MSG((o & SEQ) == 0, "cannot turn a seq into a map; clear first");
   1278             RYML_ASSERT_MSG((o & VAL) == 0, "cannot turn a val into a map; clear first");
   1279         }
   1280         else if(f & SEQ)
   1281         {
   1282             RYML_ASSERT_MSG((f & MAP) == 0, "cannot mark simultaneously as seq and map");
   1283             RYML_ASSERT_MSG((f & VAL) == 0, "cannot mark simultaneously as seq and val");
   1284             RYML_ASSERT_MSG((o & MAP) == 0, "cannot turn a map into a seq; clear first");
   1285             RYML_ASSERT_MSG((o & VAL) == 0, "cannot turn a val into a seq; clear first");
   1286         }
   1287         if(f & KEY)
   1288         {
   1289             RYML_ASSERT(!is_root(node));
   1290             auto pid = parent(node); C4_UNUSED(pid);
   1291             RYML_ASSERT(is_map(pid));
   1292         }
   1293         if((f & VAL) && !is_root(node))
   1294         {
   1295             auto pid = parent(node); C4_UNUSED(pid);
   1296             RYML_ASSERT(is_map(pid) || is_seq(pid));
   1297         }
   1298     }
   1299     #endif
   1300 
   1301     inline void _set_flags(size_t node, NodeType_e f) { _check_next_flags(node, f); _p(node)->m_type = f; }
   1302     inline void _set_flags(size_t node, type_bits  f) { _check_next_flags(node, f); _p(node)->m_type = f; }
   1303 
   1304     inline void _add_flags(size_t node, NodeType_e f) { NodeData *d = _p(node); type_bits fb = f |  d->m_type; _check_next_flags(node, fb); d->m_type = (NodeType_e) fb; }
   1305     inline void _add_flags(size_t node, type_bits  f) { NodeData *d = _p(node);                f |= d->m_type; _check_next_flags(node,  f); d->m_type = f; }
   1306 
   1307     inline void _rem_flags(size_t node, NodeType_e f) { NodeData *d = _p(node); type_bits fb = d->m_type & ~f; _check_next_flags(node, fb); d->m_type = (NodeType_e) fb; }
   1308     inline void _rem_flags(size_t node, type_bits  f) { NodeData *d = _p(node);            f = d->m_type & ~f; _check_next_flags(node,  f); d->m_type = f; }
   1309 
   1310     void _set_key(size_t node, csubstr key, type_bits more_flags=0)
   1311     {
   1312         _p(node)->m_key.scalar = key;
   1313         _add_flags(node, KEY|more_flags);
   1314     }
   1315     void _set_key(size_t node, NodeScalar const& key, type_bits more_flags=0)
   1316     {
   1317         _p(node)->m_key = key;
   1318         _add_flags(node, KEY|more_flags);
   1319     }
   1320 
   1321     void _set_val(size_t node, csubstr val, type_bits more_flags=0)
   1322     {
   1323         RYML_ASSERT(num_children(node) == 0);
   1324         RYML_ASSERT(!is_seq(node) && !is_map(node));
   1325         _p(node)->m_val.scalar = val;
   1326         _add_flags(node, VAL|more_flags);
   1327     }
   1328     void _set_val(size_t node, NodeScalar const& val, type_bits more_flags=0)
   1329     {
   1330         RYML_ASSERT(num_children(node) == 0);
   1331         RYML_ASSERT( ! is_container(node));
   1332         _p(node)->m_val = val;
   1333         _add_flags(node, VAL|more_flags);
   1334     }
   1335 
   1336     void _set(size_t node, NodeInit const& i)
   1337     {
   1338         RYML_ASSERT(i._check());
   1339         NodeData *n = _p(node);
   1340         RYML_ASSERT(n->m_key.scalar.empty() || i.key.scalar.empty() || i.key.scalar == n->m_key.scalar);
   1341         _add_flags(node, i.type);
   1342         if(n->m_key.scalar.empty())
   1343         {
   1344             if( ! i.key.scalar.empty())
   1345             {
   1346                 _set_key(node, i.key.scalar);
   1347             }
   1348         }
   1349         n->m_key.tag = i.key.tag;
   1350         n->m_val = i.val;
   1351     }
   1352 
   1353     void _set_parent_as_container_if_needed(size_t in)
   1354     {
   1355         NodeData const* n = _p(in);
   1356         size_t ip = parent(in);
   1357         if(ip != NONE)
   1358         {
   1359             if( ! (is_seq(ip) || is_map(ip)))
   1360             {
   1361                 if((in == first_child(ip)) && (in == last_child(ip)))
   1362                 {
   1363                     if( ! n->m_key.empty() || has_key(in))
   1364                     {
   1365                         _add_flags(ip, MAP);
   1366                     }
   1367                     else
   1368                     {
   1369                         _add_flags(ip, SEQ);
   1370                     }
   1371                 }
   1372             }
   1373         }
   1374     }
   1375 
   1376     void _seq2map(size_t node)
   1377     {
   1378         RYML_ASSERT(is_seq(node));
   1379         for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
   1380         {
   1381             NodeData *C4_RESTRICT ch = _p(i);
   1382             if(ch->m_type.is_keyval())
   1383                 continue;
   1384             ch->m_type.add(KEY);
   1385             ch->m_key = ch->m_val;
   1386         }
   1387         auto *C4_RESTRICT n = _p(node);
   1388         n->m_type.rem(SEQ);
   1389         n->m_type.add(MAP);
   1390     }
   1391 
   1392     size_t _do_reorder(size_t *node, size_t count);
   1393 
   1394     void _swap(size_t n_, size_t m_);
   1395     void _swap_props(size_t n_, size_t m_);
   1396     void _swap_hierarchy(size_t n_, size_t m_);
   1397     void _copy_hierarchy(size_t dst_, size_t src_);
   1398 
   1399     inline void _copy_props(size_t dst_, size_t src_)
   1400     {
   1401         _copy_props(dst_, this, src_);
   1402     }
   1403 
   1404     inline void _copy_props_wo_key(size_t dst_, size_t src_)
   1405     {
   1406         _copy_props_wo_key(dst_, this, src_);
   1407     }
   1408 
   1409     void _copy_props(size_t dst_, Tree const* that_tree, size_t src_)
   1410     {
   1411         auto      & C4_RESTRICT dst = *_p(dst_);
   1412         auto const& C4_RESTRICT src = *that_tree->_p(src_);
   1413         dst.m_type = src.m_type;
   1414         dst.m_key  = src.m_key;
   1415         dst.m_val  = src.m_val;
   1416     }
   1417 
   1418     void _copy_props_wo_key(size_t dst_, Tree const* that_tree, size_t src_)
   1419     {
   1420         auto      & C4_RESTRICT dst = *_p(dst_);
   1421         auto const& C4_RESTRICT src = *that_tree->_p(src_);
   1422         dst.m_type = (src.m_type & ~_KEYMASK) | (dst.m_type & _KEYMASK);
   1423         dst.m_val  = src.m_val;
   1424     }
   1425 
   1426     inline void _clear_type(size_t node)
   1427     {
   1428         _p(node)->m_type = NOTYPE;
   1429     }
   1430 
   1431     inline void _clear(size_t node)
   1432     {
   1433         auto *C4_RESTRICT n = _p(node);
   1434         n->m_type = NOTYPE;
   1435         n->m_key.clear();
   1436         n->m_val.clear();
   1437         n->m_parent = NONE;
   1438         n->m_first_child = NONE;
   1439         n->m_last_child = NONE;
   1440     }
   1441 
   1442     inline void _clear_key(size_t node)
   1443     {
   1444         _p(node)->m_key.clear();
   1445         _rem_flags(node, KEY);
   1446     }
   1447 
   1448     inline void _clear_val(size_t node)
   1449     {
   1450         _p(node)->m_val.clear();
   1451         _rem_flags(node, VAL);
   1452     }
   1453 
   1454 private:
   1455 
   1456     void _clear_range(size_t first, size_t num);
   1457 
   1458     size_t _claim();
   1459     void   _claim_root();
   1460     void   _release(size_t node);
   1461     void   _free_list_add(size_t node);
   1462     void   _free_list_rem(size_t node);
   1463 
   1464     void _set_hierarchy(size_t node, size_t parent, size_t after_sibling);
   1465     void _rem_hierarchy(size_t node);
   1466 
   1467 public:
   1468 
   1469     // members are exposed, but you should NOT access them directly
   1470 
   1471     NodeData * m_buf;
   1472     size_t m_cap;
   1473 
   1474     size_t m_size;
   1475 
   1476     size_t m_free_head;
   1477     size_t m_free_tail;
   1478 
   1479     substr m_arena;
   1480     size_t m_arena_pos;
   1481 
   1482     Callbacks m_callbacks;
   1483 
   1484     TagDirective m_tag_directives[RYML_MAX_TAG_DIRECTIVES];
   1485 
   1486 };
   1487 
   1488 } // namespace yml
   1489 } // namespace c4
   1490 
   1491 
   1492 C4_SUPPRESS_WARNING_MSVC_POP
   1493 C4_SUPPRESS_WARNING_GCC_CLANG_POP
   1494 
   1495 
   1496 #endif /* _C4_YML_TREE_HPP_ */