#pragma once #include #include #include #include #include #include #include #include namespace utils { class buffer_serializer; class buffer_deserializer; template concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer) { { ac.serialize(serializer) } -> std::same_as; { a.deserialize(deserializer) } -> std::same_as; }; /* Use concept instead, to prevent overhead of virtual function calls struct serializable { virtual ~serializable() = default; virtual void serialize(buffer_serializer& buffer) const = 0; virtual void deserialize(buffer_deserializer& buffer) = 0; }; */ namespace detail { template struct has_serialize_function : std::false_type { }; template struct has_serialize_function(), std::declval&>()) )>> : std::true_type { }; template struct has_deserialize_function : std::false_type { }; template struct has_deserialize_function(), std::declval&>()))>> : std::true_type { }; template struct has_construct_function : std::false_type { }; template struct has_construct_function()))>> : std::bool_constant())), T>> { }; } class buffer_deserializer { public: template buffer_deserializer(const std::span buffer, bool no_debugging = false) : no_debugging_(no_debugging) , buffer_(reinterpret_cast(buffer.data()), buffer.size() * sizeof(T)) { static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); } template buffer_deserializer(const std::vector& buffer, bool no_debugging = false) : buffer_deserializer(std::span(buffer), no_debugging) { } std::span read_data(const size_t length) { #ifndef NDEBUG const uint64_t real_old_size = this->offset_; (void)real_old_size; #endif if (this->offset_ + length > this->buffer_.size()) { throw std::runtime_error("Out of bounds read from byte buffer"); } const std::span result(this->buffer_.data() + this->offset_, length); this->offset_ += length; #ifndef NDEBUG if (!this->no_debugging_) { uint64_t old_size{}; if (this->offset_ + sizeof(old_size) > this->buffer_.size()) { throw std::runtime_error("Out of bounds read from byte buffer"); } memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size)); if (old_size != real_old_size) { throw std::runtime_error("Reading from serialized buffer mismatches written data!"); } this->offset_ += sizeof(old_size); } #endif return result; } void read(void* data, const size_t length) { const auto span = this->read_data(length); memcpy(data, span.data(), length); } template void read(T& object) { constexpr auto is_trivially_copyable = std::is_trivially_copyable_v; if constexpr (Serializable) { object.deserialize(*this); } else if constexpr (detail::has_deserialize_function::value) { ::deserialize(*this, object); } else if constexpr (is_trivially_copyable) { union { T* type_{}; void* void_; } pointers; pointers.type_ = &object; this->read(pointers.void_, sizeof(object)); } else { static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!"); std::abort(); } } template T read() { auto object = this->construct_object(); this->read(object); return object; } template void read_optional(std::optional& val) { if (this->read()) { val = this->read(); } else { val = {}; } } template requires(std::is_invocable_r_v) void read_optional(std::optional& val, const F& factory) { if (this->read()) { val.emplace(factory()); this->read(*val); } else { val = {}; } } template void read_vector(std::vector& result) { const auto size = this->read(); result.clear(); result.reserve(size); for (uint64_t i = 0; i < size; ++i) { result.emplace_back(this->read()); } } template std::vector read_vector() { std::vector result{}; this->read_vector(result); return result; } template void read_map(Map& map) { using key_type = typename Map::key_type; using value_type = typename Map::mapped_type; map.clear(); const auto size = this->read(); for (uint64_t i = 0; i < size; ++i) { auto key = this->read(); auto value = this->read(); map.emplace(std::move(key), std::move(value)); } } template Map read_map() { Map map{}; this->read_map(map); return map; } template void read_string(std::basic_string& result) { const auto size = this->read(); result.clear(); result.reserve(size); for (uint64_t i = 0; i < size; ++i) { result.push_back(this->read()); } } template std::basic_string read_string() { std::basic_string result{}; this->read_string(result); return result; } size_t get_remaining_size() const { return this->buffer_.size() - offset_; } std::span get_remaining_data() { return this->read_data(this->get_remaining_size()); } size_t get_offset() const { return this->offset_; } template requires(std::is_invocable_r_v) void register_factory(F factory) { this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* { return new T(f()); }; } private: bool no_debugging_{false}; size_t offset_{0}; std::span buffer_{}; std::unordered_map> factories_{}; template T construct_object() { if constexpr (detail::has_construct_function::value) { return T::construct(*this); } else if constexpr (std::is_default_constructible_v) { return {}; } else { const auto factory = this->factories_.find(std::type_index(typeid(T))); if (factory == this->factories_.end()) { throw std::runtime_error( "Object construction failed. Missing factory for type: " + std::string(typeid(T).name())); } auto* object = static_cast(factory->second()); auto obj = std::move(*object); delete object; return obj; } } }; class buffer_serializer { public: buffer_serializer() = default; void write(const void* buffer, const size_t length) { #ifndef NDEBUG const uint64_t old_size = this->buffer_.size(); #endif const auto* byte_buffer = static_cast(buffer); this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length); #ifndef NDEBUG const auto* security_buffer = reinterpret_cast(&old_size); this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size)); #endif } void write(const buffer_serializer& object) { const auto& buffer = object.get_buffer(); this->write(buffer.data(), buffer.size()); } template void write(const T& object) { constexpr auto is_trivially_copyable = std::is_trivially_copyable_v; if constexpr (Serializable) { object.serialize(*this); } else if constexpr (detail::has_serialize_function::value) { ::serialize(*this, object); } else if constexpr (is_trivially_copyable) { union { const T* type_{}; const void* void_; } pointers; pointers.type_ = &object; this->write(pointers.void_, sizeof(object)); } else { static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!"); std::abort(); } } template void write_optional(const std::optional& val) { this->write(val.has_value()); if (val.has_value()) { this->write(*val); } } template void write_span(const std::span vec) { this->write(static_cast(vec.size())); for (const auto& v : vec) { this->write(v); } } template void write_vector(const std::vector vec) { this->write_span(std::span(vec)); } template void write_string(const std::basic_string_view str) { this->write_span(str); } template void write_string(const std::basic_string& str) { this->write_string(std::basic_string_view(str)); } template void write_map(const Map& map) { this->write(map.size()); for (const auto& entry : map) { this->write(entry.first); this->write(entry.second); } } const std::vector& get_buffer() const { return this->buffer_; } std::vector move_buffer() { return std::move(this->buffer_); } private: std::vector buffer_{}; }; template <> inline void buffer_deserializer::read(bool& object) { object = this->read() != 0; } template <> inline void buffer_deserializer::read(std::string& object) { object = this->read_string(); } template <> inline void buffer_deserializer::read(std::wstring& object) { object = this->read_string(); } template <> inline void buffer_serializer::write(const bool& object) { this->write(object ? 1 : 0); } template <> inline void buffer_serializer::write(const std::string& object) { this->write_string(object); } template <> inline void buffer_serializer::write(const std::wstring& object) { this->write_string(object); } }