From b8b880a91d379a7b9b618ad82bf68f46dbf022d7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 24 May 2020 01:20:48 -0400 Subject: [PATCH] Extends encoding to handle vector, floats and doubles. --- Reflection/Struct.cpp | 56 ++++++++++++++++++++++++++++++++----------- Reflection/Struct.hpp | 5 ++-- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index 34d0b2c7e..330a4f545 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -312,22 +312,23 @@ std::vector Reflection::Struct::serialise() const { }; auto append = [push_name, this] (std::vector &result, const std::string &key, const std::string &output_name, const std::type_info *type, size_t offset) { - auto push_int = [push_name, &result, &output_name] (uint8_t type, auto x) { - result.push_back(type); - push_name(result, output_name); + auto push_int = [push_name, &result, &output_name] (auto x) { for(size_t c = 0; c < sizeof(x); ++c) result.push_back(uint8_t((x) >> (8 * c))); }; - auto push_string = [push_name, &result, &output_name] (const std::string &text) { + auto push_named_int = [push_int, push_name, &result, &output_name] (uint8_t type, auto x) { + result.push_back(type); + push_name(result, output_name); + push_int(x); + }; + + auto push_string = [push_int, push_name, &result, &output_name] (const std::string &text) { result.push_back(0x02); push_name(result, output_name); const uint32_t string_length = uint32_t(text.size() + 1); - result.push_back(uint8_t(string_length)); - result.push_back(uint8_t(string_length >> 8)); - result.push_back(uint8_t(string_length >> 16)); - result.push_back(uint8_t(string_length >> 24)); + push_int(string_length); std::copy(text.begin(), text.end(), std::back_inserter(result)); result.push_back(0); }; @@ -352,23 +353,50 @@ std::vector Reflection::Struct::serialise() const { // Test for ints that will safely convert to an int32. int32_t int32; if(Reflection::get(*this, key, int32, offset)) { - push_int(0x10, int32); + push_named_int(0x10, int32); return; } // Test for ints that can be converted to an int64. int64_t int64; if(Reflection::get(*this, key, int64, offset)) { - push_int(0x12, int64); + push_named_int(0x12, int64); return; } - /* All ints should now be dealt with. */ - - // There's only one potential float type: a double. + // There's only one BSON float type: a double. double float64; if(Reflection::get(*this, key, float64, offset)) { - // TODO: place as little-endian IEEE 754-2008. + result.push_back(0x01); + push_name(result, output_name); + + // The following declines to assume an internal representation + // for doubles, constructing IEEE 708 from first principles. + // Which is probably absurd given how often I've assumed + // e.g. two's complement. + int exponent; + const double mantissa = frexp(fabs(float64), &exponent); + exponent += 1022; + const uint64_t integer_mantissa = + static_cast(mantissa * 9007199254740992.0); + const uint64_t binary64 = + ((float64 < 0) ? 0x8000'0000'0000'0000 : 0) | + (integer_mantissa & 0x000f'ffff'ffff'ffff) | + (static_cast(exponent) << 52); + push_int(binary64); + + return; + } + + // Store std::vectors as binary data. + if(*type == typeid(std::vector)) { + result.push_back(0x05); + push_name(result, output_name); + + auto source = reinterpret_cast *>(get(key)); + push_int(uint32_t(source->size())); + result.push_back(0x00); + std::copy(source->begin(), source->end(), std::back_inserter(result)); return; } diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index f777f1a16..703e39ad1 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -295,8 +296,8 @@ template class StructImpl: public Struct { private: template bool declare_reflectable(Type *t, const std::string &name) { - Reflection::Struct *const str = static_cast(t); - if(str) { + if constexpr (std::is_base_of::value) { + Reflection::Struct *const str = static_cast(t); declare_emplace(str, name); return true; }