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; }
423 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()); }
448 RYML_DEPRECATED(
"use has_other_siblings()") static
bool has_siblings(
id_type ) {
return true; }
494 C4_ALWAYS_INLINE
bool is_block(
id_type node)
const {
return _p(node)->m_type.is_block(); }
497 C4_ALWAYS_INLINE
bool is_flow(
id_type node)
const {
return _p(node)->m_type.is_flow(); }
513 C4_ALWAYS_INLINE
bool is_quoted(
id_type node)
const {
return _p(node)->m_type.is_quoted(); }
515 C4_ALWAYS_INLINE
NodeType key_style(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_key(node));
return _p(node)->m_type.key_style(); }
516 C4_ALWAYS_INLINE
NodeType val_style(
id_type node)
const { _RYML_CB_ASSERT(m_callbacks, has_val(node) || is_root(node));
return _p(node)->m_type.val_style(); }
522 void clear_style(
id_type node,
bool recurse=
false);
523 void set_style_conditionally(
id_type node,
544 void set_key(
id_type node, csubstr
key) { _RYML_CB_ASSERT(m_callbacks, has_key(node)); _p(node)->m_key.scalar =
key; }
545 void set_val(
id_type node, csubstr val) { _RYML_CB_ASSERT(m_callbacks, has_val(node)); _p(node)->m_val.scalar = val; }
547 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); }
548 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); }
550 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); }
551 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); }
552 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); }
553 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); }
590 void resolve(
bool clear_anchors=
true);
600 void normalize_tags();
601 void normalize_tags_long();
603 id_type num_tag_directives()
const;
604 bool add_tag_directive(csubstr directive);
606 void clear_tag_directives();
610 size_t resolve_tag(substr output, csubstr tag,
id_type node_id)
const;
613 size_t needed = resolve_tag(output, tag, node_id);
614 return needed <= output.len ? output.first(needed) : output;
636 _RYML_CB_ASSERT(m_callbacks, parent !=
NONE);
637 _RYML_CB_ASSERT(m_callbacks, is_container(parent) || is_root(parent));
638 _RYML_CB_ASSERT(m_callbacks, after ==
NONE || (_p(after)->m_parent == parent));
640 _set_hierarchy(child, parent, after);
650 _set_hierarchy(child, parent, _p(parent)->m_last_child);
656 #if defined(__clang__)
657 # pragma clang diagnostic push
658 # pragma clang diagnostic ignored "-Wnull-dereference"
659 #elif defined(__GNUC__)
660 # pragma GCC diagnostic push
662 # pragma GCC diagnostic ignored "-Wnull-dereference"
669 return insert_child(_p(node)->m_parent, after);
680 remove_children(node);
685 void remove_children(
id_type node);
700 return change_type(node, (
NodeType)type);
703 #if defined(__clang__)
704 # pragma clang diagnostic pop
705 #elif defined(__GNUC__)
706 # pragma GCC diagnostic pop
742 void set_root_as_stream();
801 RYML_DEPRECATED(
"use arena_size() instead") size_t arena_pos()
const {
return m_arena_pos; }
807 size_t arena_slack()
const { _RYML_CB_ASSERT(m_callbacks, m_arena.len >= m_arena_pos);
return m_arena.len - m_arena_pos; }
810 csubstr
arena()
const {
return m_arena.first(m_arena_pos); }
812 substr
arena() {
return m_arena.first(m_arena_pos); }
817 return m_arena.is_super(s);
832 ->
typename std::enable_if<std::is_floating_point<T>::value, csubstr>::type
834 substr rem(m_arena.sub(m_arena_pos));
838 rem = _grow_arena(num);
840 _RYML_CB_ASSERT(m_callbacks, num <= rem.len);
842 rem = _request_span(num);
858 ->
typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type
860 substr rem(m_arena.sub(m_arena_pos));
864 rem = _grow_arena(num);
866 _RYML_CB_ASSERT(m_callbacks, num <= rem.len);
868 rem = _request_span(num);
886 substr rem(m_arena.sub(m_arena_pos));
890 rem = _grow_arena(num);
892 _RYML_CB_ASSERT(m_callbacks, num <= rem.len);
894 return _request_span(num);
902 else if(m_arena.str ==
nullptr)
910 return _request_span(0);
917 C4_ALWAYS_INLINE
static csubstr
to_arena(std::nullptr_t)
936 substr cp = alloc_arena(s.len);
937 _RYML_CB_ASSERT(m_callbacks, cp.len == s.len);
938 _RYML_CB_ASSERT(m_callbacks, !s.overlaps(cp));
939 #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
940 C4_SUPPRESS_WARNING_GCC_PUSH
941 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow=")
942 C4_SUPPRESS_WARNING_GCC(
"-Wrestrict")
945 memcpy(cp.str, s.str, s.len);
946 #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
947 C4_SUPPRESS_WARNING_GCC_POP
964 if(sz > arena_slack())
965 _grow_arena(sz - arena_slack());
966 substr s = _request_span(sz);
976 if(arena_cap > m_arena.len)
979 buf.str = (
char*) m_callbacks.m_allocate(arena_cap, m_arena.str, m_callbacks.m_user_data);
983 _RYML_CB_ASSERT(m_callbacks, m_arena.len >= 0);
985 m_callbacks.m_free(m_arena.str, m_arena.len, m_callbacks.m_user_data);
995 substr _grow_arena(
size_t more)
997 size_t cap = m_arena.len + more;
998 cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap;
999 cap = cap < 64 ? 64 : cap;
1001 return m_arena.sub(m_arena_pos);
1004 substr _request_span(
size_t sz)
1006 _RYML_CB_ASSERT(m_callbacks, m_arena_pos + sz <= m_arena.len);
1008 s = m_arena.sub(m_arena_pos, sz);
1013 substr _relocated(csubstr s, substr next_arena)
const
1015 _RYML_CB_ASSERT(m_callbacks, m_arena.is_super(s));
1016 _RYML_CB_ASSERT(m_callbacks, m_arena.sub(0, m_arena_pos).is_super(s));
1017 auto pos = (s.str - m_arena.str);
1018 substr r(next_arena.str + pos, s.len);
1019 _RYML_CB_ASSERT(m_callbacks, r.str - next_arena.str == pos);
1020 _RYML_CB_ASSERT(m_callbacks, next_arena.sub(0, m_arena_pos).is_super(r));
1036 operator bool()
const {
return target !=
NONE; }
1042 csubstr resolved()
const;
1044 csubstr unresolved()
const;
1048 lookup_result lookup_path(csubstr path,
id_type start=
NONE)
const;
1054 id_type lookup_path_or_modify(csubstr default_value, csubstr path,
id_type start=
NONE);
1066 struct _lookup_path_token
1070 _lookup_path_token() : value(), type() {}
1071 _lookup_path_token(csubstr v, NodeType t) : value(v), type(t) {}
1072 operator bool()
const {
return type !=
NOTYPE; }
1073 bool is_index()
const {
return value.begins_with(
'[') && value.ends_with(
']'); }
1078 void _lookup_path (lookup_result *r)
const;
1079 void _lookup_path_modify(lookup_result *r);
1081 id_type _next_node (lookup_result *r, _lookup_path_token *parent)
const;
1082 id_type _next_node_modify(lookup_result *r, _lookup_path_token *parent);
1084 static void _advance(lookup_result *r,
size_t more);
1086 _lookup_path_token _next_token(lookup_result *r, _lookup_path_token
const& parent)
const;
1092 void _copy(Tree
const& that);
1093 void _move(Tree & that) noexcept;
1095 void _relocate(substr next_arena);
1101 #if ! RYML_USE_ASSERT
1106 NodeData *n = _p(node);
1111 RYML_ASSERT_MSG((f &
SEQ) == 0,
"cannot mark simultaneously as map and seq");
1112 RYML_ASSERT_MSG((f &
VAL) == 0,
"cannot mark simultaneously as map and val");
1113 RYML_ASSERT_MSG((o &
SEQ) == 0,
"cannot turn a seq into a map; clear first");
1114 RYML_ASSERT_MSG((o &
VAL) == 0,
"cannot turn a val into a map; clear first");
1118 RYML_ASSERT_MSG((f &
MAP) == 0,
"cannot mark simultaneously as seq and map");
1119 RYML_ASSERT_MSG((f &
VAL) == 0,
"cannot mark simultaneously as seq and val");
1120 RYML_ASSERT_MSG((o &
MAP) == 0,
"cannot turn a map into a seq; clear first");
1121 RYML_ASSERT_MSG((o &
VAL) == 0,
"cannot turn a val into a seq; clear first");
1125 _RYML_CB_ASSERT(m_callbacks, !is_root(node));
1126 auto pid = parent(node); C4_UNUSED(pid);
1127 _RYML_CB_ASSERT(m_callbacks, is_map(pid));
1129 if((f &
VAL) && !is_root(node))
1131 auto pid = parent(node); C4_UNUSED(pid);
1132 _RYML_CB_ASSERT(m_callbacks, is_map(pid) || is_seq(pid));
1137 void _set_flags(
id_type node,
NodeType_e f) { _check_next_flags(node, f); _p(node)->m_type = f; }
1138 void _set_flags(
id_type node,
type_bits f) { _check_next_flags(node, f); _p(node)->m_type = f; }
1141 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; }
1144 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; }
1148 _p(node)->m_key.scalar =
key;
1149 _add_flags(node,
KEY|more_flags);
1153 _p(node)->m_key =
key;
1154 _add_flags(node,
KEY|more_flags);
1159 _RYML_CB_ASSERT(m_callbacks, num_children(node) == 0);
1160 _RYML_CB_ASSERT(m_callbacks, !is_seq(node) && !is_map(node));
1161 _p(node)->m_val.scalar = val;
1162 _add_flags(node,
VAL|more_flags);
1166 _RYML_CB_ASSERT(m_callbacks, num_children(node) == 0);
1167 _RYML_CB_ASSERT(m_callbacks, ! is_container(node));
1168 _p(node)->m_val = val;
1169 _add_flags(node,
VAL|more_flags);
1172 void _set(
id_type node, NodeInit
const& i)
1174 _RYML_CB_ASSERT(m_callbacks, i._check());
1175 NodeData *n = _p(node);
1176 _RYML_CB_ASSERT(m_callbacks, n->m_key.scalar.empty() || i.key.scalar.empty() || i.key.scalar == n->m_key.scalar);
1177 _add_flags(node, i.type);
1178 if(n->m_key.scalar.empty())
1180 if( ! i.key.scalar.empty())
1182 _set_key(node, i.key.scalar);
1185 n->m_key.tag = i.key.tag;
1189 void _set_parent_as_container_if_needed(
id_type in)
1191 NodeData
const* n = _p(in);
1195 if( ! (is_seq(ip) || is_map(ip)))
1197 if((in == first_child(ip)) && (in == last_child(ip)))
1199 if( ! n->m_key.empty() || has_key(in))
1201 _add_flags(ip,
MAP);
1205 _add_flags(ip,
SEQ);
1214 _RYML_CB_ASSERT(m_callbacks, is_seq(node));
1215 for(
id_type i = first_child(node); i !=
NONE; i = next_sibling(i))
1217 NodeData *C4_RESTRICT ch = _p(i);
1218 if(ch->m_type.is_keyval())
1220 ch->m_type.add(
KEY);
1221 ch->m_key = ch->m_val;
1223 auto *C4_RESTRICT n = _p(node);
1237 _copy_props(dst_,
this, src_);
1242 _copy_props_wo_key(dst_,
this, src_);
1245 void _copy_props(
id_type dst_, Tree
const* that_tree,
id_type src_)
1247 auto & C4_RESTRICT dst = *_p(dst_);
1248 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1249 dst.m_type = src.m_type;
1250 dst.m_key = src.m_key;
1251 dst.m_val = src.m_val;
1256 auto & C4_RESTRICT dst = *_p(dst_);
1257 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1258 dst.m_type = (src.m_type & src_mask) | (dst.m_type & ~src_mask);
1259 dst.m_key = src.m_key;
1260 dst.m_val = src.m_val;
1263 void _copy_props_wo_key(
id_type dst_, Tree
const* that_tree,
id_type src_)
1265 auto & C4_RESTRICT dst = *_p(dst_);
1266 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1268 dst.m_val = src.m_val;
1273 auto & C4_RESTRICT dst = *_p(dst_);
1274 auto const& C4_RESTRICT src = *that_tree->_p(src_);
1275 dst.m_type = (src.m_type & ((~
_KEYMASK)|src_mask)) | (dst.m_type & (
_KEYMASK|~src_mask));
1276 dst.m_val = src.m_val;
1279 void _clear_type(
id_type node)
1281 _p(node)->m_type =
NOTYPE;
1286 auto *C4_RESTRICT n = _p(node);
1291 n->m_first_child =
NONE;
1292 n->m_last_child =
NONE;
1297 _p(node)->m_key.clear();
1298 _rem_flags(node,
KEY);
1303 _p(node)->m_val.clear();
1304 _rem_flags(node,
VAL);
1318 void _free_list_add(
id_type node);
1319 void _free_list_rem(
id_type node);
1322 void _rem_hierarchy(
id_type node);
1364 ->
typename std::enable_if<!std::is_arithmetic<T>::value,
bool>::type
1366 return C4_LIKELY(!(tree->type(
id) &
VALNIL)) ?
from_chars(tree->val(
id), v) :
false;
1375 ->
typename std::enable_if<!std::is_arithmetic<T>::value,
bool>::type
1377 return C4_LIKELY(!(tree->type(
id) &
KEYNIL)) ?
from_chars(tree->key(
id), v) :
false;
1389 inline auto read(Tree
const* C4_RESTRICT tree,
id_type id, T *v)
1390 ->
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value,
bool>::type
1392 using U =
typename std::remove_cv<T>::type;
1393 enum { ischar = std::is_same<char, U>::value || std::is_same<signed char, U>::value || std::is_same<unsigned char, U>::value };
1394 csubstr val = tree->val(
id);
1395 NodeType ty = tree->type(
id);
1396 if(C4_UNLIKELY((ty &
VALNIL) || val.empty()))
1400 char first = val[0];
1401 if(ty.is_val_quoted() && (first !=
'0' && !ischar))
1403 else if(first ==
'+')
1414 inline auto readkey(Tree
const* C4_RESTRICT tree,
id_type id, T *v)
1415 ->
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_floating_point<T>::value,
bool>::type
1417 using U =
typename std::remove_cv<T>::type;
1418 enum { ischar = std::is_same<char, U>::value || std::is_same<signed char, U>::value || std::is_same<unsigned char, U>::value };
1419 csubstr
key = tree->key(
id);
1420 NodeType ty = tree->type(
id);
1425 char first =
key[0];
1426 if(ty.is_key_quoted() && (first !=
'0' && !ischar))
1428 else if(first ==
'+')
1440 static_assert(std::is_floating_point<T>::value,
"must be floating point");
1441 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(
"-Wfloat-equal");
1442 if(C4_UNLIKELY(std::isnan(val)))
1443 return to_chars(buf, csubstr(
".nan"));
1444 else if(C4_UNLIKELY(val == std::numeric_limits<T>::infinity()))
1445 return to_chars(buf, csubstr(
".inf"));
1446 else if(C4_UNLIKELY(val == -std::numeric_limits<T>::infinity()))
1447 return to_chars(buf, csubstr(
"-.inf"));
1449 C4_SUPPRESS_WARNING_GCC_CLANG_POP
1458 static_assert(std::is_floating_point<T>::value,
"must be floating point");
1459 if(buf.begins_with(
'+'))
1467 else if(C4_UNLIKELY(buf ==
".nan" || buf ==
".NaN" || buf ==
".NAN"))
1469 *val = std::numeric_limits<T>::quiet_NaN();
1472 else if(C4_UNLIKELY(buf ==
".inf" || buf ==
".Inf" || buf ==
".INF"))
1474 *val = std::numeric_limits<T>::infinity();
1477 else if(C4_UNLIKELY(buf ==
"-.inf" || buf ==
"-.Inf" || buf ==
"-.INF"))
1479 *val = -std::numeric_limits<T>::infinity();
1511 typename std::enable_if<std::is_floating_point<T>::value,
bool>::type
1514 csubstr val = tree->val(
id);
1541 typename std::enable_if<std::is_floating_point<T>::value,
bool>::type
1544 csubstr
key = tree->key(
id);
1557 C4_SUPPRESS_WARNING_MSVC_POP
1558 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...
This is the main driver of parsing logic: it scans the YAML or JSON source for tokens,...
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
NodeType key_style(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
NodeType val_style(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_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 a Tree.
lookup_result(csubstr path_, id_type start)