From 5461fb2f08d9aaa1ac2f83ca1bb4a8673b1be7c3 Mon Sep 17 00:00:00 2001 From: Leonid Stryzhevskyi Date: Wed, 24 Apr 2024 04:24:37 +0300 Subject: [PATCH] Introduce data::Tree::Node --- src/CMakeLists.txt | 2 + src/oatpp/data/Tree.cpp | 287 ++++++++++++++++++++++++++++++++ src/oatpp/data/Tree.hpp | 207 +++++++++++++++++++++++ test/CMakeLists.txt | 2 + test/oatpp/AllTestsMain.cpp | 11 ++ test/oatpp/data/TreeTest.cpp | 159 ++++++++++++++++++ test/oatpp/data/TreeTest.hpp | 40 +++++ test/oatpp/json/BooleanTest.cpp | 2 +- 8 files changed, 709 insertions(+), 1 deletion(-) create mode 100644 src/oatpp/data/Tree.cpp create mode 100644 src/oatpp/data/Tree.hpp create mode 100644 test/oatpp/data/TreeTest.cpp create mode 100644 test/oatpp/data/TreeTest.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a5b8305..88e33088 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,6 +62,8 @@ add_library(oatpp oatpp/concurrency/Utils.hpp oatpp/data/Bundle.cpp oatpp/data/Bundle.hpp + oatpp/data/Tree.cpp + oatpp/data/Tree.hpp oatpp/data/buffer/FIFOBuffer.cpp oatpp/data/buffer/FIFOBuffer.hpp oatpp/data/buffer/IOBuffer.cpp diff --git a/src/oatpp/data/Tree.cpp b/src/oatpp/data/Tree.cpp new file mode 100644 index 00000000..cc46f0aa --- /dev/null +++ b/src/oatpp/data/Tree.cpp @@ -0,0 +1,287 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#include "Tree.hpp" + +namespace oatpp { namespace data { + +Tree::Node::Node() + : m_type(Type::NULL_VALUE) + , m_data(0) +{} + +Tree::Node::Node(const Node& other) + : Node() +{ + setCopy(other); +} + +Tree::Node::Node(Node&& other) noexcept + : m_type(other.m_type) + , m_data(other.m_data) +{ + other.m_type = Type::NULL_VALUE; + other.m_data = 0; +} + +Tree::Node::~Node() { + deleteValueObject(); +} + +Tree::Node& Tree::Node::operator = (const Node& other) { + setCopy(other); + return *this; +} + +Tree::Node& Tree::Node::operator = (Node&& other) noexcept { + setMove(std::forward(other)); + return *this; +} + +void Tree::Node::deleteValueObject() { + + switch (m_type) { + + case Type::NULL_VALUE: + + case Type::INTEGER: + case Type::FLOAT: + + case Type::BOOL: + + case Type::INT_8: + case Type::UINT_8: + case Type::INT_16: + case Type::UINT_16: + case Type::INT_32: + case Type::UINT_32: + case Type::INT_64: + case Type::UINT_64: + + case Type::FLOAT_32: + case Type::FLOAT_64: + break; + + case Type::STRING: { + auto data = reinterpret_cast(m_data); + delete data; + break; + } + case Type::VECTOR: { + auto data = reinterpret_cast *>(m_data); + delete data; + break; + } + case Type::MAP: { + auto data = reinterpret_cast> *>(m_data); + delete data; + break; + } + + default: + // DO-NOTHING + break; + + } +} + +Tree::Node::Type Tree::Node::getType() const { + return m_type; +} + +void Tree::Node::setCopy(const Node& other) { + + deleteValueObject(); + m_type = other.m_type; + + switch (other.m_type) { + + case Type::NULL_VALUE: + break; + + case Type::INTEGER: + case Type::FLOAT: + + case Type::BOOL: + case Type::INT_8: + case Type::UINT_8: + case Type::INT_16: + case Type::UINT_16: + case Type::INT_32: + case Type::UINT_32: + case Type::INT_64: + case Type::UINT_64: + case Type::FLOAT_32: + case Type::FLOAT_64: + { + m_data = other.m_data; + break; + } + + case Type::STRING: { + auto otherData = reinterpret_cast(other.m_data); + if(otherData == nullptr) { + throw std::runtime_error("[oatpp::data::Tree::Node::setCopy()]: other.data is null, other.type is 'STRING'"); + } + auto ptr = new oatpp::String(*otherData); + m_data = reinterpret_cast(ptr); + break; + } + case Type::VECTOR: { + auto otherData = reinterpret_cast *>(other.m_data); + if(otherData == nullptr) { + throw std::runtime_error("[oatpp::data::Tree::Node::setCopy()]: other.data is null, other.type is 'VECTOR'"); + } + auto ptr = new std::vector(*otherData); + m_data = reinterpret_cast(ptr); + break; + } + case Type::MAP: { + auto otherData = reinterpret_cast> *>(other.m_data); + if(otherData == nullptr) { + throw std::runtime_error("[oatpp::data::Tree::Node::setCopy()]: other.data is null, other.type is 'MAP'"); + } + auto ptr = new std::vector>(*otherData); + m_data = reinterpret_cast(ptr); + break; + } + + default: + m_data = other.m_data; + break; + + } + +} + +void Tree::Node::setMove(Node&& other) { + deleteValueObject(); + m_type = other.m_type; + m_data = other.m_data; + other.m_type = Type::NULL_VALUE; + other.m_data = 0; +} + +void Tree::Node::setNull() { + deleteValueObject(); + m_type = Type::NULL_VALUE; + m_data = 0; +} + +void Tree::Node::setInteger(v_int64 value) { + deleteValueObject(); + m_type = Type::INTEGER; + std::memcpy (&m_data, &value, sizeof(v_int64)); +} + +void Tree::Node::setFloat(v_float64 value) { + deleteValueObject(); + m_type = Type::FLOAT; + std::memcpy (&m_data, &value, sizeof(v_float64)); +} + +void Tree::Node::setString(const oatpp::String& value) { + deleteValueObject(); + m_type = Type::STRING; + auto data = new oatpp::String(value); + m_data = reinterpret_cast(data); +} + +void Tree::Node::setVector(const std::vector& value) { + deleteValueObject(); + m_type = Type::VECTOR; + auto data = new std::vector(value); + m_data = reinterpret_cast(data); +} + +void Tree::Node::setMap(const std::vector>& value) { + deleteValueObject(); + m_type = Type::MAP; + auto data = new std::vector>(value); + m_data = reinterpret_cast(data); +} + +bool Tree::Node::isNull() const { + return m_type == Type::NULL_VALUE; +} + +v_int64 Tree::Node::getInteger() const { + if(m_type != Type::INTEGER) { + throw std::runtime_error("[oatpp::data::Tree::Node::getInteger()]: NOT an arbitrary INTEGER."); + } + v_int64 result; + std::memcpy (&result, &m_data, sizeof(v_float64)); + return result; +} + +v_float64 Tree::Node::getFloat() const { + if(m_type != Type::FLOAT) { + throw std::runtime_error("[oatpp::data::Tree::Node::getInteger()]: NOT an arbitrary FLOAT."); + } + v_float64 result; + std::memcpy (&result, &m_data, sizeof(v_float64)); + return result; +} + +const oatpp::String& Tree::Node::getString() const { + if(m_type != Type::STRING) { + throw std::runtime_error("[oatpp::data::Tree::Node::getInteger()]: NOT a STRING."); + } + auto data = reinterpret_cast(m_data); + return *data; +} + +const std::vector& Tree::Node::getVector() const { + if(m_type != Type::VECTOR) { + throw std::runtime_error("[oatpp::data::Tree::Node::getInteger()]: NOT a VECTOR."); + } + auto data = reinterpret_cast*>(m_data); + return *data; +} + +const std::vector>& Tree::Node::getMap() const { + if(m_type != Type::MAP) { + throw std::runtime_error("[oatpp::data::Tree::Node::getInteger()]: NOT a MAP."); + } + auto data = reinterpret_cast>*>(m_data); + return *data; +} + +std::vector& Tree::Node::getVector() { + if(m_type != Type::VECTOR) { + throw std::runtime_error("[oatpp::data::Tree::Node::getInteger()]: NOT a VECTOR."); + } + auto data = reinterpret_cast*>(m_data); + return *data; +} + +std::vector>& Tree::Node::getMap() { + if(m_type != Type::MAP) { + throw std::runtime_error("[oatpp::data::Tree::Node::getInteger()]: NOT a MAP."); + } + auto data = reinterpret_cast>*>(m_data); + return *data; +} + +}} diff --git a/src/oatpp/data/Tree.hpp b/src/oatpp/data/Tree.hpp new file mode 100644 index 00000000..6a949b79 --- /dev/null +++ b/src/oatpp/data/Tree.hpp @@ -0,0 +1,207 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#ifndef oatpp_data_Tree_hpp +#define oatpp_data_Tree_hpp + +#include "oatpp/Types.hpp" + +namespace oatpp { namespace data { + +class Tree { +public: + + class Node { + public: + + enum class Type : v_int32 { + + NULL_VALUE = 0, + + INTEGER = 1, + FLOAT = 2, + + BOOL = 3, + + INT_8 = 4, + UINT_8 = 5, + INT_16 = 6, + UINT_16 = 7, + INT_32 = 8, + UINT_32 = 9, + INT_64 = 10, + UINT_64 = 11, + + FLOAT_32 = 12, + FLOAT_64 = 13, + + STRING = 14, + + VECTOR = 15, + MAP = 16 + + }; + + template + struct NodePrimitiveType { + }; + + private: + typedef v_uint64 LARGEST_TYPE; + private: + void deleteValueObject(); + private: + Type m_type; + LARGEST_TYPE m_data; + public: + + Node(); + Node(const Node& other); + Node(Node&& other) noexcept; + + ~Node(); + + Node& operator = (const Node& other); + Node& operator = (Node&& other) noexcept; + + Type getType() const; + + void setCopy(const Node& other); + void setMove(Node&& other); + + template + void setValue(T value) { + deleteValueObject(); + m_type = NodePrimitiveType::type; + m_data = 0; + std::memcpy (&m_data, &value, sizeof(T)); + } + + template + T getValue() const { + if(m_type != NodePrimitiveType::type) { + throw std::runtime_error(std::string("[oatpp::data::Tree::Node::getValue()]: NOT a ") + NodePrimitiveType::name); + } + T result; + std::memcpy (&result, &m_data, sizeof(T)); + return result; + } + + void setNull(); + + void setInteger(v_int64 value); + void setFloat(v_float64 value); + + void setString(const oatpp::String& value); + void setVector(const std::vector& value); + void setMap(const std::vector>& value); + + bool isNull() const; + + v_int64 getInteger() const; + v_float64 getFloat() const; + + const oatpp::String& getString() const; + + const std::vector& getVector() const; + const std::vector>& getMap() const; + + std::vector& getVector(); + std::vector>& getMap(); + + }; + +public: + +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::BOOL; + static constexpr const char* name = "BOOL"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::INT_8; + static constexpr const char* name = "INT_8"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::UINT_8; + static constexpr const char* name = "UINT_8"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::INT_16; + static constexpr const char* name = "INT_16"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::UINT_16; + static constexpr const char* name = "UINT_16"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::INT_32; + static constexpr const char* name = "INT_32"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::UINT_32; + static constexpr const char* name = "UINT_32"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::INT_64; + static constexpr const char* name = "INT_64"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::UINT_64; + static constexpr const char* name = "UINT_64"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::FLOAT_32; + static constexpr const char* name = "FLOAT_32"; +}; + +template<> +struct Tree::Node::NodePrimitiveType { + static constexpr Type type = Type::FLOAT_64; + static constexpr const char* name = "FLOAT_64"; +}; + +}} + +#endif //oatpp_data_Tree_hpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 59137fbf..6ac3bc00 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -46,6 +46,8 @@ add_executable(oatppAllTests oatpp/data/share/StringTemplateTest.hpp oatpp/data/stream/BufferStreamTest.cpp oatpp/data/stream/BufferStreamTest.hpp + oatpp/data/TreeTest.cpp + oatpp/data/TreeTest.hpp oatpp/encoding/Base64Test.cpp oatpp/encoding/Base64Test.hpp oatpp/encoding/UnicodeTest.cpp diff --git a/test/oatpp/AllTestsMain.cpp b/test/oatpp/AllTestsMain.cpp index 78c17d62..06dc615c 100644 --- a/test/oatpp/AllTestsMain.cpp +++ b/test/oatpp/AllTestsMain.cpp @@ -53,6 +53,7 @@ #include "oatpp/data/resource/InMemoryDataTest.hpp" #include "oatpp/data/stream/BufferStreamTest.hpp" +#include "oatpp/data/TreeTest.hpp" #include "oatpp/data/share/LazyStringMapTest.hpp" #include "oatpp/data/share/StringTemplateTest.hpp" #include "oatpp/data/share/MemoryLabelTest.hpp" @@ -73,8 +74,12 @@ namespace { void runTests() { + /* oatpp::Environment::printCompilationConfig(); + OATPP_LOGD("Tests", "oatpp::String size=%lu", sizeof(oatpp::String)) + OATPP_LOGD("Tests", "std::string size=%lu", sizeof(std::string)) + OATPP_LOGD("Tests", "coroutine handle size=%lu", sizeof(oatpp::async::CoroutineHandle)) OATPP_LOGD("Tests", "coroutine size=%lu", sizeof(oatpp::async::AbstractCoroutine)) OATPP_LOGD("Tests", "action size=%lu", sizeof(oatpp::async::Action)) @@ -97,7 +102,11 @@ void runTests() { OATPP_RUN_TEST(oatpp::data::buffer::ProcessorTest); OATPP_RUN_TEST(oatpp::data::stream::BufferStreamTest); + */ + OATPP_RUN_TEST(oatpp::data::TreeTest); + + /* OATPP_RUN_TEST(oatpp::data::mapping::type::ObjectWrapperTest); OATPP_RUN_TEST(oatpp::data::mapping::type::TypeTest); @@ -224,6 +233,8 @@ void runTests() { } + */ + } } diff --git a/test/oatpp/data/TreeTest.cpp b/test/oatpp/data/TreeTest.cpp new file mode 100644 index 00000000..ec2a2467 --- /dev/null +++ b/test/oatpp/data/TreeTest.cpp @@ -0,0 +1,159 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#include "TreeTest.hpp" + +#include "oatpp/data/Tree.hpp" +#include "oatpp/utils/Conversion.hpp" + +#include + +namespace oatpp { namespace data { + +namespace { + +template +void testNodeValue(T value) { + + Tree::Node node; + + node.setValue(value); + auto v = node.getValue(); + OATPP_ASSERT(v == value && "value check") + + node.setValue(std::numeric_limits::min()); + auto min = node.getValue(); + OATPP_ASSERT(min == std::numeric_limits::min() && "min check") + + node.setValue(std::numeric_limits::max()); + auto max = node.getValue(); + OATPP_ASSERT(max == std::numeric_limits::max() && "max check") + +} + +} + +void TreeTest::onRun() { + + testNodeValue(true); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + testNodeValue(16); + + { + Tree::Node node; + oatpp::String original = "Hello World!"; + node.setString(original); + auto stored = node.getString(); + OATPP_ASSERT(stored == original) + OATPP_ASSERT(stored.get() == original.get()) + } + + { + Tree::Node node1; + Tree::Node node2; + + node1.setString("Hello World!"); + node2 = node1; + + OATPP_ASSERT(node1.getString() == "Hello World!") + OATPP_ASSERT(node1.getType() == Tree::Node::Type::STRING) + + OATPP_ASSERT(node2.getString() == "Hello World!") + OATPP_ASSERT(node2.getType() == Tree::Node::Type::STRING) + } + + { + Tree::Node node1; + Tree::Node node2; + + node1.setString("Hello World!"); + node2 = std::move(node1); + + OATPP_ASSERT(node1.isNull()) + OATPP_ASSERT(node2.getString() == "Hello World!") + OATPP_ASSERT(node2.getType() == Tree::Node::Type::STRING) + } + + { + std::vector originalVector(10); + for(v_uint32 i = 0; i < 10; i ++) { + originalVector.at(i).setValue(i); + } + + Tree::Node node; + node.setVector(originalVector); + + auto& vector = node.getVector(); + + OATPP_ASSERT(vector.size() == originalVector.size()) + + for(v_uint32 i = 0; i < originalVector.size(); i ++) { + OATPP_ASSERT(originalVector.at(i).getValue() == vector.at(i).getValue()) + } + + originalVector.resize(5); + OATPP_ASSERT(vector.size() == 10) + + vector.at(0).setString("Hello"); + + OATPP_ASSERT(vector.at(0).getString() == "Hello") + OATPP_ASSERT(originalVector.at(0).getValue() == 0) + + } + + { + std::vector> originalMap(10); + for(v_uint32 i = 0; i < 10; i ++) { + originalMap.at(i).first = "node_" + utils::Conversion::int32ToStr(i); + originalMap.at(i).second.setValue(i); + } + + Tree::Node node; + node.setMap(originalMap); + + auto& map = node.getMap(); + + OATPP_ASSERT(map.size() == originalMap.size()) + + for(v_uint32 i = 0; i < originalMap.size(); i ++) { + OATPP_ASSERT(originalMap.at(i).first = map.at(i).first) + OATPP_ASSERT(originalMap.at(i).second.getValue() == map.at(i).second.getValue()) + } + + originalMap.resize(5); + OATPP_ASSERT(map.size() == 10) + + } + +} + +}} diff --git a/test/oatpp/data/TreeTest.hpp b/test/oatpp/data/TreeTest.hpp new file mode 100644 index 00000000..d82e52c5 --- /dev/null +++ b/test/oatpp/data/TreeTest.hpp @@ -0,0 +1,40 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#ifndef oatpp_json_TreeTest_hpp +#define oatpp_json_TreeTest_hpp + +#include "oatpp-test/UnitTest.hpp" + +namespace oatpp { namespace data { + +class TreeTest : public oatpp::test::UnitTest { +public: + TreeTest() : UnitTest("TEST[oatpp::data::TreeTest]") {} + void onRun() override; +}; + +}} + +#endif /* oatpp_json_TreeTest_hpp */ diff --git a/test/oatpp/json/BooleanTest.cpp b/test/oatpp/json/BooleanTest.cpp index a1e2641b..585bff39 100644 --- a/test/oatpp/json/BooleanTest.cpp +++ b/test/oatpp/json/BooleanTest.cpp @@ -60,4 +60,4 @@ void BooleanTest::onRun() { } } -}} \ No newline at end of file +}}