From b6f8c9e242e19811b99e4a8c0eba0f583f6b56d9 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Fri, 19 Jun 2020 07:34:04 -0500 Subject: [PATCH] [ox] Add HashMap to serializaton handlers --- deps/ox/src/ox/mc/read.cpp | 4 ++- deps/ox/src/ox/mc/read.hpp | 32 ++++++++++++++++++++ deps/ox/src/ox/mc/test/tests.cpp | 9 +++++- deps/ox/src/ox/mc/write.cpp | 5 +++- deps/ox/src/ox/mc/write.hpp | 47 ++++++++++++++++++++++++++++-- deps/ox/src/ox/model/descwrite.hpp | 8 +++++ deps/ox/src/ox/oc/read.hpp | 19 +++++++++++- deps/ox/src/ox/oc/test/tests.cpp | 8 ++++- deps/ox/src/ox/oc/write.hpp | 18 ++++++++++++ deps/ox/src/ox/std/hashmap.hpp | 26 +++++++++++++++++ deps/ox/src/ox/std/string.cpp | 2 +- deps/ox/src/ox/std/string.hpp | 2 +- deps/ox/src/ox/std/vector.hpp | 8 ++--- 13 files changed, 175 insertions(+), 13 deletions(-) diff --git a/deps/ox/src/ox/mc/read.cpp b/deps/ox/src/ox/mc/read.cpp index ba7349e0..569b3ec7 100644 --- a/deps/ox/src/ox/mc/read.cpp +++ b/deps/ox/src/ox/mc/read.cpp @@ -26,7 +26,9 @@ MetalClawReader::~MetalClawReader() noexcept { if (m_parent) { m_parent->m_buffIt += m_buffIt; } - oxAssert(m_field == m_fields, "MetalClawReader: incorrect fields number given"); + if (m_field != m_fields) { + oxTrace("ox::mc::MetalClawReader::error") << "MetalClawReader: incorrect fields number given"; + } } Error MetalClawReader::field(const char*, int8_t *val) { diff --git a/deps/ox/src/ox/mc/read.hpp b/deps/ox/src/ox/mc/read.hpp index 336c8415..51220c29 100644 --- a/deps/ox/src/ox/mc/read.hpp +++ b/deps/ox/src/ox/mc/read.hpp @@ -55,6 +55,10 @@ class MetalClawReader { template [[nodiscard]] Error field(const char*, T *val, std::size_t len); + // map handler + template + [[nodiscard]] Error field(const char*, HashMap *val); + // array handler, with callback to allow handling individual elements template [[nodiscard]] Error field(const char*, Handler handler); @@ -191,6 +195,34 @@ Error MetalClawReader::field(const char *name, T *val, std::size_t valLen) { return OxError(0); } +template +Error MetalClawReader::field(const char*, HashMap *val) { + if (m_unionIdx == -1 || m_unionIdx == m_field) { + if (m_fieldPresence.get(m_field)) { + // read the length + if (m_buffIt >= m_buffLen) { + return OxError(MC_BUFFENDED); + } + std::size_t bytesRead = 0; + auto len = mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead); + m_buffIt += bytesRead; + oxReturnError(len.error); + + // read the list + auto reader = child(""); + reader.setTypeInfo("List", len.value); + for (std::size_t i = 0; i < len.value; i++) { + auto keyLen = reader.stringLength(nullptr); + auto wkey = ox_malloca(keyLen + 1, char, 0); + oxReturnError(reader.field("", SerStr(wkey.get(), keyLen))); + oxReturnError(reader.field("", &val->at(wkey.get()))); + } + } + } + ++m_field; + return OxError(0); +} + template Error MetalClawReader::field(const char*, Handler handler) { if (m_unionIdx == -1 || m_unionIdx == m_field) { diff --git a/deps/ox/src/ox/mc/test/tests.cpp b/deps/ox/src/ox/mc/test/tests.cpp index 2c06c11d..a91e53dd 100644 --- a/deps/ox/src/ox/mc/test/tests.cpp +++ b/deps/ox/src/ox/mc/test/tests.cpp @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "ox/std/hashmap.hpp" #undef NDEBUG #include @@ -36,7 +37,7 @@ struct TestStructNest { struct TestStruct { static constexpr auto TypeName = "TestStruct"; - static constexpr auto Fields = 16; + static constexpr auto Fields = 17; bool Bool = false; int32_t Int = 0; int32_t Int1 = 0; @@ -51,6 +52,7 @@ struct TestStruct { char *CString = nullptr; ox::BString<32> String = ""; uint32_t List[4] = {0, 0, 0, 0}; + ox::HashMap Map; TestStructNest EmptyStruct; TestStructNest Struct; @@ -95,6 +97,7 @@ ox::Error model(T *io, TestStruct *obj) { oxReturnError(io->field("CString", ox::SerStr(&obj->CString))); oxReturnError(io->field("String", &obj->String)); oxReturnError(io->field("List", obj->List, 4)); + oxReturnError(io->field("Map", &obj->Map)); oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct)); oxReturnError(io->field("Struct", &obj->Struct)); return OxError(0); @@ -133,6 +136,8 @@ std::map tests = { testIn.List[1] = 2; testIn.List[2] = 3; testIn.List[3] = 4; + testIn.Map["asdf"] = 93; + testIn.Map["aoeu"] = 94; testIn.Struct.Bool = false; testIn.Struct.Int = 300; testIn.Struct.String = "Test String 2"; @@ -158,6 +163,8 @@ std::map tests = { 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.Map["asdf"] == testOut.Map["asdf"], "Map[\"asdf\"] value mismatch"); + oxAssert(testIn.Map["aoeu"] == testOut.Map["aoeu"], "Map[\"aoeu\"] 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"); diff --git a/deps/ox/src/ox/mc/write.cpp b/deps/ox/src/ox/mc/write.cpp index 93915e68..5a1ba0ad 100644 --- a/deps/ox/src/ox/mc/write.cpp +++ b/deps/ox/src/ox/mc/write.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "write.hpp" @@ -22,7 +23,9 @@ MetalClawWriter::MetalClawWriter(uint8_t *buff, std::size_t buffLen, int unionId } MetalClawWriter::~MetalClawWriter() noexcept { - oxAssert(m_field == m_fields, "MetalClawWriter: incorrect fields number given"); + if (m_field != m_fields) { + oxTrace("ox::mc::MetalClawWriter::error") << "MetalClawReader: incorrect fields number given"; + } } Error MetalClawWriter::field(const char*, int8_t *val) noexcept { diff --git a/deps/ox/src/ox/mc/write.hpp b/deps/ox/src/ox/mc/write.hpp index 6d5913fb..60e23e1f 100644 --- a/deps/ox/src/ox/mc/write.hpp +++ b/deps/ox/src/ox/mc/write.hpp @@ -19,6 +19,7 @@ #include "intops.hpp" #include "err.hpp" +#include "ox/std/hashmap.hpp" #include "presenceindicator.hpp" #include "types.hpp" @@ -56,7 +57,10 @@ class MetalClawWriter { [[nodiscard]] Error field(const char*, T *val, std::size_t len); template - [[nodiscard]] Error field(const char*, ox::Vector *val); + [[nodiscard]] Error field(const char*, Vector *val); + + template + [[nodiscard]] Error field(const char*, HashMap *val); template [[nodiscard]] Error field(const char*, ox::BString *val) noexcept; @@ -153,10 +157,49 @@ Error MetalClawWriter::field(const char*, T *val, std::size_t len) { } template -Error MetalClawWriter::field(const char*, ox::Vector *val) { +Error MetalClawWriter::field(const char*, Vector *val) { return field(nullptr, val->data(), val->size()); } +template +[[nodiscard]] Error MetalClawWriter::field(const char*, HashMap *val) { + auto &keys = val->keys(); + auto len = keys.size(); + bool fieldSet = false; + + if (len && (m_unionIdx == -1 || m_unionIdx == m_field)) { + // write the length + const auto arrLen = mc::encodeInteger(len); + if (m_buffIt + arrLen.length < m_buffLen) { + ox_memcpy(&m_buff[m_buffIt], arrLen.data, arrLen.length); + m_buffIt += arrLen.length; + } else { + return OxError(MC_BUFFENDED); + } + + MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt); + // double len for both key and value + writer.setTypeInfo("Map", len * 2); + + // write the array + for (std::size_t i = 0; i < len; i++) { + auto &key = keys[i]; + const auto keyLen = ox_strlen(key); + auto wkey = static_cast(ox_alloca(keyLen)); + memcpy(wkey, key.c_str(), keyLen + 1); + oxReturnError(writer.field("", SerStr(&wkey, keyLen))); + oxReturnError(writer.field("", &(*val)[key])); + } + + m_buffIt += writer.m_buffIt; + fieldSet = true; + } + + oxReturnError(m_fieldPresence.set(m_field, fieldSet)); + m_field++; + return OxError(0); +} + template Error MetalClawWriter::appendInteger(I val) noexcept { bool fieldSet = false; diff --git a/deps/ox/src/ox/model/descwrite.hpp b/deps/ox/src/ox/model/descwrite.hpp index 4fb9984f..d664a064 100644 --- a/deps/ox/src/ox/model/descwrite.hpp +++ b/deps/ox/src/ox/model/descwrite.hpp @@ -132,6 +132,9 @@ class TypeDescWriter { template DescriptorType *type(Vector *val, bool *alreadyExisted); + template + DescriptorType *type(HashMap *val, bool *alreadyExisted); + template DescriptorType *type(UnionView val, bool *alreadyExisted); @@ -204,6 +207,11 @@ DescriptorType *TypeDescWriter::type(Vector *val, bool *alreadyExisted) { return type(val->data(), alreadyExisted); } +template +DescriptorType *TypeDescWriter::type(HashMap*, bool *alreadyExisted) { + return type(static_cast(nullptr), alreadyExisted); +} + template DescriptorType *TypeDescWriter::type(UnionView val, bool *alreadyExisted) { return type(val.get(), alreadyExisted); diff --git a/deps/ox/src/ox/oc/read.hpp b/deps/ox/src/ox/oc/read.hpp index 28460540..3403fab4 100644 --- a/deps/ox/src/ox/oc/read.hpp +++ b/deps/ox/src/ox/oc/read.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +52,10 @@ class OrganicClawReader { [[nodiscard]] Error field(const char *key, T *val, std::size_t len); template - [[nodiscard]] Error field(const char *key, ox::Vector *val); + [[nodiscard]] Error field(const char *key, Vector *val); + + template + [[nodiscard]] Error field(const char*, HashMap *val); template [[nodiscard]] Error field(const char *key, T *val); @@ -158,6 +162,19 @@ Error OrganicClawReader::field(const char *key, ox::Vector *val) { return field(key, val->data(), val->size()); } +template +[[nodiscard]] Error OrganicClawReader::field(const char *key, HashMap *val) { + const auto &srcVal = value(key); + auto keys = srcVal.getMemberNames(); + auto srcSize = srcVal.size(); + OrganicClawReader r(srcVal); + for (decltype(srcSize) i = 0; i < srcSize; ++i) { + auto k = keys[i].c_str(); + oxReturnError(r.field(k, &val->at(k))); + } + return OxError(0); +} + template Error readOC(const char *json, std::size_t jsonSize, T *val) noexcept { // OrganicClawReader constructor can throw, but readOC should return its errors. diff --git a/deps/ox/src/ox/oc/test/tests.cpp b/deps/ox/src/ox/oc/test/tests.cpp index 4c2f420b..ceeacb5a 100644 --- a/deps/ox/src/ox/oc/test/tests.cpp +++ b/deps/ox/src/ox/oc/test/tests.cpp @@ -45,6 +45,7 @@ struct TestStruct { char *CString = nullptr; ox::BString<32> String = ""; uint32_t List[4] = {0, 0, 0, 0}; + ox::HashMap Map; TestStructNest EmptyStruct; TestStructNest Struct; @@ -74,7 +75,7 @@ ox::Error model(T *io, TestStructNest *obj) { template ox::Error model(T *io, TestStruct *obj) { - io->setTypeInfo("TestStruct", 16); + io->setTypeInfo("TestStruct", 17); oxReturnError(io->field("Bool", &obj->Bool)); oxReturnError(io->field("Int", &obj->Int)); oxReturnError(io->field("Int1", &obj->Int1)); @@ -89,6 +90,7 @@ ox::Error model(T *io, TestStruct *obj) { oxReturnError(io->field("CString", ox::SerStr(&obj->CString))); oxReturnError(io->field("String", &obj->String)); oxReturnError(io->field("List", obj->List, 4)); + oxReturnError(io->field("Map", &obj->Map)); oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct)); oxReturnError(io->field("Struct", &obj->Struct)); return OxError(0); @@ -119,6 +121,8 @@ std::map tests = { testIn.List[1] = 2; testIn.List[2] = 3; testIn.List[3] = 4; + testIn.Map["asdf"] = 93; + testIn.Map["aoeu"] = 94; testIn.Struct.Bool = false; testIn.Struct.Int = 300; testIn.Struct.String = "Test String 2"; @@ -146,6 +150,8 @@ std::map tests = { 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.Map["asdf"] == testOut->Map["asdf"], "Map[\"asdf\"] value mismatch"); + oxAssert(testIn.Map["aoeu"] == testOut->Map["aoeu"], "Map[\"aoeu\"] 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"); diff --git a/deps/ox/src/ox/oc/write.hpp b/deps/ox/src/ox/oc/write.hpp index 365392f9..a07d46a2 100644 --- a/deps/ox/src/ox/oc/write.hpp +++ b/deps/ox/src/ox/oc/write.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -53,6 +54,9 @@ class OrganicClawWriter { template [[nodiscard]] Error field(const char*, ox::Vector *val); + template + [[nodiscard]] Error field(const char*, HashMap *val); + template [[nodiscard]] Error field(const char*, ox::BString *val); @@ -129,6 +133,20 @@ Error OrganicClawWriter::field(const char *key, ox::Vector *val) { return field(key, val->data(), val->size()); } +template +[[nodiscard]] Error OrganicClawWriter::field(const char *key, ox::HashMap *val) { + if (targetValid()) { + auto &keys = val->keys(); + OrganicClawWriter w; + for (std::size_t i = 0; i < keys.size(); ++i) { + auto k = keys[i].c_str(); + oxReturnError(w.field(k, &val->at(k))); + } + value(key) = w.m_json; + } + ++m_fieldIt; + return OxError(0); +} template ValErr> writeOC(T *val) { diff --git a/deps/ox/src/ox/std/hashmap.hpp b/deps/ox/src/ox/std/hashmap.hpp index 97c35a19..ec98a91d 100644 --- a/deps/ox/src/ox/std/hashmap.hpp +++ b/deps/ox/src/ox/std/hashmap.hpp @@ -16,6 +16,9 @@ namespace ox { template class HashMap { + using key_t = K; + using value_t = T; + private: struct Pair { K key = {}; @@ -31,6 +34,8 @@ class HashMap { ~HashMap(); + bool operator==(const HashMap &other) const; + HashMap &operator=(HashMap &other); /** @@ -47,6 +52,8 @@ class HashMap { std::size_t size() const noexcept; + const Vector &keys() const noexcept; + private: void expand(); @@ -78,6 +85,20 @@ HashMap::~HashMap() { } } +template +bool HashMap::operator==(const HashMap &other) const { + if (m_keys != other.m_keys) { + return false; + } + for (int i = 0; i < m_keys.size(); i++) { + auto &k = m_keys[i]; + if (at(k) != other.at(k)) { + return false; + } + } + return true; +} + template HashMap &HashMap::operator=(HashMap &other) { this->~HashMap(); @@ -115,6 +136,11 @@ std::size_t HashMap::size() const noexcept { return m_keys.size(); } +template +const Vector &HashMap::keys() const noexcept { + return m_keys; +} + template void HashMap::expand() { Vector r; diff --git a/deps/ox/src/ox/std/string.cpp b/deps/ox/src/ox/std/string.cpp index 5a09ed65..81277671 100644 --- a/deps/ox/src/ox/std/string.cpp +++ b/deps/ox/src/ox/std/string.cpp @@ -39,7 +39,7 @@ String::String(const char *str, std::size_t size) noexcept { m_buff[size] = 0; } -String::String(String &other) noexcept { +String::String(const 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 7a32f4f3..b0d932fa 100644 --- a/deps/ox/src/ox/std/string.hpp +++ b/deps/ox/src/ox/std/string.hpp @@ -29,7 +29,7 @@ class String { String(const char *str, std::size_t size) noexcept; - String(String&) noexcept; + String(const String&) noexcept; String(String&&) noexcept; diff --git a/deps/ox/src/ox/std/vector.hpp b/deps/ox/src/ox/std/vector.hpp index cfb6c487..be7081f0 100644 --- a/deps/ox/src/ox/std/vector.hpp +++ b/deps/ox/src/ox/std/vector.hpp @@ -27,13 +27,13 @@ class Vector { explicit Vector(std::size_t size) noexcept; - Vector(Vector &other) noexcept; + Vector(const Vector &other) noexcept; Vector(Vector &&other) noexcept; ~Vector() noexcept; - Vector &operator=(Vector &other) noexcept; + Vector &operator=(const Vector &other) noexcept; Vector &operator=(Vector &&other) noexcept; @@ -97,7 +97,7 @@ Vector::Vector(std::size_t size) noexcept { } template -Vector::Vector(Vector &other) noexcept { +Vector::Vector(const Vector &other) noexcept { m_size = other.m_size; m_cap = other.m_cap; m_items = reinterpret_cast(new AllocAlias[m_cap]); @@ -128,7 +128,7 @@ Vector::~Vector() noexcept { } template -Vector &Vector::operator=(Vector &other) noexcept { +Vector &Vector::operator=(const Vector &other) noexcept { this->~Vector(); m_size = other.m_size; m_cap = other.m_cap;