16 Commits

Author SHA1 Message Date
75bae99daa remove id from dispatcher
All checks were successful
Linux arm64 / Build (push) Successful in 16s
2025-08-04 10:11:11 +02:00
90ae25bc8e fix byte swapping
All checks were successful
Linux arm64 / Build (push) Successful in 17s
2025-08-04 09:40:48 +02:00
0c82680af0 refactor DataBufferOperators
All checks were successful
Linux arm64 / Build (push) Successful in 17s
2025-08-03 19:46:11 +02:00
695d15588f don't use insert 2025-08-03 18:39:13 +02:00
7eb96163ab fix SerializableMessage
All checks were successful
Linux arm64 / Build (push) Successful in 16s
2025-08-01 12:52:46 +02:00
b8dafa4eb1 refactor DataBuffer
All checks were successful
Linux arm64 / Build (push) Successful in 16s
2025-08-01 12:44:05 +02:00
c5b3281be7 remove unused include 2025-08-01 12:41:22 +02:00
9374332cd2 refactor SerializableMessage
All checks were successful
Linux arm64 / Build (push) Successful in 16s
2025-07-31 17:56:01 +02:00
bce37f59df allow message serialization through DataBuffer
All checks were successful
Linux arm64 / Build (push) Successful in 15s
2025-07-31 15:01:37 +02:00
a1a4176801 add more DataBuffer serialization types
All checks were successful
Linux arm64 / Build (push) Successful in 17s
2025-07-31 14:12:33 +02:00
5e9a0a9bae remove unused files
All checks were successful
Linux arm64 / Build (push) Successful in 17s
2025-07-29 14:52:49 +02:00
01e406cd89 refactor bitfield io 2025-07-29 14:52:22 +02:00
366a40afee optional dispatch definition 2025-07-29 09:27:19 +02:00
45a3c427fb fix concretemessage
All checks were successful
Linux arm64 / Build (push) Successful in 16s
2025-07-18 17:27:58 +02:00
143b2f357c use generic handler
All checks were successful
Linux arm64 / Build (push) Successful in 15s
2025-07-18 16:45:27 +02:00
2149172b41 redundant template parameter
All checks were successful
Linux arm64 / Build (push) Successful in 1m3s
2025-07-18 16:20:17 +02:00
21 changed files with 768 additions and 471 deletions

View File

@@ -4,34 +4,13 @@
namespace sp { namespace sp {
/** bool IsLittleEndian();
* \brief Serialize value to (network byte order) big endian
*/
template <typename T>
void ToNetwork(T& value) {}
template <> void SwapBytes(std::uint8_t* begin, std::uint8_t* end);
void ToNetwork<std::uint16_t>(std::uint16_t& value);
template <> template<typename T>
void ToNetwork<std::uint32_t>(std::uint32_t& value); 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::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);
} // namespace sp } // namespace sp

View File

