[ox] Add HashMap<String, T> to serializaton handlers

This commit is contained in:
Gary Talent 2020-06-19 07:34:04 -05:00
parent 0698353fbf
commit b6f8c9e242
13 changed files with 175 additions and 13 deletions

View File

@ -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) {

View File

@ -55,6 +55,10 @@ class MetalClawReader {
template<typename T>
[[nodiscard]] Error field(const char*, T *val, std::size_t len);
// map handler
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
// array handler, with callback to allow handling individual elements
template<typename T, typename Handler>
[[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<typename T>
Error MetalClawReader::field(const char*, HashMap<String, T> *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<ArrayLength>(&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<typename T, typename Handler>
Error MetalClawReader::field(const char*, Handler handler) {
if (m_unionIdx == -1 || m_unionIdx == m_field) {

View File

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "ox/std/hashmap.hpp"
#undef NDEBUG
#include <assert.h>
@ -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<ox::String, int> 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<std::string, ox::Error(*)()> 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<std::string, ox::Error(*)()> 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");

View File

@ -9,6 +9,7 @@
#include <ox/std/assert.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/trace.hpp>
#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 {

View File

@ -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<typename T>
[[nodiscard]] Error field(const char*, ox::Vector<T> *val);
[[nodiscard]] Error field(const char*, Vector<T> *val);
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
template<std::size_t L>
[[nodiscard]] Error field(const char*, ox::BString<L> *val) noexcept;
@ -153,10 +157,49 @@ Error MetalClawWriter::field(const char*, T *val, std::size_t len) {
}
template<typename T>
Error MetalClawWriter::field(const char*, ox::Vector<T> *val) {
Error MetalClawWriter::field(const char*, Vector<T> *val) {
return field(nullptr, val->data(), val->size());
}
template<typename T>
[[nodiscard]] Error MetalClawWriter::field(const char*, HashMap<String, T> *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<char*>(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<typename I>
Error MetalClawWriter::appendInteger(I val) noexcept {
bool fieldSet = false;

View File

@ -132,6 +132,9 @@ class TypeDescWriter {
template<typename T>
DescriptorType *type(Vector<T> *val, bool *alreadyExisted);
template<typename T>
DescriptorType *type(HashMap<String, T> *val, bool *alreadyExisted);
template<typename U>
DescriptorType *type(UnionView<U> val, bool *alreadyExisted);
@ -204,6 +207,11 @@ DescriptorType *TypeDescWriter::type(Vector<T> *val, bool *alreadyExisted) {
return type(val->data(), alreadyExisted);
}
template<typename T>
DescriptorType *TypeDescWriter::type(HashMap<String, T>*, bool *alreadyExisted) {
return type(static_cast<T*>(nullptr), alreadyExisted);
}
template<typename U>
DescriptorType *TypeDescWriter::type(UnionView<U> val, bool *alreadyExisted) {
return type(val.get(), alreadyExisted);

View File

@ -12,6 +12,7 @@
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/string.hpp>
#include <ox/std/vector.hpp>
@ -51,7 +52,10 @@ class OrganicClawReader {
[[nodiscard]] Error field(const char *key, T *val, std::size_t len);
template<typename T>
[[nodiscard]] Error field(const char *key, ox::Vector<T> *val);
[[nodiscard]] Error field(const char *key, Vector<T> *val);
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
template<typename T>
[[nodiscard]] Error field(const char *key, T *val);
@ -158,6 +162,19 @@ Error OrganicClawReader::field(const char *key, ox::Vector<T> *val) {
return field(key, val->data(), val->size());
}
template<typename T>
[[nodiscard]] Error OrganicClawReader::field(const char *key, HashMap<String, T> *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<typename T>
Error readOC(const char *json, std::size_t jsonSize, T *val) noexcept {
// OrganicClawReader constructor can throw, but readOC should return its errors.

View File

@ -45,6 +45,7 @@ struct TestStruct {
char *CString = nullptr;
ox::BString<32> String = "";
uint32_t List[4] = {0, 0, 0, 0};
ox::HashMap<ox::String, int> Map;
TestStructNest EmptyStruct;
TestStructNest Struct;
@ -74,7 +75,7 @@ ox::Error model(T *io, TestStructNest *obj) {
template<typename T>
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<std::string, ox::Error(*)()> 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<std::string, ox::Error(*)()> 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");

View File

@ -12,6 +12,7 @@
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/string.hpp>
#include <ox/std/vector.hpp>
@ -53,6 +54,9 @@ class OrganicClawWriter {
template<typename T>
[[nodiscard]] Error field(const char*, ox::Vector<T> *val);
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
template<std::size_t L>
[[nodiscard]] Error field(const char*, ox::BString<L> *val);
@ -129,6 +133,20 @@ Error OrganicClawWriter::field(const char *key, ox::Vector<T> *val) {
return field(key, val->data(), val->size());
}
template<typename T>
[[nodiscard]] Error OrganicClawWriter::field(const char *key, ox::HashMap<String, T> *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<typename T>
ValErr<Vector<char>> writeOC(T *val) {

View File

@ -16,6 +16,9 @@ namespace ox {
template<typename K, typename T>
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<K> &keys() const noexcept;
private:
void expand();
@ -78,6 +85,20 @@ HashMap<K, T>::~HashMap() {
}
}
template<typename K, typename T>
bool HashMap<K, T>::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<typename K, typename T>
HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> &other) {
this->~HashMap<K, T>();
@ -115,6 +136,11 @@ std::size_t HashMap<K, T>::size() const noexcept {
return m_keys.size();
}
template<typename K, typename T>
const Vector<K> &HashMap<K, T>::keys() const noexcept {
return m_keys;
}
template<typename K, typename T>
void HashMap<K, T>::expand() {
Vector<Pair*> r;

View File

@ -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;
}

View File

@ -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;

View File

@ -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<T>::Vector(std::size_t size) noexcept {
}
template<typename T>
Vector<T>::Vector(Vector<T> &other) noexcept {
Vector<T>::Vector(const Vector<T> &other) noexcept {
m_size = other.m_size;
m_cap = other.m_cap;
m_items = reinterpret_cast<T*>(new AllocAlias<T>[m_cap]);
@ -128,7 +128,7 @@ Vector<T>::~Vector() noexcept {
}
template<typename T>
Vector<T> &Vector<T>::operator=(Vector<T> &other) noexcept {
Vector<T> &Vector<T>::operator=(const Vector<T> &other) noexcept {
this->~Vector<T>();
m_size = other.m_size;
m_cap = other.m_cap;