diff --git a/data/Deserializer.cpp b/data/Deserializer.cpp new file mode 100644 index 00000000..b7963481 --- /dev/null +++ b/data/Deserializer.cpp @@ -0,0 +1 @@ +#include "Deserializer.h" diff --git a/data/Deserializer.h b/data/Deserializer.h new file mode 100644 index 00000000..688ae954 --- /dev/null +++ b/data/Deserializer.h @@ -0,0 +1,108 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_DESERIALIZER_H +#define MESSMER_CPPUTILS_DATA_DESERIALIZER_H + +#include "Data.h" +#include "../macros.h" +#include "../assert/assert.h" + +namespace cpputils { + class Deserializer final { + public: + Deserializer(const Data *source); + ~Deserializer(); + + uint8_t readUint8(); + int8_t readInt8(); + uint16_t readUint16(); + int16_t readInt16(); + uint32_t readUint32(); + int32_t readInt32(); + uint64_t readUint64(); + int64_t readInt64(); + Data readData(); + + void finished(); + + private: + template DataType _read(); + + size_t _pos; + const Data *_source; + bool _finishedCalled; + + DISALLOW_COPY_AND_ASSIGN(Deserializer); + }; + + inline Deserializer::Deserializer(const Data *source): _pos(0), _source(source), _finishedCalled(false) { + } + + inline Deserializer::~Deserializer() { + ASSERT(_finishedCalled, "Forgot to call Deserializer::finished()"); + } + + inline uint8_t Deserializer::readUint8() { + return _read(); + } + + inline int8_t Deserializer::readInt8() { + return _read(); + } + + inline uint16_t Deserializer::readUint16() { + return _read(); + } + + inline int16_t Deserializer::readInt16() { + return _read(); + } + + inline uint32_t Deserializer::readUint32() { + return _read(); + } + + inline int32_t Deserializer::readInt32() { + return _read(); + } + + inline uint64_t Deserializer::readUint64() { + return _read(); + } + + inline int64_t Deserializer::readInt64() { + return _read(); + } + + template + inline DataType Deserializer::_read() { + static_assert(std::is_pod::value, "Can only deserialize PODs"); + if (_pos + sizeof(DataType) > _source->size()) { + _finishedCalled = true; // We don't want the destructor assertion to fail when destructor is called because of the exception + throw std::runtime_error("Deserialization failed - size overflow"); + } + DataType result = *reinterpret_cast(_source->dataOffset(_pos)); + _pos += sizeof(DataType); + return result; + } + + inline Data Deserializer::readData() { + uint64_t size = readUint64(); + if (_pos + size > _source->size()) { + _finishedCalled = true; // We don't want the destructor assertion to fail when destructor is called because of the exception + throw std::runtime_error("Deserialization failed - size overflow"); + } + Data result(size); + std::memcpy(static_cast(result.data()), static_cast(_source->dataOffset(_pos)), size); + _pos += size; + return result; + } + + inline void Deserializer::finished() { + _finishedCalled = true; + if (_pos != _source->size()) { + throw std::runtime_error("Deserialization failed - size not fully used."); + } + } +} + +#endif diff --git a/data/Serializer.cpp b/data/Serializer.cpp new file mode 100644 index 00000000..03ed1de3 --- /dev/null +++ b/data/Serializer.cpp @@ -0,0 +1 @@ +#include "Serializer.h" diff --git a/data/Serializer.h b/data/Serializer.h new file mode 100644 index 00000000..639d2d7b --- /dev/null +++ b/data/Serializer.h @@ -0,0 +1,104 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_SERIALIZER_H +#define MESSMER_CPPUTILS_DATA_SERIALIZER_H + +#include "Data.h" +#include "../macros.h" + +namespace cpputils { + //TODO Test Serializer/Deserializer + //TODO Also test system (big endian/little endian) by adding a serialized data file to the repository and (a) reading it and (b) rewriting and comparing it + class Serializer final { + public: + Serializer(size_t size); + + void writeUint8(uint8_t value); + void writeInt8(int8_t value); + void writeUint16(uint16_t value); + void writeInt16(int16_t value); + void writeUint32(uint32_t value); + void writeInt32(int32_t value); + void writeUint64(uint64_t value); + void writeInt64(int64_t value); + void writeData(const Data &data); + + static size_t DataSize(const Data &data); + + Data finished(); + + private: + template void _write(DataType obj); + + size_t _pos; + Data _result; + + DISALLOW_COPY_AND_ASSIGN(Serializer); + }; + + inline Serializer::Serializer(size_t size): _pos(0), _result(size) { + } + + inline void Serializer::writeUint8(uint8_t value) { + _write(value); + } + + inline void Serializer::writeInt8(int8_t value) { + _write(value); + } + + inline void Serializer::writeUint16(uint16_t value) { + _write(value); + } + + inline void Serializer::writeInt16(int16_t value) { + _write(value); + } + + inline void Serializer::writeUint32(uint32_t value) { + _write(value); + } + + inline void Serializer::writeInt32(int32_t value) { + _write(value); + } + + inline void Serializer::writeUint64(uint64_t value) { + _write(value); + } + + inline void Serializer::writeInt64(int64_t value) { + _write(value); + } + + template + inline void Serializer::_write(DataType obj) { + static_assert(std::is_pod::value, "Can only serialize PODs"); + if (_pos + sizeof(DataType) > _result.size()) { + throw std::runtime_error("Serialization failed - size overflow"); + } + *reinterpret_cast(_result.dataOffset(_pos)) = obj; + _pos += sizeof(DataType); + } + + inline void Serializer::writeData(const Data &data) { + writeUint64(data.size()); + if (_pos + data.size() > _result.size()) { + throw std::runtime_error("Serialization failed - size overflow"); + } + std::memcpy(static_cast(_result.dataOffset(_pos)), static_cast(data.data()), data.size()); + _pos += data.size(); + } + + inline size_t Serializer::DataSize(const Data &data) { + return sizeof(uint64_t) + data.size(); + } + + Data Serializer::finished() { + if (_pos != _result.size()) { + throw std::runtime_error("Serialization failed - size not fully used."); + } + return std::move(_result); + } +} + +#endif