From 7d9b7394dde4e5e20b72bc0453c6026f43ed8495 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sat, 8 Jul 2017 15:50:59 +0200 Subject: [PATCH] Network: Added Packet class for serialization --- src/network/CMakeLists.txt | 2 + src/network/packet.cpp | 229 +++++++++++++++++++++++++++++++++++++ src/network/packet.h | 192 +++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+) create mode 100644 src/network/packet.cpp create mode 100644 src/network/packet.h diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index aeabe430e..ac9d028da 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -1,11 +1,13 @@ set(SRCS network.cpp + packet.cpp room.cpp room_member.cpp ) set(HEADERS network.h + packet.h room.h room_member.h ) diff --git a/src/network/packet.cpp b/src/network/packet.cpp new file mode 100644 index 000000000..b3a61d824 --- /dev/null +++ b/src/network/packet.cpp @@ -0,0 +1,229 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include +#include "network/packet.h" + +namespace Network { + +Packet::Packet() : read_pos(0), is_valid(true) {} + +Packet::~Packet() {} + +void Packet::Append(const void* in_data, std::size_t size_in_bytes) { + if (in_data && (size_in_bytes > 0)) { + std::size_t start = data.size(); + data.resize(start + size_in_bytes); + std::memcpy(&data[start], in_data, size_in_bytes); + } +} + +void Packet::Read(void* out_data, std::size_t size_in_bytes) { + if (out_data && CheckSize(size_in_bytes)) { + std::memcpy(out_data, &data[read_pos], size_in_bytes); + read_pos += size_in_bytes; + } +} + +void Packet::Clear() { + data.clear(); + read_pos = 0; + is_valid = true; +} + +const void* Packet::GetData() const { + return !data.empty() ? &data[0] : NULL; +} + +void Packet::IgnoreBytes(u32 length) { + read_pos += length; +} + +std::size_t Packet::GetDataSize() const { + return data.size(); +} + +bool Packet::EndOfPacket() const { + return read_pos >= data.size(); +} + +Packet::operator BoolType() const { + return is_valid ? &Packet::CheckSize : NULL; +} + +Packet& Packet::operator>>(bool& out_data) { + u8 value; + if (*this >> value) { + out_data = (value != 0); + } + return *this; +} + +Packet& Packet::operator>>(s8& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::operator>>(u8& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::operator>>(s16& out_data) { + s16 value; + Read(&value, sizeof(value)); + out_data = ntohs(value); + return *this; +} + +Packet& Packet::operator>>(u16& out_data) { + u16 value; + Read(&value, sizeof(value)); + out_data = ntohs(value); + return *this; +} + +Packet& Packet::operator>>(s32& out_data) { + s32 value; + Read(&value, sizeof(value)); + out_data = ntohl(value); + return *this; +} + +Packet& Packet::operator>>(u32& out_data) { + u32 value; + Read(&value, sizeof(value)); + out_data = ntohl(value); + return *this; +} + +Packet& Packet::operator>>(float& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::operator>>(double& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::operator>>(char* out_data) { + // First extract string length + u32 length = 0; + *this >> length; + + if ((length > 0) && CheckSize(length)) { + // Then extract characters + std::memcpy(out_data, &data[read_pos], length); + out_data[length] = '\0'; + + // Update reading position + read_pos += length; + } + + return *this; +} + +Packet& Packet::operator>>(std::string& out_data) { + // First extract string length + u32 length = 0; + *this >> length; + + out_data.clear(); + if ((length > 0) && CheckSize(length)) { + // Then extract characters + out_data.assign(&data[read_pos], length); + + // Update reading position + read_pos += length; + } + + return *this; +} + +Packet& Packet::operator<<(bool in_data) { + *this << static_cast(in_data); + return *this; +} + +Packet& Packet::operator<<(s8 in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::operator<<(u8 in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::operator<<(s16 in_data) { + s16 toWrite = htons(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::operator<<(u16 in_data) { + u16 toWrite = htons(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::operator<<(s32 in_data) { + s32 toWrite = htonl(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::operator<<(u32 in_data) { + u32 toWrite = htonl(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::operator<<(float in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::operator<<(double in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::operator<<(const char* in_data) { + // First insert string length + u32 length = std::strlen(in_data); + *this << length; + + // Then insert characters + Append(in_data, length * sizeof(char)); + + return *this; +} + +Packet& Packet::operator<<(const std::string& in_data) { + // First insert string length + u32 length = static_cast(in_data.size()); + *this << length; + + // Then insert characters + if (length > 0) + Append(in_data.c_str(), length * sizeof(std::string::value_type)); + + return *this; +} + +bool Packet::CheckSize(std::size_t size) { + is_valid = is_valid && (read_pos + size <= data.size()); + + return is_valid; +} + +} // namespace Network diff --git a/src/network/packet.h b/src/network/packet.h new file mode 100644 index 000000000..6d84cfbac --- /dev/null +++ b/src/network/packet.h @@ -0,0 +1,192 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/common_types.h" + +namespace Network { + +/// A class for serialize data for network transfer. It also handles endianess +class Packet { + /// A bool-like type that cannot be converted to integer or pointer types + typedef bool (Packet::*BoolType)(std::size_t); + +public: + Packet(); + ~Packet(); + + /** + * Append data to the end of the packet + * @param data Pointer to the sequence of bytes to append + * @param size_in_bytes Number of bytes to append + */ + void Append(const void* data, std::size_t size_in_bytes); + + /** + * Reads data from the current read position of the packet + * @param out_data Pointer where the data should get written to + * @param size_in_bytes Number of bytes to read + */ + void Read(void* out_data, std::size_t size_in_bytes); + + /** + * Clear the packet + * After calling Clear, the packet is empty. + */ + void Clear(); + + /** + * Ignores bytes while reading + * @param length THe number of bytes to ignore + */ + void IgnoreBytes(u32 length); + + /** + * Get a pointer to the data contained in the packet + * @return Pointer to the data + */ + const void* GetData() const; + + /** + * This function returns the number of bytes pointed to by + * what getData returns. + * @return Data size, in bytes + */ + std::size_t GetDataSize() const; + + /** + * This function is useful to know if there is some data + * left to be read, without actually reading it. + * @return True if all data was read, false otherwise + */ + bool EndOfPacket() const; + /** + * Test the validity of the packet, for reading + * This operator allows to test the packet as a boolean + * variable, to check if a reading operation was successful. + * + * A packet will be in an invalid state if it has no more + * data to read. + * + * This behaviour is the same as standard C++ streams. + * + * Usage example: + * @code + * float x; + * packet >> x; + * if (packet) + * { + * // ok, x was extracted successfully + * } + * + * // -- or -- + * + * float x; + * if (packet >> x) + * { + * // ok, x was extracted successfully + * } + * @endcode + * + * Don't focus on the return type, it's equivalent to bool but + * it disallows unwanted implicit conversions to integer or + * pointer types. + * + * @return True if last data extraction from packet was successful + */ + operator BoolType() const; + + /// Overloads of operator >> to read data from the packet + Packet& operator>>(bool& out_data); + Packet& operator>>(s8& out_data); + Packet& operator>>(u8& out_data); + Packet& operator>>(s16& out_data); + Packet& operator>>(u16& out_data); + Packet& operator>>(s32& out_data); + Packet& operator>>(u32& out_data); + Packet& operator>>(float& out_data); + Packet& operator>>(double& out_data); + Packet& operator>>(char* out_data); + Packet& operator>>(std::string& out_data); + template + Packet& operator>>(std::vector& out_data); + template + Packet& operator>>(std::array& out_data); + + /// Overloads of operator << to write data into the packet + Packet& operator<<(bool in_data); + Packet& operator<<(s8 in_data); + Packet& operator<<(u8 in_data); + Packet& operator<<(s16 in_data); + Packet& operator<<(u16 in_data); + Packet& operator<<(s32 in_data); + Packet& operator<<(u32 in_data); + Packet& operator<<(float in_data); + Packet& operator<<(double in_data); + Packet& operator<<(const char* in_data); + Packet& operator<<(const std::string& in_data); + template + Packet& operator<<(const std::vector& in_data); + template + Packet& operator<<(const std::array& data); + +private: + /// Disallow comparisons between packets + bool operator==(const Packet& right) const; + bool operator!=(const Packet& right) const; + + /** + * Check if the packet can extract a given number of bytes + * This function updates accordingly the state of the packet. + * @param size Size to check + * @return True if size bytes can be read from the packet + */ + bool CheckSize(std::size_t size); + + // Member data + std::vector data; ///< Data stored in the packet + std::size_t read_pos; ///< Current reading position in the packet + bool is_valid; ///< Reading state of the packet +}; + +template +Packet& Packet::operator>>(std::vector& out_data) { + for (u32 i = 0; i < out_data.size(); ++i) { + T character = 0; + *this >> character; + out_data[i] = character; + } + return *this; +} + +template +Packet& Packet::operator>>(std::array& out_data) { + for (u32 i = 0; i < out_data.size(); ++i) { + T character = 0; + *this >> character; + out_data[i] = character; + } + return *this; +} + +template +Packet& Packet::operator<<(const std::vector& in_data) { + for (u32 i = 0; i < in_data.size(); ++i) { + *this << in_data[i]; + } + return *this; +} + +template +Packet& Packet::operator<<(const std::array& in_data) { + for (u32 i = 0; i < in_data.size(); ++i) { + *this << in_data[i]; + } + return *this; +} + +} // namespace Network