#ifndef __FIBRE_SIMPLE_SERDES #define __FIBRE_SIMPLE_SERDES #include "cpp_utils.hpp" #include "limits.h" #include // TODO: make C++11 backport of this #include #include template struct SimpleSerializer; template using LittleEndianSerializer = SimpleSerializer; template using BigEndianSerializer = SimpleSerializer; /* @brief Serializer/deserializer for arbitrary integral number types */ // TODO: allow reading an arbitrary number of bits template struct SimpleSerializer::value>> { static constexpr size_t BIT_WIDTH = std::numeric_limits::digits; static constexpr size_t BYTE_WIDTH = (BIT_WIDTH + 7) / 8; template static std::optional read(TIterator* begin, TIterator end = nullptr) { T result = 0; if (BigEndian) { for (size_t i = BYTE_WIDTH; i > 0; (i++, (*begin)++)) { if (end && !(*begin < end)) return std::nullopt; uint8_t byte = **begin; result |= static_cast(byte) << ((i - 1) << 3); } } else { for (size_t i = 0; i < BYTE_WIDTH; (i++, (*begin)++)) { if (end && !(*begin < end)) return std::nullopt; uint8_t byte = **begin; result |= static_cast(byte) << (i << 3); } } return result; } template static bool write(T value, TIterator* begin, TIterator end = nullptr) { if (BigEndian) { for (size_t i = BYTE_WIDTH; i > 0; (i--, (*begin)++)) { if (end && !(*begin < end)) return false; uint8_t byte = static_cast((value >> ((i - 1) << 3)) & 0xff); **begin = byte; } } else { for (size_t i = 0; i < BYTE_WIDTH; (i++, (*begin)++)) { if (end && !(*begin < end)) return false; uint8_t byte = static_cast((value >> (i << 3)) & 0xff); **begin = byte; } } return true; } }; template inline std::optional read_le(fibre::cbufptr_t* buffer) { static_assert(is_complete>(), "no LittleEndianSerializer is defined for type T"); return LittleEndianSerializer::read(&buffer->begin(), buffer->end()); } template inline bool write_le(T value, fibre::bufptr_t* buffer) { static_assert(is_complete>(), "no LittleEndianSerializer is defined for type T"); return LittleEndianSerializer::write(value, &buffer->begin(), buffer->end()); } template::value>> inline size_t write_le(T value, uint8_t* buffer){ //TODO: add static_assert that this is still a little endian machine std::memcpy(&buffer[0], &value, sizeof(value)); return sizeof(value); } template typename std::enable_if_t::value, size_t> write_le(T value, uint8_t* buffer) { return write_le>(value, buffer); } template<> inline size_t write_le(float value, uint8_t* buffer) { static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected"); static_assert(std::numeric_limits::is_iec559, "IEEE 754 floating point expected"); uint32_t value_as_uint32; std::memcpy(&value_as_uint32, &value, sizeof(uint32_t)); return write_le(value_as_uint32, buffer); } template inline size_t read_le(T* value, const uint8_t* buffer){ // TODO: add static_assert that this is still a little endian machine std::memcpy(value, buffer, sizeof(*value)); return sizeof(*value); } template<> inline size_t read_le(float* value, const uint8_t* buffer) { static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected"); static_assert(std::numeric_limits::is_iec559, "IEEE 754 floating point expected"); return read_le(reinterpret_cast(value), buffer); } // @brief Reads a value of type T from the buffer. // @param buffer Pointer to the buffer to be read. The pointer is updated by the number of bytes that were read. // @param length The number of available bytes in buffer. This value is updated to subtract the bytes that were read. template static inline T read_le(const uint8_t** buffer, size_t* length) { T result; size_t cnt = read_le(&result, *buffer); *buffer += cnt; *length -= cnt; return result; } #endif