|
|
|
|
#include "c4/yml/tree.hpp"
|
|
|
|
|
#include "c4/yml/detail/parser_dbg.hpp"
|
|
|
|
|
#include "c4/yml/node.hpp"
|
|
|
|
|
#include "c4/yml/detail/stack.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wtype-limits")
|
|
|
|
|
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4296/*expression is always 'boolean_value'*/)
|
|
|
|
|
|
|
|
|
|
namespace c4 {
|
|
|
|
|
namespace yml {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
csubstr normalize_tag(csubstr tag)
|
|
|
|
|
{
|
|
|
|
|
YamlTag_e t = to_tag(tag);
|
|
|
|
|
if(t != TAG_NONE)
|
|
|
|
|
return from_tag(t);
|
|
|
|
|
if(tag.begins_with("!<"))
|
|
|
|
|
tag = tag.sub(1);
|
|
|
|
|
if(tag.begins_with("<!"))
|
|
|
|
|
return tag;
|
|
|
|
|
return tag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
csubstr normalize_tag_long(csubstr tag)
|
|
|
|
|
{
|
|
|
|
|
YamlTag_e t = to_tag(tag);
|
|
|
|
|
if(t != TAG_NONE)
|
|
|
|
|
return from_tag_long(t);
|
|
|
|
|
if(tag.begins_with("!<"))
|
|
|
|
|
tag = tag.sub(1);
|
|
|
|
|
if(tag.begins_with("<!"))
|
|
|
|
|
return tag;
|
|
|
|
|
return tag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
YamlTag_e to_tag(csubstr tag)
|
|
|
|
|
{
|
|
|
|
|
if(tag.begins_with("!<"))
|
|
|
|
|
tag = tag.sub(1);
|
|
|
|
|
if(tag.begins_with("!!"))
|
|
|
|
|
tag = tag.sub(2);
|
|
|
|
|
else if(tag.begins_with('!'))
|
|
|
|
|
return TAG_NONE;
|
|
|
|
|
else if(tag.begins_with("tag:yaml.org,2002:"))
|
|
|
|
|
{
|
|
|
|
|
RYML_ASSERT(csubstr("tag:yaml.org,2002:").len == 18);
|
|
|
|
|
tag = tag.sub(18);
|
|
|
|
|
}
|
|
|
|
|
else if(tag.begins_with("<tag:yaml.org,2002:"))
|
|
|
|
|
{
|
|
|
|
|
RYML_ASSERT(csubstr("<tag:yaml.org,2002:").len == 19);
|
|
|
|
|
tag = tag.sub(19);
|
|
|
|
|
if(!tag.len)
|
|
|
|
|
return TAG_NONE;
|
|
|
|
|
tag = tag.offs(0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(tag == "map")
|
|
|
|
|
return TAG_MAP;
|
|
|
|
|
else if(tag == "omap")
|
|
|
|
|
return TAG_OMAP;
|
|
|
|
|
else if(tag == "pairs")
|
|
|
|
|
return TAG_PAIRS;
|
|
|
|
|
else if(tag == "set")
|
|
|
|
|
return TAG_SET;
|
|
|
|
|
else if(tag == "seq")
|
|
|
|
|
return TAG_SEQ;
|
|
|
|
|
else if(tag == "binary")
|
|
|
|
|
return TAG_BINARY;
|
|
|
|
|
else if(tag == "bool")
|
|
|
|
|
return TAG_BOOL;
|
|
|
|
|
else if(tag == "float")
|
|
|
|
|
return TAG_FLOAT;
|
|
|
|
|
else if(tag == "int")
|
|
|
|
|
return TAG_INT;
|
|
|
|
|
else if(tag == "merge")
|
|
|
|
|
return TAG_MERGE;
|
|
|
|
|
else if(tag == "null")
|
|
|
|
|
return TAG_NULL;
|
|
|
|
|
else if(tag == "str")
|
|
|
|
|
return TAG_STR;
|
|
|
|
|
else if(tag == "timestamp")
|
|
|
|
|
return TAG_TIMESTAMP;
|
|
|
|
|
else if(tag == "value")
|
|
|
|
|
return TAG_VALUE;
|
|
|
|
|
|
|
|
|
|
return TAG_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
csubstr from_tag_long(YamlTag_e tag)
|
|
|
|
|
{
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
|
|
|
|
case TAG_MAP:
|
|
|
|
|
return {"<tag:yaml.org,2002:map>"};
|
|
|
|
|
case TAG_OMAP:
|
|
|
|
|
return {"<tag:yaml.org,2002:omap>"};
|
|
|
|
|
case TAG_PAIRS:
|
|
|
|
|
return {"<tag:yaml.org,2002:pairs>"};
|
|
|
|
|
case TAG_SET:
|
|
|
|
|
return {"<tag:yaml.org,2002:set>"};
|
|
|
|
|
case TAG_SEQ:
|
|
|
|
|
return {"<tag:yaml.org,2002:seq>"};
|
|
|
|
|
case TAG_BINARY:
|
|
|
|
|
return {"<tag:yaml.org,2002:binary>"};
|
|
|
|
|
case TAG_BOOL:
|
|
|
|
|
return {"<tag:yaml.org,2002:bool>"};
|
|
|
|
|
case TAG_FLOAT:
|
|
|
|
|
return {"<tag:yaml.org,2002:float>"};
|
|
|
|
|
case TAG_INT:
|
|
|
|
|
return {"<tag:yaml.org,2002:int>"};
|
|
|
|
|
case TAG_MERGE:
|
|
|
|
|
return {"<tag:yaml.org,2002:merge>"};
|
|
|
|
|
case TAG_NULL:
|
|
|
|
|
return {"<tag:yaml.org,2002:null>"};
|
|
|
|
|
case TAG_STR:
|
|
|
|
|
return {"<tag:yaml.org,2002:str>"};
|
|
|
|
|
case TAG_TIMESTAMP:
|
|
|
|
|
return {"<tag:yaml.org,2002:timestamp>"};
|
|
|
|
|
case TAG_VALUE:
|
|
|
|
|
return {"<tag:yaml.org,2002:value>"};
|
|
|
|
|
case TAG_YAML:
|
|
|
|
|
return {"<tag:yaml.org,2002:yaml>"};
|
|
|
|
|
case TAG_NONE:
|
|
|
|
|
return {""};
|
|
|
|
|
}
|
|
|
|
|
return {""};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
csubstr from_tag(YamlTag_e tag)
|
|
|
|
|
{
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
|
|
|
|
case TAG_MAP:
|
|
|
|
|
return {"!!map"};
|
|
|
|
|
case TAG_OMAP:
|
|
|
|
|
return {"!!omap"};
|
|
|
|
|
case TAG_PAIRS:
|
|
|
|
|
return {"!!pairs"};
|
|
|
|
|
case TAG_SET:
|
|
|
|
|
return {"!!set"};
|
|
|
|
|
case TAG_SEQ:
|
|
|
|
|
return {"!!seq"};
|
|
|
|
|
case TAG_BINARY:
|
|
|
|
|
return {"!!binary"};
|
|
|
|
|
case TAG_BOOL:
|
|
|
|
|
return {"!!bool"};
|
|
|
|
|
case TAG_FLOAT:
|
|
|
|
|
return {"!!float"};
|
|
|
|
|
case TAG_INT:
|
|
|
|
|
return {"!!int"};
|
|
|
|
|
case TAG_MERGE:
|
|
|
|
|
return {"!!merge"};
|
|
|
|
|
case TAG_NULL:
|
|
|
|
|
return {"!!null"};
|
|
|
|
|
case TAG_STR:
|
|
|
|
|
return {"!!str"};
|
|
|
|
|
case TAG_TIMESTAMP:
|
|
|
|
|
return {"!!timestamp"};
|
|
|
|
|
case TAG_VALUE:
|
|
|
|
|
return {"!!value"};
|
|
|
|
|
case TAG_YAML:
|
|
|
|
|
return {"!!yaml"};
|
|
|
|
|
case TAG_NONE:
|
|
|
|
|
return {""};
|
|
|
|
|
}
|
|
|
|
|
return {""};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const char* NodeType::type_str(NodeType_e ty)
|
|
|
|
|
{
|
|
|
|
|
switch(ty & _TYMASK)
|
|
|
|
|
{
|
|
|
|
|
case KEYVAL:
|
|
|
|
|
return "KEYVAL";
|
|
|
|
|
case KEY:
|
|
|
|
|
return "KEY";
|
|
|
|
|
case VAL:
|
|
|
|
|
return "VAL";
|
|
|
|
|
case MAP:
|
|
|
|
|
return "MAP";
|
|
|
|
|
case SEQ:
|
|
|
|
|
return "SEQ";
|
|
|
|
|
case KEYMAP:
|
|
|
|
|
return "KEYMAP";
|
|
|
|
|
case KEYSEQ:
|
|
|
|
|
return "KEYSEQ";
|
|
|
|
|
case DOCSEQ:
|
|
|
|
|
return "DOCSEQ";
|
|
|
|
|
case DOCMAP:
|
|
|
|
|
return "DOCMAP";
|
|
|
|
|
case DOCVAL:
|
|
|
|
|
return "DOCVAL";
|
|
|
|
|
case DOC:
|
|
|
|
|
return "DOC";
|
|
|
|
|
case STREAM:
|
|
|
|
|
return "STREAM";
|
|
|
|
|
case NOTYPE:
|
|
|
|
|
return "NOTYPE";
|
|
|
|
|
default:
|
|
|
|
|
if((ty & KEYVAL) == KEYVAL)
|
|
|
|
|
return "KEYVAL***";
|
|
|
|
|
if((ty & KEYMAP) == KEYMAP)
|
|
|
|
|
return "KEYMAP***";
|
|
|
|
|
if((ty & KEYSEQ) == KEYSEQ)
|
|
|
|
|
return "KEYSEQ***";
|
|
|
|
|
if((ty & DOCSEQ) == DOCSEQ)
|
|
|
|
|
return "DOCSEQ***";
|
|
|
|
|
if((ty & DOCMAP) == DOCMAP)
|
|
|
|
|
return "DOCMAP***";
|
|
|
|
|
if((ty & DOCVAL) == DOCVAL)
|
|
|
|
|
return "DOCVAL***";
|
|
|
|
|
if(ty & KEY)
|
|
|
|
|
return "KEY***";
|
|
|
|
|
if(ty & VAL)
|
|
|
|
|
return "VAL***";
|
|
|
|
|
if(ty & MAP)
|
|
|
|
|
return "MAP***";
|
|
|
|
|
if(ty & SEQ)
|
|
|
|
|
return "SEQ***";
|
|
|
|
|
if(ty & DOC)
|
|
|
|
|
return "DOC***";
|
|
|
|
|
return "(unk)";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
NodeRef Tree::rootref()
|
|
|
|
|
{
|
|
|
|
|
return NodeRef(this, root_id());
|
|
|
|
|
}
|
|
|
|
|
NodeRef const Tree::rootref() const
|
|
|
|
|
{
|
|
|
|
|
return NodeRef(const_cast<Tree*>(this), root_id());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodeRef Tree::ref(size_t id)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size);
|
|
|
|
|
return NodeRef(this, id);
|
|
|
|
|
}
|
|
|
|
|
NodeRef const Tree::ref(size_t id) const
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size);
|
|
|
|
|
return NodeRef(const_cast<Tree*>(this), id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodeRef Tree::operator[] (csubstr key)
|
|
|
|
|
{
|
|
|
|
|
return rootref()[key];
|
|
|
|
|
}
|
|
|
|
|
NodeRef const Tree::operator[] (csubstr key) const
|
|
|
|
|
{
|
|
|
|
|
return rootref()[key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodeRef Tree::operator[] (size_t i)
|
|
|
|
|
{
|
|
|
|
|
return rootref()[i];
|
|
|
|
|
}
|
|
|
|
|
NodeRef const Tree::operator[] (size_t i) const
|
|
|
|
|
{
|
|
|
|
|
return rootref()[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodeRef Tree::docref(size_t i)
|
|
|
|
|
{
|
|
|
|
|
return ref(doc(i));
|
|
|
|
|
}
|
|
|
|
|
NodeRef const Tree::docref(size_t i) const
|
|
|
|
|
{
|
|
|
|
|
return ref(doc(i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
Tree::Tree(Callbacks const& cb)
|
|
|
|
|
: m_buf(nullptr)
|
|
|
|
|
, m_cap(0)
|
|
|
|
|
, m_size(0)
|
|
|
|
|
, m_free_head(NONE)
|
|
|
|
|
, m_free_tail(NONE)
|
|
|
|
|
, m_arena()
|
|
|
|
|
, m_arena_pos(0)
|
|
|
|
|
, m_callbacks(cb)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tree::Tree(size_t node_capacity, size_t arena_capacity, Callbacks const& cb)
|
|
|
|
|
: Tree(cb)
|
|
|
|
|
{
|
|
|
|
|
reserve(node_capacity);
|
|
|
|
|
reserve_arena(arena_capacity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tree::~Tree()
|
|
|
|
|
{
|
|
|
|
|
_free();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tree::Tree(Tree const& that) noexcept : Tree(that.m_callbacks)
|
|
|
|
|
{
|
|
|
|
|
_copy(that);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tree& Tree::operator= (Tree const& that) noexcept
|
|
|
|
|
{
|
|
|
|
|
_free();
|
|
|
|
|
m_callbacks = that.m_callbacks;
|
|
|
|
|
_copy(that);
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tree::Tree(Tree && that) noexcept : Tree(that.m_callbacks)
|
|
|
|
|
{
|
|
|
|
|
_move(that);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tree& Tree::operator= (Tree && that) noexcept
|
|
|
|
|
{
|
|
|
|
|
_free();
|
|
|
|
|
m_callbacks = that.m_callbacks;
|
|
|
|
|
_move(that);
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_free()
|
|
|
|
|
{
|
|
|
|
|
if(m_buf)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_cap > 0);
|
|
|
|
|
_RYML_CB_FREE(m_callbacks, m_buf, NodeData, m_cap);
|
|
|
|
|
}
|
|
|
|
|
if(m_arena.str)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_arena.len > 0);
|
|
|
|
|
_RYML_CB_FREE(m_callbacks, m_arena.str, char, m_arena.len);
|
|
|
|
|
}
|
|
|
|
|
_clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC_PUSH
|
|
|
|
|
#if defined(__GNUC__) && __GNUC__>= 8
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wclass-memaccess") // error: ‘void* memset(void*, int, size_t)’ clearing an object of type ‘class c4::yml::Tree’ with no trivial copy-assignment; use assignment or value-initialization instead
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void Tree::_clear()
|
|
|
|
|
{
|
|
|
|
|
m_buf = nullptr;
|
|
|
|
|
m_cap = 0;
|
|
|
|
|
m_size = 0;
|
|
|
|
|
m_free_head = 0;
|
|
|
|
|
m_free_tail = 0;
|
|
|
|
|
m_arena = {};
|
|
|
|
|
m_arena_pos = 0;
|
|
|
|
|
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
|
|
|
|
|
m_tag_directives[i] = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_copy(Tree const& that)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_buf == nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_arena.str == nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_arena.len == 0);
|
|
|
|
|
m_buf = _RYML_CB_ALLOC_HINT(m_callbacks, NodeData, that.m_cap, that.m_buf);
|
|
|
|
|
memcpy(m_buf, that.m_buf, that.m_cap * sizeof(NodeData));
|
|
|
|
|
m_cap = that.m_cap;
|
|
|
|
|
m_size = that.m_size;
|
|
|
|
|
m_free_head = that.m_free_head;
|
|
|
|
|
m_free_tail = that.m_free_tail;
|
|
|
|
|
m_arena_pos = that.m_arena_pos;
|
|
|
|
|
m_arena = that.m_arena;
|
|
|
|
|
if(that.m_arena.str)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, that.m_arena.len > 0);
|
|
|
|
|
substr arena;
|
|
|
|
|
arena.str = _RYML_CB_ALLOC_HINT(m_callbacks, char, that.m_arena.len, that.m_arena.str);
|
|
|
|
|
arena.len = that.m_arena.len;
|
|
|
|
|
_relocate(arena); // does a memcpy of the arena and updates nodes using the old arena
|
|
|
|
|
m_arena = arena;
|
|
|
|
|
}
|
|
|
|
|
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
|
|
|
|
|
m_tag_directives[i] = that.m_tag_directives[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_move(Tree & that)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_buf == nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_arena.str == nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_arena.len == 0);
|
|
|
|
|
m_buf = that.m_buf;
|
|
|
|
|
m_cap = that.m_cap;
|
|
|
|
|
m_size = that.m_size;
|
|
|
|
|
m_free_head = that.m_free_head;
|
|
|
|
|
m_free_tail = that.m_free_tail;
|
|
|
|
|
m_arena = that.m_arena;
|
|
|
|
|
m_arena_pos = that.m_arena_pos;
|
|
|
|
|
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
|
|
|
|
|
m_tag_directives[i] = that.m_tag_directives[i];
|
|
|
|
|
that._clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_relocate(substr next_arena)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, next_arena.not_empty());
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, next_arena.len >= m_arena.len);
|
|
|
|
|
memcpy(next_arena.str, m_arena.str, m_arena_pos);
|
|
|
|
|
for(NodeData *C4_RESTRICT n = m_buf, *e = m_buf + m_cap; n != e; ++n)
|
|
|
|
|
{
|
|
|
|
|
if(in_arena(n->m_key.scalar))
|
|
|
|
|
n->m_key.scalar = _relocated(n->m_key.scalar, next_arena);
|
|
|
|
|
if(in_arena(n->m_key.tag))
|
|
|
|
|
n->m_key.tag = _relocated(n->m_key.tag, next_arena);
|
|
|
|
|
if(in_arena(n->m_key.anchor))
|
|
|
|
|
n->m_key.anchor = _relocated(n->m_key.anchor, next_arena);
|
|
|
|
|
if(in_arena(n->m_val.scalar))
|
|
|
|
|
n->m_val.scalar = _relocated(n->m_val.scalar, next_arena);
|
|
|
|
|
if(in_arena(n->m_val.tag))
|
|
|
|
|
n->m_val.tag = _relocated(n->m_val.tag, next_arena);
|
|
|
|
|
if(in_arena(n->m_val.anchor))
|
|
|
|
|
n->m_val.anchor = _relocated(n->m_val.anchor, next_arena);
|
|
|
|
|
}
|
|
|
|
|
for(TagDirective &C4_RESTRICT td : m_tag_directives)
|
|
|
|
|
{
|
|
|
|
|
if(in_arena(td.prefix))
|
|
|
|
|
td.prefix = _relocated(td.prefix, next_arena);
|
|
|
|
|
if(in_arena(td.handle))
|
|
|
|
|
td.handle = _relocated(td.handle, next_arena);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::reserve(size_t cap)
|
|
|
|
|
{
|
|
|
|
|
if(cap > m_cap)
|
|
|
|
|
{
|
|
|
|
|
NodeData *buf = _RYML_CB_ALLOC_HINT(m_callbacks, NodeData, cap, m_buf);
|
|
|
|
|
if(m_buf)
|
|
|
|
|
{
|
|
|
|
|
memcpy(buf, m_buf, m_cap * sizeof(NodeData));
|
|
|
|
|
_RYML_CB_FREE(m_callbacks, m_buf, NodeData, m_cap);
|
|
|
|
|
}
|
|
|
|
|
size_t first = m_cap, del = cap - m_cap;
|
|
|
|
|
m_cap = cap;
|
|
|
|
|
m_buf = buf;
|
|
|
|
|
_clear_range(first, del);
|
|
|
|
|
if(m_free_head != NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_buf != nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_free_tail != NONE);
|
|
|
|
|
m_buf[m_free_tail].m_next_sibling = first;
|
|
|
|
|
m_buf[first].m_prev_sibling = m_free_tail;
|
|
|
|
|
m_free_tail = cap-1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_free_tail == NONE);
|
|
|
|
|
m_free_head = first;
|
|
|
|
|
m_free_tail = cap-1;
|
|
|
|
|
}
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_free_head == NONE || (m_free_head >= 0 && m_free_head < cap));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_free_tail == NONE || (m_free_tail >= 0 && m_free_tail < cap));
|
|
|
|
|
|
|
|
|
|
if( ! m_size)
|
|
|
|
|
_claim_root();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::clear()
|
|
|
|
|
{
|
|
|
|
|
_clear_range(0, m_cap);
|
|
|
|
|
m_size = 0;
|
|
|
|
|
if(m_buf)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_cap >= 0);
|
|
|
|
|
m_free_head = 0;
|
|
|
|
|
m_free_tail = m_cap-1;
|
|
|
|
|
_claim_root();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_free_head = NONE;
|
|
|
|
|
m_free_tail = NONE;
|
|
|
|
|
}
|
|
|
|
|
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
|
|
|
|
|
m_tag_directives[i] = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_claim_root()
|
|
|
|
|
{
|
|
|
|
|
size_t r = _claim();
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, r == 0);
|
|
|
|
|
_set_hierarchy(r, NONE, NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::_clear_range(size_t first, size_t num)
|
|
|
|
|
{
|
|
|
|
|
if(num == 0)
|
|
|
|
|
return; // prevent overflow when subtracting
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, first >= 0 && first + num <= m_cap);
|
|
|
|
|
memset(m_buf + first, 0, num * sizeof(NodeData)); // TODO we should not need this
|
|
|
|
|
for(size_t i = first, e = first + num; i < e; ++i)
|
|
|
|
|
{
|
|
|
|
|
_clear(i);
|
|
|
|
|
NodeData *n = m_buf + i;
|
|
|
|
|
n->m_prev_sibling = i - 1;
|
|
|
|
|
n->m_next_sibling = i + 1;
|
|
|
|
|
}
|
|
|
|
|
m_buf[first + num - 1].m_next_sibling = NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC_POP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::_release(size_t i)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, i >= 0 && i < m_cap);
|
|
|
|
|
|
|
|
|
|
_rem_hierarchy(i);
|
|
|
|
|
_free_list_add(i);
|
|
|
|
|
_clear(i);
|
|
|
|
|
|
|
|
|
|
--m_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// add to the front of the free list
|
|
|
|
|
void Tree::_free_list_add(size_t i)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, i >= 0 && i < m_cap);
|
|
|
|
|
NodeData &C4_RESTRICT w = m_buf[i];
|
|
|
|
|
|
|
|
|
|
w.m_parent = NONE;
|
|
|
|
|
w.m_next_sibling = m_free_head;
|
|
|
|
|
w.m_prev_sibling = NONE;
|
|
|
|
|
if(m_free_head != NONE)
|
|
|
|
|
m_buf[m_free_head].m_prev_sibling = i;
|
|
|
|
|
m_free_head = i;
|
|
|
|
|
if(m_free_tail == NONE)
|
|
|
|
|
m_free_tail = m_free_head;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_free_list_rem(size_t i)
|
|
|
|
|
{
|
|
|
|
|
if(m_free_head == i)
|
|
|
|
|
m_free_head = _p(i)->m_next_sibling;
|
|
|
|
|
_rem_hierarchy(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
size_t Tree::_claim()
|
|
|
|
|
{
|
|
|
|
|
if(m_free_head == NONE || m_buf == nullptr)
|
|
|
|
|
{
|
|
|
|
|
size_t sz = 2 * m_cap;
|
|
|
|
|
sz = sz ? sz : 16;
|
|
|
|
|
reserve(sz);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_free_head != NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_size < m_cap);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_free_head >= 0 && m_free_head < m_cap);
|
|
|
|
|
|
|
|
|
|
size_t ichild = m_free_head;
|
|
|
|
|
NodeData *child = m_buf + ichild;
|
|
|
|
|
|
|
|
|
|
++m_size;
|
|
|
|
|
m_free_head = child->m_next_sibling;
|
|
|
|
|
if(m_free_head == NONE)
|
|
|
|
|
{
|
|
|
|
|
m_free_tail = NONE;
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, m_size == m_cap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_clear(ichild);
|
|
|
|
|
|
|
|
|
|
return ichild;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC_PUSH
|
|
|
|
|
C4_SUPPRESS_WARNING_CLANG_PUSH
|
|
|
|
|
C4_SUPPRESS_WARNING_CLANG("-Wnull-dereference")
|
|
|
|
|
#if defined(__GNUC__) && (__GNUC__ >= 6)
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC("-Wnull-dereference")
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void Tree::_set_hierarchy(size_t ichild, size_t iparent, size_t iprev_sibling)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, iparent == NONE || (iparent >= 0 && iparent < m_cap));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, iprev_sibling == NONE || (iprev_sibling >= 0 && iprev_sibling < m_cap));
|
|
|
|
|
|
|
|
|
|
NodeData *C4_RESTRICT child = get(ichild);
|
|
|
|
|
|
|
|
|
|
child->m_parent = iparent;
|
|
|
|
|
child->m_prev_sibling = NONE;
|
|
|
|
|
child->m_next_sibling = NONE;
|
|
|
|
|
|
|
|
|
|
if(iparent == NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ichild == 0);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, iprev_sibling == NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(iparent == NONE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
size_t inext_sibling = iprev_sibling != NONE ? next_sibling(iprev_sibling) : first_child(iparent);
|
|
|
|
|
NodeData *C4_RESTRICT parent = get(iparent);
|
|
|
|
|
NodeData *C4_RESTRICT psib = get(iprev_sibling);
|
|
|
|
|
NodeData *C4_RESTRICT nsib = get(inext_sibling);
|
|
|
|
|
|
|
|
|
|
if(psib)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, next_sibling(iprev_sibling) == id(nsib));
|
|
|
|
|
child->m_prev_sibling = id(psib);
|
|
|
|
|
psib->m_next_sibling = id(child);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, psib->m_prev_sibling != psib->m_next_sibling || psib->m_prev_sibling == NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(nsib)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, prev_sibling(inext_sibling) == id(psib));
|
|
|
|
|
child->m_next_sibling = id(nsib);
|
|
|
|
|
nsib->m_prev_sibling = id(child);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, nsib->m_prev_sibling != nsib->m_next_sibling || nsib->m_prev_sibling == NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(parent->m_first_child == NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent->m_last_child == NONE);
|
|
|
|
|
parent->m_first_child = id(child);
|
|
|
|
|
parent->m_last_child = id(child);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(child->m_next_sibling == parent->m_first_child)
|
|
|
|
|
parent->m_first_child = id(child);
|
|
|
|
|
|
|
|
|
|
if(child->m_prev_sibling == parent->m_last_child)
|
|
|
|
|
parent->m_last_child = id(child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC_POP
|
|
|
|
|
C4_SUPPRESS_WARNING_CLANG_POP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::_rem_hierarchy(size_t i)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, i >= 0 && i < m_cap);
|
|
|
|
|
|
|
|
|
|
NodeData &C4_RESTRICT w = m_buf[i];
|
|
|
|
|
|
|
|
|
|
// remove from the parent
|
|
|
|
|
if(w.m_parent != NONE)
|
|
|
|
|
{
|
|
|
|
|
NodeData &C4_RESTRICT p = m_buf[w.m_parent];
|
|
|
|
|
if(p.m_first_child == i)
|
|
|
|
|
{
|
|
|
|
|
p.m_first_child = w.m_next_sibling;
|
|
|
|
|
}
|
|
|
|
|
if(p.m_last_child == i)
|
|
|
|
|
{
|
|
|
|
|
p.m_last_child = w.m_prev_sibling;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove from the used list
|
|
|
|
|
if(w.m_prev_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
NodeData *C4_RESTRICT prev = get(w.m_prev_sibling);
|
|
|
|
|
prev->m_next_sibling = w.m_next_sibling;
|
|
|
|
|
}
|
|
|
|
|
if(w.m_next_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
NodeData *C4_RESTRICT next = get(w.m_next_sibling);
|
|
|
|
|
next->m_prev_sibling = w.m_prev_sibling;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::reorder()
|
|
|
|
|
{
|
|
|
|
|
size_t r = root_id();
|
|
|
|
|
_do_reorder(&r, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
size_t Tree::_do_reorder(size_t *node, size_t count)
|
|
|
|
|
{
|
|
|
|
|
// swap this node if it's not in place
|
|
|
|
|
if(*node != count)
|
|
|
|
|
{
|
|
|
|
|
_swap(*node, count);
|
|
|
|
|
*node = count;
|
|
|
|
|
}
|
|
|
|
|
++count; // bump the count from this node
|
|
|
|
|
|
|
|
|
|
// now descend in the hierarchy
|
|
|
|
|
for(size_t i = first_child(*node); i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
// this child may have been relocated to a different index,
|
|
|
|
|
// so get an updated version
|
|
|
|
|
count = _do_reorder(&i, count);
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::_swap(size_t n_, size_t m_)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, (parent(n_) != NONE) || type(n_) == NOTYPE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, (parent(m_) != NONE) || type(m_) == NOTYPE);
|
|
|
|
|
NodeType tn = type(n_);
|
|
|
|
|
NodeType tm = type(m_);
|
|
|
|
|
if(tn != NOTYPE && tm != NOTYPE)
|
|
|
|
|
{
|
|
|
|
|
_swap_props(n_, m_);
|
|
|
|
|
_swap_hierarchy(n_, m_);
|
|
|
|
|
}
|
|
|
|
|
else if(tn == NOTYPE && tm != NOTYPE)
|
|
|
|
|
{
|
|
|
|
|
_copy_props(n_, m_);
|
|
|
|
|
_free_list_rem(n_);
|
|
|
|
|
_copy_hierarchy(n_, m_);
|
|
|
|
|
_clear(m_);
|
|
|
|
|
_free_list_add(m_);
|
|
|
|
|
}
|
|
|
|
|
else if(tn != NOTYPE && tm == NOTYPE)
|
|
|
|
|
{
|
|
|
|
|
_copy_props(m_, n_);
|
|
|
|
|
_free_list_rem(m_);
|
|
|
|
|
_copy_hierarchy(m_, n_);
|
|
|
|
|
_clear(n_);
|
|
|
|
|
_free_list_add(n_);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
C4_NEVER_REACH();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::_swap_hierarchy(size_t ia, size_t ib)
|
|
|
|
|
{
|
|
|
|
|
if(ia == ib) return;
|
|
|
|
|
|
|
|
|
|
for(size_t i = first_child(ia); i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
if(i == ib || i == ia)
|
|
|
|
|
continue;
|
|
|
|
|
_p(i)->m_parent = ib;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(size_t i = first_child(ib); i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
if(i == ib || i == ia)
|
|
|
|
|
continue;
|
|
|
|
|
_p(i)->m_parent = ia;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto & C4_RESTRICT a = *_p(ia);
|
|
|
|
|
auto & C4_RESTRICT b = *_p(ib);
|
|
|
|
|
auto & C4_RESTRICT pa = *_p(a.m_parent);
|
|
|
|
|
auto & C4_RESTRICT pb = *_p(b.m_parent);
|
|
|
|
|
|
|
|
|
|
if(&pa == &pb)
|
|
|
|
|
{
|
|
|
|
|
if((pa.m_first_child == ib && pa.m_last_child == ia)
|
|
|
|
|
||
|
|
|
|
|
(pa.m_first_child == ia && pa.m_last_child == ib))
|
|
|
|
|
{
|
|
|
|
|
std::swap(pa.m_first_child, pa.m_last_child);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bool changed = false;
|
|
|
|
|
if(pa.m_first_child == ia)
|
|
|
|
|
{
|
|
|
|
|
pa.m_first_child = ib;
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
if(pa.m_last_child == ia)
|
|
|
|
|
{
|
|
|
|
|
pa.m_last_child = ib;
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
if(pb.m_first_child == ib && !changed)
|
|
|
|
|
{
|
|
|
|
|
pb.m_first_child = ia;
|
|
|
|
|
}
|
|
|
|
|
if(pb.m_last_child == ib && !changed)
|
|
|
|
|
{
|
|
|
|
|
pb.m_last_child = ia;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(pa.m_first_child == ia)
|
|
|
|
|
pa.m_first_child = ib;
|
|
|
|
|
if(pa.m_last_child == ia)
|
|
|
|
|
pa.m_last_child = ib;
|
|
|
|
|
if(pb.m_first_child == ib)
|
|
|
|
|
pb.m_first_child = ia;
|
|
|
|
|
if(pb.m_last_child == ib)
|
|
|
|
|
pb.m_last_child = ia;
|
|
|
|
|
}
|
|
|
|
|
std::swap(a.m_first_child , b.m_first_child);
|
|
|
|
|
std::swap(a.m_last_child , b.m_last_child);
|
|
|
|
|
|
|
|
|
|
if(a.m_prev_sibling != ib && b.m_prev_sibling != ia &&
|
|
|
|
|
a.m_next_sibling != ib && b.m_next_sibling != ia)
|
|
|
|
|
{
|
|
|
|
|
if(a.m_prev_sibling != NONE && a.m_prev_sibling != ib)
|
|
|
|
|
_p(a.m_prev_sibling)->m_next_sibling = ib;
|
|
|
|
|
if(a.m_next_sibling != NONE && a.m_next_sibling != ib)
|
|
|
|
|
_p(a.m_next_sibling)->m_prev_sibling = ib;
|
|
|
|
|
if(b.m_prev_sibling != NONE && b.m_prev_sibling != ia)
|
|
|
|
|
_p(b.m_prev_sibling)->m_next_sibling = ia;
|
|
|
|
|
if(b.m_next_sibling != NONE && b.m_next_sibling != ia)
|
|
|
|
|
_p(b.m_next_sibling)->m_prev_sibling = ia;
|
|
|
|
|
std::swap(a.m_prev_sibling, b.m_prev_sibling);
|
|
|
|
|
std::swap(a.m_next_sibling, b.m_next_sibling);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(a.m_next_sibling == ib) // n will go after m
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, b.m_prev_sibling == ia);
|
|
|
|
|
if(a.m_prev_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, a.m_prev_sibling != ib);
|
|
|
|
|
_p(a.m_prev_sibling)->m_next_sibling = ib;
|
|
|
|
|
}
|
|
|
|
|
if(b.m_next_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, b.m_next_sibling != ia);
|
|
|
|
|
_p(b.m_next_sibling)->m_prev_sibling = ia;
|
|
|
|
|
}
|
|
|
|
|
size_t ns = b.m_next_sibling;
|
|
|
|
|
b.m_prev_sibling = a.m_prev_sibling;
|
|
|
|
|
b.m_next_sibling = ia;
|
|
|
|
|
a.m_prev_sibling = ib;
|
|
|
|
|
a.m_next_sibling = ns;
|
|
|
|
|
}
|
|
|
|
|
else if(a.m_prev_sibling == ib) // m will go after n
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, b.m_next_sibling == ia);
|
|
|
|
|
if(b.m_prev_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, b.m_prev_sibling != ia);
|
|
|
|
|
_p(b.m_prev_sibling)->m_next_sibling = ia;
|
|
|
|
|
}
|
|
|
|
|
if(a.m_next_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, a.m_next_sibling != ib);
|
|
|
|
|
_p(a.m_next_sibling)->m_prev_sibling = ib;
|
|
|
|
|
}
|
|
|
|
|
size_t ns = b.m_prev_sibling;
|
|
|
|
|
a.m_prev_sibling = b.m_prev_sibling;
|
|
|
|
|
a.m_next_sibling = ib;
|
|
|
|
|
b.m_prev_sibling = ia;
|
|
|
|
|
b.m_next_sibling = ns;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
C4_NEVER_REACH();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, a.m_next_sibling != ia);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, a.m_prev_sibling != ia);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, b.m_next_sibling != ib);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, b.m_prev_sibling != ib);
|
|
|
|
|
|
|
|
|
|
if(a.m_parent != ib && b.m_parent != ia)
|
|
|
|
|
{
|
|
|
|
|
std::swap(a.m_parent, b.m_parent);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(a.m_parent == ib && b.m_parent != ia)
|
|
|
|
|
{
|
|
|
|
|
a.m_parent = b.m_parent;
|
|
|
|
|
b.m_parent = ia;
|
|
|
|
|
}
|
|
|
|
|
else if(a.m_parent != ib && b.m_parent == ia)
|
|
|
|
|
{
|
|
|
|
|
b.m_parent = a.m_parent;
|
|
|
|
|
a.m_parent = ib;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
C4_NEVER_REACH();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::_copy_hierarchy(size_t dst_, size_t src_)
|
|
|
|
|
{
|
|
|
|
|
auto const& C4_RESTRICT src = *_p(src_);
|
|
|
|
|
auto & C4_RESTRICT dst = *_p(dst_);
|
|
|
|
|
auto & C4_RESTRICT prt = *_p(src.m_parent);
|
|
|
|
|
for(size_t i = src.m_first_child; i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
_p(i)->m_parent = dst_;
|
|
|
|
|
}
|
|
|
|
|
if(src.m_prev_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
_p(src.m_prev_sibling)->m_next_sibling = dst_;
|
|
|
|
|
}
|
|
|
|
|
if(src.m_next_sibling != NONE)
|
|
|
|
|
{
|
|
|
|
|
_p(src.m_next_sibling)->m_prev_sibling = dst_;
|
|
|
|
|
}
|
|
|
|
|
if(prt.m_first_child == src_)
|
|
|
|
|
{
|
|
|
|
|
prt.m_first_child = dst_;
|
|
|
|
|
}
|
|
|
|
|
if(prt.m_last_child == src_)
|
|
|
|
|
{
|
|
|
|
|
prt.m_last_child = dst_;
|
|
|
|
|
}
|
|
|
|
|
dst.m_parent = src.m_parent;
|
|
|
|
|
dst.m_first_child = src.m_first_child;
|
|
|
|
|
dst.m_last_child = src.m_last_child;
|
|
|
|
|
dst.m_prev_sibling = src.m_prev_sibling;
|
|
|
|
|
dst.m_next_sibling = src.m_next_sibling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::_swap_props(size_t n_, size_t m_)
|
|
|
|
|
{
|
|
|
|
|
NodeData &C4_RESTRICT n = *_p(n_);
|
|
|
|
|
NodeData &C4_RESTRICT m = *_p(m_);
|
|
|
|
|
std::swap(n.m_type, m.m_type);
|
|
|
|
|
std::swap(n.m_key, m.m_key);
|
|
|
|
|
std::swap(n.m_val, m.m_val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::move(size_t node, size_t after)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! is_root(node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, has_sibling(node, after) && has_sibling(after, node));
|
|
|
|
|
|
|
|
|
|
_rem_hierarchy(node);
|
|
|
|
|
_set_hierarchy(node, parent(node), after);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void Tree::move(size_t node, size_t new_parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, new_parent != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! is_root(node));
|
|
|
|
|
|
|
|
|
|
_rem_hierarchy(node);
|
|
|
|
|
_set_hierarchy(node, new_parent, after);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::move(Tree *src, size_t node, size_t new_parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, new_parent != NONE);
|
|
|
|
|
|
|
|
|
|
size_t dup = duplicate(src, node, new_parent, after);
|
|
|
|
|
src->remove(node);
|
|
|
|
|
return dup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::set_root_as_stream()
|
|
|
|
|
{
|
|
|
|
|
size_t root = root_id();
|
|
|
|
|
if(is_stream(root))
|
|
|
|
|
return;
|
|
|
|
|
// don't use _add_flags() because it's checked and will fail
|
|
|
|
|
if(!has_children(root))
|
|
|
|
|
{
|
|
|
|
|
if(is_val(root))
|
|
|
|
|
{
|
|
|
|
|
_p(root)->m_type.add(SEQ);
|
|
|
|
|
size_t next_doc = append_child(root);
|
|
|
|
|
_copy_props_wo_key(next_doc, root);
|
|
|
|
|
_p(next_doc)->m_type.add(DOC);
|
|
|
|
|
_p(next_doc)->m_type.rem(SEQ);
|
|
|
|
|
}
|
|
|
|
|
_p(root)->m_type = STREAM;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, !has_key(root));
|
|
|
|
|
size_t next_doc = append_child(root);
|
|
|
|
|
_copy_props_wo_key(next_doc, root);
|
|
|
|
|
_add_flags(next_doc, DOC);
|
|
|
|
|
for(size_t prev = NONE, ch = first_child(root), next = next_sibling(ch); ch != NONE; )
|
|
|
|
|
{
|
|
|
|
|
if(ch == next_doc)
|
|
|
|
|
break;
|
|
|
|
|
move(ch, next_doc, prev);
|
|
|
|
|
prev = ch;
|
|
|
|
|
ch = next;
|
|
|
|
|
next = next_sibling(next);
|
|
|
|
|
}
|
|
|
|
|
_p(root)->m_type = STREAM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::remove_children(size_t node)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, get(node) != nullptr);
|
|
|
|
|
size_t ich = get(node)->m_first_child;
|
|
|
|
|
while(ich != NONE)
|
|
|
|
|
{
|
|
|
|
|
remove_children(ich);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, get(ich) != nullptr);
|
|
|
|
|
size_t next = get(ich)->m_next_sibling;
|
|
|
|
|
_release(ich);
|
|
|
|
|
if(ich == get(node)->m_last_child)
|
|
|
|
|
break;
|
|
|
|
|
ich = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Tree::change_type(size_t node, NodeType type)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, type.is_val() || type.is_map() || type.is_seq());
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, type.is_val() + type.is_map() + type.is_seq() == 1);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, type.has_key() == has_key(node) || (has_key(node) && !type.has_key()));
|
|
|
|
|
NodeData *d = _p(node);
|
|
|
|
|
if(type.is_map() && is_map(node))
|
|
|
|
|
return false;
|
|
|
|
|
else if(type.is_seq() && is_seq(node))
|
|
|
|
|
return false;
|
|
|
|
|
else if(type.is_val() && is_val(node))
|
|
|
|
|
return false;
|
|
|
|
|
d->m_type = (d->m_type & (~(MAP|SEQ|VAL))) | type;
|
|
|
|
|
remove_children(node);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
size_t Tree::duplicate(size_t node, size_t parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
return duplicate(this, node, parent, after);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::duplicate(Tree const* src, size_t node, size_t parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! src->is_root(node));
|
|
|
|
|
|
|
|
|
|
size_t copy = _claim();
|
|
|
|
|
|
|
|
|
|
_copy_props(copy, src, node);
|
|
|
|
|
_set_hierarchy(copy, parent, after);
|
|
|
|
|
duplicate_children(src, node, copy, NONE);
|
|
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
size_t Tree::duplicate_children(size_t node, size_t parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
return duplicate_children(this, node, parent, after);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::duplicate_children(Tree const* src, size_t node, size_t parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, after == NONE || has_child(parent, after));
|
|
|
|
|
|
|
|
|
|
size_t prev = after;
|
|
|
|
|
for(size_t i = src->first_child(node); i != NONE; i = src->next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
prev = duplicate(src, i, parent, prev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return prev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void Tree::duplicate_contents(size_t node, size_t where)
|
|
|
|
|
{
|
|
|
|
|
duplicate_contents(this, node, where);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::duplicate_contents(Tree const *src, size_t node, size_t where)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, where != NONE);
|
|
|
|
|
_copy_props_wo_key(where, src, node);
|
|
|
|
|
duplicate_children(src, node, where, last_child(where));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
size_t Tree::duplicate_children_no_rep(size_t node, size_t parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
return duplicate_children_no_rep(this, node, parent, after);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::duplicate_children_no_rep(Tree const *src, size_t node, size_t parent, size_t after)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, after == NONE || has_child(parent, after));
|
|
|
|
|
|
|
|
|
|
// don't loop using pointers as there may be a relocation
|
|
|
|
|
|
|
|
|
|
// find the position where "after" is
|
|
|
|
|
size_t after_pos = NONE;
|
|
|
|
|
if(after != NONE)
|
|
|
|
|
{
|
|
|
|
|
for(size_t i = first_child(parent), icount = 0; i != NONE; ++icount, i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
if(i == after)
|
|
|
|
|
{
|
|
|
|
|
after_pos = icount;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, after_pos != NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for each child to be duplicated...
|
|
|
|
|
size_t prev = after;
|
|
|
|
|
for(size_t i = src->first_child(node), icount = 0; i != NONE; ++icount, i = src->next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
if(is_seq(parent))
|
|
|
|
|
{
|
|
|
|
|
prev = duplicate(i, parent, prev);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_map(parent));
|
|
|
|
|
// does the parent already have a node with key equal to that of the current duplicate?
|
|
|
|
|
size_t rep = NONE, rep_pos = NONE;
|
|
|
|
|
for(size_t j = first_child(parent), jcount = 0; j != NONE; ++jcount, j = next_sibling(j))
|
|
|
|
|
{
|
|
|
|
|
if(key(j) == key(i))
|
|
|
|
|
{
|
|
|
|
|
rep = j;
|
|
|
|
|
rep_pos = jcount;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(rep == NONE) // there is no repetition; just duplicate
|
|
|
|
|
{
|
|
|
|
|
prev = duplicate(src, i, parent, prev);
|
|
|
|
|
}
|
|
|
|
|
else // yes, there is a repetition
|
|
|
|
|
{
|
|
|
|
|
if(after_pos != NONE && rep_pos < after_pos)
|
|
|
|
|
{
|
|
|
|
|
// rep is located before the node which will be inserted,
|
|
|
|
|
// and will be overridden by the duplicate. So replace it.
|
|
|
|
|
remove(rep);
|
|
|
|
|
prev = duplicate(src, i, parent, prev);
|
|
|
|
|
}
|
|
|
|
|
else if(after_pos == NONE || rep_pos >= after_pos)
|
|
|
|
|
{
|
|
|
|
|
// rep is located after the node which will be inserted
|
|
|
|
|
// and overrides it. So move the rep into this node's place.
|
|
|
|
|
if(rep != prev)
|
|
|
|
|
{
|
|
|
|
|
move(rep, prev);
|
|
|
|
|
prev = rep;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // there's a repetition
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return prev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void Tree::merge_with(Tree const *src, size_t src_node, size_t dst_node)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
|
|
|
|
|
if(src_node == NONE)
|
|
|
|
|
src_node = src->root_id();
|
|
|
|
|
if(dst_node == NONE)
|
|
|
|
|
dst_node = root_id();
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, src->has_val(src_node) || src->is_seq(src_node) || src->is_map(src_node));
|
|
|
|
|
|
|
|
|
|
if(src->has_val(src_node))
|
|
|
|
|
{
|
|
|
|
|
if( ! has_val(dst_node))
|
|
|
|
|
{
|
|
|
|
|
if(has_children(dst_node))
|
|
|
|
|
remove_children(dst_node);
|
|
|
|
|
}
|
|
|
|
|
if(src->is_keyval(src_node))
|
|
|
|
|
_copy_props(dst_node, src, src_node);
|
|
|
|
|
else if(src->is_val(src_node))
|
|
|
|
|
_copy_props_wo_key(dst_node, src, src_node);
|
|
|
|
|
else
|
|
|
|
|
C4_NEVER_REACH();
|
|
|
|
|
}
|
|
|
|
|
else if(src->is_seq(src_node))
|
|
|
|
|
{
|
|
|
|
|
if( ! is_seq(dst_node))
|
|
|
|
|
{
|
|
|
|
|
if(has_children(dst_node))
|
|
|
|
|
remove_children(dst_node);
|
|
|
|
|
_clear_type(dst_node);
|
|
|
|
|
if(src->has_key(src_node))
|
|
|
|
|
to_seq(dst_node, src->key(src_node));
|
|
|
|
|
else
|
|
|
|
|
to_seq(dst_node);
|
|
|
|
|
}
|
|
|
|
|
for(size_t sch = src->first_child(src_node); sch != NONE; sch = src->next_sibling(sch))
|
|
|
|
|
{
|
|
|
|
|
size_t dch = append_child(dst_node);
|
|
|
|
|
_copy_props_wo_key(dch, src, sch);
|
|
|
|
|
merge_with(src, sch, dch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(src->is_map(src_node))
|
|
|
|
|
{
|
|
|
|
|
if( ! is_map(dst_node))
|
|
|
|
|
{
|
|
|
|
|
if(has_children(dst_node))
|
|
|
|
|
remove_children(dst_node);
|
|
|
|
|
_clear_type(dst_node);
|
|
|
|
|
if(src->has_key(src_node))
|
|
|
|
|
to_map(dst_node, src->key(src_node));
|
|
|
|
|
else
|
|
|
|
|
to_map(dst_node);
|
|
|
|
|
}
|
|
|
|
|
for(size_t sch = src->first_child(src_node); sch != NONE; sch = src->next_sibling(sch))
|
|
|
|
|
{
|
|
|
|
|
size_t dch = find_child(dst_node, src->key(sch));
|
|
|
|
|
if(dch == NONE)
|
|
|
|
|
{
|
|
|
|
|
dch = append_child(dst_node);
|
|
|
|
|
_copy_props(dch, src, sch);
|
|
|
|
|
}
|
|
|
|
|
merge_with(src, sch, dch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
C4_NEVER_REACH();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
/** @todo make this part of the public API, refactoring as appropriate
|
|
|
|
|
* to be able to use the same resolver to handle multiple trees (one
|
|
|
|
|
* at a time) */
|
|
|
|
|
struct ReferenceResolver
|
|
|
|
|
{
|
|
|
|
|
struct refdata
|
|
|
|
|
{
|
|
|
|
|
NodeType type;
|
|
|
|
|
size_t node;
|
|
|
|
|
size_t prev_anchor;
|
|
|
|
|
size_t target;
|
|
|
|
|
size_t parent_ref;
|
|
|
|
|
size_t parent_ref_sibling;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Tree *t;
|
|
|
|
|
/** from the specs: "an alias node refers to the most recent
|
|
|
|
|
* node in the serialization having the specified anchor". So
|
|
|
|
|
* we need to start looking upward from ref nodes.
|
|
|
|
|
*
|
|
|
|
|
* @see http://yaml.org/spec/1.2/spec.html#id2765878 */
|
|
|
|
|
stack<refdata> refs;
|
|
|
|
|
|
|
|
|
|
ReferenceResolver(Tree *t_) : t(t_), refs(t_->callbacks())
|
|
|
|
|
{
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void store_anchors_and_refs()
|
|
|
|
|
{
|
|
|
|
|
// minimize (re-)allocations by counting first
|
|
|
|
|
size_t num_anchors_and_refs = count_anchors_and_refs(t->root_id());
|
|
|
|
|
if(!num_anchors_and_refs)
|
|
|
|
|
return;
|
|
|
|
|
refs.reserve(num_anchors_and_refs);
|
|
|
|
|
|
|
|
|
|
// now descend through the hierarchy
|
|
|
|
|
_store_anchors_and_refs(t->root_id());
|
|
|
|
|
|
|
|
|
|
// finally connect the reference list
|
|
|
|
|
size_t prev_anchor = npos;
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
for(auto &rd : refs)
|
|
|
|
|
{
|
|
|
|
|
rd.prev_anchor = prev_anchor;
|
|
|
|
|
if(rd.type.is_anchor())
|
|
|
|
|
prev_anchor = count;
|
|
|
|
|
++count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t count_anchors_and_refs(size_t n)
|
|
|
|
|
{
|
|
|
|
|
size_t c = 0;
|
|
|
|
|
c += t->has_key_anchor(n);
|
|
|
|
|
c += t->has_val_anchor(n);
|
|
|
|
|
c += t->is_key_ref(n);
|
|
|
|
|
c += t->is_val_ref(n);
|
|
|
|
|
for(size_t ch = t->first_child(n); ch != NONE; ch = t->next_sibling(ch))
|
|
|
|
|
c += count_anchors_and_refs(ch);
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _store_anchors_and_refs(size_t n)
|
|
|
|
|
{
|
|
|
|
|
if(t->is_key_ref(n) || t->is_val_ref(n) || (t->has_key(n) && t->key(n) == "<<"))
|
|
|
|
|
{
|
|
|
|
|
if(t->is_seq(n))
|
|
|
|
|
{
|
|
|
|
|
// for merging multiple inheritance targets
|
|
|
|
|
// <<: [ *CENTER, *BIG ]
|
|
|
|
|
for(size_t ich = t->first_child(n); ich != NONE; ich = t->next_sibling(ich))
|
|
|
|
|
{
|
|
|
|
|
RYML_ASSERT(t->num_children(ich) == 0);
|
|
|
|
|
refs.push({VALREF, ich, npos, npos, n, t->next_sibling(n)});
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(t->is_key_ref(n) && t->key(n) != "<<") // insert key refs BEFORE inserting val refs
|
|
|
|
|
{
|
|
|
|
|
RYML_CHECK((!t->has_key(n)) || t->key(n).ends_with(t->key_ref(n)));
|
|
|
|
|
refs.push({KEYREF, n, npos, npos, NONE, NONE});
|
|
|
|
|
}
|
|
|
|
|
if(t->is_val_ref(n))
|
|
|
|
|
{
|
|
|
|
|
RYML_CHECK((!t->has_val(n)) || t->val(n).ends_with(t->val_ref(n)));
|
|
|
|
|
refs.push({VALREF, n, npos, npos, NONE, NONE});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(t->has_key_anchor(n))
|
|
|
|
|
{
|
|
|
|
|
RYML_CHECK(t->has_key(n));
|
|
|
|
|
refs.push({KEYANCH, n, npos, npos, NONE, NONE});
|
|
|
|
|
}
|
|
|
|
|
if(t->has_val_anchor(n))
|
|
|
|
|
{
|
|
|
|
|
RYML_CHECK(t->has_val(n) || t->is_container(n));
|
|
|
|
|
refs.push({VALANCH, n, npos, npos, NONE, NONE});
|
|
|
|
|
}
|
|
|
|
|
for(size_t ch = t->first_child(n); ch != NONE; ch = t->next_sibling(ch))
|
|
|
|
|
{
|
|
|
|
|
_store_anchors_and_refs(ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t lookup_(refdata *C4_RESTRICT ra)
|
|
|
|
|
{
|
|
|
|
|
RYML_ASSERT(ra->type.is_key_ref() || ra->type.is_val_ref());
|
|
|
|
|
RYML_ASSERT(ra->type.is_key_ref() != ra->type.is_val_ref());
|
|
|
|
|
csubstr refname;
|
|
|
|
|
if(ra->type.is_val_ref())
|
|
|
|
|
{
|
|
|
|
|
refname = t->val_ref(ra->node);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RYML_ASSERT(ra->type.is_key_ref());
|
|
|
|
|
refname = t->key_ref(ra->node);
|
|
|
|
|
}
|
|
|
|
|
while(ra->prev_anchor != npos)
|
|
|
|
|
{
|
|
|
|
|
ra = &refs[ra->prev_anchor];
|
|
|
|
|
if(t->has_anchor(ra->node, refname))
|
|
|
|
|
return ra->node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef RYML_ERRMSG_SIZE
|
|
|
|
|
#define RYML_ERRMSG_SIZE 1024
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
char errmsg[RYML_ERRMSG_SIZE];
|
|
|
|
|
snprintf(errmsg, RYML_ERRMSG_SIZE, "anchor does not exist: '%.*s'",
|
|
|
|
|
static_cast<int>(refname.size()), refname.data());
|
|
|
|
|
c4::yml::error(errmsg);
|
|
|
|
|
return NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void resolve()
|
|
|
|
|
{
|
|
|
|
|
store_anchors_and_refs();
|
|
|
|
|
if(refs.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* from the specs: "an alias node refers to the most recent
|
|
|
|
|
* node in the serialization having the specified anchor". So
|
|
|
|
|
* we need to start looking upward from ref nodes.
|
|
|
|
|
*
|
|
|
|
|
* @see http://yaml.org/spec/1.2/spec.html#id2765878 */
|
|
|
|
|
for(size_t i = 0, e = refs.size(); i < e; ++i)
|
|
|
|
|
{
|
|
|
|
|
auto &C4_RESTRICT rd = refs.top(i);
|
|
|
|
|
if( ! rd.type.is_ref())
|
|
|
|
|
continue;
|
|
|
|
|
rd.target = lookup_(&rd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}; // ReferenceResolver
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
void Tree::resolve()
|
|
|
|
|
{
|
|
|
|
|
if(m_size == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
detail::ReferenceResolver rr(this);
|
|
|
|
|
|
|
|
|
|
// insert the resolved references
|
|
|
|
|
size_t prev_parent_ref = NONE;
|
|
|
|
|
size_t prev_parent_ref_after = NONE;
|
|
|
|
|
for(auto const& C4_RESTRICT rd : rr.refs)
|
|
|
|
|
{
|
|
|
|
|
if( ! rd.type.is_ref())
|
|
|
|
|
continue;
|
|
|
|
|
if(rd.parent_ref != NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_seq(rd.parent_ref));
|
|
|
|
|
size_t after, p = parent(rd.parent_ref);
|
|
|
|
|
if(prev_parent_ref != rd.parent_ref)
|
|
|
|
|
{
|
|
|
|
|
after = rd.parent_ref;//prev_sibling(rd.parent_ref_sibling);
|
|
|
|
|
prev_parent_ref_after = after;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
after = prev_parent_ref_after;
|
|
|
|
|
}
|
|
|
|
|
prev_parent_ref = rd.parent_ref;
|
|
|
|
|
prev_parent_ref_after = duplicate_children_no_rep(rd.target, p, after);
|
|
|
|
|
remove(rd.node);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(has_key(rd.node) && is_key_ref(rd.node) && key(rd.node) == "<<")
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_keyval(rd.node));
|
|
|
|
|
size_t p = parent(rd.node);
|
|
|
|
|
size_t after = prev_sibling(rd.node);
|
|
|
|
|
duplicate_children_no_rep(rd.target, p, after);
|
|
|
|
|
remove(rd.node);
|
|
|
|
|
}
|
|
|
|
|
else if(rd.type.is_key_ref())
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_key_ref(rd.node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, has_key_anchor(rd.target) || has_val_anchor(rd.target));
|
|
|
|
|
if(has_val_anchor(rd.target) && val_anchor(rd.target) == key_ref(rd.node))
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, !is_container(rd.target));
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, has_val(rd.target));
|
|
|
|
|
_p(rd.node)->m_key.scalar = val(rd.target);
|
|
|
|
|
_add_flags(rd.node, KEY);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, key_anchor(rd.target) == key_ref(rd.node));
|
|
|
|
|
_p(rd.node)->m_key.scalar = key(rd.target);
|
|
|
|
|
_add_flags(rd.node, VAL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, rd.type.is_val_ref());
|
|
|
|
|
if(has_key_anchor(rd.target) && key_anchor(rd.target) == val_ref(rd.node))
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, !is_container(rd.target));
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, has_val(rd.target));
|
|
|
|
|
_p(rd.node)->m_val.scalar = key(rd.target);
|
|
|
|
|
_add_flags(rd.node, VAL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
duplicate_contents(rd.target, rd.node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clear anchors and refs
|
|
|
|
|
for(auto const& C4_RESTRICT ar : rr.refs)
|
|
|
|
|
{
|
|
|
|
|
rem_anchor_ref(ar.node);
|
|
|
|
|
if(ar.parent_ref != NONE)
|
|
|
|
|
if(type(ar.parent_ref) != NOTYPE)
|
|
|
|
|
remove(ar.parent_ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
size_t Tree::num_children(size_t node) const
|
|
|
|
|
{
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
++count;
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::child(size_t node, size_t pos) const
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
if(count++ == pos)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::child_pos(size_t node, size_t ch) const
|
|
|
|
|
{
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
if(i == ch)
|
|
|
|
|
return count;
|
|
|
|
|
++count;
|
|
|
|
|
}
|
|
|
|
|
return npos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(__clang__)
|
|
|
|
|
# pragma clang diagnostic push
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wnull-dereference"
|
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
|
# pragma GCC diagnostic push
|
|
|
|
|
# if __GNUC__ >= 6
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wnull-dereference"
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
size_t Tree::find_child(size_t node, csubstr const& name) const
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_map(node));
|
|
|
|
|
if(get(node)->m_first_child == NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, _p(node)->m_last_child == NONE);
|
|
|
|
|
return NONE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, _p(node)->m_last_child != NONE);
|
|
|
|
|
}
|
|
|
|
|
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
|
|
|
|
|
{
|
|
|
|
|
if(_p(i)->m_key.scalar == name)
|
|
|
|
|
{
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(__clang__)
|
|
|
|
|
# pragma clang diagnostic pop
|
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
|
# pragma GCC diagnostic pop
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void Tree::to_val(size_t node, csubstr val, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || ! parent_is_map(node));
|
|
|
|
|
_set_flags(node, VAL|more_flags);
|
|
|
|
|
_p(node)->m_key.clear();
|
|
|
|
|
_p(node)->m_val = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::to_keyval(size_t node, csubstr key, csubstr val, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_map(node));
|
|
|
|
|
_set_flags(node, KEYVAL|more_flags);
|
|
|
|
|
_p(node)->m_key = key;
|
|
|
|
|
_p(node)->m_val = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::to_map(size_t node, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || ! parent_is_map(node)); // parent must not have children with keys
|
|
|
|
|
_set_flags(node, MAP|more_flags);
|
|
|
|
|
_p(node)->m_key.clear();
|
|
|
|
|
_p(node)->m_val.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::to_map(size_t node, csubstr key, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_map(node));
|
|
|
|
|
_set_flags(node, KEY|MAP|more_flags);
|
|
|
|
|
_p(node)->m_key = key;
|
|
|
|
|
_p(node)->m_val.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::to_seq(size_t node, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_seq(node));
|
|
|
|
|
_set_flags(node, SEQ|more_flags);
|
|
|
|
|
_p(node)->m_key.clear();
|
|
|
|
|
_p(node)->m_val.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::to_seq(size_t node, csubstr key, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_map(node));
|
|
|
|
|
_set_flags(node, KEY|SEQ|more_flags);
|
|
|
|
|
_p(node)->m_key = key;
|
|
|
|
|
_p(node)->m_val.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::to_doc(size_t node, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_set_flags(node, DOC|more_flags);
|
|
|
|
|
_p(node)->m_key.clear();
|
|
|
|
|
_p(node)->m_val.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::to_stream(size_t node, type_bits more_flags)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
|
|
|
|
|
_set_flags(node, STREAM|more_flags);
|
|
|
|
|
_p(node)->m_key.clear();
|
|
|
|
|
_p(node)->m_val.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
size_t Tree::num_tag_directives() const
|
|
|
|
|
{
|
|
|
|
|
// this assumes we have a very small number of tag directives
|
|
|
|
|
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
|
|
|
|
|
if(m_tag_directives[i].handle.empty())
|
|
|
|
|
return i;
|
|
|
|
|
return RYML_MAX_TAG_DIRECTIVES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::clear_tag_directives()
|
|
|
|
|
{
|
|
|
|
|
for(TagDirective &td : m_tag_directives)
|
|
|
|
|
td = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::add_tag_directive(TagDirective const& td)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, !td.handle.empty());
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, !td.prefix.empty());
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, td.handle.begins_with('!'));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, td.handle.ends_with('!'));
|
|
|
|
|
// https://yaml.org/spec/1.2.2/#rule-ns-word-char
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, td.handle == '!' || td.handle == "!!" || td.handle.trim('!').first_not_of("01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-") == npos);
|
|
|
|
|
size_t pos = num_tag_directives();
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, pos < RYML_MAX_TAG_DIRECTIVES);
|
|
|
|
|
m_tag_directives[pos] = td;
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::resolve_tag(substr output, csubstr tag, size_t node_id) const
|
|
|
|
|
{
|
|
|
|
|
// lookup from the end. We want to find the first directive that
|
|
|
|
|
// matches the tag and has a target node id leq than the given
|
|
|
|
|
// node_id.
|
|
|
|
|
for(size_t i = RYML_MAX_TAG_DIRECTIVES-1; i != (size_t)-1; --i)
|
|
|
|
|
{
|
|
|
|
|
auto const& td = m_tag_directives[i];
|
|
|
|
|
if(td.handle.empty())
|
|
|
|
|
continue;
|
|
|
|
|
if(tag.begins_with(td.handle) && td.next_node_id <= node_id)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, tag.len >= td.handle.len);
|
|
|
|
|
csubstr rest = tag.sub(td.handle.len);
|
|
|
|
|
size_t len = 1u + td.prefix.len + rest.len + 1u;
|
|
|
|
|
size_t numpc = rest.count('%');
|
|
|
|
|
if(numpc == 0)
|
|
|
|
|
{
|
|
|
|
|
if(len <= output.len)
|
|
|
|
|
{
|
|
|
|
|
output.str[0] = '<';
|
|
|
|
|
memcpy(1u + output.str, td.prefix.str, td.prefix.len);
|
|
|
|
|
memcpy(1u + output.str + td.prefix.len, rest.str, rest.len);
|
|
|
|
|
output.str[1u + td.prefix.len + rest.len] = '>';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// need to decode URI % sequences
|
|
|
|
|
size_t pos = rest.find('%');
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, pos != npos);
|
|
|
|
|
do {
|
|
|
|
|
size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
|
|
|
|
|
if(next == npos)
|
|
|
|
|
next = rest.len;
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, pos+1 < next);
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, pos+1 + 2 <= next);
|
|
|
|
|
size_t delta = next - (pos+1);
|
|
|
|
|
len -= delta;
|
|
|
|
|
pos = rest.find('%', pos+1);
|
|
|
|
|
} while(pos != npos);
|
|
|
|
|
if(len <= output.len)
|
|
|
|
|
{
|
|
|
|
|
size_t prev = 0, wpos = 0;
|
|
|
|
|
auto appendstr = [&](csubstr s) { memcpy(output.str + wpos, s.str, s.len); wpos += s.len; };
|
|
|
|
|
auto appendchar = [&](char c) { output.str[wpos++] = c; };
|
|
|
|
|
appendchar('<');
|
|
|
|
|
appendstr(td.prefix);
|
|
|
|
|
pos = rest.find('%');
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, pos != npos);
|
|
|
|
|
do {
|
|
|
|
|
size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
|
|
|
|
|
if(next == npos)
|
|
|
|
|
next = rest.len;
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, pos+1 < next);
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, pos+1 + 2 <= next);
|
|
|
|
|
uint8_t val;
|
|
|
|
|
if(C4_UNLIKELY(!read_hex(rest.range(pos+1, next), &val) || val > 127))
|
|
|
|
|
_RYML_CB_ERR(m_callbacks, "invalid URI character");
|
|
|
|
|
appendstr(rest.range(prev, pos));
|
|
|
|
|
appendchar((char)val);
|
|
|
|
|
prev = next;
|
|
|
|
|
pos = rest.find('%', pos+1);
|
|
|
|
|
} while(pos != npos);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, pos == npos);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, prev > 0);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, rest.len >= prev);
|
|
|
|
|
appendstr(rest.sub(prev));
|
|
|
|
|
appendchar('>');
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, wpos == len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0; // return 0 to signal that the tag is local and cannot be resolved
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
csubstr _transform_tag(Tree *t, csubstr tag, size_t node)
|
|
|
|
|
{
|
|
|
|
|
size_t required_size = t->resolve_tag(substr{}, tag, node);
|
|
|
|
|
if(!required_size)
|
|
|
|
|
return tag;
|
|
|
|
|
const char *prev_arena = t->arena().str;
|
|
|
|
|
substr buf = t->alloc_arena(required_size);
|
|
|
|
|
_RYML_CB_ASSERT(t->m_callbacks, t->arena().str == prev_arena);
|
|
|
|
|
size_t actual_size = t->resolve_tag(buf, tag, node);
|
|
|
|
|
_RYML_CB_ASSERT(t->m_callbacks, actual_size <= required_size);
|
|
|
|
|
return buf.first(actual_size);
|
|
|
|
|
}
|
|
|
|
|
void _resolve_tags(Tree *t, size_t node)
|
|
|
|
|
{
|
|
|
|
|
for(size_t child = t->first_child(node); child != NONE; child = t->next_sibling(child))
|
|
|
|
|
{
|
|
|
|
|
if(t->has_key(child) && t->has_key_tag(child))
|
|
|
|
|
t->set_key_tag(child, _transform_tag(t, t->key_tag(child), child));
|
|
|
|
|
if(t->has_val(child) && t->has_val_tag(child))
|
|
|
|
|
t->set_val_tag(child, _transform_tag(t, t->val_tag(child), child));
|
|
|
|
|
_resolve_tags(t, child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
size_t _count_resolved_tags_size(Tree const* t, size_t node)
|
|
|
|
|
{
|
|
|
|
|
size_t sz = 0;
|
|
|
|
|
for(size_t child = t->first_child(node); child != NONE; child = t->next_sibling(child))
|
|
|
|
|
{
|
|
|
|
|
if(t->has_key(child) && t->has_key_tag(child))
|
|
|
|
|
sz += t->resolve_tag(substr{}, t->key_tag(child), child);
|
|
|
|
|
if(t->has_val(child) && t->has_val_tag(child))
|
|
|
|
|
sz += t->resolve_tag(substr{}, t->val_tag(child), child);
|
|
|
|
|
sz += _count_resolved_tags_size(t, child);
|
|
|
|
|
}
|
|
|
|
|
return sz;
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
void Tree::resolve_tags()
|
|
|
|
|
{
|
|
|
|
|
if(empty())
|
|
|
|
|
return;
|
|
|
|
|
if(num_tag_directives() == 0)
|
|
|
|
|
return;
|
|
|
|
|
size_t needed_size = _count_resolved_tags_size(this, root_id());
|
|
|
|
|
if(needed_size)
|
|
|
|
|
reserve_arena(arena_pos() + needed_size);
|
|
|
|
|
_resolve_tags(this, root_id());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
csubstr Tree::lookup_result::resolved() const
|
|
|
|
|
{
|
|
|
|
|
csubstr p = path.first(path_pos);
|
|
|
|
|
if(p.ends_with('.'))
|
|
|
|
|
p = p.first(p.len-1);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
csubstr Tree::lookup_result::unresolved() const
|
|
|
|
|
{
|
|
|
|
|
return path.sub(path_pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_advance(lookup_result *r, size_t more) const
|
|
|
|
|
{
|
|
|
|
|
r->path_pos += more;
|
|
|
|
|
if(r->path.sub(r->path_pos).begins_with('.'))
|
|
|
|
|
++r->path_pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tree::lookup_result Tree::lookup_path(csubstr path, size_t start) const
|
|
|
|
|
{
|
|
|
|
|
if(start == NONE)
|
|
|
|
|
start = root_id();
|
|
|
|
|
lookup_result r(path, start);
|
|
|
|
|
if(path.empty())
|
|
|
|
|
return r;
|
|
|
|
|
_lookup_path(&r);
|
|
|
|
|
if(r.target == NONE && r.closest == start)
|
|
|
|
|
r.closest = NONE;
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::lookup_path_or_modify(csubstr default_value, csubstr path, size_t start)
|
|
|
|
|
{
|
|
|
|
|
size_t target = _lookup_path_or_create(path, start);
|
|
|
|
|
if(parent_is_map(target))
|
|
|
|
|
to_keyval(target, key(target), default_value);
|
|
|
|
|
else
|
|
|
|
|
to_val(target, default_value);
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::lookup_path_or_modify(Tree const *src, size_t src_node, csubstr path, size_t start)
|
|
|
|
|
{
|
|
|
|
|
size_t target = _lookup_path_or_create(path, start);
|
|
|
|
|
merge_with(src, src_node, target);
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::_lookup_path_or_create(csubstr path, size_t start)
|
|
|
|
|
{
|
|
|
|
|
if(start == NONE)
|
|
|
|
|
start = root_id();
|
|
|
|
|
lookup_result r(path, start);
|
|
|
|
|
_lookup_path(&r);
|
|
|
|
|
if(r.target != NONE)
|
|
|
|
|
{
|
|
|
|
|
C4_ASSERT(r.unresolved().empty());
|
|
|
|
|
return r.target;
|
|
|
|
|
}
|
|
|
|
|
_lookup_path_modify(&r);
|
|
|
|
|
return r.target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_lookup_path(lookup_result *r) const
|
|
|
|
|
{
|
|
|
|
|
C4_ASSERT( ! r->unresolved().empty());
|
|
|
|
|
_lookup_path_token parent{"", type(r->closest)};
|
|
|
|
|
size_t node;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
node = _next_node(r, &parent);
|
|
|
|
|
if(node != NONE)
|
|
|
|
|
r->closest = node;
|
|
|
|
|
if(r->unresolved().empty())
|
|
|
|
|
{
|
|
|
|
|
r->target = node;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} while(node != NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Tree::_lookup_path_modify(lookup_result *r)
|
|
|
|
|
{
|
|
|
|
|
C4_ASSERT( ! r->unresolved().empty());
|
|
|
|
|
_lookup_path_token parent{"", type(r->closest)};
|
|
|
|
|
size_t node;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
node = _next_node_modify(r, &parent);
|
|
|
|
|
if(node != NONE)
|
|
|
|
|
r->closest = node;
|
|
|
|
|
if(r->unresolved().empty())
|
|
|
|
|
{
|
|
|
|
|
r->target = node;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} while(node != NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::_next_node(lookup_result * r, _lookup_path_token *parent) const
|
|
|
|
|
{
|
|
|
|
|
_lookup_path_token token = _next_token(r, *parent);
|
|
|
|
|
if( ! token)
|
|
|
|
|
return NONE;
|
|
|
|
|
|
|
|
|
|
size_t node = NONE;
|
|
|
|
|
csubstr prev = token.value;
|
|
|
|
|
if(token.type == MAP || token.type == SEQ)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, !token.value.begins_with('['));
|
|
|
|
|
//_RYML_CB_ASSERT(m_callbacks, is_container(r->closest) || r->closest == NONE);
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_map(r->closest));
|
|
|
|
|
node = find_child(r->closest, token.value);
|
|
|
|
|
}
|
|
|
|
|
else if(token.type == KEYVAL)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, r->unresolved().empty());
|
|
|
|
|
if(is_map(r->closest))
|
|
|
|
|
node = find_child(r->closest, token.value);
|
|
|
|
|
}
|
|
|
|
|
else if(token.type == KEY)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, token.value.begins_with('[') && token.value.ends_with(']'));
|
|
|
|
|
token.value = token.value.offs(1, 1).trim(' ');
|
|
|
|
|
size_t idx = 0;
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, from_chars(token.value, &idx));
|
|
|
|
|
node = child(r->closest, idx);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
C4_NEVER_REACH();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(node != NONE)
|
|
|
|
|
{
|
|
|
|
|
*parent = token;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
csubstr p = r->path.sub(r->path_pos > 0 ? r->path_pos - 1 : r->path_pos);
|
|
|
|
|
r->path_pos -= prev.len;
|
|
|
|
|
if(p.begins_with('.'))
|
|
|
|
|
r->path_pos -= 1u;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Tree::_next_node_modify(lookup_result * r, _lookup_path_token *parent)
|
|
|
|
|
{
|
|
|
|
|
_lookup_path_token token = _next_token(r, *parent);
|
|
|
|
|
if( ! token)
|
|
|
|
|
return NONE;
|
|
|
|
|
|
|
|
|
|
size_t node = NONE;
|
|
|
|
|
if(token.type == MAP || token.type == SEQ)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, !token.value.begins_with('['));
|
|
|
|
|
//_RYML_CB_ASSERT(m_callbacks, is_container(r->closest) || r->closest == NONE);
|
|
|
|
|
if( ! is_container(r->closest))
|
|
|
|
|
{
|
|
|
|
|
if(has_key(r->closest))
|
|
|
|
|
to_map(r->closest, key(r->closest));
|
|
|
|
|
else
|
|
|
|
|
to_map(r->closest);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(is_map(r->closest))
|
|
|
|
|
node = find_child(r->closest, token.value);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
size_t pos = NONE;
|
|
|
|
|
_RYML_CB_CHECK(m_callbacks, c4::atox(token.value, &pos));
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, pos != NONE);
|
|
|
|
|
node = child(r->closest, pos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(node == NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_map(r->closest));
|
|
|
|
|
node = append_child(r->closest);
|
|
|
|
|
NodeData *n = _p(node);
|
|
|
|
|
n->m_key.scalar = token.value;
|
|
|
|
|
n->m_type.add(KEY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(token.type == KEYVAL)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, r->unresolved().empty());
|
|
|
|
|
if(is_map(r->closest))
|
|
|
|
|
{
|
|
|
|
|
node = find_child(r->closest, token.value);
|
|
|
|
|
if(node == NONE)
|
|
|
|
|
node = append_child(r->closest);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, !is_seq(r->closest));
|
|
|
|
|
_add_flags(r->closest, MAP);
|
|
|
|
|
node = append_child(r->closest);
|
|
|
|
|
}
|
|
|
|
|
NodeData *n = _p(node);
|
|
|
|
|
n->m_key.scalar = token.value;
|
|
|
|
|
n->m_val.scalar = "";
|
|
|
|
|
n->m_type.add(KEYVAL);
|
|
|
|
|
}
|
|
|
|
|
else if(token.type == KEY)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, token.value.begins_with('[') && token.value.ends_with(']'));
|
|
|
|
|
token.value = token.value.offs(1, 1).trim(' ');
|
|
|
|
|
size_t idx;
|
|
|
|
|
if( ! from_chars(token.value, &idx))
|
|
|
|
|
return NONE;
|
|
|
|
|
if( ! is_container(r->closest))
|
|
|
|
|
{
|
|
|
|
|
if(has_key(r->closest))
|
|
|
|
|
{
|
|
|
|
|
csubstr k = key(r->closest);
|
|
|
|
|
_clear_type(r->closest);
|
|
|
|
|
to_seq(r->closest, k);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_clear_type(r->closest);
|
|
|
|
|
to_seq(r->closest);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, is_container(r->closest));
|
|
|
|
|
node = child(r->closest, idx);
|
|
|
|
|
if(node == NONE)
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, num_children(r->closest) <= idx);
|
|
|
|
|
for(size_t i = num_children(r->closest); i <= idx; ++i)
|
|
|
|
|
{
|
|
|
|
|
node = append_child(r->closest);
|
|
|
|
|
if(i < idx)
|
|
|
|
|
{
|
|
|
|
|
if(is_map(r->closest))
|
|
|
|
|
to_keyval(node, /*"~"*/{}, /*"~"*/{});
|
|
|
|
|
else if(is_seq(r->closest))
|
|
|
|
|
to_val(node, /*"~"*/{});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
C4_NEVER_REACH();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, node != NONE);
|
|
|
|
|
*parent = token;
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** types of tokens:
|
|
|
|
|
* - seeing "map." ---> "map"/MAP
|
|
|
|
|
* - finishing "scalar" ---> "scalar"/KEYVAL
|
|
|
|
|
* - seeing "seq[n]" ---> "seq"/SEQ (--> "[n]"/KEY)
|
|
|
|
|
* - seeing "[n]" ---> "[n]"/KEY
|
|
|
|
|
*/
|
|
|
|
|
Tree::_lookup_path_token Tree::_next_token(lookup_result *r, _lookup_path_token const& parent) const
|
|
|
|
|
{
|
|
|
|
|
csubstr unres = r->unresolved();
|
|
|
|
|
if(unres.empty())
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
// is it an indexation like [0], [1], etc?
|
|
|
|
|
if(unres.begins_with('['))
|
|
|
|
|
{
|
|
|
|
|
size_t pos = unres.find(']');
|
|
|
|
|
if(pos == csubstr::npos)
|
|
|
|
|
return {};
|
|
|
|
|
csubstr idx = unres.first(pos + 1);
|
|
|
|
|
_advance(r, pos + 1);
|
|
|
|
|
return {idx, KEY};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no. so it must be a name
|
|
|
|
|
size_t pos = unres.first_of(".[");
|
|
|
|
|
if(pos == csubstr::npos)
|
|
|
|
|
{
|
|
|
|
|
_advance(r, unres.len);
|
|
|
|
|
NodeType t;
|
|
|
|
|
if(( ! parent) || parent.type.is_seq())
|
|
|
|
|
return {unres, VAL};
|
|
|
|
|
return {unres, KEYVAL};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// it's either a map or a seq
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, unres[pos] == '.' || unres[pos] == '[');
|
|
|
|
|
if(unres[pos] == '.')
|
|
|
|
|
{
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, pos != 0);
|
|
|
|
|
_advance(r, pos + 1);
|
|
|
|
|
return {unres.first(pos), MAP};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_RYML_CB_ASSERT(m_callbacks, unres[pos] == '[');
|
|
|
|
|
_advance(r, pos);
|
|
|
|
|
return {unres.first(pos), SEQ};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace ryml
|
|
|
|
|
} // namespace c4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C4_SUPPRESS_WARNING_GCC_POP
|
|
|
|
|
C4_SUPPRESS_WARNING_MSVC_POP
|