mirror of https://github.com/SeanOMik/libki.git
serialization: Fix inconsistencies in binary serialization
- Fixed incorrect byte alignment following bit-based types. - Boolean types are now serialized as 1 bit. - write_to/read_from methods now take an is_file argument. Types such as enums, and strings change their serialization format depending on if they're in file mode or not. - Enums now serialize the element name in file mode, and element value otherwise.
This commit is contained in:
parent
8d1b9c21be
commit
40d088efe3
|
@ -26,8 +26,8 @@ namespace pclass
|
||||||
*/
|
*/
|
||||||
bool inherits(const Type &type) const;
|
bool inherits(const Type &type) const;
|
||||||
|
|
||||||
void write_to(BitStream &stream, Value &value) const override = 0;
|
void write_to(BitStream &stream, const bool is_file, Value &value) const override = 0;
|
||||||
Value read_from(BitStream &stream) const override = 0;
|
Value read_from(BitStream &stream, const bool is_file) const override = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const IClassType *m_base_class;
|
const IClassType *m_base_class;
|
||||||
|
@ -64,20 +64,20 @@ namespace pclass
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_to(BitStream &stream, Value &value) const override
|
void write_to(BitStream &stream, const bool is_file, Value &value) const override
|
||||||
{
|
{
|
||||||
const auto &object = value.get<ClassT>();
|
const auto &object = value.get<ClassT>();
|
||||||
const auto &properties = object.get_properties();
|
const auto &properties = object.get_properties();
|
||||||
for (auto it = properties.begin(); it != properties.end(); ++it)
|
for (auto it = properties.begin(); it != properties.end(); ++it)
|
||||||
it->write_value_to(stream);
|
it->write_value_to(stream, is_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value read_from(BitStream &stream) const override
|
Value read_from(BitStream &stream, const bool is_file) const override
|
||||||
{
|
{
|
||||||
auto object = ClassT(*this, get_type_system());
|
auto object = ClassT(*this, get_type_system());
|
||||||
auto &properties = object.get_properties();
|
auto &properties = object.get_properties();
|
||||||
for (auto it = properties.begin(); it != properties.end(); ++it)
|
for (auto it = properties.begin(); it != properties.end(); ++it)
|
||||||
it->read_value_from(stream);
|
it->read_value_from(stream, is_file);
|
||||||
return Value::make_value<ClassT>(object);
|
return Value::make_value<ClassT>(object);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,8 +22,8 @@ namespace pclass
|
||||||
void set_value(enum_value_t value);
|
void set_value(enum_value_t value);
|
||||||
void set_value(const std::string &element_name);
|
void set_value(const std::string &element_name);
|
||||||
|
|
||||||
void write_to(BitStream& stream) const;
|
void write_to(BitStream &stream, const bool is_file) const;
|
||||||
void read_from(BitStream& stream);
|
void read_from(BitStream &stream, const bool is_file);
|
||||||
|
|
||||||
operator enum_value_t() const;
|
operator enum_value_t() const;
|
||||||
void operator=(enum_value_t value);
|
void operator=(enum_value_t value);
|
||||||
|
|
|
@ -49,8 +49,8 @@ namespace pclass
|
||||||
|
|
||||||
EnumType &add_element(const std::string &name, enum_value_t value);
|
EnumType &add_element(const std::string &name, enum_value_t value);
|
||||||
|
|
||||||
void write_to(BitStream &stream, Value &value) const override;
|
void write_to(BitStream &stream, const bool is_file, Value &value) const override;
|
||||||
Value read_from(BitStream &stream) const override;
|
Value read_from(BitStream &stream, const bool is_file) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Element *> m_elements;
|
std::vector<Element *> m_elements;
|
||||||
|
@ -75,17 +75,17 @@ namespace pclass
|
||||||
m_kind = Kind::ENUM;
|
m_kind = Kind::ENUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_to(BitStream &stream, Value &value) const override
|
void write_to(BitStream &stream, const bool is_file, Value &value) const override
|
||||||
{
|
{
|
||||||
auto &enum_reference = value.get<EnumT>();
|
auto &enum_reference = value.get<EnumT>();
|
||||||
auto &underlying_reference = reinterpret_cast<const underlying_type &>(enum_reference);
|
auto &underlying_reference = reinterpret_cast<const underlying_type &>(enum_reference);
|
||||||
detail::primitive_type_helper<underlying_type>::write_to(stream, underlying_reference);
|
detail::primitive_type_helper<underlying_type>::write_to(stream, is_file, underlying_reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value read_from(BitStream &stream) const override
|
Value read_from(BitStream &stream, const bool is_file) const override
|
||||||
{
|
{
|
||||||
Value read_result =
|
Value read_result =
|
||||||
detail::primitive_type_helper<underlying_type>::read_from(stream);
|
detail::primitive_type_helper<underlying_type>::read_from(stream, is_file);
|
||||||
auto underlying_value = read_result.get<underlying_type>();
|
auto underlying_value = read_result.get<underlying_type>();
|
||||||
return Value::make_value<EnumT>(
|
return Value::make_value<EnumT>(
|
||||||
static_cast<EnumT>(underlying_value)
|
static_cast<EnumT>(underlying_value)
|
||||||
|
|
|
@ -8,50 +8,60 @@ namespace pclass
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Provides implementations to PrimitiveType<ValueT>::write_to,
|
* Provides implementations to PrimitiveType<ValueT>::is_byte_based,
|
||||||
* and PrimitiveType<ValueT>::read_from.
|
* PrimitiveType<ValueT>::write_to, and PrimitiveType<ValueT>::read_from.
|
||||||
*/
|
*/
|
||||||
template <typename ValueT, typename Enable = void>
|
template <typename ValueT, typename Enable = void>
|
||||||
struct primitive_type_helper
|
struct primitive_type_helper
|
||||||
{
|
{
|
||||||
static void write_to(BitStream &stream, const ValueT &value)
|
static bool is_byte_based()
|
||||||
{
|
{
|
||||||
// Provide a compiler error if this is not specialized
|
// Provide a compiler error if this is not specialized
|
||||||
static_assert(
|
static_assert(
|
||||||
sizeof(ValueT) == 0,
|
sizeof(ValueT) == 0,
|
||||||
"Missing specialization of primitive_type_writer::write_to"
|
"Missing specialization of primitive_type_helper::is_byte_based"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value read_from(BitStream &stream)
|
static void write_to(BitStream &stream, const bool is_file, const ValueT &value)
|
||||||
{
|
{
|
||||||
// Provide a compiler error if this is not specialized
|
// Provide a compiler error if this is not specialized
|
||||||
static_assert(
|
static_assert(
|
||||||
sizeof(ValueT) == 0,
|
sizeof(ValueT) == 0,
|
||||||
"Missing specialization of PrimitiveTypeReader::read_from"
|
"Missing specialization of primitive_type_helper::write_to"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// This should be impossible to reach.
|
static Value read_from(BitStream &stream, const bool is_file)
|
||||||
throw runtime_error("Missing specialization of PrimitiveTypeReader::read_from");
|
{
|
||||||
|
// Provide a compiler error if this is not specialized
|
||||||
|
static_assert(
|
||||||
|
sizeof(ValueT) == 0,
|
||||||
|
"Missing specialization of primitive_type_helper::read_from"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialization of primitive_type_helper for integer types.
|
* Specialization of primitive_type_helper for integer types.
|
||||||
* This includes instantiations of ki::BitInteger<>.
|
|
||||||
*/
|
*/
|
||||||
template <typename ValueT>
|
template <typename ValueT>
|
||||||
struct primitive_type_helper<
|
struct primitive_type_helper<
|
||||||
ValueT,
|
ValueT,
|
||||||
typename std::enable_if<is_integral<ValueT>::value>::type
|
typename std::enable_if<std::is_integral<ValueT>::value>::type
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
static void write_to(BitStream &stream, const ValueT &value)
|
static bool is_byte_based()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_to(BitStream &stream, const bool is_file, const ValueT &value)
|
||||||
{
|
{
|
||||||
stream.write<ValueT>(value);
|
stream.write<ValueT>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value read_from(BitStream &stream)
|
static Value read_from(BitStream &stream, const bool is_file)
|
||||||
{
|
{
|
||||||
return Value::make_value<ValueT>(
|
return Value::make_value<ValueT>(
|
||||||
stream.read<ValueT>()
|
stream.read<ValueT>()
|
||||||
|
@ -60,8 +70,63 @@ namespace pclass
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialization of primitive_type_helper for floating point
|
* Specialization of primitive_type_helper for ki::BitInteger<> types.
|
||||||
* types.
|
*/
|
||||||
|
template <uint8_t N, bool Unsigned>
|
||||||
|
struct primitive_type_helper<ki::BitInteger<N, Unsigned>>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using type = ki::BitInteger<N, Unsigned>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool is_byte_based()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_to(BitStream &stream, const bool is_file, const type &value)
|
||||||
|
{
|
||||||
|
stream.write<type>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value read_from(BitStream &stream, const bool is_file)
|
||||||
|
{
|
||||||
|
return Value::make_value<type>(
|
||||||
|
stream.read<type>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialization of primitive_type_helper for boolean types.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct primitive_type_helper<bool>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using underlying_type = ki::BitInteger<1, true>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool is_byte_based()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_to(BitStream &stream, const bool is_file, const bool &value)
|
||||||
|
{
|
||||||
|
stream.write<underlying_type>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value read_from(BitStream &stream, const bool is_file)
|
||||||
|
{
|
||||||
|
return Value::make_value<bool>(
|
||||||
|
stream.read<underlying_type>() != 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialization of primitive_type_helper for floating point types.
|
||||||
*/
|
*/
|
||||||
template <typename ValueT>
|
template <typename ValueT>
|
||||||
struct primitive_type_helper<
|
struct primitive_type_helper<
|
||||||
|
@ -69,27 +134,33 @@ namespace pclass
|
||||||
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
|
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
static void write_to(BitStream &stream, const ValueT &value)
|
private:
|
||||||
|
/**
|
||||||
|
* An unsigned integer type with the same size as the floating point type
|
||||||
|
* ValueT.
|
||||||
|
*/
|
||||||
|
using uint_type = typename bits<bitsizeof<ValueT>::value>::uint_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool is_byte_based()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_to(BitStream &stream, const bool is_file, const ValueT &value)
|
||||||
{
|
{
|
||||||
// Reinterpret the reference as a reference to an integer
|
// Reinterpret the reference as a reference to an integer
|
||||||
const uint_type &v = *(
|
const uint_type &v = *(
|
||||||
reinterpret_cast<const uint_type *>(&value)
|
reinterpret_cast<const uint_type *>(&value)
|
||||||
);
|
);
|
||||||
stream.write<uint_type>(v, bitsizeof<ValueT>::value);
|
stream.write<uint_type>(v, bitsizeof<ValueT>::value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value read_from(BitStream &stream)
|
static Value read_from(BitStream &stream, const bool is_file)
|
||||||
{
|
{
|
||||||
uint_type uint_value = stream.read<uint_type>(bitsizeof<ValueT>::value);
|
uint_type uint_value = stream.read<uint_type>(bitsizeof<ValueT>::value);
|
||||||
return Value::make_value<ValueT>(*reinterpret_cast<ValueT *>(&uint_value));
|
return Value::make_value<ValueT>(*reinterpret_cast<ValueT *>(&uint_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* An unsigned integer type with the same size as the floating point type
|
|
||||||
* ValueT.
|
|
||||||
*/
|
|
||||||
using uint_type = typename bits<bitsizeof<ValueT>::value>::uint_type;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,21 +177,47 @@ namespace pclass
|
||||||
using type = std::basic_string<_Elem, _Traits, _Alloc>;
|
using type = std::basic_string<_Elem, _Traits, _Alloc>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void write_to(BitStream &stream, const type &value)
|
static bool is_byte_based()
|
||||||
{
|
{
|
||||||
// Write the length as an unsigned short
|
return true;
|
||||||
stream.write<uint16_t>(value.length());
|
}
|
||||||
|
|
||||||
|
static void write_to(BitStream &stream, const bool is_file, const type &value)
|
||||||
|
{
|
||||||
|
if (is_file)
|
||||||
|
{
|
||||||
|
// TODO: Determine how the size of the length prefix is chosen
|
||||||
|
// while in file mode
|
||||||
|
stream.write<uint8_t>(value.length() * 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write the length as an unsigned short
|
||||||
|
stream.write<uint16_t>(value.length());
|
||||||
|
}
|
||||||
|
|
||||||
// Write each character as _Elem
|
// Write each character as _Elem
|
||||||
for (auto it = value.begin(); it != value.end(); ++it)
|
for (auto it = value.begin(); it != value.end(); ++it)
|
||||||
stream.write<_Elem>(*it);
|
stream.write<_Elem>(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value read_from(BitStream &stream)
|
static Value read_from(BitStream &stream, const bool is_file)
|
||||||
{
|
{
|
||||||
// Read the length and create a new string with the correct capacity
|
std::size_t length;
|
||||||
auto length = stream.read<uint16_t>();
|
if (is_file)
|
||||||
auto value = type(length, ' ');;
|
{
|
||||||
|
// TODO: Determine how the size of the length prefix is chosen
|
||||||
|
// while in file mode
|
||||||
|
length = stream.read<uint8_t>() / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Read the length as an unsigned short
|
||||||
|
length = stream.read<uint16_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new string with the correct capacity
|
||||||
|
auto value = type(length, ' ');
|
||||||
|
|
||||||
// Read each character into the string
|
// Read each character into the string
|
||||||
for (auto it = value.begin(); it != value.end(); ++it)
|
for (auto it = value.begin(); it != value.end(); ++it)
|
||||||
|
@ -153,13 +250,18 @@ namespace pclass
|
||||||
}
|
}
|
||||||
~PrimitiveType() = default;
|
~PrimitiveType() = default;
|
||||||
|
|
||||||
void write_to(BitStream &stream, Value &value) const override
|
bool is_byte_based() const override
|
||||||
|
{
|
||||||
|
return detail::primitive_type_helper<ValueT>::is_byte_based();
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_to(BitStream &stream, const bool is_file, Value &value) const override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Value casted_value = value.as<ValueT>();
|
Value casted_value = value.as<ValueT>();
|
||||||
detail::primitive_type_helper<ValueT>::write_to(
|
detail::primitive_type_helper<ValueT>::write_to(
|
||||||
stream,
|
stream, is_file,
|
||||||
casted_value.get<ValueT>()
|
casted_value.get<ValueT>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -171,11 +273,11 @@ namespace pclass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value read_from(BitStream &stream) const override
|
Value read_from(BitStream &stream, const bool is_file) const override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return detail::primitive_type_helper<ValueT>::read_from(stream);
|
return detail::primitive_type_helper<ValueT>::read_from(stream, is_file);
|
||||||
}
|
}
|
||||||
catch (runtime_error &e)
|
catch (runtime_error &e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,14 +101,14 @@ namespace pclass
|
||||||
* @param[in] stream The stream to write to.
|
* @param[in] stream The stream to write to.
|
||||||
* @param[in] index The index of the element to retrieve the value from
|
* @param[in] index The index of the element to retrieve the value from
|
||||||
*/
|
*/
|
||||||
virtual void write_value_to(BitStream &stream, std::size_t index = 0) const;
|
virtual void write_value_to(BitStream &stream, const bool is_file, std::size_t index = 0) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a value from a BitStream into the specified element of this property.
|
* Read a value from a BitStream into the specified element of this property.
|
||||||
* @param[in] stream The stream to read from.
|
* @param[in] stream The stream to read from.
|
||||||
* @param[in] index The index of the element to read a value into.
|
* @param[in] index The index of the element to read a value into.
|
||||||
*/
|
*/
|
||||||
virtual void read_value_from(BitStream &stream, std::size_t index = 0);
|
virtual void read_value_from(BitStream &stream, const bool is_file, std::size_t index = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const PropertyClass *m_instance;
|
const PropertyClass *m_instance;
|
||||||
|
|
|
@ -55,6 +55,11 @@ namespace pclass
|
||||||
hash_t get_hash() const;
|
hash_t get_hash() const;
|
||||||
Kind get_kind() const;
|
Kind get_kind() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Whether or not this type works in bytes, rather than bits.
|
||||||
|
*/
|
||||||
|
virtual bool is_byte_based() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TypeSystem used to define this Type instance.
|
* The TypeSystem used to define this Type instance.
|
||||||
*/
|
*/
|
||||||
|
@ -71,14 +76,14 @@ namespace pclass
|
||||||
* @param[in] stream The stream to write to.
|
* @param[in] stream The stream to write to.
|
||||||
* @param[in] value The value to write to the stream.
|
* @param[in] value The value to write to the stream.
|
||||||
*/
|
*/
|
||||||
virtual void write_to(BitStream &stream, Value &value) const;
|
virtual void write_to(BitStream &stream, const bool is_file, Value &value) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a value of this type from a BitStream.
|
* Read a value of this type from a BitStream.
|
||||||
* @param stream[in] The stream to read from.
|
* @param stream[in] The stream to read from.
|
||||||
* @returns The value read from the stream.
|
* @returns The value read from the stream.
|
||||||
*/
|
*/
|
||||||
virtual Value read_from(BitStream &stream) const;
|
virtual Value read_from(BitStream &stream, const bool is_file) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Kind m_kind;
|
Kind m_kind;
|
||||||
|
|
|
@ -56,21 +56,42 @@ namespace pclass
|
||||||
m_value = value;
|
m_value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enum::set_value(const std::string& element_name)
|
void Enum::set_value(const std::string &element_name)
|
||||||
{
|
{
|
||||||
m_value = get_type().get_element(element_name).get_value();
|
m_value = get_type().get_element(element_name).get_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enum::write_to(BitStream& stream) const
|
void Enum::write_to(BitStream& stream, const bool is_file) const
|
||||||
{
|
{
|
||||||
detail::primitive_type_helper<enum_value_t>::write_to(stream, m_value);
|
if (is_file)
|
||||||
|
{
|
||||||
|
// Write the element name
|
||||||
|
const auto &name = get_type().get_element(m_value).get_name();
|
||||||
|
detail::primitive_type_helper<std::string>::write_to(stream, is_file, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write the element value
|
||||||
|
detail::primitive_type_helper<enum_value_t>::write_to(stream, is_file, m_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enum::read_from(BitStream& stream)
|
void Enum::read_from(BitStream& stream, const bool is_file)
|
||||||
{
|
{
|
||||||
const auto value = detail::primitive_type_helper<enum_value_t>
|
if (is_file)
|
||||||
::read_from(stream).get<enum_value_t>();
|
{
|
||||||
set_value(value);
|
// Set the value using the element name
|
||||||
|
const auto name = detail::primitive_type_helper<std::string>
|
||||||
|
::read_from(stream, is_file).get<std::string>();
|
||||||
|
set_value(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set the value using the element value
|
||||||
|
const auto value = detail::primitive_type_helper<enum_value_t>
|
||||||
|
::read_from(stream, is_file).get<enum_value_t>();
|
||||||
|
set_value(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Enum::operator enum_value_t() const
|
Enum::operator enum_value_t() const
|
||||||
|
|
|
@ -109,16 +109,16 @@ namespace pclass
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnumType::write_to(BitStream &stream, Value &value) const
|
void EnumType::write_to(BitStream &stream, const bool is_file, Value &value) const
|
||||||
{
|
{
|
||||||
// Get an Enum reference and use it to write to the stream
|
// Get an Enum reference and use it to write to the stream
|
||||||
value.as<Enum>().get<Enum>().write_to(stream);
|
value.as<Enum>().get<Enum>().write_to(stream, is_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value EnumType::read_from(BitStream &stream) const
|
Value EnumType::read_from(BitStream &stream, const bool is_file) const
|
||||||
{
|
{
|
||||||
auto value = Enum(*this);
|
auto value = Enum(*this);
|
||||||
value.read_from(stream);
|
value.read_from(stream, is_file);
|
||||||
return Value::make_value<Enum>(value);
|
return Value::make_value<Enum>(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,19 +81,19 @@ namespace pclass
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IProperty::write_value_to(BitStream& stream, const std::size_t index) const
|
void IProperty::write_value_to(BitStream &stream, const bool is_file, const std::size_t index) const
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= get_element_count())
|
if (index < 0 || index >= get_element_count())
|
||||||
throw runtime_error("Index out of bounds.");
|
throw runtime_error("Index out of bounds.");
|
||||||
auto ref_value = get_value(index);
|
auto ref_value = get_value(index);
|
||||||
get_type().write_to(stream, ref_value);
|
get_type().write_to(stream, is_file, ref_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IProperty::read_value_from(BitStream& stream, const std::size_t index)
|
void IProperty::read_value_from(BitStream &stream, const bool is_file, const std::size_t index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= get_element_count())
|
if (index < 0 || index >= get_element_count())
|
||||||
throw runtime_error("Index out of bounds.");
|
throw runtime_error("Index out of bounds.");
|
||||||
set_value(get_type().read_from(stream), index);
|
set_value(get_type().read_from(stream, is_file), index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,14 +38,19 @@ namespace pclass
|
||||||
return m_type_system;
|
return m_type_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Type::write_to(BitStream &stream, Value &value) const
|
bool Type::is_byte_based() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Type::write_to(BitStream &stream, const bool is_file, Value &value) const
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "Type '" << m_name << "' does not implement Type::write_to.";
|
oss << "Type '" << m_name << "' does not implement Type::write_to.";
|
||||||
throw runtime_error(oss.str());
|
throw runtime_error(oss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Value Type::read_from(BitStream &stream) const
|
Value Type::read_from(BitStream &stream, const bool is_file) const
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "Type '" << m_name << "' does not implement Type::read_from.";
|
oss << "Type '" << m_name << "' does not implement Type::read_from.";
|
||||||
|
|
|
@ -70,7 +70,10 @@ namespace net
|
||||||
on_message(message);
|
on_message(message);
|
||||||
else
|
else
|
||||||
on_invalid_message(InvalidDMLMessageErrorCode::INSUFFICIENT_ACCESS);
|
on_invalid_message(InvalidDMLMessageErrorCode::INSUFFICIENT_ACCESS);
|
||||||
delete message;
|
|
||||||
|
// WARNING: Refactor, and utilize smart pointers to avoid memory leaks in DML messages.
|
||||||
|
// The following line breaks asynchronous code.
|
||||||
|
// delete message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ namespace serialization
|
||||||
const auto compression_header_pos = stream.tell();
|
const auto compression_header_pos = stream.tell();
|
||||||
if (FLAG_IS_SET(m_flags, flags::COMPRESSED))
|
if (FLAG_IS_SET(m_flags, flags::COMPRESSED))
|
||||||
{
|
{
|
||||||
stream.write<bool>(false);
|
if (m_is_file)
|
||||||
|
stream.write<bool>(false);
|
||||||
stream.write<uint32_t>(0);
|
stream.write<uint32_t>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +103,8 @@ namespace serialization
|
||||||
// Write the compression header
|
// Write the compression header
|
||||||
const auto use_compression = compressed.size() < size_bytes;
|
const auto use_compression = compressed.size() < size_bytes;
|
||||||
stream.seek(compression_header_pos);
|
stream.seek(compression_header_pos);
|
||||||
stream.write<bool>(use_compression);
|
if (m_is_file)
|
||||||
|
stream.write<bool>(use_compression);
|
||||||
stream.write<uint32_t>(size_bytes);
|
stream.write<uint32_t>(size_bytes);
|
||||||
|
|
||||||
// Write the compressed data
|
// Write the compressed data
|
||||||
|
@ -157,14 +159,14 @@ namespace serialization
|
||||||
stream.write(size_bits);
|
stream.write(size_bits);
|
||||||
stream.seek(end_pos);
|
stream.seek(end_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-align the stream so that our position lies on a byte
|
|
||||||
// TODO: Look into how/when the serialization re-aligns as this may not be the right place
|
|
||||||
stream.seek(BitStream::stream_pos(stream.tell().as_bytes(), 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinarySerializer::save_property(const pclass::IProperty &prop, BitStream &stream) const
|
void BinarySerializer::save_property(const pclass::IProperty &prop, BitStream &stream) const
|
||||||
{
|
{
|
||||||
|
// Determine if we need to re-align the stream so that our position lies on a byte
|
||||||
|
if (prop.get_type().is_byte_based() || prop.is_dynamic() || m_is_file)
|
||||||
|
stream.seek(BitStream::stream_pos(stream.tell().as_bytes(), 0));
|
||||||
|
|
||||||
// Remember where we started writing the property data
|
// Remember where we started writing the property data
|
||||||
const auto start_pos = stream.tell();
|
const auto start_pos = stream.tell();
|
||||||
|
|
||||||
|
@ -178,7 +180,19 @@ namespace serialization
|
||||||
|
|
||||||
// If the property is dynamic, write the element count
|
// If the property is dynamic, write the element count
|
||||||
if (prop.is_dynamic())
|
if (prop.is_dynamic())
|
||||||
stream.write<uint32_t>(prop.get_element_count());
|
{
|
||||||
|
if (m_is_file)
|
||||||
|
{
|
||||||
|
// TODO: Determine how the size of the element count is chosen
|
||||||
|
// while in file mode
|
||||||
|
stream.write<uint8_t>(prop.get_element_count() * 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write the element count as an unsigned int
|
||||||
|
stream.write<uint32_t>(prop.get_element_count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto i = 0; i < prop.get_element_count(); ++i)
|
for (auto i = 0; i < prop.get_element_count(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -189,7 +203,7 @@ namespace serialization
|
||||||
save_object(prop.get_object(i), stream);
|
save_object(prop.get_object(i), stream);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
prop.write_value_to(stream, i);
|
prop.write_value_to(stream, m_is_file, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish writing the property header by writing the length
|
// Finish writing the property header by writing the length
|
||||||
|
@ -226,7 +240,9 @@ namespace serialization
|
||||||
if (FLAG_IS_SET(m_flags, flags::COMPRESSED))
|
if (FLAG_IS_SET(m_flags, flags::COMPRESSED))
|
||||||
{
|
{
|
||||||
// Read the compression header
|
// Read the compression header
|
||||||
const auto use_compression = segment_stream.read<bool>();
|
bool use_compression = true;
|
||||||
|
if (m_is_file)
|
||||||
|
use_compression = segment_stream.read<bool>();
|
||||||
const auto uncompressed_size = segment_stream.read<uint32_t>();
|
const auto uncompressed_size = segment_stream.read<uint32_t>();
|
||||||
|
|
||||||
// Work out how much data is available after the compression header
|
// Work out how much data is available after the compression header
|
||||||
|
@ -305,14 +321,15 @@ namespace serialization
|
||||||
stream.tell(), object_size
|
stream.tell(), object_size
|
||||||
);
|
);
|
||||||
auto object_stream = BitStream(*object_buffer);
|
auto object_stream = BitStream(*object_buffer);
|
||||||
stream.seek(stream.tell() + object_size, false);
|
|
||||||
|
|
||||||
// Instead of loading properties sequentially, the file format specifies
|
// Instead of loading properties sequentially, the file format specifies
|
||||||
// the hash of a property before writing its value, so we just need to
|
// the hash of a property before writing its value, so we just need to
|
||||||
// iterate for how ever many properties the object has declared.
|
// iterate over how ever many bits were specified as the object size.
|
||||||
for (std::size_t i = 0;
|
while (object_stream.tell().as_bits() < object_size)
|
||||||
i < properties.get_property_count(); i++)
|
|
||||||
{
|
{
|
||||||
|
// Re-align the object stream so that our position lies on a byte
|
||||||
|
object_stream.seek(BitStream::stream_pos(object_stream.tell().as_bytes(), 0));
|
||||||
|
|
||||||
// Read the property's size, and create a new BitBufferSegment to
|
// Read the property's size, and create a new BitBufferSegment to
|
||||||
// ensure that data is only read from inside this region.
|
// ensure that data is only read from inside this region.
|
||||||
const auto property_size =
|
const auto property_size =
|
||||||
|
@ -321,14 +338,21 @@ namespace serialization
|
||||||
object_stream.tell(), property_size
|
object_stream.tell(), property_size
|
||||||
);
|
);
|
||||||
auto property_stream = BitStream(*property_buffer);
|
auto property_stream = BitStream(*property_buffer);
|
||||||
object_stream.seek(object_stream.tell() + property_size, false);
|
|
||||||
|
|
||||||
// Get the property to load based on it's hash, and then load
|
// Get the property to load based on it's hash, and then load
|
||||||
// it's value.
|
// it's value.
|
||||||
const auto property_hash = property_stream.read<uint32_t>();
|
const auto property_hash = property_stream.read<uint32_t>();
|
||||||
auto &prop = properties.get_property(property_hash);
|
auto &prop = properties.get_property(property_hash);
|
||||||
load_property(prop, property_stream);
|
load_property(prop, property_stream);
|
||||||
|
|
||||||
|
// Seek out the end of the property in the object stream
|
||||||
|
auto byte_pos = object_stream.tell().as_bytes() + property_stream.tell().as_bytes();
|
||||||
|
object_stream.seek(BitStream::stream_pos(byte_pos, 0), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seek out the end of the object in the stream
|
||||||
|
auto byte_pos = stream.tell().as_bytes() + object_stream.tell().as_bytes();
|
||||||
|
stream.seek(BitStream::stream_pos(byte_pos, 0), false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -341,17 +365,29 @@ namespace serialization
|
||||||
// All properties on this object have been set now, so call the
|
// All properties on this object have been set now, so call the
|
||||||
// created handler on the object
|
// created handler on the object
|
||||||
dest->on_created();
|
dest->on_created();
|
||||||
|
|
||||||
// Re-align the stream so that our position lies on a byte
|
|
||||||
stream.seek(BitStream::stream_pos(stream.tell().as_bytes(), 0), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinarySerializer::load_property(pclass::IProperty &prop, BitStream &stream) const
|
void BinarySerializer::load_property(pclass::IProperty &prop, BitStream &stream) const
|
||||||
{
|
{
|
||||||
|
// Determine if we need to re-align the stream so that our position lies on a byte
|
||||||
|
if (prop.get_type().is_byte_based() || prop.is_dynamic())
|
||||||
|
stream.seek(BitStream::stream_pos(stream.tell().as_bytes(), 0));
|
||||||
|
|
||||||
// If the property is dynamic, we need to load the element count
|
// If the property is dynamic, we need to load the element count
|
||||||
if (prop.is_dynamic())
|
if (prop.is_dynamic())
|
||||||
{
|
{
|
||||||
const auto element_count = stream.read<uint32_t>();
|
std::size_t element_count;
|
||||||
|
if (m_is_file)
|
||||||
|
{
|
||||||
|
// TODO: Determine how the size of the element count is chosen
|
||||||
|
// while in file mode
|
||||||
|
element_count = stream.read<uint8_t>() / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Load the element count as an unsigned int
|
||||||
|
element_count = stream.read<uint32_t>();
|
||||||
|
}
|
||||||
prop.set_element_count(element_count);
|
prop.set_element_count(element_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +402,7 @@ namespace serialization
|
||||||
prop.set_object(object, i);
|
prop.set_object(object, i);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
prop.read_value_from(stream, i);
|
prop.read_value_from(stream, m_is_file, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,24 +54,24 @@ struct Vector3D
|
||||||
m_z == that.m_z;
|
m_z == that.m_z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_to(BitStream &stream) const
|
void write_to(BitStream &stream, const bool is_file) const
|
||||||
{
|
{
|
||||||
pclass::detail::primitive_type_helper<float>
|
pclass::detail::primitive_type_helper<float>
|
||||||
::write_to(stream, m_x);
|
::write_to(stream, is_file, m_x);
|
||||||
pclass::detail::primitive_type_helper<float>
|
pclass::detail::primitive_type_helper<float>
|
||||||
::write_to(stream, m_y);
|
::write_to(stream, is_file, m_y);
|
||||||
pclass::detail::primitive_type_helper<float>
|
pclass::detail::primitive_type_helper<float>
|
||||||
::write_to(stream, m_z);
|
::write_to(stream, is_file, m_z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_from(BitStream &stream)
|
void read_from(BitStream &stream, const bool is_file)
|
||||||
{
|
{
|
||||||
m_x = pclass::detail::primitive_type_helper<float>
|
m_x = pclass::detail::primitive_type_helper<float>
|
||||||
::read_from(stream).get<float>();
|
::read_from(stream, is_file).get<float>();
|
||||||
m_y = pclass::detail::primitive_type_helper<float>
|
m_y = pclass::detail::primitive_type_helper<float>
|
||||||
::read_from(stream).get<float>();
|
::read_from(stream, is_file).get<float>();
|
||||||
m_z = pclass::detail::primitive_type_helper<float>
|
m_z = pclass::detail::primitive_type_helper<float>
|
||||||
::read_from(stream).get<float>();
|
::read_from(stream, is_file).get<float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -93,15 +93,20 @@ namespace detail
|
||||||
template <>
|
template <>
|
||||||
struct primitive_type_helper<Vector3D>
|
struct primitive_type_helper<Vector3D>
|
||||||
{
|
{
|
||||||
static void write_to(BitStream &stream, const Vector3D &value)
|
static bool is_byte_based()
|
||||||
{
|
{
|
||||||
value.write_to(stream);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value read_from(BitStream &stream)
|
static void write_to(BitStream &stream, const bool is_file, const Vector3D &value)
|
||||||
|
{
|
||||||
|
value.write_to(stream, is_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value read_from(BitStream &stream, const bool is_file)
|
||||||
{
|
{
|
||||||
Vector3D value;
|
Vector3D value;
|
||||||
value.read_from(stream);
|
value.read_from(stream, is_file);
|
||||||
return Value::make_value<Vector3D>(value);
|
return Value::make_value<Vector3D>(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue