From 10b49b34dd0e531a5c6809ddafccd7c29235d44a Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 25 Jun 2025 19:33:11 +0200 Subject: [PATCH 1/9] working dispatcher + factory --- include/examples/DisconnectPacket.h | 25 ++- include/examples/KeepAlivePacket.h | 41 +++- include/examples/PacketExample.h | 2 +- include/examples/UpgradeTowerPacket.h | 42 +++-- include/sp/common/{Templates.h => Tuples.h} | 0 include/sp/common/VarInt.h | 2 - include/sp/default/DefaultPacket.h | 13 +- include/sp/protocol/ConcreteMessage.h | 29 +++ include/sp/protocol/Field.h | 134 ------------- include/sp/protocol/GenericHandler.h | 142 +++++++------- include/sp/protocol/Message.h | 16 -- include/sp/protocol/MessageBase.h | 25 +-- include/sp/protocol/MessageDispatcher.h | 19 +- include/sp/protocol/MessageDispatcherImpl.inl | 43 +++++ include/sp/protocol/MessageFactory.h | 22 ++- include/sp/protocol/message/ArrayFillerImpl.h | 39 ---- .../message/MessageDispatcherImpl.inl | 36 ---- .../sp/protocol/message/MessageImplBuilder.h | 49 ----- .../sp/protocol/message/MessageImplOptions.h | 66 ------- .../sp/protocol/message/MessageImplProcess.h | 107 ----------- .../message/MessageInterfaceBuilder.h | 44 ----- .../message/MessageInterfaceProcess.h | 119 ------------ .../sp/protocol/message/MessageInterfaces.h | 7 - .../protocol/message/MessageInterfacesImpl.h | 145 -------------- .../message/MessageInterfacesOptions.h | 69 ------- include/sp/protocol/message/MessageOptions.h | 37 ---- .../sp/protocol/message/MessagePrinterImpl.h | 79 -------- include/sp/protocol/message/MessagesImpl.h | 178 ------------------ .../protocol/message/OstreamFieldIterator.h | 56 ------ src/sp/common/VarInt.cpp | 4 - test/test_file.cpp | 42 ----- test/test_io.cpp | 40 ---- test/test_message.cpp | 49 +++++ test/test_packets.cpp | 61 ------ xmake.lua | 5 + 35 files changed, 315 insertions(+), 1472 deletions(-) rename include/sp/common/{Templates.h => Tuples.h} (100%) create mode 100644 include/sp/protocol/ConcreteMessage.h delete mode 100644 include/sp/protocol/Field.h delete mode 100644 include/sp/protocol/Message.h create mode 100644 include/sp/protocol/MessageDispatcherImpl.inl delete mode 100644 include/sp/protocol/message/ArrayFillerImpl.h delete mode 100644 include/sp/protocol/message/MessageDispatcherImpl.inl delete mode 100644 include/sp/protocol/message/MessageImplBuilder.h delete mode 100644 include/sp/protocol/message/MessageImplOptions.h delete mode 100644 include/sp/protocol/message/MessageImplProcess.h delete mode 100644 include/sp/protocol/message/MessageInterfaceBuilder.h delete mode 100644 include/sp/protocol/message/MessageInterfaceProcess.h delete mode 100644 include/sp/protocol/message/MessageInterfaces.h delete mode 100644 include/sp/protocol/message/MessageInterfacesImpl.h delete mode 100644 include/sp/protocol/message/MessageInterfacesOptions.h delete mode 100644 include/sp/protocol/message/MessageOptions.h delete mode 100644 include/sp/protocol/message/MessagePrinterImpl.h delete mode 100644 include/sp/protocol/message/MessagesImpl.h delete mode 100644 include/sp/protocol/message/OstreamFieldIterator.h delete mode 100644 test/test_file.cpp delete mode 100644 test/test_io.cpp create mode 100644 test/test_message.cpp delete mode 100644 test/test_packets.cpp diff --git a/include/examples/DisconnectPacket.h b/include/examples/DisconnectPacket.h index 013f7f0..84c99b4 100644 --- a/include/examples/DisconnectPacket.h +++ b/include/examples/DisconnectPacket.h @@ -4,17 +4,28 @@ #include #include -enum class DisconnectFieldsE { - Reason = 0 + +struct DisconnectPacketData { + std::string m_Reason; }; -using DisconnectFields = std::tuple; +class DisconnectPacket : public sp::MessageBase> { + private: + DisconnectPacketData m_Data; -DeclarePacket(Disconnect){ - public: - PacketConstructor(Disconnect) + public: + template + DisconnectPacket(T... args) : m_Data{args...} {} const std::string& GetReason() const { - return GetField(); + return m_Data.m_Reason; + } + + virtual sp::PacketID GetId() const { + return Disconnect; } }; + +void ff() { + sizeof(std::string); +} diff --git a/include/examples/KeepAlivePacket.h b/include/examples/KeepAlivePacket.h index 770b078..bf38ab0 100644 --- a/include/examples/KeepAlivePacket.h +++ b/include/examples/KeepAlivePacket.h @@ -4,19 +4,40 @@ #include #include -enum class KeepAliveFieldsE { - KeepAliveId = 0, + +template +class ConcreteMessage { + public: + using DataType = TData; + + template + ConcreteMessage(const T&... args) : m_Data {args ...}; + + private: + DataType m_Data; + + virtual sp::PacketID GetId() const { + return ID; + } }; -using KeepAliveFields = std::tuple< - std::uint64_t //<- KeepAliveId - >; -DeclarePacket(KeepAlive){ - public: - PacketConstructor(KeepAlive) - std::uint64_t GetKeepAliveId() const { - return GetField(); + + +struct KeepAlivePacket { + std::uint64_t m_AliveId; +}; + +class KeepAliveMessage : public sp::MessageBase> { + private: + KeepAlivePacket m_Data; + + public: + template + KeepAliveMessage(T... args) : m_Data{args...} {} + + virtual sp::PacketID GetId() const { + return KeepAlive; } }; \ No newline at end of file diff --git a/include/examples/PacketExample.h b/include/examples/PacketExample.h index bf51532..e5dd3bb 100644 --- a/include/examples/PacketExample.h +++ b/include/examples/PacketExample.h @@ -1,6 +1,6 @@ #pragma once -enum PacketId { +enum PacketIds { KeepAlive = 0, Disconnect, UpgradeTower, diff --git a/include/examples/UpgradeTowerPacket.h b/include/examples/UpgradeTowerPacket.h index fc566b2..88c3972 100644 --- a/include/examples/UpgradeTowerPacket.h +++ b/include/examples/UpgradeTowerPacket.h @@ -4,30 +4,36 @@ #include #include - -enum class UpgradeTowerFieldsE { - m_Tower = 0, - m_Upgrade, +struct UpgradeTowerPacketData { + sp::BitField, // std::uint16_t m_Tower : 12; + sp::Field // std::uint8_t m_Upgrade : 4; + > m_TowerAndUpgrade; + sp::VarInt m_Test; + std::map> m_Test2; }; -using UpgradeTowerFields = std::tuple< - sp::BitField, //<- m_Tower - sp::Field //<- m_Upgrade - >, - sp::VarInt, //<- just for testing - std::map> - >; +class UpgradeTowerPacket : public sp::MessageBase> { + private: + UpgradeTowerPacketData m_Data; -DeclarePacket(UpgradeTower){ - public: - PacketConstructor(UpgradeTower) + public: + template + UpgradeTowerPacket(T... args) : m_Data{args...} {} std::uint16_t GetTowerId() const { - return GetField<0>().GetField(); + return m_Data.m_TowerAndUpgrade.GetField<0>(); } std::uint8_t GetTowerUpgrade() const { - return GetField<0>().GetField(); + return m_Data.m_TowerAndUpgrade.GetField<1>(); } -}; \ No newline at end of file + + virtual sp::PacketID GetId() const { + return UpgradeTower; + } + + UpgradeTowerPacketData& GetData() { + return m_Data; + } +}; diff --git a/include/sp/common/Templates.h b/include/sp/common/Tuples.h similarity index 100% rename from include/sp/common/Templates.h rename to include/sp/common/Tuples.h diff --git a/include/sp/common/VarInt.h b/include/sp/common/VarInt.h index af96df8..2f0bf6b 100644 --- a/include/sp/common/VarInt.h +++ b/include/sp/common/VarInt.h @@ -8,7 +8,6 @@ #include #include #include -#include namespace sp { @@ -58,7 +57,6 @@ class VarInt { */ friend DataBuffer& operator>>(DataBuffer& in, VarInt& var); - friend std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_VarInt); }; } // namespace sp diff --git a/include/sp/default/DefaultPacket.h b/include/sp/default/DefaultPacket.h index 107a9ec..3882a40 100644 --- a/include/sp/default/DefaultPacket.h +++ b/include/sp/default/DefaultPacket.h @@ -3,16 +3,15 @@ #include #include + namespace sp { class PacketHandler; + +using PacketID = std::uint8_t; using PacketMessage = Message< - option::MsgIdType, // add id() operation - option::ReadOperations, // add read() operation - option::WriteOperations, // add write() operation - option::WriteId, // write id before data - option::Handler, // add dispatch() operation - option::DebugPrint // add ToString() operator + option::MsgIdType, // add id() operation + option::Handler // add dispatch() operation >; #define PacketConstructor(packetName) \ @@ -23,7 +22,7 @@ using PacketMessage = Message< } #define DeclarePacket(packetName) \ - class packetName##Packet : public sp::MessageBase, \ + class packetName##Packet : public sp::MessageBase, sp::option::FieldsImpl>, \ sp::option::ToStringImpl diff --git a/include/sp/protocol/ConcreteMessage.h b/include/sp/protocol/ConcreteMessage.h new file mode 100644 index 0000000..b057276 --- /dev/null +++ b/include/sp/protocol/ConcreteMessage.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace sp { + +template +class ConcreteMessage : public MessageBase { + public: + using DataType = TData; + + template + ConcreteMessage(const T&... args) : m_Data{args...} {} + + virtual ~ConcreteMessage() {} + + virtual constexpr TMessageID GetId() const override { + return ID; + } + + virtual void Dispatch(THandler& handler) const override { + handler.Handle(static_cast&>(*this)); + } + + private: + DataType m_Data; +}; + +} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/Field.h b/include/sp/protocol/Field.h deleted file mode 100644 index 5fb377f..0000000 --- a/include/sp/protocol/Field.h +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once - -#include - -namespace sp { - -/** - * \brief Example usage : - * sp::BitField, sp::Field>; - */ -template -class BitField { - using AllFields = std::tuple; - - public: - template - BitField(const std::tuple& args) { - Apply<0, T...>(args); - } - - BitField() {} - - template - BitField& operator=(const std::tuple& args) { - Apply<0, T...>(args); - return *this; - } - - AllFields& GetFields() { - return m_Fields; - } - - const AllFields& GetFields() const { - return m_Fields; - } - - template - auto& GetField() { - return std::get(this->GetFields()).GetValue(); - } - - template - const auto& GetField() const { - return std::get(this->GetFields()).GetValue(); - } - - // allow use of enums - template - const auto& GetField() const { - return std::get(FIndex)>(this->GetFields()).GetValue(); - } - - private: - template = sizeof...(T), bool> = true> - void Apply(const std::tuple& args) {} - - template = sizeof...(T)), bool> = true> - void Apply(const std::tuple& args) { - this->GetField() = std::get(args); - Apply<1 + IOffset, T...>(args); - } - - TContainer m_Value; - AllFields m_Fields; -}; - -/** - * - * \tparam ValueType the type of the value to store - * \tparam IAlignment 0 means no alignment - */ -template -class Field { - public: - using StorageType = ValueType; - static constexpr std::size_t AlignmentValue = IAlignment; - - // Provide an access to the stored value - StorageType& GetValue() { - return m_Value; - } - - const StorageType& GetValue() const { - return m_Value; - } - - Field& operator=(const StorageType& value) { - m_Value = value; - return *this; - } - - constexpr std::size_t GetAlignment() const { - return IAlignment; - } - - private: - StorageType m_Value; -}; - -template -class PrintableField { - public: - PrintableField(const T& a_Value) : m_Value(a_Value) {} - - const T& GetValue() const { - return m_Value; - } - - private: - const T& m_Value; -}; - -namespace details { - -template -struct FieldsBuilder {}; - -template <> -struct FieldsBuilder<> { - using Type = std::tuple<>; -}; - -template -struct FieldsBuilder> { - using Type = typename FieldsBuilder::Type; -}; - -template -struct FieldsBuilder { - using Type = sp::tuple_cat_t>, typename FieldsBuilder::Type>; -}; - -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/GenericHandler.h b/include/sp/protocol/GenericHandler.h index dca0813..1232176 100644 --- a/include/sp/protocol/GenericHandler.h +++ b/include/sp/protocol/GenericHandler.h @@ -8,134 +8,132 @@ namespace sp // TCommon is common interface class for all the messages // TAll is all the message types, that need to be handled, bundled in std::tuple - template + template class GenericHandler; // Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage - template - class GenericHandler > : public GenericHandler > + class GenericHandler > : public GenericHandler > { - using Base = GenericHandler >; + using Base = GenericHandler >; public: using Base::Handle; // Don't hide all Handle() functions from base classes - virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T5& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T6& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T7& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T8& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T9& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T10& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T11& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T12& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T13& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T14& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T15& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T16& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T17& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T18& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T19& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T20& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T1& msg) {} + virtual void Handle(const T2& msg) {} + virtual void Handle(const T3& msg) {} + virtual void Handle(const T4& msg) {} + virtual void Handle(const T5& msg) {} + virtual void Handle(const T6& msg) {} + virtual void Handle(const T7& msg) {} + virtual void Handle(const T8& msg) {} + virtual void Handle(const T9& msg) {} + virtual void Handle(const T10& msg) {} + virtual void Handle(const T11& msg) {} + virtual void Handle(const T12& msg) {} + virtual void Handle(const T13& msg) {} + virtual void Handle(const T14& msg) {} + virtual void Handle(const T15& msg) {} + virtual void Handle(const T16& msg) {} + virtual void Handle(const T17& msg) {} + virtual void Handle(const T18& msg) {} + virtual void Handle(const T19& msg) {} + virtual void Handle(const T20& msg) {} }; // 10 by 10 - template - class GenericHandler > : public GenericHandler > + class GenericHandler > : public GenericHandler > { - using Base = GenericHandler >; + using Base = GenericHandler >; public: using Base::Handle; // Don't hide all Handle() functions from base classes - virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T5& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T6& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T7& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T8& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T9& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T10& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T1& msg) {} + virtual void Handle(const T2& msg) {} + virtual void Handle(const T3& msg) {} + virtual void Handle(const T4& msg) {} + virtual void Handle(const T5& msg) {} + virtual void Handle(const T6& msg) {} + virtual void Handle(const T7& msg) {} + virtual void Handle(const T8& msg) {} + virtual void Handle(const T9& msg) {} + virtual void Handle(const T10& msg) {} }; // 5 by 5 - template - class GenericHandler > : public GenericHandler > + class GenericHandler > : public GenericHandler > { - using Base = GenericHandler >; + using Base = GenericHandler >; public: using Base::Handle; // Don't hide all Handle() functions from base classes - virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T5& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T1& msg) {} + virtual void Handle(const T2& msg) {} + virtual void Handle(const T3& msg) {} + virtual void Handle(const T4& msg) {} + virtual void Handle(const T5& msg) {} }; // Deal with rest with 4 types - template - class GenericHandler > + template + class GenericHandler > { public: virtual ~GenericHandler() {} - virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) {} + virtual void Handle(const T2& msg) {} + virtual void Handle(const T3& msg) {} + virtual void Handle(const T4& msg) {} + // virtual void Handle(const TCommon&) {} //Nothing to do }; // Deal with rest with 3 types - template - class GenericHandler > + template < typename T1, typename T2, typename T3> + class GenericHandler > { public: virtual ~GenericHandler() {} - virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) {} + virtual void Handle(const T2& msg) {} + virtual void Handle(const T3& msg) {} + // virtual void Handle(const TCommon&) {} //Nothing to do }; // Deal with rest with 2 types - template - class GenericHandler > + template + class GenericHandler > { public: virtual ~GenericHandler() {} - virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) {} + virtual void Handle(const T2& msg) {} + // virtual void Handle(const TCommon&) {} //Nothing to do }; // Deal with rest with 1 type - template - class GenericHandler > + template + class GenericHandler > { public: virtual ~GenericHandler() {} - virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(const TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) {} + // virtual void Handle(const TCommon&) {} //Nothing to do }; // Deal with rest with 0 type - template - class GenericHandler > + template <> + class GenericHandler > { public: virtual ~GenericHandler() {} - virtual void Handle(const TCommon&) { } //Nothing to do + // virtual void Handle(const TCommon&) {} //Nothing to do }; } // sp \ No newline at end of file diff --git a/include/sp/protocol/Message.h b/include/sp/protocol/Message.h deleted file mode 100644 index c347b24..0000000 --- a/include/sp/protocol/Message.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -// Inspired by -// https://alex-robenko.gitbook.io/comms-protocols-cpp - -#include - -namespace sp { - -template -class Message : public details::MessageInterfaceBuilder::Type { - public: - using ParsedOptions = typename details::MessageInterfaceBuilder::ParsedOptions; -}; - -} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/MessageBase.h b/include/sp/protocol/MessageBase.h index 80d0b8a..5fd2441 100644 --- a/include/sp/protocol/MessageBase.h +++ b/include/sp/protocol/MessageBase.h @@ -1,18 +1,19 @@ #pragma once -#include - -#include - -#include -#include -#include - namespace sp { -template -class MessageBase : public details::MessageImplBuilder::Type {}; +template +class MessageBase { + public: + using HandlerType = THandler; + using MessageIdType = TMessageID; + + MessageBase() {} + virtual ~MessageBase() {} + + virtual MessageIdType GetId() const = 0; + + virtual void Dispatch(HandlerType& handler) const = 0; +}; } // namespace sp - -#include \ No newline at end of file diff --git a/include/sp/protocol/MessageDispatcher.h b/include/sp/protocol/MessageDispatcher.h index 1cdba2c..c7e2f6d 100644 --- a/include/sp/protocol/MessageDispatcher.h +++ b/include/sp/protocol/MessageDispatcher.h @@ -7,6 +7,7 @@ #include #include +#include namespace sp { @@ -14,13 +15,12 @@ namespace sp { * \class MessageDispatcher * \brief Class used to dispatch messages */ -template +template class MessageDispatcher { - private: - std::map> m_Handlers; - public: using MessageBaseType = MessageBase; + using MessageIdType = typename MessageBase::MessageIdType; + using MessageHandler = typename MessageBase::HandlerType; /** * \brief Constructor @@ -39,21 +39,24 @@ class MessageDispatcher { * \param handler The packet handler */ void RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler); - + /** * \brief Unregister a packet handler * \param type The packet type * \param handler The packet handler */ void UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler); - + /** * \brief Unregister a packet handler * \param handler The packet handler */ void UnregisterHandler(MessageHandler* a_Handler); + + private: + std::map> m_Handlers; }; -#include - } // namespace sp + +#include \ No newline at end of file diff --git a/include/sp/protocol/MessageDispatcherImpl.inl b/include/sp/protocol/MessageDispatcherImpl.inl new file mode 100644 index 0000000..408a9b9 --- /dev/null +++ b/include/sp/protocol/MessageDispatcherImpl.inl @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +namespace sp { + +template +void MessageDispatcher::RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) { + assert(a_Handler); + auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler); + if (found == m_Handlers[a_MessageType].end()) + m_Handlers[a_MessageType].push_back(a_Handler); +} + +template +void MessageDispatcher::UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) { + auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler); + if (found != m_Handlers[a_MessageType].end()) + m_Handlers[a_MessageType].erase(found); +} + +template +void MessageDispatcher::UnregisterHandler(MessageHandler* a_Handler) { + for (auto& pair : m_Handlers) { + if (pair.second.empty()) + continue; + + MessageIdType type = pair.first; + + pair.second.erase(std::remove(pair.second.begin(), pair.second.end(), a_Handler), pair.second.end()); + } +} + +template +void MessageDispatcher::Dispatch(const MessageBase& a_Message) { + MessageIdType type = a_Message.GetId(); + for (auto& handler : m_Handlers[type]) { + a_Message.Dispatch(*handler); + } +} + +} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/MessageFactory.h b/include/sp/protocol/MessageFactory.h index abc5a99..c7c5d59 100644 --- a/include/sp/protocol/MessageFactory.h +++ b/include/sp/protocol/MessageFactory.h @@ -2,28 +2,36 @@ #include #include -#include #include -#include +#include namespace sp { template class MessageFactory { public: - using IdType = typename TBase::MsgIdType; + using IdType = typename TBase::MessageIdType; - MessageFactory() : m_Factory(details::ArrayFiller::ArrayCreate()) {} + MessageFactory() { + constexpr std::size_t messageCount = std::tuple_size_v; + m_Factory.resize(messageCount); + TupleForEach([this](const auto& message){ + std::size_t messageID = static_cast(message.GetId()); + using MessageType = std::remove_const_t>; + m_Factory.emplace(m_Factory.begin() + messageID, []() -> std::unique_ptr { return std::make_unique(); }); + }, TTMessages{}); + } std::unique_ptr CreateMessage(IdType id) const { - if (id >= m_Factory.size()) + std::size_t idSize = static_cast(id); + if (idSize >= m_Factory.size()) return nullptr; - return m_Factory.at(id)(); + return m_Factory.at(idSize)(); } private: - details::ArrayType m_Factory; + std::vector(void)>> m_Factory; }; diff --git a/include/sp/protocol/message/ArrayFillerImpl.h b/include/sp/protocol/message/ArrayFillerImpl.h deleted file mode 100644 index 556cd8f..0000000 --- a/include/sp/protocol/message/ArrayFillerImpl.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -namespace sp { -namespace details { - -template -using ArrayType = std::vector(void)>>; - - -template -struct ArrayFiller {}; - -template -struct ArrayFiller> { - static ArrayType ArrayCreate() { - ArrayType array; - array.reserve(sizeof...(TMessages)); - ArrayFiller::ArrayAppend(array); - return array; - } -}; - -template -struct ArrayFiller { - static void ArrayAppend(details::ArrayType& array) { - ArrayFiller::ArrayAppend(array); - ArrayFiller::ArrayAppend(array); - } -}; - -template -struct ArrayFiller { - static void ArrayAppend(details::ArrayType& array) { - array.emplace_back([]() -> std::unique_ptr { return std::make_unique(); }); - } -}; - -} // namespace details -} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessageDispatcherImpl.inl b/include/sp/protocol/message/MessageDispatcherImpl.inl deleted file mode 100644 index 7319fbf..0000000 --- a/include/sp/protocol/message/MessageDispatcherImpl.inl +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -template -void MessageDispatcher::RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) { - assert(a_Handler); - auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler); - if (found == m_Handlers[a_MessageType].end()) - m_Handlers[a_MessageType].push_back(a_Handler); -} - -template -void MessageDispatcher::UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) { - auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler); - if (found != m_Handlers[a_MessageType].end()) - m_Handlers[a_MessageType].erase(found); -} - -template -void MessageDispatcher::UnregisterHandler(MessageHandler* a_Handler) { - for (auto& pair : m_Handlers) { - if (pair.second.empty()) - continue; - - MessageIdType type = pair.first; - - pair.second.erase(std::remove(pair.second.begin(), pair.second.end(), a_Handler), pair.second.end()); - } -} - -template -void MessageDispatcher::Dispatch(const MessageBase& a_Message) { - MessageIdType type = a_Message.GetId(); - for (auto& handler : m_Handlers[type]) { - a_Message.Dispatch(*handler); - } -} diff --git a/include/sp/protocol/message/MessageImplBuilder.h b/include/sp/protocol/message/MessageImplBuilder.h deleted file mode 100644 index b01219e..0000000 --- a/include/sp/protocol/message/MessageImplBuilder.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -namespace sp { -namespace details { - - -// TBase is interface class -// TOptions... are the implementation options -template -struct MessageImplBuilder { - // ParsedOptions class is supposed to be defined in comms::Message class - using InterfaceOptions = typename TBase::ParsedOptions; - - // Parse implementation options - using ImplOptions = MessageImplParsedOptions; - - // Provide GetIdImpl() if possible - static const bool HasStaticNumIdImpl = InterfaceOptions::HasMsgIdType && ImplOptions::HasStaticNumIdImpl; - using Base1 = typename MessageImplProcessStaticNumId::Type; - - // Provide DispatchImpl() if possible - static const bool HasDispatchImpl = InterfaceOptions::HasHandler && ImplOptions::HasDispatchImpl; - using Base2 = typename MessageImplProcessDispatch::Type; - - // Provide access to fields if possible - using Base3 = typename MessageImplProcessFields::Type; - - // Provide ReadImpl() if possible - static const bool HasReadImpl = InterfaceOptions::HasReadOperations && ImplOptions::HasFieldsImpl; - using Base4 = typename MessageImplProcessReadFields::Type; - - // Provide WriteImpl() if possible - static const bool HasWriteImpl = InterfaceOptions::HasWriteOperations && ImplOptions::HasFieldsImpl; - using Base5 = typename MessageImplProcessWriteFields::Type; - - // Provide ValidImpl() if possible - static const bool HasValidImpl = InterfaceOptions::HasValid && ImplOptions::HasFieldsImpl; - using Base6 = typename MessageImplProcessValidFields::Type; - - // Provide ToStringImpl() if possible - static const bool HasToStringImpl = InterfaceOptions::HasToString; - using Base7 = typename MessageImplProcessToString::Type; - - // The last BaseN must be taken as final type. - using Type = Base7; -}; - -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/message/MessageImplOptions.h b/include/sp/protocol/message/MessageImplOptions.h deleted file mode 100644 index 5944a8d..0000000 --- a/include/sp/protocol/message/MessageImplOptions.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -namespace sp { -namespace option { - -// Provide static numeric ID, to facilitate implementation of GetIdImpl() -template -struct StaticNumIdImpl {}; - -// Facilitate implementation of DispatchImpl() -template -struct DispatchImpl {}; - -// Provide fields of the message, facilitate implementation of -// ReadImpl(), WriteImpl(), ValidImpl(), etc... -template -struct FieldsImpl {}; - -// Print fields of the message, facilitate implementation of -// ToStringImpl() -template -struct ToStringImpl {}; - -} // namespace option - - -namespace details { - - - - - -template -class MessageImplParsedOptions; - -template <> -struct MessageImplParsedOptions<> { - static const bool HasStaticNumIdImpl = false; - static const bool HasDispatchImpl = false; - static const bool HasFieldsImpl = false; -}; - - - - - -template -struct MessageImplParsedOptions, TOptions...> : public MessageImplParsedOptions { - static const bool HasStaticNumIdImpl = true; - static const std::uintmax_t MsgId = TId; -}; - -template -struct MessageImplParsedOptions, TOptions...> : public MessageImplParsedOptions { - static const bool HasDispatchImpl = true; - using ActualMessage = TActual; -}; - -template -struct MessageImplParsedOptions, TOptions...> : public MessageImplParsedOptions { - static const bool HasFieldsImpl = true; - using Fields = TFields; -}; - -} // namespace details -} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessageImplProcess.h b/include/sp/protocol/message/MessageImplProcess.h deleted file mode 100644 index 31706f6..0000000 --- a/include/sp/protocol/message/MessageImplProcess.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -namespace sp { -namespace details { - - -// ToString impl -template -struct MessageImplProcessToString; - -template -struct MessageImplProcessToString { - using Type = MessageImplToStringBase; -}; - -template -struct MessageImplProcessToString { - using Type = TBase; -}; - - -// id impl -template -struct MessageImplProcessStaticNumId; - -template -struct MessageImplProcessStaticNumId { - using Type = MessageImplStaticNumIdBase; -}; - -template -struct MessageImplProcessStaticNumId { - using Type = TBase; -}; - -// dispatch impl -template -struct MessageImplProcessDispatch; - -template -struct MessageImplProcessDispatch { - using Type = MessageImplDispatchBase; -}; - -template -struct MessageImplProcessDispatch { - using Type = TBase; -}; - -// fields impl -template -struct MessageImplProcessFields; - -template -struct MessageImplProcessFields { - using Type = MessageImplFieldsBase; -}; - -template -struct MessageImplProcessFields { - using Type = TBase; -}; - -// read impl -template -struct MessageImplProcessReadFields; - -template -struct MessageImplProcessReadFields { - using Type = MessageImplFieldsReadBase; -}; - -template -struct MessageImplProcessReadFields { - using Type = TBase; -}; - -// write impl -template -struct MessageImplProcessWriteFields; - -template -struct MessageImplProcessWriteFields { - using Type = MessageImplFieldsWriteBase; -}; - -template -struct MessageImplProcessWriteFields { - using Type = TBase; -}; - -// valid impl -template -struct MessageImplProcessValidFields; - -template -struct MessageImplProcessValidFields { - using Type = MessageImplFieldsValidBase; -}; - -template -struct MessageImplProcessValidFields { - using Type = TBase; -}; - -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/message/MessageInterfaceBuilder.h b/include/sp/protocol/message/MessageInterfaceBuilder.h deleted file mode 100644 index 7496a18..0000000 --- a/include/sp/protocol/message/MessageInterfaceBuilder.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include - -namespace sp { -namespace details { - -class EmptyBase {}; - -template -struct MessageInterfaceBuilder { - // Parse the options - using ParsedOptions = MessageInterfaceParsedOptions; - - // Add ID retrieval functionality if ID type was provided - using Base1 = typename MessageInterfaceProcessMsgId::Type; - - // Add ReadData() and WriteData(), that use the right endian - using Base2 = typename MessageInterfaceProcessEndian::Type; - - // Add read functionality if Read type was provided - using Base3 = typename MessageInterfaceProcessRead::Type; - - // Add write functionality if Write type was provided - using Base4 = typename MessageInterfaceProcessWrite::Type; - - // add dispatch functionality if Handler type was provided - using Base5 = typename MessageInterfaceProcessHandler::Type; - - // add valid functionality if Valid tpe was provided - using Base6 = typename MessageInterfaceProcessValid::Type; - - // add write id functionality if write id and write was provided - using Base7 = typename MessageInterfaceProcessWriteId::Type; - - // add ToString() if HasToString was provided - using Base8 = typename MessageInterfaceProcessToString::Type; - - // The last Base8 must be taken as final type. - using Type = Base8; -}; - -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/message/MessageInterfaceProcess.h b/include/sp/protocol/message/MessageInterfaceProcess.h deleted file mode 100644 index 9b4fb97..0000000 --- a/include/sp/protocol/message/MessageInterfaceProcess.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -namespace sp { -namespace details { - -// Build message Id -template -struct MessageInterfaceProcessMsgId; - -template -struct MessageInterfaceProcessMsgId { - using Type = MessageInterfaceIdTypeBase; -}; - -template -struct MessageInterfaceProcessMsgId { - using Type = TBase; -}; - -// Build endianess -template -struct MessageInterfaceProcessEndian; - -template -struct MessageInterfaceProcessEndian { - using Type = MessageInterfaceLittleEndian; -}; - -template -struct MessageInterfaceProcessEndian { - using Type = MessageInterfaceBigEndian; -}; - -// Build read -template -struct MessageInterfaceProcessRead; - -template -struct MessageInterfaceProcessRead { - using Type = MessageInterfaceReadBase; -}; - -template -struct MessageInterfaceProcessRead { - using Type = TBase; -}; - -// Build write -template -struct MessageInterfaceProcessWrite; - -template -struct MessageInterfaceProcessWrite { - using Type = MessageInterfaceWriteBase; -}; - -template -struct MessageInterfaceProcessWrite { - using Type = TBase; -}; - -// Build handler -template -struct MessageInterfaceProcessHandler; - -template -struct MessageInterfaceProcessHandler { - using Type = MessageInterfaceHandlerBase; -}; - -template -struct MessageInterfaceProcessHandler { - using Type = TBase; -}; - -// Build valid -template -struct MessageInterfaceProcessValid; - -template -struct MessageInterfaceProcessValid { - using Type = MessageInterfaceValidityBase; -}; - -template -struct MessageInterfaceProcessValid { - using Type = TBase; -}; - -// Build id writing -template -struct MessageInterfaceProcessWriteId; - -template -struct MessageInterfaceProcessWriteId { - using Type = MessageInterfaceWriteIdBase; -}; - -template -struct MessageInterfaceProcessWriteId { - using Type = TBase; -}; - -// Build to string -template -struct MessageInterfaceProcessToString; - -template -struct MessageInterfaceProcessToString { - using Type = MessageInterfaceToStringBase; -}; - -template -struct MessageInterfaceProcessToString { - using Type = TBase; -}; - -} // namespace details -} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessageInterfaces.h b/include/sp/protocol/message/MessageInterfaces.h deleted file mode 100644 index a1b1176..0000000 --- a/include/sp/protocol/message/MessageInterfaces.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -#include -#include -#include diff --git a/include/sp/protocol/message/MessageInterfacesImpl.h b/include/sp/protocol/message/MessageInterfacesImpl.h deleted file mode 100644 index d9df12e..0000000 --- a/include/sp/protocol/message/MessageInterfacesImpl.h +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once - -#include - -namespace sp { -namespace details { - - -// ID retrieval chunk -template -class MessageInterfaceIdTypeBase : public TBase { - public: - using MsgIdType = TId; - MsgIdType GetId() const { - return GetIdImpl(); - } - - protected: - virtual MsgIdType GetIdImpl() const = 0; -}; - -// Big endian serialisation chunk -template -class MessageInterfaceBigEndian : public TBase { - protected: - template - void ReadData(T& value, DataBuffer& buffer) { - buffer >> value; - FromNetwork(value); - } - - template - void WriteData(T value, DataBuffer& buffer) const { - ToNetwork(value); - buffer << value; - } -}; - -// Little endian serialisation chunk -template -class MessageInterfaceLittleEndian : public TBase { - protected: - template - void ReadData(T& value, DataBuffer& buffer) { - buffer >> value; - TrySwapBytes(value); - FromNetwork(value); - } - - template - void WriteData(const T& value, DataBuffer& buffer) { - ToNetwork(value); - TrySwapBytes(value); - buffer << value; - } -}; - -// Read functionality chunk -template -class MessageInterfaceReadBase : public TBase { - public: - void Read(DataBuffer& buffer) { - return ReadImpl(buffer); - } - - protected: - virtual void ReadImpl(DataBuffer& buffer) = 0; -}; - -// Write functionality chunk -template -class MessageInterfaceWriteBase : public TBase { - public: - void Write(DataBuffer& buffer) const { - WriteImpl(buffer); - } - - // helper - DataBuffer Write() const { - DataBuffer buffer; - this->Write(buffer); - return buffer; - } - - protected: - virtual void WriteImpl(DataBuffer& buffer) const = 0; -}; - -// Handler functionality chunk -template -class MessageInterfaceHandlerBase : public TBase { - public: - using HandlerType = typename THandler::HandlerT; - - void Dispatch(HandlerType& handler) const { - DispatchImpl(handler); - } - - protected: - virtual void DispatchImpl(HandlerType& handler) const = 0; -}; - -// Validity functionality chunk -template -class MessageInterfaceValidityBase : public TBase { - public: - bool Valid() const { - return ValidImpl(); - } - - protected: - virtual bool ValidImpl() const = 0; -}; - -// Writing id functionality chunk -template -class MessageInterfaceWriteIdBase : public TBase { - public: - void Write(DataBuffer& buffer) const { - buffer << VarInt{this->GetId()}; - this->WriteImpl(buffer); - } - - // helper - DataBuffer Write() const { - DataBuffer buffer; - this->Write(buffer); - return buffer; - } -}; - -// Debug print functionality chunk -template -class MessageInterfaceToStringBase : public TBase { - public: - friend std::ostream& operator<<(std::ostream& a_Stream, const MessageInterfaceToStringBase& a_Message) { - return a_Message.OpOutImpl(a_Stream); - } - - protected: - virtual std::ostream& OpOutImpl(std::ostream& a_Stream) const = 0; -}; - -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/message/MessageInterfacesOptions.h b/include/sp/protocol/message/MessageInterfacesOptions.h deleted file mode 100644 index a311c16..0000000 --- a/include/sp/protocol/message/MessageInterfacesOptions.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -namespace sp { -namespace details { - - -template -struct MessageInterfaceParsedOptions {}; - -template <> -struct MessageInterfaceParsedOptions<> { - static const bool HasMsgIdType = false; - static const bool HasLittleEndian = false; - static const bool HasReadOperations = false; - static const bool HasWriteOperations = false; - static const bool HasWriteId = false; - static const bool HasHandler = false; - static const bool HasValid = false; - static const bool HasToString = false; -}; - - - - - -template -struct MessageInterfaceParsedOptions, TOptions...> : public MessageInterfaceParsedOptions { - static const bool HasMsgIdType = true; - using MsgIdType = T; -}; - -template -struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { - static const bool HasLittleEndian = true; -}; - -template -struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { - static const bool HasReadOperations = true; -}; - -template -struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { - static const bool HasWriteOperations = true; -}; - -template -struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { - static const bool HasWriteId = true; -}; - -template -struct MessageInterfaceParsedOptions, TOptions...> : public MessageInterfaceParsedOptions { - static const bool HasHandler = true; - using HandlerType = option::Handler; -}; - -template -struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { - static const bool HasValid = true; -}; - -template -struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { - static const bool HasToString = true; -}; - -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/message/MessageOptions.h b/include/sp/protocol/message/MessageOptions.h deleted file mode 100644 index dadd1e0..0000000 --- a/include/sp/protocol/message/MessageOptions.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -namespace sp { -namespace option { - -// Define type used to store message ID -template -struct MsgIdType {}; - -// Enable reading -struct ReadOperations {}; - -// Enable writing -struct WriteOperations {}; - -// Enable id writing -struct WriteId {}; - -// Use little endian for serialisation (instead of default big) -struct LittleEndian {}; - -// Include validity check in public interface -struct ValidCheckInterface {}; - -// Add a ToString() method containing fields -struct DebugPrint {}; - -// Define handler class -template -struct Handler { - using HandlerT = T; -}; - -} // namespace option -} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessagePrinterImpl.h b/include/sp/protocol/message/MessagePrinterImpl.h deleted file mode 100644 index 1b4cfc4..0000000 --- a/include/sp/protocol/message/MessagePrinterImpl.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace sp { -namespace details { - -template -struct IdPrinter {}; - -template <> -struct IdPrinter<> { - static std::string PrintMessageId() { - return ""; - } -}; - -template -struct IdPrinter { - static std::string PrintMessageId() { - return IdPrinter::PrintMessageId(); - } -}; - -template -struct IdPrinter, TOptions...> { - static std::string PrintMessageId() { - return "(Id: " + std::to_string(TId) + ")"; - } -}; - -} // namespace details - -template -std::ostream& operator<<(std::ostream& a_Stream, const std::tuple& a_Fields); - -template -struct FieldPrinter { - static std::ostream& PrintField(std::ostream& a_Stream, const sp::Field& a_Field) { - return a_Stream << sp::Reflector::GetClassName() << "=" << PrintableField(a_Field.GetValue()); - } -}; - -template -struct FieldPrinter, IAlignment> { - static std::ostream& PrintField( - std::ostream& a_Stream, const sp::Field, IAlignment>& a_Field) { - a_Stream << "BitField<" << sp::Reflector::GetClassName() << ">["; - a_Stream << a_Field.GetValue().GetFields() << "]"; - return a_Stream; - } -}; - -template -std::ostream& operator<<(std::ostream& a_Stream, const std::tuple& a_Fields) { - bool first = true; - TupleForEach( - [&a_Stream, &first](const auto& a_Field) { - if (!first) - a_Stream << ", "; - using TField = typename std::decay::type; - constexpr std::size_t alignment = TField::AlignmentValue; - FieldPrinter::PrintField(a_Stream, a_Field); - first = false; - }, - a_Fields); - return a_Stream; -} - -template -std::ostream& operator<<(std::ostream& a_Stream, const sp::MessageBase& a_Message) { - a_Stream << sp::GetBasicClassName(a_Message) << sp::details::IdPrinter::PrintMessageId() << "[" - << a_Message.GetFields() << "]"; - return a_Stream; -} - -} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessagesImpl.h b/include/sp/protocol/message/MessagesImpl.h deleted file mode 100644 index 3afb4b3..0000000 --- a/include/sp/protocol/message/MessagesImpl.h +++ /dev/null @@ -1,178 +0,0 @@ -#pragma once - -namespace sp { - -template -class MessageBase; - -namespace details { - -template -std::string PrintMessage(const MessageBase& a_Message); - -// ID information chunk -template -class MessageImplToStringBase : public TBase { - protected: - virtual std::ostream& OpOutImpl(std::ostream& a_Stream) const override{ - return a_Stream << static_cast(*this); - } -}; - - - -// ID information chunk -template -class MessageImplStaticNumIdBase : public TBase { - public: - // Reuse the message ID type defined in the interface - using MsgIdType = typename TBase::MsgIdType; - - protected: - virtual MsgIdType GetIdImpl() const override { - return static_cast(TId); - } -}; - - - -// Dispatch implementation chunk -template -class MessageImplDispatchBase : public TBase { - public: - // Reuse the Handler type defined in the interface class - using Handler = typename TBase::HandlerType; - - protected: - virtual void DispatchImpl(Handler& handler) const override { - handler.Handle(static_cast(*this)); - } -}; - - - -template -class MessageImplFieldsBase : public TBase { - public: - using AllFields = typename details::FieldsBuilder::Type; - - template - void Construct(Args... args) { - m_Fields = std::make_tuple(args...); - } - - AllFields& GetFields() { - return m_Fields; - } - - const AllFields& GetFields() const { - return m_Fields; - } - - template - auto& GetField() { - return std::get(GetFields()).GetValue(); - } - - template - const auto& GetField() const { - return std::get(GetFields()).GetValue(); - } - - // allow use of enums - template - const auto& GetField() const { - return std::get(FIndex)>(this->GetFields()).GetValue(); - } - - private: - AllFields m_Fields; -}; - -template -class MessageImplFieldsReadBase : public TBase { - private: - // normal reading - template - void ReadField(Field& field, DataBuffer& buffer) { - this->ReadData(field.GetValue(), buffer); - } - - // reading field in bitfield - template - void ReadField(Field& field, TFieldType data, std::size_t offset) { - static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8; - // we suppose that the first element is at the highest bits - field.GetValue() = (data >> TotalBitCount - IAlignment - offset) & ((1 << IAlignment) - 1); - } - - // reading bitfield - template - void ReadField(Field, 0>& field, DataBuffer& buffer) { - TContainer data; - this->ReadData(data, buffer); - std::size_t offset = 0; - TupleForEach( - [data, this, &offset](auto& field) { - this->ReadField(field, data, offset); - offset += field.GetAlignment(); - }, - field.GetValue().GetFields()); - } - - void ReadImpl(DataBuffer& buffer) override { - auto& allFields = this->GetFields(); - TupleForEach([&buffer, this](auto& field) { this->ReadField(field, buffer); }, allFields); - } -}; - -template -class MessageImplFieldsWriteBase : public TBase { - private: - // normal writing - template - void WriteField(const Field& field, DataBuffer& buffer) const { - this->WriteData(field.GetValue(), buffer); - } - - // writing field in bitfield - template - void WriteField(const Field& field, TFieldType& data, std::size_t offset) const { - static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8; - // we suppose that the first element is at the highest bits - data |= (field.GetValue() & ((1 << IAlignment) - 1)) << TotalBitCount - IAlignment - offset; - } - - // writing bitfield - template - void WriteField(const Field, 0>& field, DataBuffer& buffer) const { - TContainer data = 0; - std::size_t offset = 0; - TupleForEach( - [&data, this, &offset](auto& field) { - this->WriteField(field, data, offset); - offset += field.GetAlignment(); - }, - field.GetValue().GetFields()); - this->WriteData(data, buffer); - } - - void WriteImpl(DataBuffer& buffer) const override { - auto& allFields = this->GetFields(); - TupleForEach([&buffer, this](const auto& field) { this->WriteField(field, buffer); }, allFields); - } -}; - -template -class MessageImplFieldsValidBase : public TBase { - protected: - bool ValidImpl() const override { - // Access fields via interface provided in previous chunk - // auto& allFields = TBase::GetFields(); - //... // validate all the fields - return true; - } -}; - -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/message/OstreamFieldIterator.h b/include/sp/protocol/message/OstreamFieldIterator.h deleted file mode 100644 index 6d26c42..0000000 --- a/include/sp/protocol/message/OstreamFieldIterator.h +++ /dev/null @@ -1,56 +0,0 @@ -// infix_iterator.h -// -// Lifted from Jerry Coffin's 's prefix_ostream_iterator -#pragma once - -#include -#include - -namespace sp { - -template > -class OstreamFieldIterator { - private: - std::basic_ostream* m_Os; - std::string m_Delimiter; - bool m_FirstElem; - - public: - using iterator_category = std::output_iterator_tag; - using value_type = void; - using difference_type = void; - using pointer = void; - using reference = void; - - using char_type = charT; - using traits_type = traits; - using ostream_type = std::basic_ostream; - - OstreamFieldIterator(ostream_type& a_Stream) : m_Os(&a_Stream), m_Delimiter(0), m_FirstElem(true) {} - OstreamFieldIterator(ostream_type& a_Stream, std::string&& a_Delimiter) : - m_Os(&a_Stream), m_Delimiter(std::move(a_Delimiter)), m_FirstElem(true) {} - - auto& operator=(const T& item) { - // Here's the only real change from ostream_iterator: - // Normally, the '*m_Os << item;' would come before the 'if'. - if (!m_FirstElem && !m_Delimiter.empty()) - *m_Os << m_Delimiter; - *m_Os << sp::PrintableField(item); - m_FirstElem = false; - return *this; - } - - auto& operator*() { - return *this; - } - - auto& operator++() { - return *this; - } - - auto& operator++(int) { - return *this; - } -}; - -} // namespace sp diff --git a/src/sp/common/VarInt.cpp b/src/sp/common/VarInt.cpp index a2e0913..a02bf6f 100644 --- a/src/sp/common/VarInt.cpp +++ b/src/sp/common/VarInt.cpp @@ -49,8 +49,4 @@ DataBuffer& operator>>(DataBuffer& in, VarInt& var) { return in; } -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_VarInt) { - return a_Stream << a_VarInt.GetValue().GetValue(); -} - } // namespace sp diff --git a/test/test_file.cpp b/test/test_file.cpp deleted file mode 100644 index f777338..0000000 --- a/test/test_file.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include - -#include -#include - -class CustomPacketHandler : public sp::PacketHandler { - void Handle(const KeepAlivePacket& packet) { - std::cout << "KeepAlive handled ! " << packet.GetKeepAliveId() << "\n"; - } - - void Handle(const DisconnectPacket& packet) { - std::cout << "Disconnect handled ! " << packet.GetReason() << "\n"; - } - - void Handle(const UpgradeTowerPacket& packet) { - std::cout << "UpgradeTower handled !\n"; - } -}; - -using FileStream = sp::io::Stream; - -int main() { - auto handler = std::make_shared(); - - FileStream stream(sp::io::File{"test.txt", sp::io::FileTag::In | sp::io::FileTag::Out}, {}); - stream.GetDispatcher().RegisterHandler(PacketId::Disconnect, handler.get()); - stream.GetDispatcher().RegisterHandler(PacketId::KeepAlive, handler.get()); - - stream.SendMessage(KeepAlivePacket{96}); - stream.SendMessage(KeepAlivePacket{69}); - stream.SendMessage(DisconnectPacket{ - "This is in the " - "fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile !"}); - stream.GetOption().m_Enabled = false; - stream.SendMessage(DisconnectPacket{ - "This is in the " - "fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile !"}); - - stream.RecieveMessages(); - - return 0; -} \ No newline at end of file diff --git a/test/test_io.cpp b/test/test_io.cpp deleted file mode 100644 index d67c521..0000000 --- a/test/test_io.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include - -#include -#include - -using DataBufferStream = sp::io::Stream; - -class CustomPacketHandler : public sp::PacketHandler { - void Handle(const KeepAlivePacket& packet) { - std::cout << "KeepAlive handled ! " << packet.GetKeepAliveId() << "\n"; - } - - void Handle(const DisconnectPacket& packet) { - std::cout << "Disconnect handled ! " << packet.GetReason() << "\n"; - } - - void Handle(const UpgradeTowerPacket& packet) { - std::cout << "UpgradeTower handled !\n"; - } -}; - -int main() { - auto handler = std::make_shared(); - - DataBufferStream stream; - stream.GetDispatcher().RegisterHandler(PacketId::Disconnect, handler.get()); - - // this should not be dispatched - stream.SendMessage(KeepAlivePacket{96}); - stream.RecieveMessages(); - - stream.GetDispatcher().RegisterHandler(PacketId::KeepAlive, handler.get()); - - stream.SendMessage(KeepAlivePacket{69}); - stream.RecieveMessages(); - stream.SendMessage(DisconnectPacket{"A valid reason"}); - stream.RecieveMessages(); - - return 0; -} \ No newline at end of file diff --git a/test/test_message.cpp b/test/test_message.cpp new file mode 100644 index 0000000..047ab2a --- /dev/null +++ b/test/test_message.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +#include +#include + +enum class PacketID { KeepAlive = 0}; + +class PacketHandler; + +using PacketBase = sp::MessageBase; + +template +using Message = sp::ConcreteMessage; + +struct KeepAlivePacket { + std::uint64_t m_KeepAlive; +}; + +using KeepAliveMessage = Message; + +using AllMessages = std::tuple; + +class PacketHandler : public sp::GenericHandler {}; + +class MyHandler : public PacketHandler { + public: + virtual void Handle(const KeepAliveMessage& msg) { + std::cout << "yo !" << "\n"; + } +}; + +using PacketDispatcher = sp::MessageDispatcher; + +using PacketFactory = sp::MessageFactory; + +int main() { + KeepAliveMessage m{5U}; + MyHandler h; + PacketDispatcher d; + d.RegisterHandler(PacketID::KeepAlive, &h); + d.Dispatch(m); + PacketFactory f; + auto message = f.CreateMessage(PacketID::KeepAlive); + d.Dispatch(*message); + return 0; +} \ No newline at end of file diff --git a/test/test_packets.cpp b/test/test_packets.cpp deleted file mode 100644 index 47f0fd4..0000000 --- a/test/test_packets.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include - -#include -#include - -#include -#include - -class KeepAliveHandler : public sp::PacketHandler { - void Handle(const KeepAlivePacket& packet) { - std::cout << "KeepAlive handled !!\n"; - std::cout << packet << std::endl; - } - - void Handle(const DisconnectPacket& packet) { - std::cout << "Disconnect handled !\n"; - } - - void Handle(const UpgradeTowerPacket& packet) { - std::cout << "UpgradeTower handled !\n"; - } -}; - -int main() { - std::map> yes = {{"woa", {5, 8}}, {"insane", {6, 9}}}; - auto upgradeTower = std::make_unique(std::make_tuple(666, 9), 789, yes); - auto keepAlive = std::make_unique(6969); - - sp::PacketMessage* msg = upgradeTower.get(); - - auto handler = std::make_shared(); - msg->Dispatch(*handler); - keepAlive->Dispatch(*handler); - - sp::DataBuffer buffer = msg->Write(); - - std::uint8_t msgId; - buffer >> msgId; - - auto upgradeTower2 = std::make_unique(); - upgradeTower2->Read(buffer); - - std::cout << "Test : " << *msg << "\n"; - - sp::PacketFactory factory; - auto packet = factory.CreateMessage(msgId); - if (packet == nullptr) { - std::cout << "Bad ID !\n"; - return 1; - } - std::cout << (unsigned)packet->GetId() << std::endl; - packet->Dispatch(*handler); - - sp::PacketDispatcher dispatcher; - dispatcher.RegisterHandler(PacketId::KeepAlive, handler.get()); - dispatcher.Dispatch(*packet); - dispatcher.UnregisterHandler(PacketId::KeepAlive, handler.get()); - dispatcher.UnregisterHandler(handler.get()); - - return 0; -} \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index 1785976..1a9d65c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,5 +1,9 @@ add_rules("mode.debug", "mode.release") +add_requires("boost_pfr") + +set_warnings("all") + set_languages("c++17") local modules = { @@ -71,6 +75,7 @@ target("SimpleProtocol") add_files("src/sp/**.cpp") set_group("Library") set_kind("$(kind)") + add_packages("boost_pfr", {public = true}) add_headerfiles("include/(sp/**.h)", "include/(sp/**.inl)") -- 2.49.1 From 073872df944a4639124f3d40cf67a9743e54926d Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 26 Jun 2025 14:43:27 +0200 Subject: [PATCH 2/9] byte swapping --- include/sp/common/ByteSwapping.h | 17 -- include/sp/default/DefaultPacket.h | 31 ---- include/sp/default/DefaultPacketDispatcher.h | 15 -- include/sp/default/DefaultPacketFactory.h | 7 - include/sp/default/DefaultPacketHandler.h | 9 -- include/sp/protocol/ConcreteMessage.h | 2 +- include/sp/protocol/MessagePrinter.h | 61 -------- src/sp/common/ByteSwapping.cpp | 45 ++---- src/sp/io/File.cpp | 2 +- test/test_message.cpp | 2 +- test/type_name.hpp | 155 +++++++++++++++++++ 11 files changed, 168 insertions(+), 178 deletions(-) delete mode 100644 include/sp/default/DefaultPacket.h delete mode 100644 include/sp/default/DefaultPacketDispatcher.h delete mode 100644 include/sp/default/DefaultPacketFactory.h delete mode 100644 include/sp/default/DefaultPacketHandler.h delete mode 100644 include/sp/protocol/MessagePrinter.h create mode 100644 test/type_name.hpp diff --git a/include/sp/common/ByteSwapping.h b/include/sp/common/ByteSwapping.h index 907c215..19578a5 100644 --- a/include/sp/common/ByteSwapping.h +++ b/include/sp/common/ByteSwapping.h @@ -4,8 +4,6 @@ namespace sp { -bool IsSystemBigEndian(); - /** * \brief Serialize value to (network byte order) big endian */ @@ -36,19 +34,4 @@ void FromNetwork(std::uint32_t& value); template <> void FromNetwork(std::uint64_t& value); -/** - * \brief Swap bytes if the value is any kind of integer - */ -template -void TrySwapBytes(T& value) {} - -template <> -void TrySwapBytes(std::uint16_t& value); - -template <> -void TrySwapBytes(std::uint32_t& value); - -template <> -void TrySwapBytes(std::uint64_t& value); - } // namespace sp diff --git a/include/sp/default/DefaultPacket.h b/include/sp/default/DefaultPacket.h deleted file mode 100644 index 3882a40..0000000 --- a/include/sp/default/DefaultPacket.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - - -namespace sp { -class PacketHandler; - -using PacketID = std::uint8_t; - -using PacketMessage = Message< - option::MsgIdType, // add id() operation - option::Handler // add dispatch() operation - >; - -#define PacketConstructor(packetName) \ - packetName##Packet() {} \ - template \ - packetName##Packet(Args... args) { \ - Construct(args...); \ - } - -#define DeclarePacket(packetName) \ - class packetName##Packet : public sp::MessageBase, sp::option::FieldsImpl>, \ - sp::option::ToStringImpl - - - -} // namespace sp diff --git a/include/sp/default/DefaultPacketDispatcher.h b/include/sp/default/DefaultPacketDispatcher.h deleted file mode 100644 index aeb7fd5..0000000 --- a/include/sp/default/DefaultPacketDispatcher.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace sp { - -using PacketDispatcher = MessageDispatcher< - PacketMessage::ParsedOptions::MsgIdType, - PacketMessage, - PacketMessage::ParsedOptions::HandlerType::HandlerT - >; - -} // namespace sp diff --git a/include/sp/default/DefaultPacketFactory.h b/include/sp/default/DefaultPacketFactory.h deleted file mode 100644 index 8f7e4bd..0000000 --- a/include/sp/default/DefaultPacketFactory.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -namespace sp { -using PacketFactory = sp::MessageFactory; -} // namespace sp diff --git a/include/sp/default/DefaultPacketHandler.h b/include/sp/default/DefaultPacketHandler.h deleted file mode 100644 index 6be435d..0000000 --- a/include/sp/default/DefaultPacketHandler.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -// the tuple AllPackets must be defined ! - -namespace sp { -class PacketHandler : public sp::GenericHandler {}; -} // namespace sp diff --git a/include/sp/protocol/ConcreteMessage.h b/include/sp/protocol/ConcreteMessage.h index b057276..78bc34a 100644 --- a/include/sp/protocol/ConcreteMessage.h +++ b/include/sp/protocol/ConcreteMessage.h @@ -14,7 +14,7 @@ class ConcreteMessage : public MessageBase { virtual ~ConcreteMessage() {} - virtual constexpr TMessageID GetId() const override { + virtual TMessageID GetId() const override { return ID; } diff --git a/include/sp/protocol/MessagePrinter.h b/include/sp/protocol/MessagePrinter.h deleted file mode 100644 index 7c3b8c7..0000000 --- a/include/sp/protocol/MessagePrinter.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace sp { - -template ::value, bool> = true> -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_Field) { - return a_Stream << std::to_string(a_Field.GetValue()); -} - -inline std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_Field) { - return a_Stream << a_Field.GetValue(); -} - -inline std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_Field) { - return a_Stream << "\"" << a_Field.GetValue() << "\""; -} - - - - - -template -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data); - -template -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data); - -template -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data); - - - - - -template -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data) { - return a_Stream << PrintableField(a_Data.GetValue().first) << " => " << PrintableField(a_Data.GetValue().second); -} - -template -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data) { - a_Stream << "{"; - std::copy(a_Data.GetValue().begin(), a_Data.GetValue().end(), OstreamFieldIterator>(std::cout, ", ")); - return a_Stream << "}"; -} - -template -std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data) { - a_Stream << "{"; - std::copy(a_Data.GetValue().begin(), a_Data.GetValue().end(), OstreamFieldIterator(std::cout, ", ")); - return a_Stream << "}"; -} - -} // namespace sp diff --git a/src/sp/common/ByteSwapping.cpp b/src/sp/common/ByteSwapping.cpp index b6f15a2..3a02546 100644 --- a/src/sp/common/ByteSwapping.cpp +++ b/src/sp/common/ByteSwapping.cpp @@ -1,27 +1,21 @@ #include #ifdef _WIN32 + #include + #else + #include +#include + +#define htonll htobe64 +#define ntohll be64toh + #endif -#include - namespace sp { -template -void SwapBytes(T& value) { - char* ptr = reinterpret_cast(&value); - std::reverse(ptr, ptr + sizeof(T)); -} - -bool IsSystemBigEndian() { - static constexpr std::uint16_t test = 10; - static const bool isBigEndian = reinterpret_cast(&test)[1] == 10; - return isBigEndian; -} - template <> void ToNetwork(std::uint16_t& value) { value = htons(value); @@ -34,9 +28,7 @@ void ToNetwork(std::uint32_t& value) { template <> void ToNetwork(std::uint64_t& value) { - if (IsSystemBigEndian()) - return; - SwapBytes(value); + value = htonll(value); } template <> @@ -51,24 +43,7 @@ void FromNetwork(std::uint32_t& value) { template <> void FromNetwork(std::uint64_t& value) { - if (IsSystemBigEndian()) - return; - SwapBytes(value); -} - -template <> -void TrySwapBytes(std::uint16_t& value) { - SwapBytes(value); -} - -template <> -void TrySwapBytes(std::uint32_t& value) { - SwapBytes(value); -} - -template <> -void TrySwapBytes(std::uint64_t& value) { - SwapBytes(value); + value = ntohll(value); } } // namespace sp diff --git a/src/sp/io/File.cpp b/src/sp/io/File.cpp index 2bccc19..300c0aa 100644 --- a/src/sp/io/File.cpp +++ b/src/sp/io/File.cpp @@ -11,7 +11,7 @@ File::IOInterface(const std::string& a_FilePath, unsigned int a_OpenMode) { } File::IOInterface(File&& other) : - m_FileOutput(std::move(other.m_FileOutput)), m_FileInput(std::move(other.m_FileInput)) {} + m_FileInput(std::move(other.m_FileInput)), m_FileOutput(std::move(other.m_FileOutput)) {} DataBuffer File::Read(std::size_t a_Amount) { DataBuffer buffer; diff --git a/test/test_message.cpp b/test/test_message.cpp index 047ab2a..f135d7b 100644 --- a/test/test_message.cpp +++ b/test/test_message.cpp @@ -37,7 +37,7 @@ using PacketDispatcher = sp::MessageDispatcher; using PacketFactory = sp::MessageFactory; int main() { - KeepAliveMessage m{5U}; + KeepAliveMessage m{5UL}; MyHandler h; PacketDispatcher d; d.RegisterHandler(PacketID::KeepAlive, &h); diff --git a/test/type_name.hpp b/test/type_name.hpp new file mode 100644 index 0000000..f8afd63 --- /dev/null +++ b/test/type_name.hpp @@ -0,0 +1,155 @@ +// Copyright (c) 2018 Will Wray https://keybase.io/willwray +// +// Distributed under the Boost Software License, Version 1.0. +// (http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include +#include +#include + +#if __has_include() +# include +# include +# include +#endif + +constexpr bool CXXABI +{ +#if __has_include() + true +#endif +}; + +/* + "type_name_rt.hpp": get type names at runtime (hence 'rt') + ^^^^^^^^^^^^^^^^ + This header defines, in global scope (i.e. not namespace'd): + (1) A function template type_name_str() for extracting a type's name. + (2) A variable template type_name_rt initialized to the type's name. + (also an incomplete class template IdT, an implementation detail.) + The template type parameter T is mapped to a readable name for the type. + The work is done at runtime by what is the most standard current method; + runtime type information (RTTI) and, on CXXABI, a demangle call + (for compile-time alternatives see type_name_pt or type_name_ct). + (1) type_name_str() + Returns a std::string copy of the demangled typeid name. + On each call it does all the work, and cleans it all up + (i.e. it frees any demangle allocation once copied from). + + (2) type_name_rt + A std::string_view global constant (a view into the + demangle buffer, on CXXABI, which is not ever free'd). + All work is done in static initialization, before main() + + Failure is signalled by an empty return value; "" + (indicates a demangle failure as typeid is assumed fail-safe). + Requirements: + C++17 for string_view, constexpr-if, CTAD (unique_ptr) and __has_include + RTTI, the compiler's runtime type information, must be enabled + Dependencies: ,, for std::conditional + (RTTI) + for typeid(T).name(), an implementation-defined name. + (on CXXABI platforms only - GCC, Clang, etc.) + for abi::__cxa_demangle(name,...) + to map typeid(T).name to a human readable name for T. + for std::free, for std::unique_ptr + E.g. + int i; + std::cout << type_name_rt << "\n^^^ tada!"; + --- stdout --- + int + ^^^ tada! +*/ + +// IdT wraps T as template param so typeid can't decay ref or cv quals. +// An implementation detail; must be a 3-character id, any 3 chars will do. +template struct IdT {}; + +namespace impl +{ +// demangle( const char* name) +// +// (1) On non-CXXABI returns name, regardless of the template parameter. +// i.e. the function does nothing but return its parameter, a char*. +// +// (2) On CXXABI the demangle ABI is called and the result is returned +// with return type depending on the boolean template argument: +// (a) char* by default (Free=false). Any demangle malloc is not free'd. +// (b) unique_ptr (Free=true) to RAII-free any malloc'd chars. +// +// The input name should be a valid mangled name like typeid(T).name() +// Null return value implies demangle fail (no malloc, free is harmless). +// +template +auto +demangle(char const* name) noexcept(!CXXABI) +{ + if constexpr (!CXXABI) { + return name; // NOP: assume already demangled if not on CXXABI + } else { + auto dmg = abi::__cxa_demangle(name, nullptr, nullptr, nullptr); + if constexpr (Free) + return std::unique_ptr( dmg, std::free); + else + return dmg; + } +} + +// prefix_len (constant): prefix length of demangled typeid(IdT) +// for different compilers (remove 4 chars "int>" from the length) +size_t IdT_prefix_len() +{ + static size_t const len = std::strlen(demangle<>(typeid(IdT).name())) + - std::strlen("int>"); + return len; +} + +template +using remove_cvref_t = std::remove_cv_t>; + +template +inline constexpr bool is_cvref_v = !std::is_same_v>; + +// type_name_rt() Returns string, frees any malloc from ABI demangle +// type_name_rt() Returns string_view, does not free demangle malloc +// +template +auto +type_name_rt() noexcept(!CXXABI) -> std::conditional_t +{ + if constexpr (!is_cvref_v) + { + if (auto dmg = demangle(typeid(T).name())) + { + return { &*dmg }; + } + } + else // wrap all cvref types for now - maybe only do functions and arrays + { + if (auto dmg = demangle(typeid(IdT).name())) + { + size_t const p = IdT_prefix_len(); + return { &*dmg + p, std::strlen(&*dmg) - p - 1 }; + } + } + return ""; +} +} // namespace impl + +// type_name_str() Returns a std::string copy of the demangled typeid name. +// +template +std::string +const type_name_str() { return impl::type_name_rt(); } + +// type_name_rt Global constant; "The Demangle that Never Dangles" +// +template +inline +std::string_view +const type_name_rt = impl::type_name_rt(); \ No newline at end of file -- 2.49.1 From 59bedd648278728e71e978a3fc33f13da377de8d Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 26 Jun 2025 16:02:18 +0200 Subject: [PATCH 3/9] read/write for basic types --- .gitignore | 4 ++- include/sp/protocol/ConcreteMessage.h | 12 ++++++++- include/sp/protocol/MessageBase.h | 5 ++++ include/sp/protocol/MessageIO.h | 38 +++++++++++++++++++++++++++ test/test_message.cpp | 18 ++++++++++++- xmake.lua | 2 ++ 6 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 include/sp/protocol/MessageIO.h diff --git a/.gitignore b/.gitignore index fb11540..c1251e7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ build/ .DS_Store -.vscode \ No newline at end of file +.vscode + +*.bin \ No newline at end of file diff --git a/include/sp/protocol/ConcreteMessage.h b/include/sp/protocol/ConcreteMessage.h index 78bc34a..2a0da69 100644 --- a/include/sp/protocol/ConcreteMessage.h +++ b/include/sp/protocol/ConcreteMessage.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace sp { @@ -8,6 +9,7 @@ template class ConcreteMessage : public MessageBase { public: using DataType = TData; + using ThisType = ConcreteMessage; template ConcreteMessage(const T&... args) : m_Data{args...} {} @@ -19,7 +21,15 @@ class ConcreteMessage : public MessageBase { } virtual void Dispatch(THandler& handler) const override { - handler.Handle(static_cast&>(*this)); + handler.Handle(static_cast(*this)); + } + + virtual void Read(std::istream& a_Is) override { + details::ReadMessage(a_Is, m_Data); + } + + virtual void Write(std::ostream& a_Os) const override { + details::WriteMessage(a_Os, m_Data); } private: diff --git a/include/sp/protocol/MessageBase.h b/include/sp/protocol/MessageBase.h index 5fd2441..ba3e10b 100644 --- a/include/sp/protocol/MessageBase.h +++ b/include/sp/protocol/MessageBase.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace sp { template @@ -14,6 +16,9 @@ class MessageBase { virtual MessageIdType GetId() const = 0; virtual void Dispatch(HandlerType& handler) const = 0; + + virtual void Read(std::istream& a_Is) = 0; + virtual void Write(std::ostream& a_Os) const = 0; }; } // namespace sp diff --git a/include/sp/protocol/MessageIO.h b/include/sp/protocol/MessageIO.h new file mode 100644 index 0000000..d3661f5 --- /dev/null +++ b/include/sp/protocol/MessageIO.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace sp { +namespace details { + +template +void WriteField(std::ostream& a_Os, const T& a_Data) { + T swapped = a_Data; + ToNetwork(swapped); + a_Os.write(reinterpret_cast(&swapped), sizeof(a_Data)); +} + +template +void ReadField(std::istream& a_Is, T& a_Data) { + a_Is.read(reinterpret_cast(&a_Data), sizeof(a_Data)); + FromNetwork(a_Data); +} + +template +void WriteMessage(std::ostream& a_Os, const TData& a_MessageData) { + boost::pfr::for_each_field(a_MessageData, [&a_Os](const auto& a_Field) { + WriteField(a_Os, a_Field); + }); +} + +template +void ReadMessage(std::istream& a_Is, TData& a_MessageData) { + boost::pfr::for_each_field(a_MessageData, [&a_Is](auto& a_Field) { + ReadField(a_Is, a_Field); + }); +} + +} // namespace details +} // namespace sp diff --git a/test/test_message.cpp b/test/test_message.cpp index f135d7b..5842e70 100644 --- a/test/test_message.cpp +++ b/test/test_message.cpp @@ -5,8 +5,9 @@ #include #include +#include -enum class PacketID { KeepAlive = 0}; +enum class PacketID { KeepAlive = 0 }; class PacketHandler; @@ -38,6 +39,9 @@ using PacketFactory = sp::MessageFactory; int main() { KeepAliveMessage m{5UL}; + + // dispatch tests + MyHandler h; PacketDispatcher d; d.RegisterHandler(PacketID::KeepAlive, &h); @@ -45,5 +49,17 @@ int main() { PacketFactory f; auto message = f.CreateMessage(PacketID::KeepAlive); d.Dispatch(*message); + + + + // write tests + + std::ofstream file {"test.bin"}; + + message->Write(file); + // file << std::endl; + m.Write(file); + // file << std::endl; + // message->Read(file); return 0; } \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index 1a9d65c..e925610 100644 --- a/xmake.lua +++ b/xmake.lua @@ -106,6 +106,8 @@ for _, file in ipairs(os.files("test/**.cpp")) do add_files(file) add_includedirs("include") + set_rundir(".") + add_deps("SimpleProtocol") add_tests("compile_and_run") -- 2.49.1 From 0d26879152589c6fa2ad82dca924a241c106d94a Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 26 Jun 2025 19:17:52 +0200 Subject: [PATCH 4/9] feat: add streams --- .../sp/{protocol => common}/GenericHandler.h | 0 include/sp/common/Reflection.h | 60 ------- include/sp/common/VarInt.h | 5 +- include/sp/io/File.h | 33 ---- include/sp/io/IOInterface.h | 58 ------- include/sp/io/IOInterfaceImpl.inl | 129 -------------- include/sp/io/IoInterface.h | 13 ++ include/sp/io/Memory.h | 23 --- include/sp/io/MessageEncapsulator.h | 16 ++ include/sp/io/MessageIO.h | 36 ++++ include/sp/io/MessageStream.h | 34 ++++ include/sp/io/MessageStream.inl | 63 +++++++ include/sp/io/StdIo.h | 40 +++++ include/sp/protocol/ConcreteMessage.h | 13 +- include/sp/protocol/MessageBase.h | 5 +- include/sp/protocol/MessageFactory.h | 1 + include/sp/protocol/MessageHandler.h | 133 ++++++++++++++ include/sp/protocol/MessageIO.h | 38 ---- src/sp/common/VarInt.cpp | 17 +- src/sp/extensions/Compress.cpp | 89 ---------- src/sp/extensions/TcpListener.cpp | 114 ------------ src/sp/extensions/TcpSocket.cpp | 164 ------------------ src/sp/io/File.cpp | 31 ---- src/sp/io/Memory.cpp | 16 -- test/test_message.cpp | 32 +++- 25 files changed, 385 insertions(+), 778 deletions(-) rename include/sp/{protocol => common}/GenericHandler.h (100%) delete mode 100644 include/sp/common/Reflection.h delete mode 100644 include/sp/io/File.h delete mode 100644 include/sp/io/IOInterface.h delete mode 100644 include/sp/io/IOInterfaceImpl.inl create mode 100644 include/sp/io/IoInterface.h delete mode 100644 include/sp/io/Memory.h create mode 100644 include/sp/io/MessageEncapsulator.h create mode 100644 include/sp/io/MessageIO.h create mode 100644 include/sp/io/MessageStream.h create mode 100644 include/sp/io/MessageStream.inl create mode 100644 include/sp/io/StdIo.h create mode 100644 include/sp/protocol/MessageHandler.h delete mode 100644 include/sp/protocol/MessageIO.h diff --git a/include/sp/protocol/GenericHandler.h b/include/sp/common/GenericHandler.h similarity index 100% rename from include/sp/protocol/GenericHandler.h rename to include/sp/common/GenericHandler.h diff --git a/include/sp/common/Reflection.h b/include/sp/common/Reflection.h deleted file mode 100644 index d0fcc4c..0000000 --- a/include/sp/common/Reflection.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include - -namespace sp { - -template -std::string GetBasicClassName(const T& a_Value) { - int status; - char* demangled = abi::__cxa_demangle(typeid(a_Value).name(), 0, 0, &status); - if (status != 0) - return ""; - return std::string(demangled); -} - -template -std::string GetBasicClassName() { - int status; - char* demangled = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status); - if (status != 0) - return ""; - return std::string(demangled); -} - -template -struct Reflector { - static std::string GetClassName() { - return GetBasicClassName(); - } -}; - -template <> -struct Reflector { - static std::string GetClassName() { - return "std::string"; - } -}; - -template -struct Reflector> { - static std::string GetClassName(); -}; - -template -struct Reflector> { - static std::string GetClassName(); -}; - -template -std::string Reflector>::GetClassName() { - return "std::vector<" + Reflector::GetClassName() + ">"; -} - -template -std::string Reflector>::GetClassName() { - return "std::map<" + Reflector::GetClassName() + ", " + Reflector::GetClassName() + ">"; -} - -} // namespace sp diff --git a/include/sp/common/VarInt.h b/include/sp/common/VarInt.h index 2f0bf6b..7608dfb 100644 --- a/include/sp/common/VarInt.h +++ b/include/sp/common/VarInt.h @@ -7,12 +7,14 @@ #include #include -#include +#include namespace sp { class DataBuffer; +using ReadFunc = std::function; + /** * \class VarInt * \brief Variable-length format such that smaller numbers use fewer bytes. @@ -57,6 +59,7 @@ class VarInt { */ friend DataBuffer& operator>>(DataBuffer& in, VarInt& var); + void Read(const ReadFunc& read); }; } // namespace sp diff --git a/include/sp/io/File.h b/include/sp/io/File.h deleted file mode 100644 index 2b329e6..0000000 --- a/include/sp/io/File.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -namespace sp { -namespace io { - -struct FileTag { - enum OpenMode { - In = 1, - Out = 1 << 1, - }; -}; - -template <> -class IOInterface { - private: - std::unique_ptr m_FileInput; - std::unique_ptr m_FileOutput; - - public: - IOInterface(const std::string& a_FilePath, unsigned int a_OpenMode); - IOInterface(IOInterface&& other); - - DataBuffer Read(std::size_t a_Amount); - void Write(const sp::DataBuffer& a_Data); -}; - -using File = IOInterface; - -} // namespace io -} // namespace sp diff --git a/include/sp/io/IOInterface.h b/include/sp/io/IOInterface.h deleted file mode 100644 index aca8cbd..0000000 --- a/include/sp/io/IOInterface.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include -#include - -namespace sp { -namespace io { - -template -class IOInterface { - public: - DataBuffer Read(std::size_t a_Amount); - void Write(const DataBuffer& a_Data); -}; - -template -class MessageEncapsulator { - public: - static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option); - static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option); -}; - -template -class Stream { - protected: - MessageDispatcher m_Dispatcher; - IOInterface m_Interface; - std::tuple m_Options; - - using MessageBase = typename MessageDispatcher::MessageBaseType; - - public: - Stream() {} - Stream(IOInterface&& a_Interface, TOptions&&... a_Options); - Stream(Stream&& a_Stream); - - void RecieveMessages(); - void SendMessage(const MessageBase& a_Message); - - - template - TOption& GetOption() { - return std::get(m_Options); - } - - MessageDispatcher& GetDispatcher() { - return m_Dispatcher; - } - - private: - static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOptions&... a_Options); - static DataBuffer Decapsulate(DataBuffer& a_Data, const TOptions&... a_Options); -}; - -} // namespace io -} // namespace sp - -#include \ No newline at end of file diff --git a/include/sp/io/IOInterfaceImpl.inl b/include/sp/io/IOInterfaceImpl.inl deleted file mode 100644 index 12e153b..0000000 --- a/include/sp/io/IOInterfaceImpl.inl +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include -#include - -namespace sp { - -namespace details { - -template -struct MessageEncapsulatorPack {}; - -template <> -struct MessageEncapsulatorPack<> { - static DataBuffer Encapsulate(const DataBuffer& a_Data) { - return a_Data; - } - static DataBuffer Decapsulate(DataBuffer& a_Data) { - return a_Data; - } -}; - -template -struct MessageEncapsulatorPack { - static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) { - DataBuffer data = io::MessageEncapsulator::Encapsulate(a_Data, a_Option); - return MessageEncapsulatorPack::Encapsulate(data, a_Options...); - } - static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) { - DataBuffer data = io::MessageEncapsulator::Decapsulate(a_Data, a_Option); - return MessageEncapsulatorPack::Decapsulate(data, a_Options...); - } -}; - -} // namespace details - -namespace io { - -template -Stream::Stream(IOInterface&& a_Interface, TOptions&&... a_Options) : - m_Interface(std::move(a_Interface)), m_Options(std::make_tuple(std::move(a_Options)...)) {} - -template -Stream::Stream( - Stream&& a_Stream) : - m_Dispatcher(std::move(a_Stream.m_Dispatcher)), m_Interface(std::move(a_Stream.m_Interface)) {} - -template -void Stream::SendMessage(const MessageBase& a_Message) { - DataBuffer data = a_Message.Write(); - TupleForEach([&data](const auto&... a_Options) { - data = Encapsulate(data, a_Options...); - }, m_Options); - DataBuffer finalData; - finalData.Reserve(data.GetSize() + sizeof(VarInt::MAX_VALUE)); - finalData << VarInt{data.GetSize()} << data; - m_Interface.Write(finalData); -} - -template -void Stream::RecieveMessages() { - while (true) { - // reading the first VarInt part byte by byte - std::uint64_t lenghtValue = 0; - unsigned int readPos = 0; - - while (true) { - static constexpr int SEGMENT_BITS = (1 << 7) - 1; - static constexpr int CONTINUE_BIT = 1 << 7; - - DataBuffer buffer = m_Interface.Read(sizeof(std::uint8_t)); - - // eof - if (buffer.GetSize() == 0) - return; - - std::uint8_t part; - buffer >> part; - lenghtValue |= static_cast(part & SEGMENT_BITS) << readPos; - - if ((part & CONTINUE_BIT) == 0) - break; - - readPos += 7; - - if (readPos >= 8 * sizeof(lenghtValue)) - throw std::runtime_error("VarInt is too big"); - } - - // nothing to read - if (lenghtValue == 0) - return; - - DataBuffer buffer; - buffer = m_Interface.Read(lenghtValue); - - TupleForEach([&buffer, lenghtValue](const auto&... a_Options) { - buffer = Decapsulate(buffer, a_Options...); - }, m_Options); - - VarInt packetType; - buffer >> packetType; - - static const MessageFactory messageFactory; - - std::unique_ptr message = messageFactory.CreateMessage(packetType.GetValue()); - - assert(message != nullptr); - - message->Read(buffer); - - GetDispatcher().Dispatch(*message); - } -} - -template -DataBuffer Stream::Encapsulate( - const DataBuffer& a_Data, const TOptions&... a_Options) { - return details::MessageEncapsulatorPack::Encapsulate(a_Data, a_Options...); -} - -template -DataBuffer Stream::Decapsulate( - DataBuffer& a_Data, const TOptions&... a_Options) { - return details::MessageEncapsulatorPack::Decapsulate(a_Data, a_Options...); -} - -} // namespace io -} // namespace sp \ No newline at end of file diff --git a/include/sp/io/IoInterface.h b/include/sp/io/IoInterface.h new file mode 100644 index 0000000..037dfef --- /dev/null +++ b/include/sp/io/IoInterface.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace sp { + +class IoInterface { + public: + virtual DataBuffer Read(std::size_t a_Amount) = 0; + virtual void Write(const DataBuffer& a_Data) = 0; +}; + +} // namespace sp diff --git a/include/sp/io/Memory.h b/include/sp/io/Memory.h deleted file mode 100644 index 83021bc..0000000 --- a/include/sp/io/Memory.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -namespace sp { -namespace io { - -struct MemoryTag {}; - -template <> -class IOInterface { - private: - sp::DataBuffer m_VirtualIO; - - public: - sp::DataBuffer Read(std::size_t a_Amount); - void Write(const sp::DataBuffer& a_Data); -}; - -using Memory = IOInterface; - -} // namespace io -} // namespace sp diff --git a/include/sp/io/MessageEncapsulator.h b/include/sp/io/MessageEncapsulator.h new file mode 100644 index 0000000..d8796fd --- /dev/null +++ b/include/sp/io/MessageEncapsulator.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace sp { + +class MessageEncapsulator { + public: + MessageEncapsulator() {} + virtual ~MessageEncapsulator() {} + + virtual DataBuffer Encapsulate(const DataBuffer& a_Data) = 0; + virtual DataBuffer Decapsulate(DataBuffer& a_Data) = 0; +}; + +} // namespace sp diff --git a/include/sp/io/MessageIO.h b/include/sp/io/MessageIO.h new file mode 100644 index 0000000..9dc5440 --- /dev/null +++ b/include/sp/io/MessageIO.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace sp { +namespace details { + +template +void WriteField(DataBuffer& a_Buffer, const T& a_Data) { + T swapped = a_Data; + ToNetwork(swapped); + a_Buffer << swapped; +} + +template +void ReadField(DataBuffer& a_Buffer, T& a_Data) { + a_Buffer >> a_Data; + FromNetwork(a_Data); +} + +template +DataBuffer WriteMessage(const TData& a_MessageData) { + DataBuffer buffer; + boost::pfr::for_each_field(a_MessageData, [&buffer](const auto& a_Field) { WriteField(buffer, a_Field); }); + return buffer; +} + +template +void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) { + boost::pfr::for_each_field(a_MessageData, [&a_Buffer](auto& a_Field) { ReadField(a_Buffer, a_Field); }); +} + +} // namespace details +} // namespace sp diff --git a/include/sp/io/MessageStream.h b/include/sp/io/MessageStream.h new file mode 100644 index 0000000..c5442f8 --- /dev/null +++ b/include/sp/io/MessageStream.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include + +namespace sp { + +template +class MessageStream { + private: + std::vector m_Encapsulators; + std::shared_ptr m_Stream; + + using MessageBaseType = typename TMessageFactory::MessageBaseType; + using MessageIdType = typename MessageBaseType::MessageIdType; + + public: + MessageStream(std::shared_ptr&& a_Stream) : m_Stream(std::move(a_Stream)) {} + + std::unique_ptr ReadMessage(); + std::unique_ptr ReadMessage(MessageIdType a_Id); + + void WriteMessage(const MessageBaseType& a_Message, bool a_WriteId = true); + + private: + DataBuffer ReadAndDecapsulate(); + std::unique_ptr MakeMessage(DataBuffer& buffer, MessageIdType a_Id); +}; + +} // namespace sp + +#include \ No newline at end of file diff --git a/include/sp/io/MessageStream.inl b/include/sp/io/MessageStream.inl new file mode 100644 index 0000000..98231f9 --- /dev/null +++ b/include/sp/io/MessageStream.inl @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +namespace sp { + +template +DataBuffer MessageStream::ReadAndDecapsulate() { + VarInt messageLength; + messageLength.Read([this](std::uint8_t& data) { m_Stream->Read(1) >> data; }); + std::size_t amount = messageLength.GetValue(); + DataBuffer buffer = m_Stream->Read(amount); + + for (MessageEncapsulator& enc : m_Encapsulators) { + buffer = enc.Decapsulate(buffer); + } + + return buffer; +} + +template +std::unique_ptr MessageStream::MakeMessage(DataBuffer& buffer, MessageIdType a_Id) { + static const TMessageFactory FACTORY; + auto message = FACTORY.CreateMessage(a_Id); + message->Read(buffer); + return message; +} + +template +std::unique_ptr MessageStream::ReadMessage(MessageIdType a_Id) { + + DataBuffer buffer = ReadAndDecapsulate(); + + return MakeMessage(buffer, a_Id); +} + +template +std::unique_ptr MessageStream::ReadMessage() { + DataBuffer buffer = ReadAndDecapsulate(); + + VarInt messageId; + buffer >> messageId; + + return MakeMessage(buffer, MessageIdType(messageId.GetValue())); +} + +template +void MessageStream::WriteMessage(const MessageBaseType& a_Message, bool a_WriteId) { + DataBuffer buffer; + if (a_WriteId) + buffer << VarInt{static_cast(a_Message.GetId())}; + buffer << a_Message.Write(); + for (MessageEncapsulator& enc : m_Encapsulators) { + buffer = enc.Encapsulate(buffer); + } + DataBuffer header; + header << VarInt{buffer.GetSize()}; + m_Stream->Write(header); + m_Stream->Write(buffer); +} + +} // namespace sp diff --git a/include/sp/io/StdIo.h b/include/sp/io/StdIo.h new file mode 100644 index 0000000..50de28e --- /dev/null +++ b/include/sp/io/StdIo.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace sp { + +class StdInput : public IoInterface { + private: + std::istream& m_Io; + + public: + StdInput(std::istream& a_Io) : m_Io(a_Io) {} + + virtual DataBuffer Read(std::size_t a_Amount) override { + DataBuffer buffer(a_Amount); + m_Io.read(reinterpret_cast(buffer.data()), a_Amount); + return buffer; + } + + virtual void Write(const DataBuffer& a_Data) override {} +}; + +class StdOuput : public IoInterface { + private: + std::ostream& m_Io; + + public: + StdOuput(std::ostream& a_Io) : m_Io(a_Io) {} + + virtual DataBuffer Read(std::size_t a_Amount) override { + return {}; + } + + virtual void Write(const DataBuffer& a_Data) override { + m_Io.write(reinterpret_cast(a_Data.data()), a_Data.GetSize()); + } +}; + +} // namespace sp diff --git a/include/sp/protocol/ConcreteMessage.h b/include/sp/protocol/ConcreteMessage.h index 2a0da69..f1a4960 100644 --- a/include/sp/protocol/ConcreteMessage.h +++ b/include/sp/protocol/ConcreteMessage.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace sp { @@ -9,7 +9,6 @@ template class ConcreteMessage : public MessageBase { public: using DataType = TData; - using ThisType = ConcreteMessage; template ConcreteMessage(const T&... args) : m_Data{args...} {} @@ -21,15 +20,15 @@ class ConcreteMessage : public MessageBase { } virtual void Dispatch(THandler& handler) const override { - handler.Handle(static_cast(*this)); + handler.Handle(static_cast(m_Data)); } - virtual void Read(std::istream& a_Is) override { - details::ReadMessage(a_Is, m_Data); + virtual void Read(DataBuffer& a_Buffer) override { + details::ReadMessage(a_Buffer, m_Data); } - virtual void Write(std::ostream& a_Os) const override { - details::WriteMessage(a_Os, m_Data); + virtual DataBuffer Write() const override { + return details::WriteMessage(m_Data); } private: diff --git a/include/sp/protocol/MessageBase.h b/include/sp/protocol/MessageBase.h index ba3e10b..4db490b 100644 --- a/include/sp/protocol/MessageBase.h +++ b/include/sp/protocol/MessageBase.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace sp { @@ -17,8 +18,8 @@ class MessageBase { virtual void Dispatch(HandlerType& handler) const = 0; - virtual void Read(std::istream& a_Is) = 0; - virtual void Write(std::ostream& a_Os) const = 0; + virtual void Read(DataBuffer& a_Buffer) = 0; + virtual DataBuffer Write() const = 0; }; } // namespace sp diff --git a/include/sp/protocol/MessageFactory.h b/include/sp/protocol/MessageFactory.h index c7c5d59..0957977 100644 --- a/include/sp/protocol/MessageFactory.h +++ b/include/sp/protocol/MessageFactory.h @@ -12,6 +12,7 @@ template class MessageFactory { public: using IdType = typename TBase::MessageIdType; + using MessageBaseType = TBase; MessageFactory() { constexpr std::size_t messageCount = std::tuple_size_v; diff --git a/include/sp/protocol/MessageHandler.h b/include/sp/protocol/MessageHandler.h new file mode 100644 index 0000000..b8d4ee7 --- /dev/null +++ b/include/sp/protocol/MessageHandler.h @@ -0,0 +1,133 @@ +#pragma once + +#include + +namespace sp +{ + // This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/ + + // TAll is all the message types, that need to be handled, bundled in std::tuple + template + class MessageHandler; + + // Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage + template + class MessageHandler > : public MessageHandler > + { + using Base = MessageHandler >; + public: + using Base::Handle; // Don't hide all Handle() functions from base classes + virtual void Handle(const typename T1::DataType& msg) {} + virtual void Handle(const typename T2::DataType& msg) {} + virtual void Handle(const typename T3::DataType& msg) {} + virtual void Handle(const typename T4::DataType& msg) {} + virtual void Handle(const typename T5::DataType& msg) {} + virtual void Handle(const typename T6::DataType& msg) {} + virtual void Handle(const typename T7::DataType& msg) {} + virtual void Handle(const typename T8::DataType& msg) {} + virtual void Handle(const typename T9::DataType& msg) {} + virtual void Handle(const typename T10::DataType& msg) {} + virtual void Handle(const typename T11::DataType& msg) {} + virtual void Handle(const typename T12::DataType& msg) {} + virtual void Handle(const typename T13::DataType& msg) {} + virtual void Handle(const typename T14::DataType& msg) {} + virtual void Handle(const typename T15::DataType& msg) {} + virtual void Handle(const typename T16::DataType& msg) {} + virtual void Handle(const typename T17::DataType& msg) {} + virtual void Handle(const typename T18::DataType& msg) {} + virtual void Handle(const typename T19::DataType& msg) {} + virtual void Handle(const typename T20::DataType& msg) {} + }; + + // 10 by 10 + template + class MessageHandler > : public MessageHandler > + { + using Base = MessageHandler >; + public: + using Base::Handle; // Don't hide all Handle() functions from base classes + virtual void Handle(const typename T1::DataType& msg) {} + virtual void Handle(const typename T2::DataType& msg) {} + virtual void Handle(const typename T3::DataType& msg) {} + virtual void Handle(const typename T4::DataType& msg) {} + virtual void Handle(const typename T5::DataType& msg) {} + virtual void Handle(const typename T6::DataType& msg) {} + virtual void Handle(const typename T7::DataType& msg) {} + virtual void Handle(const typename T8::DataType& msg) {} + virtual void Handle(const typename T9::DataType& msg) {} + virtual void Handle(const typename T10::DataType& msg) {} + }; + + // 5 by 5 + template < + typename T1, typename T2, typename T3, typename T4, typename T5, + typename... TRest> + class MessageHandler > : public MessageHandler > + { + using Base = MessageHandler >; + public: + using Base::Handle; // Don't hide all Handle() functions from base classes + virtual void Handle(const typename T1::DataType& msg) {} + virtual void Handle(const typename T2::DataType& msg) {} + virtual void Handle(const typename T3::DataType& msg) {} + virtual void Handle(const typename T4::DataType& msg) {} + virtual void Handle(const typename T5::DataType& msg) {} + }; + + // Deal with rest with 4 types + template + class MessageHandler > + { + public: + virtual ~MessageHandler() {} + virtual void Handle(const typename T1::DataType& msg) {} + virtual void Handle(const typename T2::DataType& msg) {} + virtual void Handle(const typename T3::DataType& msg) {} + virtual void Handle(const typename T4::DataType& msg) {} + }; + + // Deal with rest with 3 types + template < typename T1, typename T2, typename T3> + class MessageHandler > + { + public: + virtual ~MessageHandler() {} + virtual void Handle(const typename T1::DataType& msg) {} + virtual void Handle(const typename T2::DataType& msg) {} + virtual void Handle(const typename T3::DataType& msg) {} + }; + + // Deal with rest with 2 types + template + class MessageHandler > + { + public: + virtual ~MessageHandler() {} + virtual void Handle(const typename T1::DataType& msg) {} + virtual void Handle(const typename T2::DataType& msg) {} + }; + + // Deal with rest with 1 type + template + class MessageHandler > + { + public: + virtual ~MessageHandler() {} + virtual void Handle(const typename T1::DataType& msg) {} + }; + + // Deal with rest with 0 type + template <> + class MessageHandler > + { + public: + virtual ~MessageHandler() {} + }; + +} // sp \ No newline at end of file diff --git a/include/sp/protocol/MessageIO.h b/include/sp/protocol/MessageIO.h deleted file mode 100644 index d3661f5..0000000 --- a/include/sp/protocol/MessageIO.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace sp { -namespace details { - -template -void WriteField(std::ostream& a_Os, const T& a_Data) { - T swapped = a_Data; - ToNetwork(swapped); - a_Os.write(reinterpret_cast(&swapped), sizeof(a_Data)); -} - -template -void ReadField(std::istream& a_Is, T& a_Data) { - a_Is.read(reinterpret_cast(&a_Data), sizeof(a_Data)); - FromNetwork(a_Data); -} - -template -void WriteMessage(std::ostream& a_Os, const TData& a_MessageData) { - boost::pfr::for_each_field(a_MessageData, [&a_Os](const auto& a_Field) { - WriteField(a_Os, a_Field); - }); -} - -template -void ReadMessage(std::istream& a_Is, TData& a_MessageData) { - boost::pfr::for_each_field(a_MessageData, [&a_Is](auto& a_Field) { - ReadField(a_Is, a_Field); - }); -} - -} // namespace details -} // namespace sp diff --git a/src/sp/common/VarInt.cpp b/src/sp/common/VarInt.cpp index a02bf6f..30e2463 100644 --- a/src/sp/common/VarInt.cpp +++ b/src/sp/common/VarInt.cpp @@ -29,24 +29,29 @@ DataBuffer& operator<<(DataBuffer& out, const VarInt& var) { } DataBuffer& operator>>(DataBuffer& in, VarInt& var) { - var.m_Value = 0; + var.Read([&in](std::uint8_t& data){ + in.ReadSome(&data, 1); + }); + return in; +} + +void VarInt::Read(const ReadFunc& read) { + m_Value = 0; unsigned int position = 0; std::uint8_t currentByte; while (true) { - in.ReadSome(¤tByte, 1); - var.m_Value |= static_cast(currentByte & SEGMENT_BITS) << position; + read(currentByte); + m_Value |= static_cast(currentByte & SEGMENT_BITS) << position; if ((currentByte & CONTINUE_BIT) == 0) break; position += 7; - if (position >= 8 * sizeof(var.m_Value)) + if (position >= 8 * sizeof(m_Value)) throw std::runtime_error("VarInt is too big"); } - - return in; } } // namespace sp diff --git a/src/sp/extensions/Compress.cpp b/src/sp/extensions/Compress.cpp index fbb67ac..e69de29 100644 --- a/src/sp/extensions/Compress.cpp +++ b/src/sp/extensions/Compress.cpp @@ -1,89 +0,0 @@ -#include - -#include -#include -#include - -namespace sp { -namespace zlib { - -static DataBuffer Inflate(const std::uint8_t* source, std::size_t size, std::size_t uncompressedSize) { - DataBuffer result; - result.Resize(uncompressedSize); - - uncompress(static_cast(result.data()), reinterpret_cast(&uncompressedSize), static_cast(source), - static_cast(size)); - - assert(result.GetSize() == uncompressedSize); - return result; -} - -static DataBuffer Deflate(const std::uint8_t* source, std::size_t size) { - DataBuffer result; - uLongf compressedSize = size; - - result.Resize(size); // Resize for the compressed data to fit into - compress(static_cast(result.data()), &compressedSize, static_cast(source), static_cast(size)); - result.Resize(compressedSize); // Resize to cut useless data - - return result; -} - -DataBuffer Compress(const DataBuffer& buffer, std::size_t a_CompressionThreshold) { - DataBuffer packet; - - if (buffer.GetSize() < a_CompressionThreshold) { - // Don't compress since it's a small packet - packet << VarInt{0}; - packet << buffer; - return packet; - } - - DataBuffer compressedData = Deflate(buffer.data(), buffer.GetSize()); - VarInt uncompressedDataLength = buffer.GetSize(); - - if (compressedData.GetSize() >= buffer.GetSize()) { - // the compression is overkill so we don't send the compressed buffer - packet << VarInt{0}; - packet << buffer; - } else { - packet << uncompressedDataLength; - packet << compressedData; - } - - return packet; -} - -DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength) { - VarInt uncompressedLength; - buffer >> uncompressedLength; - - std::uint64_t compressedLength = packetLength - uncompressedLength.GetSerializedLength(); - - if (uncompressedLength.GetValue() == 0) { - // Data already uncompressed. Nothing to do - DataBuffer ret; - buffer.ReadSome(ret, compressedLength); - return ret; - } - - assert(buffer.GetReadOffset() + compressedLength <= buffer.GetSize()); - - return Inflate(buffer.data() + buffer.GetReadOffset(), compressedLength, uncompressedLength.GetValue()); -} - -} // namespace zlib - -namespace io { - -DataBuffer MessageEncapsulator::Encapsulate(const DataBuffer& a_Data, const option::ZlibCompress& a_Option) { - static constexpr std::size_t MAX_COMPRESS_THRESHOLD = VarInt::MAX_VALUE; - return zlib::Compress(a_Data, a_Option.m_Enabled ? a_Option.m_CompressionThreshold : MAX_COMPRESS_THRESHOLD); -} - -DataBuffer MessageEncapsulator::Decapsulate(DataBuffer& a_Data, const option::ZlibCompress& a_Option) { - return zlib::Decompress(a_Data, a_Data.GetSize()); -} - -} // namespace io -} // namespace sp diff --git a/src/sp/extensions/TcpListener.cpp b/src/sp/extensions/TcpListener.cpp index 330c975..e69de29 100644 --- a/src/sp/extensions/TcpListener.cpp +++ b/src/sp/extensions/TcpListener.cpp @@ -1,114 +0,0 @@ -#include - - -#ifdef _WIN32 - -// Windows - -#include -#include - -#define ioctl ioctlsocket -#define WOULDBLOCK WSAEWOULDBLOCK -#define MSG_DONTWAIT 0 - -#else - -// Linux/Unix - -#include -#include -#include -#include -#include -#include -#include - -#define closesocket close -#define WOULDBLOCK EWOULDBLOCK -#define SD_BOTH SHUT_RDWR - -#endif - - - -#ifndef INVALID_SOCKET -#define INVALID_SOCKET -1 -#endif - - -namespace sp { -namespace io { - -TcpListener::TcpListener(std::uint16_t a_Port, int a_MaxConnexions) { - if ((m_Handle = static_cast(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) < 0) { - throw SocketError("Failed to create server socket"); - } - - struct sockaddr_in address; - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(a_Port); - - if (bind(m_Handle, reinterpret_cast(&address), sizeof(address)) < 0) - throw SocketError("Failed to create server socket"); - - if (listen(m_Handle, a_MaxConnexions) < 0) - throw SocketError("Failed to create server socket"); - - socklen_t len = sizeof(address); - if (getsockname(m_Handle, reinterpret_cast(&address), &len) < 0) - throw SocketError("Failed to create server socket"); - - m_Port = ntohs(address.sin_port); - m_MaxConnections = a_MaxConnexions; -} - - -TcpListener::~TcpListener() { - Close(); -} - -std::unique_ptr TcpListener::Accept() { - sockaddr remoteAddress; - int addrlen = sizeof(remoteAddress); - - auto newSocket = std::make_unique(); - - newSocket->m_Handle = static_cast( - accept(m_Handle, reinterpret_cast(&remoteAddress), reinterpret_cast(&addrlen))); - - if (newSocket->m_Handle < 0) - return nullptr; - - newSocket->m_Status = TcpSocket::Status::Connected; - return newSocket; -} - -void TcpListener::Close() { - if (m_Handle > 0) { - closesocket(m_Handle); - shutdown(m_Handle, SD_BOTH); - } -} - -bool TcpListener::SetBlocking(bool a_Blocking) { - unsigned long mode = !a_Blocking; - - if (ioctl(m_Handle, FIONBIO, &mode) < 0) { - return false; - } - - return true; -} - -std::uint16_t TcpListener::GetListeningPort() const { - return m_Port; -} - -int TcpListener::GetMaximumConnections() const { - return m_MaxConnections; -} - -} // namespace io -} // namespace sp diff --git a/src/sp/extensions/TcpSocket.cpp b/src/sp/extensions/TcpSocket.cpp index ab08b3a..e69de29 100644 --- a/src/sp/extensions/TcpSocket.cpp +++ b/src/sp/extensions/TcpSocket.cpp @@ -1,164 +0,0 @@ -#include - -#ifdef _WIN32 - -// Windows - -#include -#include - -#define ioctl ioctlsocket -#define WOULDBLOCK WSAEWOULDBLOCK -#define MSG_DONTWAIT 0 - -#else - -// Linux/Unix - -#include -#include -#include -#include -#include -#include -#include - -#define closesocket close -#define WOULDBLOCK EWOULDBLOCK - -#endif - - - -#ifndef INVALID_SOCKET -#define INVALID_SOCKET -1 -#endif - - -namespace sp { -namespace io { - -TcpSocket::IOInterface() : m_Handle(static_cast(INVALID_SOCKET)), m_Status(Status::Disconnected) {} - -TcpSocket::IOInterface(const std::string& a_Host, std::uint16_t a_Port) : IOInterface() { - Connect(a_Host, a_Port); -} - -TcpSocket::IOInterface(IOInterface&& a_Other) { - std::swap(m_Handle, a_Other.m_Handle); - std::swap(m_Status, a_Other.m_Status); -} - -TcpSocket::~IOInterface() {} - -void TcpSocket::Connect(const std::string& a_Host, std::uint16_t a_Port) { - struct addrinfo hints {}; - - struct addrinfo* result = nullptr; - - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - m_Status = Status::Error; - - if (getaddrinfo(a_Host.c_str(), std::to_string(static_cast(a_Port)).c_str(), &hints, &result) != 0) { - throw SocketError("Failed to get address info"); - } - - m_Handle = static_cast(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - if (m_Handle < 0) { - throw SocketError("Failed to create socket"); - } - - struct addrinfo* ptr = nullptr; - for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) { - struct sockaddr* sockaddr = ptr->ai_addr; - if (connect(m_Handle, sockaddr, sizeof(sockaddr_in)) == 0) { - break; - } - } - - freeaddrinfo(result); - - if (!ptr) { - throw SocketError("Could not find a suitable interface for connecting"); - } - - m_Status = Status::Connected; -} - -DataBuffer TcpSocket::Read(std::size_t a_Amount) { - DataBuffer buffer(a_Amount); - - std::size_t totalRecieved = 0; - - while (totalRecieved < a_Amount) { - int recvAmount = - recv(m_Handle, reinterpret_cast(buffer.data() + totalRecieved), static_cast(a_Amount - totalRecieved), 0); - if (recvAmount <= 0) { -#if defined(_WIN32) || defined(WIN32) - int err = WSAGetLastError(); -#else - int err = errno; -#endif - if (err == WOULDBLOCK) { - // we are in non blocking mode and nothing is available - return {}; - } - - Disconnect(); - m_Status = Status::Error; - throw SocketError("Error while reading"); - } - totalRecieved += recvAmount; - } - return buffer; -} - -void TcpSocket::Write(const sp::DataBuffer& a_Data) { - if (GetStatus() != Status::Connected) - return; - - std::size_t sent = 0; - - while (sent < a_Data.GetSize()) { - int cur = send(m_Handle, reinterpret_cast(a_Data.data() + sent), static_cast(a_Data.GetSize() - sent), 0); - - if (cur <= 0) { - Disconnect(); - m_Status = Status::Error; - return; - } - sent += static_cast(cur); - } -} - -bool TcpSocket::SetBlocking(bool a_Block) { - unsigned long mode = !a_Block; - - if (ioctl(m_Handle, FIONBIO, &mode) < 0) { - return false; - } - - return true; -} - -TcpSocket::Status TcpSocket::GetStatus() const { - return m_Status; -} - -void TcpSocket::Disconnect() { - if (m_Handle > 0) - closesocket(m_Handle); - m_Status = Status::Disconnected; -} - -TcpSocket& TcpSocket::operator=(IOInterface&& a_Other) { - std::swap(m_Handle, a_Other.m_Handle); - std::swap(m_Status, a_Other.m_Status); - return *this; -} - -} // namespace io -} // namespace sp diff --git a/src/sp/io/File.cpp b/src/sp/io/File.cpp index 300c0aa..e69de29 100644 --- a/src/sp/io/File.cpp +++ b/src/sp/io/File.cpp @@ -1,31 +0,0 @@ -#include - -namespace sp { -namespace io { - -File::IOInterface(const std::string& a_FilePath, unsigned int a_OpenMode) { - if (a_OpenMode & FileTag::OpenMode::In) - m_FileInput = std::make_unique(a_FilePath, std::ios::binary); - if (a_OpenMode & FileTag::OpenMode::Out) - m_FileOutput = std::make_unique(a_FilePath, std::ios::binary); -} - -File::IOInterface(File&& other) : - m_FileInput(std::move(other.m_FileInput)), m_FileOutput(std::move(other.m_FileOutput)) {} - -DataBuffer File::Read(std::size_t a_Amount) { - DataBuffer buffer; - buffer.Resize(a_Amount); - assert(m_FileInput != nullptr); - m_FileInput->read(reinterpret_cast(buffer.data()), a_Amount); - return buffer; -} - -void File::Write(const sp::DataBuffer& a_Data) { - assert(m_FileOutput != nullptr); - m_FileOutput->write(reinterpret_cast(a_Data.data()), a_Data.GetSize()); - m_FileOutput->flush(); -} - -} // namespace io -} // namespace sp diff --git a/src/sp/io/Memory.cpp b/src/sp/io/Memory.cpp index 57ffd4f..e69de29 100644 --- a/src/sp/io/Memory.cpp +++ b/src/sp/io/Memory.cpp @@ -1,16 +0,0 @@ -#include - -namespace sp { -namespace io { - -sp::DataBuffer Memory::Read(std::size_t a_Amount) { - DataBuffer data; - m_VirtualIO.ReadSome(data, a_Amount > m_VirtualIO.GetRemaining() ? m_VirtualIO.GetRemaining() : a_Amount); - return data; -} -void Memory::Write(const sp::DataBuffer& a_Data) { - m_VirtualIO << a_Data; -} - -} // namespace io -} // namespace sp diff --git a/test/test_message.cpp b/test/test_message.cpp index 5842e70..6b8a49e 100644 --- a/test/test_message.cpp +++ b/test/test_message.cpp @@ -1,7 +1,9 @@ #include -#include #include #include +#include +#include +#include #include #include @@ -24,12 +26,12 @@ using KeepAliveMessage = Message; using AllMessages = std::tuple; -class PacketHandler : public sp::GenericHandler {}; +class PacketHandler : public sp::MessageHandler {}; class MyHandler : public PacketHandler { public: - virtual void Handle(const KeepAliveMessage& msg) { - std::cout << "yo !" << "\n"; + virtual void Handle(const KeepAlivePacket& msg) { + std::cout << "I recieved a keep alive : " << msg.m_KeepAlive << "\n"; } }; @@ -37,8 +39,10 @@ using PacketDispatcher = sp::MessageDispatcher; using PacketFactory = sp::MessageFactory; +using PacketStream = sp::MessageStream; + int main() { - KeepAliveMessage m{5UL}; + KeepAliveMessage m{69UL}; // dispatch tests @@ -56,9 +60,23 @@ int main() { std::ofstream file {"test.bin"}; - message->Write(file); + PacketStream p(std::make_shared(file)); + + p.WriteMessage(m); + + file.flush(); + + std::ifstream file2 {"test.bin"}; + + PacketStream p2(std::make_shared(file2)); + + auto message2 = p2.ReadMessage(); + + d.Dispatch(*message2); + + // message->Write(file); // file << std::endl; - m.Write(file); + // m.Write(file); // file << std::endl; // message->Read(file); return 0; -- 2.49.1 From ed0b06f78dc5696bf52565b02df7b72b73abf334 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 27 Jun 2025 18:53:03 +0200 Subject: [PATCH 5/9] finish io --- include/sp/extensions/Compress.h | 35 ++--- include/sp/extensions/tcp/TcpListener.h | 3 +- include/sp/extensions/tcp/TcpSocket.h | 30 ++--- include/sp/io/IoInterface.h | 3 +- include/sp/io/MessageEncapsulator.h | 19 ++- include/sp/io/MessageStream.h | 23 +++- include/sp/io/MessageStream.inl | 8 +- src/sp/extensions/Compress.cpp | 85 ++++++++++++ src/sp/extensions/TcpListener.cpp | 114 ++++++++++++++++ src/sp/extensions/TcpSocket.cpp | 164 ++++++++++++++++++++++++ test/test_message.cpp | 13 +- 11 files changed, 443 insertions(+), 54 deletions(-) diff --git a/include/sp/extensions/Compress.h b/include/sp/extensions/Compress.h index b3b06e3..e6b6ce6 100644 --- a/include/sp/extensions/Compress.h +++ b/include/sp/extensions/Compress.h @@ -6,20 +6,7 @@ */ #include -#include - -namespace sp { -namespace option { - -struct ZlibCompress { - bool m_Enabled = true; - std::size_t m_CompressionThreshold = 64; -}; - -} // namespace option -} // namespace sp - -#include +#include namespace sp { namespace zlib { @@ -41,14 +28,20 @@ DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength); } // namespace zlib -namespace io { -template <> -class MessageEncapsulator { +class ZlibCompress : public MessageEncapsulator { + private: + std::size_t m_CompressionThreshold; + public: - static DataBuffer Encapsulate(const DataBuffer& a_Data, const option::ZlibCompress& a_Option); - static DataBuffer Decapsulate(DataBuffer& a_Data, const option::ZlibCompress& a_Option); + ZlibCompress() : m_CompressionThreshold(64) {} + ZlibCompress(const ZlibCompress&) = default; + virtual ~ZlibCompress() {} + + protected: + virtual DataBuffer EncapsulateImpl(const DataBuffer& a_Data) override; + virtual DataBuffer DecapsulateImpl(DataBuffer& a_Data) override; }; -} // namespace io -} // namespace sp + +} // namespace sp \ No newline at end of file diff --git a/include/sp/extensions/tcp/TcpListener.h b/include/sp/extensions/tcp/TcpListener.h index 07e2e06..6000e19 100644 --- a/include/sp/extensions/tcp/TcpListener.h +++ b/include/sp/extensions/tcp/TcpListener.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace sp { namespace io { @@ -10,7 +11,7 @@ namespace io { */ class TcpListener : private NonCopyable { public: - using SocketHandle = TcpTag::SocketHandle; + using SocketHandle = TcpSocket::SocketHandle; /** * \brief Starts listening for guests to connect diff --git a/include/sp/extensions/tcp/TcpSocket.h b/include/sp/extensions/tcp/TcpSocket.h index 2ec39d3..0abdfd6 100644 --- a/include/sp/extensions/tcp/TcpSocket.h +++ b/include/sp/extensions/tcp/TcpSocket.h @@ -1,14 +1,12 @@ #pragma once #include -#include +#include namespace sp { namespace io { -struct TcpTag { - using SocketHandle = int; -}; +class TcpListener; class SocketError : public std::exception { private: @@ -22,10 +20,9 @@ class SocketError : public std::exception { } }; -template <> -class IOInterface : private NonCopyable { +class TcpSocket : public sp::IoInterface { public: - using SocketHandle = TcpTag::SocketHandle; + using SocketHandle = int; /** * \enum Status @@ -40,14 +37,14 @@ class IOInterface : private NonCopyable { Error, }; - IOInterface(); - IOInterface(const std::string& a_Host, std::uint16_t a_Port); - IOInterface(IOInterface&& a_Other); - IOInterface& operator=(IOInterface&& a_Other); - virtual ~IOInterface(); + TcpSocket(); + TcpSocket(const std::string& a_Host, std::uint16_t a_Port); + TcpSocket(TcpSocket&& a_Other); + TcpSocket& operator=(TcpSocket&& a_Other); + virtual ~TcpSocket(); - DataBuffer Read(std::size_t a_Amount); - void Write(const sp::DataBuffer& a_Data); + virtual DataBuffer Read(std::size_t a_Amount) override; + virtual void Write(const sp::DataBuffer& a_Data) override; /** * \brief Allows to set the socket in non blocking/blocking mode @@ -79,10 +76,5 @@ class IOInterface : private NonCopyable { friend class TcpListener; }; -/** - * \typedef TcpSocket - */ -using TcpSocket = IOInterface; - } // namespace io } // namespace sp diff --git a/include/sp/io/IoInterface.h b/include/sp/io/IoInterface.h index 037dfef..32c48ef 100644 --- a/include/sp/io/IoInterface.h +++ b/include/sp/io/IoInterface.h @@ -1,10 +1,11 @@ #pragma once #include +#include namespace sp { -class IoInterface { +class IoInterface : private NonCopyable { public: virtual DataBuffer Read(std::size_t a_Amount) = 0; virtual void Write(const DataBuffer& a_Data) = 0; diff --git a/include/sp/io/MessageEncapsulator.h b/include/sp/io/MessageEncapsulator.h index d8796fd..4a2a30b 100644 --- a/include/sp/io/MessageEncapsulator.h +++ b/include/sp/io/MessageEncapsulator.h @@ -5,12 +5,27 @@ namespace sp { class MessageEncapsulator { + protected: + bool m_Enabled = true; + public: MessageEncapsulator() {} virtual ~MessageEncapsulator() {} - virtual DataBuffer Encapsulate(const DataBuffer& a_Data) = 0; - virtual DataBuffer Decapsulate(DataBuffer& a_Data) = 0; + DataBuffer Encapsulate(const DataBuffer& a_Data) { + if (!m_Enabled) + return a_Data; + return EncapsulateImpl(a_Data); + } + DataBuffer Decapsulate(DataBuffer& a_Data) { + if (!m_Enabled) + return a_Data; + return DecapsulateImpl(a_Data); + } + + protected: + virtual DataBuffer EncapsulateImpl(const DataBuffer& a_Data) = 0; + virtual DataBuffer DecapsulateImpl(DataBuffer& a_Data) = 0; }; } // namespace sp diff --git a/include/sp/io/MessageStream.h b/include/sp/io/MessageStream.h index c5442f8..3aea568 100644 --- a/include/sp/io/MessageStream.h +++ b/include/sp/io/MessageStream.h @@ -9,8 +9,8 @@ namespace sp { template class MessageStream { - private: - std::vector m_Encapsulators; + protected: + std::vector> m_Encapsulators; std::shared_ptr m_Stream; using MessageBaseType = typename TMessageFactory::MessageBaseType; @@ -19,11 +19,30 @@ class MessageStream { public: MessageStream(std::shared_ptr&& a_Stream) : m_Stream(std::move(a_Stream)) {} + template + MessageStream(std::shared_ptr&& a_Stream, TEnc&&... a_Encapsulators) : + m_Stream(std::move(a_Stream)){ + m_Encapsulators.reserve(sizeof...(a_Encapsulators)); + AddEncapsulators(std::move(a_Encapsulators ...)); + } + std::unique_ptr ReadMessage(); std::unique_ptr ReadMessage(MessageIdType a_Id); void WriteMessage(const MessageBaseType& a_Message, bool a_WriteId = true); + template + void AddEncapsulators(Args&& ... a_Encapsulators) { + AddEncapsulators(std::move(std::make_tuple<>(a_Encapsulators ...))); + } + + template + void AddEncapsulators(std::tuple&& a_Encapsulators) { + TupleForEach([this](auto&& a_Encapsulator){ + m_Encapsulators.push_back(std::move(a_Encapsulator)); + }, a_Encapsulators); + } + private: DataBuffer ReadAndDecapsulate(); std::unique_ptr MakeMessage(DataBuffer& buffer, MessageIdType a_Id); diff --git a/include/sp/io/MessageStream.inl b/include/sp/io/MessageStream.inl index 98231f9..4a6f300 100644 --- a/include/sp/io/MessageStream.inl +++ b/include/sp/io/MessageStream.inl @@ -12,8 +12,8 @@ DataBuffer MessageStream::ReadAndDecapsulate() { std::size_t amount = messageLength.GetValue(); DataBuffer buffer = m_Stream->Read(amount); - for (MessageEncapsulator& enc : m_Encapsulators) { - buffer = enc.Decapsulate(buffer); + for (auto& enc : m_Encapsulators) { + buffer = enc->Decapsulate(buffer); } return buffer; @@ -51,8 +51,8 @@ void MessageStream::WriteMessage(const MessageBaseType& a_Messa if (a_WriteId) buffer << VarInt{static_cast(a_Message.GetId())}; buffer << a_Message.Write(); - for (MessageEncapsulator& enc : m_Encapsulators) { - buffer = enc.Encapsulate(buffer); + for (auto& enc : m_Encapsulators) { + buffer = enc->Encapsulate(buffer); } DataBuffer header; header << VarInt{buffer.GetSize()}; diff --git a/src/sp/extensions/Compress.cpp b/src/sp/extensions/Compress.cpp index e69de29..49faa3e 100644 --- a/src/sp/extensions/Compress.cpp +++ b/src/sp/extensions/Compress.cpp @@ -0,0 +1,85 @@ +#include + +#include +#include +#include + +namespace sp { +namespace zlib { + +static DataBuffer Inflate(const std::uint8_t* source, std::size_t size, std::size_t uncompressedSize) { + DataBuffer result; + result.Resize(uncompressedSize); + + uncompress(static_cast(result.data()), reinterpret_cast(&uncompressedSize), static_cast(source), + static_cast(size)); + + assert(result.GetSize() == uncompressedSize); + return result; +} + +static DataBuffer Deflate(const std::uint8_t* source, std::size_t size) { + DataBuffer result; + uLongf compressedSize = size; + + result.Resize(size); // Resize for the compressed data to fit into + compress(static_cast(result.data()), &compressedSize, static_cast(source), static_cast(size)); + result.Resize(compressedSize); // Resize to cut useless data + + return result; +} + +DataBuffer Compress(const DataBuffer& buffer, std::size_t a_CompressionThreshold) { + DataBuffer packet; + + if (buffer.GetSize() < a_CompressionThreshold) { + // Don't compress since it's a small packet + packet << VarInt{0}; + packet << buffer; + return packet; + } + + DataBuffer compressedData = Deflate(buffer.data(), buffer.GetSize()); + VarInt uncompressedDataLength = buffer.GetSize(); + + if (compressedData.GetSize() >= buffer.GetSize()) { + // the compression is overkill so we don't send the compressed buffer + packet << VarInt{0}; + packet << buffer; + } else { + packet << uncompressedDataLength; + packet << compressedData; + } + + return packet; +} + +DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength) { + VarInt uncompressedLength; + buffer >> uncompressedLength; + + std::uint64_t compressedLength = packetLength - uncompressedLength.GetSerializedLength(); + + if (uncompressedLength.GetValue() == 0) { + // Data already uncompressed. Nothing to do + DataBuffer ret; + buffer.ReadSome(ret, compressedLength); + return ret; + } + + assert(buffer.GetReadOffset() + compressedLength <= buffer.GetSize()); + + return Inflate(buffer.data() + buffer.GetReadOffset(), compressedLength, uncompressedLength.GetValue()); +} + +} // namespace zlib + +DataBuffer ZlibCompress::EncapsulateImpl(const DataBuffer& a_Data) { + return zlib::Compress(a_Data, m_CompressionThreshold); +} + +DataBuffer ZlibCompress::DecapsulateImpl(DataBuffer& a_Data) { + return zlib::Decompress(a_Data, a_Data.GetSize()); +} + +} // namespace sp diff --git a/src/sp/extensions/TcpListener.cpp b/src/sp/extensions/TcpListener.cpp index e69de29..330c975 100644 --- a/src/sp/extensions/TcpListener.cpp +++ b/src/sp/extensions/TcpListener.cpp @@ -0,0 +1,114 @@ +#include + + +#ifdef _WIN32 + +// Windows + +#include +#include + +#define ioctl ioctlsocket +#define WOULDBLOCK WSAEWOULDBLOCK +#define MSG_DONTWAIT 0 + +#else + +// Linux/Unix + +#include +#include +#include +#include +#include +#include +#include + +#define closesocket close +#define WOULDBLOCK EWOULDBLOCK +#define SD_BOTH SHUT_RDWR + +#endif + + + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif + + +namespace sp { +namespace io { + +TcpListener::TcpListener(std::uint16_t a_Port, int a_MaxConnexions) { + if ((m_Handle = static_cast(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) < 0) { + throw SocketError("Failed to create server socket"); + } + + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(a_Port); + + if (bind(m_Handle, reinterpret_cast(&address), sizeof(address)) < 0) + throw SocketError("Failed to create server socket"); + + if (listen(m_Handle, a_MaxConnexions) < 0) + throw SocketError("Failed to create server socket"); + + socklen_t len = sizeof(address); + if (getsockname(m_Handle, reinterpret_cast(&address), &len) < 0) + throw SocketError("Failed to create server socket"); + + m_Port = ntohs(address.sin_port); + m_MaxConnections = a_MaxConnexions; +} + + +TcpListener::~TcpListener() { + Close(); +} + +std::unique_ptr TcpListener::Accept() { + sockaddr remoteAddress; + int addrlen = sizeof(remoteAddress); + + auto newSocket = std::make_unique(); + + newSocket->m_Handle = static_cast( + accept(m_Handle, reinterpret_cast(&remoteAddress), reinterpret_cast(&addrlen))); + + if (newSocket->m_Handle < 0) + return nullptr; + + newSocket->m_Status = TcpSocket::Status::Connected; + return newSocket; +} + +void TcpListener::Close() { + if (m_Handle > 0) { + closesocket(m_Handle); + shutdown(m_Handle, SD_BOTH); + } +} + +bool TcpListener::SetBlocking(bool a_Blocking) { + unsigned long mode = !a_Blocking; + + if (ioctl(m_Handle, FIONBIO, &mode) < 0) { + return false; + } + + return true; +} + +std::uint16_t TcpListener::GetListeningPort() const { + return m_Port; +} + +int TcpListener::GetMaximumConnections() const { + return m_MaxConnections; +} + +} // namespace io +} // namespace sp diff --git a/src/sp/extensions/TcpSocket.cpp b/src/sp/extensions/TcpSocket.cpp index e69de29..047666f 100644 --- a/src/sp/extensions/TcpSocket.cpp +++ b/src/sp/extensions/TcpSocket.cpp @@ -0,0 +1,164 @@ +#include + +#ifdef _WIN32 + +// Windows + +#include +#include + +#define ioctl ioctlsocket +#define WOULDBLOCK WSAEWOULDBLOCK +#define MSG_DONTWAIT 0 + +#else + +// Linux/Unix + +#include +#include +#include +#include +#include +#include +#include + +#define closesocket close +#define WOULDBLOCK EWOULDBLOCK + +#endif + + + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif + + +namespace sp { +namespace io { + +TcpSocket::TcpSocket() : m_Handle(static_cast(INVALID_SOCKET)), m_Status(Status::Disconnected) {} + +TcpSocket::TcpSocket(const std::string& a_Host, std::uint16_t a_Port) : TcpSocket() { + Connect(a_Host, a_Port); +} + +TcpSocket::TcpSocket(TcpSocket&& a_Other) { + std::swap(m_Handle, a_Other.m_Handle); + std::swap(m_Status, a_Other.m_Status); +} + +TcpSocket::~TcpSocket() {} + +void TcpSocket::Connect(const std::string& a_Host, std::uint16_t a_Port) { + struct addrinfo hints {}; + + struct addrinfo* result = nullptr; + + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + m_Status = Status::Error; + + if (getaddrinfo(a_Host.c_str(), std::to_string(static_cast(a_Port)).c_str(), &hints, &result) != 0) { + throw SocketError("Failed to get address info"); + } + + m_Handle = static_cast(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + if (m_Handle < 0) { + throw SocketError("Failed to create socket"); + } + + struct addrinfo* ptr = nullptr; + for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) { + struct sockaddr* sockaddr = ptr->ai_addr; + if (connect(m_Handle, sockaddr, sizeof(sockaddr_in)) == 0) { + break; + } + } + + freeaddrinfo(result); + + if (!ptr) { + throw SocketError("Could not find a suitable interface for connecting"); + } + + m_Status = Status::Connected; +} + +DataBuffer TcpSocket::Read(std::size_t a_Amount) { + DataBuffer buffer(a_Amount); + + std::size_t totalRecieved = 0; + + while (totalRecieved < a_Amount) { + int recvAmount = + recv(m_Handle, reinterpret_cast(buffer.data() + totalRecieved), static_cast(a_Amount - totalRecieved), 0); + if (recvAmount <= 0) { +#if defined(_WIN32) || defined(WIN32) + int err = WSAGetLastError(); +#else + int err = errno; +#endif + if (err == WOULDBLOCK) { + // we are in non blocking mode and nothing is available + return {}; + } + + Disconnect(); + m_Status = Status::Error; + throw SocketError("Error while reading"); + } + totalRecieved += recvAmount; + } + return buffer; +} + +void TcpSocket::Write(const sp::DataBuffer& a_Data) { + if (GetStatus() != Status::Connected) + return; + + std::size_t sent = 0; + + while (sent < a_Data.GetSize()) { + int cur = send(m_Handle, reinterpret_cast(a_Data.data() + sent), static_cast(a_Data.GetSize() - sent), 0); + + if (cur <= 0) { + Disconnect(); + m_Status = Status::Error; + return; + } + sent += static_cast(cur); + } +} + +bool TcpSocket::SetBlocking(bool a_Block) { + unsigned long mode = !a_Block; + + if (ioctl(m_Handle, FIONBIO, &mode) < 0) { + return false; + } + + return true; +} + +TcpSocket::Status TcpSocket::GetStatus() const { + return m_Status; +} + +void TcpSocket::Disconnect() { + if (m_Handle > 0) + closesocket(m_Handle); + m_Status = Status::Disconnected; +} + +TcpSocket& TcpSocket::operator=(TcpSocket&& a_Other) { + std::swap(m_Handle, a_Other.m_Handle); + std::swap(m_Status, a_Other.m_Status); + return *this; +} + +} // namespace io +} // namespace sp diff --git a/test/test_message.cpp b/test/test_message.cpp index 6b8a49e..fadd268 100644 --- a/test/test_message.cpp +++ b/test/test_message.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include #include @@ -20,6 +22,7 @@ using Message = sp::ConcreteMessage; struct KeepAlivePacket { std::uint64_t m_KeepAlive; + std::string mdc; }; using KeepAliveMessage = Message; @@ -31,7 +34,7 @@ class PacketHandler : public sp::MessageHandler {}; class MyHandler : public PacketHandler { public: virtual void Handle(const KeepAlivePacket& msg) { - std::cout << "I recieved a keep alive : " << msg.m_KeepAlive << "\n"; + std::cout << "I recieved a keep alive : " << msg.m_KeepAlive << " : " << msg.mdc << "\n"; } }; @@ -42,7 +45,7 @@ using PacketFactory = sp::MessageFactory; using PacketStream = sp::MessageStream; int main() { - KeepAliveMessage m{69UL}; + KeepAliveMessage m{69UL, "ceci est une mdc aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}; // dispatch tests @@ -58,9 +61,11 @@ int main() { // write tests + auto compress = std::make_shared(); + std::ofstream file {"test.bin"}; - PacketStream p(std::make_shared(file)); + PacketStream p(std::make_shared(file), compress); p.WriteMessage(m); @@ -68,7 +73,7 @@ int main() { std::ifstream file2 {"test.bin"}; - PacketStream p2(std::make_shared(file2)); + PacketStream p2(std::make_shared(file2), compress); auto message2 = p2.ReadMessage(); -- 2.49.1 From 6c242806900e97460586b6039ead337c78dd1f25 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 30 Jun 2025 12:07:59 +0200 Subject: [PATCH 6/9] use std::decay --- include/sp/protocol/MessageFactory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sp/protocol/MessageFactory.h b/include/sp/protocol/MessageFactory.h index 0957977..e14a409 100644 --- a/include/sp/protocol/MessageFactory.h +++ b/include/sp/protocol/MessageFactory.h @@ -19,7 +19,7 @@ class MessageFactory { m_Factory.resize(messageCount); TupleForEach([this](const auto& message){ std::size_t messageID = static_cast(message.GetId()); - using MessageType = std::remove_const_t>; + using MessageType = std::decay_t; m_Factory.emplace(m_Factory.begin() + messageID, []() -> std::unique_ptr { return std::make_unique(); }); }, TTMessages{}); } -- 2.49.1 From 6f667deece9402997abff6525604db4679b115b9 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 30 Jun 2025 12:08:16 +0200 Subject: [PATCH 7/9] add BitField --- include/sp/io/MessageIO.h | 44 ++++++++++++++++++++++++++++++---- include/sp/protocol/BitField.h | 37 ++++++++++++++++++++++++++++ test/test_message.cpp | 11 +++++---- 3 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 include/sp/protocol/BitField.h diff --git a/include/sp/io/MessageIO.h b/include/sp/io/MessageIO.h index 9dc5440..6d7b596 100644 --- a/include/sp/io/MessageIO.h +++ b/include/sp/io/MessageIO.h @@ -3,19 +3,51 @@ #include #include #include +#include namespace sp { namespace details { +template +void WriteField(DataBuffer& a_Buffer, const BitField& a_Data, std::uint64_t& a_DataRaw, std::size_t& a_Offset) { + T cut = *a_Data & ((1 << a_Data.GetBitSize()) - 1); + std::size_t pushCount = sizeof(T) * 8 - a_Offset - a_Data.GetBitSize(); + a_DataRaw |= cut << pushCount; + a_Offset += a_Data.GetBitSize(); + if (a_Offset == sizeof(T) * 8) { + T filled = static_cast(a_DataRaw); + ToNetwork(filled); + a_Buffer << filled; + a_Offset = 0; + a_DataRaw = 0; + } +} + template -void WriteField(DataBuffer& a_Buffer, const T& a_Data) { +void WriteField(DataBuffer& a_Buffer, const T& a_Data, std::uint64_t& a_DataRaw, std::size_t& a_Offset) { T swapped = a_Data; ToNetwork(swapped); a_Buffer << swapped; } +template +void ReadField(DataBuffer& a_Buffer, BitField& a_Data, std::size_t& a_Offset) { + a_Buffer >> *a_Data; + FromNetwork(*a_Data); + + *a_Data >>= sizeof(T) * 8 - a_Offset - a_Data.GetBitSize(); + *a_Data &= (1 << a_Data.GetBitSize()) - 1; + + if (a_Offset != sizeof(T) * 8) { + a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() - sizeof(T)); + a_Offset += a_Data.GetBitSize(); + } else { + a_Offset = 0; + } +} + template -void ReadField(DataBuffer& a_Buffer, T& a_Data) { +void ReadField(DataBuffer& a_Buffer, T& a_Data, std::size_t& a_Offset) { a_Buffer >> a_Data; FromNetwork(a_Data); } @@ -23,13 +55,17 @@ void ReadField(DataBuffer& a_Buffer, T& a_Data) { template DataBuffer WriteMessage(const TData& a_MessageData) { DataBuffer buffer; - boost::pfr::for_each_field(a_MessageData, [&buffer](const auto& a_Field) { WriteField(buffer, a_Field); }); + std::size_t currentOffset = 0; + std::uint64_t dataRaw = 0; + boost::pfr::for_each_field(a_MessageData, + [&buffer, &dataRaw, ¤tOffset](const auto& a_Field) { WriteField(buffer, a_Field, dataRaw, currentOffset); }); return buffer; } template void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) { - boost::pfr::for_each_field(a_MessageData, [&a_Buffer](auto& a_Field) { ReadField(a_Buffer, a_Field); }); + std::size_t currentOffset = 0; + boost::pfr::for_each_field(a_MessageData, [&a_Buffer, ¤tOffset](auto& a_Field) { ReadField(a_Buffer, a_Field, currentOffset); }); } } // namespace details diff --git a/include/sp/protocol/BitField.h b/include/sp/protocol/BitField.h new file mode 100644 index 0000000..5d13434 --- /dev/null +++ b/include/sp/protocol/BitField.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace sp { + +template +class BitField { + private: + static constexpr int BITS_IN_CHAR = 8; + static_assert(sizeof(T) * BITS_IN_CHAR > BitSize, "The bit count must be lower than the actual type size !"); + + T m_Data; + + public: + BitField() : m_Data{} {}; + BitField(T a_Data) : m_Data(a_Data) {} + + BitField& operator=(T a_Data) { + m_Data = a_Data; + return *this; + } + + constexpr std::size_t GetBitSize() const { + return BitSize; + } + + T& operator*() { + return m_Data; + } + + const T& operator*() const { + return m_Data; + } +}; + +} // namespace sp diff --git a/test/test_message.cpp b/test/test_message.cpp index fadd268..f16993a 100644 --- a/test/test_message.cpp +++ b/test/test_message.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -21,8 +22,8 @@ template using Message = sp::ConcreteMessage; struct KeepAlivePacket { - std::uint64_t m_KeepAlive; - std::string mdc; + sp::BitField one; + sp::BitField two; }; using KeepAliveMessage = Message; @@ -34,7 +35,7 @@ class PacketHandler : public sp::MessageHandler {}; class MyHandler : public PacketHandler { public: virtual void Handle(const KeepAlivePacket& msg) { - std::cout << "I recieved a keep alive : " << msg.m_KeepAlive << " : " << msg.mdc << "\n"; + std::cout << "I recieved a keep alive : " << *msg.one << " : " << *msg.two << "\n"; } }; @@ -45,7 +46,7 @@ using PacketFactory = sp::MessageFactory; using PacketStream = sp::MessageStream; int main() { - KeepAliveMessage m{69UL, "ceci est une mdc aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}; + KeepAliveMessage m{69, 5}; // dispatch tests @@ -79,6 +80,8 @@ int main() { d.Dispatch(*message2); + // Todo : verify bitfields + // message->Write(file); // file << std::endl; // m.Write(file); -- 2.49.1 From 4aa25c4189cc345d1f9d6a0843b2de8c30929d2e Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 10 Jul 2025 11:57:39 +0200 Subject: [PATCH 8/9] add asserts --- include/sp/io/StdIo.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/sp/io/StdIo.h b/include/sp/io/StdIo.h index 50de28e..4fbb5e4 100644 --- a/include/sp/io/StdIo.h +++ b/include/sp/io/StdIo.h @@ -18,7 +18,9 @@ class StdInput : public IoInterface { return buffer; } - virtual void Write(const DataBuffer& a_Data) override {} + virtual void Write(const DataBuffer& a_Data) override { + assert(!"Write not implemented !"); + } }; class StdOuput : public IoInterface { @@ -29,6 +31,7 @@ class StdOuput : public IoInterface { StdOuput(std::ostream& a_Io) : m_Io(a_Io) {} virtual DataBuffer Read(std::size_t a_Amount) override { + assert(!"Read not implemented !"); return {}; } -- 2.49.1 From 632650e73d07ab84055e6d8748f8ff01a1f56f12 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 10 Jul 2025 15:13:22 +0200 Subject: [PATCH 9/9] refactor --- include/sp/io/MessageIO.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/include/sp/io/MessageIO.h b/include/sp/io/MessageIO.h index 6d7b596..89f0ed5 100644 --- a/include/sp/io/MessageIO.h +++ b/include/sp/io/MessageIO.h @@ -8,6 +8,15 @@ namespace sp { namespace details { +template +void WriteBitField(DataBuffer& a_Buffer, std::uint64_t& a_DataRaw, std::size_t& a_Offset) { + T filled = static_cast(a_DataRaw); + ToNetwork(filled); + a_Buffer << filled; + a_Offset = 0; + a_DataRaw = 0; +} + template void WriteField(DataBuffer& a_Buffer, const BitField& a_Data, std::uint64_t& a_DataRaw, std::size_t& a_Offset) { T cut = *a_Data & ((1 << a_Data.GetBitSize()) - 1); @@ -15,11 +24,7 @@ void WriteField(DataBuffer& a_Buffer, const BitField& a_Data, std::u a_DataRaw |= cut << pushCount; a_Offset += a_Data.GetBitSize(); if (a_Offset == sizeof(T) * 8) { - T filled = static_cast(a_DataRaw); - ToNetwork(filled); - a_Buffer << filled; - a_Offset = 0; - a_DataRaw = 0; + WriteBitField(a_Buffer, a_DataRaw, a_Offset); } } @@ -34,10 +39,10 @@ template void ReadField(DataBuffer& a_Buffer, BitField& a_Data, std::size_t& a_Offset) { a_Buffer >> *a_Data; FromNetwork(*a_Data); - - *a_Data >>= sizeof(T) * 8 - a_Offset - a_Data.GetBitSize(); + + *a_Data >>= sizeof(T) * 8 - a_Offset - a_Data.GetBitSize(); *a_Data &= (1 << a_Data.GetBitSize()) - 1; - + if (a_Offset != sizeof(T) * 8) { a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() - sizeof(T)); a_Offset += a_Data.GetBitSize(); @@ -65,7 +70,8 @@ DataBuffer WriteMessage(const TData& a_MessageData) { template void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) { std::size_t currentOffset = 0; - boost::pfr::for_each_field(a_MessageData, [&a_Buffer, ¤tOffset](auto& a_Field) { ReadField(a_Buffer, a_Field, currentOffset); }); + boost::pfr::for_each_field( + a_MessageData, [&a_Buffer, ¤tOffset](auto& a_Field) { ReadField(a_Buffer, a_Field, currentOffset); }); } } // namespace details -- 2.49.1