diff --git a/include/ki/pclass/ClassType.h b/include/ki/pclass/ClassType.h index 22521e6..5aa1a30 100644 --- a/include/ki/pclass/ClassType.h +++ b/include/ki/pclass/ClassType.h @@ -26,8 +26,8 @@ namespace pclass */ bool inherits(const Type &type) const; - void write_to(BitStream &stream, Value &value) const override = 0; - Value read_from(BitStream &stream) const override = 0; + void write_to(BitStream &stream, const bool is_file, Value &value) const override = 0; + Value read_from(BitStream &stream, const bool is_file) const override = 0; private: 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(); const auto &properties = object.get_properties(); 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 &properties = object.get_properties(); 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(object); } }; diff --git a/include/ki/pclass/Enum.h b/include/ki/pclass/Enum.h index 395f54d..b7fa9ef 100644 --- a/include/ki/pclass/Enum.h +++ b/include/ki/pclass/Enum.h @@ -22,8 +22,8 @@ namespace pclass void set_value(enum_value_t value); void set_value(const std::string &element_name); - void write_to(BitStream& stream) const; - void read_from(BitStream& stream); + void write_to(BitStream &stream, const bool is_file) const; + void read_from(BitStream &stream, const bool is_file); operator enum_value_t() const; void operator=(enum_value_t value); diff --git a/include/ki/pclass/EnumType.h b/include/ki/pclass/EnumType.h index cb8e284..65fdf9c 100644 --- a/include/ki/pclass/EnumType.h +++ b/include/ki/pclass/EnumType.h @@ -49,8 +49,8 @@ namespace pclass EnumType &add_element(const std::string &name, enum_value_t value); - void write_to(BitStream &stream, Value &value) const override; - Value read_from(BitStream &stream) const override; + void write_to(BitStream &stream, const bool is_file, Value &value) const override; + Value read_from(BitStream &stream, const bool is_file) const override; private: std::vector m_elements; @@ -75,17 +75,17 @@ namespace pclass 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(); auto &underlying_reference = reinterpret_cast(enum_reference); - detail::primitive_type_helper::write_to(stream, underlying_reference); + detail::primitive_type_helper::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 = - detail::primitive_type_helper::read_from(stream); + detail::primitive_type_helper::read_from(stream, is_file); auto underlying_value = read_result.get(); return Value::make_value( static_cast(underlying_value) diff --git a/include/ki/pclass/PrimitiveType.h b/include/ki/pclass/PrimitiveType.h index ad29185..f327b69 100644 --- a/include/ki/pclass/PrimitiveType.h +++ b/include/ki/pclass/PrimitiveType.h @@ -8,60 +8,125 @@ namespace pclass namespace detail { /** - * Provides implementations to PrimitiveType::write_to, - * and PrimitiveType::read_from. + * Provides implementations to PrimitiveType::is_byte_based, + * PrimitiveType::write_to, and PrimitiveType::read_from. */ template 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 static_assert( 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 static_assert( sizeof(ValueT) == 0, - "Missing specialization of PrimitiveTypeReader::read_from" + "Missing specialization of primitive_type_helper::write_to" ); + } - // This should be impossible to reach. - throw runtime_error("Missing specialization of PrimitiveTypeReader::read_from"); + static Value read_from(BitStream &stream, const bool is_file) + { + // 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. - * This includes instantiations of ki::BitInteger<>. */ template struct primitive_type_helper< ValueT, - typename std::enable_if::value>::type + typename std::enable_if::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(value); } - static Value read_from(BitStream &stream) + static Value read_from(BitStream &stream, const bool is_file) { return Value::make_value( stream.read() ); } }; + + /** + * Specialization of primitive_type_helper for ki::BitInteger<> types. + */ + template + struct primitive_type_helper> + { + private: + using type = ki::BitInteger; + + public: + static bool is_byte_based() + { + return false; + } + + static void write_to(BitStream &stream, const bool is_file, const type &value) + { + stream.write(value); + } + + static Value read_from(BitStream &stream, const bool is_file) + { + return Value::make_value( + stream.read() + ); + } + }; + + /** + * Specialization of primitive_type_helper for boolean types. + */ + template <> + struct primitive_type_helper + { + 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(value); + } + + static Value read_from(BitStream &stream, const bool is_file) + { + return Value::make_value( + stream.read() != 0 + ); + } + }; /** - * Specialization of primitive_type_helper for floating point - * types. + * Specialization of primitive_type_helper for floating point types. */ template struct primitive_type_helper< @@ -69,27 +134,33 @@ namespace pclass typename std::enable_if::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::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 const uint_type &v = *( reinterpret_cast(&value) - ); + ); stream.write(v, bitsizeof::value); } - static Value read_from(BitStream &stream) + static Value read_from(BitStream &stream, const bool is_file) { uint_type uint_value = stream.read(bitsizeof::value); return Value::make_value(*reinterpret_cast(&uint_value)); } - - private: - /** - * An unsigned integer type with the same size as the floating point type - * ValueT. - */ - using uint_type = typename bits::value>::uint_type; }; /** @@ -106,21 +177,47 @@ namespace pclass using type = std::basic_string<_Elem, _Traits, _Alloc>; public: - static void write_to(BitStream &stream, const type &value) + static bool is_byte_based() { - // Write the length as an unsigned short - stream.write(value.length()); + return true; + } + + 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(value.length() * 2); + } + else + { + // Write the length as an unsigned short + stream.write(value.length()); + } // Write each character as _Elem for (auto it = value.begin(); it != value.end(); ++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 - auto length = stream.read(); - auto value = type(length, ' ');; + std::size_t length; + if (is_file) + { + // TODO: Determine how the size of the length prefix is chosen + // while in file mode + length = stream.read() / 2; + } + else + { + // Read the length as an unsigned short + length = stream.read(); + } + + // Create a new string with the correct capacity + auto value = type(length, ' '); // Read each character into the string for (auto it = value.begin(); it != value.end(); ++it) @@ -153,13 +250,18 @@ namespace pclass } ~PrimitiveType() = default; - void write_to(BitStream &stream, Value &value) const override + bool is_byte_based() const override + { + return detail::primitive_type_helper::is_byte_based(); + } + + void write_to(BitStream &stream, const bool is_file, Value &value) const override { try { Value casted_value = value.as(); detail::primitive_type_helper::write_to( - stream, + stream, is_file, casted_value.get() ); } @@ -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 { - return detail::primitive_type_helper::read_from(stream); + return detail::primitive_type_helper::read_from(stream, is_file); } catch (runtime_error &e) { diff --git a/include/ki/pclass/Property.h b/include/ki/pclass/Property.h index 622d1a5..855ae24 100644 --- a/include/ki/pclass/Property.h +++ b/include/ki/pclass/Property.h @@ -101,14 +101,14 @@ namespace pclass * @param[in] stream The stream to write to. * @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. * @param[in] stream The stream to read from. * @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: const PropertyClass *m_instance; diff --git a/include/ki/pclass/Type.h b/include/ki/pclass/Type.h index 7d5a5c1..b82a12d 100644 --- a/include/ki/pclass/Type.h +++ b/include/ki/pclass/Type.h @@ -55,6 +55,11 @@ namespace pclass hash_t get_hash() 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. */ @@ -71,14 +76,14 @@ namespace pclass * @param[in] stream The stream to write to. * @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. * @param stream[in] The stream to read from. * @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: Kind m_kind; diff --git a/src/pclass/Enum.cpp b/src/pclass/Enum.cpp index a3fa69f..dec89a0 100644 --- a/src/pclass/Enum.cpp +++ b/src/pclass/Enum.cpp @@ -56,21 +56,42 @@ namespace pclass 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(); } - void Enum::write_to(BitStream& stream) const + void Enum::write_to(BitStream& stream, const bool is_file) const { - detail::primitive_type_helper::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::write_to(stream, is_file, name); + } + else + { + // Write the element value + detail::primitive_type_helper::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 - ::read_from(stream).get(); - set_value(value); + if (is_file) + { + // Set the value using the element name + const auto name = detail::primitive_type_helper + ::read_from(stream, is_file).get(); + set_value(name); + } + else + { + // Set the value using the element value + const auto value = detail::primitive_type_helper + ::read_from(stream, is_file).get(); + set_value(value); + } } Enum::operator enum_value_t() const diff --git a/src/pclass/EnumType.cpp b/src/pclass/EnumType.cpp index a7bac4c..8b15a4b 100644 --- a/src/pclass/EnumType.cpp +++ b/src/pclass/EnumType.cpp @@ -109,16 +109,16 @@ namespace pclass 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 - value.as().get().write_to(stream); + value.as().get().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); - value.read_from(stream); + value.read_from(stream, is_file); return Value::make_value(value); } } diff --git a/src/pclass/Property.cpp b/src/pclass/Property.cpp index 72b0c89..26ad2b8 100644 --- a/src/pclass/Property.cpp +++ b/src/pclass/Property.cpp @@ -81,19 +81,19 @@ namespace pclass 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()) throw runtime_error("Index out of bounds."); 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()) 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); } } } diff --git a/src/pclass/Type.cpp b/src/pclass/Type.cpp index d17ed95..1e8443f 100644 --- a/src/pclass/Type.cpp +++ b/src/pclass/Type.cpp @@ -38,14 +38,19 @@ namespace pclass 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; oss << "Type '" << m_name << "' does not implement Type::write_to."; 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; oss << "Type '" << m_name << "' does not implement Type::read_from."; diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index 06d75d7..71b662a 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -70,7 +70,10 @@ namespace net on_message(message); else 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; } } } diff --git a/src/serialization/BinarySerializer.cpp b/src/serialization/BinarySerializer.cpp index 17bbce8..a3ecd1d 100644 --- a/src/serialization/BinarySerializer.cpp +++ b/src/serialization/BinarySerializer.cpp @@ -27,7 +27,8 @@ namespace serialization const auto compression_header_pos = stream.tell(); if (FLAG_IS_SET(m_flags, flags::COMPRESSED)) { - stream.write(false); + if (m_is_file) + stream.write(false); stream.write(0); } @@ -102,7 +103,8 @@ namespace serialization // Write the compression header const auto use_compression = compressed.size() < size_bytes; stream.seek(compression_header_pos); - stream.write(use_compression); + if (m_is_file) + stream.write(use_compression); stream.write(size_bytes); // Write the compressed data @@ -157,14 +159,14 @@ namespace serialization stream.write(size_bits); 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 { + // 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 const auto start_pos = stream.tell(); @@ -178,7 +180,19 @@ namespace serialization // If the property is dynamic, write the element count if (prop.is_dynamic()) - stream.write(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(prop.get_element_count() * 2); + } + else + { + // Write the element count as an unsigned int + stream.write(prop.get_element_count()); + } + } for (auto i = 0; i < prop.get_element_count(); ++i) { @@ -189,7 +203,7 @@ namespace serialization save_object(prop.get_object(i), stream); } 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 @@ -226,7 +240,9 @@ namespace serialization if (FLAG_IS_SET(m_flags, flags::COMPRESSED)) { // Read the compression header - const auto use_compression = segment_stream.read(); + bool use_compression = true; + if (m_is_file) + use_compression = segment_stream.read(); const auto uncompressed_size = segment_stream.read(); // Work out how much data is available after the compression header @@ -305,14 +321,15 @@ namespace serialization stream.tell(), object_size ); auto object_stream = BitStream(*object_buffer); - stream.seek(stream.tell() + object_size, false); // Instead of loading properties sequentially, the file format specifies // the hash of a property before writing its value, so we just need to - // iterate for how ever many properties the object has declared. - for (std::size_t i = 0; - i < properties.get_property_count(); i++) + // iterate over how ever many bits were specified as the object size. + while (object_stream.tell().as_bits() < object_size) { + // 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 // ensure that data is only read from inside this region. const auto property_size = @@ -321,14 +338,21 @@ namespace serialization object_stream.tell(), property_size ); 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 // it's value. const auto property_hash = property_stream.read(); auto &prop = properties.get_property(property_hash); 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 { @@ -341,17 +365,29 @@ namespace serialization // All properties on this object have been set now, so call the // created handler on the object 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 { + // 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 (prop.is_dynamic()) { - const auto element_count = stream.read(); + 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() / 2; + } + else + { + // Load the element count as an unsigned int + element_count = stream.read(); + } prop.set_element_count(element_count); } @@ -366,7 +402,7 @@ namespace serialization prop.set_object(object, i); } else - prop.read_value_from(stream, i); + prop.read_value_from(stream, m_is_file, i); } } } diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 8ba1206..bc83e5e 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -54,24 +54,24 @@ struct Vector3D 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 - ::write_to(stream, m_x); + ::write_to(stream, is_file, m_x); pclass::detail::primitive_type_helper - ::write_to(stream, m_y); + ::write_to(stream, is_file, m_y); pclass::detail::primitive_type_helper - ::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 - ::read_from(stream).get(); + ::read_from(stream, is_file).get(); m_y = pclass::detail::primitive_type_helper - ::read_from(stream).get(); + ::read_from(stream, is_file).get(); m_z = pclass::detail::primitive_type_helper - ::read_from(stream).get(); + ::read_from(stream, is_file).get(); } private: @@ -93,15 +93,20 @@ namespace detail template <> struct primitive_type_helper { - 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; - value.read_from(stream); + value.read_from(stream, is_file); return Value::make_value(value); } };