2018-06-23 14:35:43 +02:00
|
|
|
#ifndef REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H
|
|
|
|
#define REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \file reflector.h
|
|
|
|
* \brief Contains BinaryReader and BinaryWriter supporting binary (de)serialization
|
|
|
|
* of primitive and custom types.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "../traits.h"
|
|
|
|
|
2018-06-26 00:10:09 +02:00
|
|
|
#include <c++utilities/conversion/conversionexception.h>
|
2018-06-23 14:35:43 +02:00
|
|
|
#include <c++utilities/io/binaryreader.h>
|
|
|
|
#include <c++utilities/io/binarywriter.h>
|
|
|
|
|
2018-06-26 00:10:09 +02:00
|
|
|
#include <any>
|
2018-06-23 14:35:43 +02:00
|
|
|
#include <limits>
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
#include <tuple>
|
|
|
|
|
|
|
|
namespace ReflectiveRapidJSON {
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief The AdaptedBinarySerializable class allows considering 3rd party classes as serializable.
|
|
|
|
*/
|
|
|
|
template <typename T> struct AdaptedBinarySerializable : public Traits::Bool<false> {
|
|
|
|
static constexpr const char *name = "AdaptedBinarySerializable";
|
|
|
|
static constexpr const char *qualifiedName = "ReflectiveRapidJSON::AdaptedBinarySerializable";
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Type> struct BinarySerializable;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief The BinaryReflector namespace contains BinaryReader and BinaryWriter for automatic binary (de)serialization.
|
|
|
|
*/
|
|
|
|
namespace BinaryReflector {
|
|
|
|
|
|
|
|
// define traits to distinguish between "built-in" types like int, std::string, std::vector, ... and custom structs/classes
|
|
|
|
template <typename Type>
|
2019-03-13 19:10:29 +01:00
|
|
|
using IsBuiltInType = Traits::Any<Traits::IsAnyOf<Type, char, std::uint8_t, bool, std::string, std::int16_t, std::uint16_t, std::int32_t,
|
|
|
|
std::uint32_t, std::int64_t, std::uint64_t, float, double>,
|
2018-06-26 00:10:09 +02:00
|
|
|
Traits::IsIteratable<Type>, Traits::IsSpecializingAnyOf<Type, std::pair, std::unique_ptr, std::shared_ptr>, std::is_enum<Type>>;
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type> using IsCustomType = Traits::Not<IsBuiltInType<Type>>;
|
|
|
|
|
|
|
|
class BinaryDeserializer;
|
|
|
|
class BinarySerializer;
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> void readCustomType(BinaryDeserializer &deserializer, Type &customType);
|
|
|
|
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> void writeCustomType(BinarySerializer &serializer, const Type &customType);
|
|
|
|
|
2019-06-10 22:46:06 +02:00
|
|
|
class BinaryDeserializer : public CppUtilities::BinaryReader {
|
2018-06-23 14:35:43 +02:00
|
|
|
public:
|
|
|
|
BinaryDeserializer(std::istream *stream);
|
|
|
|
|
2019-06-10 22:46:06 +02:00
|
|
|
using CppUtilities::BinaryReader::read;
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void read(Type &pair);
|
2018-06-26 00:10:09 +02:00
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::unique_ptr>> * = nullptr> void read(Type &pair);
|
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> * = nullptr> void read(Type &pair);
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> * = nullptr> void read(Type &iteratable);
|
2018-10-29 23:23:56 +01:00
|
|
|
template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> * = nullptr> void read(Type &iteratable);
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type,
|
2018-10-29 23:23:56 +01:00
|
|
|
Traits::EnableIf<IsIteratableExceptString<Type>,
|
|
|
|
Traits::None<IsMapOrHash<Type>, IsMultiMapOrHash<Type>, Traits::All<IsArray<Type>, Traits::IsResizable<Type>>>> * = nullptr>
|
2018-06-23 14:35:43 +02:00
|
|
|
void read(Type &iteratable);
|
|
|
|
template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void read(Type &customType);
|
|
|
|
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> void read(Type &customType);
|
2018-06-26 00:10:09 +02:00
|
|
|
|
2019-03-13 19:10:29 +01:00
|
|
|
std::unordered_map<std::uint64_t, std::any> m_pointer;
|
2018-06-23 14:35:43 +02:00
|
|
|
};
|
|
|
|
|
2019-06-10 22:46:06 +02:00
|
|
|
class BinarySerializer : public CppUtilities::BinaryWriter {
|
2018-06-23 14:35:43 +02:00
|
|
|
public:
|
|
|
|
BinarySerializer(std::ostream *stream);
|
|
|
|
|
2019-06-10 22:46:06 +02:00
|
|
|
using CppUtilities::BinaryWriter::write;
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void write(const Type &pair);
|
2018-06-26 00:10:09 +02:00
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr>> * = nullptr> void write(const Type &pointer);
|
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::shared_ptr>> * = nullptr> void write(const Type &pointer);
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type, Traits::EnableIf<IsIteratableExceptString<Type>, Traits::HasSize<Type>> * = nullptr> void write(const Type &iteratable);
|
|
|
|
template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void write(const Type &customType);
|
|
|
|
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> void write(const Type &customType);
|
2018-06-26 00:10:09 +02:00
|
|
|
|
2019-03-13 19:10:29 +01:00
|
|
|
std::unordered_map<std::uint64_t, bool> m_pointer;
|
2018-06-23 14:35:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
inline BinaryDeserializer::BinaryDeserializer(std::istream *stream)
|
2019-06-10 22:46:06 +02:00
|
|
|
: CppUtilities::BinaryReader(stream)
|
2018-06-23 14:35:43 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> *> void BinaryDeserializer::read(Type &pair)
|
|
|
|
{
|
|
|
|
read(pair.first);
|
|
|
|
read(pair.second);
|
|
|
|
}
|
|
|
|
|
2018-06-26 00:10:09 +02:00
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::unique_ptr>> *> void BinaryDeserializer::read(Type &pointer)
|
|
|
|
{
|
|
|
|
if (!readBool()) {
|
|
|
|
pointer.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pointer = std::make_unique<typename Type::element_type>();
|
|
|
|
read(*pointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> *> void BinaryDeserializer::read(Type &pointer)
|
|
|
|
{
|
2019-03-08 17:40:08 +01:00
|
|
|
auto mode = readByte();
|
|
|
|
if (!mode) {
|
2018-06-26 00:10:09 +02:00
|
|
|
// pointer not set
|
|
|
|
pointer.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-08 17:40:08 +01:00
|
|
|
const auto id = (mode & 0x4) ? readUInt64BE() : readVariableLengthUIntBE(); // the 3rd bit being flagged indicates a big ID
|
|
|
|
if ((mode & 0x3) == 1) {
|
2018-06-26 00:10:09 +02:00
|
|
|
// first occurence: make a new pointer
|
|
|
|
m_pointer[id] = pointer = std::make_shared<typename Type::element_type>();
|
|
|
|
read(*pointer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// further occurences: copy previous pointer
|
|
|
|
try {
|
|
|
|
pointer = std::any_cast<Type>(m_pointer[id]);
|
2019-04-19 22:24:32 +02:00
|
|
|
} catch (const std::bad_any_cast &) {
|
2019-06-10 22:46:06 +02:00
|
|
|
throw CppUtilities::ConversionException("Referenced pointer type does not match");
|
2018-06-26 00:10:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> *> void BinaryDeserializer::read(Type &iteratable)
|
|
|
|
{
|
|
|
|
const auto size = readVariableLengthUIntBE();
|
|
|
|
iteratable.resize(size);
|
|
|
|
for (auto &element : iteratable) {
|
|
|
|
read(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-29 23:23:56 +01:00
|
|
|
template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> *> void BinaryDeserializer::read(Type &iteratable)
|
2018-06-23 14:35:43 +02:00
|
|
|
{
|
|
|
|
const auto size = readVariableLengthUIntBE();
|
|
|
|
for (size_t i = 0; i != size; ++i) {
|
|
|
|
std::pair<typename std::remove_const<typename Type::value_type::first_type>::type, typename Type::value_type::second_type> value;
|
|
|
|
read(value);
|
|
|
|
iteratable.emplace(std::move(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type,
|
2018-10-29 23:23:56 +01:00
|
|
|
Traits::EnableIf<IsIteratableExceptString<Type>,
|
|
|
|
Traits::None<IsMapOrHash<Type>, IsMultiMapOrHash<Type>, Traits::All<IsArray<Type>, Traits::IsResizable<Type>>>> *>
|
2018-06-23 14:35:43 +02:00
|
|
|
void BinaryDeserializer::read(Type &iteratable)
|
|
|
|
{
|
|
|
|
const auto size = readVariableLengthUIntBE();
|
|
|
|
for (size_t i = 0; i != size; ++i) {
|
|
|
|
typename Type::value_type value;
|
|
|
|
read(value);
|
|
|
|
iteratable.emplace(std::move(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<std::is_enum<Type>> *> void BinaryDeserializer::read(Type &enumValue)
|
|
|
|
{
|
|
|
|
typename std::underlying_type<Type>::type value;
|
|
|
|
read(value);
|
|
|
|
enumValue = static_cast<Type>(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> void BinaryDeserializer::read(Type &customType)
|
|
|
|
{
|
|
|
|
readCustomType(*this, customType);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline BinarySerializer::BinarySerializer(std::ostream *stream)
|
2019-06-10 22:46:06 +02:00
|
|
|
: CppUtilities::BinaryWriter(stream)
|
2018-06-23 14:35:43 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> *> void BinarySerializer::write(const Type &pair)
|
|
|
|
{
|
|
|
|
write(pair.first);
|
|
|
|
write(pair.second);
|
|
|
|
}
|
|
|
|
|
2018-06-26 00:10:09 +02:00
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr>> *> void BinarySerializer::write(const Type &pointer)
|
|
|
|
{
|
|
|
|
const bool hasValue = pointer != nullptr;
|
|
|
|
writeBool(hasValue);
|
|
|
|
if (hasValue) {
|
|
|
|
write(*pointer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::shared_ptr>> *> void BinarySerializer::write(const Type &pointer)
|
|
|
|
{
|
|
|
|
if (pointer == nullptr) {
|
|
|
|
writeByte(0);
|
|
|
|
return;
|
|
|
|
}
|
2019-03-08 17:44:20 +01:00
|
|
|
const auto id = reinterpret_cast<std::uintptr_t>(pointer.get());
|
2019-03-08 17:40:08 +01:00
|
|
|
const auto bigId = id >= 0x80000000000000;
|
2018-06-26 00:10:09 +02:00
|
|
|
auto &alreadyWritten = m_pointer[id];
|
2019-03-13 19:10:29 +01:00
|
|
|
std::uint8_t mode = alreadyWritten ? 2 : 1;
|
2019-03-08 17:40:08 +01:00
|
|
|
if (bigId) {
|
|
|
|
mode = mode | 0x4; // "flag" 3rd bit to indicate big ID
|
|
|
|
}
|
|
|
|
writeByte(mode);
|
|
|
|
if (bigId) {
|
|
|
|
writeUInt64BE(id);
|
|
|
|
} else {
|
|
|
|
writeVariableLengthUIntBE(id);
|
|
|
|
}
|
2018-06-26 00:10:09 +02:00
|
|
|
if (!alreadyWritten) {
|
|
|
|
alreadyWritten = true;
|
|
|
|
write(*pointer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-23 14:35:43 +02:00
|
|
|
template <typename Type, Traits::EnableIf<IsIteratableExceptString<Type>, Traits::HasSize<Type>> *>
|
|
|
|
void BinarySerializer::write(const Type &iteratable)
|
|
|
|
{
|
|
|
|
writeVariableLengthUIntBE(iteratable.size());
|
|
|
|
for (const auto &element : iteratable) {
|
|
|
|
write(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<std::is_enum<Type>> *> void BinarySerializer::write(const Type &enumValue)
|
|
|
|
{
|
|
|
|
write(static_cast<typename std::underlying_type<Type>::type>(enumValue));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> void BinarySerializer::write(const Type &customType)
|
|
|
|
{
|
|
|
|
writeCustomType(*this, customType);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace BinaryReflector
|
|
|
|
} // namespace ReflectiveRapidJSON
|
|
|
|
|
|
|
|
#endif // REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H
|