diff --git a/lib/netplay/compression_adapter.h b/lib/netplay/compression_adapter.h new file mode 100644 index 00000000000..c6cc88a0b38 --- /dev/null +++ b/lib/netplay/compression_adapter.h @@ -0,0 +1,140 @@ +/* + This file is part of Warzone 2100. + Copyright (C) 2025 Warzone 2100 Project + + Warzone 2100 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; either version 2 of the License, or + (at your option) any later version. + + Warzone 2100 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 Warzone 2100; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "net_result.h" + +#include +#include +#include + +/// +/// Generic facade for integration of various compression algorithms into WZ's +/// networking code. +/// +/// Provides very basic facilities to initialize compression/decompression streams +/// and perform compression/decompression tasks. +/// +class ICompressionAdapter +{ +public: + + virtual ~ICompressionAdapter() = default; + + /// + /// The first thing that should be called on an `ICompressionAdapter` instance + /// before doing anything else with it. + /// + /// Initializes states of internal buffers, streams, etc. + /// + /// + /// In case of failure, returns an error code describing the error. + /// + virtual net::result initialize() = 0; + + /// + /// Executes the compression routine against `src` buffer of a given size. + /// The result (compressed buffer) can be later accessed via `compressionOutBuffer()` function. + /// + /// This function should be used to incrementally compress incoming data. + /// + /// Note, that it won't necessarily flush all the data to the output buffer, + /// so one would then need to call `flushCompressionStream()` to flush all + /// internal streams to the output buffer. + /// + /// Source buffer containing uncompressed data + /// Size of the source buffer in bytes + /// + /// In case of failure, returns an error code describing the error. + /// + virtual net::result compress(const void* src, size_t size) = 0; + /// + /// Flushes the internal compression stream to the output buffer. + /// The resulting data can be accessed via `compressionOutBuffer()` + /// accessor function. + /// + /// + /// In case of failure, returns an error code describing the error. + /// + virtual net::result flushCompressionStream() = 0; + /// + /// Accessor function (non-const) for compression output buffer (this will be the + /// implicit destination to `compress()` and `flushCompressionStream()` functions. + /// + virtual std::vector& compressionOutBuffer() = 0; + /// + /// Accessor function (const) for compression output buffer (this will be the + /// implicit destination to `compress()` and `flushCompressionStream()` functions. + /// + virtual const std::vector& compressionOutBuffer() const = 0; + + /// + /// Decompress the data from the compressed buffer into `dst` output buffer + /// of a given size. + /// + /// The caller is responsible for ensuring that `dst` is long enough to + /// hold the decompression result of `size` length in bytes. + /// + /// One would also need to call `resetDecompressionStreamInput()` function + /// first to specify the input buffer (containing compressed data) before + /// calling this function. + /// + /// + /// Destination buffer, where the decompressed data will go + /// Size of the destination buffer + /// + /// In case of failure, returns an error code describing the error. + /// + virtual net::result decompress(void* dst, size_t size) = 0; + /// + /// Accessor function (non-const) for decompression input buffer (this will be + /// the implicit source for `decompress()` function). + /// + virtual std::vector& decompressionInBuffer() = 0; + /// + /// Accessor function (const) for decompression input buffer (this will be + /// the implicit source for `decompress()` function). + /// + virtual const std::vector& decompressionInBuffer() const = 0; + /// + /// Remaining free space (in bytes) at the decompression output buffer. + /// + virtual size_t availableSpaceToDecompress() const = 0; + /// + /// Returns `true` if the internal decompression stream doesn't have any more + /// available bytes in the input buffer (all input was consumed by a prior + /// `decompress()` function call). + /// + virtual bool decompressionStreamConsumedAllInput() const = 0; + /// + /// Returns `true` if the decompression algorithm needs to process more input (i.e. + /// there's more data available to process). + /// + virtual bool decompressionNeedInput() const = 0; + /// + /// Update the decompression algorithm on whether it needs more input or not. + /// + virtual void setDecompressionNeedInput(bool needInput) = 0; + /// + /// Updates the available input bytes count at the decompression input stream. + /// + /// New size for the decompression input stream + virtual void resetDecompressionStreamInputSize(size_t size) = 0; +}; diff --git a/lib/netplay/tcp/netsocket.cpp b/lib/netplay/tcp/netsocket.cpp index 9f844793da8..251b0a4fb4f 100644 --- a/lib/netplay/tcp/netsocket.cpp +++ b/lib/netplay/tcp/netsocket.cpp @@ -32,10 +32,7 @@ #include #include -#if !defined(ZLIB_CONST) -# define ZLIB_CONST -#endif -#include +#include "lib/netplay/zlib_compression_adapter.h" #if defined(__clang__) #pragma clang diagnostic ignored "-Wshorten-64-to-32" // FIXME!! @@ -66,27 +63,16 @@ struct Socket * * All non-listening sockets will only use the first socket handle. */ - Socket() : ready(false), deleteLater(false), isCompressed(false), readDisconnected(false), zDeflateInSize(0) - { - memset(&zDeflate, 0, sizeof(zDeflate)); - memset(&zInflate, 0, sizeof(zInflate)); - } - ~Socket(); - SOCKET fd[SOCK_COUNT]; - bool ready; + bool ready = false; optional writeErrorCode = nullopt; - bool deleteLater; + bool deleteLater = false; char textAddress[40] = {}; - bool isCompressed; - bool readDisconnected; ///< True iff a call to recv() returned 0. - z_stream zDeflate; - z_stream zInflate; - unsigned zDeflateInSize; - bool zInflateNeedInput; - std::vector zDeflateOutBuf; - std::vector zInflateInBuf; + bool isCompressed = false; + bool readDisconnected = false; ///< True iff a call to recv() returned 0. + + ZlibCompressionAdapter compressionAdapter; }; struct SocketSet @@ -555,17 +541,19 @@ net::result readNoInt(Socket& sock, void *buf, size_t max_size, size_t if (sock.isCompressed) { - if (sock.zInflateNeedInput) + auto& compressAdapter = sock.compressionAdapter; + if (compressAdapter.decompressionNeedInput()) { // No input data, read some. - sock.zInflateInBuf.resize(max_size + 1000); + auto& decompressInBuf = compressAdapter.decompressionInBuffer(); + decompressInBuf.resize(max_size + 1000); ssize_t received; do { // v----- This weird cast is because recv() takes a char * on windows instead of a void *... - received = recv(sock.fd[SOCK_CONNECTION], (char *)&sock.zInflateInBuf[0], sock.zInflateInBuf.size(), 0); + received = recv(sock.fd[SOCK_CONNECTION], (char *)&decompressInBuf[0], decompressInBuf.size(), 0); } while (received == SOCKET_ERROR && getSockErr() == EINTR); if (received < 0) @@ -573,8 +561,7 @@ net::result readNoInt(Socket& sock, void *buf, size_t max_size, size_t return tl::make_unexpected(make_network_error_code(getSockErr())); } - sock.zInflate.next_in = &sock.zInflateInBuf[0]; - sock.zInflate.avail_in = received; + compressAdapter.resetDecompressionStreamInputSize(received); rawBytes = received; if (received == 0) @@ -583,32 +570,20 @@ net::result readNoInt(Socket& sock, void *buf, size_t max_size, size_t } else { - sock.zInflateNeedInput = false; + compressAdapter.setDecompressionNeedInput(false); } } - sock.zInflate.next_out = (Bytef *)buf; - sock.zInflate.avail_out = max_size; - int ret = inflate(&sock.zInflate, Z_NO_FLUSH); - ASSERT(ret != Z_STREAM_ERROR, "zlib inflate not working!"); - char const *err = nullptr; - switch (ret) - { - case Z_NEED_DICT: err = "Z_NEED_DICT"; break; - case Z_DATA_ERROR: err = "Z_DATA_ERROR"; break; - case Z_MEM_ERROR: err = "Z_MEM_ERROR"; break; - } - if (err != nullptr) + const auto decompressRes = compressAdapter.decompress(buf, max_size); + if (!decompressRes.has_value()) { - debug(LOG_ERROR, "Couldn't decompress data from socket. zlib error %s", err); - // Bad data! - return tl::make_unexpected(make_zlib_error_code(ret)); + return tl::make_unexpected(decompressRes.error()); } - if (sock.zInflate.avail_out != 0) + if (compressAdapter.availableSpaceToDecompress() != 0) { - sock.zInflateNeedInput = true; - ASSERT(sock.zInflate.avail_in == 0, "zlib not consuming all input!"); + compressAdapter.setDecompressionNeedInput(true); + ASSERT(compressAdapter.decompressionStreamConsumedAllInput(), "zlib not consuming all input!"); } if (sock.readDisconnected) @@ -616,7 +591,7 @@ net::result readNoInt(Socket& sock, void *buf, size_t max_size, size_t return tl::make_unexpected(make_network_error_code(ECONNRESET)); } - return max_size - sock.zInflate.avail_out; // Got some data, return how much. + return max_size - compressAdapter.availableSpaceToDecompress(); // Got some data, return how much. } ssize_t received; @@ -679,49 +654,7 @@ net::result writeAll(Socket& sock, const void *buf, size_t size, size_t } else { - #if ZLIB_VERNUM < 0x1252 - // zlib < 1.2.5.2 does not support `#define ZLIB_CONST` - // Unfortunately, some OSes (ex. OpenBSD) ship with zlib < 1.2.5.2 - // Workaround: cast away the const of the input, and disable the resulting -Wcast-qual warning - #if defined(__clang__) - # pragma clang diagnostic push - # pragma clang diagnostic ignored "-Wcast-qual" - #elif defined(__GNUC__) - # pragma GCC diagnostic push - # pragma GCC diagnostic ignored "-Wcast-qual" - #endif - - // cast away the const for earlier zlib versions - sock.zDeflate.next_in = (Bytef *)buf; // -Wcast-qual - - #if defined(__clang__) - # pragma clang diagnostic pop - #elif defined(__GNUC__) - # pragma GCC diagnostic pop - #endif - #else - // zlib >= 1.2.5.2 supports ZLIB_CONST - sock.zDeflate.next_in = (const Bytef *)buf; - #endif - - sock.zDeflate.avail_in = size; - sock.zDeflateInSize += sock.zDeflate.avail_in; - do - { - size_t alreadyHave = sock.zDeflateOutBuf.size(); - sock.zDeflateOutBuf.resize(alreadyHave + size + 20); // A bit more than size should be enough to always do everything in one go. - sock.zDeflate.next_out = (Bytef *)&sock.zDeflateOutBuf[alreadyHave]; - sock.zDeflate.avail_out = sock.zDeflateOutBuf.size() - alreadyHave; - - int ret = deflate(&sock.zDeflate, Z_NO_FLUSH); - ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); - - // Remove unused part of buffer. - sock.zDeflateOutBuf.resize(sock.zDeflateOutBuf.size() - sock.zDeflate.avail_out); - } - while (sock.zDeflate.avail_out == 0); - - ASSERT(sock.zDeflate.avail_in == 0, "zlib didn't compress everything!"); + sock.compressionAdapter.compress(buf, size); } } @@ -741,25 +674,10 @@ void socketFlush(Socket& sock, uint8_t player, size_t *rawByteCount) ASSERT(!sock.writeErrorCode.has_value(), "Socket write error?? (Player: %" PRIu8 "", player); - // Flush data out of zlib compression state. - do - { - sock.zDeflate.next_in = (Bytef *)nullptr; - sock.zDeflate.avail_in = 0; - size_t alreadyHave = sock.zDeflateOutBuf.size(); - sock.zDeflateOutBuf.resize(alreadyHave + 1000); // 100 bytes would probably be enough to flush the rest in one go. - sock.zDeflate.next_out = (Bytef *)&sock.zDeflateOutBuf[alreadyHave]; - sock.zDeflate.avail_out = sock.zDeflateOutBuf.size() - alreadyHave; - - int ret = deflate(&sock.zDeflate, Z_PARTIAL_FLUSH); - ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); + sock.compressionAdapter.flushCompressionStream(); - // Remove unused part of buffer. - sock.zDeflateOutBuf.resize(sock.zDeflateOutBuf.size() - sock.zDeflate.avail_out); - } - while (sock.zDeflate.avail_out == 0); - - if (sock.zDeflateOutBuf.empty()) + auto& compressionBuf = sock.compressionAdapter.compressionOutBuffer(); + if (compressionBuf.empty()) { return; // No data to flush out. } @@ -770,18 +688,12 @@ void socketFlush(Socket& sock, uint8_t player, size_t *rawByteCount) wzSemaphorePost(socketThreadSemaphore); } std::vector &writeQueue = socketThreadWrites[&sock]; - writeQueue.insert(writeQueue.end(), sock.zDeflateOutBuf.begin(), sock.zDeflateOutBuf.end()); + writeQueue.insert(writeQueue.end(), compressionBuf.begin(), compressionBuf.end()); wzMutexUnlock(socketThreadMutex); - // Primitive network logging, uncomment to use. - //printf("Size %3u ->%3zu, buf =", sock->zDeflateInSize, sock->zDeflateOutBuf.size()); - //for (unsigned n = 0; n < std::min(sock->zDeflateOutBuf.size(), 40); ++n) printf(" %02X", sock->zDeflateOutBuf[n]); - //printf("\n"); - // Data sent, don't send again. - rawBytes = sock.zDeflateOutBuf.size(); - sock.zDeflateInSize = 0; - sock.zDeflateOutBuf.clear(); + rawBytes = compressionBuf.size(); + compressionBuf.clear(); } void socketBeginCompression(Socket& sock) @@ -793,23 +705,14 @@ void socketBeginCompression(Socket& sock) wzMutexLock(socketThreadMutex); - // Init deflate. - sock.zDeflate.zalloc = Z_NULL; - sock.zDeflate.zfree = Z_NULL; - sock.zDeflate.opaque = Z_NULL; - int ret = deflateInit(&sock.zDeflate, 6); - ASSERT(ret == Z_OK, "deflateInit failed! Sockets won't work."); - - sock.zInflate.zalloc = Z_NULL; - sock.zInflate.zfree = Z_NULL; - sock.zInflate.opaque = Z_NULL; - sock.zInflate.avail_in = 0; - sock.zInflate.next_in = Z_NULL; - ret = inflateInit(&sock.zInflate); - ASSERT(ret == Z_OK, "deflateInit failed! Sockets won't work."); - - sock.zInflateNeedInput = true; - + const auto initRes = sock.compressionAdapter.initialize(); + if (!initRes.has_value()) + { + const auto errMsg = initRes.error().message(); + debug(LOG_NET, "Failed to initialize compression algorithms. Sockets won't work properly! Detailed error message: %s", errMsg.c_str()); + wzMutexUnlock(socketThreadMutex); + return; + } sock.isCompressed = true; wzMutexUnlock(socketThreadMutex); } @@ -827,15 +730,6 @@ bool socketSetTCPNoDelay(Socket& sock, bool nodelay) #endif } -Socket::~Socket() -{ - if (isCompressed) - { - deflateEnd(&zDeflate); - deflateEnd(&zInflate); - } -} - SocketSet *allocSocketSet() { return new SocketSet; @@ -991,7 +885,7 @@ int checkSockets(const SocketSet& set, unsigned int timeout) { ASSERT(set.fds[i]->fd[SOCK_CONNECTION] != INVALID_SOCKET, "Invalid file descriptor!"); - if (set.fds[i]->isCompressed && !set.fds[i]->zInflateNeedInput) + if (set.fds[i]->isCompressed && !set.fds[i]->compressionAdapter.decompressionNeedInput()) { compressedReady = true; break; @@ -1007,7 +901,7 @@ int checkSockets(const SocketSet& set, unsigned int timeout) int ret = 0; for (size_t i = 0; i < set.fds.size(); ++i) { - set.fds[i]->ready = set.fds[i]->isCompressed && !set.fds[i]->zInflateNeedInput; + set.fds[i]->ready = set.fds[i]->isCompressed && !set.fds[i]->compressionAdapter.decompressionNeedInput(); ++ret; } return ret; diff --git a/lib/netplay/zlib_compression_adapter.cpp b/lib/netplay/zlib_compression_adapter.cpp new file mode 100644 index 00000000000..693ac25dd73 --- /dev/null +++ b/lib/netplay/zlib_compression_adapter.cpp @@ -0,0 +1,192 @@ +/* + This file is part of Warzone 2100. + Copyright (C) 2025 Warzone 2100 Project + + Warzone 2100 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; either version 2 of the License, or + (at your option) any later version. + + Warzone 2100 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 Warzone 2100; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "zlib_compression_adapter.h" +#include "error_categories.h" + +#include "lib/framework/frame.h" // for `ASSERT` + +#include + +ZlibCompressionAdapter::ZlibCompressionAdapter() +{ + std::memset(&deflateStream_, 0, sizeof(deflateStream_)); + std::memset(&inflateStream_, 0, sizeof(inflateStream_)); +} + +ZlibCompressionAdapter::~ZlibCompressionAdapter() +{ + deflateEnd(&deflateStream_); + deflateEnd(&inflateStream_); +} + +net::result ZlibCompressionAdapter::initialize() +{ + // Init deflate stream + deflateStream_.zalloc = Z_NULL; + deflateStream_.zfree = Z_NULL; + deflateStream_.opaque = Z_NULL; + int ret = deflateInit(&deflateStream_, 6); + ASSERT(ret == Z_OK, "deflateInit failed! Sockets won't work."); + if (ret != Z_OK) + { + return tl::make_unexpected(make_zlib_error_code(ret)); + } + + // Init inflate stream + inflateStream_.zalloc = Z_NULL; + inflateStream_.zfree = Z_NULL; + inflateStream_.opaque = Z_NULL; + inflateStream_.avail_in = 0; + inflateStream_.next_in = Z_NULL; + ret = inflateInit(&inflateStream_); + ASSERT(ret == Z_OK, "deflateInit failed! Sockets won't work."); + if (ret != Z_OK) + { + return tl::make_unexpected(make_zlib_error_code(ret)); + } + + inflateNeedInput_ = true; + + return {}; +} + +void ZlibCompressionAdapter::resetCompressionStreamInput(const void* src, size_t size) +{ +#if ZLIB_VERNUM < 0x1252 + // zlib < 1.2.5.2 does not support `#define ZLIB_CONST` + // Unfortunately, some OSes (ex. OpenBSD) ship with zlib < 1.2.5.2 + // Workaround: cast away the const of the input, and disable the resulting -Wcast-qual warning +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-qual" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-qual" +#endif + + // cast away the const for earlier zlib versions + deflateStream_.next_in = (Bytef*)src; // -Wcast-qual + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +#else + // zlib >= 1.2.5.2 supports ZLIB_CONST + deflateStream_.next_in = (const Bytef*)src; +#endif + + deflateStream_.avail_in = size; +} + +net::result ZlibCompressionAdapter::compress(const void* src, size_t size) +{ + resetCompressionStreamInput(src, size); + do + { + const size_t alreadyHave = deflateOutBuf_.size(); + deflateOutBuf_.resize(alreadyHave + size + 20); // A bit more than size should be enough to always do everything in one go. + deflateStream_.next_out = (Bytef*)&deflateOutBuf_[alreadyHave]; + deflateStream_.avail_out = deflateOutBuf_.size() - alreadyHave; + + int ret = deflate(&deflateStream_, Z_NO_FLUSH); + ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); + + // Remove unused part of buffer. + deflateOutBuf_.resize(deflateOutBuf_.size() - deflateStream_.avail_out); + } while (deflateStream_.avail_out == 0); + + ASSERT(deflateStream_.avail_in == 0, "zlib didn't compress everything!"); + + return {}; +} + +net::result ZlibCompressionAdapter::flushCompressionStream() +{ + // Flush data out of zlib compression state. + do + { + deflateStream_.next_in = (Bytef*)nullptr; + deflateStream_.avail_in = 0; + const size_t alreadyHave = deflateOutBuf_.size(); + deflateOutBuf_.resize(alreadyHave + 1000); // 100 bytes would probably be enough to flush the rest in one go. + deflateStream_.next_out = (Bytef*)&deflateOutBuf_[alreadyHave]; + deflateStream_.avail_out = deflateOutBuf_.size() - alreadyHave; + + int ret = deflate(&deflateStream_, Z_PARTIAL_FLUSH); + ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); + + // Remove unused part of buffer. + deflateOutBuf_.resize(deflateOutBuf_.size() - deflateStream_.avail_out); + } while (deflateStream_.avail_out == 0); + + return {}; +} + +net::result ZlibCompressionAdapter::decompress(void* dst, size_t size) +{ + resetDecompressionStreamOutput(dst, size); + + int ret = inflate(&inflateStream_, Z_NO_FLUSH); + ASSERT(ret != Z_STREAM_ERROR, "zlib inflate not working!"); + char const* err = nullptr; + switch (ret) + { + case Z_NEED_DICT: err = "Z_NEED_DICT"; break; + case Z_DATA_ERROR: err = "Z_DATA_ERROR"; break; + case Z_MEM_ERROR: err = "Z_MEM_ERROR"; break; + } + if (ret == Z_STREAM_ERROR) + { + if (err) + { + debug(LOG_ERROR, "Couldn't decompress data from socket. zlib error %s", err); + } + else + { + debug(LOG_ERROR, "Couldn't decompress data from socket. Unknown zlib error"); + } + return tl::make_unexpected(make_zlib_error_code(ret)); + } + return {}; +} + +size_t ZlibCompressionAdapter::availableSpaceToDecompress() const +{ + return inflateStream_.avail_out; +} + +bool ZlibCompressionAdapter::decompressionStreamConsumedAllInput() const +{ + return inflateStream_.avail_in == 0; +} + +void ZlibCompressionAdapter::resetDecompressionStreamInputSize(size_t size) +{ + inflateStream_.next_in = inflateInBuf_.data(); + inflateStream_.avail_in = size; +} + +void ZlibCompressionAdapter::resetDecompressionStreamOutput(void* buf, size_t size) +{ + inflateStream_.next_out = (Bytef*)buf; + inflateStream_.avail_out = size; +} diff --git a/lib/netplay/zlib_compression_adapter.h b/lib/netplay/zlib_compression_adapter.h new file mode 100644 index 00000000000..1c6e28ba02d --- /dev/null +++ b/lib/netplay/zlib_compression_adapter.h @@ -0,0 +1,90 @@ +/* + This file is part of Warzone 2100. + Copyright (C) 2025 Warzone 2100 Project + + Warzone 2100 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; either version 2 of the License, or + (at your option) any later version. + + Warzone 2100 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 Warzone 2100; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "compression_adapter.h" + +#if !defined(ZLIB_CONST) +# define ZLIB_CONST +#endif +#include + +/// +/// Implementation of `ICompressionAdapter` interface, which uses the +/// Zlib library to compress/decompress the data. +/// +class ZlibCompressionAdapter : public ICompressionAdapter +{ +public: + + explicit ZlibCompressionAdapter(); + virtual ~ZlibCompressionAdapter() override; + + virtual net::result initialize() override; + + virtual net::result compress(const void* src, size_t size) override; + virtual net::result flushCompressionStream() override; + + virtual std::vector& compressionOutBuffer() override + { + return deflateOutBuf_; + } + + virtual const std::vector& compressionOutBuffer() const override + { + return deflateOutBuf_; + } + + virtual net::result decompress(void* dst, size_t size) override; + + virtual std::vector& decompressionInBuffer() override + { + return inflateInBuf_; + } + + virtual const std::vector& decompressionInBuffer() const override + { + return inflateInBuf_; + } + + virtual size_t availableSpaceToDecompress() const override; + virtual bool decompressionStreamConsumedAllInput() const override; + virtual bool decompressionNeedInput() const override + { + return inflateNeedInput_; + } + virtual void setDecompressionNeedInput(bool needInput) override + { + inflateNeedInput_ = needInput; + } + + virtual void resetDecompressionStreamInputSize(size_t size) override; + +private: + + void resetCompressionStreamInput(const void* src, size_t size); + void resetDecompressionStreamOutput(void* dst, size_t size); + + std::vector deflateOutBuf_; + std::vector inflateInBuf_; + z_stream deflateStream_; + z_stream inflateStream_; + bool inflateNeedInput_ = false; +};