@@ -10,10 +10,13 @@
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <string> #include <list>
#include <sp/common/VarInt.h>
#include <vector>
#include <map> #include <map>
#include <memory>
#include <sp/common/ByteSwapping.h>
#include <sp/common/VarInt.h>
#include <string>
#include <vector>
namespace sp { namespace sp {
@@ -23,16 +26,18 @@ namespace sp {
*/ */
class DataBuffer { class DataBuffer {
private: private:
typedef std::vector<std::uint8_t> Data; using Data = std::vector<std::uint8_t>;
private:
Data m_Buffer; Data m_Buffer;
std::size_t m_ReadOffset; std::size_t m_ReadOffset;
public: public:
typedef Data::iterator iterator; using iterator = Data::iterator;
typedef Data::const_iterator const_iterator; using const_iterator = Data::const_iterator;
typedef Data::reference reference; using reference = Data::reference;
typedef Data::const_reference const_reference; using const_reference = Data::const_reference;
typedef Data::difference_type difference_type; using difference_type = Data::difference_type;
DataBuffer(); DataBuffer();
DataBuffer(std::size_t a_InitialSize); DataBuffer(std::size_t a_InitialSize);
@@ -46,143 +51,25 @@ class DataBuffer {
/** /**
* \brief Append data to the buffer * \brief Append data to the buffer
* \warning No endian checks
*/ */
template <typename T> template <typename T>
void Append(const T& data) { void Append(const T& a_Data) {
std::size_t size = sizeof(data); std::size_t size = sizeof(a_Data);
std::size_t end_pos = m_Buffer.size(); std::size_t end_pos = m_Buffer.size();
m_Buffer.resize(m_Buffer.size() + size); m_Buffer.resize(m_Buffer.size() + size);
std::memcpy(&m_Buffer[end_pos], &data, size); std::memcpy(&m_Buffer[end_pos], &a_Data, size);
} }
/** /**
* \brief Append data to the buffer * \brief Read data into a_Data
* \warning No endian checks
*/ */
template <typename T> template <typename T>
DataBuffer& operator<<(const T& data) { void Read(T& a_Data) {
Append(data);
return *this;
}
/**
* \brief Append a string to the buffer
* \warning Don't use it for binary data !
* \param str The string to append
*/
DataBuffer& operator<<(const std::string& str);
/**
* \brief Append data to the buffer from another const buffer
* \param data The buffer to append
*/
DataBuffer& operator<<(const DataBuffer& data);
/**
* \brief Append a vector to the buffer by first writing the size
* \param data The vector to append
*/
template <typename 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& data) {
assert(m_ReadOffset + sizeof(T) <= GetSize()); assert(m_ReadOffset + sizeof(T) <= GetSize());
data = *(reinterpret_cast<T*>(&m_Buffer[m_ReadOffset])); std::memcpy(&a_Data, m_Buffer.data() + m_ReadOffset, sizeof(T));
m_ReadOffset += sizeof(T); m_ReadOffset += sizeof(T);
return *this;
}
/**
* \brief Read some data from the buffer and assign to the new buffer
* \param data The buffer to assign
*/
DataBuffer& operator>>(DataBuffer& data);
/**
* \brief Read a string from the buffer
* \param str The string to assign in the new buffer
* \warning Don't use it for binary data !
*/
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;
} }
/** /**
@@ -236,7 +123,6 @@ class DataBuffer {
m_Buffer.reserve(amount); m_Buffer.reserve(amount);
} }
/** /**
* \brief Clear the buffer * \brief Clear the buffer
*/ */
@@ -325,8 +211,9 @@ class DataBuffer {
}; };
/** /**
* \brief Operator << to write a DataBuffer to an ostream * \brief Append data to the buffer from another const buffer
* \param data The buffer to append
*/ */
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer); DataBuffer& operator<<(DataBuffer& a_Buffer, const DataBuffer& data);
} // namespace sp } // namespace sp

View File

@@ -0,0 +1,455 @@
#pragma once
#include <boost/pfr.hpp>
#include <sp/common/DataBuffer.h>
namespace sp {
template <typename T>
using is_default_serializable = std::bool_constant<(std::is_class_v<T> && std::is_aggregate_v<T>) || !std::is_class_v<T>>;
template <typename T>
static constexpr bool is_default_serializable_v = is_default_serializable<T>::value;
namespace details {
template <typename T>
void WriteRaw(DataBuffer& a_Buffer, T a_Data);
template <typename T>
void WriteFields(DataBuffer& a_Buffer, const T& a_Data);
template <typename T>
void WriteSharedPtr(DataBuffer& a_Buffer, const std::shared_ptr<T>& a_Data);
template <typename T>
void WriteUniquePtr(DataBuffer& a_Buffer, const std::unique_ptr<T>& a_Data);
template <typename T>
void WriteVector(DataBuffer& a_Buffer, const std::vector<T>& a_Data);
template <typename T>
void WriteList(DataBuffer& a_Buffer, const std::list<T>& a_Data);
template <typename K, typename V>
void WriteMap(DataBuffer& a_Buffer, const std::map<K, V>& a_Data);
template <typename K, typename V>
void WriteUnorderedMap(DataBuffer& a_Buffer, const std::unordered_map<K, V>& a_Data);
template <typename K, typename V>
void WritePair(DataBuffer& a_Buffer, const std::pair<K, V>& a_Data);
template <typename T, std::size_t S>
void WriteArray(DataBuffer& a_Buffer, const std::array<T, S>& a_Data);
template <typename T>
void ReadRaw(DataBuffer& a_Buffer, T& a_Data);
template <typename T>
void ReadFields(DataBuffer& a_Buffer, T& a_Data);
template <typename T>
void ReadSharedPtr(DataBuffer& a_Buffer, std::shared_ptr<T>& a_Data);
template <typename T>
void ReadUniquePtr(DataBuffer& a_Buffer, std::unique_ptr<T>& a_Data);
template <typename T>
void ReadVector(DataBuffer& a_Buffer, std::vector<T>& a_Data);
template <typename T>
void ReadList(DataBuffer& a_Buffer, std::list<T>& a_Data);
template <typename K, typename V>
void ReadMap(DataBuffer& a_Buffer, std::map<K, V>& a_Data);
template <typename K, typename V>
void ReadUnorderedMap(DataBuffer& a_Buffer, std::unordered_map<K, V>& a_Data);
template <typename K, typename V>
void ReadPair(DataBuffer& a_Buffer, std::pair<K, V>& a_Data);
template <typename T, std::size_t S>
void ReadArray(DataBuffer& a_Buffer, std::array<T, S>& a_Data);
} // namespace details
/**
* \brief Append data to the buffer (converted to big endian)
*/
template <typename T, typename = typename std::enable_if_t<is_default_serializable_v<T>>>
DataBuffer& operator<<(DataBuffer& a_Buffer, const T& a_Data) {
if constexpr (std::is_class_v<T>) {
details::WriteFields(a_Buffer, a_Data);
} else {
details::WriteRaw(a_Buffer, a_Data);
}
return a_Buffer;
}
/**
* \brief Append a string to the buffer
* \warning Don't use it for binary data !
* \param str The string to append
*/
DataBuffer& operator<<(DataBuffer& a_Buffer, const std::string& str);
/**
* \brief Operator << to write a DataBuffer to an ostream
*/
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer);
/**
* \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& a_Buffer, const std::shared_ptr<T>& a_Data) {
details::WriteSharedPtr(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \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& a_Buffer, const std::unique_ptr<T>& a_Data) {
details::WriteUniquePtr(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Append a vector to the buffer by first writing the size
* \param data The vector to append
*/
template <typename T>
DataBuffer& operator<<(DataBuffer& a_Buffer, const std::vector<T>& a_Data) {
details::WriteVector(a_Buffer, a_Data);
return a_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& a_Buffer, const std::list<T>& a_Data) {
details::WriteList(a_Buffer, a_Data);
return a_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& a_Buffer, const std::map<K, V>& a_Data) {
details::WriteMap(a_Buffer, a_Data);
return a_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& a_Buffer, const std::unordered_map<K, V>& a_Data) {
details::WriteUnorderedMap(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Append a pair to the buffer
* \param data The pair to append
*/
template <typename K, typename V>
DataBuffer& operator<<(DataBuffer& a_Buffer, const std::pair<K, V>& a_Data) {
details::WritePair(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \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& a_Buffer, const std::array<T, Size>& a_Data) {
details::WriteArray(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Read some data from the buffer and assign to desired variable
*/
template <typename T, typename = typename std::enable_if_t<is_default_serializable_v<T>>>
DataBuffer& operator>>(DataBuffer& a_Buffer, T& a_Data) {
if constexpr (std::is_class_v<T>) {
details::ReadFields(a_Buffer, a_Data);
} else {
details::ReadRaw(a_Buffer, a_Data);
}
return a_Buffer;
}
/**
* \brief Read some data from the buffer and assign to the new buffer
* \param data The buffer to assign
*/
DataBuffer& operator>>(DataBuffer& a_Buffer, DataBuffer& a_Data);
/**
* \brief Read a string from the buffer
* \param str The string to assign in the new buffer
* \warning Don't use it for binary data !
*/
DataBuffer& operator>>(DataBuffer& a_Buffer, std::string& str);
/**
* \brief Read a pointer
*/
template <typename T, typename = typename std::enable_if_t<!std::is_abstract_v<T>>>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::shared_ptr<T>& a_Data) {
details::ReadSharedPtr(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Read a pointer
*/
template <typename T, typename = typename std::enable_if_t<!std::is_abstract_v<T>>>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::unique_ptr<T>& a_Data) {
details::ReadUniquePtr(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Read a vector (size + data) from the buffer
* \pre The vector is assumed to be empty
*/
template <typename T>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::vector<T>& a_Data) {
details::ReadVector(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Read a list (size + data) from the buffer
* \pre The list is assumed to be empty
*/
template <typename T>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::list<T>& a_Data) {
details::ReadList(a_Buffer, a_Data);
return a_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& a_Buffer, std::map<K, V>& a_Data) {
details::ReadMap(a_Buffer, a_Data);
return a_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& a_Buffer, std::unordered_map<K, V>& a_Data) {
details::ReadUnorderedMap(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Read a pair
*/
template <typename K, typename V>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::pair<K, V>& a_Data) {
details::ReadPair(a_Buffer, a_Data);
return a_Buffer;
}
/**
* \brief Read an array from the buffer
*/
template <std::size_t Size, typename T>
DataBuffer& operator>>(DataBuffer& a_Buffer, std::array<T, Size>& a_Data) {
details::ReadArray(a_Buffer, a_Data);
return a_Buffer;
}
namespace details {
template <typename T>
void WriteRaw(DataBuffer& a_Buffer, T a_Data) {
SwapBytes(a_Data);
a_Buffer.Append(a_Data);
}
template <typename T>
void WriteFields(DataBuffer& a_Buffer, const T& a_Data) {
boost::pfr::for_each_field(a_Data, [&a_Buffer](const auto& a_Field) { a_Buffer << a_Field; });
}
template <typename T>
void WriteSharedPtr(DataBuffer& a_Buffer, const std::shared_ptr<T>& a_Data) {
a_Buffer << *a_Data;
}
template <typename T>
void WriteUniquePtr(DataBuffer& a_Buffer, const std::unique_ptr<T>& a_Data) {
a_Buffer << *a_Data;
}
template <typename T>
void WriteVector(DataBuffer& a_Buffer, const std::vector<T>& a_Data) {
a_Buffer << VarInt{a_Data.size()};
for (const auto& element : a_Data) {
a_Buffer << element;
}
}
template <typename T>
void WriteList(DataBuffer& a_Buffer, const std::list<T>& a_Data) {
a_Buffer << VarInt{a_Data.size()};
for (const auto& element : a_Data) {
a_Buffer << element;
}
}
template <typename K, typename V>
void WriteMap(DataBuffer& a_Buffer, const std::map<K, V>& a_Data) {
a_Buffer << VarInt{a_Data.size()};
for (const auto& [key, value] : a_Data) {
a_Buffer << key << value;
}
}
template <typename K, typename V>
void WriteUnorderedMap(DataBuffer& a_Buffer, const std::unordered_map<K, V>& a_Data) {
a_Buffer << VarInt{a_Data.size()};
for (const auto& [key, value] : a_Data) {
a_Buffer << key << value;
}
}
template <typename K, typename V>
void WritePair(DataBuffer& a_Buffer, const std::pair<K, V>& a_Data) {
a_Buffer << a_Data.first << a_Data.second;
}
template <typename T, std::size_t S>
void WriteArray(DataBuffer& a_Buffer, const std::array<T, S>& a_Data) {
for (const auto& element : a_Data) {
a_Buffer << element;
}
}
template <typename T>
void ReadRaw(DataBuffer& a_Buffer, T& a_Data) {
a_Buffer.Read(a_Data);
SwapBytes(a_Data);
}
template <typename T>
void ReadFields(DataBuffer& a_Buffer, T& a_Data) {
boost::pfr::for_each_field(a_Data, [&a_Buffer](auto& a_Field) { a_Buffer >> a_Field; });
}
template <typename T>
void ReadSharedPtr(DataBuffer& a_Buffer, std::shared_ptr<T>& a_Data) {
a_Data = std::make_shared<T>();
a_Buffer >> *a_Data;
}
template <typename T>
void ReadUniquePtr(DataBuffer& a_Buffer, std::unique_ptr<T>& a_Data) {
a_Data = std::make_unique<T>();
a_Buffer >> *a_Data;
}
template <typename T>
void ReadVector(DataBuffer& a_Buffer, std::vector<T>& a_Data) {
VarInt arraySize;
a_Buffer >> arraySize;
for (std::size_t i = 0; i < arraySize.GetValue(); i++) {
T newElement;
a_Buffer >> newElement;
a_Data.push_back(newElement);
}
}
template <typename T>
void ReadList(DataBuffer& a_Buffer, std::list<T>& a_Data) {
VarInt arraySize;
a_Buffer >> arraySize;
for (std::size_t i = 0; i < arraySize.GetValue(); i++) {
T newElement;
a_Buffer >> newElement;
a_Data.push_back(newElement);
}
}
template <typename K, typename V>
void ReadMap(DataBuffer& a_Buffer, std::map<K, V>& a_Data) {
VarInt mapSize;
a_Buffer >> mapSize;
for (std::size_t i = 0; i < mapSize.GetValue(); i++) {
K newKey;
V newValue;
a_Buffer >> newKey >> newValue;
a_Data.emplace(newKey, newValue);
}
}
template <typename K, typename V>
void ReadUnorderedMap(DataBuffer& a_Buffer, std::unordered_map<K, V>& a_Data) {
VarInt mapSize;
a_Buffer >> mapSize;
for (std::size_t i = 0; i < mapSize.GetValue(); i++) {
K newKey;
V newValue;
a_Buffer >> newKey >> newValue;
a_Data.emplace(newKey, newValue);
}
}
template <typename K, typename V>
void ReadPair(DataBuffer& a_Buffer, std::pair<K, V>& a_Data) {
a_Buffer >> a_Data.first >> a_Data.a_Data;
}
template <typename T, std::size_t S>
void ReadArray(DataBuffer& a_Buffer, std::array<T, S>& a_Data) {
for (std::size_t i = 0; i < S; i++) {
T newElement;
a_Buffer >> newElement;
a_Data[i] = newElement;
}
}
} // namespace details
} // namespace sp

View File

@@ -133,7 +133,7 @@ namespace sp
{ {
public: public:
virtual ~GenericHandler() {} virtual ~GenericHandler() {}
// virtual void Handle(const TCommon&) {} //Nothing to do virtual void Handle() {} //Nothing to do
}; };
} // sp } // sp

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <sp/common/NonCopyable.h>
#include <sp/io/IoInterface.h> #include <sp/io/IoInterface.h>
namespace sp { namespace sp {

90
include/sp/io/BitBuffer.h Normal file
View File

@@ -0,0 +1,90 @@
#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

View File

@@ -1,78 +1,53 @@
#pragma once #pragma once
#include <boost/pfr.hpp> #include <sp/io/BitBuffer.h>
#include <sp/common/ByteSwapping.h> #include <sp/common/DataBufferOperators.h>
#include <sp/common/DataBuffer.h>
#include <sp/protocol/BitField.h>
namespace sp { namespace sp {
namespace details { 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> template <typename T>
void WriteBitField(DataBuffer& a_Buffer, std::uint64_t& a_DataRaw, std::size_t& a_Offset) { void WriteField(DataBuffer& a_Buffer, const T& a_Data, BitBuffer& a_BitBuffer) {
T filled = static_cast<T>(a_DataRaw); a_Buffer << a_Data;
ToNetwork(filled); a_BitBuffer.UpdateWrite(false);
a_Buffer << filled;
a_Offset = 0;
a_DataRaw = 0;
} }
template <typename T, std::size_t BitSize> template <typename T, std::size_t BitSize>
void WriteField(DataBuffer& a_Buffer, const BitField<T, BitSize>& a_Data, std::uint64_t& a_DataRaw, std::size_t& a_Offset) { void ReadField(DataBuffer& a_Buffer, BitField<T, BitSize>& a_Data, BitBuffer& a_BitBuffer) {
T cut = *a_Data & ((1 << a_Data.GetBitSize()) - 1); a_BitBuffer.Read<T, BitSize>(*a_Data);
std::size_t pushCount = sizeof(T) * 8 - a_Offset - a_Data.GetBitSize(); a_BitBuffer.UpdateRead(true);
a_DataRaw |= cut << pushCount;
a_Offset += a_Data.GetBitSize();
if (a_Offset == sizeof(T) * 8) {
WriteBitField<T>(a_Buffer, a_DataRaw, a_Offset);
}
} }
template <typename T> template <typename T>
void WriteField(DataBuffer& a_Buffer, const T& a_Data, std::uint64_t& a_DataRaw, std::size_t& a_Offset) { void ReadField(DataBuffer& a_Buffer, T& a_Data, BitBuffer& a_BitBuffer) {
T swapped = a_Data;
ToNetwork(swapped);
a_Buffer << swapped;
}
template <typename T, std::size_t BitSize>
void ReadField(DataBuffer& a_Buffer, BitField<T, BitSize>& a_Data, std::size_t& a_Offset) {
a_Buffer >> *a_Data;
FromNetwork(*a_Data);
*a_Data >>= sizeof(T) * 8 - a_Offset - a_Data.GetBitSize();
*a_Data &= (1 << a_Data.GetBitSize()) - 1;
if (a_Offset != sizeof(T) * 8) {
a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() - sizeof(T));
a_Offset += a_Data.GetBitSize();
} else {
a_Offset = 0;
}
}
template <typename T>
void ReadField(DataBuffer& a_Buffer, T& a_Data, std::size_t& a_Offset) {
a_Buffer >> a_Data; a_Buffer >> a_Data;
FromNetwork(a_Data); a_BitBuffer.UpdateRead(false);
} }
} // namespace details
template <typename TData> template <typename TData>
DataBuffer WriteMessage(const TData& a_MessageData) { DataBuffer WriteMessage(const TData& a_MessageData) {
DataBuffer buffer; DataBuffer buffer;
std::size_t currentOffset = 0; BitBuffer bitBuffer(buffer);
std::uint64_t dataRaw = 0; boost::pfr::for_each_field(
boost::pfr::for_each_field(a_MessageData, a_MessageData, [&buffer, &bitBuffer](const auto& a_Field) { details::WriteField(buffer, a_Field, bitBuffer); });
[&buffer, &dataRaw, &currentOffset](const auto& a_Field) { WriteField(buffer, a_Field, dataRaw, currentOffset); }); bitBuffer.UpdateWrite(false);
return buffer; return buffer;
} }
template <typename TData> template <typename TData>
void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) { void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) {
std::size_t currentOffset = 0; BitBuffer bitBuffer(a_Buffer);
boost::pfr::for_each_field( boost::pfr::for_each_field(
a_MessageData, [&a_Buffer, &currentOffset](auto& a_Field) { ReadField(a_Buffer, a_Field, currentOffset); }); a_MessageData, [&a_Buffer, &bitBuffer](auto& a_Field) { details::ReadField(a_Buffer, a_Field, bitBuffer); });
bitBuffer.UpdateRead(false);
} }
} // namespace details
} // namespace sp } // namespace sp

View File

@@ -4,6 +4,7 @@
#include <sp/io/IoInterface.h> #include <sp/io/IoInterface.h>
#include <sp/io/MessageEncapsulator.h> #include <sp/io/MessageEncapsulator.h>
#include <vector> #include <vector>
#include <memory>
namespace sp { namespace sp {

View File

@@ -8,7 +8,10 @@ namespace sp {
template <typename TMessageFactory> template <typename TMessageFactory>
DataBuffer MessageStream<TMessageFactory>::ReadAndDecapsulate() { DataBuffer MessageStream<TMessageFactory>::ReadAndDecapsulate() {
VarInt messageLength; VarInt messageLength;
messageLength.Read([this](std::uint8_t& data) { m_Stream->Read(1) >> data; }); messageLength.Read([this](std::uint8_t& data) {
DataBuffer buffer = m_Stream->Read(1);
data = *buffer.data();
});
std::size_t amount = messageLength.GetValue(); std::size_t amount = messageLength.GetValue();
DataBuffer buffer = m_Stream->Read(amount); DataBuffer buffer = m_Stream->Read(amount);

View File

@@ -0,0 +1,56 @@
#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;
public:
std::shared_ptr<MessageBaseType> m_Message;
SerializableMessage() {}
SerializableMessage(std::shared_ptr<MessageBaseType>&& a_Message) : m_Message(a_Message) {}
SerializableMessage(const std::shared_ptr<MessageBaseType>& a_Message) : m_Message(a_Message) {}
MessageBaseType* operator->() {
return m_Message.get();
}
operator bool() {
return m_Message.get();
}
const MessageBaseType* operator->() const {
return m_Message.get();
}
};
template <typename TMessageFactory>
DataBuffer& operator<<(DataBuffer& a_Buffer, const 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, SerializableMessage<TMessageFactory>& a_Message) {
using MsgId = typename TMessageFactory::IdType;
using MsgBase = typename TMessageFactory::MessageBaseType;
static TMessageFactory factory;
VarInt msgId;
a_Buffer >> msgId;
auto msgPtr = std::shared_ptr<MsgBase>(factory.CreateMessage(MsgId(msgId.GetValue())).release());
a_Message = SerializableMessage<TMessageFactory>(msgPtr);
a_Message->Read(a_Buffer);
return a_Buffer;
}
} // namespace sp

View File

@@ -5,30 +5,49 @@
namespace sp { namespace sp {
template <typename TData, typename TMessageID, TMessageID ID, typename THandler> template <typename TData, typename MessageBase, typename MessageBase::MessageIdType ID, bool DefineDispatch = true>
class ConcreteMessage : public MessageBase<TMessageID, THandler> { class ConcreteMessage : public MessageBase {
public: public:
using DataType = TData; using DataType = TData;
using MessageIdType = typename MessageBase::MessageIdType;
using HandlerType = typename MessageBase::HandlerType;
template <typename... T> template <typename... T>
ConcreteMessage(const T&... args) : m_Data{args...} {} ConcreteMessage(T&&... args) : m_Data{std::move(args)...} {}
virtual ~ConcreteMessage() {} virtual ~ConcreteMessage() {}
virtual TMessageID GetId() const override { virtual MessageIdType GetId() const override {
return ID; return ID;
} }
virtual void Dispatch(THandler& handler) const override { virtual void Dispatch(HandlerType& handler) const override {
handler.Handle(static_cast<const DataType&>(m_Data)); if constexpr (DefineDispatch)
handler.Handle(*this);
} }
virtual void Read(DataBuffer& a_Buffer) override { virtual void Read(DataBuffer& a_Buffer) override {
details::ReadMessage(a_Buffer, m_Data); ReadMessage(a_Buffer, m_Data);
} }
virtual DataBuffer Write() const override { virtual DataBuffer Write() const override {
return details::WriteMessage(m_Data); 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: private:

View File

@@ -19,7 +19,6 @@ template <typename MessageBase>
class MessageDispatcher { class MessageDispatcher {
public: public:
using MessageBaseType = MessageBase; using MessageBaseType = MessageBase;
using MessageIdType = typename MessageBase::MessageIdType;
using MessageHandler = typename MessageBase::HandlerType; using MessageHandler = typename MessageBase::HandlerType;
/** /**
@@ -38,23 +37,16 @@ class MessageDispatcher {
* \param type The packet type * \param type The packet type
* \param handler The packet handler * \param handler The packet handler
*/ */
void RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler); void RegisterHandler(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);
/** /**
* \brief Unregister a packet handler * \brief Unregister a packet handler
* \param handler The packet handler * \param handler The packet handler
*/ */
void UnregisterHandler(MessageHandler* a_Handler); void UnregisterHandler(const std::shared_ptr<MessageHandler>& a_Handler);
private: private:
std::map<MessageIdType, std::vector<MessageHandler*>> m_Handlers; std::vector<std::weak_ptr<MessageHandler>> m_Handlers;
}; };
} // namespace sp } // namespace sp

View File

@@ -6,37 +6,22 @@
namespace sp { namespace sp {
template <typename MessageBase> template <typename MessageBase>
void MessageDispatcher<MessageBase>::RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) { void MessageDispatcher<MessageBase>::RegisterHandler(const std::shared_ptr<MessageHandler>& a_Handler) {
assert(a_Handler); assert(a_Handler);
auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler); m_Handlers.push_back(a_Handler);
if (found == m_Handlers[a_MessageType].end())
m_Handlers[a_MessageType].push_back(a_Handler);
} }
template <typename MessageBase> template <typename MessageBase>
void MessageDispatcher<MessageBase>::UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) { void MessageDispatcher<MessageBase>::UnregisterHandler(const std::shared_ptr<MessageHandler>& a_Handler) {
auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler); auto found = std::find(m_Handlers.begin(), m_Handlers.end(), a_Handler);
if (found != m_Handlers[a_MessageType].end()) if (found != m_Handlers.end())
m_Handlers[a_MessageType].erase(found); m_Handlers.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> template <typename MessageBase>
void MessageDispatcher<MessageBase>::Dispatch(const MessageBase& a_Message) { void MessageDispatcher<MessageBase>::Dispatch(const MessageBase& a_Message) {
MessageIdType type = a_Message.GetId(); for (auto& handler : m_Handlers) {
for (auto& handler : m_Handlers[type]) { a_Message.Dispatch(*handler.lock());
a_Message.Dispatch(*handler);
} }
} }

View File

@@ -1,133 +0,0 @@
#pragma once
#include <tuple>
namespace sp
{
// This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/
// TAll is all the message types, that need to be handled, bundled in std::tuple
template <typename TAll>
class MessageHandler;
// 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 MessageHandler<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 MessageHandler<std::tuple<TRest...> >
{
using Base = MessageHandler<std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const typename T1::DataType& msg) {}
virtual void Handle(const typename T2::DataType& msg) {}
virtual void Handle(const typename T3::DataType& msg) {}
virtual void Handle(const typename T4::DataType& msg) {}
virtual void Handle(const typename T5::DataType& msg) {}
virtual void Handle(const typename T6::DataType& msg) {}
virtual void Handle(const typename T7::DataType& msg) {}
virtual void Handle(const typename T8::DataType& msg) {}
virtual void Handle(const typename T9::DataType& msg) {}
virtual void Handle(const typename T10::DataType& msg) {}
virtual void Handle(const typename T11::DataType& msg) {}
virtual void Handle(const typename T12::DataType& msg) {}
virtual void Handle(const typename T13::DataType& msg) {}
virtual void Handle(const typename T14::DataType& msg) {}
virtual void Handle(const typename T15::DataType& msg) {}
virtual void Handle(const typename T16::DataType& msg) {}
virtual void Handle(const typename T17::DataType& msg) {}
virtual void Handle(const typename T18::DataType& msg) {}
virtual void Handle(const typename T19::DataType& msg) {}
virtual void Handle(const typename T20::DataType& msg) {}
};
// 10 by 10
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename... TRest>
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public MessageHandler<std::tuple<TRest...> >
{
using Base = MessageHandler<std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const typename T1::DataType& msg) {}
virtual void Handle(const typename T2::DataType& msg) {}
virtual void Handle(const typename T3::DataType& msg) {}
virtual void Handle(const typename T4::DataType& msg) {}
virtual void Handle(const typename T5::DataType& msg) {}
virtual void Handle(const typename T6::DataType& msg) {}
virtual void Handle(const typename T7::DataType& msg) {}
virtual void Handle(const typename T8::DataType& msg) {}
virtual void Handle(const typename T9::DataType& msg) {}
virtual void Handle(const typename T10::DataType& msg) {}
};
// 5 by 5
template <
typename T1, typename T2, typename T3, typename T4, typename T5,
typename... TRest>
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, TRest...> > : public MessageHandler<std::tuple<TRest...> >
{
using Base = MessageHandler<std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const typename T1::DataType& msg) {}
virtual void Handle(const typename T2::DataType& msg) {}
virtual void Handle(const typename T3::DataType& msg) {}
virtual void Handle(const typename T4::DataType& msg) {}
virtual void Handle(const typename T5::DataType& msg) {}
};
// Deal with rest with 4 types
template <typename T1, typename T2, typename T3, typename T4>
class MessageHandler<std::tuple<T1, T2, T3, T4> >
{
public:
virtual ~MessageHandler() {}
virtual void Handle(const typename T1::DataType& msg) {}
virtual void Handle(const typename T2::DataType& msg) {}
virtual void Handle(const typename T3::DataType& msg) {}
virtual void Handle(const typename T4::DataType& msg) {}
};
// Deal with rest with 3 types
template < typename T1, typename T2, typename T3>
class MessageHandler<std::tuple<T1, T2, T3> >
{
public:
virtual ~MessageHandler() {}
virtual void Handle(const typename T1::DataType& msg) {}
virtual void Handle(const typename T2::DataType& msg) {}
virtual void Handle(const typename T3::DataType& msg) {}
};
// Deal with rest with 2 types
template <typename T1, typename T2>
class MessageHandler<std::tuple<T1, T2> >
{
public:
virtual ~MessageHandler() {}
virtual void Handle(const typename T1::DataType& msg) {}
virtual void Handle(const typename T2::DataType& msg) {}
};
// Deal with rest with 1 type
template <typename T1>
class MessageHandler<std::tuple<T1> >
{
public:
virtual ~MessageHandler() {}
virtual void Handle(const typename T1::DataType& msg) {}
};
// Deal with rest with 0 type
template <>
class MessageHandler<std::tuple<> >
{
public:
virtual ~MessageHandler() {}
};
} // sp

View File

@@ -1,49 +1,21 @@
#include <sp/common/ByteSwapping.h> #include <sp/common/ByteSwapping.h>
#ifdef _WIN32 #include <algorithm>
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <endian.h>
#define htonll htobe64
#define ntohll be64toh
#endif
namespace sp { namespace sp {
template <> bool IsLittleEndian() {
void ToNetwork<std::uint16_t>(std::uint16_t& value) { #ifdef SP_BIG_ENDIAN
value = htons(value); return false;
#else
return true;
#endif
} }
template <> void SwapBytes(std::uint8_t* begin, std::uint8_t* end) {
void ToNetwork<std::uint32_t>(std::uint32_t& value) { if (IsLittleEndian()) {
value = htonl(value); std::reverse(begin, end);
} }
template <>
void ToNetwork<std::uint64_t>(std::uint64_t& value) {
value = htonll(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) {
value = ntohll(value);
} }
} // namespace sp } // namespace sp

View File

@@ -21,33 +21,35 @@ DataBuffer::DataBuffer(const DataBuffer& other, Data::difference_type offset) :
std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer)); std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer));
} }
DataBuffer& DataBuffer::operator<<(const std::string& str) { DataBuffer& operator<<(DataBuffer& a_Buffer, const std::string& str) {
std::size_t strlen = str.length() + 1; // including null character std::size_t strlen = str.length() + 1; // including null character
Resize(GetSize() + strlen); a_Buffer.Resize(a_Buffer.GetSize() + strlen);
std::memcpy(m_Buffer.data() + GetSize() - strlen, str.data(), strlen); std::memcpy(a_Buffer.data() + a_Buffer.GetSize() - strlen, str.data(), strlen);
return *this; return a_Buffer;
} }
DataBuffer& DataBuffer::operator<<(const DataBuffer& data) { DataBuffer& operator<<(DataBuffer& a_Buffer, const DataBuffer& data) {
m_Buffer.insert(m_Buffer.end(), data.begin(), data.end()); std::size_t end = a_Buffer.GetSize();
return *this; a_Buffer.Resize(a_Buffer.GetSize() + data.GetSize());
std::copy(data.begin(), data.end(), a_Buffer.begin() + end);
return a_Buffer;
} }
DataBuffer& DataBuffer::operator>>(std::string& str) { DataBuffer& operator>>(DataBuffer& a_Buffer, std::string& str) {
std::size_t stringSize = strlen(reinterpret_cast<const char*>(m_Buffer.data()) + m_ReadOffset) + 1; // including null character std::size_t stringSize = strlen(reinterpret_cast<const char*>(a_Buffer.data()) + a_Buffer.GetReadOffset()) + 1; // including null character
str.resize(stringSize); str.resize(stringSize);
std::copy(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), std::copy(a_Buffer.begin() + static_cast<DataBuffer::difference_type>(a_Buffer.GetReadOffset()),
m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset + stringSize), str.begin()); a_Buffer.begin() + static_cast<DataBuffer::difference_type>(a_Buffer.GetReadOffset() + stringSize), str.begin());
m_ReadOffset += stringSize; a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + stringSize);
str.resize(stringSize - 1); str.resize(stringSize - 1);
return *this; return a_Buffer;
} }
DataBuffer& DataBuffer::operator>>(DataBuffer& data) { DataBuffer& operator>>(DataBuffer& a_Buffer, DataBuffer& data) {
data.Resize(GetSize() - m_ReadOffset); data.Resize(a_Buffer.GetSize() - a_Buffer.GetReadOffset());
std::copy(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), m_Buffer.end(), data.begin()); std::copy(a_Buffer.begin() + static_cast<DataBuffer::difference_type>(a_Buffer.GetReadOffset()), a_Buffer.end(), data.begin());
m_ReadOffset = m_Buffer.size(); a_Buffer.SetReadOffset(a_Buffer.GetSize());
return *this; return a_Buffer;
} }
void DataBuffer::WriteSome(const char* buffer, std::size_t amount) { void DataBuffer::WriteSome(const char* buffer, std::size_t amount) {

View File

@@ -1,6 +1,7 @@
#include <sp/common/VarInt.h> #include <sp/common/VarInt.h>
#include <sp/common/DataBuffer.h> #include <sp/common/DataBuffer.h>
#include <sp/common/DataBufferOperators.h>
#include <stdexcept> #include <stdexcept>
namespace sp { namespace sp {

View File

View File

View File

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

View File

@@ -1,5 +1,7 @@
add_rules("mode.debug", "mode.release") add_rules("mode.debug", "mode.release")
includes("@builtin/check")
add_requires("boost_pfr") add_requires("boost_pfr")
set_warnings("all") set_warnings("all")
@@ -76,6 +78,7 @@ target("SimpleProtocol")
set_group("Library") set_group("Library")
set_kind("$(kind)") set_kind("$(kind)")
add_packages("boost_pfr", {public = true}) add_packages("boost_pfr", {public = true})
check_bigendian("SP_BIG_ENDIAN")
add_headerfiles("include/(sp/**.h)", "include/(sp/**.inl)") add_headerfiles("include/(sp/**.h)", "include/(sp/**.inl)")