| #include <algorithm> | |
| #include <cassert> | |
| #include <iterator> | |
| #include <sstream> | |
| #include "yaml-cpp/exceptions.h" | |
| #include "yaml-cpp/node/detail/memory.h" | |
| #include "yaml-cpp/node/detail/node.h" // IWYU pragma: keep | |
| #include "yaml-cpp/node/detail/node_data.h" | |
| #include "yaml-cpp/node/detail/node_iterator.h" | |
| #include "yaml-cpp/node/ptr.h" | |
| #include "yaml-cpp/node/type.h" | |
| namespace YAML { | |
| namespace detail { | |
| YAML_CPP_API std::atomic<size_t> node::m_amount{0}; | |
| const std::string& node_data::empty_scalar() { | |
| static const std::string svalue; | |
| return svalue; | |
| } | |
| node_data::node_data() | |
| : m_isDefined(false), | |
| m_mark(Mark::null_mark()), | |
| m_type(NodeType::Null), | |
| m_tag{}, | |
| m_style(EmitterStyle::Default), | |
| m_scalar{}, | |
| m_sequence{}, | |
| m_seqSize(0), | |
| m_map{}, | |
| m_undefinedPairs{} {} | |
| void node_data::mark_defined() { | |
| if (m_type == NodeType::Undefined) | |
| m_type = NodeType::Null; | |
| m_isDefined = true; | |
| } | |
| void node_data::set_mark(const Mark& mark) { m_mark = mark; } | |
| void node_data::set_type(NodeType::value type) { | |
| if (type == NodeType::Undefined) { | |
| m_type = type; | |
| m_isDefined = false; | |
| return; | |
| } | |
| m_isDefined = true; | |
| if (type == m_type) | |
| return; | |
| m_type = type; | |
| switch (m_type) { | |
| case NodeType::Null: | |
| break; | |
| case NodeType::Scalar: | |
| m_scalar.clear(); | |
| break; | |
| case NodeType::Sequence: | |
| reset_sequence(); | |
| break; | |
| case NodeType::Map: | |
| reset_map(); | |
| break; | |
| case NodeType::Undefined: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void node_data::set_tag(const std::string& tag) { m_tag = tag; } | |
| void node_data::set_style(EmitterStyle::value style) { m_style = style; } | |
| void node_data::set_null() { | |
| m_isDefined = true; | |
| m_type = NodeType::Null; | |
| } | |
| void node_data::set_scalar(const std::string& scalar) { | |
| m_isDefined = true; | |
| m_type = NodeType::Scalar; | |
| m_scalar = scalar; | |
| } | |
| // size/iterator | |
| std::size_t node_data::size() const { | |
| if (!m_isDefined) | |
| return 0; | |
| switch (m_type) { | |
| case NodeType::Sequence: | |
| compute_seq_size(); | |
| return m_seqSize; | |
| case NodeType::Map: | |
| compute_map_size(); | |
| return m_map.size() - m_undefinedPairs.size(); | |
| default: | |
| return 0; | |
| } | |
| return 0; | |
| } | |
| void node_data::compute_seq_size() const { | |
| while (m_seqSize < m_sequence.size() && m_sequence[m_seqSize]->is_defined()) | |
| m_seqSize++; | |
| } | |
| void node_data::compute_map_size() const { | |
| auto it = m_undefinedPairs.begin(); | |
| while (it != m_undefinedPairs.end()) { | |
| auto jt = std::next(it); | |
| if (it->first->is_defined() && it->second->is_defined()) | |
| m_undefinedPairs.erase(it); | |
| it = jt; | |
| } | |
| } | |
| const_node_iterator node_data::begin() const { | |
| if (!m_isDefined) | |
| return {}; | |
| switch (m_type) { | |
| case NodeType::Sequence: | |
| return const_node_iterator(m_sequence.begin()); | |
| case NodeType::Map: | |
| return const_node_iterator(m_map.begin(), m_map.end()); | |
| default: | |
| return {}; | |
| } | |
| } | |
| node_iterator node_data::begin() { | |
| if (!m_isDefined) | |
| return {}; | |
| switch (m_type) { | |
| case NodeType::Sequence: | |
| return node_iterator(m_sequence.begin()); | |
| case NodeType::Map: | |
| return node_iterator(m_map.begin(), m_map.end()); | |
| default: | |
| return {}; | |
| } | |
| } | |
| const_node_iterator node_data::end() const { | |
| if (!m_isDefined) | |
| return {}; | |
| switch (m_type) { | |
| case NodeType::Sequence: | |
| return const_node_iterator(m_sequence.end()); | |
| case NodeType::Map: | |
| return const_node_iterator(m_map.end(), m_map.end()); | |
| default: | |
| return {}; | |
| } | |
| } | |
| node_iterator node_data::end() { | |
| if (!m_isDefined) | |
| return {}; | |
| switch (m_type) { | |
| case NodeType::Sequence: | |
| return node_iterator(m_sequence.end()); | |
| case NodeType::Map: | |
| return node_iterator(m_map.end(), m_map.end()); | |
| default: | |
| return {}; | |
| } | |
| } | |
| // sequence | |
| void node_data::push_back(node& node, | |
| const shared_memory_holder& /* pMemory */) { | |
| if (m_type == NodeType::Undefined || m_type == NodeType::Null) { | |
| m_type = NodeType::Sequence; | |
| reset_sequence(); | |
| } | |
| if (m_type != NodeType::Sequence) | |
| throw BadPushback(); | |
| m_sequence.push_back(&node); | |
| } | |
| void node_data::insert(node& key, node& value, | |
| const shared_memory_holder& pMemory) { | |
| switch (m_type) { | |
| case NodeType::Map: | |
| break; | |
| case NodeType::Undefined: | |
| case NodeType::Null: | |
| case NodeType::Sequence: | |
| convert_to_map(pMemory); | |
| break; | |
| case NodeType::Scalar: | |
| throw BadSubscript(m_mark, key); | |
| } | |
| insert_map_pair(key, value); | |
| } | |
| // indexing | |
| node* node_data::get(node& key, | |
| const shared_memory_holder& /* pMemory */) const { | |
| if (m_type != NodeType::Map) { | |
| return nullptr; | |
| } | |
| for (const auto& it : m_map) { | |
| if (it.first->is(key)) | |
| return it.second; | |
| } | |
| return nullptr; | |
| } | |
| node& node_data::get(node& key, const shared_memory_holder& pMemory) { | |
| switch (m_type) { | |
| case NodeType::Map: | |
| break; | |
| case NodeType::Undefined: | |
| case NodeType::Null: | |
| case NodeType::Sequence: | |
| convert_to_map(pMemory); | |
| break; | |
| case NodeType::Scalar: | |
| throw BadSubscript(m_mark, key); | |
| } | |
| for (const auto& it : m_map) { | |
| if (it.first->is(key)) | |
| return *it.second; | |
| } | |
| node& value = pMemory->create_node(); | |
| insert_map_pair(key, value); | |
| return value; | |
| } | |
| bool node_data::remove(node& key, const shared_memory_holder& /* pMemory */) { | |
| if (m_type != NodeType::Map) | |
| return false; | |
| for (auto it = m_undefinedPairs.begin(); it != m_undefinedPairs.end();) { | |
| auto jt = std::next(it); | |
| if (it->first->is(key)) | |
| m_undefinedPairs.erase(it); | |
| it = jt; | |
| } | |
| auto it = | |
| std::find_if(m_map.begin(), m_map.end(), | |
| [&](std::pair<YAML::detail::node*, YAML::detail::node*> j) { | |
| return (j.first->is(key)); | |
| }); | |
| if (it != m_map.end()) { | |
| m_map.erase(it); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void node_data::reset_sequence() { | |
| m_sequence.clear(); | |
| m_seqSize = 0; | |
| } | |
| void node_data::reset_map() { | |
| m_map.clear(); | |
| m_undefinedPairs.clear(); | |
| } | |
| void node_data::insert_map_pair(node& key, node& value) { | |
| m_map.emplace_back(&key, &value); | |
| if (!key.is_defined() || !value.is_defined()) | |
| m_undefinedPairs.emplace_back(&key, &value); | |
| } | |
| void node_data::convert_to_map(const shared_memory_holder& pMemory) { | |
| switch (m_type) { | |
| case NodeType::Undefined: | |
| case NodeType::Null: | |
| reset_map(); | |
| m_type = NodeType::Map; | |
| break; | |
| case NodeType::Sequence: | |
| convert_sequence_to_map(pMemory); | |
| break; | |
| case NodeType::Map: | |
| break; | |
| case NodeType::Scalar: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void node_data::convert_sequence_to_map(const shared_memory_holder& pMemory) { | |
| assert(m_type == NodeType::Sequence); | |
| reset_map(); | |
| for (std::size_t i = 0; i < m_sequence.size(); i++) { | |
| std::stringstream stream; | |
| stream.imbue(std::locale::classic()); | |
| stream << i; | |
| node& key = pMemory->create_node(); | |
| key.set_scalar(stream.str()); | |
| insert_map_pair(key, *m_sequence[i]); | |
| } | |
| reset_sequence(); | |
| m_type = NodeType::Map; | |
| } | |
| } // namespace detail | |
| } // namespace YAML |