From e2e717a61b47bcb0868e2a4503f98324936f71c8 Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 17:03:48 +0800 Subject: [PATCH 1/8] Use single header yaml parser --- LICENSE | 22 +- cpp/CMakeLists.txt | 10 +- cpp/include/gar/utils/yaml.h | 21 +- cpp/src/graph_info.cc | 198 +- cpp/src/yaml.cc | 33 +- cpp/test/test_info.cc | 5 + cpp/thirdparty/mini-yaml/yaml/Yaml.cpp | 2773 ++++++++++++++++++++++++ cpp/thirdparty/mini-yaml/yaml/Yaml.hpp | 656 ++++++ 8 files changed, 3581 insertions(+), 137 deletions(-) create mode 100644 cpp/thirdparty/mini-yaml/yaml/Yaml.cpp create mode 100644 cpp/thirdparty/mini-yaml/yaml/Yaml.hpp diff --git a/LICENSE b/LICENSE index 24f1e5bc4..1633a0a91 100644 --- a/LICENSE +++ b/LICENSE @@ -409,27 +409,31 @@ SOFTWARE. ------------------------------------------------------------------------------- -thirdparty/yaml-cpp: MIT License +thirdparty/mini-yaml is referred from jimmiebergmann/mini-yaml project, +which is licensed under MIT License. -Copyright (c) 2008-2015 Jesse Beder. +MIT License + +Copyright(c) 2018 Jimmie Bergmann Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal +of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +furnished to do so, subject to the following conditions : -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + ------------------------------------------------------------------------------- diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 81fce3529..fdf8afdd4 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -147,7 +147,7 @@ endmacro() # building or find third party library # ------------------------------------------------------------------------------ find_package(Threads REQUIRED) -find_yaml_cpp() +# find_yaml_cpp() find_package(OpenSSL QUIET) if (APPLE) find_package(curl REQUIRED) @@ -178,20 +178,20 @@ endmacro() # generate gar library # ------------------------------------------------------------------------------ macro(build_gar) - file(GLOB_RECURSE CORE_SRC_FILES "src/*.cc") + file(GLOB_RECURSE CORE_SRC_FILES "src/*.cc" ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/mini-yaml/yaml/*.cpp) add_library(gar SHARED ${CORE_SRC_FILES}) install_gar_target(gar) target_compile_features(gar PRIVATE cxx_std_17) target_include_directories(gar PUBLIC $ $ - $ + $ ) target_include_directories(gar SYSTEM BEFORE PRIVATE ${GAR_ARROW_INCLUDE_DIR}) target_link_libraries(gar PRIVATE Threads::Threads ${CMAKE_DL_LIBS}) # make sure `libyaml-cpp.a` built first - add_dependencies(gar yaml-cpp) - get_target_location(YAML_CPP_LIBRARY_LOCATION yaml-cpp) + # add_dependencies(gar yaml-cpp) + # get_target_location(YAML_CPP_LIBRARY_LOCATION yaml-cpp) if(APPLE) target_link_libraries(gar PRIVATE -Wl,-force_load gar_arrow_static "${YAML_CPP_LIBRARY_LOCATION}" diff --git a/cpp/include/gar/utils/yaml.h b/cpp/include/gar/utils/yaml.h index 8b18b6ae1..12dcd2ecc 100644 --- a/cpp/include/gar/utils/yaml.h +++ b/cpp/include/gar/utils/yaml.h @@ -23,21 +23,23 @@ limitations under the License. #include "gar/utils/result.h" // forward declaration -namespace YAML { +namespace Yaml { class Node; } namespace GAR_NAMESPACE_INTERNAL { -/** A wrapper of YAML::Node to provide functions to parse yaml. */ +/** A wrapper of ::Yaml::Node to provide functions to parse yaml. */ class Yaml { public: - explicit Yaml(std::shared_ptr root_node) + using Node = ::Yaml::Node; + + explicit Yaml(std::shared_ptr root_node) : root_node_(root_node) {} ~Yaml() = default; - const YAML::Node operator[](const std::string& key) const; + const Node operator[](const std::string& key) const; /** * Loads the input string as Yaml instance. @@ -46,19 +48,12 @@ class Yaml { */ static Result> Load(const std::string& input); - /** - * Loads the input string as Yaml instance. - * - * Return Status::YamlError if input string can not be loaded(malformed). - */ - static Result> Load(const char* input); - /** * Loads the input stream as Yaml instance. * * Return Status::YamlError if input string can not be loaded(malformed). */ - static Result> Load(std::istream& input); + static Result> Load(std::iostream& input); /** * Loads the input file as a single Yaml instance. @@ -68,7 +63,7 @@ class Yaml { static Result> LoadFile(const std::string& file_name); private: - std::shared_ptr root_node_; + std::shared_ptr root_node_; }; } // namespace GAR_NAMESPACE_INTERNAL diff --git a/cpp/src/graph_info.cc b/cpp/src/graph_info.cc index 05e8c850c..1ce08980e 100644 --- a/cpp/src/graph_info.cc +++ b/cpp/src/graph_info.cc @@ -15,7 +15,7 @@ limitations under the License. #include -#include "yaml-cpp/yaml.h" +#include "yaml/Yaml.hpp" #include "gar/graph_info.h" #include "gar/utils/filesystem.h" @@ -27,39 +27,41 @@ Result VertexInfo::Load(std::shared_ptr yaml) { if (yaml == nullptr) { return Status::YamlError("yaml is nullptr"); } - std::string label = yaml->operator[]("label").as(); + std::string label = yaml->operator[]("label").As(); IdType chunk_size = - static_cast(yaml->operator[]("chunk_size").as()); + static_cast(yaml->operator[]("chunk_size").As()); std::string prefix; - if (yaml->operator[]("prefix")) { - prefix = yaml->operator[]("prefix").as(); + if (!yaml->operator[]("prefix").IsNone()) { + prefix = yaml->operator[]("prefix").As(); } InfoVersion version; - if (yaml->operator[]("version")) { + if (!yaml->operator[]("version").IsNone()) { GAR_ASSIGN_OR_RAISE( version, - InfoVersion::Parse(yaml->operator[]("version").as())); + InfoVersion::Parse(yaml->operator[]("version").As())); } VertexInfo vertex_info(label, chunk_size, version, prefix); auto property_groups = yaml->operator[]("property_groups"); - if (property_groups) { // property_groups exist - for (YAML::const_iterator it = property_groups.begin(); - it != property_groups.end(); ++it) { + if (!property_groups.IsNone()) { // property_groups exist + for (auto it = property_groups.Begin(); + it != property_groups.End(); it++) { std::string pg_prefix; - if (it->operator[]("prefix")) { - pg_prefix = it->operator[]("prefix").as(); + auto& node = (*it).second; + if (!node["prefix"].IsNone()) { + pg_prefix = node["prefix"].As(); } auto file_type = - StringToFileType(it->operator[]("file_type").as()); + StringToFileType(node["file_type"].As()); std::vector property_vec; - auto& properties = it->operator[]("properties"); - for (YAML::const_iterator iit = properties.begin(); - iit != properties.end(); ++iit) { + auto& properties = node["properties"]; + for (auto iit = properties.Begin(); + iit != properties.End(); iit++) { Property property; - property.name = iit->operator[]("name").as(); + auto& p_node = (*iit).second; + property.name = p_node["name"].As(); property.type = DataType::TypeNameToDataType( - iit->operator[]("data_type").as()); - property.is_primary = iit->operator[]("is_primary").as(); + p_node["data_type"].As()); + property.is_primary = p_node["is_primary"].As(); property_vec.push_back(property); } PropertyGroup pg(property_vec, file_type, pg_prefix); @@ -73,27 +75,31 @@ Result VertexInfo::Dump() const noexcept { if (!IsValidated()) { return Status::Invalid(); } - YAML::Node node; + ::Yaml::Node node; node["label"] = label_; - node["chunk_size"] = chunk_size_; + node["chunk_size"] = std::to_string(chunk_size_); node["prefix"] = prefix_; for (const auto& pg : property_groups_) { - YAML::Node pg_node; + Yaml::Node pg_node; if (!pg.GetPrefix().empty()) { pg_node["prefix"] = pg.GetPrefix(); } pg_node["file_type"] = FileTypeToString(pg.GetFileType()); for (auto& p : pg.GetProperties()) { - YAML::Node p_node; + Yaml::Node p_node; p_node["name"] = p.name; p_node["data_type"] = p.type.ToTypeName(); - p_node["is_primary"] = p.is_primary; - pg_node["properties"].push_back(p_node); + p_node["is_primary"] = p.is_primary ? "true" : "false"; + pg_node["properties"].PushBack(); + pg_node["properties"][pg_node["properties"].Size() - 1] = p_node; } - node["property_groups"].push_back(pg_node); + node["property_groups"].PushBack(); + node["property_groups"][node["property_groups"].Size() - 1] = pg_node; } node["version"] = version_.ToString(); - return YAML::Dump(node); + std::string dump_string; + ::Yaml::Serialize(node, dump_string); + return dump_string; } Status VertexInfo::Save(const std::string& path) const { @@ -107,65 +113,68 @@ Result EdgeInfo::Load(std::shared_ptr yaml) { if (yaml == nullptr) { return Status::YamlError("yaml is nullptr"); } - std::string src_label = yaml->operator[]("src_label").as(); - std::string edge_label = yaml->operator[]("edge_label").as(); - std::string dst_label = yaml->operator[]("dst_label").as(); + std::string src_label = yaml->operator[]("src_label").As(); + std::string edge_label = yaml->operator[]("edge_label").As(); + std::string dst_label = yaml->operator[]("dst_label").As(); IdType chunk_size = - static_cast(yaml->operator[]("chunk_size").as()); + static_cast(yaml->operator[]("chunk_size").As()); IdType src_chunk_size = - static_cast(yaml->operator[]("src_chunk_size").as()); + static_cast(yaml->operator[]("src_chunk_size").As()); IdType dst_chunk_size = - static_cast(yaml->operator[]("dst_chunk_size").as()); - bool directed = yaml->operator[]("directed").as(); + static_cast(yaml->operator[]("dst_chunk_size").As()); + bool directed = yaml->operator[]("directed").As(); std::string prefix; - if (yaml->operator[]("prefix")) { - prefix = yaml->operator[]("prefix").as(); + if (!yaml->operator[]("prefix").IsNone()) { + prefix = yaml->operator[]("prefix").As(); } InfoVersion version; - if (yaml->operator[]("version")) { + if (!yaml->operator[]("version").IsNone()) { GAR_ASSIGN_OR_RAISE( version, - InfoVersion::Parse(yaml->operator[]("version").as())); + InfoVersion::Parse(yaml->operator[]("version").As())); } EdgeInfo edge_info(src_label, edge_label, dst_label, chunk_size, src_chunk_size, dst_chunk_size, directed, version, prefix); auto adj_lists = yaml->operator[]("adj_lists"); - if (adj_lists) { - for (YAML::const_iterator it = adj_lists.begin(); it != adj_lists.end(); - ++it) { - auto ordered = it->operator[]("ordered").as(); - auto aligned = it->operator[]("aligned_by").as(); + if (adj_lists.IsSequence()) { + for (auto it = adj_lists.Begin(); it != adj_lists.End(); + it++) { + auto& node = (*it).second; + auto ordered = node["ordered"].As(); + auto aligned = node["aligned_by"].As(); auto adj_list_type = OrderedAlignedToAdjListType(ordered, aligned); auto file_type = - StringToFileType(it->operator[]("file_type").as()); + StringToFileType(node["file_type"].As()); std::string adj_list_prefix; - if (it->operator[]("prefix")) { - adj_list_prefix = it->operator[]("prefix").as(); + if (!node["prefix"].IsNone()) { + adj_list_prefix = node["prefix"].As(); } GAR_RETURN_NOT_OK( edge_info.AddAdjList(adj_list_type, file_type, adj_list_prefix)); - auto property_groups = it->operator[]("property_groups"); - if (property_groups) { // property_groups exist - for (YAML::const_iterator pg_it = property_groups.begin(); - pg_it != property_groups.end(); ++pg_it) { + auto property_groups = node["property_groups"]; + if (!property_groups.IsNone()) { // property_groups exist + for (auto pg_it = property_groups.Begin(); + pg_it != property_groups.End(); pg_it++) { + auto& pg_node = (*pg_it).second; std::string pg_prefix; - if (pg_it->operator[]("prefix")) { - pg_prefix = pg_it->operator[]("prefix").as(); + if (!pg_node["prefix"].IsNone()) { + pg_prefix = pg_node["prefix"].As(); } auto file_type = StringToFileType( - pg_it->operator[]("file_type").as()); - auto properties = pg_it->operator[]("properties"); + pg_node["file_type"].As()); + auto properties = pg_node["properties"]; std::vector property_vec; - for (YAML::const_iterator p_it = properties.begin(); - p_it != properties.end(); ++p_it) { + for (auto p_it = properties.Begin(); + p_it != properties.End(); p_it++) { + auto& p_node = (*p_it).second; Property property; - property.name = p_it->operator[]("name").as(); + property.name = p_node["name"].As(); property.type = DataType::TypeNameToDataType( - p_it->operator[]("data_type").as()); - property.is_primary = p_it->operator[]("is_primary").as(); + p_node["data_type"].As()); + property.is_primary = p_node["is_primary"].As(); property_vec.push_back(property); } PropertyGroup pg(property_vec, file_type, pg_prefix); @@ -181,43 +190,49 @@ Result EdgeInfo::Dump() const noexcept { if (!IsValidated()) { return Status::Invalid(); } - YAML::Node node; + ::Yaml::Node node; node["src_label"] = src_label_; node["edge_label"] = edge_label_; node["dst_label"] = dst_label_; - node["chunk_size"] = chunk_size_; - node["src_chunk_size"] = src_chunk_size_; - node["dst_chunk_size"] = dst_chunk_size_; + node["chunk_size"] = std::to_string(chunk_size_); + node["src_chunk_size"] = std::to_string(src_chunk_size_); + node["dst_chunk_size"] = std::to_string(dst_chunk_size_); node["prefix"] = prefix_; - node["directed"] = directed_; + node["directed"] = directed_ ? "true" : "false"; for (const auto& item : adj_list2prefix_) { - YAML::Node adj_list_node; + ::Yaml::Node adj_list_node; auto adj_list_type = item.first; auto pair = AdjListTypeToOrderedAligned(adj_list_type); - adj_list_node["ordered"] = pair.first; + adj_list_node["ordered"] = pair.first ? "true" : "false"; adj_list_node["aligned_by"] = pair.second; adj_list_node["prefix"] = adj_list2prefix_.at(adj_list_type); adj_list_node["file_type"] = FileTypeToString(adj_list2file_type_.at(adj_list_type)); for (const auto& pg : adj_list2property_groups_.at(adj_list_type)) { - YAML::Node pg_node; + ::Yaml::Node pg_node; if (!pg.GetPrefix().empty()) { pg_node["prefix"] = pg.GetPrefix(); } pg_node["file_type"] = FileTypeToString(pg.GetFileType()); for (auto& p : pg.GetProperties()) { - YAML::Node p_node; + ::Yaml::Node p_node; p_node["name"] = p.name; p_node["data_type"] = p.type.ToTypeName(); - p_node["is_primary"] = p.is_primary; - pg_node["properties"].push_back(p_node); + p_node["is_primary"] = p.is_primary ? "true" : "false"; + pg_node["properties"].PushBack(); + pg_node["properties"][pg_node["properties"].Size() - 1] = p_node; } - adj_list_node["property_groups"].push_back(pg_node); + adj_list_node["property_groups"].PushBack(); + adj_list_node["property_groups"] + [adj_list_node["property_groups"].Size() - 1] = pg_node; } - node["adj_lists"].push_back(adj_list_node); + node["adj_lists"].PushBack(); + node["adj_lists"][node["adj_lists"].Size() - 1] = adj_list_node; } node["version"] = version_.ToString(); - return YAML::Dump(node); + std::string dump_string; + ::Yaml::Serialize(node, dump_string); + return dump_string; } Status EdgeInfo::Save(const std::string& path) const { @@ -253,25 +268,24 @@ static Result ConstructGraphInfo( const std::string& no_url_path) { std::string name = default_name; std::string prefix = default_prefix; - if (graph_meta->operator[]("name")) { - name = graph_meta->operator[]("name").as(); + if (!graph_meta->operator[]("name").IsNone()) { + name = graph_meta->operator[]("name").As(); } - if (graph_meta->operator[]("prefix")) { - prefix = graph_meta->operator[]("prefix").as(); + if (!graph_meta->operator[]("prefix").IsNone()) { + prefix = graph_meta->operator[]("prefix").As(); } InfoVersion version; - if (graph_meta->operator[]("version")) { + if (!graph_meta->operator[]("version").IsNone()) { GAR_ASSIGN_OR_RAISE( version, InfoVersion::Parse( - graph_meta->operator[]("version").as())); + graph_meta->operator[]("version").As())); } GraphInfo graph_info(name, version, prefix); const auto& vertices = graph_meta->operator[]("vertices"); - if (vertices) { - for (YAML::const_iterator it = vertices.begin(); it != vertices.end(); - ++it) { - std::string vertex_meta_file = no_url_path + it->as(); + if (vertices.IsSequence()) { + for (auto it = vertices.Begin(); it != vertices.End(); it++) { + std::string vertex_meta_file = no_url_path + (*it).second.As(); GAR_ASSIGN_OR_RAISE(auto input, fs->ReadFileToValue(vertex_meta_file)); GAR_ASSIGN_OR_RAISE(auto vertex_meta, Yaml::Load(input)); @@ -280,9 +294,9 @@ static Result ConstructGraphInfo( } } const auto& edges = graph_meta->operator[]("edges"); - if (edges) { - for (YAML::const_iterator it = edges.begin(); it != edges.end(); ++it) { - std::string edge_meta_file = no_url_path + it->as(); + if (edges.IsSequence()) { + for (auto it = edges.Begin(); it != edges.End(); it++) { + std::string edge_meta_file = no_url_path + (*it).second.As(); GAR_ASSIGN_OR_RAISE(auto input, fs->ReadFileToValue(edge_meta_file)); GAR_ASSIGN_OR_RAISE(auto edge_meta, Yaml::Load(input)); @@ -325,19 +339,23 @@ Result GraphInfo::Dump() const noexcept { if (!IsValidated()) { return Status::Invalid(); } - YAML::Node node; + ::Yaml::Node node; node["name"] = name_; node["prefix"] = prefix_; node["vertices"]; node["edges"]; for (auto& path : vertex_paths_) { - node["vertices"].push_back(path); + node["vertices"].PushBack(); + node["vertices"][node["vertices"].Size() - 1] = path; } for (auto& path : edge_paths_) { - node["edges"].push_back(path); + node["edges"].PushBack(); + node["edges"][node["edges"].Size() - 1] = path; } node["version"] = version_.ToString(); - return YAML::Dump(node); + std::string dump_string; + ::Yaml::Serialize(node, dump_string); + return dump_string; } Status GraphInfo::Save(const std::string& path) const { diff --git a/cpp/src/yaml.cc b/cpp/src/yaml.cc index 1e64ba815..86d840c09 100644 --- a/cpp/src/yaml.cc +++ b/cpp/src/yaml.cc @@ -16,46 +16,39 @@ limitations under the License. #include #include -#include "yaml-cpp/yaml.h" +#include "yaml/Yaml.hpp" #include "gar/utils/result.h" #include "gar/utils/yaml.h" + namespace GAR_NAMESPACE_INTERNAL { -const YAML::Node Yaml::operator[](const std::string& key) const { +const Yaml::Node Yaml::operator[](const std::string& key) const { return root_node_->operator[](key); } Result> Yaml::Load(const std::string& input) { - std::shared_ptr root_node; - try { - root_node = std::make_shared(YAML::Load(input)); - } catch (YAML::Exception& e) { return Status::YamlError(e.what()); } - return std::make_shared(root_node); -} - -Result> Yaml::Load(const char* input) { - std::shared_ptr root_node; + std::shared_ptr root_node = std::make_shared(); try { - root_node = std::make_shared(YAML::Load(input)); - } catch (YAML::Exception& e) { return Status::YamlError(e.what()); } + ::Yaml::Parse(*root_node, input); + } catch (::Yaml::Exception& e) { return Status::YamlError(e.what()); } return std::make_shared(root_node); } -Result> Yaml::Load(std::istream& input) { - std::shared_ptr root_node; +Result> Yaml::Load(std::iostream& input) { + std::shared_ptr root_node = std::make_shared(); try { - root_node = std::make_shared(YAML::Load(input)); - } catch (YAML::Exception& e) { return Status::YamlError(e.what()); } + ::Yaml::Parse(*root_node, input); + } catch (::Yaml::Exception& e) { return Status::YamlError(e.what()); } return std::make_shared(root_node); } Result> Yaml::LoadFile(const std::string& file_name) { - std::shared_ptr root_node; + std::shared_ptr root_node = std::make_shared(); try { - root_node = std::make_shared(YAML::LoadFile(file_name)); - } catch (YAML::Exception& e) { return Status::YamlError(e.what()); } + ::Yaml::Parse(*root_node, file_name.c_str(), file_name.size()); + } catch (::Yaml::Exception& e) { return Status::YamlError(e.what()); } return std::make_shared(root_node); } diff --git a/cpp/test/test_info.cc b/cpp/test/test_info.cc index 4ffb07a16..c0c508104 100644 --- a/cpp/test/test_info.cc +++ b/cpp/test/test_info.cc @@ -75,6 +75,7 @@ TEST_CASE("test_graph_info") { // TODO(@acezen): test dump std::string save_path(std::tmpnam(nullptr)); + std::cout << "save_path: " << save_path << std::endl; REQUIRE(graph_info.Save(save_path).ok()); REQUIRE(std::filesystem::exists(save_path)); @@ -143,6 +144,7 @@ TEST_CASE("test_vertex_info") { // test save std::string save_path(std::tmpnam(nullptr)); + std::cout << "save_path: " << save_path << std::endl; REQUIRE(v_info.Save(save_path).ok()); REQUIRE(std::filesystem::exists(save_path)); @@ -294,6 +296,7 @@ TEST_CASE("test_edge_info") { // test save std::string save_path(std::tmpnam(nullptr)); + std::cout << "save_path: " << save_path << std::endl; REQUIRE(edge_info.Save(save_path).ok()); REQUIRE(std::filesystem::exists(save_path)); @@ -350,6 +353,7 @@ TEST_CASE("test_graph_info_load_from_file") { REQUIRE(edge_infos.size() == 1); } +/* TEST_CASE("test_graph_info_load_from_s3") { std::string path = "s3://graphar/ldbc/ldbc.graph.yml" @@ -363,3 +367,4 @@ TEST_CASE("test_graph_info_load_from_s3") { REQUIRE(vertex_infos.size() == 8); REQUIRE(edge_infos.size() == 23); } +*/ diff --git a/cpp/thirdparty/mini-yaml/yaml/Yaml.cpp b/cpp/thirdparty/mini-yaml/yaml/Yaml.cpp new file mode 100644 index 000000000..70adec6f3 --- /dev/null +++ b/cpp/thirdparty/mini-yaml/yaml/Yaml.cpp @@ -0,0 +1,2773 @@ +/* +* MIT License +* +* Copyright(c) 2018 Jimmie Bergmann +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files(the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions : +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +*/ + +#include "Yaml.hpp" +#include +#include +#include +#include +#include +#include +#include + + +// Implementation access definitions. +#define NODE_IMP static_cast(m_pImp) +#define NODE_IMP_EXT(node) static_cast(node.m_pImp) +#define TYPE_IMP static_cast(m_pImp)->m_pImp + + +#define IT_IMP static_cast(m_pImp) + + +namespace Yaml +{ + class ReaderLine; + + // Exception message definitions. + static const std::string g_ErrorInvalidCharacter = "Invalid character found."; + static const std::string g_ErrorKeyMissing = "Missing key."; + static const std::string g_ErrorKeyIncorrect = "Incorrect key."; + static const std::string g_ErrorValueIncorrect = "Incorrect value."; + static const std::string g_ErrorTabInOffset = "Tab found in offset."; + static const std::string g_ErrorBlockSequenceNotAllowed = "Sequence entries are not allowed in this context."; + static const std::string g_ErrorUnexpectedDocumentEnd = "Unexpected document end."; + static const std::string g_ErrorDiffEntryNotAllowed = "Different entry is not allowed in this context."; + static const std::string g_ErrorIncorrectOffset = "Incorrect offset."; + static const std::string g_ErrorSequenceError = "Error in sequence node."; + static const std::string g_ErrorCannotOpenFile = "Cannot open file."; + static const std::string g_ErrorIndentation = "Space indentation is less than 2."; + static const std::string g_ErrorInvalidBlockScalar = "Invalid block scalar."; + static const std::string g_ErrorInvalidQuote = "Invalid quote."; + static const std::string g_EmptyString = ""; + static Yaml::Node g_NoneNode; + + // Global function definitions. Implemented at end of this source file. + static std::string ExceptionMessage(const std::string & message, ReaderLine & line); + static std::string ExceptionMessage(const std::string & message, ReaderLine & line, const size_t errorPos); + static std::string ExceptionMessage(const std::string & message, const size_t errorLine, const size_t errorPos); + static std::string ExceptionMessage(const std::string & message, const size_t errorLine, const std::string & data); + + static bool FindQuote(const std::string & input, size_t & start, size_t & end, size_t searchPos = 0); + static size_t FindNotCited(const std::string & input, char token, size_t & preQuoteCount); + static size_t FindNotCited(const std::string & input, char token); + static bool ValidateQuote(const std::string & input); + static void CopyNode(const Node & from, Node & to); + static bool ShouldBeCited(const std::string & key); + static void AddEscapeTokens(std::string & input, const std::string & tokens); + static void RemoveAllEscapeTokens(std::string & input); + + // Exception implementations + Exception::Exception(const std::string & message, const eType type) : + std::runtime_error(message), + m_Type(type) + { + } + + Exception::eType Exception::Type() const + { + return m_Type; + } + + const char * Exception::Message() const + { + return what(); + } + + InternalException::InternalException(const std::string & message) : + Exception(message, InternalError) + { + + } + + ParsingException::ParsingException(const std::string & message) : + Exception(message, ParsingError) + { + + } + + OperationException::OperationException(const std::string & message) : + Exception(message, OperationError) + { + + } + + + class TypeImp + { + + public: + + virtual ~TypeImp() + { + } + + virtual const std::string & GetData() const = 0; + virtual bool SetData(const std::string & data) = 0; + virtual size_t GetSize() const = 0; + virtual Node * GetNode(const size_t index) = 0; + virtual Node * GetNode(const std::string & key) = 0; + virtual Node * Insert(const size_t index) = 0; + virtual Node * PushFront() = 0; + virtual Node * PushBack() = 0; + virtual void Erase(const size_t index) = 0; + virtual void Erase(const std::string & key) = 0; + + }; + + class SequenceImp : public TypeImp + { + + public: + + ~SequenceImp() + { + for(auto it = m_Sequence.begin(); it != m_Sequence.end(); it++) + { + delete it->second; + } + } + + virtual const std::string & GetData() const + { + return g_EmptyString; + } + + virtual bool SetData(const std::string & data) + { + return false; + } + + virtual size_t GetSize() const + { + return m_Sequence.size(); + } + + virtual Node * GetNode(const size_t index) + { + auto it = m_Sequence.find(index); + if(it != m_Sequence.end()) + { + return it->second; + } + return nullptr; + } + + virtual Node * GetNode(const std::string & key) + { + return nullptr; + } + + virtual Node * Insert(const size_t index) + { + if(m_Sequence.size() == 0) + { + Node * pNode = new Node; + m_Sequence.insert({0, pNode}); + return pNode; + } + + if(index >= m_Sequence.size()) + { + auto it = m_Sequence.end(); + --it; + Node * pNode = new Node; + m_Sequence.insert({it->first, pNode}); + return pNode; + } + + auto it = m_Sequence.cbegin(); + while(it != m_Sequence.cend()) + { + m_Sequence[it->first+1] = it->second; + + if(it->first == index) + { + break; + } + } + + Node * pNode = new Node; + m_Sequence.insert({index, pNode}); + return pNode; + } + + virtual Node * PushFront() + { + for(auto it = m_Sequence.cbegin(); it != m_Sequence.cend(); it++) + { + m_Sequence[it->first+1] = it->second; + } + + Node * pNode = new Node; + m_Sequence.insert({0, pNode}); + return pNode; + } + + virtual Node * PushBack() + { + size_t index = 0; + if(m_Sequence.size()) + { + auto it = m_Sequence.end(); + --it; + index = it->first + 1; + } + + Node * pNode = new Node; + m_Sequence.insert({index, pNode}); + return pNode; + } + + virtual void Erase(const size_t index) + { + auto it = m_Sequence.find(index); + if(it == m_Sequence.end()) + { + return; + } + delete it->second; + m_Sequence.erase(index); + } + + virtual void Erase(const std::string & key) + { + } + + std::map m_Sequence; + + }; + + class MapImp : public TypeImp + { + + public: + + ~MapImp() + { + for(auto it = m_Map.begin(); it != m_Map.end(); it++) + { + delete it->second; + } + } + + virtual const std::string & GetData() const + { + return g_EmptyString; + } + + virtual bool SetData(const std::string & data) + { + return false; + } + + virtual size_t GetSize() const + { + return m_Map.size(); + } + + virtual Node * GetNode(const size_t index) + { + return nullptr; + } + + virtual Node * GetNode(const std::string & key) + { + auto it = m_Map.find(key); + if(it == m_Map.end()) + { + Node * pNode = new Node; + m_Map.insert({key, pNode}); + return pNode; + } + return it->second; + } + + virtual Node * Insert(const size_t index) + { + return nullptr; + } + + virtual Node * PushFront() + { + return nullptr; + } + + virtual Node * PushBack() + { + return nullptr; + } + + virtual void Erase(const size_t index) + { + } + + virtual void Erase(const std::string & key) + { + auto it = m_Map.find(key); + if(it == m_Map.end()) + { + return; + } + delete it->second; + m_Map.erase(key); + } + + std::map m_Map; + + }; + + class ScalarImp : public TypeImp + { + + public: + + ~ScalarImp() + { + } + + virtual const std::string & GetData() const + { + return m_Value; + } + + virtual bool SetData(const std::string & data) + { + m_Value = data; + return true; + } + + virtual size_t GetSize() const + { + return 0; + } + + virtual Node * GetNode(const size_t index) + { + return nullptr; + } + + virtual Node * GetNode(const std::string & key) + { + return nullptr; + } + + virtual Node * Insert(const size_t index) + { + return nullptr; + } + + virtual Node * PushFront() + { + return nullptr; + } + + virtual Node * PushBack() + { + return nullptr; + } + + virtual void Erase(const size_t index) + { + } + + virtual void Erase(const std::string & key) + { + } + + std::string m_Value; + + }; + + + // Node implementations. + class NodeImp + { + + public: + + NodeImp() : + m_Type(Node::None), + m_pImp(nullptr) + { + } + + ~NodeImp() + { + Clear(); + } + + void Clear() + { + if(m_pImp != nullptr) + { + delete m_pImp; + m_pImp = nullptr; + } + m_Type = Node::None; + } + + void InitSequence() + { + if(m_Type != Node::SequenceType || m_pImp == nullptr) + { + if(m_pImp) + { + delete m_pImp; + } + m_pImp = new SequenceImp; + m_Type = Node::SequenceType; + } + } + + void InitMap() + { + if(m_Type != Node::MapType || m_pImp == nullptr) + { + if(m_pImp) + { + delete m_pImp; + } + m_pImp = new MapImp; + m_Type = Node::MapType; + } + } + + void InitScalar() + { + if(m_Type != Node::ScalarType || m_pImp == nullptr) + { + if(m_pImp) + { + delete m_pImp; + } + m_pImp = new ScalarImp; + m_Type = Node::ScalarType; + } + + } + + Node::eType m_Type; ///< Type of node. + TypeImp * m_pImp; ///< Imp of type. + + }; + + // Iterator implementation class + class IteratorImp + { + + public: + + virtual ~IteratorImp() + { + } + + virtual Node::eType GetType() const = 0; + virtual void InitBegin(SequenceImp * pSequenceImp) = 0; + virtual void InitEnd(SequenceImp * pSequenceImp) = 0; + virtual void InitBegin(MapImp * pMapImp) = 0; + virtual void InitEnd(MapImp * pMapImp) = 0; + + }; + + class SequenceIteratorImp : public IteratorImp + { + + public: + + virtual Node::eType GetType() const + { + return Node::SequenceType; + } + + virtual void InitBegin(SequenceImp * pSequenceImp) + { + m_Iterator = pSequenceImp->m_Sequence.begin(); + } + + virtual void InitEnd(SequenceImp * pSequenceImp) + { + m_Iterator = pSequenceImp->m_Sequence.end(); + } + + virtual void InitBegin(MapImp * pMapImp) + { + } + + virtual void InitEnd(MapImp * pMapImp) + { + } + + void Copy(const SequenceIteratorImp & it) + { + m_Iterator = it.m_Iterator; + } + + std::map::iterator m_Iterator; + + }; + + class MapIteratorImp : public IteratorImp + { + + public: + + virtual Node::eType GetType() const + { + return Node::MapType; + } + + virtual void InitBegin(SequenceImp * pSequenceImp) + { + } + + virtual void InitEnd(SequenceImp * pSequenceImp) + { + } + + virtual void InitBegin(MapImp * pMapImp) + { + m_Iterator = pMapImp->m_Map.begin(); + } + + virtual void InitEnd(MapImp * pMapImp) + { + m_Iterator = pMapImp->m_Map.end(); + } + + void Copy(const MapIteratorImp & it) + { + m_Iterator = it.m_Iterator; + } + + std::map::iterator m_Iterator; + + }; + + class SequenceConstIteratorImp : public IteratorImp + { + + public: + + virtual Node::eType GetType() const + { + return Node::SequenceType; + } + + virtual void InitBegin(SequenceImp * pSequenceImp) + { + m_Iterator = pSequenceImp->m_Sequence.begin(); + } + + virtual void InitEnd(SequenceImp * pSequenceImp) + { + m_Iterator = pSequenceImp->m_Sequence.end(); + } + + virtual void InitBegin(MapImp * pMapImp) + { + } + + virtual void InitEnd(MapImp * pMapImp) + { + } + + void Copy(const SequenceConstIteratorImp & it) + { + m_Iterator = it.m_Iterator; + } + + std::map::const_iterator m_Iterator; + + }; + + class MapConstIteratorImp : public IteratorImp + { + + public: + + virtual Node::eType GetType() const + { + return Node::MapType; + } + + virtual void InitBegin(SequenceImp * pSequenceImp) + { + } + + virtual void InitEnd(SequenceImp * pSequenceImp) + { + } + + virtual void InitBegin(MapImp * pMapImp) + { + m_Iterator = pMapImp->m_Map.begin(); + } + + virtual void InitEnd(MapImp * pMapImp) + { + m_Iterator = pMapImp->m_Map.end(); + } + + void Copy(const MapConstIteratorImp & it) + { + m_Iterator = it.m_Iterator; + } + + std::map::const_iterator m_Iterator; + + }; + + + // Iterator class + Iterator::Iterator() : + m_Type(None), + m_pImp(nullptr) + { + } + + Iterator::~Iterator() + { + if(m_pImp) + { + switch(m_Type) + { + case SequenceType: + delete static_cast(m_pImp); + break; + case MapType: + delete static_cast(m_pImp); + break; + default: + break; + } + + } + } + + Iterator::Iterator(const Iterator & it) : + m_Type(None), + m_pImp(nullptr) + { + *this = it; + } + + Iterator & Iterator::operator = (const Iterator & it) + { + if(m_pImp) + { + switch(m_Type) + { + case SequenceType: + delete static_cast(m_pImp); + break; + case MapType: + delete static_cast(m_pImp); + break; + default: + break; + } + m_pImp = nullptr; + m_Type = None; + } + + IteratorImp * pNewImp = nullptr; + + switch(it.m_Type) + { + case SequenceType: + m_Type = SequenceType; + pNewImp = new SequenceIteratorImp; + static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; + break; + case MapType: + m_Type = MapType; + pNewImp = new MapIteratorImp; + static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; + break; + default: + break; + } + + m_pImp = pNewImp; + return *this; + } + + std::pair Iterator::operator *() + { + switch(m_Type) + { + case SequenceType: + return { g_EmptyString, *(static_cast(m_pImp)->m_Iterator->second)}; + break; + case MapType: + return {static_cast(m_pImp)->m_Iterator->first, + *(static_cast(m_pImp)->m_Iterator->second)}; + break; + default: + break; + } + + g_NoneNode.Clear(); + return { g_EmptyString, g_NoneNode}; + } + + Iterator & Iterator::operator ++ (int dummy) + { + switch(m_Type) + { + case SequenceType: + static_cast(m_pImp)->m_Iterator++; + break; + case MapType: + static_cast(m_pImp)->m_Iterator++; + break; + default: + break; + } + return *this; + } + + Iterator & Iterator::operator -- (int dummy) + { + switch(m_Type) + { + case SequenceType: + static_cast(m_pImp)->m_Iterator--; + break; + case MapType: + static_cast(m_pImp)->m_Iterator--; + break; + default: + break; + } + return *this; + } + + bool Iterator::operator == (const Iterator & it) + { + if(m_Type != it.m_Type) + { + return false; + } + + switch(m_Type) + { + case SequenceType: + return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; + break; + case MapType: + return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; + break; + default: + break; + } + + return false; + } + + bool Iterator::operator != (const Iterator & it) + { + return !(*this == it); + } + + + // Const Iterator class + ConstIterator::ConstIterator() : + m_Type(None), + m_pImp(nullptr) + { + } + + ConstIterator::~ConstIterator() + { + if(m_pImp) + { + switch(m_Type) + { + case SequenceType: + delete static_cast(m_pImp); + break; + case MapType: + delete static_cast(m_pImp); + break; + default: + break; + } + + } + } + + ConstIterator::ConstIterator(const ConstIterator & it) : + m_Type(None), + m_pImp(nullptr) + { + *this = it; + } + + ConstIterator & ConstIterator::operator = (const ConstIterator & it) + { + if(m_pImp) + { + switch(m_Type) + { + case SequenceType: + delete static_cast(m_pImp); + break; + case MapType: + delete static_cast(m_pImp); + break; + default: + break; + } + m_pImp = nullptr; + m_Type = None; + } + + IteratorImp * pNewImp = nullptr; + + switch(it.m_Type) + { + case SequenceType: + m_Type = SequenceType; + pNewImp = new SequenceConstIteratorImp; + static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; + break; + case MapType: + m_Type = MapType; + pNewImp = new MapConstIteratorImp; + static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; + break; + default: + break; + } + + m_pImp = pNewImp; + return *this; + } + + std::pair ConstIterator::operator *() + { + switch(m_Type) + { + case SequenceType: + return { g_EmptyString, *(static_cast(m_pImp)->m_Iterator->second)}; + break; + case MapType: + return {static_cast(m_pImp)->m_Iterator->first, + *(static_cast(m_pImp)->m_Iterator->second)}; + break; + default: + break; + } + + g_NoneNode.Clear(); + return { g_EmptyString, g_NoneNode}; + } + + ConstIterator & ConstIterator::operator ++ (int dummy) + { + switch(m_Type) + { + case SequenceType: + static_cast(m_pImp)->m_Iterator++; + break; + case MapType: + static_cast(m_pImp)->m_Iterator++; + break; + default: + break; + } + return *this; + } + + ConstIterator & ConstIterator::operator -- (int dummy) + { + switch(m_Type) + { + case SequenceType: + static_cast(m_pImp)->m_Iterator--; + break; + case MapType: + static_cast(m_pImp)->m_Iterator--; + break; + default: + break; + } + return *this; + } + + bool ConstIterator::operator == (const ConstIterator & it) + { + if(m_Type != it.m_Type) + { + return false; + } + + switch(m_Type) + { + case SequenceType: + return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; + break; + case MapType: + return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; + break; + default: + break; + } + + return false; + } + + bool ConstIterator::operator != (const ConstIterator & it) + { + return !(*this == it); + } + + + // Node class + Node::Node() : + m_pImp(new NodeImp) + { + } + + Node::Node(const Node & node) : + Node() + { + *this = node; + } + + Node::Node(const std::string & value) : + Node() + { + *this = value; + } + + Node::Node(const char * value) : + Node() + { + *this = value; + } + + Node::~Node() + { + delete static_cast(m_pImp); + } + + Node::eType Node::Type() const + { + return NODE_IMP->m_Type; + } + + bool Node::IsNone() const + { + return NODE_IMP->m_Type == Node::None; + } + + bool Node::IsSequence() const + { + return NODE_IMP->m_Type == Node::SequenceType; + } + + bool Node::IsMap() const + { + return NODE_IMP->m_Type == Node::MapType; + } + + bool Node::IsScalar() const + { + return NODE_IMP->m_Type == Node::ScalarType; + } + + void Node::Clear() + { + NODE_IMP->Clear(); + } + + size_t Node::Size() const + { + if(TYPE_IMP == nullptr) + { + return 0; + } + + return TYPE_IMP->GetSize(); + } + + Node & Node::Insert(const size_t index) + { + NODE_IMP->InitSequence(); + return *TYPE_IMP->Insert(index); + } + + Node & Node::PushFront() + { + NODE_IMP->InitSequence(); + return *TYPE_IMP->PushFront(); + } + Node & Node::PushBack() + { + NODE_IMP->InitSequence(); + return *TYPE_IMP->PushBack(); + } + + Node & Node::operator[](const size_t index) + { + NODE_IMP->InitSequence(); + Node * pNode = TYPE_IMP->GetNode(index); + if(pNode == nullptr) + { + g_NoneNode.Clear(); + return g_NoneNode; + } + return *pNode; + } + + Node & Node::operator[](const std::string & key) + { + NODE_IMP->InitMap(); + return *TYPE_IMP->GetNode(key); + } + + void Node::Erase(const size_t index) + { + if(TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::SequenceType) + { + return; + } + + return TYPE_IMP->Erase(index); + } + + void Node::Erase(const std::string & key) + { + if(TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::MapType) + { + return; + } + + return TYPE_IMP->Erase(key); + } + + Node & Node::operator = (const Node & node) + { + NODE_IMP->Clear(); + CopyNode(node, *this); + return *this; + } + + Node & Node::operator = (const std::string & value) + { + NODE_IMP->InitScalar(); + TYPE_IMP->SetData(value); + return *this; + } + + Node & Node::operator = (const char * value) + { + NODE_IMP->InitScalar(); + TYPE_IMP->SetData(value ? std::string(value) : ""); + return *this; + } + + Iterator Node::Begin() + { + Iterator it; + + if(TYPE_IMP != nullptr) + { + IteratorImp * pItImp = nullptr; + + switch(NODE_IMP->m_Type) + { + case Node::SequenceType: + it.m_Type = Iterator::SequenceType; + pItImp = new SequenceIteratorImp; + pItImp->InitBegin(static_cast(TYPE_IMP)); + break; + case Node::MapType: + it.m_Type = Iterator::MapType; + pItImp = new MapIteratorImp; + pItImp->InitBegin(static_cast(TYPE_IMP)); + break; + default: + break; + } + + it.m_pImp = pItImp; + } + + return it; + } + + ConstIterator Node::Begin() const + { + ConstIterator it; + + if(TYPE_IMP != nullptr) + { + IteratorImp * pItImp = nullptr; + + switch(NODE_IMP->m_Type) + { + case Node::SequenceType: + it.m_Type = ConstIterator::SequenceType; + pItImp = new SequenceConstIteratorImp; + pItImp->InitBegin(static_cast(TYPE_IMP)); + break; + case Node::MapType: + it.m_Type = ConstIterator::MapType; + pItImp = new MapConstIteratorImp; + pItImp->InitBegin(static_cast(TYPE_IMP)); + break; + default: + break; + } + + it.m_pImp = pItImp; + } + + return it; + } + + Iterator Node::End() + { + Iterator it; + + if(TYPE_IMP != nullptr) + { + IteratorImp * pItImp = nullptr; + + switch(NODE_IMP->m_Type) + { + case Node::SequenceType: + it.m_Type = Iterator::SequenceType; + pItImp = new SequenceIteratorImp; + pItImp->InitEnd(static_cast(TYPE_IMP)); + break; + case Node::MapType: + it.m_Type = Iterator::MapType; + pItImp = new MapIteratorImp; + pItImp->InitEnd(static_cast(TYPE_IMP)); + break; + default: + break; + } + + it.m_pImp = pItImp; + } + + return it; + } + + ConstIterator Node::End() const + { + ConstIterator it; + + if(TYPE_IMP != nullptr) + { + IteratorImp * pItImp = nullptr; + + switch(NODE_IMP->m_Type) + { + case Node::SequenceType: + it.m_Type = ConstIterator::SequenceType; + pItImp = new SequenceConstIteratorImp; + pItImp->InitEnd(static_cast(TYPE_IMP)); + break; + case Node::MapType: + it.m_Type = ConstIterator::MapType; + pItImp = new MapConstIteratorImp; + pItImp->InitEnd(static_cast(TYPE_IMP)); + break; + default: + break; + } + + it.m_pImp = pItImp; + } + + return it; + } + + const std::string & Node::AsString() const + { + if(TYPE_IMP == nullptr) + { + return g_EmptyString; + } + + return TYPE_IMP->GetData(); + } + + + + // Reader implementations + /** + * @breif Line information structure. + * + */ + class ReaderLine + { + + public: + + /** + * @breif Constructor. + * + */ + ReaderLine(const std::string & data = "", + const size_t no = 0, + const size_t offset = 0, + const Node::eType type = Node::None, + const unsigned char flags = 0) : + Data(data), + No(no), + Offset(offset), + Type(type), + Flags(flags), + NextLine(nullptr) + { + } + + enum eFlag + { + LiteralScalarFlag, ///< Literal scalar type, defined as "|". + FoldedScalarFlag, ///< Folded scalar type, defined as "<". + ScalarNewlineFlag ///< Scalar ends with a newline. + }; + + /** + * @breif Set flag. + * + */ + void SetFlag(const eFlag flag) + { + Flags |= FlagMask[static_cast(flag)]; + } + + /** + * @breif Set flags by mask value. + * + */ + void SetFlags(const unsigned char flags) + { + Flags |= flags; + } + + /** + * @breif Unset flag. + * + */ + void UnsetFlag(const eFlag flag) + { + Flags &= ~FlagMask[static_cast(flag)]; + } + + /** + * @breif Unset flags by mask value. + * + */ + void UnsetFlags(const unsigned char flags) + { + Flags &= ~flags; + } + + /** + * @breif Get flag value. + * + */ + bool GetFlag(const eFlag flag) const + { + return Flags & FlagMask[static_cast(flag)]; + } + + /** + * @breif Copy and replace scalar flags from another ReaderLine. + * + */ + void CopyScalarFlags(ReaderLine * from) + { + if (from == nullptr) + { + return; + } + + unsigned char newFlags = from->Flags & (FlagMask[0] | FlagMask[1] | FlagMask[2]); + Flags |= newFlags; + } + + static const unsigned char FlagMask[3]; + + std::string Data; ///< Data of line. + size_t No; ///< Line number. + size_t Offset; ///< Offset to first character in data. + Node::eType Type; ///< Type of line. + unsigned char Flags; ///< Flags of line. + ReaderLine * NextLine; ///< Pointer to next line. + + + + }; + + const unsigned char ReaderLine::FlagMask[3] = { 0x01, 0x02, 0x04 }; + + + /** + * @breif Implementation class of Yaml parsing. + * Parsing incoming stream and outputs a root node. + * + */ + class ParseImp + { + + public: + + /** + * @breif Default constructor. + * + */ + ParseImp() + { + } + + /** + * @breif Destructor. + * + */ + ~ParseImp() + { + ClearLines(); + } + + /** + * @breif Run full parsing procedure. + * + */ + void Parse(Node & root, std::iostream & stream) + { + try + { + root.Clear(); + ReadLines(stream); + PostProcessLines(); + //Print(); + ParseRoot(root); + } + catch(Exception e) + { + root.Clear(); + throw; + } + } + + private: + + /** + * @breif Copy constructor. + * + */ + ParseImp(const ParseImp & copy) + { + + } + + /** + * @breif Read all lines. + * Ignoring: + * - Empty lines. + * - Comments. + * - Document start/end. + * + */ + void ReadLines(std::iostream & stream) + { + std::string line = ""; + size_t lineNo = 0; + bool documentStartFound = false; + bool foundFirstNotEmpty = false; + std::streampos streamPos = 0; + + // Read all lines, as long as the stream is ok. + while (!stream.eof() && !stream.fail()) + { + // Read line + streamPos = stream.tellg(); + std::getline(stream, line); + lineNo++; + + // Remove comment + const size_t commentPos = FindNotCited(line, '#'); + if(commentPos != std::string::npos) + { + line.resize(commentPos); + } + + // Start of document. + if (documentStartFound == false && line == "---") + { + // Erase all lines before this line. + ClearLines(); + documentStartFound = true; + continue; + } + + // End of document. + if (line == "...") + { + break; + } + else if(line == "---") + { + stream.seekg(streamPos); + break; + } + + // Remove trailing return. + if (line.size()) + { + if (line[line.size() - 1] == '\r') + { + line.resize(line.size() - 1); + } + } + + // Validate characters. + for (size_t i = 0; i < line.size(); i++) + { + if (line[i] != '\t' && (line[i] < 32 || line[i] > 125)) + { + throw ParsingException(ExceptionMessage(g_ErrorInvalidCharacter, lineNo, i + 1)); + } + } + + // Validate tabs + const size_t firstTabPos = line.find_first_of('\t'); + size_t startOffset = line.find_first_not_of(" \t"); + + // Make sure no tabs are in the very front. + if (startOffset != std::string::npos) + { + if(firstTabPos < startOffset) + { + throw ParsingException(ExceptionMessage(g_ErrorTabInOffset, lineNo, firstTabPos)); + } + + // Remove front spaces. + line = line.substr(startOffset); + } + else + { + startOffset = 0; + line = ""; + } + + // Add line. + if(foundFirstNotEmpty == false) + { + if(line.size()) + { + foundFirstNotEmpty = true; + } + else + { + continue; + } + } + + ReaderLine * pLine = new ReaderLine(line, lineNo, startOffset); + m_Lines.push_back(pLine); + } + } + + /** + * @breif Run post-processing on all lines. + * Basically split lines into multiple lines if needed, to follow the parsing algorithm. + * + */ + void PostProcessLines() + { + for (auto it = m_Lines.begin(); it != m_Lines.end();) + { + // Sequence. + if (PostProcessSequenceLine(it) == true) + { + continue; + } + + // Mapping. + if (PostProcessMappingLine(it) == true) + { + continue; + } + + // Scalar. + PostProcessScalarLine(it); + } + + // Set next line of all lines. + if (m_Lines.size()) + { + if (m_Lines.back()->Type != Node::ScalarType) + { + throw ParsingException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *m_Lines.back())); + } + + if (m_Lines.size() > 1) + { + auto prevEnd = m_Lines.end(); + --prevEnd; + + for (auto it = m_Lines.begin(); it != prevEnd; it++) + { + auto nextIt = it; + ++nextIt; + + (*it)->NextLine = *nextIt; + } + } + } + } + + /** + * @breif Run post-processing and check for sequence. + * Split line into two lines if sequence token is not on it's own line. + * + * @return true if line is sequence, else false. + * + */ + bool PostProcessSequenceLine(std::list::iterator & it) + { + ReaderLine * pLine = *it; + + // Sequence split + if (IsSequenceStart(pLine->Data) == false) + { + return false; + } + + pLine->Type = Node::SequenceType; + + ClearTrailingEmptyLines(++it); + + const size_t valueStart = pLine->Data.find_first_not_of(" \t", 1); + if (valueStart == std::string::npos) + { + return true; + } + + // Create new line and insert + std::string newLine = pLine->Data.substr(valueStart); + it = m_Lines.insert(it, new ReaderLine(newLine, pLine->No, pLine->Offset + valueStart)); + pLine->Data = ""; + + return false; + } + + /** + * @breif Run post-processing and check for mapping. + * Split line into two lines if mapping value is not on it's own line. + * + * @return true if line is mapping, else move on to scalar parsing. + * + */ + bool PostProcessMappingLine(std::list::iterator & it) + { + ReaderLine * pLine = *it; + + // Find map key. + size_t preKeyQuotes = 0; + size_t tokenPos = FindNotCited(pLine->Data, ':', preKeyQuotes); + if (tokenPos == std::string::npos) + { + return false; + } + if(preKeyQuotes > 1) + { + throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine)); + } + + pLine->Type = Node::MapType; + + // Get key + std::string key = pLine->Data.substr(0, tokenPos); + const size_t keyEnd = key.find_last_not_of(" \t"); + if (keyEnd == std::string::npos) + { + throw ParsingException(ExceptionMessage(g_ErrorKeyMissing, *pLine)); + } + key.resize(keyEnd + 1); + + // Handle cited key. + if(preKeyQuotes == 1) + { + if(key.front() != '"' || key.back() != '"') + { + throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine)); + } + + key = key.substr(1, key.size() - 2); + } + RemoveAllEscapeTokens(key); + + // Get value + std::string value = ""; + size_t valueStart = std::string::npos; + if (tokenPos + 1 != pLine->Data.size()) + { + valueStart = pLine->Data.find_first_not_of(" \t", tokenPos + 1); + if (valueStart != std::string::npos) + { + value = pLine->Data.substr(valueStart); + } + } + + // Make sure the value is not a sequence start. + if (IsSequenceStart(value) == true) + { + throw ParsingException(ExceptionMessage(g_ErrorBlockSequenceNotAllowed, *pLine, valueStart)); + } + + pLine->Data = key; + + + // Remove all empty lines after map key. + ClearTrailingEmptyLines(++it); + + // Add new empty line? + size_t newLineOffset = valueStart; + if(newLineOffset == std::string::npos) + { + if(it != m_Lines.end() && (*it)->Offset > pLine->Offset) + { + return true; + } + + newLineOffset = tokenPos + 2; + } + else + { + newLineOffset += pLine->Offset; + } + + // Add new line with value. + unsigned char dummyBlockFlags = 0; + if(IsBlockScalar(value, pLine->No, dummyBlockFlags) == true) + { + newLineOffset = pLine->Offset; + } + ReaderLine * pNewLine = new ReaderLine(value, pLine->No, newLineOffset, Node::ScalarType); + it = m_Lines.insert(it, pNewLine); + + // Return false in order to handle next line(scalar value). + return false; + } + + /** + * @breif Run post-processing and check for scalar. + * Checking for multi-line scalars. + * + * @return true if scalar search should continue, else false. + * + */ + void PostProcessScalarLine(std::list::iterator & it) + { + ReaderLine * pLine = *it; + pLine->Type = Node::ScalarType; + + size_t parentOffset = pLine->Offset; + if(pLine != m_Lines.front()) + { + std::list::iterator lastIt = it; + --lastIt; + parentOffset = (*lastIt)->Offset; + } + + std::list::iterator lastNotEmpty = it++; + + // Find last empty lines + while(it != m_Lines.end()) + { + pLine = *it; + pLine->Type = Node::ScalarType; + if(pLine->Data.size()) + { + if(pLine->Offset <= parentOffset) + { + break; + } + else + { + lastNotEmpty = it; + } + } + ++it; + } + + ClearTrailingEmptyLines(++lastNotEmpty); + } + + /** + * @breif Process root node and start of document. + * + */ + void ParseRoot(Node & root) + { + // Get first line and start type. + auto it = m_Lines.begin(); + if(it == m_Lines.end()) + { + return; + } + Node::eType type = (*it)->Type; + ReaderLine * pLine = *it; + + // Handle next line. + switch(type) + { + case Node::SequenceType: + ParseSequence(root, it); + break; + case Node::MapType: + ParseMap(root, it); + break; + case Node::ScalarType: + ParseScalar(root, it); + break; + default: + break; + } + + if(it != m_Lines.end()) + { + throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); + } + + } + + /** + * @breif Process sequence node. + * + */ + void ParseSequence(Node & node, std::list::iterator & it) + { + ReaderLine * pNextLine = nullptr; + while(it != m_Lines.end()) + { + ReaderLine * pLine = *it; + Node & childNode = node.PushBack(); + + // Move to next line, error check. + ++it; + if(it == m_Lines.end()) + { + throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); + } + + // Handle value of map + Node::eType valueType = (*it)->Type; + switch(valueType) + { + case Node::SequenceType: + ParseSequence(childNode, it); + break; + case Node::MapType: + ParseMap(childNode, it); + break; + case Node::ScalarType: + ParseScalar(childNode, it); + break; + default: + break; + } + + // Check next line. if sequence and correct level, go on, else exit. + // If same level but but of type map = error. + if(it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset)) + { + break; + } + if(pNextLine->Offset > pLine->Offset) + { + throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine)); + } + if(pNextLine->Type != Node::SequenceType) + { + throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine)); + } + + } + } + + /** + * @breif Process map node. + * + */ + void ParseMap(Node & node, std::list::iterator & it) + { + ReaderLine * pNextLine = nullptr; + while(it != m_Lines.end()) + { + ReaderLine * pLine = *it; + Node & childNode = node[pLine->Data]; + + // Move to next line, error check. + ++it; + if(it == m_Lines.end()) + { + throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); + } + + // Handle value of map + Node::eType valueType = (*it)->Type; + switch(valueType) + { + case Node::SequenceType: + ParseSequence(childNode, it); + break; + case Node::MapType: + ParseMap(childNode, it); + break; + case Node::ScalarType: + ParseScalar(childNode, it); + break; + default: + break; + } + + // Check next line. if map and correct level, go on, else exit. + // if same level but but of type map = error. + if(it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset)) + { + break; + } + if(pNextLine->Offset > pLine->Offset) + { + throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine)); + } + if(pNextLine->Type != pLine->Type) + { + throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine)); + } + + } + } + + /** + * @breif Process scalar node. + * + */ + void ParseScalar(Node & node, std::list::iterator & it) + { + std::string data = ""; + ReaderLine * pFirstLine = *it; + ReaderLine * pLine = *it; + + // Check if current line is a block scalar. + unsigned char blockFlags = 0; + bool isBlockScalar = IsBlockScalar(pLine->Data, pLine->No, blockFlags); + const bool newLineFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]); + const bool foldedFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::FoldedScalarFlag)]); + const bool literalFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::LiteralScalarFlag)]); + size_t parentOffset = 0; + + // Find parent offset + if(it != m_Lines.begin()) + { + std::list::iterator parentIt = it; + --parentIt; + parentOffset = (*parentIt)->Offset; + } + + // Move to next iterator/line if current line is a block scalar. + if(isBlockScalar) + { + ++it; + if(it == m_Lines.end() || (pLine = *it)->Type != Node::ScalarType) + { + return; + } + } + + // Not a block scalar, cut end spaces/tabs + if(isBlockScalar == false) + { + while(1) + { + pLine = *it; + + if(parentOffset != 0 && pLine->Offset <= parentOffset) + { + throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); + } + + const size_t endOffset = pLine->Data.find_last_not_of(" \t"); + if(endOffset == std::string::npos) + { + data += "\n"; + } + else + { + data += pLine->Data.substr(0, endOffset + 1); + } + + // Move to next line + ++it; + if(it == m_Lines.end() || (*it)->Type != Node::ScalarType) + { + break; + } + + data += " "; + } + + if(ValidateQuote(data) == false) + { + throw ParsingException(ExceptionMessage(g_ErrorInvalidQuote, *pFirstLine)); + } + } + // Block scalar + else + { + pLine = *it; + size_t blockOffset = pLine->Offset; + if(blockOffset <= parentOffset) + { + throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); + } + + bool addedSpace = false; + while(it != m_Lines.end() && (*it)->Type == Node::ScalarType) + { + pLine = *it; + + const size_t endOffset = pLine->Data.find_last_not_of(" \t"); + if(endOffset != std::string::npos && pLine->Offset < blockOffset) + { + throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); + } + + if(endOffset == std::string::npos) + { + if(addedSpace) + { + data[data.size() - 1] = '\n'; + addedSpace = false; + } + else + { + data += "\n"; + } + + ++it; + continue; + } + else + { + if(blockOffset != pLine->Offset && foldedFlag) + { + if(addedSpace) + { + data[data.size() - 1] = '\n'; + addedSpace = false; + } + else + { + data += "\n"; + } + } + data += std::string(pLine->Offset - blockOffset, ' '); + data += pLine->Data; + } + + // Move to next line + ++it; + if(it == m_Lines.end() || (*it)->Type != Node::ScalarType) + { + if(newLineFlag) + { + data += "\n"; + } + break; + } + + if(foldedFlag) + { + data += " "; + addedSpace = true; + } + else if(literalFlag && endOffset != std::string::npos) + { + data += "\n"; + } + } + } + + if(data.size() && (data[0] == '"' || data[0] == '\'')) + { + data = data.substr(1, data.size() - 2 ); + } + + node = data; + } + + /** + * @breif Debug printing. + * + */ + void Print() + { + for (auto it = m_Lines.begin(); it != m_Lines.end(); it++) + { + + ReaderLine * pLine = *it; + + // Print type + if (pLine->Type == Node::SequenceType) + { + std::cout << "seq "; + } + else if (pLine->Type == Node::MapType) + { + std::cout << "map "; + } + else if (pLine->Type == Node::ScalarType) + { + std::cout << "sca "; + } + else + { + std::cout << " "; + } + + // Print flags + if (pLine->GetFlag(ReaderLine::FoldedScalarFlag)) + { + std::cout << "f"; + } + else + { + std::cout << "-"; + } + if (pLine->GetFlag(ReaderLine::LiteralScalarFlag)) + { + std::cout << "l"; + } + else + { + std::cout << "-"; + } + if (pLine->GetFlag(ReaderLine::ScalarNewlineFlag)) + { + std::cout << "n"; + } + else + { + std::cout << "-"; + } + if (pLine->NextLine == nullptr) + { + std::cout << "e"; + } + else + { + std::cout << "-"; + } + + + std::cout << "| "; + std::cout << pLine->No << " "; + std::cout << std::string(pLine->Offset, ' '); + + if (pLine->Type == Node::ScalarType) + { + std::string scalarValue = pLine->Data; + for (size_t i = 0; (i = scalarValue.find("\n", i)) != std::string::npos;) + { + scalarValue.replace(i, 1, "\\n"); + i += 2; + } + std::cout << scalarValue << std::endl; + } + else if (pLine->Type == Node::MapType) + { + std::cout << pLine->Data + ":" << std::endl; + } + else if (pLine->Type == Node::SequenceType) + { + std::cout << "-" << std::endl; + } + else + { + std::cout << "> UNKOWN TYPE <" << std::endl; + } + } + } + + /** + * @breif Clear all read lines. + * + */ + void ClearLines() + { + for (auto it = m_Lines.begin(); it != m_Lines.end(); it++) + { + delete *it; + } + m_Lines.clear(); + } + + void ClearTrailingEmptyLines(std::list::iterator & it) + { + while(it != m_Lines.end()) + { + ReaderLine * pLine = *it; + if(pLine->Data.size() == 0) + { + delete *it; + it = m_Lines.erase(it); + } + else + { + return; + } + + } + } + + static bool IsSequenceStart(const std::string & data) + { + if (data.size() == 0 || data[0] != '-') + { + return false; + } + + if (data.size() >= 2 && data[1] != ' ') + { + return false; + } + + return true; + } + + static bool IsBlockScalar(const std::string & data, const size_t line, unsigned char & flags) + { + flags = 0; + if(data.size() == 0) + { + return false; + } + + if(data[0] == '|') + { + if(data.size() >= 2) + { + if(data[1] != '-' && data[1] != ' ' && data[1] != '\t') + { + throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data)); + } + } + else + { + flags |= ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]; + } + flags |= ReaderLine::FlagMask[static_cast(ReaderLine::LiteralScalarFlag)]; + return true; + } + + if(data[0] == '>') + { + if(data.size() >= 2) + { + if(data[1] != '-' && data[1] != ' ' && data[1] != '\t') + { + throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data)); + } + } + else + { + flags |= ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]; + } + flags |= ReaderLine::FlagMask[static_cast(ReaderLine::FoldedScalarFlag)]; + return true; + } + + return false; + } + + std::list m_Lines; ///< List of lines. + + }; + + // Parsing functions + void Parse(Node & root, const char * filename) + { + std::ifstream f(filename, std::ifstream::binary); + if (f.is_open() == false) + { + throw OperationException(g_ErrorCannotOpenFile); + } + + f.seekg(0, f.end); + size_t fileSize = static_cast(f.tellg()); + f.seekg(0, f.beg); + + std::unique_ptr data(new char[fileSize]); + f.read(data.get(), fileSize); + f.close(); + + Parse(root, data.get(), fileSize); + } + + void Parse(Node & root, std::iostream & stream) + { + ParseImp * pImp = nullptr; + + try + { + pImp = new ParseImp; + pImp->Parse(root, stream); + delete pImp; + } + catch (const Exception e) + { + delete pImp; + throw; + } + } + + void Parse(Node & root, const std::string & string) + { + std::stringstream ss(string); + Parse(root, ss); + } + + void Parse(Node & root, const char * buffer, const size_t size) + { + std::stringstream ss(std::string(buffer, size)); + Parse(root, ss); + } + + + // Serialize configuration structure. + SerializeConfig::SerializeConfig(const size_t spaceIndentation, + const size_t scalarMaxLength, + const bool sequenceMapNewline, + const bool mapScalarNewline) : + SpaceIndentation(spaceIndentation), + ScalarMaxLength(scalarMaxLength), + SequenceMapNewline(sequenceMapNewline), + MapScalarNewline(mapScalarNewline) + { + } + + + // Serialization functions + void Serialize(const Node & root, const char * filename, const SerializeConfig & config) + { + std::stringstream stream; + Serialize(root, stream, config); + + std::ofstream f(filename); + if (f.is_open() == false) + { + throw OperationException(g_ErrorCannotOpenFile); + } + + f.write(stream.str().c_str(), stream.str().size()); + f.close(); + } + + size_t LineFolding(const std::string & input, std::vector & folded, const size_t maxLength) + { + folded.clear(); + if(input.size() == 0) + { + return 0; + } + + size_t currentPos = 0; + size_t lastPos = 0; + size_t spacePos = std::string::npos; + while(currentPos < input.size()) + { + currentPos = lastPos + maxLength; + + if(currentPos < input.size()) + { + spacePos = input.find_first_of(' ', currentPos); + } + + if(spacePos == std::string::npos || currentPos >= input.size()) + { + const std::string endLine = input.substr(lastPos); + if(endLine.size()) + { + folded.push_back(endLine); + } + + return folded.size(); + } + + folded.push_back(input.substr(lastPos, spacePos - lastPos)); + + lastPos = spacePos + 1; + } + + return folded.size(); + } + + static void SerializeLoop(const Node & node, std::iostream & stream, bool useLevel, const size_t level, const SerializeConfig & config) + { + const size_t indention = config.SpaceIndentation; + + switch(node.Type()) + { + case Node::SequenceType: + { + for(auto it = node.Begin(); it != node.End(); it++) + { + const Node & value = (*it).second; + if(value.IsNone()) + { + continue; + } + stream << std::string(level, ' ') << "- "; + useLevel = false; + if(value.IsSequence() || (value.IsMap() && config.SequenceMapNewline == true)) + { + useLevel = true; + stream << "\n"; + } + + SerializeLoop(value, stream, useLevel, level + 2, config); + } + + } + break; + case Node::MapType: + { + size_t count = 0; + for(auto it = node.Begin(); it != node.End(); it++) + { + const Node & value = (*it).second; + if(value.IsNone()) + { + continue; + } + + if(useLevel || count > 0) + { + stream << std::string(level, ' '); + } + + std::string key = (*it).first; + AddEscapeTokens(key, "\\\""); + if(ShouldBeCited(key)) + { + stream << "\"" << key << "\"" << ": "; + } + else + { + stream << key << ": "; + } + + + useLevel = false; + if(value.IsScalar() == false || (value.IsScalar() && config.MapScalarNewline)) + { + useLevel = true; + stream << "\n"; + } + + SerializeLoop(value, stream, useLevel, level + indention, config); + + useLevel = true; + count++; + } + + } + break; + case Node::ScalarType: + { + const std::string value = node.As(); + + // Empty scalar + if(value.size() == 0) + { + stream << "\n"; + break; + } + + // Get lines of scalar. + std::string line = ""; + std::vector lines; + std::istringstream iss(value); + while (iss.eof() == false) + { + std::getline(iss, line); + lines.push_back(line); + } + + // Block scalar + const std::string & lastLine = lines.back(); + const bool endNewline = lastLine.size() == 0; + if(endNewline) + { + lines.pop_back(); + } + + // Literal + if(lines.size() > 1) + { + stream << "|"; + } + // Folded/plain + else + { + const std::string frontLine = lines.front(); + if(config.ScalarMaxLength == 0 || lines.front().size() <= config.ScalarMaxLength || + LineFolding(frontLine, lines, config.ScalarMaxLength) == 1) + { + if(useLevel) + { + stream << std::string(level, ' '); + } + + if(ShouldBeCited(value)) + { + stream << "\"" << value << "\"\n"; + break; + } + stream << value << "\n"; + break; + } + else + { + stream << ">"; + } + } + + if(endNewline == false) + { + stream << "-"; + } + stream << "\n"; + + + for(auto it = lines.begin(); it != lines.end(); it++) + { + stream << std::string(level, ' ') << (*it) << "\n"; + } + } + break; + + default: + break; + } + } + + void Serialize(const Node & root, std::iostream & stream, const SerializeConfig & config) + { + if(config.SpaceIndentation < 2) + { + throw OperationException(g_ErrorIndentation); + } + + SerializeLoop(root, stream, false, 0, config); + } + + void Serialize(const Node & root, std::string & string, const SerializeConfig & config) + { + std::stringstream stream; + Serialize(root, stream, config); + string = stream.str(); + } + + + + // Static function implementations + std::string ExceptionMessage(const std::string & message, ReaderLine & line) + { + return message + std::string(" Line ") + std::to_string(line.No) + std::string(": ") + line.Data; + } + + std::string ExceptionMessage(const std::string & message, ReaderLine & line, const size_t errorPos) + { + return message + std::string(" Line ") + std::to_string(line.No) + std::string(" column ") + std::to_string(errorPos + 1) + std::string(": ") + line.Data; + } + + std::string ExceptionMessage(const std::string & message, const size_t errorLine, const size_t errorPos) + { + return message + std::string(" Line ") + std::to_string(errorLine) + std::string(" column ") + std::to_string(errorPos); + } + + std::string ExceptionMessage(const std::string & message, const size_t errorLine, const std::string & data) + { + return message + std::string(" Line ") + std::to_string(errorLine) + std::string(": ") + data; + } + + bool FindQuote(const std::string & input, size_t & start, size_t & end, size_t searchPos) + { + start = end = std::string::npos; + size_t qPos = searchPos; + bool foundStart = false; + + while(qPos != std::string::npos) + { + // Find first quote. + qPos = input.find_first_of("\"'", qPos); + if(qPos == std::string::npos) + { + return false; + } + + const char token = input[qPos]; + if(token == '"' && (qPos == 0 || input[qPos-1] != '\\')) + { + // Found start quote. + if(foundStart == false) + { + start = qPos; + foundStart = true; + } + // Found end quote + else + { + end = qPos; + return true; + } + } + + // Check if it's possible for another loop. + if(qPos + 1 == input.size()) + { + return false; + } + qPos++; + } + + return false; + } + + size_t FindNotCited(const std::string & input, char token, size_t & preQuoteCount) + { + preQuoteCount = 0; + size_t tokenPos = input.find_first_of(token); + if(tokenPos == std::string::npos) + { + return std::string::npos; + } + + // Find all quotes + std::vector> quotes; + + size_t quoteStart = 0; + size_t quoteEnd = 0; + while(FindQuote(input, quoteStart, quoteEnd, quoteEnd)) + { + quotes.push_back({quoteStart, quoteEnd}); + + if(quoteEnd + 1 == input.size()) + { + break; + } + quoteEnd++; + } + + if(quotes.size() == 0) + { + return tokenPos; + } + + size_t currentQuoteIndex = 0; + std::pair currentQuote = {0, 0}; + + while(currentQuoteIndex < quotes.size()) + { + currentQuote = quotes[currentQuoteIndex]; + + if(tokenPos < currentQuote.first) + { + return tokenPos; + } + preQuoteCount++; + if(tokenPos <= currentQuote.second) + { + // Find next token + if(tokenPos + 1 == input.size()) + { + return std::string::npos; + } + tokenPos = input.find_first_of(token, tokenPos + 1); + if(tokenPos == std::string::npos) + { + return std::string::npos; + } + } + + currentQuoteIndex++; + } + + return tokenPos; + } + + size_t FindNotCited(const std::string & input, char token) + { + size_t dummy = 0; + return FindNotCited(input, token, dummy); + } + + bool ValidateQuote(const std::string & input) + { + if(input.size() == 0) + { + return true; + } + + char token = 0; + size_t searchPos = 0; + if(input[0] == '\"' || input[0] == '\'') + { + if(input.size() == 1) + { + return false; + } + token = input[0]; + searchPos = 1; + } + + while(searchPos != std::string::npos && searchPos < input.size() - 1) + { + searchPos = input.find_first_of("\"'", searchPos + 1); + if(searchPos == std::string::npos) + { + break; + } + + const char foundToken = input[searchPos]; + + if(input[searchPos] == '\"' || input[searchPos] == '\'') + { + if(token == 0 && input[searchPos-1] != '\\') + { + return false; + } + //if(foundToken == token) + //{ + + /*if(foundToken == token && searchPos == input.size() - 1 && input[searchPos-1] != '\\') + { + return true; + if(searchPos == input.size() - 1) + { + return true; + } + return false; + } + else */ + if(foundToken == token && input[searchPos-1] != '\\') + { + if(searchPos == input.size() - 1) + { + return true; + } + return false; + } + //} + } + } + + return token == 0; + } + + void CopyNode(const Node & from, Node & to) + { + const Node::eType type = from.Type(); + + switch(type) + { + case Node::SequenceType: + for(auto it = from.Begin(); it != from.End(); it++) + { + const Node & currentNode = (*it).second; + Node & newNode = to.PushBack(); + CopyNode(currentNode, newNode); + } + break; + case Node::MapType: + for(auto it = from.Begin(); it != from.End(); it++) + { + const Node & currentNode = (*it).second; + Node & newNode = to[(*it).first]; + CopyNode(currentNode, newNode); + } + break; + case Node::ScalarType: + to = from.As(); + break; + case Node::None: + break; + } + } + + bool ShouldBeCited(const std::string & key) + { + return key.find_first_of("\":{}[],&*#?|-<>=!%@") != std::string::npos; + } + + void AddEscapeTokens(std::string & input, const std::string & tokens) + { + for(auto it = tokens.begin(); it != tokens.end(); it++) + { + const char token = *it; + const std::string replace = std::string("\\") + std::string(1, token); + size_t found = input.find_first_of(token); + while(found != std::string::npos) + { + input.replace(found, 1, replace); + found = input.find_first_of(token, found + 2); + } + } + } + + void RemoveAllEscapeTokens(std::string & input) + { + size_t found = input.find_first_of("\\"); + while(found != std::string::npos) + { + if(found + 1 == input.size()) + { + return; + } + + std::string replace(1, input[found + 1]); + input.replace(found, 2, replace); + found = input.find_first_of("\\", found + 1); + } + } + + +} diff --git a/cpp/thirdparty/mini-yaml/yaml/Yaml.hpp b/cpp/thirdparty/mini-yaml/yaml/Yaml.hpp new file mode 100644 index 000000000..586657fb2 --- /dev/null +++ b/cpp/thirdparty/mini-yaml/yaml/Yaml.hpp @@ -0,0 +1,656 @@ +/* +* MIT License +* +* Copyright(c) 2018 Jimmie Bergmann +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files(the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions : +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +*/ + +/* +YAML documentation: +http://yaml.org/spec/1.0/index.html +https://www.codeproject.com/Articles/28720/YAML-Parser-in-C +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +/** +* @breif Namespace wrapping mini-yaml classes. +* +*/ +namespace Yaml +{ + + /** + * @breif Forward declarations. + * + */ + class Node; + + + /** + * @breif Helper classes and functions + * + */ + namespace impl + { + + /** + * @breif Helper functionality, converting string to any data type. + * Strings are left untouched. + * + */ + template + struct StringConverter + { + static T Get(const std::string & data) + { + T type; + std::stringstream ss(data); + ss >> type; + return type; + } + + static T Get(const std::string & data, const T & defaultValue) + { + T type; + std::stringstream ss(data); + ss >> type; + + if(ss.fail()) + { + return defaultValue; + } + + return type; + } + }; + template<> + struct StringConverter + { + static std::string Get(const std::string & data) + { + return data; + } + + static std::string Get(const std::string & data, const std::string & defaultValue) + { + if(data.size() == 0) + { + return defaultValue; + } + return data; + } + }; + + template<> + struct StringConverter + { + static bool Get(const std::string & data) + { + std::string tmpData = data; + std::transform(tmpData.begin(), tmpData.end(), tmpData.begin(), ::tolower); + if(tmpData == "true" || tmpData == "yes" || tmpData == "1") + { + return true; + } + + return false; + } + + static bool Get(const std::string & data, const bool & defaultValue) + { + if(data.size() == 0) + { + return defaultValue; + } + + return Get(data); + } + }; + + } + + + /** + * @breif Exception class. + * + */ + class Exception : public std::runtime_error + { + + public: + + /** + * @breif Enumeration of exception types. + * + */ + enum eType + { + InternalError, ///< Internal error. + ParsingError, ///< Invalid parsing data. + OperationError ///< User operation error. + }; + + /** + * @breif Constructor. + * + * @param message Exception message. + * @param type Type of exception. + * + */ + Exception(const std::string & message, const eType type); + + /** + * @breif Get type of exception. + * + */ + eType Type() const; + + /** + * @breif Get message of exception. + * + */ + const char * Message() const; + + private: + + eType m_Type; ///< Type of exception. + + }; + + + /** + * @breif Internal exception class. + * + * @see Exception + * + */ + class InternalException : public Exception + { + + public: + + /** + * @breif Constructor. + * + * @param message Exception message. + * + */ + InternalException(const std::string & message); + + }; + + + /** + * @breif Parsing exception class. + * + * @see Exception + * + */ + class ParsingException : public Exception + { + + public: + + /** + * @breif Constructor. + * + * @param message Exception message. + * + */ + ParsingException(const std::string & message); + + }; + + + /** + * @breif Operation exception class. + * + * @see Exception + * + */ + class OperationException : public Exception + { + + public: + + /** + * @breif Constructor. + * + * @param message Exception message. + * + */ + OperationException(const std::string & message); + + }; + + + /** + * @breif Iterator class. + * + */ + class Iterator + { + + public: + + friend class Node; + + /** + * @breif Default constructor. + * + */ + Iterator(); + + /** + * @breif Copy constructor. + * + */ + Iterator(const Iterator & it); + + /** + * @breif Assignment operator. + * + */ + Iterator & operator = (const Iterator & it); + + /** + * @breif Destructor. + * + */ + ~Iterator(); + + /** + * @breif Get node of iterator. + * First pair item is the key of map value, empty if type is sequence. + * + */ + std::pair operator *(); + + /** + * @breif Post-increment operator. + * + */ + Iterator & operator ++ (int); + + /** + * @breif Post-decrement operator. + * + */ + Iterator & operator -- (int); + + /** + * @breif Check if iterator is equal to other iterator. + * + */ + bool operator == (const Iterator & it); + + /** + * @breif Check if iterator is not equal to other iterator. + * + */ + bool operator != (const Iterator & it); + + private: + + enum eType + { + None, + SequenceType, + MapType + }; + + eType m_Type; ///< Type of iterator. + void * m_pImp; ///< Implementation of iterator class. + + }; + + + /** + * @breif Constant iterator class. + * + */ + class ConstIterator + { + + public: + + friend class Node; + + /** + * @breif Default constructor. + * + */ + ConstIterator(); + + /** + * @breif Copy constructor. + * + */ + ConstIterator(const ConstIterator & it); + + /** + * @breif Assignment operator. + * + */ + ConstIterator & operator = (const ConstIterator & it); + + /** + * @breif Destructor. + * + */ + ~ConstIterator(); + + /** + * @breif Get node of iterator. + * First pair item is the key of map value, empty if type is sequence. + * + */ + std::pair operator *(); + + /** + * @breif Post-increment operator. + * + */ + ConstIterator & operator ++ (int); + + /** + * @breif Post-decrement operator. + * + */ + ConstIterator & operator -- (int); + + /** + * @breif Check if iterator is equal to other iterator. + * + */ + bool operator == (const ConstIterator & it); + + /** + * @breif Check if iterator is not equal to other iterator. + * + */ + bool operator != (const ConstIterator & it); + + private: + + enum eType + { + None, + SequenceType, + MapType + }; + + eType m_Type; ///< Type of iterator. + void * m_pImp; ///< Implementation of constant iterator class. + + }; + + + /** + * @breif Node class. + * + */ + class Node + { + + public: + + friend class Iterator; + + /** + * @breif Enumeration of node types. + * + */ + enum eType + { + None, + SequenceType, + MapType, + ScalarType + }; + + /** + * @breif Default constructor. + * + */ + Node(); + + /** + * @breif Copy constructor. + * + */ + Node(const Node & node); + + /** + * @breif Assignment constructors. + * Converts node to scalar type if needed. + * + */ + Node(const std::string & value); + Node(const char * value); + + /** + * @breif Destructor. + * + */ + ~Node(); + + /** + * @breif Functions for checking type of node. + * + */ + eType Type() const; + bool IsNone() const; + bool IsSequence() const; + bool IsMap() const; + bool IsScalar() const; + + /** + * @breif Completely clear node. + * + */ + void Clear(); + + /** + * @breif Get node as given template type. + * + */ + template + T As() const + { + return impl::StringConverter::Get(AsString()); + } + + /** + * @breif Get node as given template type. + * + */ + template + T As(const T & defaultValue) const + { + return impl::StringConverter::Get(AsString(), defaultValue); + } + + /** + * @breif Get size of node. + * Nodes of type None or Scalar will return 0. + * + */ + size_t Size() const; + + // Sequence operators + + /** + * @breif Insert sequence item at given index. + * Converts node to sequence type if needed. + * Adding new item to end of sequence if index is larger than sequence size. + * + */ + Node & Insert(const size_t index); + + /** + * @breif Add new sequence index to back. + * Converts node to sequence type if needed. + * + */ + Node & PushFront(); + + /** + * @breif Add new sequence index to front. + * Converts node to sequence type if needed. + * + */ + Node & PushBack(); + + /** + * @breif Get sequence/map item. + * Converts node to sequence/map type if needed. + * + * @param index Sequence index. Returns None type Node if index is unknown. + * @param key Map key. Creates a new node if key is unknown. + * + */ + Node & operator [] (const size_t index); + Node & operator [] (const std::string & key); + + /** + * @breif Erase item. + * No action if node is not a sequence or map. + * + */ + void Erase(const size_t index); + void Erase(const std::string & key); + + /** + * @breif Assignment operators. + * + */ + Node & operator = (const Node & node); + Node & operator = (const std::string & value); + Node & operator = (const char * value); + + /** + * @breif Get start iterator. + * + */ + Iterator Begin(); + ConstIterator Begin() const; + + /** + * @breif Get end iterator. + * + */ + Iterator End(); + ConstIterator End() const; + + + private: + + /** + * @breif Get as string. If type is scalar, else empty. + * + */ + const std::string & AsString() const; + + void * m_pImp; ///< Implementation of node class. + + }; + + + /** + * @breif Parsing functions. + * Population given root node with deserialized data. + * + * @param root Root node to populate. + * @param filename Path of input file. + * @param stream Input stream. + * @param string String of input data. + * @param buffer Char array of input data. + * @param size Buffer size. + * + * @throw InternalException An internal error occurred. + * @throw ParsingException Invalid input YAML data. + * @throw OperationException If filename or buffer pointer is invalid. + * + */ + void Parse(Node & root, const char * filename); + void Parse(Node & root, std::iostream & stream); + void Parse(Node & root, const std::string & string); + void Parse(Node & root, const char * buffer, const size_t size); + + + /** + * @breif Serialization configuration structure, + * describing output behavior. + * + */ + struct SerializeConfig + { + + /** + * @breif Constructor. + * + * @param spaceIndentation Number of spaces per indentation. + * @param scalarMaxLength Maximum length of scalars. Serialized as folder scalars if exceeded. + * Ignored if equal to 0. + * @param sequenceMapNewline Put maps on a new line if parent node is a sequence. + * @param mapScalarNewline Put scalars on a new line if parent node is a map. + * + */ + SerializeConfig(const size_t spaceIndentation = 2, + const size_t scalarMaxLength = 64, + const bool sequenceMapNewline = false, + const bool mapScalarNewline = false); + + size_t SpaceIndentation; ///< Number of spaces per indentation. + size_t ScalarMaxLength; ///< Maximum length of scalars. Serialized as folder scalars if exceeded. + bool SequenceMapNewline; ///< Put maps on a new line if parent node is a sequence. + bool MapScalarNewline; ///< Put scalars on a new line if parent node is a map. + }; + + + /** + * @breif Serialization functions. + * + * @param root Root node to serialize. + * @param filename Path of output file. + * @param stream Output stream. + * @param string String of output data. + * @param config Serialization configurations. + * + * @throw InternalException An internal error occurred. + * @throw OperationException If filename or buffer pointer is invalid. + * If config is invalid. + * + */ + void Serialize(const Node & root, const char * filename, const SerializeConfig & config = {2, 64, false, false}); + void Serialize(const Node & root, std::iostream & stream, const SerializeConfig & config = {2, 64, false, false}); + void Serialize(const Node & root, std::string & string, const SerializeConfig & config = {2, 64, false, false}); + +} From 688e81cf68205fccef7a1dfd4dc82d898659e752 Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 17:51:40 +0800 Subject: [PATCH 2/8] Update --- cpp/include/gar/utils/yaml.h | 3 +-- cpp/src/graph_info.cc | 30 +++++++++++++----------------- cpp/src/yaml.cc | 1 - cpp/test/test_info.cc | 26 ++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/cpp/include/gar/utils/yaml.h b/cpp/include/gar/utils/yaml.h index 12dcd2ecc..069bd7765 100644 --- a/cpp/include/gar/utils/yaml.h +++ b/cpp/include/gar/utils/yaml.h @@ -34,8 +34,7 @@ class Yaml { public: using Node = ::Yaml::Node; - explicit Yaml(std::shared_ptr root_node) - : root_node_(root_node) {} + explicit Yaml(std::shared_ptr root_node) : root_node_(root_node) {} ~Yaml() = default; diff --git a/cpp/src/graph_info.cc b/cpp/src/graph_info.cc index 1ce08980e..6c9f8b7eb 100644 --- a/cpp/src/graph_info.cc +++ b/cpp/src/graph_info.cc @@ -43,24 +43,21 @@ Result VertexInfo::Load(std::shared_ptr yaml) { VertexInfo vertex_info(label, chunk_size, version, prefix); auto property_groups = yaml->operator[]("property_groups"); if (!property_groups.IsNone()) { // property_groups exist - for (auto it = property_groups.Begin(); - it != property_groups.End(); it++) { + for (auto it = property_groups.Begin(); it != property_groups.End(); it++) { std::string pg_prefix; auto& node = (*it).second; if (!node["prefix"].IsNone()) { pg_prefix = node["prefix"].As(); } - auto file_type = - StringToFileType(node["file_type"].As()); + auto file_type = StringToFileType(node["file_type"].As()); std::vector property_vec; auto& properties = node["properties"]; - for (auto iit = properties.Begin(); - iit != properties.End(); iit++) { + for (auto iit = properties.Begin(); iit != properties.End(); iit++) { Property property; auto& p_node = (*iit).second; property.name = p_node["name"].As(); - property.type = DataType::TypeNameToDataType( - p_node["data_type"].As()); + property.type = + DataType::TypeNameToDataType(p_node["data_type"].As()); property.is_primary = p_node["is_primary"].As(); property_vec.push_back(property); } @@ -139,14 +136,12 @@ Result EdgeInfo::Load(std::shared_ptr yaml) { auto adj_lists = yaml->operator[]("adj_lists"); if (adj_lists.IsSequence()) { - for (auto it = adj_lists.Begin(); it != adj_lists.End(); - it++) { + for (auto it = adj_lists.Begin(); it != adj_lists.End(); it++) { auto& node = (*it).second; auto ordered = node["ordered"].As(); auto aligned = node["aligned_by"].As(); auto adj_list_type = OrderedAlignedToAdjListType(ordered, aligned); - auto file_type = - StringToFileType(node["file_type"].As()); + auto file_type = StringToFileType(node["file_type"].As()); std::string adj_list_prefix; if (!node["prefix"].IsNone()) { adj_list_prefix = node["prefix"].As(); @@ -163,12 +158,12 @@ Result EdgeInfo::Load(std::shared_ptr yaml) { if (!pg_node["prefix"].IsNone()) { pg_prefix = pg_node["prefix"].As(); } - auto file_type = StringToFileType( - pg_node["file_type"].As()); + auto file_type = + StringToFileType(pg_node["file_type"].As()); auto properties = pg_node["properties"]; std::vector property_vec; - for (auto p_it = properties.Begin(); - p_it != properties.End(); p_it++) { + for (auto p_it = properties.Begin(); p_it != properties.End(); + p_it++) { auto& p_node = (*p_it).second; Property property; property.name = p_node["name"].As(); @@ -285,7 +280,8 @@ static Result ConstructGraphInfo( const auto& vertices = graph_meta->operator[]("vertices"); if (vertices.IsSequence()) { for (auto it = vertices.Begin(); it != vertices.End(); it++) { - std::string vertex_meta_file = no_url_path + (*it).second.As(); + std::string vertex_meta_file = + no_url_path + (*it).second.As(); GAR_ASSIGN_OR_RAISE(auto input, fs->ReadFileToValue(vertex_meta_file)); GAR_ASSIGN_OR_RAISE(auto vertex_meta, Yaml::Load(input)); diff --git a/cpp/src/yaml.cc b/cpp/src/yaml.cc index 86d840c09..6ca0e3e52 100644 --- a/cpp/src/yaml.cc +++ b/cpp/src/yaml.cc @@ -21,7 +21,6 @@ limitations under the License. #include "gar/utils/result.h" #include "gar/utils/yaml.h" - namespace GAR_NAMESPACE_INTERNAL { const Yaml::Node Yaml::operator[](const std::string& key) const { diff --git a/cpp/test/test_info.cc b/cpp/test/test_info.cc index c0c508104..326b31f05 100644 --- a/cpp/test/test_info.cc +++ b/cpp/test/test_info.cc @@ -41,6 +41,7 @@ TEST_CASE("test_graph_info") { GAR_NAMESPACE::VertexInfo vertex_info("test_vertex", 100, version, "test_vertex_prefix"); auto st = graph_info.AddVertex(vertex_info); + graph_info.AddVertexInfoPath("/tmp/test_vertex.vertex.yml"); REQUIRE(st.ok()); REQUIRE(graph_info.GetVertexInfos().size() == 1); auto maybe_vertex_info = graph_info.GetVertexInfo("test_vertex"); @@ -58,6 +59,7 @@ TEST_CASE("test_graph_info") { GAR_NAMESPACE::EdgeInfo edge_info(src_label, edge_label, dst_label, 1024, 100, 100, true, version); st = graph_info.AddEdge(edge_info); + graph_info.AddEdgeInfoPath("/tmp/test_edge.edge.yml"); REQUIRE(st.ok()); REQUIRE(graph_info.GetEdgeInfos().size() == 1); auto maybe_edge_info = @@ -109,6 +111,13 @@ TEST_CASE("test_vertex_info") { REQUIRE(v_info.AddPropertyGroup(pg2).IsInvalidOperation()); REQUIRE(v_info.GetPropertyGroups().size() == 1); + GAR_NAMESPACE::Property p2; + p2.name = "name"; + p2.type = GAR_NAMESPACE::DataType(GAR_NAMESPACE::Type::STRING); + p2.is_primary = false; + GAR_NAMESPACE::PropertyGroup pg3({p2}, GAR_NAMESPACE::FileType::CSV); + REQUIRE(v_info.AddPropertyGroup(pg3).ok()); + // test get property meta REQUIRE(v_info.GetPropertyType(p.name) == p.type); REQUIRE(v_info.IsPrimaryKey(p.name) == p.is_primary); @@ -353,6 +362,23 @@ TEST_CASE("test_graph_info_load_from_file") { REQUIRE(edge_infos.size() == 1); } +TEST_CASE("test_graph_info_load_ldbc") { + std::string root; + REQUIRE(GetTestResourceRoot(&root).ok()); + + std::string path = root + "/ldbc/ldbc.graph.yml"; + auto graph_info_result = GAR_NAMESPACE::GraphInfo::Load(path); + if (graph_info_result.has_error()) { + std::cout << graph_info_result.status().message() << std::endl; + } + auto graph_info = graph_info_result.value(); + REQUIRE(graph_info.GetName() == "ldbc"); + const auto& vertex_infos = graph_info.GetVertexInfos(); + const auto& edge_infos = graph_info.GetEdgeInfos(); + REQUIRE(vertex_infos.size() == 8); + REQUIRE(edge_infos.size() == 23); +} + /* TEST_CASE("test_graph_info_load_from_s3") { std::string path = From f4ca77ea6f34f77c30add5f2d0d5143f853c43e1 Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 19:44:58 +0800 Subject: [PATCH 3/8] Clean --- cpp/src/yaml.cc | 2 +- cpp/test/test_info.cc | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cpp/src/yaml.cc b/cpp/src/yaml.cc index 6ca0e3e52..bdae56edc 100644 --- a/cpp/src/yaml.cc +++ b/cpp/src/yaml.cc @@ -46,7 +46,7 @@ Result> Yaml::Load(std::iostream& input) { Result> Yaml::LoadFile(const std::string& file_name) { std::shared_ptr root_node = std::make_shared(); try { - ::Yaml::Parse(*root_node, file_name.c_str(), file_name.size()); + ::Yaml::Parse(*root_node, file_name.c_str()); } catch (::Yaml::Exception& e) { return Status::YamlError(e.what()); } return std::make_shared(root_node); } diff --git a/cpp/test/test_info.cc b/cpp/test/test_info.cc index 326b31f05..411a4f700 100644 --- a/cpp/test/test_info.cc +++ b/cpp/test/test_info.cc @@ -379,7 +379,21 @@ TEST_CASE("test_graph_info_load_ldbc") { REQUIRE(edge_infos.size() == 23); } -/* +TEST_CASE("test_vertex_info_xxx") { + std::string root; + REQUIRE(GetTestResourceRoot(&root).ok()); + + std::string vertex_meta_file = + root + "/ldbc_sample/parquet/" + "person.vertex.yml"; + auto vertex_meta = GAR_NAMESPACE::Yaml::LoadFile(vertex_meta_file).value(); + auto maybe_vertex_info = GAR_NAMESPACE::VertexInfo::Load(vertex_meta); + if (maybe_vertex_info.has_error()) { + std::cout << maybe_vertex_info.status().message() << std::endl; + } + auto vertex_info = maybe_vertex_info.value(); + REQUIRE(vertex_info.GetLabel() == "person"); +} + TEST_CASE("test_graph_info_load_from_s3") { std::string path = "s3://graphar/ldbc/ldbc.graph.yml" @@ -393,4 +407,3 @@ TEST_CASE("test_graph_info_load_from_s3") { REQUIRE(vertex_infos.size() == 8); REQUIRE(edge_infos.size() == 23); } -*/ From 2f369026d2320e96b4d280efbfbb0f4292aa8057 Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 19:47:52 +0800 Subject: [PATCH 4/8] Removed submodule yaml-cpp --- .gitmodules | 3 --- cpp/thirdparty/yaml-cpp | 1 - 2 files changed, 4 deletions(-) delete mode 160000 cpp/thirdparty/yaml-cpp diff --git a/.gitmodules b/.gitmodules index 90bbe0715..c12022f30 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "testing"] path = testing url = https://github.com/GraphScope/gar-test.git -[submodule "cpp/thirdparty/yaml-cpp"] - path = cpp/thirdparty/yaml-cpp - url = https://github.com/jbeder/yaml-cpp.git [submodule "cpp/thirdparty/Catch2"] path = cpp/thirdparty/Catch2 url = https://github.com/catchorg/Catch2.git diff --git a/cpp/thirdparty/yaml-cpp b/cpp/thirdparty/yaml-cpp deleted file mode 160000 index 1b50109f7..000000000 --- a/cpp/thirdparty/yaml-cpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b50109f7bea60bd382d8ea7befce3d2bd67da5f From 96baac646fcdf85deb6b8745ce6a8d6d16e24225 Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 19:50:12 +0800 Subject: [PATCH 5/8] Update --- cpp/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index fdf8afdd4..66f27b157 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -147,7 +147,6 @@ endmacro() # building or find third party library # ------------------------------------------------------------------------------ find_package(Threads REQUIRED) -# find_yaml_cpp() find_package(OpenSSL QUIET) if (APPLE) find_package(curl REQUIRED) @@ -189,9 +188,6 @@ macro(build_gar) target_include_directories(gar SYSTEM BEFORE PRIVATE ${GAR_ARROW_INCLUDE_DIR}) target_link_libraries(gar PRIVATE Threads::Threads ${CMAKE_DL_LIBS}) - # make sure `libyaml-cpp.a` built first - # add_dependencies(gar yaml-cpp) - # get_target_location(YAML_CPP_LIBRARY_LOCATION yaml-cpp) if(APPLE) target_link_libraries(gar PRIVATE -Wl,-force_load gar_arrow_static "${YAML_CPP_LIBRARY_LOCATION}" From d5b7b3c7afb2179f38b1c7936a0adb396daac3c4 Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 19:51:11 +0800 Subject: [PATCH 6/8] Fix --- cpp/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 66f27b157..0f6381aff 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -190,12 +190,10 @@ macro(build_gar) if(APPLE) target_link_libraries(gar PRIVATE -Wl,-force_load gar_arrow_static - "${YAML_CPP_LIBRARY_LOCATION}" "${GAR_PARQUET_STATIC_LIB}" "${GAR_ARROW_BUNDLED_DEPS_STATIC_LIB}") else() target_link_libraries(gar PRIVATE -Wl,--exclude-libs,ALL -Wl,--whole-archive gar_arrow_static - "${YAML_CPP_LIBRARY_LOCATION}" "${GAR_PARQUET_STATIC_LIB}" "${GAR_ARROW_BUNDLED_DEPS_STATIC_LIB}" -Wl,--no-whole-archive) endif() From ca4806e01346d4836792794cdc488228c060c81b Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 20:27:06 +0800 Subject: [PATCH 7/8] Clean --- cpp/include/gar/utils/yaml.h | 9 ++++----- cpp/src/graph_info.cc | 4 ++-- cpp/src/yaml.cc | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cpp/include/gar/utils/yaml.h b/cpp/include/gar/utils/yaml.h index 069bd7765..7719b1397 100644 --- a/cpp/include/gar/utils/yaml.h +++ b/cpp/include/gar/utils/yaml.h @@ -32,13 +32,12 @@ namespace GAR_NAMESPACE_INTERNAL { /** A wrapper of ::Yaml::Node to provide functions to parse yaml. */ class Yaml { public: - using Node = ::Yaml::Node; - - explicit Yaml(std::shared_ptr root_node) : root_node_(root_node) {} + explicit Yaml(std::shared_ptr<::Yaml::Node> root_node) + : root_node_(root_node) {} ~Yaml() = default; - const Node operator[](const std::string& key) const; + const ::Yaml::Node operator[](const std::string& key) const; /** * Loads the input string as Yaml instance. @@ -62,7 +61,7 @@ class Yaml { static Result> LoadFile(const std::string& file_name); private: - std::shared_ptr root_node_; + std::shared_ptr<::Yaml::Node> root_node_; }; } // namespace GAR_NAMESPACE_INTERNAL diff --git a/cpp/src/graph_info.cc b/cpp/src/graph_info.cc index 6c9f8b7eb..3feda3330 100644 --- a/cpp/src/graph_info.cc +++ b/cpp/src/graph_info.cc @@ -77,13 +77,13 @@ Result VertexInfo::Dump() const noexcept { node["chunk_size"] = std::to_string(chunk_size_); node["prefix"] = prefix_; for (const auto& pg : property_groups_) { - Yaml::Node pg_node; + ::Yaml::Node pg_node; if (!pg.GetPrefix().empty()) { pg_node["prefix"] = pg.GetPrefix(); } pg_node["file_type"] = FileTypeToString(pg.GetFileType()); for (auto& p : pg.GetProperties()) { - Yaml::Node p_node; + ::Yaml::Node p_node; p_node["name"] = p.name; p_node["data_type"] = p.type.ToTypeName(); p_node["is_primary"] = p.is_primary ? "true" : "false"; diff --git a/cpp/src/yaml.cc b/cpp/src/yaml.cc index bdae56edc..30b50587c 100644 --- a/cpp/src/yaml.cc +++ b/cpp/src/yaml.cc @@ -23,12 +23,12 @@ limitations under the License. namespace GAR_NAMESPACE_INTERNAL { -const Yaml::Node Yaml::operator[](const std::string& key) const { +const ::Yaml::Node Yaml::operator[](const std::string& key) const { return root_node_->operator[](key); } Result> Yaml::Load(const std::string& input) { - std::shared_ptr root_node = std::make_shared(); + std::shared_ptr<::Yaml::Node> root_node = std::make_shared<::Yaml::Node>(); try { ::Yaml::Parse(*root_node, input); } catch (::Yaml::Exception& e) { return Status::YamlError(e.what()); } @@ -36,7 +36,7 @@ Result> Yaml::Load(const std::string& input) { } Result> Yaml::Load(std::iostream& input) { - std::shared_ptr root_node = std::make_shared(); + std::shared_ptr<::Yaml::Node> root_node = std::make_shared<::Yaml::Node>(); try { ::Yaml::Parse(*root_node, input); } catch (::Yaml::Exception& e) { return Status::YamlError(e.what()); } @@ -44,7 +44,7 @@ Result> Yaml::Load(std::iostream& input) { } Result> Yaml::LoadFile(const std::string& file_name) { - std::shared_ptr root_node = std::make_shared(); + std::shared_ptr<::Yaml::Node> root_node = std::make_shared<::Yaml::Node>(); try { ::Yaml::Parse(*root_node, file_name.c_str()); } catch (::Yaml::Exception& e) { return Status::YamlError(e.what()); } From 09690747699727c28d13994c969a6fe0dddf5266 Mon Sep 17 00:00:00 2001 From: acezen Date: Thu, 20 Apr 2023 20:30:42 +0800 Subject: [PATCH 8/8] Clean --- cpp/test/test_info.cc | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/cpp/test/test_info.cc b/cpp/test/test_info.cc index 411a4f700..046d73b41 100644 --- a/cpp/test/test_info.cc +++ b/cpp/test/test_info.cc @@ -77,7 +77,6 @@ TEST_CASE("test_graph_info") { // TODO(@acezen): test dump std::string save_path(std::tmpnam(nullptr)); - std::cout << "save_path: " << save_path << std::endl; REQUIRE(graph_info.Save(save_path).ok()); REQUIRE(std::filesystem::exists(save_path)); @@ -153,7 +152,6 @@ TEST_CASE("test_vertex_info") { // test save std::string save_path(std::tmpnam(nullptr)); - std::cout << "save_path: " << save_path << std::endl; REQUIRE(v_info.Save(save_path).ok()); REQUIRE(std::filesystem::exists(save_path)); @@ -305,7 +303,6 @@ TEST_CASE("test_edge_info") { // test save std::string save_path(std::tmpnam(nullptr)); - std::cout << "save_path: " << save_path << std::endl; REQUIRE(edge_info.Save(save_path).ok()); REQUIRE(std::filesystem::exists(save_path)); @@ -362,38 +359,6 @@ TEST_CASE("test_graph_info_load_from_file") { REQUIRE(edge_infos.size() == 1); } -TEST_CASE("test_graph_info_load_ldbc") { - std::string root; - REQUIRE(GetTestResourceRoot(&root).ok()); - - std::string path = root + "/ldbc/ldbc.graph.yml"; - auto graph_info_result = GAR_NAMESPACE::GraphInfo::Load(path); - if (graph_info_result.has_error()) { - std::cout << graph_info_result.status().message() << std::endl; - } - auto graph_info = graph_info_result.value(); - REQUIRE(graph_info.GetName() == "ldbc"); - const auto& vertex_infos = graph_info.GetVertexInfos(); - const auto& edge_infos = graph_info.GetEdgeInfos(); - REQUIRE(vertex_infos.size() == 8); - REQUIRE(edge_infos.size() == 23); -} - -TEST_CASE("test_vertex_info_xxx") { - std::string root; - REQUIRE(GetTestResourceRoot(&root).ok()); - - std::string vertex_meta_file = - root + "/ldbc_sample/parquet/" + "person.vertex.yml"; - auto vertex_meta = GAR_NAMESPACE::Yaml::LoadFile(vertex_meta_file).value(); - auto maybe_vertex_info = GAR_NAMESPACE::VertexInfo::Load(vertex_meta); - if (maybe_vertex_info.has_error()) { - std::cout << maybe_vertex_info.status().message() << std::endl; - } - auto vertex_info = maybe_vertex_info.value(); - REQUIRE(vertex_info.GetLabel() == "person"); -} - TEST_CASE("test_graph_info_load_from_s3") { std::string path = "s3://graphar/ldbc/ldbc.graph.yml"