diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e2d433907e..013ad0caa7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -26,6 +26,8 @@ add_files( pool_type.hpp random_func.cpp random_func.hpp + serialisation.cpp + serialisation.hpp smallmap_type.hpp smallmatrix_type.hpp smallstack_type.hpp diff --git a/src/core/serialisation.cpp b/src/core/serialisation.cpp new file mode 100644 index 0000000000..7fed2cf1a8 --- /dev/null +++ b/src/core/serialisation.cpp @@ -0,0 +1,137 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file serialisation.cpp Implementation related to (de)serialisation of buffers. */ + +#include "../stdafx.h" +#include "serialisation.hpp" + +/** + * Is it safe to write to the packet, i.e. didn't we run over the buffer? + * @param bytes_to_write The amount of bytes we want to try to write. + * @return True iff the given amount of bytes can be written to the packet. + */ +static bool BufferCanWriteToPacket(const std::vector &buffer, size_t limit, size_t bytes_to_write) +{ + return buffer.size() + bytes_to_write <= limit; +} + +/* + * The next couple of functions make sure we can send + * uint8, uint16, uint32 and uint64 endian-safe + * over the network. The least significant bytes are + * sent first. + * + * So 0x01234567 would be sent as 67 45 23 01. + * + * A bool is sent as a uint8 where zero means false + * and non-zero means true. + */ + +/** + * Package a boolean in the packet. + * @param data The data to send. + */ +void BufferSend_bool(std::vector &buffer, size_t limit, bool data) +{ + BufferSend_uint8(buffer, limit, data ? 1 : 0); +} + +/** + * Package a 8 bits integer in the packet. + * @param data The data to send. + */ +void BufferSend_uint8(std::vector &buffer, size_t limit, uint8 data) +{ + assert(BufferCanWriteToPacket(buffer, limit, sizeof(data))); + buffer.emplace_back(data); +} + +/** + * Package a 16 bits integer in the packet. + * @param data The data to send. + */ +void BufferSend_uint16(std::vector &buffer, size_t limit, uint16 data) +{ + assert(BufferCanWriteToPacket(buffer, limit, sizeof(data))); + buffer.insert(buffer.end(), { + (uint8)GB(data, 0, 8), + (uint8)GB(data, 8, 8), + }); +} + +/** + * Package a 32 bits integer in the packet. + * @param data The data to send. + */ +void BufferSend_uint32(std::vector &buffer, size_t limit, uint32 data) +{ + assert(BufferCanWriteToPacket(buffer, limit, sizeof(data))); + buffer.insert(buffer.end(), { + (uint8)GB(data, 0, 8), + (uint8)GB(data, 8, 8), + (uint8)GB(data, 16, 8), + (uint8)GB(data, 24, 8), + }); +} + +/** + * Package a 64 bits integer in the packet. + * @param data The data to send. + */ +void BufferSend_uint64(std::vector &buffer, size_t limit, uint64 data) +{ + assert(BufferCanWriteToPacket(buffer, limit, sizeof(data))); + buffer.insert(buffer.end(), { + (uint8)GB(data, 0, 8), + (uint8)GB(data, 8, 8), + (uint8)GB(data, 16, 8), + (uint8)GB(data, 24, 8), + (uint8)GB(data, 32, 8), + (uint8)GB(data, 40, 8), + (uint8)GB(data, 48, 8), + (uint8)GB(data, 56, 8), + }); +} + +/** + * Sends a string over the network. It sends out + * the string + '\0'. No size-byte or something. + * @param data The string to send + */ +void BufferSend_string(std::vector &buffer, size_t limit, const std::string_view data) +{ + assert(BufferCanWriteToPacket(buffer, limit, data.size() + 1)); + buffer.insert(buffer.end(), data.begin(), data.end()); + buffer.emplace_back('\0'); +} + +/** + * Send as many of the bytes as possible in the packet. This can mean + * that it is possible that not all bytes are sent. To cope with this + * the function returns the amount of bytes that were actually sent. + * @param begin The begin of the buffer to send. + * @param end The end of the buffer to send. + * @return The number of bytes that were added to this packet. + */ +size_t BufferSend_bytes(std::vector &buffer, size_t limit, const byte *begin, const byte *end) +{ + size_t amount = std::min(end - begin, limit - buffer.size()); + buffer.insert(buffer.end(), begin, begin + amount); + return amount; +} + +/** + * Sends a binary data over the network. + * @param data The data to send + */ +void BufferSend_binary(std::vector &buffer, size_t limit, const char *data, const size_t size) +{ + assert(data != nullptr); + assert(BufferCanWriteToPacket(buffer, limit, size)); + buffer.insert(buffer.end(), data, data + size); +} diff --git a/src/core/serialisation.hpp b/src/core/serialisation.hpp new file mode 100644 index 0000000000..1431824689 --- /dev/null +++ b/src/core/serialisation.hpp @@ -0,0 +1,78 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file serialisation.hpp Functions related to (de)serialisation of buffers */ + +#ifndef SERIALISATION_HPP +#define SERIALISATION_HPP + +#include "bitmath_func.hpp" + +#include +#include + +void BufferSend_bool (std::vector &buffer, size_t limit, bool data); +void BufferSend_uint8 (std::vector &buffer, size_t limit, uint8 data); +void BufferSend_uint16(std::vector &buffer, size_t limit, uint16 data); +void BufferSend_uint32(std::vector &buffer, size_t limit, uint32 data); +void BufferSend_uint64(std::vector &buffer, size_t limit, uint64 data); +void BufferSend_string(std::vector &buffer, size_t limit, const std::string_view data); +size_t BufferSend_bytes (std::vector &buffer, size_t limit, const byte *begin, const byte *end); +void BufferSend_binary(std::vector &buffer, size_t limit, const char *data, const size_t size); + +template +struct BufferSerialisationHelper { + void Send_bool(bool data) + { + T *self = static_cast(this); + BufferSend_bool(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), data); + } + + void Send_uint8(uint8 data) + { + T *self = static_cast(this); + BufferSend_uint8(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), data); + } + + void Send_uint16(uint16 data) + { + T *self = static_cast(this); + BufferSend_uint16(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), data); + } + + void Send_uint32(uint32 data) + { + T *self = static_cast(this); + BufferSend_uint32(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), data); + } + + void Send_uint64(uint64 data) + { + T *self = static_cast(this); + BufferSend_uint64(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), data); + } + + void Send_string(const std::string_view data) + { + T *self = static_cast(this); + BufferSend_string(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), data); + } + + size_t Send_bytes(const byte *begin, const byte *end) + { + T *self = static_cast(this); + return BufferSend_bytes(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), begin, end); + } + + void Send_binary(const char *data, const size_t size) + { + T *self = static_cast(this); + BufferSend_binary(self->GetSerialisationBuffer(), self->GetSerialisationLimit(), data, size); + } +}; + +#endif /* SERIALISATION_HPP */ diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index cef68c902e..64ecfee2c4 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -85,121 +85,6 @@ bool Packet::CanWriteToPacket(size_t bytes_to_write) return this->Size() + bytes_to_write <= this->limit; } -/* - * The next couple of functions make sure we can send - * uint8, uint16, uint32 and uint64 endian-safe - * over the network. The least significant bytes are - * sent first. - * - * So 0x01234567 would be sent as 67 45 23 01. - * - * A bool is sent as a uint8 where zero means false - * and non-zero means true. - */ - -/** - * Package a boolean in the packet. - * @param data The data to send. - */ -void Packet::Send_bool(bool data) -{ - this->Send_uint8(data ? 1 : 0); -} - -/** - * Package a 8 bits integer in the packet. - * @param data The data to send. - */ -void Packet::Send_uint8(uint8 data) -{ - assert(this->CanWriteToPacket(sizeof(data))); - this->buffer.emplace_back(data); -} - -/** - * Package a 16 bits integer in the packet. - * @param data The data to send. - */ -void Packet::Send_uint16(uint16 data) -{ - assert(this->CanWriteToPacket(sizeof(data))); - this->buffer.insert(this->buffer.end(), { - (uint8)GB(data, 0, 8), - (uint8)GB(data, 8, 8), - }); -} - -/** - * Package a 32 bits integer in the packet. - * @param data The data to send. - */ -void Packet::Send_uint32(uint32 data) -{ - assert(this->CanWriteToPacket(sizeof(data))); - this->buffer.insert(this->buffer.end(), { - (uint8)GB(data, 0, 8), - (uint8)GB(data, 8, 8), - (uint8)GB(data, 16, 8), - (uint8)GB(data, 24, 8), - }); -} - -/** - * Package a 64 bits integer in the packet. - * @param data The data to send. - */ -void Packet::Send_uint64(uint64 data) -{ - assert(this->CanWriteToPacket(sizeof(data))); - this->buffer.insert(this->buffer.end(), { - (uint8)GB(data, 0, 8), - (uint8)GB(data, 8, 8), - (uint8)GB(data, 16, 8), - (uint8)GB(data, 24, 8), - (uint8)GB(data, 32, 8), - (uint8)GB(data, 40, 8), - (uint8)GB(data, 48, 8), - (uint8)GB(data, 56, 8), - }); -} - -/** - * Sends a string over the network. It sends out - * the string + '\0'. No size-byte or something. - * @param data The string to send - */ -void Packet::Send_string(const std::string_view data) -{ - assert(this->CanWriteToPacket(data.size() + 1)); - this->buffer.insert(this->buffer.end(), data.begin(), data.end()); - this->buffer.emplace_back('\0'); -} - -/** - * Send as many of the bytes as possible in the packet. This can mean - * that it is possible that not all bytes are sent. To cope with this - * the function returns the amount of bytes that were actually sent. - * @param begin The begin of the buffer to send. - * @param end The end of the buffer to send. - * @return The number of bytes that were added to this packet. - */ -size_t Packet::Send_bytes(const byte *begin, const byte *end) -{ - size_t amount = std::min(end - begin, this->limit - this->Size()); - this->buffer.insert(this->buffer.end(), begin, begin + amount); - return amount; -} - -/** - * Sends a binary data over the network. - * @param data The data to send - */ -void Packet::Send_binary(const char *data, const size_t size) -{ - assert(data != nullptr); - assert(this->CanWriteToPacket(size)); - this->buffer.insert(this->buffer.end(), data, data + size); -} /* diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 31b42fda20..bae98d65fb 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -16,6 +16,7 @@ #include "config.h" #include "core.h" #include "../../string_type.h" +#include "../../core/serialisation.hpp" #include #include #include @@ -42,7 +43,7 @@ typedef uint8 PacketType; ///< Identifier for the packet * - years that are leap years in the 'days since X' to 'date' calculations: * (year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0)) */ -struct Packet { +struct Packet : public BufferSerialisationHelper { private: /** The current read/write position in the packet */ PacketSize pos; @@ -63,15 +64,10 @@ public: /* Sending/writing of packets */ void PrepareToSend(); + std::vector &GetSerialisationBuffer() { return this->buffer; } + size_t GetSerialisationLimit() const { return this->limit; } + bool CanWriteToPacket(size_t bytes_to_write); - void Send_bool (bool data); - void Send_uint8 (uint8 data); - void Send_uint16(uint16 data); - void Send_uint32(uint32 data); - void Send_uint64(uint64 data); - void Send_string(const std::string_view data); - size_t Send_bytes (const byte *begin, const byte *end); - void Send_binary(const char *data, const size_t size); /* Reading/receiving of packets */ size_t ReadRawPacketSize() const;