#ifndef VOXEL_UTIL_SERIALIZATION_H #define VOXEL_UTIL_SERIALIZATION_H #include "span.h" #include namespace zylann { enum Endianess { // ENDIANESS_BIG_ENDIAN, ENDIANESS_LITTLE_ENDIAN }; inline Endianess get_platform_endianess() { // https://stackoverflow.com/questions/4181951/how-to-check-whether-a-system-is-big-endian-or-little-endian#4181991 const int n = 1; if (*(char *)&n == 1) { return ENDIANESS_LITTLE_ENDIAN; } return ENDIANESS_BIG_ENDIAN; // TODO In C++20 we'll be able to use std::endian } template struct MemoryWriterTemplate { Container_T &data; // Using network-order by default // TODO Apparently big-endian is dead // I chose it originally to match "network byte order", // but as I read comments about it there seem to be no reason to continue using it. Needs a version increment. Endianess endianess = ENDIANESS_BIG_ENDIAN; MemoryWriterTemplate(Container_T &p_data, Endianess p_endianess) : data(p_data), endianess(p_endianess) {} inline void store_8(uint8_t v) { data.push_back(v); } inline void store_16(uint16_t v) { if (endianess == ENDIANESS_BIG_ENDIAN) { data.push_back(v >> 8); data.push_back(v & 0xff); } else { data.push_back(v & 0xff); data.push_back(v >> 8); } } inline void store_32(uint32_t v) { if (endianess == ENDIANESS_BIG_ENDIAN) { data.push_back(v >> 24); data.push_back(v >> 16); data.push_back(v >> 8); data.push_back(v & 0xff); } else { data.push_back(v & 0xff); data.push_back(v >> 8); data.push_back(v >> 16); data.push_back(v >> 24); } } inline void store_64(uint64_t v) { if (endianess == ENDIANESS_BIG_ENDIAN) { data.push_back(v >> 56); data.push_back(v >> 48); data.push_back(v >> 40); data.push_back(v >> 32); data.push_back(v >> 24); data.push_back(v >> 16); data.push_back(v >> 8); data.push_back(v & 0xff); } else { data.push_back(v & 0xff); data.push_back(v >> 8); data.push_back(v >> 16); data.push_back(v >> 24); data.push_back(v >> 32); data.push_back(v >> 40); data.push_back(v >> 48); data.push_back(v >> 56); } } inline void store_float(float v) { union M { uint32_t i; float f; } m; m.f = v; store_32(m.i); } inline void store_buffer(Span p_data) { const size_t begin = data.size(); data.resize(data.size() + p_data.size()); memcpy(&data[begin], p_data.data(), p_data.size()); } }; // Default typedef MemoryWriterTemplate> MemoryWriter; struct ByteSpanWithPosition { Span data; size_t pos = 0; ByteSpanWithPosition(Span p_data, size_t initial_pos) : data(p_data), pos(initial_pos) {} inline void push_back(uint8_t v) { #ifdef DEBUG_ENABLED ZN_ASSERT(pos != data.size()); #endif data[pos++] = v; } }; typedef MemoryWriterTemplate MemoryWriterExistingBuffer; struct MemoryReader { Span data; size_t pos = 0; // Using network-order by default Endianess endianess = ENDIANESS_BIG_ENDIAN; MemoryReader(Span p_data, Endianess p_endianess) : data(p_data), endianess(p_endianess) {} inline uint8_t get_8() { //ERR_FAIL_COND_V(pos >= data.size(), 0); return data[pos++]; } inline uint16_t get_16() { //ERR_FAIL_COND_V(pos + 1 >= data.size(), 0); uint16_t v; if (endianess == ENDIANESS_BIG_ENDIAN) { v = (static_cast(data[pos]) << 8) | data[pos + 1]; } else { v = (static_cast(data[pos]) | static_cast(data[pos + 1]) << 8); } pos += sizeof(uint16_t); return v; } inline uint32_t get_32() { //ERR_FAIL_COND_V(pos + 3 >= data.size(), 0); uint32_t v; if (endianess == ENDIANESS_BIG_ENDIAN) { v = // (static_cast(data[pos]) << 24) | // (static_cast(data[pos + 1]) << 16) | // (static_cast(data[pos + 2]) << 8) | data[pos + 3]; } else { v = // static_cast(data[pos]) | // (static_cast(data[pos + 1]) << 8) | // (static_cast(data[pos + 2]) << 16) | (static_cast(data[pos + 3]) << 24); } pos += sizeof(uint32_t); return v; } inline uint64_t get_64() { //ERR_FAIL_COND_V(pos + 3 >= data.size(), 0); uint64_t v; if (endianess == ENDIANESS_BIG_ENDIAN) { v = // (static_cast(data[pos]) << 56) | // (static_cast(data[pos + 1]) << 48) | // (static_cast(data[pos + 2]) << 40) | // (static_cast(data[pos + 3]) << 32) | // (static_cast(data[pos + 4]) << 24) | // (static_cast(data[pos + 5]) << 16) | // (static_cast(data[pos + 6]) << 8) | // data[pos + 7]; } else { v = // static_cast(data[pos]) | // (static_cast(data[pos + 1]) << 8) | // (static_cast(data[pos + 2]) << 16) | // (static_cast(data[pos + 3]) << 24) | // (static_cast(data[pos + 4]) << 32) | // (static_cast(data[pos + 5]) << 40) | // (static_cast(data[pos + 6]) << 48) | // (static_cast(data[pos + 7]) << 56); } pos += sizeof(uint64_t); return v; } inline float get_float() { union M { uint32_t i; float f; } m; m.i = get_32(); return m.f; } inline size_t get_buffer(Span p_dst) { #ifdef DEBUG_ENABLED ZN_ASSERT(pos <= data.size()); #endif size_t end = pos + p_dst.size(); if (end > data.size()) { end = data.size(); } const size_t len = end - pos; memcpy(p_dst.data(), &data[pos], len); pos += len; return len; } // For API compatibility inline size_t get_position() const { return pos; } }; } // namespace zylann #endif // VOXEL_UTIL_SERIALIZATION_H