diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 2e7f8c62ca..8370462c5a 100644 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -93,7 +93,7 @@ public: typedef std::shared_ptr< SphericalHarmonics > SHPointer; -inline SerDes &operator<<(SerDes &ser, const SphericalHarmonics &h) { +inline DataSerializer &operator<<(DataSerializer &ser, const SphericalHarmonics &h) { ser << h.L00 << h.spare0; ser << h.L1m1 << h.spare1; ser << h.L10 << h.spare2; @@ -106,7 +106,7 @@ inline SerDes &operator<<(SerDes &ser, const SphericalHarmonics &h) { return ser; } -inline SerDes &operator>>(SerDes &des, SphericalHarmonics &h) { +inline DataDeserializer &operator>>(DataDeserializer &des, SphericalHarmonics &h) { des >> h.L00 >> h.spare0; des >> h.L1m1 >> h.spare1; des >> h.L10 >> h.spare2; @@ -185,7 +185,7 @@ public: _maxMip == other._maxMip; } - SerDes &operator<<(SerDes &dsd) { + DataSerializer &operator<<(DataSerializer &dsd) { dsd << _borderColor; dsd << _maxAnisotropy; dsd << _filter; @@ -237,7 +237,7 @@ protected: friend class Deserializer; }; -inline SerDes &operator<<(SerDes &ser, const Sampler::Desc &d) { +inline DataSerializer &operator<<(DataSerializer &ser, const Sampler::Desc &d) { ser << d._borderColor; ser << d._maxAnisotropy; ser << d._filter; @@ -251,7 +251,7 @@ inline SerDes &operator<<(SerDes &ser, const Sampler::Desc &d) { return ser; } -inline SerDes &operator>>(SerDes &dsr, Sampler::Desc &d) { +inline DataDeserializer &operator>>(DataDeserializer &dsr, Sampler::Desc &d) { dsr >> d._borderColor; dsr >> d._maxAnisotropy; dsr >> d._filter; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index dc3d23948d..6845aeb55b 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -43,7 +43,7 @@ struct GPUKTXPayload { TextureUsageType _usageType; glm::ivec2 _originalSize { 0, 0 }; - void serialize(SerDes &ser) { + void serialize(DataSerializer &ser) { ser << CURRENT_VERSION; ser << _samplerDesc; @@ -55,7 +55,7 @@ struct GPUKTXPayload { ser.addPadding(PADDING); } - bool unserialize(SerDes &dsr) { + bool unserialize(DataDeserializer &dsr) { Version version = 0; uint32 usageData; uint8_t usagetype = 0; @@ -88,7 +88,7 @@ struct GPUKTXPayload { auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); if (found != keyValues.end()) { auto value = found->_value; - SerDes dsr(value.data(), value.size()); + DataDeserializer dsr(value.data(), value.size()); return payload.unserialize(dsr); } return false; @@ -109,13 +109,13 @@ struct IrradianceKTXPayload { SphericalHarmonics _irradianceSH; - void serialize(SerDes &ser) const { + void serialize(DataSerializer &ser) const { ser << CURRENT_VERSION; ser << _irradianceSH; ser.addPadding(PADDING); } - bool unserialize(SerDes &des) { + bool unserialize(DataDeserializer &des) { Version version; if (des.length() != SIZE) { return false; @@ -138,7 +138,7 @@ struct IrradianceKTXPayload { auto found = std::find_if(keyValues.begin(), keyValues.end(), isIrradianceKTX); if (found != keyValues.end()) { auto value = found->_value; - SerDes des(value.data(), value.size()); + DataDeserializer des(value.data(), value.size()); return payload.unserialize(des); } return false; @@ -449,7 +449,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture, const glm::ivec gpuKeyval._originalSize = originalSize; Byte keyvalPayload[GPUKTXPayload::SIZE]; - SerDes ser(keyvalPayload, sizeof(keyvalPayload)); + DataSerializer ser(keyvalPayload, sizeof(keyvalPayload)); gpuKeyval.serialize(ser); @@ -461,7 +461,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture, const glm::ivec irradianceKeyval._irradianceSH = *texture.getIrradiance(); Byte irradianceKeyvalPayload[IrradianceKTXPayload::SIZE]; - SerDes ser(irradianceKeyvalPayload, sizeof(irradianceKeyvalPayload)); + DataSerializer ser(irradianceKeyvalPayload, sizeof(irradianceKeyvalPayload)); irradianceKeyval.serialize(ser); keyValues.emplace_back(IrradianceKTXPayload::KEY, (uint32)IrradianceKTXPayload::SIZE, (ktx::Byte*) &irradianceKeyvalPayload); diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index c13d58226b..3745582728 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -17,6 +17,7 @@ #include "OctreeLogging.h" #include "NumericalConstants.h" #include +#include "SerDes.h" bool OctreePacketData::_debug = false; AtomicUIntStat OctreePacketData::_totalBytesOfOctalCodes { 0 }; @@ -847,10 +848,10 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteA } int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, AACube& result) { - aaCubeData cube; - memcpy(&cube, dataBytes, sizeof(aaCubeData)); - result = AACube(cube.corner, cube.scale); - return sizeof(aaCubeData); + DataDeserializer des(dataBytes, sizeof(aaCubeData)); + des >> result; + + return des.length(); } int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QRect& result) { diff --git a/libraries/shared/src/AACube.h b/libraries/shared/src/AACube.h index 66b29e3185..27c424cadb 100644 --- a/libraries/shared/src/AACube.h +++ b/libraries/shared/src/AACube.h @@ -20,6 +20,7 @@ #include #include "BoxBase.h" +#include "SerDes.h" class AABox; class Extents; @@ -80,6 +81,10 @@ private: glm::vec3 _corner; float _scale; + + friend DataSerializer& operator<<(DataSerializer &ser, const AACube &cube); + friend DataDeserializer& operator>>(DataDeserializer &des, AACube &cube); + }; inline bool operator==(const AACube& a, const AACube& b) { @@ -99,5 +104,16 @@ inline QDebug operator<<(QDebug debug, const AACube& cube) { return debug; } +inline DataSerializer& operator<<(DataSerializer &ser, const AACube &cube) { + ser << cube._corner; + ser << cube._scale; + return ser; +} + +inline DataDeserializer& operator>>(DataDeserializer &des, AACube &cube) { + des >> cube._corner; + des >> cube._scale; + return des; +} #endif // hifi_AACube_h diff --git a/libraries/shared/src/SerDes.cpp b/libraries/shared/src/SerDes.cpp index c64430f201..044ea1fad2 100644 --- a/libraries/shared/src/SerDes.cpp +++ b/libraries/shared/src/SerDes.cpp @@ -12,10 +12,10 @@ #include #include "SerDes.h" -const int SerDes::DEFAULT_SIZE; -const char SerDes::PADDING_CHAR; +const int DataSerializer::DEFAULT_SIZE; +const char DataSerializer::PADDING_CHAR; -QDebug operator<<(QDebug debug, const SerDes &ds) { +QDebug operator<<(QDebug debug, const DataSerializer &ds) { debug << "{ capacity =" << ds.capacity() << "; length = " << ds.length() << "; pos = " << ds.pos() << "}"; debug << "\n"; @@ -54,7 +54,7 @@ QDebug operator<<(QDebug debug, const SerDes &ds) { } -void SerDes::changeAllocation(size_t new_size) { +void DataSerializer::changeAllocation(size_t new_size) { while ( _capacity < new_size) { _capacity *= 2; } diff --git a/libraries/shared/src/SerDes.h b/libraries/shared/src/SerDes.h index 356bb45522..ee38a87aa8 100644 --- a/libraries/shared/src/SerDes.h +++ b/libraries/shared/src/SerDes.h @@ -17,7 +17,7 @@ #include /** - * @brief Data serializer/deserializer + * @brief Data serializer * * When encoding, this class takes in data and encodes it into a buffer. No attempt is made to store version numbers, lengths, * or any other metadata. It's entirely up to the user to use the class in such a way that the process can be @@ -37,7 +37,7 @@ * uint16_t count = 1; * glm::vec3 pos{1.5, 2.0, 9.0}; * - * SerDes ser; + * Serializer ser; * ser << version; * ser << count; * ser << pos; @@ -45,44 +45,20 @@ * // Serialized data is in ser.buffer(), ser.length() long. * @endcode * - * Example of decoding: - * - * @code {.cpp} - * // Incoming data has been placed in: - * // char buffer[1024]; - * - * uint8_t version; - * uint16_t count; - * glm::vec3 pos; - * - * SerDes des(buffer, sizeof(buffer)); - * des >> version; - * des >> count; - * des >> pos; - * @endcode - * * This object should be modified directly to add support for any primitive and common datatypes in the code. To support serializing/deserializing * classes and structures, implement a `operator<<` and `operator>>` functions for that object, eg: * * @code {.cpp} - * SerDes &operator<<(SerDes &ser, const Object &o) { + * Serializer &operator<<(Serializer &ser, const Object &o) { * ser << o._borderColor; * ser << o._maxAnisotropy; * ser << o._filter; * return ser; * } - * - * SerDes &operator>>(SerDes &des, Object &o) { - * des >> o._borderColor; - * des >> o._maxAnisotropy; - * des >> o._filter; - * return des; - * } - * * @endcode * */ -class SerDes { +class DataSerializer { public: /** * @brief Default size for a dynamically allocated buffer. @@ -106,7 +82,7 @@ class SerDes { * * The buffer is SerDes::DEFAULT_SIZE bytes by default, and doubles in size every time the limit is reached. */ - SerDes() { + DataSerializer() { _capacity = DEFAULT_SIZE; _pos = 0; _length = 0; @@ -125,7 +101,7 @@ class SerDes { * @param externalStore External data store * @param storeLength Length of the data store */ - SerDes(char *externalStore, size_t storeLength) { + DataSerializer(char *externalStore, size_t storeLength) { _capacity = storeLength; _length = storeLength; _pos = 0; @@ -144,16 +120,16 @@ class SerDes { * @param externalStore External data store * @param storeLength Length of the data store */ - SerDes(uint8_t *externalStore, size_t storeLength) : SerDes((char*)externalStore, storeLength) { + DataSerializer(uint8_t *externalStore, size_t storeLength) : DataSerializer((char*)externalStore, storeLength) { } - SerDes(const SerDes &) = delete; - SerDes &operator=(const SerDes &) = delete; + DataSerializer(const DataSerializer &) = delete; + DataSerializer &operator=(const DataSerializer &) = delete; - ~SerDes() { + ~DataSerializer() { if (!_storeIsExternal) { delete[] _store; } @@ -169,7 +145,7 @@ class SerDes { * @param bytes Number of bytes to add */ void addPadding(size_t bytes) { - if (!extendBy(bytes)) { + if (!extendBy(bytes, "padding")) { return; } @@ -184,7 +160,7 @@ class SerDes { * @param c Character to add * @return SerDes& This object */ - SerDes &operator<<(uint8_t c) { + DataSerializer &operator<<(uint8_t c) { return *this << int8_t(c); } @@ -194,8 +170,8 @@ class SerDes { * @param c Character to add * @return SerDes& This object */ - SerDes &operator<<(int8_t c) { - if (!extendBy(1)) { + DataSerializer &operator<<(int8_t c) { + if (!extendBy(1, "int8_t")) { return *this; } @@ -203,32 +179,6 @@ class SerDes { return *this; } - /** - * @brief Read an uint8_t from the buffer - * - * @param c Character to read - * @return SerDes& This object - */ - SerDes &operator>>(uint8_t &c) { - return *this >> reinterpret_cast(c); - } - - /** - * @brief Read an int8_t from the buffer - * - * @param c Character to read - * @return SerDes& This object - */ - SerDes &operator>>(int8_t &c) { - if ( _pos < _length ) { - c = _store[_pos++]; - } else { - _overflow = true; - qCritical() << "Deserializer trying to read past end of input, reading 8 bits from position " << _pos << ", length " << _length; - } - - return *this; - } /////////////////////////////////////////////////////////// @@ -238,7 +188,7 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(uint16_t val) { + DataSerializer &operator<<(uint16_t val) { return *this << int16_t(val); } @@ -248,8 +198,8 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(int16_t val) { - if (!extendBy(sizeof(val))) { + DataSerializer &operator<<(int16_t val) { + if (!extendBy(sizeof(val), "int16_t")) { return *this; } @@ -258,33 +208,7 @@ class SerDes { return *this; } - /** - * @brief Read an uint16_t from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(uint16_t &val) { - return *this >> reinterpret_cast(val); - } - /** - * @brief Read an int16_t from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(int16_t &val) { - if ( _pos + sizeof(val) <= _length ) { - memcpy((char*)&val, &_store[_pos], sizeof(val)); - _pos += sizeof(val); - } else { - _overflow = true; - qCritical() << "Deserializer trying to read past end of input, reading 16 bits from position " << _pos << ", length " << _length; - } - - return *this; - } /////////////////////////////////////////////////////////// @@ -294,7 +218,7 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(uint32_t val) { + DataSerializer &operator<<(uint32_t val) { return *this << int32_t(val); } @@ -304,8 +228,8 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(int32_t val) { - if (!extendBy(sizeof(val))) { + DataSerializer &operator<<(int32_t val) { + if (!extendBy(sizeof(val), "int32_t")) { return *this; } @@ -314,33 +238,6 @@ class SerDes { return *this; } - /** - * @brief Read an uint32_t from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(uint32_t &val) { - return *this >> reinterpret_cast(val); - } - - /** - * @brief Read an int32_t from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(int32_t &val) { - if ( _pos + sizeof(val) <= _length ) { - memcpy((char*)&val, &_store[_pos], sizeof(val)); - _pos += sizeof(val); - } else { - _overflow = true; - qCritical() << "Deserializer trying to read past end of input, reading 32 bits from position " << _pos << ", length " << _length; - } - return *this; - } - /////////////////////////////////////////////////////////// @@ -350,33 +247,15 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(float val) { - if (!extendBy(sizeof(val))) { - return *this; - } - - memcpy(&_store[_pos], (char*)&val, sizeof(val)); - _pos += sizeof(val); - return *this; - } - - /** - * @brief Read an float from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(float &val) { - if ( _pos + sizeof(val) <= _length ) { - memcpy((char*)&val, &_store[_pos], sizeof(val)); + DataSerializer &operator<<(float val) { + if (extendBy(sizeof(val), "float")) { + memcpy(&_store[_pos], (char*)&val, sizeof(val)); _pos += sizeof(val); - } else { - _overflow = true; - qCritical() << "Deserializer trying to read past end of input, reading float from position " << _pos << ", length " << _length; } return *this; } + /////////////////////////////////////////////////////////// @@ -386,38 +265,14 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(glm::vec3 val) { + DataSerializer &operator<<(glm::vec3 val) { size_t sz = sizeof(val.x); - if (!extendBy(sz*3)) { - return *this; - } - - memcpy(&_store[_pos ], (char*)&val.x, sz); - memcpy(&_store[_pos + sz ], (char*)&val.y, sz); - memcpy(&_store[_pos + sz*2], (char*)&val.z, sz); - - _pos += sz*3; - return *this; - } - - /** - * @brief Read a glm::vec3 from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(glm::vec3 &val) { - size_t sz = sizeof(val.x); - - if ( _pos + sz*3 <= _length ) { - memcpy((char*)&val.x, &_store[_pos ], sz); - memcpy((char*)&val.y, &_store[_pos + sz ], sz); - memcpy((char*)&val.z, &_store[_pos + sz*2], sz); + if (extendBy(sz*3, "glm::vec3")) { + memcpy(&_store[_pos ], (char*)&val.x, sz); + memcpy(&_store[_pos + sz ], (char*)&val.y, sz); + memcpy(&_store[_pos + sz*2], (char*)&val.z, sz); _pos += sz*3; - } else { - _overflow = true; - qCritical() << "Deserializer trying to read past end of input, reading glm::vec3 from position " << _pos << ", length " << _length; } return *this; } @@ -430,40 +285,15 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(glm::vec4 val) { + DataSerializer &operator<<(glm::vec4 val) { size_t sz = sizeof(val.x); - if (!extendBy(sz*4)) { - return *this; - } - - memcpy(&_store[_pos ], (char*)&val.x, sz); - memcpy(&_store[_pos + sz ], (char*)&val.y, sz); - memcpy(&_store[_pos + sz*2], (char*)&val.z, sz); - memcpy(&_store[_pos + sz*3], (char*)&val.w, sz); - - _pos += sz*4; - return *this; - } - - /** - * @brief Read a glm::vec4 from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(glm::vec4 &val) { - size_t sz = sizeof(val.x); - - if ( _pos + sz*4 <= _length ) { - memcpy((char*)&val.x, &_store[_pos ], sz); - memcpy((char*)&val.y, &_store[_pos + sz ], sz); - memcpy((char*)&val.z, &_store[_pos + sz*2], sz); - memcpy((char*)&val.w, &_store[_pos + sz*3], sz); + if (extendBy(sz*4, "glm::vec4")) { + memcpy(&_store[_pos ], (char*)&val.x, sz); + memcpy(&_store[_pos + sz ], (char*)&val.y, sz); + memcpy(&_store[_pos + sz*2], (char*)&val.z, sz); + memcpy(&_store[_pos + sz*3], (char*)&val.w, sz); _pos += sz*4; - } else { - _overflow = true; - qCritical() << "Deserializer trying to read past end of input, reading glm::vec3 from position " << _pos << ", length " << _length; } return *this; } @@ -476,39 +306,18 @@ class SerDes { * @param val Value to add * @return SerDes& This object */ - SerDes &operator<<(glm::ivec2 val) { + DataSerializer &operator<<(glm::ivec2 val) { size_t sz = sizeof(val.x); - if (!extendBy(sz*2)) { - return *this; - } - - memcpy(&_store[_pos ], (char*)&val.x, sz); - memcpy(&_store[_pos + sz ], (char*)&val.y, sz); - - _pos += sz*2; - return *this; - } - - /** - * @brief Read a glm::ivec2 from the buffer - * - * @param val Value to read - * @return SerDes& This object - */ - SerDes &operator>>(glm::ivec2 &val) { - size_t sz = sizeof(val.x); - - if ( _pos + sz*2 <= _length ) { - memcpy((char*)&val.x, &_store[_pos ], sz); - memcpy((char*)&val.y, &_store[_pos + sz ], sz); + if (extendBy(sz*2, "glm::ivec2")) { + memcpy(&_store[_pos ], (char*)&val.x, sz); + memcpy(&_store[_pos + sz ], (char*)&val.y, sz); _pos += sz*2; - } else { - _overflow = true; - qCritical() << "Deserializer trying to read past end of input, reading glm::ivec2 from position " << _pos << ", length " << _length; } return *this; } + + /////////////////////////////////////////////////////////// /** @@ -519,11 +328,12 @@ class SerDes { * @param val Value to write * @return SerDes& This object */ - SerDes &operator<<(const char *val) { + DataSerializer &operator<<(const char *val) { size_t len = strlen(val)+1; - extendBy(len); - memcpy(&_store[_pos], val, len); - _pos += len; + if (extendBy(len, "string")) { + memcpy(&_store[_pos], val, len); + _pos += len; + } return *this; } @@ -535,7 +345,7 @@ class SerDes { * @param val Value to write * @return SerDes& This object */ - SerDes &operator<<(const QString &val) { + DataSerializer &operator<<(const QString &val) { return *this << val.toUtf8().constData(); } @@ -612,14 +422,15 @@ class SerDes { * @param ds This object * @return QDebug */ - friend QDebug operator<<(QDebug debug, const SerDes &ds); + friend QDebug operator<<(QDebug debug, const DataSerializer &ds); private: - bool extendBy(size_t bytes) { + bool extendBy(size_t bytes, const QString &type_name) { //qDebug() << "Extend by" << bytes; if ( _capacity < _length + bytes) { if ( _storeIsExternal ) { + qCritical() << "Serializer trying to write past end of input, writing" << bytes << "bytes for" << type_name << " from position " << _pos << ", length " << _length; _overflow = true; return false; } @@ -641,3 +452,336 @@ class SerDes { size_t _length = 0; size_t _pos = 0; }; + +/** + * @brief Data deserializer + * + * This class operates on a fixed size buffer. If an attempt to read past the end is made, the read fails, + * and the overflow flag is set. + * + * The class was written for the maximum simplicity possible and inline friendliness. + * + * Example of decoding: + * + * @code {.cpp} + * // Incoming data has been placed in: + * // char buffer[1024]; + * + * uint8_t version; + * uint16_t count; + * glm::vec3 pos; + * + * Deserializer des(buffer, sizeof(buffer)); + * des >> version; + * des >> count; + * des >> pos; + * @endcode + * + * This object should be modified directly to add support for any primitive and common datatypes in the code. To support deserializing + * classes and structures, implement an `operator>>` function for that object, eg: + * + * @code {.cpp} + * Deserializer &operator>>(Deserializer &des, Object &o) { + * des >> o._borderColor; + * des >> o._maxAnisotropy; + * des >> o._filter; + * return des; + * } + * @endcode + * + */ +class DataDeserializer { + public: + /** + * @brief Construct a Deserializer + * * + * @param externalStore External data store + * @param storeLength Length of the data store + */ + DataDeserializer(const char *externalStore, size_t storeLength) { + _length = storeLength; + _pos = 0; + _store = externalStore; + } + + /** + * @brief Construct a Deserializer + * + * @param externalStore External data store + * @param storeLength Length of the data store + */ + DataDeserializer(const uint8_t *externalStore, size_t storeLength) : DataDeserializer((const char*)externalStore, storeLength) { + + } + + /** + * @brief Construct a new Deserializer reading data from a Serializer + * + * This is a convenience function for testing. + * + * @param serializer Serializer with data + */ + DataDeserializer(const DataSerializer &serializer) : DataDeserializer(serializer.buffer(), serializer.length()) { + + } + + /** + * @brief Skips padding in the input + * + * @param bytes Number of bytes to skip + */ + void skipPadding(size_t bytes) { + if (!canAdvanceBy(bytes, "padding")) { + return; + } + + _pos += bytes; + } + + + /** + * @brief Read an uint8_t from the buffer + * + * @param c Character to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(uint8_t &c) { + return *this >> reinterpret_cast(c); + } + + /** + * @brief Read an int8_t from the buffer + * + * @param c Character to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(int8_t &c) { + if ( canAdvanceBy(1, "int8_t") ) { + c = _store[_pos++]; + } + + return *this; + } + + /////////////////////////////////////////////////////////// + + /** + * @brief Read an uint16_t from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(uint16_t &val) { + return *this >> reinterpret_cast(val); + } + + /** + * @brief Read an int16_t from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(int16_t &val) { + if ( canAdvanceBy(sizeof(val), "int16_t") ) { + memcpy((char*)&val, &_store[_pos], sizeof(val)); + _pos += sizeof(val); + } + + return *this; + } + + /////////////////////////////////////////////////////////// + + /** + * @brief Read an uint32_t from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(uint32_t &val) { + return *this >> reinterpret_cast(val); + } + + /** + * @brief Read an int32_t from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(int32_t &val) { + if ( canAdvanceBy(sizeof(val), "int32_t") ) { + memcpy((char*)&val, &_store[_pos], sizeof(val)); + _pos += sizeof(val); + } + return *this; + } + + + /////////////////////////////////////////////////////////// + + + /** + * @brief Read an float from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(float &val) { + if ( canAdvanceBy(sizeof(val), "float") ) { + memcpy((char*)&val, &_store[_pos], sizeof(val)); + _pos += sizeof(val); + } + return *this; + } + + /////////////////////////////////////////////////////////// + + + + + /** + * @brief Read a glm::vec3 from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(glm::vec3 &val) { + size_t sz = sizeof(val.x); + + if ( canAdvanceBy(sz*3, "glm::vec3") ) { + memcpy((char*)&val.x, &_store[_pos ], sz); + memcpy((char*)&val.y, &_store[_pos + sz ], sz); + memcpy((char*)&val.z, &_store[_pos + sz*2], sz); + + _pos += sz*3; + } + + return *this; + } + + /////////////////////////////////////////////////////////// + + + /** + * @brief Read a glm::vec4 from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(glm::vec4 &val) { + size_t sz = sizeof(val.x); + + if ( canAdvanceBy(sz*4, "glm::vec4")) { + memcpy((char*)&val.x, &_store[_pos ], sz); + memcpy((char*)&val.y, &_store[_pos + sz ], sz); + memcpy((char*)&val.z, &_store[_pos + sz*2], sz); + memcpy((char*)&val.w, &_store[_pos + sz*3], sz); + + _pos += sz*4; + } + return *this; + } + + /////////////////////////////////////////////////////////// + + + /** + * @brief Read a glm::ivec2 from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(glm::ivec2 &val) { + size_t sz = sizeof(val.x); + + if ( canAdvanceBy(sz*2, "glm::ivec2") ) { + memcpy((char*)&val.x, &_store[_pos ], sz); + memcpy((char*)&val.y, &_store[_pos + sz ], sz); + + _pos += sz*2; + } + + return *this; + } + + /////////////////////////////////////////////////////////// + + /** + * @brief Pointer to the start of the internal buffer. + * + * The allocated amount can be found with capacity(). + * + * The end of the stored data can be found with length(). + * + * @return Pointer to buffer + */ + const char *buffer() const { return _store; } + + /** + * @brief Current position in the buffer. Starts at 0. + * + * @return size_t + */ + size_t pos() const { return _pos; } + + /** + * @brief Last position that was written to in the buffer. Starts at 0. + * + * @return size_t + */ + size_t length() const { return _length; } + + /** + * @brief Whether there's any data in the buffer + * + * @return true Something has been written + * @return false The buffer is empty + */ + bool isEmpty() const { return _length == 0; } + + /** + * @brief The buffer size limit has been reached + * + * This can only return true for a statically allocated buffer. + * + * @return true Limit reached + * @return false There is still room + */ + bool isOverflow() const { return _overflow; } + + /** + * @brief Reset the serializer to the start, clear overflow bit. + * + */ + void rewind() { _pos = 0; _overflow = false; } + + /** + * @brief Dump the contents of this object into QDebug + * + * This produces a dump of the internal state, and an ASCII/hex dump of + * the contents, for debugging. + * + * @param debug Qt QDebug stream + * @param ds This object + * @return QDebug + */ + friend QDebug operator<<(QDebug debug, const DataDeserializer &ds); + + private: + bool canAdvanceBy(size_t bytes, const QString &type_name) { + //qDebug() << "Checking advance by" << bytes; + + if ( _length < _pos + bytes) { + qCritical() << "Deserializer trying to read past end of input, reading" << bytes << "bytes for" << type_name << "from position " << _pos << ". Max length " << _length; + _overflow = true; + return false; + } + + return true; + } + + const char *_store; + bool _overflow = false; + size_t _length = 0; + size_t _pos = 0; +};