#pragma once #include "endian.hpp" #include "types.hpp" #include "../utils.hpp" #include #include namespace sibs { class DeserializeException : public std::runtime_error { public: DeserializeException(const std::string &errMsg) : std::runtime_error(errMsg) {} }; /** * Endian independent deserializer */ class SafeDeserializer { public: SafeDeserializer(const u8 *_data, usize _size) : data(_data), size(_size) { } /* * Throws DeserializeException on failure */ template T extract() { constexpr usize typeSize = sizeof(T); verifyExtractSize(typeSize); size -= typeSize; T result; #if BYTE_ORDER == BIG_ENDIAN switch(typeSize) { case 1: result = *(T*)data; break; case 2: *(u16*)&result = byteswap(*(u16*)data); break; case 4: *(u32*)&result = byteswap(*(u32*)data); break; case 8: *(u64*)&result = byteswap(*(u64*)data); break; default: { for(int i = 0; i < typeSize; ++i) { ((char*)&result)[i] = data[typeSize - 1 - i]; } break; } } #else result = *(T*)data; #endif data += typeSize; return result; } /* * Throws DeserializeException on failure */ void extract(u8 *destination, usize destinationSize) { if(destinationSize > 0) { verifyExtractSize(destinationSize); size -= destinationSize; memcpy(destination, data, destinationSize); data += destinationSize; } } bool empty() const { return size == 0; } const u8* getBuffer() { return data; } usize getSize() const { return size; } void skip(usize bytesToSkip) { bytesToSkip = bytesToSkip < size ? bytesToSkip : size; size -= bytesToSkip; data += bytesToSkip; } private: void verifyExtractSize(usize typeSize) const { if(typeSize > size) { std::string errMsg = "Unable to extract "; errMsg += std::to_string(typeSize); errMsg += " bytes, only "; errMsg += std::to_string(size); errMsg += " bytes left in buffer"; throw DeserializeException(errMsg); } } private: const u8 *data; usize size; }; }