From e2952ec8c1d73d438a814bdcaa55786e84b79bf4 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Wed, 6 May 2020 20:38:06 -0500 Subject: [PATCH] [ox/claw] Add Claw --- deps/ox/src/ox/CMakeLists.txt | 1 + deps/ox/src/ox/claw/CMakeLists.txt | 13 ++ deps/ox/src/ox/claw/claw.hpp | 12 ++ deps/ox/src/ox/claw/format.hpp | 19 +++ deps/ox/src/ox/claw/read.cpp | 54 +++++++ deps/ox/src/ox/claw/read.hpp | 54 +++++++ deps/ox/src/ox/claw/test/CMakeLists.txt | 14 ++ deps/ox/src/ox/claw/test/tests.cpp | 206 ++++++++++++++++++++++++ deps/ox/src/ox/claw/write.hpp | 90 +++++++++++ deps/ox/src/ox/mc/write.hpp | 10 ++ deps/ox/src/ox/oc/test/tests.cpp | 6 +- deps/ox/src/ox/oc/write.hpp | 9 +- deps/ox/src/ox/std/string.cpp | 12 +- deps/ox/src/ox/std/string.hpp | 2 + 14 files changed, 493 insertions(+), 9 deletions(-) create mode 100644 deps/ox/src/ox/claw/CMakeLists.txt create mode 100644 deps/ox/src/ox/claw/claw.hpp create mode 100644 deps/ox/src/ox/claw/format.hpp create mode 100644 deps/ox/src/ox/claw/read.cpp create mode 100644 deps/ox/src/ox/claw/read.hpp create mode 100644 deps/ox/src/ox/claw/test/CMakeLists.txt create mode 100644 deps/ox/src/ox/claw/test/tests.cpp create mode 100644 deps/ox/src/ox/claw/write.hpp diff --git a/deps/ox/src/ox/CMakeLists.txt b/deps/ox/src/ox/CMakeLists.txt index 28ab4e51..c2434fe7 100644 --- a/deps/ox/src/ox/CMakeLists.txt +++ b/deps/ox/src/ox/CMakeLists.txt @@ -2,6 +2,7 @@ if(${OX_USE_STDLIB}) add_subdirectory(clargs) add_subdirectory(oc) endif() +add_subdirectory(claw) add_subdirectory(fs) add_subdirectory(mc) add_subdirectory(ptrarith) diff --git a/deps/ox/src/ox/claw/CMakeLists.txt b/deps/ox/src/ox/claw/CMakeLists.txt new file mode 100644 index 00000000..65519a9c --- /dev/null +++ b/deps/ox/src/ox/claw/CMakeLists.txt @@ -0,0 +1,13 @@ + +add_library( + OxClaw + read.cpp +) + +target_link_libraries( + OxClaw + OxMetalClaw + OxOrganicClaw +) + +add_subdirectory(test) diff --git a/deps/ox/src/ox/claw/claw.hpp b/deps/ox/src/ox/claw/claw.hpp new file mode 100644 index 00000000..dbf41917 --- /dev/null +++ b/deps/ox/src/ox/claw/claw.hpp @@ -0,0 +1,12 @@ +/* + * Copyright 2016 - 2020 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "read.hpp" +#include "write.hpp" diff --git a/deps/ox/src/ox/claw/format.hpp b/deps/ox/src/ox/claw/format.hpp new file mode 100644 index 00000000..9d8df25b --- /dev/null +++ b/deps/ox/src/ox/claw/format.hpp @@ -0,0 +1,19 @@ +/* + * Copyright 2016 - 2020 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +namespace ox { + +enum class ClawFormat: int { + None, + Metal, + Organic, +}; + +} diff --git a/deps/ox/src/ox/claw/read.cpp b/deps/ox/src/ox/claw/read.cpp new file mode 100644 index 00000000..cd3bd533 --- /dev/null +++ b/deps/ox/src/ox/claw/read.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2016 - 2020 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "read.hpp" + +namespace ox::detail { + +ValErr readHeader(const char *buff, std::size_t buffLen) noexcept { + const auto s1End = ox_strchr(buff, ';', buffLen); + if (!s1End) { + return OxError(1); + } + const auto s1Size = s1End - buff; + String fmt(buff, s1Size); + buff += s1Size + 1; + buffLen -= s1Size + 1; + + const auto s2End = ox_strchr(buff, ';', buffLen); + if (!s2End) { + return OxError(1); + } + const auto s2Size = s2End - buff; + String typeName(buff, s2Size); + buff += s2Size + 1; + buffLen -= s2Size + 1; + + const auto s3End = ox_strchr(buff, ';', buffLen); + if (!s3End) { + return OxError(1); + } + const auto s3Size = s3End - buff; + String versionStr(buff, s3Size); + buff += s3Size + 1; + + ClawHeader hdr; + if (fmt == "M1") { + hdr.fmt = ClawFormat::Metal; + } else if (fmt == "O1") { + hdr.fmt = ClawFormat::Organic; + } else { + return OxError(1); + } + hdr.typeName = typeName; + hdr.typeVersion = ox_atoi(versionStr.c_str()); + hdr.data = buff; + return hdr; +} + +} diff --git a/deps/ox/src/ox/claw/read.hpp b/deps/ox/src/ox/claw/read.hpp new file mode 100644 index 00000000..35fe7989 --- /dev/null +++ b/deps/ox/src/ox/claw/read.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 2016 - 2020 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include +#include + +#include "format.hpp" + +namespace ox { + +namespace detail { + +struct ClawHeader { + String typeName; + int typeVersion = -1; + ClawFormat fmt = ClawFormat::None; + const char *data = nullptr; +}; + +ValErr readHeader(const char *buff, std::size_t buffLen) noexcept; + +} + +template +Error readClaw(char *buff, std::size_t buffLen, T *val) { + auto header = detail::readHeader(buff, buffLen); + oxReturnError(header); + switch (header.value.fmt) { + case ClawFormat::Metal: + { + MetalClawReader reader(bit_cast(header.value.data), buffLen); + return model(&reader, val); + } + case ClawFormat::Organic: + { + OrganicClawReader reader(bit_cast(header.value.data), buffLen); + return model(&reader, val); + } + case ClawFormat::None: + return OxError(1); + } + return OxError(1); +} + +} diff --git a/deps/ox/src/ox/claw/test/CMakeLists.txt b/deps/ox/src/ox/claw/test/CMakeLists.txt new file mode 100644 index 00000000..db45b4f6 --- /dev/null +++ b/deps/ox/src/ox/claw/test/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable( + ClawTest + tests.cpp +) + +target_link_libraries( + ClawTest + OxClaw +) + +add_test("Test\\ ClawTest\\ ClawHeaderReader" ClawTest ClawHeaderReader) +add_test("Test\\ ClawTest\\ ClawHeaderReader2" ClawTest ClawHeaderReader2) +add_test("Test\\ ClawTest\\ ClawWriter" ClawTest ClawWriter) +add_test("Test\\ ClawTest\\ ClawReader" ClawTest ClawReader) diff --git a/deps/ox/src/ox/claw/test/tests.cpp b/deps/ox/src/ox/claw/test/tests.cpp new file mode 100644 index 00000000..93376056 --- /dev/null +++ b/deps/ox/src/ox/claw/test/tests.cpp @@ -0,0 +1,206 @@ +/* + * Copyright 2016 - 2020 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#undef NDEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +union TestUnion { + static constexpr auto TypeName = "TestUnion"; + static constexpr auto Fields = 3; + bool Bool; + uint32_t Int = 5; + char String[32]; +}; + +struct TestStructNest { + static constexpr auto TypeName = "TestStructNest"; + static constexpr auto Fields = 3; + bool Bool = false; + uint32_t Int = 0; + ox::BString<32> String = ""; +}; + +struct TestStruct { + static constexpr auto TypeName = "TestStruct"; + static constexpr auto Fields = 16; + bool Bool = false; + int32_t Int = 0; + int32_t Int1 = 0; + int32_t Int2 = 0; + int32_t Int3 = 0; + int32_t Int4 = 0; + int32_t Int5 = 0; + int32_t Int6 = 0; + int32_t Int7 = 0; + int32_t Int8 = 0; + TestUnion Union; + char *CString = nullptr; + ox::BString<32> String = ""; + uint32_t List[4] = {0, 0, 0, 0}; + TestStructNest EmptyStruct; + TestStructNest Struct; + + ~TestStruct() { + delete[] CString; + } + +}; + +template +ox::Error model(T *io, TestUnion *obj) { + io->template setTypeInfo(); + oxReturnError(io->field("Bool", &obj->Bool)); + oxReturnError(io->field("Int", &obj->Int)); + oxReturnError(io->field("String", ox::SerStr(obj->String))); + return OxError(0); +} + +template +ox::Error model(T *io, TestStructNest *obj) { + io->template setTypeInfo(); + oxReturnError(io->field("Bool", &obj->Bool)); + oxReturnError(io->field("Int", &obj->Int)); + oxReturnError(io->field("String", &obj->String)); + return OxError(0); +} + +template +ox::Error model(T *io, TestStruct *obj) { + io->template setTypeInfo(); + oxReturnError(io->field("Bool", &obj->Bool)); + oxReturnError(io->field("Int", &obj->Int)); + oxReturnError(io->field("Int1", &obj->Int1)); + oxReturnError(io->field("Int2", &obj->Int2)); + oxReturnError(io->field("Int3", &obj->Int3)); + oxReturnError(io->field("Int4", &obj->Int4)); + oxReturnError(io->field("Int5", &obj->Int5)); + oxReturnError(io->field("Int6", &obj->Int6)); + oxReturnError(io->field("Int7", &obj->Int7)); + oxReturnError(io->field("Int8", &obj->Int8)); + oxReturnError(io->field("Union", ox::UnionView{&obj->Union, 1})); + oxReturnError(io->field("CString", ox::SerStr(&obj->CString))); + oxReturnError(io->field("String", &obj->String)); + oxReturnError(io->field("List", obj->List, 4)); + oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct)); + oxReturnError(io->field("Struct", &obj->Struct)); + return OxError(0); +} + +std::map tests = { + { + { + "ClawHeaderReader", + [] { + ox::String hdr = "O1;com.drinkingtea.ox.claw.test.Header;2;"; + auto [ch, err] = ox::detail::readHeader(hdr.c_str(), hdr.len() + 1); + oxAssert(err, "Error parsing header"); + oxAssert(ch.fmt == ox::ClawFormat::Organic, "Format wrong"); + oxAssert(ch.typeName == "com.drinkingtea.ox.claw.test.Header", "Type name wrong"); + oxAssert(ch.typeVersion == 2, "Type version wrong"); + return OxError(0); + } + }, + { + "ClawHeaderReader2", + [] { + ox::String hdr = "M1;com.drinkingtea.ox.claw.test.Header2;3;"; + auto [ch, err] = ox::detail::readHeader(hdr.c_str(), hdr.len() + 1); + oxAssert(err, "Error parsing header"); + oxAssert(ch.fmt == ox::ClawFormat::Metal, "Format wrong"); + oxAssert(ch.typeName == "com.drinkingtea.ox.claw.test.Header2", "Type name wrong"); + oxAssert(ch.typeVersion == 3, "Type version wrong"); + return OxError(0); + } + }, + { + "ClawWriter", + [] { + // This test doesn't confirm much, but it does show that the writer + // doesn't segfault + TestStruct ts; + oxReturnError(ox::writeClaw(&ts, ox::ClawFormat::Metal)); + return OxError(0); + } + }, + { + "ClawReader", + [] { + TestStruct testIn, testOut; + testIn.Bool = true; + testIn.Int = 42; + testIn.Union.Int = 42; + testIn.String = "Test String 1"; + testIn.CString = new char[ox_strlen("c-string") + 1]; + ox_strcpy(testIn.CString, "c-string"); + testIn.List[0] = 1; + testIn.List[1] = 2; + testIn.List[2] = 3; + testIn.List[3] = 4; + testIn.Struct.Bool = false; + testIn.Struct.Int = 300; + testIn.Struct.String = "Test String 2"; + + auto [buff, err] = ox::writeClaw(&testIn, ox::ClawFormat::Metal); + oxAssert(err, "writeMC failed"); + oxAssert(ox::readClaw(buff.data(), buff.size(), &testOut), "writeMC failed"); + //std::cout << testIn.Union.Int << "|" << testOut.Union.Int << "|\n"; + + oxAssert(testIn.Bool == testOut.Bool, "Bool value mismatch"); + oxAssert(testIn.Int == testOut.Int, "Int value mismatch"); + oxAssert(testIn.Int1 == testOut.Int1, "Int1 value mismatch"); + oxAssert(testIn.Int2 == testOut.Int2, "Int2 value mismatch"); + oxAssert(testIn.Int3 == testOut.Int3, "Int3 value mismatch"); + oxAssert(testIn.Int4 == testOut.Int4, "Int4 value mismatch"); + oxAssert(testIn.Int5 == testOut.Int5, "Int5 value mismatch"); + oxAssert(testIn.Int6 == testOut.Int6, "Int6 value mismatch"); + oxAssert(testIn.Int7 == testOut.Int7, "Int7 value mismatch"); + oxAssert(testIn.Int8 == testOut.Int8, "Int8 value mismatch"); + oxAssert(ox_strcmp(testIn.CString, testOut.CString) == 0, "CString value mismatch"); + oxAssert(testIn.Union.Int == testOut.Union.Int, "Union.Int value mismatch"); + oxAssert(testIn.String == testOut.String, "String value mismatch"); + oxAssert(testIn.List[0] == testOut.List[0], "List[0] value mismatch"); + oxAssert(testIn.List[1] == testOut.List[1], "List[1] value mismatch"); + oxAssert(testIn.List[2] == testOut.List[2], "List[2] value mismatch"); + oxAssert(testIn.List[3] == testOut.List[3], "List[3] value mismatch"); + oxAssert(testIn.EmptyStruct.Bool == testOut.EmptyStruct.Bool, "EmptyStruct.Bool value mismatch"); + oxAssert(testIn.EmptyStruct.Int == testOut.EmptyStruct.Int, "EmptyStruct.Int value mismatch"); + oxAssert(testIn.EmptyStruct.String == testOut.EmptyStruct.String, "EmptyStruct.String value mismatch"); + oxAssert(testIn.Struct.Int == testOut.Struct.Int, "Struct.Int value mismatch"); + oxAssert(testIn.Struct.String == testOut.Struct.String, "Struct.String value mismatch"); + oxAssert(testIn.Struct.Bool == testOut.Struct.Bool, "Struct.Bool value mismatch"); + + return OxError(0); + } + }, + } +}; + +int main(int argc, const char **args) { + int retval = -1; + if (argc > 0) { + auto testName = args[1]; + if (tests.find(testName) != tests.end()) { + retval = tests[testName](); + } else { + retval = 1; + } + } + return retval; +} diff --git a/deps/ox/src/ox/claw/write.hpp b/deps/ox/src/ox/claw/write.hpp new file mode 100644 index 00000000..5c1968bb --- /dev/null +++ b/deps/ox/src/ox/claw/write.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2016 - 2020 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include +#include + +#include "format.hpp" + +namespace ox { + +namespace detail { + +struct TypeInfoCatcher { + + const char *name = nullptr; + + template + constexpr void setTypeInfo(const char *name = T::TypeName, int = T::Fields) noexcept { + this->name = name; + } + + constexpr ox::Error field(...) noexcept { + return OxError(0); + } + +}; + +template +[[nodiscard]] constexpr int getTypeVersion(int version = T::TypeVersion) noexcept { + return version; +} + +[[nodiscard]] constexpr int getTypeVersion(...) noexcept { + return -1; +} + +template +constexpr const char *getTypeName(T *t) noexcept { + TypeInfoCatcher tnc; + model(&tnc, t); + return tnc.name; +} + +template +ValErr writeClawHeader(T *t, ClawFormat fmt) noexcept { + String out; + switch (fmt) { + case ClawFormat::Metal: + out += "M1;"; + break; + case ClawFormat::Organic: + out += "O1;"; + break; + default: + return OxError(1); + } + out += detail::getTypeName(t); + out += ";"; + const auto tn = detail::getTypeVersion(t); + if (tn > -1) { + out += tn; + } + out += ";"; + return out; +} + +} + +template +constexpr ValErr> writeClaw(T *t, ClawFormat fmt) { + auto [header, headerErr] = detail::writeClawHeader(t, fmt); + oxReturnError(headerErr); + const auto [data, dataErr] = fmt == ClawFormat::Metal ? writeMC(t) : writeOC(t); + oxReturnError(dataErr); + ox::Vector out(header.len() + data.size()); + memcpy(out.data(), header.data(), header.len()); + memcpy(out.data() + header.len(), data.data(), data.size()); + return out; +} + +} diff --git a/deps/ox/src/ox/mc/write.hpp b/deps/ox/src/ox/mc/write.hpp index f1228644..903a6461 100644 --- a/deps/ox/src/ox/mc/write.hpp +++ b/deps/ox/src/ox/mc/write.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "intops.hpp" @@ -183,6 +184,15 @@ void MetalClawWriter::setTypeInfo(const char*, int fields) { memset(m_buff, 0, m_buffIt); } +template +ValErr> writeMC(T *val) { + Vector buff(10 * units::MB); + MetalClawWriter writer(bit_cast(buff.data()), buff.size()); + oxReturnError(model(&writer, val)); + buff.resize(writer.size()); + return buff; +} + template Error writeMC(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) { MetalClawWriter writer(buff, buffLen); diff --git a/deps/ox/src/ox/oc/test/tests.cpp b/deps/ox/src/ox/oc/test/tests.cpp index b868c44e..4c2f420b 100644 --- a/deps/ox/src/ox/oc/test/tests.cpp +++ b/deps/ox/src/ox/oc/test/tests.cpp @@ -125,8 +125,8 @@ std::map tests = { auto [oc, writeErr] = ox::writeOC(&testIn); oxAssert(writeErr, "writeOC failed"); - std::cout << oc.c_str() << '\n'; - auto [testOut, readErr] = ox::readOC(oc.c_str()); + std::cout << oc.data() << '\n'; + auto [testOut, readErr] = ox::readOC(oc.data()); oxAssert(readErr, "readOC failed"); oxAssert(testIn.Bool == testOut->Bool, "Bool value mismatch"); @@ -176,7 +176,7 @@ std::map tests = { oxAssert(ocErr, "Data generation failed"); auto type = ox::buildTypeDef(&testIn); oxAssert(type.error, "Descriptor write failed"); - ox::walkModel(type.value, ox::bit_cast(oc.c_str()), oc.len() + 1, + ox::walkModel(type.value, ox::bit_cast(oc.data()), oc.size(), [](const ox::Vector&, const ox::Vector&, const ox::DescriptorField &f, ox::OrganicClawReader *rdr) -> ox::Error { //std::cout << f.fieldName.c_str() << '\n'; auto fieldName = f.fieldName.c_str(); diff --git a/deps/ox/src/ox/oc/write.hpp b/deps/ox/src/ox/oc/write.hpp index fa1cef29..b954a617 100644 --- a/deps/ox/src/ox/oc/write.hpp +++ b/deps/ox/src/ox/oc/write.hpp @@ -20,7 +20,7 @@ namespace ox { class OrganicClawWriter { template - friend ValErr writeOC(T *val); + friend ValErr> writeOC(T *val); protected: Json::Value m_json; @@ -131,11 +131,14 @@ Error OrganicClawWriter::field(const char *key, ox::Vector *val) { template -ValErr writeOC(T *val) { +ValErr> writeOC(T *val) { OrganicClawWriter writer; oxReturnError(model(&writer, val)); Json::StreamWriterBuilder jsonBuilder; - return String(Json::writeString(jsonBuilder, writer.m_json).c_str()); + auto str = Json::writeString(jsonBuilder, writer.m_json); + Vector buff(str.size() + 1); + memcpy(buff.data(), str.c_str(), str.size() + 1); + return buff; } } diff --git a/deps/ox/src/ox/std/string.cpp b/deps/ox/src/ox/std/string.cpp index f1b25c3e..5a09ed65 100644 --- a/deps/ox/src/ox/std/string.cpp +++ b/deps/ox/src/ox/std/string.cpp @@ -13,7 +13,7 @@ namespace ox { String::String() noexcept { if (m_buff.size()) { - m_buff[0] = 0;; + m_buff[0] = 0; } else { m_buff.push_back(0); } @@ -21,18 +21,24 @@ String::String() noexcept { String::String(std::size_t cap) noexcept { m_buff.resize(cap + 1); - m_buff[0] = 0;; + m_buff[0] = 0; } String::String(const char *str) noexcept { if (m_buff.size()) { - m_buff[0] = 0;; + m_buff[0] = 0; } else { m_buff.push_back(0); } *this = str; } +String::String(const char *str, std::size_t size) noexcept { + m_buff.resize(size + 1); + memcpy(m_buff.data(), str, size); + m_buff[size] = 0; +} + String::String(String &other) noexcept { m_buff = other.m_buff; } diff --git a/deps/ox/src/ox/std/string.hpp b/deps/ox/src/ox/std/string.hpp index 740a885b..7a32f4f3 100644 --- a/deps/ox/src/ox/std/string.hpp +++ b/deps/ox/src/ox/std/string.hpp @@ -27,6 +27,8 @@ class String { String(const char *str) noexcept; + String(const char *str, std::size_t size) noexcept; + String(String&) noexcept; String(String&&) noexcept;