Path: blob/master/dep/rapidyaml/include/c4/yml/tree.hpp
4264 views
#ifndef _C4_YML_TREE_HPP_1#define _C4_YML_TREE_HPP_234#include "c4/error.hpp"5#include "c4/types.hpp"6#ifndef _C4_YML_COMMON_HPP_7#include "c4/yml/common.hpp"8#endif910#include <c4/charconv.hpp>11#include <cmath>12#include <limits>131415C4_SUPPRESS_WARNING_MSVC_PUSH16C4_SUPPRESS_WARNING_MSVC(4251) // needs to have dll-interface to be used by clients of struct17C4_SUPPRESS_WARNING_MSVC(4296) // expression is always 'boolean_value'18C4_SUPPRESS_WARNING_GCC_CLANG_PUSH19C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")20C4_SUPPRESS_WARNING_GCC("-Wtype-limits")212223namespace c4 {24namespace yml {2526struct NodeScalar;27struct NodeInit;28struct NodeData;29class NodeRef;30class ConstNodeRef;31class Tree;323334/** encode a floating point value to a string. */35template<class T>36size_t to_chars_float(substr buf, T val)37{38C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wfloat-equal");39static_assert(std::is_floating_point<T>::value, "must be floating point");40if(C4_UNLIKELY(std::isnan(val)))41return to_chars(buf, csubstr(".nan"));42else if(C4_UNLIKELY(val == std::numeric_limits<T>::infinity()))43return to_chars(buf, csubstr(".inf"));44else if(C4_UNLIKELY(val == -std::numeric_limits<T>::infinity()))45return to_chars(buf, csubstr("-.inf"));46return to_chars(buf, val);47C4_SUPPRESS_WARNING_GCC_CLANG_POP48}495051/** decode a floating point from string. Accepts special values: .nan,52* .inf, -.inf */53template<class T>54bool from_chars_float(csubstr buf, T *C4_RESTRICT val)55{56static_assert(std::is_floating_point<T>::value, "must be floating point");57if(C4_LIKELY(from_chars(buf, val)))58{59return true;60}61else if(C4_UNLIKELY(buf == ".nan" || buf == ".NaN" || buf == ".NAN"))62{63*val = std::numeric_limits<T>::quiet_NaN();64return true;65}66else if(C4_UNLIKELY(buf == ".inf" || buf == ".Inf" || buf == ".INF"))67{68*val = std::numeric_limits<T>::infinity();69return true;70}71else if(C4_UNLIKELY(buf == "-.inf" || buf == "-.Inf" || buf == "-.INF"))72{73*val = -std::numeric_limits<T>::infinity();74return true;75}76else77{78return false;79}80}818283//-----------------------------------------------------------------------------84//-----------------------------------------------------------------------------85//-----------------------------------------------------------------------------8687/** the integral type necessary to cover all the bits marking node tags */88using tag_bits = uint16_t;8990/** a bit mask for marking tags for types */91typedef enum : tag_bits {92// container types93TAG_NONE = 0,94TAG_MAP = 1, /**< !!map Unordered set of key: value pairs without duplicates. @see https://yaml.org/type/map.html */95TAG_OMAP = 2, /**< !!omap Ordered sequence of key: value pairs without duplicates. @see https://yaml.org/type/omap.html */96TAG_PAIRS = 3, /**< !!pairs Ordered sequence of key: value pairs allowing duplicates. @see https://yaml.org/type/pairs.html */97TAG_SET = 4, /**< !!set Unordered set of non-equal values. @see https://yaml.org/type/set.html */98TAG_SEQ = 5, /**< !!seq Sequence of arbitrary values. @see https://yaml.org/type/seq.html */99// scalar types100TAG_BINARY = 6, /**< !!binary A sequence of zero or more octets (8 bit values). @see https://yaml.org/type/binary.html */101TAG_BOOL = 7, /**< !!bool Mathematical Booleans. @see https://yaml.org/type/bool.html */102TAG_FLOAT = 8, /**< !!float Floating-point approximation to real numbers. https://yaml.org/type/float.html */103TAG_INT = 9, /**< !!float Mathematical integers. https://yaml.org/type/int.html */104TAG_MERGE = 10, /**< !!merge Specify one or more mapping to be merged with the current one. https://yaml.org/type/merge.html */105TAG_NULL = 11, /**< !!null Devoid of value. https://yaml.org/type/null.html */106TAG_STR = 12, /**< !!str A sequence of zero or more Unicode characters. https://yaml.org/type/str.html */107TAG_TIMESTAMP = 13, /**< !!timestamp A point in time https://yaml.org/type/timestamp.html */108TAG_VALUE = 14, /**< !!value Specify the default value of a mapping https://yaml.org/type/value.html */109TAG_YAML = 15, /**< !!yaml Specify the default value of a mapping https://yaml.org/type/yaml.html */110} YamlTag_e;111112YamlTag_e to_tag(csubstr tag);113csubstr from_tag(YamlTag_e tag);114csubstr from_tag_long(YamlTag_e tag);115csubstr normalize_tag(csubstr tag);116csubstr normalize_tag_long(csubstr tag);117118struct TagDirective119{120/** Eg `!e!` in `%TAG !e! tag:example.com,2000:app/` */121csubstr handle;122/** Eg `tag:example.com,2000:app/` in `%TAG !e! tag:example.com,2000:app/` */123csubstr prefix;124/** The next node to which this tag directive applies */125size_t next_node_id;126};127128#ifndef RYML_MAX_TAG_DIRECTIVES129/** the maximum number of tag directives in a Tree */130#define RYML_MAX_TAG_DIRECTIVES 4131#endif132133134135//-----------------------------------------------------------------------------136//-----------------------------------------------------------------------------137//-----------------------------------------------------------------------------138139140/** the integral type necessary to cover all the bits marking node types */141using type_bits = uint64_t;142143144/** a bit mask for marking node types */145typedef enum : type_bits {146// a convenience define, undefined below147#define c4bit(v) (type_bits(1) << v)148NOTYPE = 0, ///< no node type is set149VAL = c4bit(0), ///< a leaf node, has a (possibly empty) value150KEY = c4bit(1), ///< is member of a map, must have non-empty key151MAP = c4bit(2), ///< a map: a parent of keyvals152SEQ = c4bit(3), ///< a seq: a parent of vals153DOC = c4bit(4), ///< a document154STREAM = c4bit(5)|SEQ, ///< a stream: a seq of docs155KEYREF = c4bit(6), ///< a *reference: the key references an &anchor156VALREF = c4bit(7), ///< a *reference: the val references an &anchor157KEYANCH = c4bit(8), ///< the key has an &anchor158VALANCH = c4bit(9), ///< the val has an &anchor159KEYTAG = c4bit(10), ///< the key has an explicit tag/type160VALTAG = c4bit(11), ///< the val has an explicit tag/type161_TYMASK = c4bit(12)-1, // all the bits up to here162VALQUO = c4bit(12), ///< the val is quoted by '', "", > or |163KEYQUO = c4bit(13), ///< the key is quoted by '', "", > or |164KEYVAL = KEY|VAL,165KEYSEQ = KEY|SEQ,166KEYMAP = KEY|MAP,167DOCMAP = DOC|MAP,168DOCSEQ = DOC|SEQ,169DOCVAL = DOC|VAL,170_KEYMASK = KEY | KEYQUO | KEYANCH | KEYREF | KEYTAG,171_VALMASK = VAL | VALQUO | VALANCH | VALREF | VALTAG,172// these flags are from a work in progress and should be used with care173_WIP_STYLE_FLOW_SL = c4bit(14), ///< mark container with single-line flow format (seqs as '[val1,val2], maps as '{key: val, key2: val2}')174_WIP_STYLE_FLOW_ML = c4bit(15), ///< mark container with multi-line flow format (seqs as '[val1,\nval2], maps as '{key: val,\nkey2: val2}')175_WIP_STYLE_BLOCK = c4bit(16), ///< mark container with block format (seqs as '- val\n', maps as 'key: val')176_WIP_KEY_LITERAL = c4bit(17), ///< mark key scalar as multiline, block literal |177_WIP_VAL_LITERAL = c4bit(18), ///< mark val scalar as multiline, block literal |178_WIP_KEY_FOLDED = c4bit(19), ///< mark key scalar as multiline, block folded >179_WIP_VAL_FOLDED = c4bit(20), ///< mark val scalar as multiline, block folded >180_WIP_KEY_SQUO = c4bit(21), ///< mark key scalar as single quoted181_WIP_VAL_SQUO = c4bit(22), ///< mark val scalar as single quoted182_WIP_KEY_DQUO = c4bit(23), ///< mark key scalar as double quoted183_WIP_VAL_DQUO = c4bit(24), ///< mark val scalar as double quoted184_WIP_KEY_PLAIN = c4bit(25), ///< mark key scalar as plain scalar (unquoted, even when multiline)185_WIP_VAL_PLAIN = c4bit(26), ///< mark val scalar as plain scalar (unquoted, even when multiline)186_WIP_KEY_STYLE = _WIP_KEY_LITERAL|_WIP_KEY_FOLDED|_WIP_KEY_SQUO|_WIP_KEY_DQUO|_WIP_KEY_PLAIN,187_WIP_VAL_STYLE = _WIP_VAL_LITERAL|_WIP_VAL_FOLDED|_WIP_VAL_SQUO|_WIP_VAL_DQUO|_WIP_VAL_PLAIN,188_WIP_KEY_FT_NL = c4bit(27), ///< features: mark key scalar as having \n in its contents189_WIP_VAL_FT_NL = c4bit(28), ///< features: mark val scalar as having \n in its contents190_WIP_KEY_FT_SQ = c4bit(29), ///< features: mark key scalar as having single quotes in its contents191_WIP_VAL_FT_SQ = c4bit(30), ///< features: mark val scalar as having single quotes in its contents192_WIP_KEY_FT_DQ = c4bit(31), ///< features: mark key scalar as having double quotes in its contents193_WIP_VAL_FT_DQ = c4bit(32), ///< features: mark val scalar as having double quotes in its contents194#undef c4bit195} NodeType_e;196197198//-----------------------------------------------------------------------------199//-----------------------------------------------------------------------------200//-----------------------------------------------------------------------------201202/** wraps a NodeType_e element with some syntactic sugar and predicates */203struct NodeType204{205public:206207NodeType_e type;208209public:210211C4_ALWAYS_INLINE NodeType() : type(NOTYPE) {}212C4_ALWAYS_INLINE NodeType(NodeType_e t) : type(t) {}213C4_ALWAYS_INLINE NodeType(type_bits t) : type((NodeType_e)t) {}214215C4_ALWAYS_INLINE const char *type_str() const { return type_str(type); }216static const char* type_str(NodeType_e t);217218C4_ALWAYS_INLINE void set(NodeType_e t) { type = t; }219C4_ALWAYS_INLINE void set(type_bits t) { type = (NodeType_e)t; }220221C4_ALWAYS_INLINE void add(NodeType_e t) { type = (NodeType_e)(type|t); }222C4_ALWAYS_INLINE void add(type_bits t) { type = (NodeType_e)(type|t); }223224C4_ALWAYS_INLINE void rem(NodeType_e t) { type = (NodeType_e)(type & ~t); }225C4_ALWAYS_INLINE void rem(type_bits t) { type = (NodeType_e)(type & ~t); }226227C4_ALWAYS_INLINE void clear() { type = NOTYPE; }228229public:230231C4_ALWAYS_INLINE operator NodeType_e & C4_RESTRICT () { return type; }232C4_ALWAYS_INLINE operator NodeType_e const& C4_RESTRICT () const { return type; }233234C4_ALWAYS_INLINE bool operator== (NodeType_e t) const { return type == t; }235C4_ALWAYS_INLINE bool operator!= (NodeType_e t) const { return type != t; }236237public:238239#if defined(__clang__)240# pragma clang diagnostic push241# pragma clang diagnostic ignored "-Wnull-dereference"242#elif defined(__GNUC__)243# pragma GCC diagnostic push244# if __GNUC__ >= 6245# pragma GCC diagnostic ignored "-Wnull-dereference"246# endif247#endif248249C4_ALWAYS_INLINE bool is_notype() const { return type == NOTYPE; }250C4_ALWAYS_INLINE bool is_stream() const { return ((type & STREAM) == STREAM) != 0; }251C4_ALWAYS_INLINE bool is_doc() const { return (type & DOC) != 0; }252C4_ALWAYS_INLINE bool is_container() const { return (type & (MAP|SEQ|STREAM)) != 0; }253C4_ALWAYS_INLINE bool is_map() const { return (type & MAP) != 0; }254C4_ALWAYS_INLINE bool is_seq() const { return (type & SEQ) != 0; }255C4_ALWAYS_INLINE bool has_key() const { return (type & KEY) != 0; }256C4_ALWAYS_INLINE bool has_val() const { return (type & VAL) != 0; }257C4_ALWAYS_INLINE bool is_val() const { return (type & KEYVAL) == VAL; }258C4_ALWAYS_INLINE bool is_keyval() const { return (type & KEYVAL) == KEYVAL; }259C4_ALWAYS_INLINE bool has_key_tag() const { return (type & (KEY|KEYTAG)) == (KEY|KEYTAG); }260C4_ALWAYS_INLINE bool has_val_tag() const { return ((type & VALTAG) && (type & (VAL|MAP|SEQ))); }261C4_ALWAYS_INLINE bool has_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); }262C4_ALWAYS_INLINE bool is_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); }263C4_ALWAYS_INLINE bool has_val_anchor() const { return (type & VALANCH) != 0 && (type & (VAL|SEQ|MAP)) != 0; }264C4_ALWAYS_INLINE bool is_val_anchor() const { return (type & VALANCH) != 0 && (type & (VAL|SEQ|MAP)) != 0; }265C4_ALWAYS_INLINE bool has_anchor() const { return (type & (KEYANCH|VALANCH)) != 0; }266C4_ALWAYS_INLINE bool is_anchor() const { return (type & (KEYANCH|VALANCH)) != 0; }267C4_ALWAYS_INLINE bool is_key_ref() const { return (type & KEYREF) != 0; }268C4_ALWAYS_INLINE bool is_val_ref() const { return (type & VALREF) != 0; }269C4_ALWAYS_INLINE bool is_ref() const { return (type & (KEYREF|VALREF)) != 0; }270C4_ALWAYS_INLINE bool is_anchor_or_ref() const { return (type & (KEYANCH|VALANCH|KEYREF|VALREF)) != 0; }271C4_ALWAYS_INLINE bool is_key_quoted() const { return (type & (KEY|KEYQUO)) == (KEY|KEYQUO); }272C4_ALWAYS_INLINE bool is_val_quoted() const { return (type & (VAL|VALQUO)) == (VAL|VALQUO); }273C4_ALWAYS_INLINE bool is_quoted() const { return (type & (KEY|KEYQUO)) == (KEY|KEYQUO) || (type & (VAL|VALQUO)) == (VAL|VALQUO); }274275// these predicates are a work in progress and subject to change. Don't use yet.276C4_ALWAYS_INLINE bool default_block() const { return (type & (_WIP_STYLE_BLOCK|_WIP_STYLE_FLOW_ML|_WIP_STYLE_FLOW_SL)) == 0; }277C4_ALWAYS_INLINE bool marked_block() const { return (type & (_WIP_STYLE_BLOCK)) != 0; }278C4_ALWAYS_INLINE bool marked_flow_sl() const { return (type & (_WIP_STYLE_FLOW_SL)) != 0; }279C4_ALWAYS_INLINE bool marked_flow_ml() const { return (type & (_WIP_STYLE_FLOW_ML)) != 0; }280C4_ALWAYS_INLINE bool marked_flow() const { return (type & (_WIP_STYLE_FLOW_ML|_WIP_STYLE_FLOW_SL)) != 0; }281C4_ALWAYS_INLINE bool key_marked_literal() const { return (type & (_WIP_KEY_LITERAL)) != 0; }282C4_ALWAYS_INLINE bool val_marked_literal() const { return (type & (_WIP_VAL_LITERAL)) != 0; }283C4_ALWAYS_INLINE bool key_marked_folded() const { return (type & (_WIP_KEY_FOLDED)) != 0; }284C4_ALWAYS_INLINE bool val_marked_folded() const { return (type & (_WIP_VAL_FOLDED)) != 0; }285C4_ALWAYS_INLINE bool key_marked_squo() const { return (type & (_WIP_KEY_SQUO)) != 0; }286C4_ALWAYS_INLINE bool val_marked_squo() const { return (type & (_WIP_VAL_SQUO)) != 0; }287C4_ALWAYS_INLINE bool key_marked_dquo() const { return (type & (_WIP_KEY_DQUO)) != 0; }288C4_ALWAYS_INLINE bool val_marked_dquo() const { return (type & (_WIP_VAL_DQUO)) != 0; }289C4_ALWAYS_INLINE bool key_marked_plain() const { return (type & (_WIP_KEY_PLAIN)) != 0; }290C4_ALWAYS_INLINE bool val_marked_plain() const { return (type & (_WIP_VAL_PLAIN)) != 0; }291292#if defined(__clang__)293# pragma clang diagnostic pop294#elif defined(__GNUC__)295# pragma GCC diagnostic pop296#endif297298};299300301//-----------------------------------------------------------------------------302//-----------------------------------------------------------------------------303//-----------------------------------------------------------------------------304305/** a node scalar is a csubstr, which may be tagged and anchored. */306struct NodeScalar307{308csubstr tag;309csubstr scalar;310csubstr anchor;311312public:313314/// initialize as an empty scalar315inline NodeScalar() noexcept : tag(), scalar(), anchor() {}316317/// initialize as an untagged scalar318template<size_t N>319inline NodeScalar(const char (&s)[N]) noexcept : tag(), scalar(s), anchor() {}320inline NodeScalar(csubstr s ) noexcept : tag(), scalar(s), anchor() {}321322/// initialize as a tagged scalar323template<size_t N, size_t M>324inline NodeScalar(const char (&t)[N], const char (&s)[N]) noexcept : tag(t), scalar(s), anchor() {}325inline NodeScalar(csubstr t , csubstr s ) noexcept : tag(t), scalar(s), anchor() {}326327public:328329~NodeScalar() noexcept = default;330NodeScalar(NodeScalar &&) noexcept = default;331NodeScalar(NodeScalar const&) noexcept = default;332NodeScalar& operator= (NodeScalar &&) noexcept = default;333NodeScalar& operator= (NodeScalar const&) noexcept = default;334335public:336337bool empty() const noexcept { return tag.empty() && scalar.empty() && anchor.empty(); }338339void clear() noexcept { tag.clear(); scalar.clear(); anchor.clear(); }340341void set_ref_maybe_replacing_scalar(csubstr ref, bool has_scalar) noexcept342{343csubstr trimmed = ref.begins_with('*') ? ref.sub(1) : ref;344anchor = trimmed;345if((!has_scalar) || !scalar.ends_with(trimmed))346scalar = ref;347}348};349C4_MUST_BE_TRIVIAL_COPY(NodeScalar);350351352//-----------------------------------------------------------------------------353//-----------------------------------------------------------------------------354//-----------------------------------------------------------------------------355356/** convenience class to initialize nodes */357struct NodeInit358{359360NodeType type;361NodeScalar key;362NodeScalar val;363364public:365366/// initialize as an empty node367NodeInit() : type(NOTYPE), key(), val() {}368/// initialize as a typed node369NodeInit(NodeType_e t) : type(t), key(), val() {}370/// initialize as a sequence member371NodeInit(NodeScalar const& v) : type(VAL), key(), val(v) { _add_flags(); }372/// initialize as a mapping member373NodeInit( NodeScalar const& k, NodeScalar const& v) : type(KEYVAL), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); }374/// initialize as a mapping member with explicit type375NodeInit(NodeType_e t, NodeScalar const& k, NodeScalar const& v) : type(t ), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); }376/// initialize as a mapping member with explicit type (eg SEQ or MAP)377NodeInit(NodeType_e t, NodeScalar const& k ) : type(t ), key(k.tag, k.scalar), val( ) { _add_flags(KEY); }378379public:380381void clear()382{383type.clear();384key.clear();385val.clear();386}387388void _add_flags(type_bits more_flags=0)389{390type = (type|more_flags);391if( ! key.tag.empty())392type = (type|KEYTAG);393if( ! val.tag.empty())394type = (type|VALTAG);395if( ! key.anchor.empty())396type = (type|KEYANCH);397if( ! val.anchor.empty())398type = (type|VALANCH);399}400401bool _check() const402{403// key cannot be empty404RYML_ASSERT(key.scalar.empty() == ((type & KEY) == 0));405// key tag cannot be empty406RYML_ASSERT(key.tag.empty() == ((type & KEYTAG) == 0));407// val may be empty even though VAL is set. But when VAL is not set, val must be empty408RYML_ASSERT(((type & VAL) != 0) || val.scalar.empty());409// val tag cannot be empty410RYML_ASSERT(val.tag.empty() == ((type & VALTAG) == 0));411return true;412}413};414415416//-----------------------------------------------------------------------------417//-----------------------------------------------------------------------------418//-----------------------------------------------------------------------------419420/** contains the data for each YAML node. */421struct NodeData422{423NodeType m_type;424425NodeScalar m_key;426NodeScalar m_val;427428size_t m_parent;429size_t m_first_child;430size_t m_last_child;431size_t m_next_sibling;432size_t m_prev_sibling;433};434C4_MUST_BE_TRIVIAL_COPY(NodeData);435436437//-----------------------------------------------------------------------------438//-----------------------------------------------------------------------------439//-----------------------------------------------------------------------------440441class RYML_EXPORT Tree442{443public:444445/** @name construction and assignment */446/** @{ */447448Tree() : Tree(get_callbacks()) {}449Tree(Callbacks const& cb);450Tree(size_t node_capacity, size_t arena_capacity=0) : Tree(node_capacity, arena_capacity, get_callbacks()) {}451Tree(size_t node_capacity, size_t arena_capacity, Callbacks const& cb);452453~Tree();454455Tree(Tree const& that) noexcept;456Tree(Tree && that) noexcept;457458Tree& operator= (Tree const& that) noexcept;459Tree& operator= (Tree && that) noexcept;460461/** @} */462463public:464465/** @name memory and sizing */466/** @{ */467468void reserve(size_t node_capacity);469470/** clear the tree and zero every node471* @note does NOT clear the arena472* @see clear_arena() */473void clear();474inline void clear_arena() { m_arena_pos = 0; }475476inline bool empty() const { return m_size == 0; }477478inline size_t size() const { return m_size; }479inline size_t capacity() const { return m_cap; }480inline size_t slack() const { RYML_ASSERT(m_cap >= m_size); return m_cap - m_size; }481482Callbacks const& callbacks() const { return m_callbacks; }483void callbacks(Callbacks const& cb) { m_callbacks = cb; }484485/** @} */486487public:488489/** @name node getters */490/** @{ */491492//! get the index of a node belonging to this tree.493//! @p n can be nullptr, in which case a494size_t id(NodeData const* n) const495{496if( ! n)497{498return NONE;499}500RYML_ASSERT(n >= m_buf && n < m_buf + m_cap);501return static_cast<size_t>(n - m_buf);502}503504//! get a pointer to a node's NodeData.505//! i can be NONE, in which case a nullptr is returned506inline NodeData *get(size_t i)507{508if(i == NONE)509return nullptr;510RYML_ASSERT(i >= 0 && i < m_cap);511return m_buf + i;512}513//! get a pointer to a node's NodeData.514//! i can be NONE, in which case a nullptr is returned.515inline NodeData const *get(size_t i) const516{517if(i == NONE)518return nullptr;519RYML_ASSERT(i >= 0 && i < m_cap);520return m_buf + i;521}522523//! An if-less form of get() that demands a valid node index.524//! This function is implementation only; use at your own risk.525inline NodeData * _p(size_t i) { RYML_ASSERT(i != NONE && i >= 0 && i < m_cap); return m_buf + i; }526//! An if-less form of get() that demands a valid node index.527//! This function is implementation only; use at your own risk.528inline NodeData const * _p(size_t i) const { RYML_ASSERT(i != NONE && i >= 0 && i < m_cap); return m_buf + i; }529530//! Get the id of the root node531size_t root_id() { if(m_cap == 0) { reserve(16); } RYML_ASSERT(m_cap > 0 && m_size > 0); return 0; }532//! Get the id of the root node533size_t root_id() const { RYML_ASSERT(m_cap > 0 && m_size > 0); return 0; }534535//! Get a NodeRef of a node by id536NodeRef ref(size_t id);537//! Get a NodeRef of a node by id538ConstNodeRef ref(size_t id) const;539//! Get a NodeRef of a node by id540ConstNodeRef cref(size_t id);541//! Get a NodeRef of a node by id542ConstNodeRef cref(size_t id) const;543544//! Get the root as a NodeRef545NodeRef rootref();546//! Get the root as a NodeRef547ConstNodeRef rootref() const;548//! Get the root as a NodeRef549ConstNodeRef crootref();550//! Get the root as a NodeRef551ConstNodeRef crootref() const;552553//! find a root child by name, return it as a NodeRef554//! @note requires the root to be a map.555NodeRef operator[] (csubstr key);556//! find a root child by name, return it as a NodeRef557//! @note requires the root to be a map.558ConstNodeRef operator[] (csubstr key) const;559560//! find a root child by index: return the root node's @p i-th child as a NodeRef561//! @note @i is NOT the node id, but the child's position562NodeRef operator[] (size_t i);563//! find a root child by index: return the root node's @p i-th child as a NodeRef564//! @note @i is NOT the node id, but the child's position565ConstNodeRef operator[] (size_t i) const;566567//! get the i-th document of the stream568//! @note @i is NOT the node id, but the doc position within the stream569NodeRef docref(size_t i);570//! get the i-th document of the stream571//! @note @i is NOT the node id, but the doc position within the stream572ConstNodeRef docref(size_t i) const;573574/** @} */575576public:577578/** @name node property getters */579/** @{ */580581NodeType type(size_t node) const { return _p(node)->m_type; }582const char* type_str(size_t node) const { return NodeType::type_str(_p(node)->m_type); }583584csubstr const& key (size_t node) const { RYML_ASSERT(has_key(node)); return _p(node)->m_key.scalar; }585csubstr const& key_tag (size_t node) const { RYML_ASSERT(has_key_tag(node)); return _p(node)->m_key.tag; }586csubstr const& key_ref (size_t node) const { RYML_ASSERT(is_key_ref(node) && ! has_key_anchor(node)); return _p(node)->m_key.anchor; }587csubstr const& key_anchor(size_t node) const { RYML_ASSERT( ! is_key_ref(node) && has_key_anchor(node)); return _p(node)->m_key.anchor; }588NodeScalar const& keysc (size_t node) const { RYML_ASSERT(has_key(node)); return _p(node)->m_key; }589590csubstr const& val (size_t node) const { RYML_ASSERT(has_val(node)); return _p(node)->m_val.scalar; }591csubstr const& val_tag (size_t node) const { RYML_ASSERT(has_val_tag(node)); return _p(node)->m_val.tag; }592csubstr const& val_ref (size_t node) const { RYML_ASSERT(is_val_ref(node) && ! has_val_anchor(node)); return _p(node)->m_val.anchor; }593csubstr const& val_anchor(size_t node) const { RYML_ASSERT( ! is_val_ref(node) && has_val_anchor(node)); return _p(node)->m_val.anchor; }594NodeScalar const& valsc (size_t node) const { RYML_ASSERT(has_val(node)); return _p(node)->m_val; }595596/** @} */597598public:599600/** @name node predicates */601/** @{ */602603C4_ALWAYS_INLINE bool is_stream(size_t node) const { return _p(node)->m_type.is_stream(); }604C4_ALWAYS_INLINE bool is_doc(size_t node) const { return _p(node)->m_type.is_doc(); }605C4_ALWAYS_INLINE bool is_container(size_t node) const { return _p(node)->m_type.is_container(); }606C4_ALWAYS_INLINE bool is_map(size_t node) const { return _p(node)->m_type.is_map(); }607C4_ALWAYS_INLINE bool is_seq(size_t node) const { return _p(node)->m_type.is_seq(); }608C4_ALWAYS_INLINE bool has_key(size_t node) const { return _p(node)->m_type.has_key(); }609C4_ALWAYS_INLINE bool has_val(size_t node) const { return _p(node)->m_type.has_val(); }610C4_ALWAYS_INLINE bool is_val(size_t node) const { return _p(node)->m_type.is_val(); }611C4_ALWAYS_INLINE bool is_keyval(size_t node) const { return _p(node)->m_type.is_keyval(); }612C4_ALWAYS_INLINE bool has_key_tag(size_t node) const { return _p(node)->m_type.has_key_tag(); }613C4_ALWAYS_INLINE bool has_val_tag(size_t node) const { return _p(node)->m_type.has_val_tag(); }614C4_ALWAYS_INLINE bool has_key_anchor(size_t node) const { return _p(node)->m_type.has_key_anchor(); }615C4_ALWAYS_INLINE bool is_key_anchor(size_t node) const { return _p(node)->m_type.is_key_anchor(); }616C4_ALWAYS_INLINE bool has_val_anchor(size_t node) const { return _p(node)->m_type.has_val_anchor(); }617C4_ALWAYS_INLINE bool is_val_anchor(size_t node) const { return _p(node)->m_type.is_val_anchor(); }618C4_ALWAYS_INLINE bool has_anchor(size_t node) const { return _p(node)->m_type.has_anchor(); }619C4_ALWAYS_INLINE bool is_anchor(size_t node) const { return _p(node)->m_type.is_anchor(); }620C4_ALWAYS_INLINE bool is_key_ref(size_t node) const { return _p(node)->m_type.is_key_ref(); }621C4_ALWAYS_INLINE bool is_val_ref(size_t node) const { return _p(node)->m_type.is_val_ref(); }622C4_ALWAYS_INLINE bool is_ref(size_t node) const { return _p(node)->m_type.is_ref(); }623C4_ALWAYS_INLINE bool is_anchor_or_ref(size_t node) const { return _p(node)->m_type.is_anchor_or_ref(); }624C4_ALWAYS_INLINE bool is_key_quoted(size_t node) const { return _p(node)->m_type.is_key_quoted(); }625C4_ALWAYS_INLINE bool is_val_quoted(size_t node) const { return _p(node)->m_type.is_val_quoted(); }626C4_ALWAYS_INLINE bool is_quoted(size_t node) const { return _p(node)->m_type.is_quoted(); }627628C4_ALWAYS_INLINE bool parent_is_seq(size_t node) const { RYML_ASSERT(has_parent(node)); return is_seq(_p(node)->m_parent); }629C4_ALWAYS_INLINE bool parent_is_map(size_t node) const { RYML_ASSERT(has_parent(node)); return is_map(_p(node)->m_parent); }630631/** true when key and val are empty, and has no children */632C4_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()); }633/** true when the node has an anchor named a */634C4_ALWAYS_INLINE bool has_anchor(size_t node, csubstr a) const { return _p(node)->m_key.anchor == a || _p(node)->m_val.anchor == a; }635636C4_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); }637C4_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); }638static bool _is_null(csubstr s) noexcept639{640return s.str == nullptr ||641s == "~" ||642s == "null" ||643s == "Null" ||644s == "NULL";645}646647/** @} */648649public:650651/** @name hierarchy predicates */652/** @{ */653654bool is_root(size_t node) const { RYML_ASSERT(_p(node)->m_parent != NONE || node == 0); return _p(node)->m_parent == NONE; }655656bool has_parent(size_t node) const { return _p(node)->m_parent != NONE; }657658/** true if @p node has a child with id @p ch */659bool has_child(size_t node, size_t ch) const { return _p(ch)->m_parent == node; }660/** true if @p node has a child with key @p key */661bool has_child(size_t node, csubstr key) const { return find_child(node, key) != npos; }662/** true if @p node has any children key */663bool has_children(size_t node) const { return _p(node)->m_first_child != NONE; }664665/** true if @p node has a sibling with id @p sib */666bool has_sibling(size_t node, size_t sib) const { return _p(node)->m_parent == _p(sib)->m_parent; }667/** true if one of the node's siblings has the given key */668bool has_sibling(size_t node, csubstr key) const { return find_sibling(node, key) != npos; }669/** true if node is not a single child */670bool has_other_siblings(size_t node) const671{672NodeData const *n = _p(node);673if(C4_LIKELY(n->m_parent != NONE))674{675n = _p(n->m_parent);676return n->m_first_child != n->m_last_child;677}678return false;679}680681RYML_DEPRECATED("use has_other_siblings()") bool has_siblings(size_t /*node*/) const { return true; }682683/** @} */684685public:686687/** @name hierarchy getters */688/** @{ */689690size_t parent(size_t node) const { return _p(node)->m_parent; }691692size_t prev_sibling(size_t node) const { return _p(node)->m_prev_sibling; }693size_t next_sibling(size_t node) const { return _p(node)->m_next_sibling; }694695/** O(#num_children) */696size_t num_children(size_t node) const;697size_t child_pos(size_t node, size_t ch) const;698size_t first_child(size_t node) const { return _p(node)->m_first_child; }699size_t last_child(size_t node) const { return _p(node)->m_last_child; }700size_t child(size_t node, size_t pos) const;701size_t find_child(size_t node, csubstr const& key) const;702703/** O(#num_siblings) */704/** counts with this */705size_t num_siblings(size_t node) const { return is_root(node) ? 1 : num_children(_p(node)->m_parent); }706/** does not count with this */707size_t num_other_siblings(size_t node) const { size_t ns = num_siblings(node); RYML_ASSERT(ns > 0); return ns-1; }708size_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); }709size_t first_sibling(size_t node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_first_child; }710size_t last_sibling(size_t node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_last_child; }711size_t sibling(size_t node, size_t pos) const { return child(_p(node)->m_parent, pos); }712size_t find_sibling(size_t node, csubstr const& key) const { return find_child(_p(node)->m_parent, key); }713714size_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.715716/** @} */717718public:719720/** @name node modifiers */721/** @{ */722723void to_keyval(size_t node, csubstr key, csubstr val, type_bits more_flags=0);724void to_map(size_t node, csubstr key, type_bits more_flags=0);725void to_seq(size_t node, csubstr key, type_bits more_flags=0);726void to_val(size_t node, csubstr val, type_bits more_flags=0);727void to_map(size_t node, type_bits more_flags=0);728void to_seq(size_t node, type_bits more_flags=0);729void to_doc(size_t node, type_bits more_flags=0);730void to_stream(size_t node, type_bits more_flags=0);731732void set_key(size_t node, csubstr key) { RYML_ASSERT(has_key(node)); _p(node)->m_key.scalar = key; }733void set_val(size_t node, csubstr val) { RYML_ASSERT(has_val(node)); _p(node)->m_val.scalar = val; }734735void set_key_tag(size_t node, csubstr tag) { RYML_ASSERT(has_key(node)); _p(node)->m_key.tag = tag; _add_flags(node, KEYTAG); }736void 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); }737738void 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); }739void 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); }740void 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); }741void 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); }742743void rem_key_anchor(size_t node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYANCH); }744void rem_val_anchor(size_t node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALANCH); }745void rem_key_ref (size_t node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYREF); }746void rem_val_ref (size_t node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALREF); }747void 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); }748749/** @} */750751public:752753/** @name tree modifiers */754/** @{ */755756/** reorder the tree in memory so that all the nodes are stored757* in a linear sequence when visited in depth-first order.758* This will invalidate existing ids, since the node id is its759* position in the node array. */760void reorder();761762/** Resolve references (aliases <- anchors) in the tree.763*764* Dereferencing is opt-in; after parsing, Tree::resolve()765* has to be called explicitly for obtaining resolved references in the766* tree. This method will resolve all references and substitute the767* anchored values in place of the reference.768*769* This method first does a full traversal of the tree to gather all770* anchors and references in a separate collection, then it goes through771* that collection to locate the names, which it does by obeying the YAML772* standard diktat that "an alias node refers to the most recent node in773* the serialization having the specified anchor"774*775* So, depending on the number of anchor/alias nodes, this is a776* potentially expensive operation, with a best-case linear complexity777* (from the initial traversal). This potential cost is the reason for778* requiring an explicit call.779*/780void resolve();781782/** @} */783784public:785786/** @name tag directives */787/** @{ */788789void resolve_tags();790791size_t num_tag_directives() const;792size_t add_tag_directive(TagDirective const& td);793void clear_tag_directives();794795size_t resolve_tag(substr output, csubstr tag, size_t node_id) const;796csubstr resolve_tag_sub(substr output, csubstr tag, size_t node_id) const797{798size_t needed = resolve_tag(output, tag, node_id);799return needed <= output.len ? output.first(needed) : output;800}801802using tag_directive_const_iterator = TagDirective const*;803tag_directive_const_iterator begin_tag_directives() const { return m_tag_directives; }804tag_directive_const_iterator end_tag_directives() const { return m_tag_directives + num_tag_directives(); }805806struct TagDirectiveProxy807{808tag_directive_const_iterator b, e;809tag_directive_const_iterator begin() const { return b; }810tag_directive_const_iterator end() const { return e; }811};812813TagDirectiveProxy tag_directives() const { return TagDirectiveProxy{begin_tag_directives(), end_tag_directives()}; }814815/** @} */816817public:818819/** @name modifying hierarchy */820/** @{ */821822/** create and insert a new child of @p parent. insert after the (to-be)823* sibling @p after, which must be a child of @p parent. To insert as the824* first child, set after to NONE */825C4_ALWAYS_INLINE size_t insert_child(size_t parent, size_t after)826{827RYML_ASSERT(parent != NONE);828RYML_ASSERT(is_container(parent) || is_root(parent));829RYML_ASSERT(after == NONE || (_p(after)->m_parent == parent));830size_t child = _claim();831_set_hierarchy(child, parent, after);832return child;833}834/** create and insert a node as the first child of @p parent */835C4_ALWAYS_INLINE size_t prepend_child(size_t parent) { return insert_child(parent, NONE); }836/** create and insert a node as the last child of @p parent */837C4_ALWAYS_INLINE size_t append_child(size_t parent) { return insert_child(parent, _p(parent)->m_last_child); }838839public:840841#if defined(__clang__)842# pragma clang diagnostic push843# pragma clang diagnostic ignored "-Wnull-dereference"844#elif defined(__GNUC__)845# pragma GCC diagnostic push846# if __GNUC__ >= 6847# pragma GCC diagnostic ignored "-Wnull-dereference"848# endif849#endif850851//! create and insert a new sibling of n. insert after "after"852C4_ALWAYS_INLINE size_t insert_sibling(size_t node, size_t after)853{854return insert_child(_p(node)->m_parent, after);855}856/** create and insert a node as the first node of @p parent */857C4_ALWAYS_INLINE size_t prepend_sibling(size_t node) { return prepend_child(_p(node)->m_parent); }858C4_ALWAYS_INLINE size_t append_sibling(size_t node) { return append_child(_p(node)->m_parent); }859860public:861862/** remove an entire branch at once: ie remove the children and the node itself */863inline void remove(size_t node)864{865remove_children(node);866_release(node);867}868869/** remove all the node's children, but keep the node itself */870void remove_children(size_t node);871872/** change the @p type of the node to one of MAP, SEQ or VAL. @p873* type must have one and only one of MAP,SEQ,VAL; @p type may874* possibly have KEY, but if it does, then the @p node must also875* have KEY. Changing to the same type is a no-op. Otherwise,876* changing to a different type will initialize the node with an877* empty value of the desired type: changing to VAL will878* initialize with a null scalar (~), changing to MAP will879* initialize with an empty map ({}), and changing to SEQ will880* initialize with an empty seq ([]). */881bool change_type(size_t node, NodeType type);882883bool change_type(size_t node, type_bits type)884{885return change_type(node, (NodeType)type);886}887888#if defined(__clang__)889# pragma clang diagnostic pop890#elif defined(__GNUC__)891# pragma GCC diagnostic pop892#endif893894public:895896/** change the node's position in the parent */897void move(size_t node, size_t after);898899/** change the node's parent and position */900void move(size_t node, size_t new_parent, size_t after);901902/** change the node's parent and position to a different tree903* @return the index of the new node in the destination tree */904size_t move(Tree * src, size_t node, size_t new_parent, size_t after);905906/** ensure the first node is a stream. Eg, change this tree907*908* DOCMAP909* MAP910* KEYVAL911* KEYVAL912* SEQ913* VAL914*915* to916*917* STREAM918* DOCMAP919* MAP920* KEYVAL921* KEYVAL922* SEQ923* VAL924*925* If the root is already a stream, this is a no-op.926*/927void set_root_as_stream();928929public:930931/** recursively duplicate a node from this tree into a new parent,932* placing it after one of its children933* @return the index of the copy */934size_t duplicate(size_t node, size_t new_parent, size_t after);935/** recursively duplicate a node from a different tree into a new parent,936* placing it after one of its children937* @return the index of the copy */938size_t duplicate(Tree const* src, size_t node, size_t new_parent, size_t after);939940/** recursively duplicate the node's children (but not the node)941* @return the index of the last duplicated child */942size_t duplicate_children(size_t node, size_t parent, size_t after);943/** recursively duplicate the node's children (but not the node), where944* the node is from a different tree945* @return the index of the last duplicated child */946size_t duplicate_children(Tree const* src, size_t node, size_t parent, size_t after);947948void duplicate_contents(size_t node, size_t where);949void duplicate_contents(Tree const* src, size_t node, size_t where);950951/** duplicate the node's children (but not the node) in a new parent, but952* omit repetitions where a duplicated node has the same key (in maps) or953* value (in seqs). If one of the duplicated children has the same key954* (in maps) or value (in seqs) as one of the parent's children, the one955* that is placed closest to the end will prevail. */956size_t duplicate_children_no_rep(size_t node, size_t parent, size_t after);957size_t duplicate_children_no_rep(Tree const* src, size_t node, size_t parent, size_t after);958959public:960961void merge_with(Tree const* src, size_t src_node=NONE, size_t dst_root=NONE);962963/** @} */964965public:966967/** @name internal string arena */968/** @{ */969970/** get the current size of the tree's internal arena */971RYML_DEPRECATED("use arena_size() instead") size_t arena_pos() const { return m_arena_pos; }972/** get the current size of the tree's internal arena */973inline size_t arena_size() const { return m_arena_pos; }974/** get the current capacity of the tree's internal arena */975inline size_t arena_capacity() const { return m_arena.len; }976/** get the current slack of the tree's internal arena */977inline size_t arena_slack() const { RYML_ASSERT(m_arena.len >= m_arena_pos); return m_arena.len - m_arena_pos; }978979/** get the current arena */980substr arena() const { return m_arena.first(m_arena_pos); }981982/** return true if the given substring is part of the tree's string arena */983bool in_arena(csubstr s) const984{985return m_arena.is_super(s);986}987988/** serialize the given floating-point variable to the tree's989* arena, growing it as needed to accomodate the serialization.990*991* @note Growing the arena may cause relocation of the entire992* existing arena, and thus change the contents of individual993* nodes, and thus cost O(numnodes)+O(arenasize). To avoid this994* cost, ensure that the arena is reserved to an appropriate size995* using .reserve_arena()996*997* @see alloc_arena() */998template<class T>999typename std::enable_if<std::is_floating_point<T>::value, csubstr>::type1000to_arena(T const& C4_RESTRICT a)1001{1002substr rem(m_arena.sub(m_arena_pos));1003size_t num = to_chars_float(rem, a);1004if(num > rem.len)1005{1006rem = _grow_arena(num);1007num = to_chars_float(rem, a);1008RYML_ASSERT(num <= rem.len);1009}1010rem = _request_span(num);1011return rem;1012}10131014/** serialize the given non-floating-point variable to the tree's1015* arena, growing it as needed to accomodate the serialization.1016*1017* @note Growing the arena may cause relocation of the entire1018* existing arena, and thus change the contents of individual1019* nodes, and thus cost O(numnodes)+O(arenasize). To avoid this1020* cost, ensure that the arena is reserved to an appropriate size1021* using .reserve_arena()1022*1023* @see alloc_arena() */1024template<class T>1025typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type1026to_arena(T const& C4_RESTRICT a)1027{1028substr rem(m_arena.sub(m_arena_pos));1029size_t num = to_chars(rem, a);1030if(num > rem.len)1031{1032rem = _grow_arena(num);1033num = to_chars(rem, a);1034RYML_ASSERT(num <= rem.len);1035}1036rem = _request_span(num);1037return rem;1038}10391040/** serialize the given csubstr to the tree's arena, growing the1041* arena as needed to accomodate the serialization.1042*1043* @note Growing the arena may cause relocation of the entire1044* existing arena, and thus change the contents of individual1045* nodes, and thus cost O(numnodes)+O(arenasize). To avoid this1046* cost, ensure that the arena is reserved to an appropriate size1047* using .reserve_arena()1048*1049* @see alloc_arena() */1050csubstr to_arena(csubstr a)1051{1052if(a.len > 0)1053{1054substr rem(m_arena.sub(m_arena_pos));1055size_t num = to_chars(rem, a);1056if(num > rem.len)1057{1058rem = _grow_arena(num);1059num = to_chars(rem, a);1060RYML_ASSERT(num <= rem.len);1061}1062return _request_span(num);1063}1064else1065{1066if(a.str == nullptr)1067{1068return csubstr{};1069}1070else if(m_arena.str == nullptr)1071{1072// Arena is empty and we want to store a non-null1073// zero-length string.1074// Even though the string has zero length, we need1075// some "memory" to store a non-nullptr string1076_grow_arena(1);1077}1078return _request_span(0);1079}1080}1081C4_ALWAYS_INLINE csubstr to_arena(const char *s)1082{1083return to_arena(to_csubstr(s));1084}1085C4_ALWAYS_INLINE csubstr to_arena(std::nullptr_t)1086{1087return csubstr{};1088}10891090/** copy the given substr to the tree's arena, growing it by the1091* required size1092*1093* @note Growing the arena may cause relocation of the entire1094* existing arena, and thus change the contents of individual1095* nodes, and thus cost O(numnodes)+O(arenasize). To avoid this1096* cost, ensure that the arena is reserved to an appropriate size1097* using .reserve_arena()1098*1099* @see alloc_arena() */1100substr copy_to_arena(csubstr s)1101{1102substr cp = alloc_arena(s.len);1103RYML_ASSERT(cp.len == s.len);1104RYML_ASSERT(!s.overlaps(cp));1105#if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)1106C4_SUPPRESS_WARNING_GCC_PUSH1107C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow=") // no need for terminating \01108C4_SUPPRESS_WARNING_GCC( "-Wrestrict") // there's an assert to ensure no violation of restrict behavior1109#endif1110if(s.len)1111memcpy(cp.str, s.str, s.len);1112#if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)1113C4_SUPPRESS_WARNING_GCC_POP1114#endif1115return cp;1116}11171118/** grow the tree's string arena by the given size and return a substr1119* of the added portion1120*1121* @note Growing the arena may cause relocation of the entire1122* existing arena, and thus change the contents of individual1123* nodes, and thus cost O(numnodes)+O(arenasize). To avoid this1124* cost, ensure that the arena is reserved to an appropriate size1125* using .reserve_arena().1126*1127* @see reserve_arena() */1128substr alloc_arena(size_t sz)1129{1130if(sz > arena_slack())1131_grow_arena(sz - arena_slack());1132substr s = _request_span(sz);1133return s;1134}11351136/** ensure the tree's internal string arena is at least the given capacity1137* @note This operation has a potential complexity of O(numNodes)+O(arenasize).1138* Growing the arena may cause relocation of the entire1139* existing arena, and thus change the contents of individual nodes. */1140void reserve_arena(size_t arena_cap)1141{1142if(arena_cap > m_arena.len)1143{1144substr buf;1145buf.str = (char*) m_callbacks.m_allocate(arena_cap, m_arena.str, m_callbacks.m_user_data);1146buf.len = arena_cap;1147if(m_arena.str)1148{1149RYML_ASSERT(m_arena.len >= 0);1150_relocate(buf); // does a memcpy and changes nodes using the arena1151m_callbacks.m_free(m_arena.str, m_arena.len, m_callbacks.m_user_data);1152}1153m_arena = buf;1154}1155}11561157/** @} */11581159private:11601161substr _grow_arena(size_t more)1162{1163size_t cap = m_arena.len + more;1164cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap;1165cap = cap < 64 ? 64 : cap;1166reserve_arena(cap);1167return m_arena.sub(m_arena_pos);1168}11691170substr _request_span(size_t sz)1171{1172substr s;1173s = m_arena.sub(m_arena_pos, sz);1174m_arena_pos += sz;1175return s;1176}11771178substr _relocated(csubstr s, substr next_arena) const1179{1180RYML_ASSERT(m_arena.is_super(s));1181RYML_ASSERT(m_arena.sub(0, m_arena_pos).is_super(s));1182auto pos = (s.str - m_arena.str);1183substr r(next_arena.str + pos, s.len);1184RYML_ASSERT(r.str - next_arena.str == pos);1185RYML_ASSERT(next_arena.sub(0, m_arena_pos).is_super(r));1186return r;1187}11881189public:11901191/** @name lookup */1192/** @{ */11931194struct lookup_result1195{1196size_t target;1197size_t closest;1198size_t path_pos;1199csubstr path;12001201inline operator bool() const { return target != NONE; }12021203lookup_result() : target(NONE), closest(NONE), path_pos(0), path() {}1204lookup_result(csubstr path_, size_t start) : target(NONE), closest(start), path_pos(0), path(path_) {}12051206/** get the part ot the input path that was resolved */1207csubstr resolved() const;1208/** get the part ot the input path that was unresolved */1209csubstr unresolved() const;1210};12111212/** for example foo.bar[0].baz */1213lookup_result lookup_path(csubstr path, size_t start=NONE) const;12141215/** defaulted lookup: lookup @p path; if the lookup fails, recursively modify1216* the tree so that the corresponding lookup_path() would return the1217* default value.1218* @see lookup_path() */1219size_t lookup_path_or_modify(csubstr default_value, csubstr path, size_t start=NONE);12201221/** defaulted lookup: lookup @p path; if the lookup fails, recursively modify1222* the tree so that the corresponding lookup_path() would return the1223* branch @p src_node (from the tree @p src).1224* @see lookup_path() */1225size_t lookup_path_or_modify(Tree const *src, size_t src_node, csubstr path, size_t start=NONE);12261227/** @} */12281229private:12301231struct _lookup_path_token1232{1233csubstr value;1234NodeType type;1235_lookup_path_token() : value(), type() {}1236_lookup_path_token(csubstr v, NodeType t) : value(v), type(t) {}1237inline operator bool() const { return type != NOTYPE; }1238bool is_index() const { return value.begins_with('[') && value.ends_with(']'); }1239};12401241size_t _lookup_path_or_create(csubstr path, size_t start);12421243void _lookup_path (lookup_result *r) const;1244void _lookup_path_modify(lookup_result *r);12451246size_t _next_node (lookup_result *r, _lookup_path_token *parent) const;1247size_t _next_node_modify(lookup_result *r, _lookup_path_token *parent);12481249void _advance(lookup_result *r, size_t more) const;12501251_lookup_path_token _next_token(lookup_result *r, _lookup_path_token const& parent) const;12521253private:12541255void _clear();1256void _free();1257void _copy(Tree const& that);1258void _move(Tree & that);12591260void _relocate(substr next_arena);12611262public:12631264#if ! RYML_USE_ASSERT1265C4_ALWAYS_INLINE void _check_next_flags(size_t, type_bits) {}1266#else1267void _check_next_flags(size_t node, type_bits f)1268{1269auto n = _p(node);1270type_bits o = n->m_type; // old1271C4_UNUSED(o);1272if(f & MAP)1273{1274RYML_ASSERT_MSG((f & SEQ) == 0, "cannot mark simultaneously as map and seq");1275RYML_ASSERT_MSG((f & VAL) == 0, "cannot mark simultaneously as map and val");1276RYML_ASSERT_MSG((o & SEQ) == 0, "cannot turn a seq into a map; clear first");1277RYML_ASSERT_MSG((o & VAL) == 0, "cannot turn a val into a map; clear first");1278}1279else if(f & SEQ)1280{1281RYML_ASSERT_MSG((f & MAP) == 0, "cannot mark simultaneously as seq and map");1282RYML_ASSERT_MSG((f & VAL) == 0, "cannot mark simultaneously as seq and val");1283RYML_ASSERT_MSG((o & MAP) == 0, "cannot turn a map into a seq; clear first");1284RYML_ASSERT_MSG((o & VAL) == 0, "cannot turn a val into a seq; clear first");1285}1286if(f & KEY)1287{1288RYML_ASSERT(!is_root(node));1289auto pid = parent(node); C4_UNUSED(pid);1290RYML_ASSERT(is_map(pid));1291}1292if((f & VAL) && !is_root(node))1293{1294auto pid = parent(node); C4_UNUSED(pid);1295RYML_ASSERT(is_map(pid) || is_seq(pid));1296}1297}1298#endif12991300inline void _set_flags(size_t node, NodeType_e f) { _check_next_flags(node, f); _p(node)->m_type = f; }1301inline void _set_flags(size_t node, type_bits f) { _check_next_flags(node, f); _p(node)->m_type = f; }13021303inline 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; }1304inline 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; }13051306inline 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; }1307inline 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; }13081309void _set_key(size_t node, csubstr key, type_bits more_flags=0)1310{1311_p(node)->m_key.scalar = key;1312_add_flags(node, KEY|more_flags);1313}1314void _set_key(size_t node, NodeScalar const& key, type_bits more_flags=0)1315{1316_p(node)->m_key = key;1317_add_flags(node, KEY|more_flags);1318}13191320void _set_val(size_t node, csubstr val, type_bits more_flags=0)1321{1322RYML_ASSERT(num_children(node) == 0);1323RYML_ASSERT(!is_seq(node) && !is_map(node));1324_p(node)->m_val.scalar = val;1325_add_flags(node, VAL|more_flags);1326}1327void _set_val(size_t node, NodeScalar const& val, type_bits more_flags=0)1328{1329RYML_ASSERT(num_children(node) == 0);1330RYML_ASSERT( ! is_container(node));1331_p(node)->m_val = val;1332_add_flags(node, VAL|more_flags);1333}13341335void _set(size_t node, NodeInit const& i)1336{1337RYML_ASSERT(i._check());1338NodeData *n = _p(node);1339RYML_ASSERT(n->m_key.scalar.empty() || i.key.scalar.empty() || i.key.scalar == n->m_key.scalar);1340_add_flags(node, i.type);1341if(n->m_key.scalar.empty())1342{1343if( ! i.key.scalar.empty())1344{1345_set_key(node, i.key.scalar);1346}1347}1348n->m_key.tag = i.key.tag;1349n->m_val = i.val;1350}13511352void _set_parent_as_container_if_needed(size_t in)1353{1354NodeData const* n = _p(in);1355size_t ip = parent(in);1356if(ip != NONE)1357{1358if( ! (is_seq(ip) || is_map(ip)))1359{1360if((in == first_child(ip)) && (in == last_child(ip)))1361{1362if( ! n->m_key.empty() || has_key(in))1363{1364_add_flags(ip, MAP);1365}1366else1367{1368_add_flags(ip, SEQ);1369}1370}1371}1372}1373}13741375void _seq2map(size_t node)1376{1377RYML_ASSERT(is_seq(node));1378for(size_t i = first_child(node); i != NONE; i = next_sibling(i))1379{1380NodeData *C4_RESTRICT ch = _p(i);1381if(ch->m_type.is_keyval())1382continue;1383ch->m_type.add(KEY);1384ch->m_key = ch->m_val;1385}1386auto *C4_RESTRICT n = _p(node);1387n->m_type.rem(SEQ);1388n->m_type.add(MAP);1389}13901391size_t _do_reorder(size_t *node, size_t count);13921393void _swap(size_t n_, size_t m_);1394void _swap_props(size_t n_, size_t m_);1395void _swap_hierarchy(size_t n_, size_t m_);1396void _copy_hierarchy(size_t dst_, size_t src_);13971398inline void _copy_props(size_t dst_, size_t src_)1399{1400_copy_props(dst_, this, src_);1401}14021403inline void _copy_props_wo_key(size_t dst_, size_t src_)1404{1405_copy_props_wo_key(dst_, this, src_);1406}14071408void _copy_props(size_t dst_, Tree const* that_tree, size_t src_)1409{1410auto & C4_RESTRICT dst = *_p(dst_);1411auto const& C4_RESTRICT src = *that_tree->_p(src_);1412dst.m_type = src.m_type;1413dst.m_key = src.m_key;1414dst.m_val = src.m_val;1415}14161417void _copy_props_wo_key(size_t dst_, Tree const* that_tree, size_t src_)1418{1419auto & C4_RESTRICT dst = *_p(dst_);1420auto const& C4_RESTRICT src = *that_tree->_p(src_);1421dst.m_type = (src.m_type & ~_KEYMASK) | (dst.m_type & _KEYMASK);1422dst.m_val = src.m_val;1423}14241425inline void _clear_type(size_t node)1426{1427_p(node)->m_type = NOTYPE;1428}14291430inline void _clear(size_t node)1431{1432auto *C4_RESTRICT n = _p(node);1433n->m_type = NOTYPE;1434n->m_key.clear();1435n->m_val.clear();1436n->m_parent = NONE;1437n->m_first_child = NONE;1438n->m_last_child = NONE;1439}14401441inline void _clear_key(size_t node)1442{1443_p(node)->m_key.clear();1444_rem_flags(node, KEY);1445}14461447inline void _clear_val(size_t node)1448{1449_p(node)->m_val.clear();1450_rem_flags(node, VAL);1451}14521453private:14541455void _clear_range(size_t first, size_t num);14561457size_t _claim();1458void _claim_root();1459void _release(size_t node);1460void _free_list_add(size_t node);1461void _free_list_rem(size_t node);14621463void _set_hierarchy(size_t node, size_t parent, size_t after_sibling);1464void _rem_hierarchy(size_t node);14651466public:14671468// members are exposed, but you should NOT access them directly14691470NodeData * m_buf;1471size_t m_cap;14721473size_t m_size;14741475size_t m_free_head;1476size_t m_free_tail;14771478substr m_arena;1479size_t m_arena_pos;14801481Callbacks m_callbacks;14821483TagDirective m_tag_directives[RYML_MAX_TAG_DIRECTIVES];14841485};14861487} // namespace yml1488} // namespace c4148914901491C4_SUPPRESS_WARNING_MSVC_POP1492C4_SUPPRESS_WARNING_GCC_CLANG_POP149314941495#endif /* _C4_YML_TREE_HPP_ */149614971498