//////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it freely, // subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; // you must not claim that you wrote the original software. // If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, // and must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include namespace sf { //////////////////////////////////////////////////////////// void Packet::append(const void* data, std::size_t sizeInBytes) { if (data && (sizeInBytes > 0)) { const auto* begin = reinterpret_cast(data); const auto* end = begin + sizeInBytes; m_data.insert(m_data.end(), begin, end); } } //////////////////////////////////////////////////////////// std::size_t Packet::getReadPosition() const { return m_readPos; } //////////////////////////////////////////////////////////// void Packet::clear() { m_data.clear(); m_readPos = 0; m_isValid = true; } //////////////////////////////////////////////////////////// const void* Packet::getData() const { return !m_data.empty() ? m_data.data() : nullptr; } //////////////////////////////////////////////////////////// std::size_t Packet::getDataSize() const { return m_data.size(); } //////////////////////////////////////////////////////////// bool Packet::endOfPacket() const { return m_readPos >= m_data.size(); } //////////////////////////////////////////////////////////// Packet::operator bool() const { return m_isValid; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(bool& data) { std::uint8_t value = 0; if (*this >> value) data = (value != 0); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::int8_t& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::uint8_t& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::int16_t& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); data = static_cast(ntohs(static_cast(data))); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::uint16_t& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); data = ntohs(data); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::int32_t& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); data = static_cast(ntohl(static_cast(data))); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::uint32_t& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); data = ntohl(data); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::int64_t& data) { if (checkSize(sizeof(data))) { // Since ntohll is not available everywhere, we have to convert // to network byte order (big endian) manually std::array bytes{}; std::memcpy(bytes.data(), &m_data[m_readPos], bytes.size()); data = toInteger(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::uint64_t& data) { if (checkSize(sizeof(data))) { // Since ntohll is not available everywhere, we have to convert // to network byte order (big endian) manually std::array bytes{}; std::memcpy(bytes.data(), &m_data[m_readPos], sizeof(data)); data = toInteger(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(float& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(double& data) { if (checkSize(sizeof(data))) { std::memcpy(&data, &m_data[m_readPos], sizeof(data)); m_readPos += sizeof(data); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(char* data) { assert(data && "Packet::operator>> Data must not be null"); // First extract string length std::uint32_t length = 0; *this >> length; if ((length > 0) && checkSize(length)) { // Then extract characters std::memcpy(data, &m_data[m_readPos], length); data[length] = '\0'; // Update reading position m_readPos += length; } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::string& data) { // First extract string length std::uint32_t length = 0; *this >> length; data.clear(); if ((length > 0) && checkSize(length)) { // Then extract characters data.assign(reinterpret_cast(&m_data[m_readPos]), length); // Update reading position m_readPos += length; } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(wchar_t* data) { assert(data && "Packet::operator>> Data must not be null"); // First extract string length std::uint32_t length = 0; *this >> length; if ((length > 0) && checkSize(length * sizeof(std::uint32_t))) { // Then extract characters for (std::uint32_t i = 0; i < length; ++i) { std::uint32_t character = 0; *this >> character; data[i] = static_cast(character); } data[length] = L'\0'; } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(std::wstring& data) { // First extract string length std::uint32_t length = 0; *this >> length; data.clear(); if ((length > 0) && checkSize(length * sizeof(std::uint32_t))) { // Then extract characters for (std::uint32_t i = 0; i < length; ++i) { std::uint32_t character = 0; *this >> character; data += static_cast(character); } } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator>>(String& data) { // First extract the string length std::uint32_t length = 0; *this >> length; data.clear(); if ((length > 0) && checkSize(length * sizeof(std::uint32_t))) { // Then extract characters for (std::uint32_t i = 0; i < length; ++i) { std::uint32_t character = 0; *this >> character; data += static_cast(character); } } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(bool data) { *this << static_cast(data); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::int8_t data) { append(&data, sizeof(data)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::uint8_t data) { append(&data, sizeof(data)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::int16_t data) { const auto toWrite = static_cast(htons(static_cast(data))); append(&toWrite, sizeof(toWrite)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::uint16_t data) { const std::uint16_t toWrite = htons(data); append(&toWrite, sizeof(toWrite)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::int32_t data) { const auto toWrite = static_cast(htonl(static_cast(data))); append(&toWrite, sizeof(toWrite)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::uint32_t data) { const std::uint32_t toWrite = htonl(data); append(&toWrite, sizeof(toWrite)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::int64_t data) { // Since htonll is not available everywhere, we have to convert // to network byte order (big endian) manually const std::array toWrite = {static_cast((data >> 56) & 0xFF), static_cast((data >> 48) & 0xFF), static_cast((data >> 40) & 0xFF), static_cast((data >> 32) & 0xFF), static_cast((data >> 24) & 0xFF), static_cast((data >> 16) & 0xFF), static_cast((data >> 8) & 0xFF), static_cast((data) & 0xFF)}; append(toWrite.data(), toWrite.size()); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(std::uint64_t data) { // Since htonll is not available everywhere, we have to convert // to network byte order (big endian) manually const std::array toWrite = {static_cast((data >> 56) & 0xFF), static_cast((data >> 48) & 0xFF), static_cast((data >> 40) & 0xFF), static_cast((data >> 32) & 0xFF), static_cast((data >> 24) & 0xFF), static_cast((data >> 16) & 0xFF), static_cast((data >> 8) & 0xFF), static_cast((data) & 0xFF)}; append(toWrite.data(), toWrite.size()); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(float data) { append(&data, sizeof(data)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(double data) { append(&data, sizeof(data)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(const char* data) { assert(data && "Packet::operator<< Data must not be null"); // First insert string length const auto length = static_cast(std::strlen(data)); *this << length; // Then insert characters append(data, length * sizeof(char)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(const std::string& data) { // First insert string length const auto length = static_cast(data.size()); *this << length; // Then insert characters if (length > 0) append(data.c_str(), length * sizeof(std::string::value_type)); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(const wchar_t* data) { assert(data && "Packet::operator<< Data must not be null"); // First insert string length const auto length = static_cast(std::wcslen(data)); *this << length; // Then insert characters for (const wchar_t* c = data; *c != L'\0'; ++c) *this << static_cast(*c); return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(const std::wstring& data) { // First insert string length const auto length = static_cast(data.size()); *this << length; // Then insert characters if (length > 0) { for (const wchar_t c : data) *this << static_cast(c); } return *this; } //////////////////////////////////////////////////////////// Packet& Packet::operator<<(const String& data) { // First insert the string length const auto length = static_cast(data.getSize()); *this << length; // Then insert characters if (length > 0) { for (const unsigned int datum : data) *this << datum; } return *this; } //////////////////////////////////////////////////////////// bool Packet::checkSize(std::size_t size) { // Determine if size is big enough to trigger an overflow const bool overflowDetected = m_readPos + size < m_readPos; m_isValid = m_isValid && (m_readPos + size <= m_data.size()) && !overflowDetected; return m_isValid; } //////////////////////////////////////////////////////////// const void* Packet::onSend(std::size_t& size) { size = getDataSize(); return getData(); } //////////////////////////////////////////////////////////// void Packet::onReceive(const void* data, std::size_t size) { append(data, size); } } // namespace sf