1 Commits
v2.1.0 ... ssl

Author SHA1 Message Date
b482420d3a begin ssl support
All checks were successful
Linux arm64 / Build (push) Successful in 15s
2025-03-01 20:01:05 +01:00
71 changed files with 1938 additions and 1928 deletions

4
.gitignore vendored
View File

@@ -6,6 +6,4 @@ build/
.DS_Store
.vscode
*.bin
.vscode

View File

@@ -1,6 +1,6 @@
# SimpleProtocolLib
C++ Protocol engine used to (mainly) communicate with network
Network engine used to (mainly) communicate with packets
# Integrate with xmake

View File

@@ -4,28 +4,17 @@
#include <sp/protocol/Field.h>
#include <sp/protocol/MessageBase.h>
struct DisconnectPacketData {
std::string m_Reason;
enum class DisconnectFieldsE {
Reason = 0
};
class DisconnectPacket : public sp::MessageBase<sp::PacketMessage, sp::option::DispatchImpl<DisconnectPacket>> {
private:
DisconnectPacketData m_Data;
using DisconnectFields = std::tuple<std::string /*Reason*/>;
public:
template<typename ... T>
DisconnectPacket(T... args) : m_Data{args...} {}
DeclarePacket(Disconnect){
public:
PacketConstructor(Disconnect)
const std::string& GetReason() const {
return m_Data.m_Reason;
}
virtual sp::PacketID GetId() const {
return Disconnect;
return GetField<DisconnectFieldsE, DisconnectFieldsE::Reason>();
}
};
void ff() {
sizeof(std::string);
}

View File

@@ -4,40 +4,19 @@
#include <sp/protocol/Field.h>
#include <sp/protocol/MessageBase.h>
template <sp::PacketID ID, typename TData>
class ConcreteMessage {
public:
using DataType = TData;
template<typename... T>
ConcreteMessage(const T&... args) : m_Data {args ...};
private:
DataType m_Data;
virtual sp::PacketID GetId() const {
return ID;
}
enum class KeepAliveFieldsE {
KeepAliveId = 0,
};
using KeepAliveFields = std::tuple<
std::uint64_t //<- KeepAliveId
>;
DeclarePacket(KeepAlive){
public:
PacketConstructor(KeepAlive)
struct KeepAlivePacket {
std::uint64_t m_AliveId;
};
class KeepAliveMessage : public sp::MessageBase<sp::PacketMessage, sp::option::DispatchImpl<DisconnectPacket>> {
private:
KeepAlivePacket m_Data;
public:
template <typename... T>
KeepAliveMessage(T... args) : m_Data{args...} {}
virtual sp::PacketID GetId() const {
return KeepAlive;
std::uint64_t GetKeepAliveId() const {
return GetField<KeepAliveFieldsE, KeepAliveFieldsE::KeepAliveId>();
}
};

View File

@@ -1,6 +1,6 @@
#pragma once
enum PacketIds {
enum PacketId {
KeepAlive = 0,
Disconnect,
UpgradeTower,

View File

@@ -4,36 +4,28 @@
#include <sp/protocol/Field.h>
#include <sp/protocol/MessageBase.h>
struct UpgradeTowerPacketData {
sp::BitField<std::uint16_t,
sp::Field<std::uint16_t, 12>, // std::uint16_t m_Tower : 12;
sp::Field<std::uint8_t, 4> // std::uint8_t m_Upgrade : 4;
> m_TowerAndUpgrade;
sp::VarInt m_Test;
std::map<std::string, std::vector<int>> m_Test2;
enum class UpgradeTowerFieldsE {
m_Tower = 0,
m_Upgrade,
};
class UpgradeTowerPacket : public sp::MessageBase<sp::PacketMessage, sp::option::DispatchImpl<UpgradeTowerPacket>> {
private:
UpgradeTowerPacketData m_Data;
using UpgradeTowerFields = std::tuple<
sp::BitField<std::uint16_t,
sp::Field<std::uint16_t, 12>, //<- m_Tower
sp::Field<std::uint8_t, 4> //<- m_Upgrade
>
>;
public:
template <typename... T>
UpgradeTowerPacket(T... args) : m_Data{args...} {}
DeclarePacket(UpgradeTower){
public:
PacketConstructor(UpgradeTower)
std::uint16_t GetTowerId() const {
return m_Data.m_TowerAndUpgrade.GetField<0>();
return GetField<0>().GetField<UpgradeTowerFieldsE, UpgradeTowerFieldsE::m_Tower>();
}
std::uint8_t GetTowerUpgrade() const {
return m_Data.m_TowerAndUpgrade.GetField<1>();
return GetField<0>().GetField<UpgradeTowerFieldsE, UpgradeTowerFieldsE::m_Upgrade>();
}
virtual sp::PacketID GetId() const {
return UpgradeTower;
}
UpgradeTowerPacketData& GetData() {
return m_Data;
}
};
};

View File

@@ -4,13 +4,51 @@
namespace sp {
bool IsLittleEndian();
bool IsSystemBigEndian();
void SwapBytes(std::uint8_t* begin, std::uint8_t* end);
/**
* \brief Serialize value to (network byte order) big endian
*/
template <typename T>
void ToNetwork(T& value) {}
template<typename T>
void SwapBytes(T& a_Data) {
SwapBytes(reinterpret_cast<std::uint8_t*>(&a_Data), reinterpret_cast<std::uint8_t*>(&a_Data) + sizeof(T));
}
template <>
void ToNetwork<std::uint16_t>(std::uint16_t& value);
template <>
void ToNetwork<std::uint32_t>(std::uint32_t& value);
template <>
void ToNetwork<std::uint64_t>(std::uint64_t& value);
/**
* \brief Deserialize value from (network byte order) big endian
*/
template <typename T>
void FromNetwork(T& value) {}
template <>
void FromNetwork<std::uint16_t>(std::uint16_t& value);
template <>
void FromNetwork<std::uint32_t>(std::uint32_t& value);
template <>
void FromNetwork<std::uint64_t>(std::uint64_t& value);
/**
* \brief Swap bytes if the value is any kind of integer
*/
template <typename T>
void TrySwapBytes(T& value) {}
template <>
void TrySwapBytes<std::uint16_t>(std::uint16_t& value);
template <>
void TrySwapBytes<std::uint32_t>(std::uint32_t& value);
template <>
void TrySwapBytes<std::uint64_t>(std::uint64_t& value);
} // namespace sp

View File

@@ -10,13 +10,10 @@
#include <cassert>
#include <cstdint>
#include <cstring>
#include <list>
#include <map>
#include <memory>
#include <sp/common/ByteSwapping.h>
#include <sp/common/VarInt.h>
#include <string>
#include <sp/common/VarInt.h>
#include <vector>
#include <map>
namespace sp {
@@ -26,21 +23,18 @@ namespace sp {
*/
class DataBuffer {
private:
using Data = std::vector<std::uint8_t>;
private:
typedef std::vector<std::uint8_t> Data;
Data m_Buffer;
std::size_t m_ReadOffset;
public:
using iterator = Data::iterator;
using const_iterator = Data::const_iterator;
using reference = Data::reference;
using const_reference = Data::const_reference;
using difference_type = Data::difference_type;
typedef Data::iterator iterator;
typedef Data::const_iterator const_iterator;
typedef Data::reference reference;
typedef Data::const_reference const_reference;
typedef Data::difference_type difference_type;
DataBuffer();
DataBuffer(std::size_t a_InitialSize);
DataBuffer(const DataBuffer& other);
DataBuffer(const DataBuffer& other, difference_type offset);
DataBuffer(DataBuffer&& other);
@@ -51,23 +45,21 @@ class DataBuffer {
/**
* \brief Append data to the buffer
* \warning No endian checks
*/
template <typename T>
void Append(const T& a_Data) {
std::size_t size = sizeof(a_Data);
void Append(const T& data) {
std::size_t size = sizeof(data);
std::size_t end_pos = m_Buffer.size();
m_Buffer.resize(m_Buffer.size() + size);
std::memcpy(&m_Buffer[end_pos], &a_Data, size);
std::memcpy(&m_Buffer[end_pos], &data, size);
}
/**
* \brief Append data to the buffer (converted to big endian)
* \brief Append data to the buffer
*/
template <typename T>
DataBuffer& operator<<(T a_Data) {
SwapBytes(a_Data);
Append(a_Data);
DataBuffer& operator<<(const T& data) {
Append(data);
return *this;
}
@@ -85,23 +77,51 @@ class DataBuffer {
DataBuffer& operator<<(const DataBuffer& data);
/**
* \brief Read data into a_Data
* \warning No endian checks
* \brief Append a vector to the buffer by first writing the size
* \param data The vector to append
*/
template <typename T>
void Read(T& a_Data) {
assert(m_ReadOffset + sizeof(T) <= GetSize());
std::memcpy(&a_Data, m_Buffer.data() + m_ReadOffset, sizeof(T));
m_ReadOffset += sizeof(T);
DataBuffer& operator<<(const std::vector<T>& data) {
*this << VarInt{data.size()};
for (const auto& element : data) {
*this << element;
}
return *this;
}
/**
* \brief Append a map to the buffer by first writing the size
* \param data The map to append
*/
template <typename K, typename V>
DataBuffer& operator<<(const std::map<K, V>& data) {
*this << VarInt{data.size()};
for (const auto& element : data) {
*this << element.first << element.second;
}
return *this;
}
/**
* \brief Append an array to the buffer by first writing the size
* \param data The buffer to append
*/
template <typename T, std::size_t Size>
DataBuffer& operator<<(const std::array<T, Size>& data) {
for (const auto& element : data) {
*this << element;
}
return *this;
}
/**
* \brief Read some data from the buffer and assign to desired variable
*/
template <typename T>
DataBuffer& operator>>(T& a_Data) {
Read(a_Data);
SwapBytes(a_Data);
DataBuffer& operator>>(T& data) {
assert(m_ReadOffset + sizeof(T) <= GetSize());
data = *(reinterpret_cast<T*>(&m_Buffer[m_ReadOffset]));
m_ReadOffset += sizeof(T);
return *this;
}
@@ -118,6 +138,52 @@ class DataBuffer {
*/
DataBuffer& operator>>(std::string& str);
/**
* \brief Read a vector (size + data) from the buffer
* \pre The vector is assumed to be empty
*/
template <typename T>
DataBuffer& operator>>(std::vector<T>& data) {
VarInt arraySize;
*this >> arraySize;
for (std::size_t i = 0; i < arraySize.GetValue(); i++) {
T newElement;
*this >> newElement;
data.push_back(newElement);
}
return *this;
}
/**
* \brief Read a map (size + data) from the buffer
* \pre The map is assumed to be empty
*/
template <typename K, typename V>
DataBuffer& operator>>(std::map<K, V>& data) {
VarInt mapSize;
*this >> mapSize;
for (std::size_t i = 0; i < mapSize.GetValue(); i++) {
K newKey;
V newValue;
*this >> newKey >> newValue;
data.emplace(newKey, newValue);
}
return *this;
}
/**
* \brief Read an array from the buffer
*/
template <std::size_t Size, typename T>
DataBuffer& operator>>(std::array<T, Size>& data) {
for (std::size_t i = 0; i < Size; i++) {
T newElement;
*this >> newElement;
data[i] = newElement;
}
return *this;
}
/**
* \brief Write some data to the buffer
* \param buffer The buffer to write
@@ -263,5 +329,3 @@ class DataBuffer {
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer);
} // namespace sp
#include "DataBuffer.inl"

View File

@@ -1,201 +0,0 @@
#pragma once
namespace sp {
/**
* \brief Append a pointer to the buffer
* \param data The data to append
*/
template <typename T, typename = typename std::enable_if_t<!std::is_abstract_v<T>>>
DataBuffer& operator<<(DataBuffer& buffer, const std::shared_ptr<T>& data) {
return buffer << *data;
}
/**
* \brief Append a pointer to the buffer
* \param data The data to append
*/
template <typename T, typename = typename std::enable_if_t<!std::is_abstract_v<T>>>
DataBuffer& operator<<(DataBuffer& buffer, const std::unique_ptr<T>& data) {
return buffer << *data;
}
/**
* \brief Append a vector to the buffer by first writing the size
* \param data The vector to append
*/
template <typename T>
DataBuffer& operator<<(DataBuffer& buffer, const std::vector<T>& data) {
buffer << VarInt{data.size()};
for (const auto& element : data) {
buffer << element;
}
return buffer;
}
/**
* \brief Append a list to the buffer by first writing the size
* \param data The list to append
*/
template <typename T>
DataBuffer& operator<<(DataBuffer& buffer, const std::list<T>& data) {
buffer << VarInt{data.size()};
for (const auto& element : data) {
buffer << element;
}
return buffer;
}
/**
* \brief Append a map to the buffer by first writing the size
* \param data The map to append
*/
template <typename K, typename V>
DataBuffer& operator<<(DataBuffer& buffer, const std::map<K, V>& data) {
buffer << VarInt{data.size()};
for (const auto& [key, value] : data) {
buffer << key << value;
}
return buffer;
}
/**
* \brief Append a map to the buffer by first writing the size
* \param data The map to append
*/
template <typename K, typename V>
DataBuffer& operator<<(DataBuffer& buffer, const std::unordered_map<K, V>& data) {
buffer << VarInt{data.size()};
for (const auto& [key, value] : data) {
buffer << key << value;
}
return buffer;
}
/**
* \brief Append a pair to the buffer
* \param data The pair to append
*/
template <typename K, typename V>
DataBuffer& operator<<(DataBuffer& buffer, const std::pair<K, V>& data) {
return buffer << data.first << data.second;
}
/**
* \brief Append an array to the buffer by first writing the size
* \param data The buffer to append
*/
template <typename T, std::size_t Size>
DataBuffer& operator<<(DataBuffer& buffer, const std::array<T, Size>& data) {
for (const auto& element : data) {
buffer << element;
}
return buffer;
}
/**
* \brief Read a pointer
*/
template <typename T, typename = typename std::enable_if_t<!std::is_abstract_v<T>>>
DataBuffer& operator>>(DataBuffer& buffer, std::shared_ptr<T>& data) {
data = std::make_shared<T>();
return buffer >> *data;
}
/**
* \brief Read a pointer
*/
template <typename T, typename = typename std::enable_if_t<!std::is_abstract_v<T>>>
DataBuffer& operator>>(DataBuffer& buffer, std::unique_ptr<T>& data) {
data = std::make_unique<T>();
return buffer >> *data;
}
/**
* \brief Read a vector (size + data) from the buffer
* \pre The vector is assumed to be empty
*/
template <typename T>
DataBuffer& operator>>(DataBuffer& buffer, std::vector<T>& data) {
VarInt arraySize;
buffer >> arraySize;
for (std::size_t i = 0; i < arraySize.GetValue(); i++) {
T newElement;
buffer >> newElement;
data.push_back(newElement);
}
return buffer;
}
/**
* \brief Read a list (size + data) from the buffer
* \pre The list is assumed to be empty
*/
template <typename T>
DataBuffer& operator>>(DataBuffer& buffer, std::list<T>& data) {
VarInt arraySize;
buffer >> arraySize;
for (std::size_t i = 0; i < arraySize.GetValue(); i++) {
T newElement;
buffer >> newElement;
data.push_back(newElement);
}
return buffer;
}
/**
* \brief Read a map (size + data) from the buffer
* \pre The map is assumed to be empty
*/
template <typename K, typename V>
DataBuffer& operator>>(DataBuffer& buffer, std::map<K, V>& data) {
VarInt mapSize;
buffer >> mapSize;
for (std::size_t i = 0; i < mapSize.GetValue(); i++) {
K newKey;
V newValue;
buffer >> newKey >> newValue;
data.emplace(newKey, newValue);
}
return buffer;
}
/**
* \brief Read a map (size + data) from the buffer
* \pre The map is assumed to be empty
*/
template <typename K, typename V>
DataBuffer& operator>>(DataBuffer& buffer, std::unordered_map<K, V>& data) {
VarInt mapSize;
buffer >> mapSize;
for (std::size_t i = 0; i < mapSize.GetValue(); i++) {
K newKey;
V newValue;
buffer >> newKey >> newValue;
data.emplace(newKey, newValue);
}
return buffer;
}
/**
* \brief Read a pair
*/
template <typename K, typename V>
DataBuffer& operator>>(DataBuffer& buffer, std::pair<K, V>& data) {
return buffer >> data.first >> data.second;
}
/**
* \brief Read an array from the buffer
*/
template <std::size_t Size, typename T>
DataBuffer& operator>>(DataBuffer& buffer, std::array<T, Size>& data) {
for (std::size_t i = 0; i < Size; i++) {
T newElement;
buffer >> newElement;
data[i] = newElement;
}
return buffer;
}
} // namespace sp

View File

@@ -1,139 +0,0 @@
#pragma once
#include <tuple>
namespace sp
{
// This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/
// 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 <typename TAll>
class GenericHandler;
// Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15,
typename T16, typename T17, typename T18, typename T19, typename T20,
typename... TRest>
class GenericHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T11, T13, T14, T15, T16, T17, T18, T19, T20, TRest...> > : public GenericHandler<std::tuple<TRest...> >
{
using Base = GenericHandler<std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
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 <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename... TRest>
class GenericHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public GenericHandler<std::tuple<TRest...> >
{
using Base = GenericHandler<std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
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 <
typename T1, typename T2, typename T3, typename T4, typename T5,
typename... TRest>
class GenericHandler<std::tuple<T1, T2, T3, T4, T5, TRest...> > : public GenericHandler<std::tuple<TRest...> >
{
using Base = GenericHandler<std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
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 <typename T1, typename T2, typename T3, typename T4>
class GenericHandler<std::tuple<T1, T2, T3, T4> >
{
public:
virtual ~GenericHandler() {}
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 < typename T1, typename T2, typename T3>
class GenericHandler<std::tuple<T1, T2, T3> >
{
public:
virtual ~GenericHandler() {}
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 <typename T1, typename T2>
class GenericHandler<std::tuple<T1, T2> >
{
public:
virtual ~GenericHandler() {}
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 <typename T1>
class GenericHandler<std::tuple<T1> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) {}
// virtual void Handle(const TCommon&) {} //Nothing to do
};
// Deal with rest with 0 type
template <>
class GenericHandler<std::tuple<> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle() {} //Nothing to do
};
} // sp

View File

@@ -1,25 +0,0 @@
#pragma once
/**
* \file NonCopyable.h
* \brief File containing the sp::NonCopyable class
*/
namespace sp {
/**
* \class NonCopyable
* \brief Class used to make a class non copyable
* \note Inherit from this class privately to make a class non copyable
*/
class NonCopyable {
public:
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
protected:
NonCopyable() {}
~NonCopyable() {}
};
} // namespace sp

View File

@@ -1,8 +1,6 @@
#pragma once
#include <cstdint>
#include <string>
#include <tuple>
namespace sp {
@@ -52,48 +50,4 @@ void TupleForEach(TFunc&& func, TTuple&& tuple) {
details::TupleForEachHelper<TupleSize>::Exec(std::forward<TTuple>(tuple), std::forward<TFunc>(func));
}
namespace details {
template <typename T, typename U = void>
struct is_mappish_impl : std::false_type {};
template <typename T>
struct is_mappish_impl<T, std::void_t<typename T::key_type, typename T::mapped_type,
decltype(std::declval<T&>()[std::declval<const typename T::key_type&>()])>> : std::true_type {};
template <typename T, typename U = void>
struct is_vectish_impl : std::false_type {};
template <typename T>
struct is_vectish_impl<T,
std::void_t<typename T::value_type, decltype(std::declval<T&>()[std::declval<const typename T::value_type&>()])>>
: std::true_type {};
template <typename T, typename U = void>
struct is_pairish_impl : std::false_type {};
template <typename T>
struct is_pairish_impl<T,
std::void_t<typename T::first_type, decltype(std::declval<T&>()[std::declval<const typename T::first_type&>()])>>
: std::true_type {};
template <typename T>
using is_general_t = std::integral_constant<bool,
(std::is_same_v<T, std::string> || (!std::is_same_v<T, char> && !std::is_same_v<T, unsigned char> && !std::is_arithmetic_v<T> &&
!is_pairish_impl<T>::value && !is_mappish_impl<T>::value && !is_vectish_impl<T>::value))>;
template <typename T>
using is_primitive =
std::integral_constant<bool, std::is_same_v<T, char> || std::is_same_v<T, unsigned char> || std::is_arithmetic_v<T>>;
} // namespace details
} // namespace sp

View File

@@ -7,14 +7,11 @@
#include <cstddef>
#include <cstdint>
#include <functional>
namespace sp {
class DataBuffer;
using ReadFunc = std::function<void(std::uint8_t&)>;
/**
* \class VarInt
* \brief Variable-length format such that smaller numbers use fewer bytes.
@@ -58,8 +55,6 @@ class VarInt {
* \param var The variable integer to deserialize
*/
friend DataBuffer& operator>>(DataBuffer& in, VarInt& var);
void Read(const ReadFunc& read);
};
} // namespace sp

View File

@@ -0,0 +1,30 @@
#pragma once
#include <sp/protocol/GenericHandler.h>
#include <sp/protocol/Message.h>
namespace sp {
class PacketHandler;
using PacketMessage = Message<
option::MsgIdType<std::uint8_t>, // add id() operation
option::ReadOperations, // add read() operation
option::WriteOperations, // add write() operation
option::WriteId, // write id before data
option::Handler<PacketHandler> // add dispatch() operation
>;
#define PacketConstructor(packetName) \
packetName##Packet() {} \
template <typename... Args> \
packetName##Packet(Args... args) { \
Construct(args...); \
}
#define DeclarePacket(packetName) \
class packetName##Packet : public sp::MessageBase<sp::PacketMessage, sp::option::StaticNumIdImpl<packetName>, \
sp::option::DispatchImpl<packetName##Packet>, sp::option::FieldsImpl<packetName##Fields>>
} // namespace sp

View File

@@ -0,0 +1,15 @@
#pragma once
#include <sp/default/DefaultPacket.h>
#include <sp/default/DefaultPacketHandler.h>
#include <sp/protocol/MessageDispatcher.h>
namespace sp {
using PacketDispatcher = MessageDispatcher<
PacketMessage::ParsedOptions::MsgIdType,
PacketMessage,
PacketMessage::ParsedOptions::HandlerType::HandlerT
>;
} // namespace sp

View File

@@ -0,0 +1,7 @@
#pragma once
#include <sp/protocol/MessageFactory.h>
namespace sp {
using PacketFactory = sp::MessageFactory<sp::PacketMessage, AllPackets>;
} // namespace sp

View File

@@ -0,0 +1,9 @@
#pragma once
#include <sp/protocol/GenericHandler.h>
// the tuple AllPackets must be defined !
namespace sp {
class PacketHandler : public sp::GenericHandler<PacketMessage, AllPackets> {};
} // namespace sp

View File

@@ -6,7 +6,20 @@
*/
#include <cstdint>
#include <sp/io/MessageEncapsulator.h>
#include <sp/common/DataBuffer.h>
namespace sp {
namespace option {
struct ZlibCompress {
bool m_Enabled = true;
std::size_t m_CompressionThreshold = 64;
};
} // namespace option
} // namespace sp
#include <sp/io/IOInterface.h>
namespace sp {
namespace zlib {
@@ -28,20 +41,14 @@ DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength);
} // namespace zlib
namespace io {
class ZlibCompress : public MessageEncapsulator {
private:
std::size_t m_CompressionThreshold;
template <>
class MessageEncapsulator<option::ZlibCompress> {
public:
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;
static DataBuffer Encapsulate(const DataBuffer& a_Data, const option::ZlibCompress& a_Option);
static DataBuffer Decapsulate(DataBuffer& a_Data, const option::ZlibCompress& a_Option);
};
} // namespace sp
} // namespace io
} // namespace sp

View File

@@ -0,0 +1,41 @@
#pragma once
/**
* \file Encrypt.h
* \brief File containing encrypt utilities
*/
#include <sp/common/DataBuffer.h>
namespace sp {
namespace option {
struct TlsEncrypt {
bool m_Enabled = true;
};
} // namespace option
} // namespace sp
#include <sp/io/IOInterface.h>
namespace sp {
namespace ssl {
// encrypt
// decrypt
} // namespace ssl
namespace io {
template <>
class MessageEncapsulator<option::TlsEncrypt> {
public:
static DataBuffer Encapsulate(const DataBuffer& a_Data, const option::TlsEncrypt& a_Option);
static DataBuffer Decapsulate(DataBuffer& a_Data, const option::TlsEncrypt& a_Option);
};
} // namespace io
} // namespace sp

View File

@@ -2,8 +2,4 @@
#if __has_include(<sp/extensions/Compress.h>)
#include <sp/extensions/Compress.h>
#endif
#if __has_include(<sp/extensions/Tcp.h>)
#include <sp/extensions/Tcp.h>
#endif

View File

@@ -1,4 +0,0 @@
#pragma once
#include <sp/extensions/tcp/TcpListener.h>
#include <sp/extensions/tcp/TcpSocket.h>

View File

@@ -1,68 +0,0 @@
#pragma once
#include <sp/extensions/tcp/TcpSocket.h>
#include <memory>
namespace sp {
namespace io {
/**
* \class TcpListener
*/
class TcpListener : private NonCopyable {
public:
using SocketHandle = TcpSocket::SocketHandle;
/**
* \brief Starts listening for guests to connect
* \param port The port to listen to
* \param maxConnexions The maximum amount of connexion that can happen at the same time. \n
* Every other guests will be kicked if this amount is reached.
* \return Whether this action was succesfull
*/
TcpListener(std::uint16_t a_Port, int a_MaxConnexions);
/**
* \brief Default destructor
*/
~TcpListener();
/**
* \brief Tries to accept an incoming request to connect
* \return the new socket if a new connexion was accepted or nullptr
*/
std::unique_ptr<TcpSocket> Accept();
/**
* \brief Closes the socket
*/
void Close();
/**
* \brief Allows to set the socket in non blocking/blocking mode
* \param a_Blocking If set to true, every call to Read will wait until the socket receives something
* \return true if the operation was successful
*/
bool SetBlocking(bool a_Blocking);
/**
* \brief Getter of the m_Port member
* \return The port which the socket listen to
*/
std::uint16_t GetListeningPort() const;
/**
* \brief Getter of the m_MaxConnections member
* \return The maximum amount of connexions that can happen at the same time.
*/
int GetMaximumConnections() const;
private:
SocketHandle m_Handle;
std::uint16_t m_Port;
int m_MaxConnections;
};
} // namespace io
} // namespace sp

View File

@@ -1,80 +0,0 @@
#pragma once
#include <sp/common/NonCopyable.h>
#include <sp/io/IoInterface.h>
namespace sp {
namespace io {
class TcpListener;
class SocketError : public std::exception {
private:
std::string m_Error;
public:
SocketError(std::string&& a_Msg) : m_Error(std::move(a_Msg)) {}
virtual const char* what() const noexcept override {
return m_Error.c_str();
}
};
class TcpSocket : public sp::IoInterface {
public:
using SocketHandle = int;
/**
* \enum Status
* \brief Describes the state of a socket
*/
enum class Status {
/** The socket is connected */
Connected,
/** The socket is not connected */
Disconnected,
/** Something bad happened */
Error,
};
TcpSocket();
TcpSocket(const std::string& a_Host, std::uint16_t a_Port);
TcpSocket(TcpSocket&& a_Other);
TcpSocket& operator=(TcpSocket&& a_Other);
virtual ~TcpSocket();
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
* \param a_Block If set to true, every call to Read will wait until the socket receives something
* \return true if the operation was successful
*/
bool SetBlocking(bool a_Block);
/**
* \brief Getter of the m_Status member
* \return The TcpSocket::Status of this socket
*/
Status GetStatus() const;
/**
* \brief Disconnects the socket from the remote
* \note Does nothing if the socket is not connected. \n
* This function is also called by the destructor.
*/
void Disconnect();
private:
SocketHandle m_Handle;
Status m_Status;
void Connect(const std::string& a_Host, std::uint16_t a_Port);
friend class TcpListener;
};
} // namespace io
} // namespace sp

View File

@@ -1,90 +0,0 @@
#pragma once
#include <sp/common/DataBuffer.h>
#include <sp/protocol/BitField.h>
namespace sp {
// TODO: flush if offset exceeds 64
class BitBuffer {
private:
using Data = std::uint64_t;
private:
DataBuffer& m_Buffer;
Data m_Data;
std::size_t m_Offset;
bool m_WasBitField;
public:
BitBuffer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer), m_Data(0), m_Offset(0), m_WasBitField(false) {}
void UpdateWrite(bool a_IsBitField) {
if (m_Offset == 0)
return;
if ((m_Offset % 8 == 0) || (!a_IsBitField && m_WasBitField)) {
Flush();
}
m_WasBitField = a_IsBitField;
}
void UpdateRead(bool a_IsBitField) {
if (m_Offset == 0)
return;
if ((m_Offset % 8 == 0) || (!a_IsBitField && m_WasBitField)) {
MoveReadOffset();
}
m_WasBitField = a_IsBitField;
}
template <typename T, std::size_t BitSize>
void Append(T a_Data) {
Data bin = static_cast<Data>(a_Data);
bin &= ((1 << BitSize) - 1); // prevents overflow
std::size_t pushCount = sizeof(Data) * 8 - m_Offset - BitSize;
m_Data |= bin << pushCount;
m_Offset += BitSize;
}
template <typename T, std::size_t BitSize>
void Read(T& a_Data) {
std::size_t byteCount = GetByteCount(m_Offset + BitSize);
constexpr Data dataMask = (1 << BitSize) - 1;
m_Buffer.ReadSome(reinterpret_cast<std::uint8_t*>(&m_Data), byteCount);
SwapBytes(reinterpret_cast<std::uint8_t*>(&m_Data), reinterpret_cast<std::uint8_t*>(&m_Data) + byteCount);
m_Data >>= byteCount * 8 - m_Offset - BitSize;
m_Data &= dataMask;
a_Data = T(m_Data);
m_Buffer.SetReadOffset(m_Buffer.GetReadOffset() - byteCount);
m_Offset += BitSize;
}
private:
void Flush() {
std::size_t byteCount = GetByteCount();
m_Data >>= (sizeof(Data) - byteCount) * 8;
SwapBytes(reinterpret_cast<std::uint8_t*>(&m_Data), reinterpret_cast<std::uint8_t*>(&m_Data) + byteCount);
m_Buffer.WriteSome(reinterpret_cast<std::uint8_t*>(&m_Data), byteCount);
m_Offset = 0;
m_WasBitField = false;
m_Data = 0;
}
void MoveReadOffset() {
std::size_t byteCount = GetByteCount();
m_Buffer.SetReadOffset(m_Buffer.GetReadOffset() + byteCount);
m_Offset = 0;
m_WasBitField = false;
m_Data = 0;
}
std::size_t GetByteCount(std::size_t a_Offset = -1) const {
if (a_Offset == static_cast<std::size_t>(-1))
a_Offset = m_Offset;
if (a_Offset <= 8)
return 1;
return (a_Offset - 1) / 8 + 1;
}
};
} // namespace sp

33
include/sp/io/File.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <fstream>
#include <sp/io/IOInterface.h>
namespace sp {
namespace io {
struct FileTag {
enum OpenMode {
In = 1,
Out = 1 << 1,
};
};
template <>
class IOInterface<FileTag> {
private:
std::unique_ptr<std::ifstream> m_FileInput;
std::unique_ptr<std::ofstream> 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<FileTag>;
} // namespace io
} // namespace sp

View File

@@ -0,0 +1,58 @@
#pragma once
#include <memory>
#include <sp/common/DataBuffer.h>
namespace sp {
namespace io {
template <typename IOTag>
class IOInterface {
public:
DataBuffer Read(std::size_t a_Amount);
void Write(const DataBuffer& a_Data);
};
template <typename TOption>
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 <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
class Stream {
protected:
MessageDispatcher m_Dispatcher;
IOInterface<IOTag> m_Interface;
std::tuple<TOptions...> m_Options;
using MessageBase = typename MessageDispatcher::MessageBaseType;
public:
Stream() {}
Stream(IOInterface<IOTag>&& a_Interface, TOptions&&... a_Options);
Stream(Stream&& a_Stream);
void RecieveMessages();
void SendMessage(const MessageBase& a_Message);
template <typename TOption>
TOption& GetOption() {
return std::get<TOption>(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 <sp/io/IOInterfaceImpl.inl>

View File

@@ -0,0 +1,128 @@
#pragma once
#include <sp/extensions/Compress.h>
#include <stdexcept>
namespace sp {
namespace details {
template <typename... TOptions>
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 <typename TOption, typename... TOptions>
struct MessageEncapsulatorPack<TOption, TOptions...> {
static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) {
DataBuffer data = io::MessageEncapsulator<TOption>::Encapsulate(a_Data, a_Option);
return MessageEncapsulatorPack<TOptions...>::Encapsulate(data, a_Options...);
}
static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) {
DataBuffer data = io::MessageEncapsulator<TOption>::Decapsulate(a_Data, a_Option);
return MessageEncapsulatorPack<TOptions...>::Decapsulate(data, a_Options...);
}
};
} // namespace details
namespace io {
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Stream(IOInterface<IOTag>&& a_Interface, TOptions&&... a_Options) :
m_Interface(std::move(a_Interface)), m_Options(std::make_tuple<TOptions...>(std::move(a_Options)...)) {}
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Stream(
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>&& a_Stream) :
m_Dispatcher(std::move(a_Stream.m_Dispatcher)), m_Interface(std::move(a_Stream.m_Interface)) {}
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
void Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::SendMessage(const MessageBase& a_Message) {
DataBuffer data = a_Message.Write();
DataBuffer encapsulated = std::apply([&data](const auto&... a_Options) {
return Encapsulate(data, a_Options...);
}, m_Options);
DataBuffer finalData;
finalData << VarInt{encapsulated.GetSize()} << encapsulated;
m_Interface.Write(finalData);
}
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
void Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::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<std::uint64_t>(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);
buffer = std::apply([&buffer, lenghtValue](const auto&... a_Options) {
return Decapsulate(buffer, a_Options...);
}, m_Options);
VarInt packetType;
buffer >> packetType;
static const MessageFactory messageFactory;
std::unique_ptr<MessageBase> message = messageFactory.CreateMessage(packetType.GetValue());
assert(message != nullptr);
message->Read(buffer);
GetDispatcher().Dispatch(*message);
}
}
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
DataBuffer Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Encapsulate(
const DataBuffer& a_Data, const TOptions&... a_Options) {
return details::MessageEncapsulatorPack<TOptions...>::Encapsulate(a_Data, a_Options...);
}
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
DataBuffer Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Decapsulate(
DataBuffer& a_Data, const TOptions&... a_Options) {
return details::MessageEncapsulatorPack<TOptions...>::Decapsulate(a_Data, a_Options...);
}
} // namespace io
} // namespace sp

View File

@@ -1,14 +0,0 @@
#pragma once
#include <sp/common/DataBuffer.h>
#include <sp/common/NonCopyable.h>
namespace sp {
class IoInterface : private NonCopyable {
public:
virtual DataBuffer Read(std::size_t a_Amount) = 0;
virtual void Write(const DataBuffer& a_Data) = 0;
};
} // namespace sp

23
include/sp/io/Memory.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <sp/io/IOInterface.h>
namespace sp {
namespace io {
struct MemoryTag {};
template <>
class IOInterface<MemoryTag> {
private:
sp::DataBuffer m_VirtualIO;
public:
sp::DataBuffer Read(std::size_t a_Amount);
void Write(const sp::DataBuffer& a_Data);
};
using Memory = IOInterface<MemoryTag>;
} // namespace io
} // namespace sp

View File

@@ -1,31 +0,0 @@
#pragma once
#include <sp/common/DataBuffer.h>
namespace sp {
class MessageEncapsulator {
protected:
bool m_Enabled = true;
public:
MessageEncapsulator() {}
virtual ~MessageEncapsulator() {}
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

View File

@@ -1,53 +0,0 @@
#pragma once
#include <boost/pfr.hpp>
#include <sp/io/BitBuffer.h>
namespace sp {
namespace details {
template <typename T, std::size_t BitSize>
void WriteField(DataBuffer& a_Buffer, const BitField<T, BitSize>& a_Data, BitBuffer& a_BitBuffer) {
a_BitBuffer.Append<T, BitSize>(*a_Data);
a_BitBuffer.UpdateWrite(true);
}
template <typename T>
void WriteField(DataBuffer& a_Buffer, const T& a_Data, BitBuffer& a_BitBuffer) {
a_Buffer << a_Data;
a_BitBuffer.UpdateWrite(false);
}
template <typename T, std::size_t BitSize>
void ReadField(DataBuffer& a_Buffer, BitField<T, BitSize>& a_Data, BitBuffer& a_BitBuffer) {
a_BitBuffer.Read<T, BitSize>(*a_Data);
a_BitBuffer.UpdateRead(true);
}
template <typename T>
void ReadField(DataBuffer& a_Buffer, T& a_Data, BitBuffer& a_BitBuffer) {
a_Buffer >> a_Data;
a_BitBuffer.UpdateRead(false);
}
} // namespace details
template <typename TData>
DataBuffer WriteMessage(const TData& a_MessageData) {
DataBuffer buffer;
BitBuffer bitBuffer(buffer);
boost::pfr::for_each_field(
a_MessageData, [&buffer, &bitBuffer](const auto& a_Field) { details::WriteField(buffer, a_Field, bitBuffer); });
bitBuffer.UpdateWrite(false);
return buffer;
}
template <typename TData>
void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) {
BitBuffer bitBuffer(a_Buffer);
boost::pfr::for_each_field(
a_MessageData, [&a_Buffer, &bitBuffer](auto& a_Field) { details::ReadField(a_Buffer, a_Field, bitBuffer); });
bitBuffer.UpdateRead(false);
}
} // namespace sp

View File

@@ -1,54 +0,0 @@
#pragma once
#include <sp/common/DataBuffer.h>
#include <sp/io/IoInterface.h>
#include <sp/io/MessageEncapsulator.h>
#include <vector>
#include <memory>
namespace sp {
template <typename TMessageFactory>
class MessageStream {
protected:
std::vector<std::shared_ptr<MessageEncapsulator>> m_Encapsulators;
std::shared_ptr<IoInterface> m_Stream;
using MessageBaseType = typename TMessageFactory::MessageBaseType;
using MessageIdType = typename MessageBaseType::MessageIdType;
public:
MessageStream(std::shared_ptr<IoInterface>&& a_Stream) : m_Stream(std::move(a_Stream)) {}
template<typename... TEnc>
MessageStream(std::shared_ptr<IoInterface>&& 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<MessageBaseType> ReadMessage();
std::unique_ptr<MessageBaseType> ReadMessage(MessageIdType a_Id);
void WriteMessage(const MessageBaseType& a_Message, bool a_WriteId = true);
template<typename... Args>
void AddEncapsulators(Args&& ... a_Encapsulators) {
AddEncapsulators(std::move(std::make_tuple<>(a_Encapsulators ...)));
}
template<typename... Args>
void AddEncapsulators(std::tuple<Args...>&& a_Encapsulators) {
TupleForEach([this](auto&& a_Encapsulator){
m_Encapsulators.push_back(std::move(a_Encapsulator));
}, a_Encapsulators);
}
private:
DataBuffer ReadAndDecapsulate();
std::unique_ptr<MessageBaseType> MakeMessage(DataBuffer& buffer, MessageIdType a_Id);
};
} // namespace sp
#include <sp/io/MessageStream.inl>

View File

@@ -1,63 +0,0 @@
#pragma once
#include <sp/common/VarInt.h>
#include <sp/protocol/MessageFactory.h>
namespace sp {
template <typename TMessageFactory>
DataBuffer MessageStream<TMessageFactory>::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 (auto& enc : m_Encapsulators) {
buffer = enc->Decapsulate(buffer);
}
return buffer;
}
template <typename TMessageFactory>
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::MakeMessage(DataBuffer& buffer, MessageIdType a_Id) {
static const TMessageFactory FACTORY;
auto message = FACTORY.CreateMessage(a_Id);
message->Read(buffer);
return message;
}
template <typename TMessageFactory>
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::ReadMessage(MessageIdType a_Id) {
DataBuffer buffer = ReadAndDecapsulate();
return MakeMessage(buffer, a_Id);
}
template <typename TMessageFactory>
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::ReadMessage() {
DataBuffer buffer = ReadAndDecapsulate();
VarInt messageId;
buffer >> messageId;
return MakeMessage(buffer, MessageIdType(messageId.GetValue()));
}
template <typename TMessageFactory>
void MessageStream<TMessageFactory>::WriteMessage(const MessageBaseType& a_Message, bool a_WriteId) {
DataBuffer buffer;
if (a_WriteId)
buffer << VarInt{static_cast<std::uint64_t>(a_Message.GetId())};
buffer << a_Message.Write();
for (auto& enc : m_Encapsulators) {
buffer = enc->Encapsulate(buffer);
}
DataBuffer header;
header << VarInt{buffer.GetSize()};
m_Stream->Write(header);
m_Stream->Write(buffer);
}
} // namespace sp

View File

@@ -1,83 +0,0 @@
#pragma once
#include <sp/common/DataBuffer.h>
namespace sp {
template <typename TMessageFactory>
class SerializableMessage {
using MessageBaseType = typename TMessageFactory::MessageBaseType;
using HandlerType = typename MessageBaseType::HandlerType;
using MessageIdType = typename MessageBaseType::MessageIdType;
private:
std::unique_ptr<MessageBaseType> m_Message;
public:
SerializableMessage(std::unique_ptr<MessageBaseType>&& a_MessagePtr) : m_Message(std::move(a_MessagePtr)) {}
operator MessageBaseType&() {
return *m_Message;
}
SerializableMessage& operator=(std::unique_ptr<MessageBaseType>&& a_MessagePtr) {
m_Message = std::move(a_MessagePtr);
return *this;
}
MessageIdType GetId() const {
return m_Message->GetId();
}
void Dispatch(HandlerType& handler) const {
m_Message->Dispatch(handler);
}
void Read(DataBuffer& a_Buffer) {
m_Message->Read(a_Buffer);
}
DataBuffer Write() const {
return m_Message->Write();
}
};
template <typename TMessageFactory>
DataBuffer& operator<<(DataBuffer& a_Buffer, const std::unique_ptr<SerializableMessage<TMessageFactory>>& a_Message) {
return a_Buffer << VarInt{static_cast<std::uint64_t>(a_Message->GetId())} << a_Message->Write();
}
template <typename TMessageFactory>
DataBuffer& operator<<(DataBuffer& a_Buffer, const std::shared_ptr<SerializableMessage<TMessageFactory>>& a_Message) {
return a_Buffer << VarInt{static_cast<std::uint64_t>(a_Message->GetId())} << a_Message->Write();
}
template <typename TMessageFactory>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::unique_ptr<SerializableMessage<TMessageFactory>>& a_Message) {
using MsgId = typename TMessageFactory::IdType;
static TMessageFactory factory;
VarInt msgId;
a_Buffer >> msgId;
a_Message = std::make_unique<SerializableMessage<TMessageFactory>>(std::move(factory.CreateMessage(MsgId(msgId.GetValue()))));
a_Message->Read(a_Buffer);
return a_Buffer;
}
template <typename TMessageFactory>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::shared_ptr<SerializableMessage<TMessageFactory>>& a_Message) {
using MsgId = typename TMessageFactory::IdType;
static TMessageFactory factory;
VarInt msgId;
a_Buffer >> msgId;
a_Message = std::make_shared<SerializableMessage<TMessageFactory>>(std::move(factory.CreateMessage(MsgId(msgId.GetValue()))));
a_Message->Read(a_Buffer);
return a_Buffer;
}
} // namespace sp

View File

@@ -1,43 +0,0 @@
#pragma once
#include <sp/io/IoInterface.h>
#include <iostream>
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<char*>(buffer.data()), a_Amount);
return buffer;
}
virtual void Write(const DataBuffer& a_Data) override {
assert(!"Write not implemented !");
}
};
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 {
assert(!"Read not implemented !");
return {};
}
virtual void Write(const DataBuffer& a_Data) override {
m_Io.write(reinterpret_cast<const char*>(a_Data.data()), a_Data.GetSize());
}
};
} // namespace sp

View File

@@ -1,37 +0,0 @@
#pragma once
#include <cstdint>
namespace sp {
template <typename T, std::size_t BitSize>
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

View File

@@ -1,57 +0,0 @@
#pragma once
#include <sp/protocol/MessageBase.h>
#include <sp/io/MessageIO.h>
namespace sp {
template <typename TData, typename MessageBase, typename MessageBase::MessageIdType ID, bool DefineDispatch = true>
class ConcreteMessage : public MessageBase {
public:
using DataType = TData;
using MessageIdType = typename MessageBase::MessageIdType;
using HandlerType = typename MessageBase::HandlerType;
template <typename... T>
ConcreteMessage(T&&... args) : m_Data{std::move(args)...} {}
virtual ~ConcreteMessage() {}
virtual MessageIdType GetId() const override {
return ID;
}
virtual void Dispatch(HandlerType& handler) const override {
if constexpr (DefineDispatch)
handler.Handle(*this);
}
virtual void Read(DataBuffer& a_Buffer) override {
ReadMessage(a_Buffer, m_Data);
}
virtual DataBuffer Write() const override {
return WriteMessage(m_Data);
}
DataType& operator*() {
return m_Data;
}
DataType* operator->() {
return &m_Data;
}
const DataType& operator*() const {
return m_Data;
}
const DataType* operator->() const {
return &m_Data;
}
private:
DataType m_Data;
};
} // namespace sp

119
include/sp/protocol/Field.h Normal file
View File

@@ -0,0 +1,119 @@
#pragma once
#include <sp/common/DataBuffer.h>
#include <sp/common/Templates.h>
namespace sp {
/**
* \brief Example usage :
* sp::BitField<std::uint16_t, sp::Field<std::uint16_t, 12>, sp::Field<std::uint8_t, 4>>;
*/
template <typename TContainer, typename... TFields>
class BitField {
using AllFields = std::tuple<TFields...>;
public:
template <typename... T>
BitField(const std::tuple<T...>& args) {
Apply<0, T...>(args);
}
BitField() {}
template <typename... T>
BitField& operator=(const std::tuple<T...>& args) {
Apply<0, T...>(args);
return *this;
}
AllFields& GetFields() {
return m_Fields;
}
const AllFields& GetFields() const {
return m_Fields;
}
template <std::size_t FIndex>
auto& GetField() {
return std::get<FIndex>(this->GetFields()).GetValue();
}
template <std::size_t FIndex>
const auto& GetField() const {
return std::get<FIndex>(this->GetFields()).GetValue();
}
// allow use of enums
template <typename E, E FIndex>
const auto& GetField() const {
return std::get<static_cast<std::size_t>(FIndex)>(this->GetFields()).GetValue();
}
private:
template <int IOffset, typename... T, std::enable_if_t<IOffset >= sizeof...(T), bool> = true>
void Apply(const std::tuple<T...>& args) {}
template <int IOffset, typename... T, std::enable_if_t<!(IOffset >= sizeof...(T)), bool> = true>
void Apply(const std::tuple<T...>& args) {
this->GetField<IOffset>() = std::get<IOffset>(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 <typename ValueType, int IAlignment>
class Field {
public:
// Provide an access to the stored value
ValueType& GetValue() {
return m_Value;
}
const ValueType& GetValue() const {
return m_Value;
}
Field& operator=(const ValueType& value) {
m_Value = value;
return *this;
}
constexpr int GetAlignment() const {
return IAlignment;
}
private:
ValueType m_Value;
};
namespace details {
template <typename... TFields>
struct FieldsBuilder {};
template <>
struct FieldsBuilder<> {
using Type = std::tuple<>;
};
template <typename... TFields>
struct FieldsBuilder<std::tuple<TFields...>> {
using Type = typename FieldsBuilder<TFields...>::Type;
};
template <typename TField, typename... TFields>
struct FieldsBuilder<TField, TFields...> {
using Type = sp::tuple_cat_t<std::tuple<Field<TField, 0>>, typename FieldsBuilder<TFields...>::Type>;
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,141 @@
#pragma once
#include <tuple>
namespace sp
{
// This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/
// 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 <typename TCommon, typename TAll>
class GenericHandler;
// Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage
template <typename TCommon,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15,
typename T16, typename T17, typename T18, typename T19, typename T20,
typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T11, T13, T14, T15, T16, T17, T18, T19, T20, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
{
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T5& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T6& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T7& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T8& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T9& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T10& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T11& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T12& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T13& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T14& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T15& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T16& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T17& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T18& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T19& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T20& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
};
// 10 by 10
template <typename TCommon,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
{
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T5& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T6& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T7& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T8& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T9& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T10& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
};
// 5 by 5
template <typename TCommon,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
{
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T5& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
};
// Deal with rest with 4 types
template <typename TCommon, typename T1, typename T2, typename T3, typename T4>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const TCommon&) { } //Nothing to do
};
// Deal with rest with 3 types
template <typename TCommon, typename T1, typename T2, typename T3>
class GenericHandler<TCommon, std::tuple<T1, T2, T3> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const TCommon&) { } //Nothing to do
};
// Deal with rest with 2 types
template <typename TCommon, typename T1, typename T2>
class GenericHandler<TCommon, std::tuple<T1, T2> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const TCommon&) { } //Nothing to do
};
// Deal with rest with 1 type
template <typename TCommon, typename T1>
class GenericHandler<TCommon, std::tuple<T1> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); }
virtual void Handle(const TCommon&) { } //Nothing to do
};
// Deal with rest with 0 type
template <typename TCommon>
class GenericHandler<TCommon, std::tuple<> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(const TCommon&) { } //Nothing to do
};
} // sp

View File

@@ -0,0 +1,16 @@
#pragma once
// Inspired by
// https://alex-robenko.gitbook.io/comms-protocols-cpp
#include <sp/protocol/message/MessageInterfaceBuilder.h>
namespace sp {
template <typename... TOptions>
class Message : public details::MessageInterfaceBuilder<TOptions...>::Type {
public:
using ParsedOptions = typename details::MessageInterfaceBuilder<TOptions...>::ParsedOptions;
};
} // namespace sp

View File

@@ -1,25 +1,16 @@
#pragma once
#include <iosfwd>
#include <sp/common/DataBuffer.h>
#include <sp/protocol/Message.h>
#include <sp/protocol/message/MessageImplOptions.h>
#include <sp/protocol/message/MessagesImpl.h>
#include <sp/protocol/message/MessageImplProcess.h>
#include <sp/protocol/message/MessageImplBuilder.h>
namespace sp {
template <typename TMessageID, typename THandler>
class MessageBase {
public:
using HandlerType = THandler;
using MessageIdType = TMessageID;
template <typename TBase, typename... TOptions>
class MessageBase : public details::MessageImplBuilder<TBase, TOptions...>::Type {};
MessageBase() {}
virtual ~MessageBase() {}
virtual MessageIdType GetId() const = 0;
virtual void Dispatch(HandlerType& handler) const = 0;
virtual void Read(DataBuffer& a_Buffer) = 0;
virtual DataBuffer Write() const = 0;
};
} // namespace sp
} // namespace sp

View File

@@ -7,7 +7,6 @@
#include <map>
#include <memory>
#include <vector>
namespace sp {
@@ -15,12 +14,13 @@ namespace sp {
* \class MessageDispatcher
* \brief Class used to dispatch messages
*/
template <typename MessageBase>
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
class MessageDispatcher {
private:
std::map<MessageIdType, std::vector<std::shared_ptr<MessageHandler>>> m_Handlers;
public:
using MessageBaseType = MessageBase;
using MessageIdType = typename MessageBase::MessageIdType;
using MessageHandler = typename MessageBase::HandlerType;
/**
* \brief Constructor
@@ -38,25 +38,20 @@ class MessageDispatcher {
* \param type The packet type
* \param handler The packet handler
*/
void RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler);
void RegisterHandler(MessageIdType a_MessageType, const std::shared_ptr<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);
void UnregisterHandler(MessageIdType a_MessageType, const std::shared_ptr<MessageHandler>& a_Handler);
/**
* \brief Unregister a packet handler
* \param handler The packet handler
*/
void UnregisterHandler(MessageHandler* a_Handler);
private:
std::map<MessageIdType, std::vector<MessageHandler*>> m_Handlers;
void UnregisterHandler(const std::shared_ptr<MessageHandler>& a_Handler);
};
} // namespace sp
#include <sp/protocol/message/MessageDispatcherImpl.inl>
#include <sp/protocol/MessageDispatcherImpl.inl>
} // namespace sp

View File

@@ -1,43 +0,0 @@
#pragma once
#include <algorithm>
#include <cassert>
namespace sp {
template <typename MessageBase>
void MessageDispatcher<MessageBase>::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 <typename MessageBase>
void MessageDispatcher<MessageBase>::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 <typename MessageBase>
void MessageDispatcher<MessageBase>::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 <typename MessageBase>
void MessageDispatcher<MessageBase>::Dispatch(const MessageBase& a_Message) {
MessageIdType type = a_Message.GetId();
for (auto& handler : m_Handlers[type]) {
a_Message.Dispatch(*handler);
}
}
} // namespace sp

View File

@@ -2,37 +2,28 @@
#include <array>
#include <functional>
#include <iostream>
#include <memory>
#include <sp/common/Tuples.h>
#include <sp/protocol/message/ArrayFillerImpl.h>
namespace sp {
template <typename TBase, typename TTMessages>
class MessageFactory {
public:
using IdType = typename TBase::MessageIdType;
using MessageBaseType = TBase;
using IdType = typename TBase::MsgIdType;
MessageFactory() {
constexpr std::size_t messageCount = std::tuple_size_v<TTMessages>;
m_Factory.resize(messageCount);
TupleForEach([this](const auto& message){
std::size_t messageID = static_cast<std::size_t>(message.GetId());
using MessageType = std::decay_t<decltype(message)>;
m_Factory.emplace(m_Factory.begin() + messageID, []() -> std::unique_ptr<TBase> { return std::make_unique<MessageType>(); });
}, TTMessages{});
}
MessageFactory() : m_Factory(details::ArrayFiller<TBase, TTMessages>::ArrayCreate()) {}
std::unique_ptr<TBase> CreateMessage(IdType id) const {
std::size_t idSize = static_cast<std::size_t>(id);
if (idSize >= m_Factory.size())
if (id >= m_Factory.size())
return nullptr;
return m_Factory.at(idSize)();
return m_Factory.at(id)();
}
private:
std::vector<std::function<std::unique_ptr<TBase>(void)>> m_Factory;
details::ArrayType<TBase> m_Factory;
};

View File

@@ -0,0 +1,39 @@
#pragma once
namespace sp {
namespace details {
template <typename TBase>
using ArrayType = std::vector<std::function<std::unique_ptr<TBase>(void)>>;
template <typename TBase, typename... TMessages>
struct ArrayFiller {};
template <typename TBase, typename... TMessages>
struct ArrayFiller<TBase, std::tuple<TMessages...>> {
static ArrayType<TBase> ArrayCreate() {
ArrayType<TBase> array;
array.reserve(sizeof...(TMessages));
ArrayFiller<TBase, TMessages...>::ArrayAppend(array);
return array;
}
};
template <typename TBase, typename TMessage, typename... TMessages>
struct ArrayFiller<TBase, TMessage, TMessages...> {
static void ArrayAppend(details::ArrayType<TBase>& array) {
ArrayFiller<TBase, TMessage>::ArrayAppend(array);
ArrayFiller<TBase, TMessages...>::ArrayAppend(array);
}
};
template <typename TBase, typename TMessage>
struct ArrayFiller<TBase, TMessage> {
static void ArrayAppend(details::ArrayType<TBase>& array) {
array.push_back([]() -> std::unique_ptr<TBase> { return std::make_unique<TMessage>(); });
}
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,34 @@
#pragma once
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::RegisterHandler(MessageIdType a_MessageType, const std::shared_ptr<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].push_back(a_Handler);
}
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::UnregisterHandler(MessageIdType a_MessageType, const std::shared_ptr<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 <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::UnregisterHandler(const std::shared_ptr<MessageHandler>& a_Handler) {
for (auto& pair : m_Handlers) {
if (pair.second.empty())
continue;
MessageIdType type = pair.first;
m_Handlers[type].erase(std::remove(m_Handlers[type].begin(), m_Handlers[type].end(), a_Handler), m_Handlers[type].end());
}
}
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::Dispatch(const MessageBase& a_Message) {
MessageIdType type = a_Message.GetId();
for (auto& handler : m_Handlers[type])
a_Message.Dispatch(*handler);
}

View File

@@ -0,0 +1,45 @@
#pragma once
namespace sp {
namespace details {
// TBase is interface class
// TOptions... are the implementation options
template <typename TBase, typename... TOptions>
struct MessageImplBuilder {
// ParsedOptions class is supposed to be defined in comms::Message class
using InterfaceOptions = typename TBase::ParsedOptions;
// Parse implementation options
using ImplOptions = MessageImplParsedOptions<TOptions...>;
// Provide GetIdImpl() if possible
static const bool HasStaticNumIdImpl = InterfaceOptions::HasMsgIdType && ImplOptions::HasStaticNumIdImpl;
using Base1 = typename MessageImplProcessStaticNumId<TBase, ImplOptions, HasStaticNumIdImpl>::Type;
// Provide DispatchImpl() if possible
static const bool HasDispatchImpl = InterfaceOptions::HasHandler && ImplOptions::HasDispatchImpl;
using Base2 = typename MessageImplProcessDispatch<Base1, ImplOptions, HasDispatchImpl>::Type;
// Provide access to fields if possible
using Base3 = typename MessageImplProcessFields<Base2, ImplOptions, ImplOptions::HasFieldsImpl>::Type;
// Provide ReadImpl() if possible
static const bool HasReadImpl = InterfaceOptions::HasReadOperations && ImplOptions::HasFieldsImpl;
using Base4 = typename MessageImplProcessReadFields<Base3, HasReadImpl>::Type;
// Provide WriteImpl() if possible
static const bool HasWriteImpl = InterfaceOptions::HasWriteOperations && ImplOptions::HasFieldsImpl;
using Base5 = typename MessageImplProcessWriteFields<Base4, HasWriteImpl>::Type;
// Provide ValidImpl() if possible
static const bool HasValidImpl = InterfaceOptions::HasValid && ImplOptions::HasFieldsImpl;
using Base6 = typename MessageImplProcessValidFields<Base5, HasValidImpl>::Type;
// The last BaseN must be taken as final type.
using Type = Base6;
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,61 @@
#pragma once
namespace sp {
namespace option {
// Provide static numeric ID, to facilitate implementation of GetIdImpl()
template <std::intmax_t TId>
struct StaticNumIdImpl {};
// Facilitate implementation of DispatchImpl()
template <typename TActual>
struct DispatchImpl {};
// Provide fields of the message, facilitate implementation of
// ReadImpl(), WriteImpl(), ValidImpl(), etc...
template <typename TFields>
struct FieldsImpl {};
} // namespace option
namespace details {
template <typename... TOptions>
class MessageImplParsedOptions;
template <>
struct MessageImplParsedOptions<> {
static const bool HasStaticNumIdImpl = false;
static const bool HasDispatchImpl = false;
static const bool HasFieldsImpl = false;
};
template <std::intmax_t TId, typename... TOptions>
struct MessageImplParsedOptions<option::StaticNumIdImpl<TId>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasStaticNumIdImpl = true;
static const std::intmax_t MsgId = TId;
};
template <typename TActual, typename... TOptions>
struct MessageImplParsedOptions<option::DispatchImpl<TActual>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasDispatchImpl = true;
using ActualMessage = TActual;
};
template <typename TFields, typename... TOptions>
struct MessageImplParsedOptions<option::FieldsImpl<TFields>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasFieldsImpl = true;
using Fields = TFields;
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,93 @@
#pragma once
namespace sp {
namespace details {
// id impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessStaticNumId;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessStaticNumId<TBase, ParsedImplOptions, true> {
using Type = MessageImplStaticNumIdBase<TBase, ParsedImplOptions::MsgId>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessStaticNumId<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// dispatch impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessDispatch;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessDispatch<TBase, ParsedImplOptions, true> {
using Type = MessageImplDispatchBase<TBase, typename ParsedImplOptions::ActualMessage>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessDispatch<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// fields impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessFields;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessFields<TBase, ParsedImplOptions, true> {
using Type = MessageImplFieldsBase<TBase, typename ParsedImplOptions::Fields>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessFields<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// read impl
template <typename TBase, bool TImplement>
struct MessageImplProcessReadFields;
template <typename TBase>
struct MessageImplProcessReadFields<TBase, true> {
using Type = MessageImplFieldsReadBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessReadFields<TBase, false> {
using Type = TBase;
};
// write impl
template <typename TBase, bool TImplement>
struct MessageImplProcessWriteFields;
template <typename TBase>
struct MessageImplProcessWriteFields<TBase, true> {
using Type = MessageImplFieldsWriteBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessWriteFields<TBase, false> {
using Type = TBase;
};
// valid impl
template <typename TBase, bool TImplement>
struct MessageImplProcessValidFields;
template <typename TBase>
struct MessageImplProcessValidFields<TBase, true> {
using Type = MessageImplFieldsValidBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessValidFields<TBase, false> {
using Type = TBase;
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,41 @@
#pragma once
#include <sp/protocol/message/MessageInterfaces.h>
namespace sp {
namespace details {
class EmptyBase {};
template <typename... TOptions>
struct MessageInterfaceBuilder {
// Parse the options
using ParsedOptions = MessageInterfaceParsedOptions<TOptions...>;
// Add ID retrieval functionality if ID type was provided
using Base1 = typename MessageInterfaceProcessMsgId<EmptyBase, ParsedOptions, ParsedOptions::HasMsgIdType>::Type;
// Add ReadData() and WriteData(), that use the right endian
using Base2 = typename MessageInterfaceProcessEndian<Base1, ParsedOptions::HasLittleEndian>::Type;
// Add read functionality if Read type was provided
using Base3 = typename MessageInterfaceProcessRead<Base2, ParsedOptions::HasReadOperations>::Type;
// Add write functionality if Write type was provided
using Base4 = typename MessageInterfaceProcessWrite<Base3, ParsedOptions::HasWriteOperations>::Type;
// add dispatch functionality if Handler type was provided
using Base5 = typename MessageInterfaceProcessHandler<Base4, ParsedOptions, ParsedOptions::HasHandler>::Type;
// add valid functionality if Valid tpe was provided
using Base6 = typename MessageInterfaceProcessValid<Base5, ParsedOptions::HasValid>::Type;
// add write id functionality if write id and write was provided
using Base7 = typename MessageInterfaceProcessWriteId<Base6, ParsedOptions::HasWriteId && ParsedOptions::HasWriteOperations>::Type;
// The last Base7 must be taken as final type.
using Type = Base7;
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,105 @@
#pragma once
namespace sp {
namespace details {
// Build message Id
template <typename TBase, typename TParsedOptions, bool THasMsgIdType>
struct MessageInterfaceProcessMsgId;
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessMsgId<TBase, TParsedOptions, true> {
using Type = MessageInterfaceIdTypeBase<TBase, typename TParsedOptions::MsgIdType>;
};
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessMsgId<TBase, TParsedOptions, false> {
using Type = TBase;
};
// Build endianess
template <typename TBase, bool THasLittleEndian>
struct MessageInterfaceProcessEndian;
template <typename TBase>
struct MessageInterfaceProcessEndian<TBase, true> {
using Type = MessageInterfaceLittleEndian<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessEndian<TBase, false> {
using Type = MessageInterfaceBigEndian<TBase>;
};
// Build read
template <typename TBase, bool THasRead>
struct MessageInterfaceProcessRead;
template <typename TBase>
struct MessageInterfaceProcessRead<TBase, true> {
using Type = MessageInterfaceReadBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessRead<TBase, false> {
using Type = TBase;
};
// Build write
template <typename TBase, bool THasWrite>
struct MessageInterfaceProcessWrite;
template <typename TBase>
struct MessageInterfaceProcessWrite<TBase, true> {
using Type = MessageInterfaceWriteBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessWrite<TBase, false> {
using Type = TBase;
};
// Build handler
template <typename TBase, typename TParsedOptions, bool THasHandler>
struct MessageInterfaceProcessHandler;
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessHandler<TBase, TParsedOptions, true> {
using Type = MessageInterfaceHandlerBase<TBase, typename TParsedOptions::HandlerType>;
};
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessHandler<TBase, TParsedOptions, false> {
using Type = TBase;
};
// Build valid
template <typename TBase, bool THasValid>
struct MessageInterfaceProcessValid;
template <typename TBase>
struct MessageInterfaceProcessValid<TBase, true> {
using Type = MessageInterfaceValidityBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessValid<TBase, false> {
using Type = TBase;
};
// Build id writing
template <typename TBase, bool THasValid>
struct MessageInterfaceProcessWriteId;
template <typename TBase>
struct MessageInterfaceProcessWriteId<TBase, true> {
using Type = MessageInterfaceWriteIdBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessWriteId<TBase, false> {
using Type = TBase;
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,7 @@
#pragma once
#include <sp/protocol/message/MessageOptions.h>
#include <sp/protocol/message/MessageInterfacesOptions.h>
#include <sp/protocol/message/MessageInterfacesImpl.h>
#include <sp/protocol/message/MessageInterfaceProcess.h>

View File

@@ -0,0 +1,133 @@
#pragma once
#include <sp/common/ByteSwapping.h>
namespace sp {
namespace details {
// ID retrieval chunk
template <typename TBase, typename TId>
class MessageInterfaceIdTypeBase : public TBase {
public:
using MsgIdType = TId;
MsgIdType GetId() const {
return GetIdImpl();
}
protected:
virtual MsgIdType GetIdImpl() const = 0;
};
// Big endian serialisation chunk
template <typename TBase>
class MessageInterfaceBigEndian : public TBase {
protected:
template <typename T>
void ReadData(T& value, DataBuffer& buffer) {
buffer >> value;
FromNetwork(value);
}
template <typename T>
void WriteData(T value, DataBuffer& buffer) const {
ToNetwork(value);
buffer << value;
}
};
// Little endian serialisation chunk
template <typename TBase>
class MessageInterfaceLittleEndian : public TBase {
protected:
template <typename T>
void ReadData(T& value, DataBuffer& buffer) {
buffer >> value;
TrySwapBytes(value);
FromNetwork(value);
}
template <typename T>
void WriteData(const T& value, DataBuffer& buffer) {
ToNetwork(value);
TrySwapBytes(value);
buffer << value;
}
};
// Read functionality chunk
template <typename TBase>
class MessageInterfaceReadBase : public TBase {
public:
void Read(DataBuffer& buffer) {
return ReadImpl(buffer);
}
protected:
virtual void ReadImpl(DataBuffer& buffer) = 0;
};
// Write functionality chunk
template <typename TBase>
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 <typename TBase, typename THandler>
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 <typename TBase>
class MessageInterfaceValidityBase : public TBase {
public:
bool Valid() const {
return ValidImpl();
}
protected:
virtual bool ValidImpl() const = 0;
};
// Writing id functionality chunk
template <typename TBase>
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;
}
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,63 @@
#pragma once
namespace sp {
namespace details {
template <typename... TOptions>
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;
};
template <typename T, typename... TOptions>
struct MessageInterfaceParsedOptions<option::MsgIdType<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasMsgIdType = true;
using MsgIdType = T;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::LittleEndian, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasLittleEndian = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::ReadOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasReadOperations = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::WriteOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasWriteOperations = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::WriteId, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasWriteId = true;
};
template <typename T, typename... TOptions>
struct MessageInterfaceParsedOptions<option::Handler<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasHandler = true;
using HandlerType = option::Handler<T>;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::ValidCheckInterface, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasValid = true;
};
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,34 @@
#pragma once
#include <sp/common/DataBuffer.h>
namespace sp {
namespace option {
// Define type used to store message ID
template <typename T>
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 {};
// Define handler class
template <typename T>
struct Handler {
using HandlerT = T;
};
} // namespace option
} // namespace sp

View File

@@ -0,0 +1,160 @@
#pragma once
namespace sp {
namespace details {
// ID information chunk
template <typename TBase, std::intmax_t TId>
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<MsgIdType>(TId);
}
};
// Dispatch implementation chunk
template <typename TBase, typename TActual>
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<const TActual&>(*this));
}
};
template <typename TBase, typename TFields>
class MessageImplFieldsBase : public TBase {
public:
using AllFields = typename details::FieldsBuilder<TFields>::Type;
template <typename... Args>
void Construct(Args... args) {
m_Fields = std::make_tuple(args...);
}
AllFields& GetFields() {
return m_Fields;
}
const AllFields& GetFields() const {
return m_Fields;
}
template <std::size_t FIndex>
auto& GetField() {
return std::get<FIndex>(GetFields()).GetValue();
}
template <std::size_t FIndex>
const auto& GetField() const {
return std::get<FIndex>(GetFields()).GetValue();
}
// allow use of enums
template <typename E, E FIndex>
const auto& GetField() const {
return std::get<static_cast<std::size_t>(FIndex)>(this->GetFields()).GetValue();
}
private:
AllFields m_Fields;
};
template <typename TBase>
class MessageImplFieldsReadBase : public TBase {
private:
// normal reading
template <typename TField>
void ReadField(Field<TField, 0>& field, DataBuffer& buffer) {
this->ReadData(field.GetValue(), buffer);
}
// reading field in bitfield
template <typename TFieldType, typename TField, int IAlignment>
void ReadField(Field<TField, IAlignment>& 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 <typename TContainer, typename TFirst, typename... TFields>
void ReadField(Field<BitField<TContainer, TFirst, TFields...>, 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 <typename TBase>
class MessageImplFieldsWriteBase : public TBase {
private:
// normal writing
template <typename TField>
void WriteField(const Field<TField, 0>& field, DataBuffer& buffer) const {
this->WriteData(field.GetValue(), buffer);
}
// writing field in bitfield
template <typename TFieldType, typename TField, int IAlignment>
void WriteField(const Field<TField, IAlignment>& 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 <typename TContainer, typename TFirst, typename... TFields>
void WriteField(const Field<BitField<TContainer, TFirst, TFields...>, 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 <typename TBase>
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

View File

@@ -1,21 +1,74 @@
#include <sp/common/ByteSwapping.h>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include <algorithm>
namespace sp {
bool IsLittleEndian() {
#ifdef SP_BIG_ENDIAN
return false;
#else
return true;
#endif
template <typename T>
void SwapBytes(T& value) {
char* ptr = reinterpret_cast<char*>(&value);
std::reverse(ptr, ptr + sizeof(T));
}
void SwapBytes(std::uint8_t* begin, std::uint8_t* end) {
if (IsLittleEndian()) {
std::reverse(begin, end);
}
bool IsSystemBigEndian() {
static constexpr std::uint16_t test = 10;
static const bool isBigEndian = reinterpret_cast<const std::uint8_t*>(&test)[1] == 10;
return isBigEndian;
}
template <>
void ToNetwork<std::uint16_t>(std::uint16_t& value) {
value = htons(value);
}
template <>
void ToNetwork<std::uint32_t>(std::uint32_t& value) {
value = htonl(value);
}
template <>
void ToNetwork<std::uint64_t>(std::uint64_t& value) {
if (IsSystemBigEndian())
return;
SwapBytes(value);
}
template <>
void FromNetwork<std::uint16_t>(std::uint16_t& value) {
value = ntohs(value);
}
template <>
void FromNetwork<std::uint32_t>(std::uint32_t& value) {
value = ntohl(value);
}
template <>
void FromNetwork<std::uint64_t>(std::uint64_t& value) {
if (IsSystemBigEndian())
return;
SwapBytes(value);
}
template <>
void TrySwapBytes<std::uint16_t>(std::uint16_t& value) {
SwapBytes(value);
}
template <>
void TrySwapBytes<std::uint32_t>(std::uint32_t& value) {
SwapBytes(value);
}
template <>
void TrySwapBytes<std::uint64_t>(std::uint64_t& value) {
SwapBytes(value);
}
} // namespace sp

View File

@@ -8,8 +8,6 @@ namespace sp {
DataBuffer::DataBuffer() : m_ReadOffset(0) {}
DataBuffer::DataBuffer(std::size_t a_InitialSize) : m_Buffer(a_InitialSize), m_ReadOffset(0) {}
DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) {}
DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) {}

View File

@@ -1,7 +1,7 @@
#include <sp/common/VarInt.h>
#include <sp/common/DataBuffer.h>
#include <stdexcept>
#include <sp/common/DataBuffer.h>
namespace sp {
@@ -29,29 +29,24 @@ DataBuffer& operator<<(DataBuffer& out, const VarInt& var) {
}
DataBuffer& operator>>(DataBuffer& in, VarInt& var) {
var.Read([&in](std::uint8_t& data){
in.ReadSome(&data, 1);
});
return in;
}
void VarInt::Read(const ReadFunc& read) {
m_Value = 0;
var.m_Value = 0;
unsigned int position = 0;
std::uint8_t currentByte;
while (true) {
read(currentByte);
m_Value |= static_cast<std::uint64_t>(currentByte & SEGMENT_BITS) << position;
in.ReadSome(&currentByte, 1);
var.m_Value |= static_cast<std::uint64_t>(currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 8 * sizeof(m_Value))
if (position >= 8 * sizeof(var.m_Value))
throw std::runtime_error("VarInt is too big");
}
return in;
}
} // namespace sp

View File

@@ -11,7 +11,7 @@ static DataBuffer Inflate(const std::uint8_t* source, std::size_t size, std::siz
DataBuffer result;
result.Resize(uncompressedSize);
uncompress(static_cast<Bytef*>(result.data()), reinterpret_cast<uLongf*>(&uncompressedSize), static_cast<const Bytef*>(source),
uncompress(static_cast<Bytef*>(result.data()), static_cast<uLongf*>(&uncompressedSize), static_cast<const Bytef*>(source),
static_cast<uLong>(size));
assert(result.GetSize() == uncompressedSize);
@@ -74,12 +74,16 @@ DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength) {
} // namespace zlib
DataBuffer ZlibCompress::EncapsulateImpl(const DataBuffer& a_Data) {
return zlib::Compress(a_Data, m_CompressionThreshold);
namespace io {
DataBuffer MessageEncapsulator<option::ZlibCompress>::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 ZlibCompress::DecapsulateImpl(DataBuffer& a_Data) {
DataBuffer MessageEncapsulator<option::ZlibCompress>::Decapsulate(DataBuffer& a_Data, const option::ZlibCompress& a_Option) {
return zlib::Decompress(a_Data, a_Data.GetSize());
}
} // namespace io
} // namespace sp

View File

@@ -0,0 +1,3 @@
#include <sp/extensions/Encrypt.h>
#include <openssl/ssl.h>

View File

@@ -1,114 +0,0 @@
#include <sp/extensions/tcp/TcpListener.h>
#ifdef _WIN32
// Windows
#include <winsock2.h>
#include <ws2tcpip.h>
#define ioctl ioctlsocket
#define WOULDBLOCK WSAEWOULDBLOCK
#define MSG_DONTWAIT 0
#else
// Linux/Unix
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#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<SocketHandle>(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<sockaddr*>(&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<sockaddr*>(&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<TcpSocket> TcpListener::Accept() {
sockaddr remoteAddress;
int addrlen = sizeof(remoteAddress);
auto newSocket = std::make_unique<TcpSocket>();
newSocket->m_Handle = static_cast<SocketHandle>(
accept(m_Handle, reinterpret_cast<sockaddr*>(&remoteAddress), reinterpret_cast<socklen_t*>(&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

View File

@@ -1,164 +0,0 @@
#include <sp/extensions/tcp/TcpSocket.h>
#ifdef _WIN32
// Windows
#include <winsock2.h>
#include <ws2tcpip.h>
#define ioctl ioctlsocket
#define WOULDBLOCK WSAEWOULDBLOCK
#define MSG_DONTWAIT 0
#else
// Linux/Unix
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#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<SocketHandle>(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<int>(a_Port)).c_str(), &hints, &result) != 0) {
throw SocketError("Failed to get address info");
}
m_Handle = static_cast<SocketHandle>(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<char*>(buffer.data() + totalRecieved), static_cast<int>(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<const char*>(a_Data.data() + sent), static_cast<int>(a_Data.GetSize() - sent), 0);
if (cur <= 0) {
Disconnect();
m_Status = Status::Error;
return;
}
sent += static_cast<std::size_t>(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

31
src/sp/io/File.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include <sp/io/File.h>
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<std::ifstream>(a_FilePath, std::ios::binary);
if (a_OpenMode & FileTag::OpenMode::Out)
m_FileOutput = std::make_unique<std::ofstream>(a_FilePath, std::ios::binary);
}
File::IOInterface(File&& other) :
m_FileOutput(std::move(other.m_FileOutput)), m_FileInput(std::move(other.m_FileInput)) {}
DataBuffer File::Read(std::size_t a_Amount) {
DataBuffer buffer;
buffer.Resize(a_Amount);
assert(m_FileInput != nullptr);
m_FileInput->read(reinterpret_cast<char*>(buffer.data()), a_Amount);
return buffer;
}
void File::Write(const sp::DataBuffer& a_Data) {
assert(m_FileOutput != nullptr);
m_FileOutput->write(reinterpret_cast<const char*>(a_Data.data()), a_Data.GetSize());
m_FileOutput->flush();
}
} // namespace io
} // namespace sp

16
src/sp/io/Memory.cpp Normal file
View File

@@ -0,0 +1,16 @@
#include <sp/io/Memory.h>
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

42
test/test_file.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include <iostream>
#include <examples/PacketExample.h>
#include <sp/io/File.h>
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<sp::io::FileTag, sp::PacketDispatcher, sp::PacketFactory, sp::option::ZlibCompress>;
int main() {
auto handler = std::make_shared<CustomPacketHandler>();
FileStream stream(sp::io::File{"test.txt", sp::io::FileTag::In | sp::io::FileTag::Out}, {});
stream.GetDispatcher().RegisterHandler(PacketId::Disconnect, handler);
stream.GetDispatcher().RegisterHandler(PacketId::KeepAlive, handler);
stream.SendMessage(KeepAlivePacket{96});
stream.SendMessage(KeepAlivePacket{69});
stream.SendMessage(DisconnectPacket{
"This is in the "
"fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile !"});
stream.GetOption<sp::option::ZlibCompress>().m_Enabled = false;
stream.SendMessage(DisconnectPacket{
"This is in the "
"fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile !"});
stream.RecieveMessages();
return 0;
}

40
test/test_io.cpp Normal file
View File

@@ -0,0 +1,40 @@
#include <iostream>
#include <examples/PacketExample.h>
#include <sp/io/Memory.h>
using DataBufferStream = sp::io::Stream<sp::io::MemoryTag, sp::PacketDispatcher, sp::PacketFactory>;
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<CustomPacketHandler>();
DataBufferStream stream;
stream.GetDispatcher().RegisterHandler(PacketId::Disconnect, handler);
// this should not be dispatched
stream.SendMessage(KeepAlivePacket{96});
stream.RecieveMessages();
stream.GetDispatcher().RegisterHandler(PacketId::KeepAlive, handler);
stream.SendMessage(KeepAlivePacket{69});
stream.RecieveMessages();
stream.SendMessage(DisconnectPacket{"A valid reason"});
stream.RecieveMessages();
return 0;
}

View File

@@ -1,104 +0,0 @@
#include <sp/common/GenericHandler.h>
#include <sp/io/MessageStream.h>
#include <sp/io/StdIo.h>
#include <sp/protocol/BitField.h>
#include <sp/protocol/ConcreteMessage.h>
#include <sp/protocol/MessageDispatcher.h>
#include <sp/protocol/MessageFactory.h>
#include <sp/extensions/Compress.h>
#include <cstdint>
#include <fstream>
#include <iostream>
enum class PacketID : std::uint8_t { KeepAlive = 0, MDC = 1 };
class PacketHandler;
using PacketBase = sp::MessageBase<PacketID, PacketHandler>;
template <typename TData, PacketID ID>
using Message = sp::ConcreteMessage<TData, PacketBase, ID>;
struct KeepAlivePacket {
sp::BitField<std::uint16_t, 12> one;
sp::BitField<std::uint16_t, 4> two;
std::shared_ptr<std::string> test;
};
struct MDCPacket {
sp::BitField<std::uint16_t, 12> one;
sp::BitField<PacketID, 4> two;
std::unique_ptr<std::string> test;
};
using KeepAliveMessage = Message<KeepAlivePacket, PacketID::KeepAlive>;
using MDCMessage = Message<MDCPacket, PacketID::MDC>;
using AllMessages = std::tuple<KeepAliveMessage, MDCMessage>;
class PacketHandler : public sp::GenericHandler<AllMessages> {};
class MyHandler : public PacketHandler {
public:
virtual void Handle(const KeepAliveMessage& msg) override {
std::cout << "I recieved a keep alive : " << *msg->one << " : " << *msg->two << " : " << (msg->test ? *msg->test : "nullptr") << "\n";
}
virtual void Handle(const MDCMessage& msg) override {
std::cout << "I recieved a mdc : " << *msg->one << " : " << static_cast<unsigned>(*msg->two) << " : " << *msg->test << "\n";
}
};
using PacketDispatcher = sp::MessageDispatcher<PacketBase>;
using PacketFactory = sp::MessageFactory<PacketBase, AllMessages>;
using PacketStream = sp::MessageStream<PacketFactory>;
int main() {
KeepAliveMessage m{69, 5, std::make_shared<std::string>("I'm a keepalive")};
// dispatch tests
MyHandler h;
PacketDispatcher d;
d.RegisterHandler(PacketID::KeepAlive, &h);
d.RegisterHandler(PacketID::MDC, &h);
d.Dispatch(m);
PacketFactory f;
auto message = f.CreateMessage(PacketID::KeepAlive);
d.Dispatch(*message);
// write tests
auto compress = std::make_shared<sp::ZlibCompress>();
std::ofstream file{"test.bin"};
PacketStream p(std::make_shared<sp::StdOuput>(file));
p.WriteMessage(m);
p.WriteMessage(MDCMessage{42, PacketID::MDC, std::make_unique<std::string>("Coucou")});
file.flush();
std::ifstream file2{"test.bin"};
PacketStream p2(std::make_shared<sp::StdInput>(file2));
auto message2 = p2.ReadMessage();
auto message3 = p2.ReadMessage();
d.Dispatch(*message2);
d.Dispatch(*message3);
// message->Write(file);
// file << std::endl;
// m.Write(file);
// file << std::endl;
// message->Read(file);
return 0;
}

57
test/test_packets.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include <iostream>
#include <examples/PacketExample.h>
#include <memory>
#include <sp/default/DefaultPacketDispatcher.h>
#include <sp/extensions/Extensions.h>
class KeepAliveHandler : public sp::PacketHandler {
void Handle(const KeepAlivePacket& packet) {
std::cout << "KeepAlive handled !\n";
}
void Handle(const DisconnectPacket& packet) {
std::cout << "Disconnect handled !\n";
}
void Handle(const UpgradeTowerPacket& packet) {
std::cout << "UpgradeTower handled !\n";
}
};
int main() {
auto upgradeTower = std::make_unique<UpgradeTowerPacket>(std::make_tuple(666, 9));
sp::PacketMessage* msg = upgradeTower.get();
auto handler = std::make_shared<KeepAliveHandler>();
msg->Dispatch(*handler);
sp::DataBuffer buffer = msg->Write();
std::uint8_t msgId;
buffer >> msgId;
auto upgradeTower2 = std::make_unique<UpgradeTowerPacket>();
upgradeTower2->Read(buffer);
std::cout << "Test : " << (unsigned)upgradeTower2->GetTowerId() << "\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);
dispatcher.Dispatch(*packet);
dispatcher.UnregisterHandler(PacketId::KeepAlive, handler);
dispatcher.UnregisterHandler(handler);
return 0;
}

View File

@@ -1,155 +0,0 @@
// 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 <cstring>
#include <string>
#include <string_view>
#include <typeinfo>
#include <type_traits>
#if __has_include(<cxxabi.h>)
# include <cxxabi.h>
# include <cstdlib>
# include <memory>
#endif
constexpr bool CXXABI
{
#if __has_include(<cxxabi.h>)
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<T>() for extracting a type's name.
(2) A variable template type_name_rt<T> initialized to the type's name.
(also an incomplete class template IdT<T>, 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<T>()
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<T>
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: <string>,<string_view>,<type_traits> for std::conditional
<typeinfo> (RTTI)
for typeid(T).name(), an implementation-defined name.
<cxxabi.h> (on CXXABI platforms only - GCC, Clang, etc.)
for abi::__cxa_demangle(name,...)
to map typeid(T).name to a human readable name for T.
<cstdlib> for std::free, <memory> for std::unique_ptr
E.g.
int i;
std::cout << type_name_rt<decltype(i)> << "\n^^^ tada!";
--- stdout ---
int
^^^ tada!
*/
// IdT<T> 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 <typename T> struct IdT {};
namespace impl
{
// demangle<bool Free=false>( 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<char> (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 <bool Free = false>
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<char, decltype(std::free)*>( dmg, std::free);
else
return dmg;
}
}
// prefix_len (constant): prefix length of demangled typeid(IdT<T>)
// 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<int>).name()))
- std::strlen("int>");
return len;
}
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename T>
inline constexpr bool is_cvref_v = !std::is_same_v<T,remove_cvref_t<T>>;
// type_name_rt<T>() Returns string, frees any malloc from ABI demangle
// type_name_rt<T,false>() Returns string_view, does not free demangle malloc
//
template <typename T, bool Free = true, typename = void>
auto
type_name_rt() noexcept(!CXXABI) -> std::conditional_t<Free, std::string,
std::string_view>
{
if constexpr (!is_cvref_v<T>)
{
if (auto dmg = demangle<Free>(typeid(T).name()))
{
return { &*dmg };
}
}
else // wrap all cvref types for now - maybe only do functions and arrays
{
if (auto dmg = demangle<Free>(typeid(IdT<T>).name()))
{
size_t const p = IdT_prefix_len();
return { &*dmg + p, std::strlen(&*dmg) - p - 1 };
}
}
return "";
}
} // namespace impl
// type_name_str<T>() Returns a std::string copy of the demangled typeid name.
//
template <typename T>
std::string
const type_name_str() { return impl::type_name_rt<T>(); }
// type_name_rt<T> Global constant; "The Demangle that Never Dangles"
//
template <typename T>
inline
std::string_view
const type_name_rt = impl::type_name_rt<T, false>();

View File

@@ -1,11 +1,5 @@
add_rules("mode.debug", "mode.release")
includes("@builtin/check")
add_requires("boost_pfr")
set_warnings("all")
set_languages("c++17")
local modules = {
@@ -15,18 +9,14 @@ local modules = {
Includes = {"include/(sp/extensions/Compress.h)"},
Sources = {"src/sp/extensions/Compress.cpp"}
},
TcpSocket = {
Option = "tcp",
Deps = {},
Includes = {"include/(sp/extensions/Tcp.h)", "include/(sp/extensions/tcp/*.h)"},
Sources = {"src/sp/extensions/Tcp*.cpp"}
Encryption = {
Option = "ssl",
Deps = {"openssl"},
Includes = {"include/(sp/extensions/Encrypt.h)"},
Sources = {"src/sp/extensions/Encrypt.cpp"}
}
}
-- Map modules to options
for name, module in table.orderpairs(modules) do
if module.Option then
@@ -34,10 +24,6 @@ for name, module in table.orderpairs(modules) do
end
end
-- Add modules requirements
for name, module in table.orderpairs(modules) do
if module.Deps then
@@ -45,10 +31,6 @@ for name, module in table.orderpairs(modules) do
end
end
-- Add modules targets
for name, module in table.orderpairs(modules) do
if module.Deps and has_config(module.Option) then
@@ -68,19 +50,14 @@ for name, module in table.orderpairs(modules) do
end
end
target("SimpleProtocol")
add_includedirs("include")
add_files("src/sp/**.cpp")
set_group("Library")
set_kind("$(kind)")
add_packages("boost_pfr", {public = true})
check_bigendian("SP_BIG_ENDIAN")
add_headerfiles("include/(sp/**.h)", "include/(sp/**.inl)")
local includeFolders = {"common", "default", "io", "protocol"}
for _, folder in ipairs(includeFolders) do
add_headerfiles("include/(sp/" .. folder .. "/**.h)")
end
-- adding extensions
for name, module in table.orderpairs(modules) do
@@ -91,14 +68,8 @@ target("SimpleProtocol")
-- we don't want extensions
remove_files("src/sp/extensions/**.cpp")
remove_headerfiles("include/(sp/extension/**.h)")
-- we need this for endian functions
if is_os("windows") then
add_links("ws2_32")
end
set_group("Library")
set_kind("$(kind)")
-- Tests
for _, file in ipairs(os.files("test/**.cpp")) do
@@ -109,8 +80,6 @@ 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")