diff --git a/src/ox/mc/CMakeLists.txt b/src/ox/mc/CMakeLists.txt index 99c58798b..0312e35f9 100644 --- a/src/ox/mc/CMakeLists.txt +++ b/src/ox/mc/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.8) add_library( OxMetalClaw + presencemask.cpp read.cpp write.cpp ) @@ -15,6 +16,8 @@ set_property( install( FILES + err.hpp + presencemask.hpp read.hpp write.hpp DESTINATION diff --git a/src/ox/mc/err.hpp b/src/ox/mc/err.hpp new file mode 100644 index 000000000..195103872 --- /dev/null +++ b/src/ox/mc/err.hpp @@ -0,0 +1,19 @@ +/* + * Copyright 2015 - 2017 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 { + MC_PRESENCEMASKOUTBOUNDS = 1, + MC_BUFFENDED = 2, + MC_OUTBUFFENDED = 4 +}; + +} diff --git a/src/ox/mc/mc.hpp b/src/ox/mc/mc.hpp new file mode 100644 index 000000000..953e4721d --- /dev/null +++ b/src/ox/mc/mc.hpp @@ -0,0 +1,12 @@ +/* + * Copyright 2015 - 2017 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/src/ox/mc/presencemask.cpp b/src/ox/mc/presencemask.cpp new file mode 100644 index 000000000..c6e28d144 --- /dev/null +++ b/src/ox/mc/presencemask.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2015 - 2017 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 +#include "err.hpp" +#include "presencemask.hpp" + +namespace ox { + +FieldPresenseMask::FieldPresenseMask(uint8_t *mask, size_t maxLen) { + m_mask = mask; + m_maxLen = maxLen; +} + +bool FieldPresenseMask::get(int i) { + if (i / 8 < m_maxLen) { + return (m_mask[i / 8] >> (i % 8)) & 1; + } else { + return MC_PRESENCEMASKOUTBOUNDS; + } +} + +int FieldPresenseMask::set(int i, bool on) { + uint8_t val = on ? 1 : 0; // normalize to 0 or 1 + if (i / 8 < m_maxLen) { + m_mask[i / 8] |= val << (i % 8); + return 0; + } else { + return MC_PRESENCEMASKOUTBOUNDS; + } +} + +void FieldPresenseMask::setMaxLen(int maxLen) { + m_maxLen = maxLen; +} + +} diff --git a/src/ox/mc/presencemask.hpp b/src/ox/mc/presencemask.hpp new file mode 100644 index 000000000..0001c1d29 --- /dev/null +++ b/src/ox/mc/presencemask.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2015 - 2017 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 + +namespace ox { + +class FieldPresenseMask { + private: + uint8_t *m_mask; + int m_maxLen = 0; + + public: + FieldPresenseMask(uint8_t *mask, size_t maxLen); + + bool get(int i); + + int set(int i, bool on); + + void setMaxLen(int); +}; + +} diff --git a/src/ox/mc/read.cpp b/src/ox/mc/read.cpp index f20a10142..3d4f51006 100644 --- a/src/ox/mc/read.cpp +++ b/src/ox/mc/read.cpp @@ -6,10 +6,50 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include #include "read.hpp" namespace ox { -namespace mc { + +MetalClawReader::MetalClawReader(uint8_t *buff, size_t buffLen): m_fieldPresence(buff, buffLen) { + m_buff = buff; + m_buffLen = buffLen; +} + +int MetalClawReader::op(const char*, int16_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, int32_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, int64_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, uint16_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, uint32_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, uint64_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, bool *val) { + *val = m_fieldPresence.get(m_field++); + return 0; +} + +void MetalClawReader::setFields(int fields) { + m_fields = fields; + m_buffIt = (fields / 8 + 1) - (fields % 8 == 0); + m_fieldPresence.setMaxLen(m_buffIt); +} } -} diff --git a/src/ox/mc/read.hpp b/src/ox/mc/read.hpp index 46fd1c8c1..8a41923b6 100644 --- a/src/ox/mc/read.hpp +++ b/src/ox/mc/read.hpp @@ -8,8 +8,141 @@ #pragma once +#include +#include +#include "err.hpp" +#include "presencemask.hpp" + namespace ox { -namespace mc { + +class MetalClawReader { + + private: + FieldPresenseMask m_fieldPresence; + int m_fields = 0; + int m_field = 0; + size_t m_buffIt = 0; + size_t m_buffLen = 0; + uint8_t *m_buff = nullptr; + + public: + MetalClawReader(uint8_t *buff, size_t buffLen); + + int op(const char*, int16_t *val); + int op(const char*, int32_t *val); + int op(const char*, int64_t *val); + + int op(const char*, uint16_t *val); + int op(const char*, uint32_t *val); + int op(const char*, uint64_t *val); + + int op(const char*, bool *val); + + template + int op(const char*, T **val, size_t len); + + template + int op(const char*, T *val); + + template + int op(const char*, ox::bstring *val); + + void setFields(int fields); + + private: + template + int readInteger(I *val); +}; + +template +int MetalClawReader::op(const char*, T *val) { + int err = 0; + MetalClawReader reader(m_buff + m_buffIt, m_buffLen - m_buffIt); + err |= ioOp(&reader, val); + m_buffIt += reader.m_buffIt; + return err; +}; + +template +int MetalClawReader::op(const char*, ox::bstring *val) { + int err = 0; + if (m_fieldPresence.get(m_field)) { + // read the length + typedef uint32_t StringLength; + size_t size = 0; + if (m_buffIt + sizeof(StringLength) < m_buffLen) { + size = ox::std::bigEndianAdapt(*((StringLength*) &m_buff[m_buffIt])); + m_field++; + m_buffIt += sizeof(StringLength); + } else { + err |= MC_BUFFENDED; + } + + // read the string + if (val->cap() >= size) { + if (m_buffIt + size < m_buffLen) { + ox_memcpy(val, &m_buff[m_buffIt], size); + m_buffIt += size; + m_field++; + } else { + err |= MC_BUFFENDED; + } + } else { + err |= MC_OUTBUFFENDED; + } + } + m_field++; + return err; +}; + +template +int MetalClawReader::readInteger(I *val) { + int err = 0; + if (m_fieldPresence.get(m_field)) { + if (m_buffIt + sizeof(I) < m_buffLen) { + *val = ox::std::bigEndianAdapt(*((I*) &m_buff[m_buffIt])); + m_buffIt += sizeof(I); + } else { + err = MC_BUFFENDED; + } + } + m_field++; + return err; +}; + +template +int MetalClawReader::op(const char*, T **val, size_t valLen) { + int err = 0; + if (m_fieldPresence.get(m_field)) { + // read the length + typedef uint32_t ArrayLength; + size_t len = 0; + if (m_buffIt + sizeof(ArrayLength) < m_buffLen) { + len = ox::std::bigEndianAdapt(*((T*) &m_buff[m_buffIt])); + m_buffIt += sizeof(ArrayLength); + } else { + err = MC_BUFFENDED; + } + + // read the list + if (valLen >= len) { + MetalClawReader reader(m_buff + m_buffIt, m_buffLen - m_buffIt); + reader.setFields(len); + for (size_t i = 0; i < len; i++) { + err |= reader.op("", &val[i]); + } + } else { + err = MC_OUTBUFFENDED; + } + } + m_field++; + return err; +}; + +template +int read(uint8_t *buff, size_t buffLen, T *val) { + MetalClawReader writer(buff, buffLen); + return ioOp(&writer, val); +} } -} diff --git a/src/ox/mc/test/CMakeLists.txt b/src/ox/mc/test/CMakeLists.txt index 7789b0f58..97544fad5 100644 --- a/src/ox/mc/test/CMakeLists.txt +++ b/src/ox/mc/test/CMakeLists.txt @@ -5,4 +5,7 @@ add_executable( tests.cpp ) -target_link_libraries(McTest) +target_link_libraries(McTest OxStd OxMetalClaw) + +add_test("Test\\ McTest\\ Writer" McTest MetalClawWriter) +add_test("Test\\ McTest\\ Reader" McTest MetalClawReader) diff --git a/src/ox/mc/test/tests.cpp b/src/ox/mc/test/tests.cpp index 92f5c5741..f7b252714 100644 --- a/src/ox/mc/test/tests.cpp +++ b/src/ox/mc/test/tests.cpp @@ -6,6 +6,106 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -int main() { - return 0; +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace ox; + +struct TestStructNest { + bool Bool = false; + int Int = 0; + bstring<32> String = ""; }; + +struct TestStruct { + bool Bool = false; + int32_t Int = 0; + bstring<32> String = ""; + TestStructNest Struct; +}; + +template +int ioOp(T *io, TestStructNest *obj) { + int32_t err = 0; + io->setFields(3); + err |= io->op("Bool", &obj->Bool); + err |= io->op("Int", &obj->Int); + err |= io->op("String", &obj->String); + return err; +} + +template +int ioOp(T *io, TestStruct *obj) { + int err = 0; + io->setFields(4); + err |= io->op("Bool", &obj->Bool); + err |= io->op("Int", &obj->Int); + err |= io->op("String", &obj->String); + err |= io->op("Struct", &obj->Struct); + return err; +} + +map tests = { + { + { + "MetalClawWriter", + [](string) { + // This test doesn't confirm much, but it does show that the writer + // doesn't segfault + size_t buffLen = 1024; + uint8_t buff[buffLen]; + TestStruct ts; + return write(buff, buffLen, &ts); + } + }, + { + "MetalClawReader", + [](string) { + int err = 0; + size_t buffLen = 1024; + uint8_t buff[buffLen]; + TestStruct testIn, testOut; + + testIn.Bool = true; + testIn.Int = 42; + testIn.String = "Test String 1"; + testIn.Struct.Bool = false; + testIn.Struct.Int = 300; + testIn.Struct.String = "Test String 2"; + + err |= write(buff, buffLen, &testIn); + err |= read(buff, buffLen, &testOut); + + err |= !(testIn.Bool == testOut.Bool); + err |= !(testIn.Int == testOut.Int); + err |= !(testIn.String == testOut.String); + err |= !(testIn.Struct.Bool == testOut.Struct.Bool); + err |= !(testIn.Struct.Int == testOut.Struct.Int); + err |= !(testIn.Struct.String == testOut.Struct.String); + + return err; + } + }, + } +}; + +int main(int argc, const char **args) { + int retval = -1; + if (argc > 1) { + auto testName = args[1]; + string testArg = ""; + if (args[2]) { + testArg = args[2]; + } + if (tests.find(testName) != tests.end()) { + retval = tests[testName](testArg); + } + } + return retval; +} diff --git a/src/ox/mc/write.cpp b/src/ox/mc/write.cpp index a5eaf6c5d..cf214d0e1 100644 --- a/src/ox/mc/write.cpp +++ b/src/ox/mc/write.cpp @@ -11,23 +11,6 @@ #include "write.hpp" namespace ox { -namespace mc { - -int FieldPresenseMask::set(int i, bool on) { - uint8_t val = on ? 1 : 0; // normalize to 0 or 1 - if (i / 8 < m_maxLen) { - m_mask[i / 8] |= ox::std::bigEndianAdapt((uint8_t) (val << (i % 8))); - return 0; - } else { - return MC_PRESENCEMASKOUTBOUNDS; - } -} - -void FieldPresenseMask::setMaxLen(int maxLen) { - m_maxLen = maxLen; -} - - MetalClawWriter::MetalClawWriter(uint8_t *buff, size_t buffLen): m_fieldPresence(buff, buffLen) { m_buff = buff; @@ -62,32 +45,10 @@ int MetalClawWriter::op(const char*, bool *val) { return m_fieldPresence.set(m_field++, *val); } -int MetalClawWriter::op(const char*, const char **val, size_t len) { - int err = 0; - if (val) { - // write the length - typedef uint32_t StringLength; - if (m_buffIt + sizeof(StringLength) + len < m_buffLen) { - *((StringLength*) &m_buff[m_buffIt]) = ox::std::bigEndianAdapt((StringLength) len); - m_field++; - m_buffIt += sizeof(StringLength); - - // write the string - ox_memcpy(&m_buff[m_buffIt], val, len); - m_buffIt += len + 1; - m_buff[m_buffIt - 1] = 0; - err |= m_fieldPresence.set(m_field, true); - m_field++; - } - } - return err; -} - void MetalClawWriter::setFields(int fields) { m_fields = fields; - m_field = fields / 8; - m_fieldPresence.setMaxLen(fields / 8); + m_buffIt = (fields / 8 + 1) - (fields % 8 == 0); + m_fieldPresence.setMaxLen(m_buffIt); } } -} diff --git a/src/ox/mc/write.hpp b/src/ox/mc/write.hpp index a385948a9..c0c243b88 100644 --- a/src/ox/mc/write.hpp +++ b/src/ox/mc/write.hpp @@ -8,28 +8,12 @@ #pragma once +#include #include +#include "err.hpp" +#include "presencemask.hpp" namespace ox { -namespace mc { - -enum { - MC_PRESENCEMASKOUTBOUNDS = 1, - MC_BUFFENDED = 2 -}; - -class FieldPresenseMask { - private: - uint8_t *m_mask; - int m_maxLen = 0; - - public: - FieldPresenseMask(uint8_t *mask, size_t maxLen); - - int set(int i, bool on); - - void setMaxLen(int); -}; class MetalClawWriter { @@ -54,15 +38,12 @@ class MetalClawWriter { int op(const char*, bool *val); - /** - * - * @param len the length of the string, not including the null terminator - */ - int op(const char*, const char **val, size_t len); - template int op(const char*, T **val, size_t len); + template + int op(const char*, ox::bstring *val); + template int op(const char*, T *val); @@ -73,55 +54,89 @@ class MetalClawWriter { int appendInteger(I val); }; +template +int MetalClawWriter::op(const char*, ox::bstring *val) { + int err = 0; + bool fieldSet = false; + if (val->len()) { + // write the length + typedef uint32_t StringLength; + if (m_buffIt + sizeof(StringLength) + val->size() < m_buffLen) { + *((StringLength*) &m_buff[m_buffIt]) = ox::std::bigEndianAdapt((StringLength) val->size()); + m_buffIt += sizeof(StringLength); + + // write the string + ox_memcpy(&m_buff[m_buffIt], val, val->size()); + m_buffIt += val->size(); + fieldSet = true; + } else { + err = 1; + } + } + err |= m_fieldPresence.set(m_field, fieldSet); + m_field++; + return err; +}; + template int MetalClawWriter::op(const char*, T *val) { - MetalClawWriter reader(m_buff, m_buffLen - m_buffLen); - return ioOp(&reader, val); + int err = 0; + MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt); + err |= ioOp(&writer, val); + m_buffIt += writer.m_buffIt; + return err; }; template int MetalClawWriter::appendInteger(I val) { int err = 0; + bool fieldSet = false; if (val) { if (m_buffIt + sizeof(I) < m_buffLen) { *((I*) &m_buff[m_buffIt]) = ox::std::bigEndianAdapt(val); - err |= m_fieldPresence.set(m_field, true); - m_field++; + fieldSet = true; m_buffIt += sizeof(I); } else { - err = MC_BUFFENDED; + err |= MC_BUFFENDED; } } + err |= m_fieldPresence.set(m_field, fieldSet); + m_field++; return err; }; template int MetalClawWriter::op(const char *fieldName, T **val, size_t len) { int err = 0; - if (val) { - // write the length - typedef uint32_t ArrayLength; - if (m_buffIt + sizeof(ArrayLength) < m_buffLen) { - *((ArrayLength*) &m_buff[m_buffIt]) = ox::std::bigEndianAdapt((ArrayLength) len); - m_field++; - m_buffIt += sizeof(ArrayLength); - } else { - err = MC_BUFFENDED; - } + bool fieldSet = false; + MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt); + writer.setFields(len); - // write the string - for (size_t i = 0; i < len; i++) { - err |= op(val[i]); - } + // write the length + typedef uint32_t ArrayLength; + if (m_buffIt + sizeof(ArrayLength) < m_buffLen) { + *((T*) &m_buff[m_buffIt]) = ox::std::bigEndianAdapt((ArrayLength) len); + m_buffIt += sizeof(ArrayLength); + } else { + err = MC_BUFFENDED; } + + // write the string + for (size_t i = 0; i < len; i++) { + err |= writer.op("", val[i]); + } + + fieldSet = true; + + err |= m_fieldPresence.set(m_field, fieldSet); + m_field++; return err; }; template -int read(uint8_t *buff, size_t buffLen, T *val) { +int write(uint8_t *buff, size_t buffLen, T *val) { MetalClawWriter writer(buff, buffLen); return ioOp(&writer, val); } } -} diff --git a/src/ox/std/string.hpp b/src/ox/std/string.hpp index 4086a3734..31863db8e 100644 --- a/src/ox/std/string.hpp +++ b/src/ox/std/string.hpp @@ -8,6 +8,8 @@ #pragma once +#include "memops.hpp" +#include "strops.hpp" #include "types.hpp" namespace ox { @@ -19,12 +21,18 @@ class bstring { uint8_t m_buff[buffLen]; public: + bstring(); + bstring(const char *str); const bstring &operator=(const char *str); const bstring &operator=(char *str); + bool operator==(const bstring &other); + + char *data(); + /** * Returns the number of characters in this string. */ @@ -34,8 +42,18 @@ class bstring { * Returns the number of bytes used for this string. */ size_t size(); + + /** + * Returns the capacity of bytes for this string. + */ + size_t cap(); }; +template +bstring::bstring() { + m_buff[0] = 0; +} + template bstring::bstring(const char *str) { *this = str; @@ -43,28 +61,47 @@ bstring::bstring(const char *str) { template const bstring &bstring::operator=(const char *str) { - return *this = (const char*) str; + size_t strLen = ox_strlen(str) + 1; + if (cap() < strLen) { + strLen = cap(); + } + ox_memcpy(m_buff, str, strLen); + // make sure last element is a null terminator + m_buff[cap() - 1] = 0; + return *this; } template const bstring &bstring::operator=(char *str) { - auto strLen = ox_strlen(str) + 1; - if (size() < strLen) { - strLen = size(); + return *this = (const char*) str; +} + +template +bool bstring::operator==(const bstring &other) { + bool retval = true; + size_t i = 0; + while (i < buffLen && (m_buff[i] || other.m_buff[i])) { + if (m_buff[i] != other.m_buff[i]) { + retval = false; + break; + } + i++; } - ox_memcpy(m_buff, str, strLen); - // make sure last element is a null terminator - m_buff[size() - 1] = 0; - return *this; + return retval; +} + +template +char *bstring::data() { + return (char*) m_buff; } template size_t bstring::len() { size_t length = 0; for (size_t i = 0; i < buffLen; i++) { - auto b = m_buff[i]; + uint8_t b = m_buff[i]; if (b) { - if ((b & (1 << 8)) == 0) { // normal ASCII character + if ((b & 128) == 0) { // normal ASCII character length++; } else if ((b & (256 << 6)) == (256 << 6)) { // start of UTF-8 character length++; @@ -78,6 +115,13 @@ size_t bstring::len() { template size_t bstring::size() { + size_t i; + for (i = 0; i < buffLen && m_buff[i]; i++); + return i + 1; // add one for null terminator +} + +template +size_t bstring::cap() { return buffLen; }