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:
pythonology 2019-06-02 22:38:29 -04:00
parent 8d1b9c21be
commit 40d088efe3
13 changed files with 279 additions and 102 deletions

View File

@ -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);
} }
}; };

View File

@ -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);

View File

@ -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)

View File

@ -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)
{ {

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);
} }
} }

View File

@ -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);
} }
} }
} }

View File

@ -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.";

View File

@ -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;
} }
} }
} }

View File

@ -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);
} }
} }
} }

View File

@ -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);
} }
}; };