1 #ifndef _C4_YML_TREE_HPP_
2 #define _C4_YML_TREE_HPP_
6 #include "c4/error.hpp"
7 #include "c4/types.hpp"
8 #ifndef _C4_YML_FWD_HPP_
11 #ifndef _C4_YML_COMMON_HPP_
14 #ifndef C4_YML_NODE_TYPE_HPP_
17 #ifndef _C4_YML_TAG_HPP_
20 #ifndef _C4_CHARCONV_HPP_
28 C4_SUPPRESS_WARNING_MSVC_PUSH
29 C4_SUPPRESS_WARNING_MSVC(4251)
30 C4_SUPPRESS_WARNING_MSVC(4296)
31 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
32 C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
33 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
34 C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
40 template<
class T>
inline auto read(Tree
const* C4_RESTRICT tree,
id_type id, T *v) ->
typename std::enable_if<!std::is_arithmetic<T>::value,
bool>::type;
41 template<
class T>
inline auto read(Tree
const* C4_RESTRICT tree,
id_type id, T *v) ->
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value,
bool>::type;
42 template<
class T>
inline auto read(Tree
const* C4_RESTRICT tree,
id_type id, T *v) ->
typename std::enable_if<std::is_floating_point<T>::value,
bool>::type;
44 template<
class T>
inline auto readkey(Tree
const* C4_RESTRICT tree,
id_type id, T *v) ->
typename std::enable_if<!std::is_arithmetic<T>::value,
bool>::type;
45 template<
class T>
inline auto readkey(Tree
const* C4_RESTRICT tree,
id_type id, T *v) ->
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value,
bool>::type;
46 template<
class T>
inline auto readkey(Tree
const* C4_RESTRICT tree,
id_type id, T *v) ->
typename std::enable_if<std::is_floating_point<T>::value,
bool>::type;
76 NodeScalar(
const char (&s)[N]) noexcept : tag(), scalar(s), anchor() {}
77 NodeScalar(csubstr s ) noexcept : tag(), scalar(s), anchor() {}
80 template<
size_t N,
size_t M>
81 NodeScalar(
const char (&t)[N],
const char (&s)[N]) noexcept : tag(t), scalar(s), anchor() {}
82 NodeScalar(csubstr t , csubstr s ) noexcept : tag(t), scalar(s), anchor() {}
94 bool empty() const noexcept {
return tag.
empty() && scalar.empty() && anchor.empty(); }
96 void clear() noexcept { tag.clear(); scalar.clear(); anchor.clear(); }
100 csubstr trimmed = ref.begins_with(
'*') ? ref.sub(1) : ref;
102 if((!has_scalar) || !scalar.ends_with(trimmed))
106 C4_MUST_BE_TRIVIAL_COPY(NodeScalar);
149 type = (type|more_flags);
150 if( !
key.tag.empty())
152 if( ! val.
tag.empty())
154 if( !
key.anchor.empty())
163 RYML_ASSERT(
key.scalar.empty() == ((type &
KEY) == 0));
165 RYML_ASSERT(
key.tag.empty() == ((type &
KEYTAG) == 0));
167 RYML_ASSERT(((type &
VAL) != 0) || val.
scalar.empty());
169 RYML_ASSERT(val.
tag.empty() == ((type &
VALTAG) == 0));
218 Tree& operator= (
Tree && that) noexcept;
227 void reserve(
id_type node_capacity);
235 bool empty()
const {
return m_size == 0; }
239 id_type slack()
const { RYML_ASSERT(m_cap >= m_size);
return m_cap - m_size; }
257 _RYML_CB_ASSERT(m_callbacks, n >= m_buf && n < m_buf + m_cap);
258 return static_cast<id_type>(n - m_buf);
267 _RYML_CB_ASSERT(m_callbacks, node >= 0 && node < m_cap);
276 _RYML_CB_ASSERT(m_callbacks, node >= 0 && node < m_cap);
282 NodeData *
_p(
id_type node) { _RYML_CB_ASSERT(m_callbacks, node !=
NONE && node >= 0 && node < m_cap);
return m_buf + node; }
285 NodeData const *
_p(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, node !=
NONE && node >= 0 && node < m_cap);
return m_buf + node; }
288 id_type root_id() {
if(m_cap == 0) { reserve(16); } _RYML_CB_ASSERT(m_callbacks, m_cap > 0 && m_size > 0);
return 0; }
290 id_type root_id()
const { _RYML_CB_ASSERT(m_callbacks, m_cap > 0 && m_size > 0);
return 0; }
338 const char*
type_str(
id_type node)
const {
return NodeType::type_str(_p(node)->m_type); }
340 csubstr
const&
key (
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_key(node));
return _p(node)->m_key.scalar; }
341 csubstr
const&
key_tag (
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_key_tag(node));
return _p(node)->m_key.tag; }
342 csubstr
const&
key_ref (
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, is_key_ref(node));
return _p(node)->m_key.anchor; }
343 csubstr
const&
key_anchor(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_key_anchor(node));
return _p(node)->m_key.anchor; }
346 csubstr
const&
val (
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_val(node));
return _p(node)->m_val.scalar; }
347 csubstr
const&
val_tag (
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_val_tag(node));
return _p(node)->m_val.tag; }
348 csubstr
const&
val_ref (
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, is_val_ref(node));
return _p(node)->m_val.anchor; }
349 csubstr
const&
val_anchor(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_val_anchor(node));
return _p(node)->m_val.anchor; }
363 C4_ALWAYS_INLINE
bool is_stream(
id_type node)
const {
return _p(node)->m_type.is_stream(); }
364 C4_ALWAYS_INLINE
bool is_doc(
id_type node)
const {
return _p(node)->m_type.is_doc(); }
366 C4_ALWAYS_INLINE
bool is_map(
id_type node)
const {
return _p(node)->m_type.is_map(); }
367 C4_ALWAYS_INLINE
bool is_seq(
id_type node)
const {
return _p(node)->m_type.is_seq(); }
368 C4_ALWAYS_INLINE
bool has_key(
id_type node)
const {
return _p(node)->m_type.has_key(); }
369 C4_ALWAYS_INLINE
bool has_val(
id_type node)
const {
return _p(node)->m_type.has_val(); }
370 C4_ALWAYS_INLINE
bool is_val(
id_type node)
const {
return _p(node)->m_type.is_val(); }
371 C4_ALWAYS_INLINE
bool is_keyval(
id_type node)
const {
return _p(node)->m_type.is_keyval(); }
379 C4_ALWAYS_INLINE
bool is_ref(
id_type node)
const {
return _p(node)->m_type.is_ref(); }
381 C4_ALWAYS_INLINE
bool parent_is_seq(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_parent(node));
return is_seq(_p(node)->m_parent); }
382 C4_ALWAYS_INLINE
bool parent_is_map(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_parent(node));
return is_map(_p(node)->m_parent); }
385 C4_ALWAYS_INLINE
bool has_anchor(
id_type node, csubstr a)
const {
return _p(node)->m_key.anchor == a || _p(node)->m_val.anchor == a; }
390 C4_ALWAYS_INLINE
bool key_is_null(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_key(node));
NodeData const* C4_RESTRICT n = _p(node);
return !n->m_type.is_key_quoted() && (n->m_type.key_is_null() ||
scalar_is_null(n->m_key.scalar)); }
394 C4_ALWAYS_INLINE
bool val_is_null(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_val(node));
NodeData const* C4_RESTRICT n = _p(node);
return !n->m_type.is_val_quoted() && (n->m_type.val_is_null() ||
scalar_is_null(n->m_val.scalar)); }
403 RYML_DEPRECATED(
"use has_key_anchor()") bool is_key_anchor(
id_type node)
const {
return _p(node)->m_type.has_key_anchor(); }
404 RYML_DEPRECATED(
"use has_val_anchor()") bool is_val_anchor(
id_type node)
const {
return _p(node)->m_type.has_val_anchor(); }
405 RYML_DEPRECATED(
"use has_anchor()") bool is_anchor(
id_type node)
const {
return _p(node)->m_type.has_anchor(); }
406 RYML_DEPRECATED(
"use has_anchor_or_ref()") bool is_anchor_or_ref(
id_type node)
const {
return _p(node)->m_type.has_anchor() || _p(node)->m_type.is_ref(); }
415 bool is_root(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, _p(node)->m_parent !=
NONE || node == 0);
return _p(node)->m_parent ==
NONE; }
420 bool empty(
id_type node)
const {
return ! has_children(node) && _p(node)->m_key.empty() && (( ! (_p(node)->m_type &
VAL)) || _p(node)->m_val.empty()); }
445 RYML_DEPRECATED(
"use has_other_siblings()") static
bool has_siblings(
id_type ) {
return true; }
491 C4_ALWAYS_INLINE
bool is_block(
id_type node)
const {
return _p(node)->m_type.is_block(); }
494 C4_ALWAYS_INLINE
bool is_flow(
id_type node)
const {
return _p(node)->m_type.is_flow(); }
510 C4_ALWAYS_INLINE
bool is_quoted(
id_type node)
const {
return _p(node)->m_type.is_quoted(); }
532 void set_key(
id_type node, csubstr
key) { _RYML_CB_ASSERT(m_callbacks, has_key(node)); _p(node)->m_key.scalar =
key; }
533 void set_val(
id_type node, csubstr val) { _RYML_CB_ASSERT(m_callbacks, has_val(node)); _p(node)->m_val.scalar = val; }
535 void set_key_tag(
id_type node, csubstr tag) { _RYML_CB_ASSERT(m_callbacks, has_key(node)); _p(node)->m_key.tag = tag; _add_flags(node,
KEYTAG); }
536 void set_val_tag(
id_type node, csubstr tag) { _RYML_CB_ASSERT(m_callbacks, has_val(node) || is_container(node)); _p(node)->m_val.tag = tag; _add_flags(node,
VALTAG); }
538 void set_key_anchor(
id_type node, csubstr anchor) { _RYML_CB_ASSERT(m_callbacks, ! is_key_ref(node)); _p(node)->m_key.anchor = anchor.triml(
'&'); _add_flags(node,
KEYANCH); }
539 void set_val_anchor(
id_type node, csubstr anchor) { _RYML_CB_ASSERT(m_callbacks, ! is_val_ref(node)); _p(node)->m_val.anchor = anchor.triml(
'&'); _add_flags(node,
VALANCH); }
540 void set_key_ref (
id_type node, csubstr ref ) { _RYML_CB_ASSERT(m_callbacks, ! 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); }
541 void set_val_ref (
id_type node, csubstr ref ) { _RYML_CB_ASSERT(m_callbacks, ! 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); }
596 void normalize_tags();
597 void normalize_tags_long();
599 id_type num_tag_directives()
const;
600 bool add_tag_directive(csubstr directive);
602 void clear_tag_directives();
606 size_t resolve_tag(substr output, csubstr tag,
id_type node_id)
const;
609 size_t needed = resolve_tag(output, tag, node_id);
610 return needed <= output.len ? output.first(needed) : output;
632 _RYML_CB_ASSERT(m_callbacks, parent !=
NONE);
633 _RYML_CB_ASSERT(m_callbacks, is_container(parent) || is_root(parent));
634 _RYML_CB_ASSERT(m_callbacks, after ==
NONE || (_p(after)->m_parent == parent));
636 _set_hierarchy(child, parent, after);
646 _set_hierarchy(child, parent, _p(parent)->m_last_child);
652 #if defined(__clang__)
653 # pragma clang diagnostic push
654 # pragma clang diagnostic ignored "-Wnull-dereference"
655 #elif defined(__GNUC__)
656 # pragma GCC diagnostic push
658 # pragma GCC diagnostic ignored "-Wnull-dereference"
665 return insert_child(_p(node)->m_parent, after);
676 remove_children(node);
681 void remove_children(
id_type node);
696 return change_type(node, (
NodeType)type);
699 #if defined(__clang__)
700 # pragma clang diagnostic pop
701 #elif defined(__GNUC__)
702 # pragma GCC diagnostic pop
738 void set_root_as_stream();
782 RYML_DEPRECATED(
"use arena_size() instead") size_t arena_pos()
const {
return m_arena_pos; }
788 size_t arena_slack()
const { _RYML_CB_ASSERT(m_callbacks, m_arena.len >= m_arena_pos);
return m_arena.len - m_arena_pos; }
791 csubstr
arena()
const {
return m_arena.first(m_arena_pos); }
793 substr
arena() {
return m_arena.first(m_arena_pos); }
798 return m_arena.is_super(s);
813 ->
typename std::enable_if<std::is_floating_point<T>::value, csubstr>::type
815 substr rem(m_arena.sub(m_arena_pos));
819 rem = _grow_arena(num);
821 _RYML_CB_ASSERT(m_callbacks, num <= rem.len);
823 rem = _request_span(num);
839 ->
typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type
841 substr rem(m_arena.sub(m_arena_pos));
845 rem = _grow_arena(num);
847 _RYML_CB_ASSERT(m_callbacks, num <= rem.len);
849 rem = _request_span(num);
867 substr rem(m_arena.sub(m_arena_pos));
871 rem = _grow_arena(num);
873 _RYML_CB_ASSERT(m_callbacks, num <= rem.len);
875 return _request_span(num);
883 else if(m_arena.str ==
nullptr)
891 return _request_span(0);
898 C4_ALWAYS_INLINE
static csubstr
to_arena(std::nullptr_t)
917 substr cp = alloc_arena(s.len);
918 _RYML_CB_ASSERT(m_callbacks, cp.len == s.len);
919 _RYML_CB_ASSERT(m_callbacks, !s.overlaps(cp));
920 #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
921 C4_SUPPRESS_WARNING_GCC_PUSH
922 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow=")
923 C4_SUPPRESS_WARNING_GCC(
"-Wrestrict")
926 memcpy(cp.str, s.str, s.len);
927 #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
928 C4_SUPPRESS_WARNING_GCC_POP
945 if(sz > arena_slack())
946 _grow_arena(sz - arena_slack());
947 substr s = _request_span(sz);
957 if(arena_cap > m_arena.len)
960 buf.str = (
char*) m_callbacks.m_allocate(arena_cap, m_arena.str, m_callbacks.m_user_data);
964 _RYML_CB_ASSERT(m_callbacks, m_arena.len >= 0);
966 m_callbacks.m_free(m_arena.str, m_arena.len, m_callbacks.m_user_data);
976 substr _grow_arena(
size_t more)
978 size_t cap = m_arena.len + more;
979 cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap;
980 cap = cap < 64 ? 64 : cap;
982 return m_arena.sub(m_arena_pos);
985 substr _request_span(
size_t sz)
987 _RYML_CB_ASSERT(m_callbacks, m_arena_pos + sz <= m_arena.len);
989 s = m_arena.sub(m_arena_pos, sz);
994 substr _relocated(csubstr s, substr next_arena)
const
996 _RYML_CB_ASSERT(m_callbacks, m_arena.is_super(s));
997 _RYML_CB_ASSERT(m_callbacks, m_arena.sub(0, m_arena_pos).is_super(s));
998 auto pos = (s.str - m_arena.str);
999 substr r(next_arena.str + pos, s.len);
1000 _RYML_CB_ASSERT(m_callbacks, r.str - next_arena.str == pos);
1001 _RYML_CB_ASSERT(m_callbacks, next_arena.sub(0, m_arena_pos).is_super(r));
1017 operator bool()
const {
return target !=
NONE; }
1023 csubstr resolved()
const;
1025 csubstr unresolved()
const;
1029 lookup_result lookup_path(csubstr path,
id_type start=
NONE)
const;
1035 id_type lookup_path_or_modify(csubstr default_value, csubstr path,
id_type start=
NONE);
1047 struct _lookup_path_token
1051 _lookup_path_token() : value(), type() {}
1052 _lookup_path_token(csubstr v, NodeType t) : value(v), type(t) {}
1053 operator bool()
const {
return type !=
NOTYPE; }
1054 bool is_index()
const {
return value.begins_with(
'[') && value.ends_with(
']'); }
1059 void _lookup_path (lookup_result *r)
const;
1060 void _lookup_path_modify(lookup_result *r);
1062 id_type _next_node (lookup_result *r, _lookup_path_token *parent)
const;
1063 id_type _next_node_modify(lookup_result *r, _lookup_path_token *parent);
1065 static void _advance(lookup_result *r,
size_t more);
1067 _lookup_path_token _next_token(lookup_result *r, _lookup_path_token
const& parent)
const;
1073 void _copy(Tree
const& that);
1074 void _move(Tree & that) noexcept;
1076 void _relocate(substr next_arena);
1082 #if ! RYML_USE_ASSERT
1087 NodeData *n = _p(node);
1092 RYML_ASSERT_MSG((f &
SEQ) == 0,
"cannot mark simultaneously as map and seq");
1093 RYML_ASSERT_MSG((f &
VAL) == 0,
"cannot mark simultaneously as map and val");
1094 RYML_ASSERT_MSG((o &
SEQ) == 0,
"cannot turn a seq into a map; clear first");
1095 RYML_ASSERT_MSG((o &
VAL) == 0,
"cannot turn a val into a map; clear first");
1099 RYML_ASSERT_MSG((f &
MAP) == 0,
"cannot mark simultaneously as seq and map");
1100 RYML_ASSERT_MSG((f &
VAL) == 0,
"cannot mark simultaneously as seq and val");
1101 RYML_ASSERT_MSG((o &
MAP) == 0,
"cannot turn a map into a seq; clear first");
1102 RYML_ASSERT_MSG((o &
VAL) == 0,
"cannot turn a val into a seq; clear first");
1106 _RYML_CB_ASSERT(m_callbacks, !is_root(node));
1107 auto pid = parent(node); C4_UNUSED(pid);
1108 _RYML_CB_ASSERT(m_callbacks, is_map(pid));
1110 if((f &
VAL) && !is_root(node))
1112 auto pid = parent(node); C4_UNUSED(pid);
1113 _RYML_CB_ASSERT(m_callbacks, is_map(pid) || is_seq(pid));
1118 void _set_flags(
id_type node,
NodeType_e f) { _check_next_flags(node, f); _p(node)->m_type = f; }
1119 void _set_flags(
id_type node,
type_bits f) { _check_next_flags(node, f); _p(node)->m_type = f; }
1122 void _add_flags(
id_type node,
type_bits f) { NodeData *d = _p(node); f |= d->m_type; _check_next_flags(node, f); d->m_type = f; }
1125 void _rem_flags(
id_type node,
type_bits f) { NodeData *d = _p(node); f = d->m_type & ~f; _check_next_flags(node, f); d->m_type = f; }
1129 _p(node)->m_key.scalar =
key;
1130 _add_flags(node,
KEY|more_flags);
1134 _p(node)->m_key =
key;
1135 _add_flags(node,
KEY|more_flags);
1140 _RYML_CB_ASSERT(m_callbacks, num_children(node) == 0);
1141 _RYML_CB_ASSERT(m_callbacks, !is_seq(node) && !is_map(node));
1142 _p(node)->m_val.scalar = val;
1143 _add_flags(node,
VAL|more_flags);
1147 _RYML_CB_ASSERT(m_callbacks, num_children(node) == 0);
1148 _RYML_CB_ASSERT(m_callbacks, ! is_container(node));
1149 _p(node)->m_val = val;
1150 _add_flags(node,
VAL|more_flags);
1153 void _set(
id_type node, NodeInit
const& i)
1155 _RYML_CB_ASSERT(m_callbacks, i._check());
1156 NodeData *n = _p(node);
1157 _RYML_CB_ASSERT(m_callbacks, n->m_key.scalar.empty() || i.key.scalar.empty() || i.key.scalar == n->m_key.scalar);
1158 _add_flags(node, i.type);
1159 if(n->m_key.scalar.empty())
1161 if( ! i.key.scalar.empty())
1163 _set_key(node, i.key.scalar);
1166 n->m_key.tag = i.key.tag;
1170 void _set_parent_as_container_if_needed(
id_type in)
1172 NodeData
const* n = _p(in);
1176 if( ! (is_seq(ip) || is_map(ip)))
1178 if((in == first_child(ip)) && (in == last_child(ip)))
1180 if( ! n->m_key.empty() || has_key(in))
1182 _add_flags(ip,
MAP);
1186 _add_flags(ip,
SEQ);
1195 _RYML_CB_ASSERT(m_callbacks, is_seq(node));
1196 for(
id_type i = first_child(node); i !=
NONE; i = next_sibling(i))
1198 NodeData *C4_RESTRICT ch = _p(i);
1199 if(ch->m_type.is_keyval())
1201 ch->m_type.add(
KEY);
1202 ch->m_key = ch->m_val;
1204 auto *C4_RESTRICT n = _p(node);
1218 _copy_props(dst_,
this, src_);
1223 _copy_props_wo_key(dst_,
this, src_);
1226 void _copy_props(
id_type dst_, Tree
const* that_tree,
id_type src_)
1228 auto & C4_RESTRICT dst = *_p(dst_);
1229 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1230 dst.m_type = src.m_type;
1231 dst.m_key = src.m_key;
1232 dst.m_val = src.m_val;
1237 auto & C4_RESTRICT dst = *_p(dst_);
1238 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1239 dst.m_type = (src.m_type & src_mask) | (dst.m_type & ~src_mask);
1240 dst.m_key = src.m_key;
1241 dst.m_val = src.m_val;
1244 void _copy_props_wo_key(
id_type dst_, Tree
const* that_tree,
id_type src_)
1246 auto & C4_RESTRICT dst = *_p(dst_);
1247 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1249 dst.m_val = src.m_val;
1254 auto & C4_RESTRICT dst = *_p(dst_);
1255 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1256 dst.m_type = (src.m_type & ((~
_KEYMASK)|src_mask)) | (dst.m_type & (
_KEYMASK|~src_mask));
1257 dst.m_val = src.m_val;
1260 void _clear_type(
id_type node)
1262 _p(node)->m_type =
NOTYPE;
1267 auto *C4_RESTRICT n = _p(node);
1272 n->m_first_child =
NONE;
1273 n->m_last_child =
NONE;
1278 _p(node)->m_key.clear();
1279 _rem_flags(node,
KEY);
1284 _p(node)->m_val.clear();
1285 _rem_flags(node,
VAL);
1299 void _free_list_add(
id_type node);
1300 void _free_list_rem(
id_type node);
1303 void _rem_hierarchy(
id_type node);
1345 ->
typename std::enable_if<!std::is_arithmetic<T>::value,
bool>::type
1347 return C4_LIKELY(!(tree->type(
id) &
VALNIL)) ?
from_chars(tree->val(
id), v) :
false;
1356 ->
typename std::enable_if<!std::is_arithmetic<T>::value,
bool>::type
1358 return C4_LIKELY(!(tree->type(
id) &
KEYNIL)) ?
from_chars(tree->key(
id), v) :
false;
1370 inline auto read(Tree
const* C4_RESTRICT tree,
id_type id, T *v)
1371 ->
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value,
bool>::type
1373 using U =
typename std::remove_cv<T>::type;
1374 enum { ischar = std::is_same<char, U>::value || std::is_same<signed char, U>::value || std::is_same<unsigned char, U>::value };
1375 csubstr val = tree->val(
id);
1376 NodeType ty = tree->type(
id);
1377 if(C4_UNLIKELY((ty &
VALNIL) || val.empty()))
1381 char first = val[0];
1382 if(ty.is_val_quoted() && (first !=
'0' && !ischar))
1384 else if(first ==
'+')
1395 inline auto readkey(Tree
const* C4_RESTRICT tree,
id_type id, T *v)
1396 ->
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value,
bool>::type
1398 using U =
typename std::remove_cv<T>::type;
1399 enum { ischar = std::is_same<char, U>::value || std::is_same<signed char, U>::value || std::is_same<unsigned char, U>::value };
1400 csubstr
key = tree->key(
id);
1401 NodeType ty = tree->type(
id);
1406 char first =
key[0];
1407 if(ty.is_key_quoted() && (first !=
'0' && !ischar))
1409 else if(first ==
'+')
1421 static_assert(std::is_floating_point<T>::value,
"must be floating point");
1422 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(
"-Wfloat-equal");
1423 if(C4_UNLIKELY(std::isnan(val)))
1424 return to_chars(buf, csubstr(
".nan"));
1425 else if(C4_UNLIKELY(val == std::numeric_limits<T>::infinity()))
1426 return to_chars(buf, csubstr(
".inf"));
1427 else if(C4_UNLIKELY(val == -std::numeric_limits<T>::infinity()))
1428 return to_chars(buf, csubstr(
"-.inf"));
1430 C4_SUPPRESS_WARNING_GCC_CLANG_POP
1439 static_assert(std::is_floating_point<T>::value,
"must be floating point");
1440 if(buf.begins_with(
'+'))
1448 else if(C4_UNLIKELY(buf ==
".nan" || buf ==
".NaN" || buf ==
".NAN"))
1450 *val = std::numeric_limits<T>::quiet_NaN();
1453 else if(C4_UNLIKELY(buf ==
".inf" || buf ==
".Inf" || buf ==
".INF"))
1455 *val = std::numeric_limits<T>::infinity();
1458 else if(C4_UNLIKELY(buf ==
"-.inf" || buf ==
"-.Inf" || buf ==
"-.INF"))
1460 *val = -std::numeric_limits<T>::infinity();
1492 typename std::enable_if<std::is_floating_point<T>::value,
bool>::type
1495 csubstr val = tree->val(
id);
1522 typename std::enable_if<std::is_floating_point<T>::value,
bool>::type
1525 csubstr
key = tree->key(
id);
1538 C4_SUPPRESS_WARNING_MSVC_POP
1539 C4_SUPPRESS_WARNING_GCC_CLANG_POP
Lightweight generic type-safe wrappers for converting individual values to/from strings.
Holds a pointer to an existing tree, and a node id.
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
csubstr to_arena(csubstr a)
serialize the given csubstr to the tree's arena, growing the arena as needed to accomodate the serial...
id_type num_other_siblings(id_type node) const
does not count with this
void reserve_arena(size_t arena_cap)
ensure the tree's internal string arena is at least the given capacity
bool has_sibling(id_type node, csubstr key) const
true if one of the node's siblings has the given key
csubstr resolve_tag_sub(substr output, csubstr tag, id_type node_id) const
bool is_key_plain(id_type node) const
id_type num_siblings(id_type node) const
O(num_siblings)
id_type first_child(id_type node) const
bool is_stream(id_type node) const
NodeType type(id_type node) const
id_type append_sibling(id_type node)
void set_val_ref(id_type node, csubstr ref)
id_type root_id() const
Get the id of the root node.
size_t arena_slack() const
get the current slack of the tree's internal arena
bool has_key_tag(id_type node) const
id_type prev_sibling(id_type node) const
auto to_arena(T const &a) -> typename std::enable_if< std::is_floating_point< T >::value, csubstr >::type
serialize the given floating-point variable to the tree's arena, growing it as needed to accomodate t...
NodeData * get(id_type node)
get a pointer to a node's NodeData. i can be NONE, in which case a nullptr is returned
id_type prepend_sibling(id_type node)
create and insert a node as the first node of parent
bool is_map(id_type node) const
csubstr const & key_ref(id_type node) const
void callbacks(Callbacks const &cb)
id_type sibling(id_type node, id_type pos) const
c4::yml::TagDirectiveRange tag_directives() const
bool is_key_quoted(id_type node) const
bool has_val_anchor(id_type node) const
bool is_root(id_type node) const
bool key_is_null(id_type node) const
true if the node key is empty, or its scalar verifies scalar_is_null().
substr alloc_arena(size_t sz)
grow the tree's string arena by the given size and return a substr of the added portion
bool is_keyval(id_type node) const
bool is_val_folded(id_type node) const
id_type last_sibling(id_type node) const
bool has_key(id_type node) const
bool has_other_siblings(id_type node) const
true if node is not a single child
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
id_type parent(id_type node) const
void set_key_style(id_type node, NodeType_e style)
bool is_key_unfiltered(id_type node) const
true if the key was a scalar requiring filtering and was left unfiltered during the parsing (see Pars...
void rem_key_anchor(id_type node)
Callbacks const & callbacks() const
TagDirective const * end_tag_directives() const
bool is_key_literal(id_type node) const
bool is_block(id_type node) const
id_type prepend_child(id_type parent)
create and insert a node as the first child of parent
bool is_val(id_type node) const
NodeScalar const & valsc(id_type node) const
bool type_has_any(id_type node, NodeType_e bits) const
bool is_container_styled(id_type node) const
csubstr to_arena(const char *s)
NodeData const * _p(id_type node) const
An if-less form of get() that demands a valid node index. This function is implementation only; use a...
id_type sibling_pos(id_type node, id_type sib) const
id_type append_child(id_type parent)
create and insert a node as the last child of parent
bool has_sibling(id_type node, id_type sib) const
true if node has a sibling with id sib
id_type next_sibling(id_type node) const
bool is_key_squo(id_type node) const
static csubstr to_arena(std::nullptr_t)
bool parent_is_seq(id_type node) const
csubstr const & key(id_type node) const
csubstr const & val_ref(id_type node) const
bool has_anchor(id_type node) const
id_type insert_sibling(id_type node, id_type after)
create and insert a new sibling of n. insert after "after"
bool has_val_tag(id_type node) const
bool is_key_dquo(id_type node) const
void set_key_tag(id_type node, csubstr tag)
TagDirective const * begin_tag_directives() const
void rem_anchor_ref(id_type node)
id_type last_child(id_type node) const
id_type id(NodeData const *n) const
get the index of a node belonging to this tree. n can be nullptr, in which case NONE is returned
NodeData const * get(id_type node) const
get a pointer to a node's NodeData. i can be NONE, in which case a nullptr is returned.
void set_val_anchor(id_type node, csubstr anchor)
bool is_val_plain(id_type node) const
bool is_doc(id_type node) const
bool has_child(id_type node, csubstr key) const
true if node has a child with key key
void set_container_style(id_type node, NodeType_e style)
bool is_key_ref(id_type node) const
void set_key(id_type node, csubstr key)
bool has_val(id_type node) const
bool is_val_dquo(id_type node) const
NodeScalar const & keysc(id_type node) const
size_t arena_capacity() const
get the current capacity of the tree's internal arena
csubstr const & key_anchor(id_type node) const
id_type doc(id_type i) const
gets the i document node index.
bool is_flow_ml(id_type node) const
bool is_key_folded(id_type node) const
csubstr const & val_tag(id_type node) const
bool type_has_all(id_type node, NodeType_e bits) const
void set_val(id_type node, csubstr val)
csubstr const & val(id_type node) const
id_type find_sibling(id_type node, csubstr const &key) const
bool is_seq(id_type node) const
bool is_val_styled(id_type node) const
bool parent_is_map(id_type node) const
bool has_key_anchor(id_type node) const
void remove(id_type node)
remove an entire branch at once: ie remove the children and the node itself
bool is_quoted(id_type node) const
bool change_type(id_type node, type_bits type)
Tree(id_type node_capacity, size_t arena_capacity=0)
csubstr const & key_tag(id_type node) const
void set_val_style(id_type node, NodeType_e style)
substr arena()
get the current arena
id_type root_id()
Get the id of the root node.
void rem_val_ref(id_type node)
bool has_parent(id_type node) const
bool val_is_null(id_type node) const
true if the node val is empty, or its scalar verifies scalar_is_null().
id_type first_sibling(id_type node) const
bool is_flow(id_type node) const
bool is_val_quoted(id_type node) const
TagDirective const * tag_directive_const_iterator
bool type_has_none(id_type node, NodeType_e bits) const
bool empty(id_type node) const
true when key and val are empty, and has no children
bool is_val_squo(id_type node) const
substr copy_to_arena(csubstr s)
copy the given substr to the tree's arena, growing it by the required size
csubstr arena() const
get the current arena
auto to_arena(T const &a) -> typename std::enable_if<!std::is_floating_point< T >::value, csubstr >::type
serialize the given non-floating-point variable to the tree's arena, growing it as needed to accomoda...
bool is_container(id_type node) const
size_t arena_size() const
get the current size of the tree's internal arena
bool is_ref(id_type node) const
void set_val_tag(id_type node, csubstr tag)
bool is_key_styled(id_type node) const
void set_key_anchor(id_type node, csubstr anchor)
bool is_val_ref(id_type node) const
void set_key_ref(id_type node, csubstr ref)
id_type _append_child__unprotected(id_type parent)
bool has_child(id_type node, id_type ch) const
true if node has a child with id ch
bool is_val_literal(id_type node) const
void rem_val_anchor(id_type node)
bool is_val_unfiltered(id_type node) const
true if the val was a scalar requiring filtering and was left unfiltered during the parsing (see Pars...
void rem_key_ref(id_type node)
csubstr const & val_anchor(id_type node) const
NodeData * _p(id_type node)
An if-less form of get() that demands a valid node index. This function is implementation only; use a...
bool is_flow_sl(id_type node) const
bool has_children(id_type node) const
true if node has any children key
bool has_anchor(id_type node, csubstr a) const
true when the node has an anchor named a
const char * type_str(id_type node) const
Common utilities and infrastructure used by ryml.
#define RYML_NOEXCEPT
Conditionally expands to noexcept when RYML_USE_ASSERT is 0 and is empty otherwise.
Callbacks const & get_callbacks()
get the global callbacks
bool scalar_is_null(csubstr s) noexcept
YAML-sense query of nullity.
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
NodeType_e
a bit mask for marking node types and styles
@ VALANCH
the val has an &anchor
@ NOTYPE
no node type or style is set
@ VALREF
a *reference: the val references an &anchor
@ VALNIL
the val is null (eg {a : } results in a null val)
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
@ KEYTAG
the key has a tag
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
@ VALTAG
the val has a tag
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
@ KEYREF
a *reference: the key references an &anchor
@ KEYANCH
the key has an &anchor
@ KEYNIL
the key is null (eg { : b} results in a null key)
bool from_chars(ryml::csubstr buf, vec2< T > *v)
size_t to_chars(ryml::substr buf, vec2< T > v)
size_t to_chars_float(substr buf, T val)
encode a floating point value to a string.
bool from_chars_float(csubstr buf, T *val)
decode a floating point from string.
csubstr to_csubstr(substr s) noexcept
neutral version for use in generic code
#define RYML_MAX_TAG_DIRECTIVES
the maximum number of tag directives in a Tree
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
std::enable_if< std::is_floating_point< T >::value, bool >::type read(Tree const *tree, id_type id, T *v)
convert the val of a scalar node to a floating point type, by forwarding its val to from_chars_float<...
std::enable_if< std::is_floating_point< T >::value, bool >::type readkey(Tree const *tree, id_type id, T *v)
convert the key of a scalar node to a floating point type, by forwarding its key to from_chars_float<...
a c-style callbacks class.
contains the data for each YAML node.
convenience class to initialize nodes
NodeInit(NodeScalar const &k, NodeScalar const &v)
initialize as a mapping member
NodeInit(NodeType_e t, NodeScalar const &k, NodeScalar const &v)
initialize as a mapping member with explicit type
void _add_flags(type_bits more_flags=0)
NodeInit(NodeScalar const &v, NodeType_e t)
initialize as a sequence member with explicit type
NodeInit(NodeScalar const &v)
initialize as a sequence member
NodeInit(NodeType_e t)
initialize as a typed node
NodeInit(NodeType_e t, NodeScalar const &k)
initialize as a mapping member with explicit type (eg for SEQ or MAP)
NodeInit()
initialize as an empty node
a node scalar is a csubstr, which may be tagged and anchored.
NodeScalar(const char(&t)[N], const char(&s)[N]) noexcept
initialize as a tagged scalar
bool empty() const noexcept
NodeScalar() noexcept
initialize as an empty scalar
NodeScalar(csubstr s) noexcept
NodeScalar(csubstr t, csubstr s) noexcept
NodeScalar(const char(&s)[N]) noexcept
initialize as an untagged scalar
~NodeScalar() noexcept=default
void set_ref_maybe_replacing_scalar(csubstr ref, bool has_scalar) RYML_NOEXCEPT
wraps a NodeType_e element with some syntactic sugar and predicates
Reusable object to resolve references/aliases in the tree.
lookup_result(csubstr path_, id_type start)