diff --git a/src/base/net/downloadhandler.cpp b/src/base/net/downloadhandler.cpp index 13142775c..96a5068bf 100644 --- a/src/base/net/downloadhandler.cpp +++ b/src/base/net/downloadhandler.cpp @@ -92,8 +92,8 @@ void DownloadHandler::processFinishedDownload() // Success QByteArray replyData = m_reply->readAll(); if (m_reply->rawHeader("Content-Encoding") == "gzip") { - // uncompress gzip reply - Utils::Gzip::uncompress(replyData, replyData); + // decompress gzip reply + replyData = Utils::Gzip::decompress(replyData); } if (m_saveToFile) { diff --git a/src/base/net/geoipmanager.cpp b/src/base/net/geoipmanager.cpp index 19c74455d..efa150de9 100644 --- a/src/base/net/geoipmanager.cpp +++ b/src/base/net/geoipmanager.cpp @@ -416,8 +416,10 @@ void GeoIPManager::downloadFinished(const QString &url, QByteArray data) { Q_UNUSED(url); - if (!Utils::Gzip::uncompress(data, data)) { - Logger::instance()->addMessage(tr("Could not uncompress GeoIP database file."), Log::WARNING); + bool ok = false; + data = Utils::Gzip::decompress(data, &ok); + if (!ok) { + Logger::instance()->addMessage(tr("Could not decompress GeoIP database file."), Log::WARNING); return; } diff --git a/src/base/utils/gzip.cpp b/src/base/utils/gzip.cpp index ce26eda2e..a3ec5d8de 100644 --- a/src/base/utils/gzip.cpp +++ b/src/base/utils/gzip.cpp @@ -94,54 +94,56 @@ QByteArray Utils::Gzip::compress(const QByteArray &data, const int level, bool * return output; } -bool Utils::Gzip::uncompress(QByteArray src, QByteArray &dest) +QByteArray Utils::Gzip::decompress(const QByteArray &data, bool *ok) { - dest.clear(); + if (ok) *ok = false; - if (src.size() <= 4) { - qWarning("uncompress: Input data is truncated"); - return false; - } + if (data.isEmpty()) + return {}; + + const int BUFSIZE = 1024 * 1024; + char tmpBuf[BUFSIZE] = {0}; z_stream strm; - static const int CHUNK_SIZE = 1024; - char out[CHUNK_SIZE]; - - /* allocate inflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; - strm.avail_in = static_cast(src.size()); - strm.next_in = reinterpret_cast(src.data()); + strm.next_in = reinterpret_cast(data.constData()); + strm.avail_in = uInt(data.size()); + strm.next_out = reinterpret_cast(tmpBuf); + strm.avail_out = BUFSIZE; - const int windowBits = 15; - const int ENABLE_ZLIB_GZIP = 32; + // windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing + // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection + int result = inflateInit2(&strm, (15 + 32)); + if (result != Z_OK) + return {}; - int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding - if (ret != Z_OK) - return false; + QByteArray output; + // from lzbench, level 9 average compression ratio is: 31.92%, which decompression ratio is: 1 / 0.3192 = 3.13 + output.reserve(data.size() * 3); - // run inflate() - do { - strm.avail_out = CHUNK_SIZE; - strm.next_out = reinterpret_cast(out); + // run inflate + while (true) { + result = inflate(&strm, Z_NO_FLUSH); - ret = inflate(&strm, Z_NO_FLUSH); - Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered - - switch (ret) { - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&strm); - return false; + if (result == Z_STREAM_END) { + output.append(tmpBuf, (BUFSIZE - strm.avail_out)); + break; } - dest.append(out, CHUNK_SIZE - strm.avail_out); - } - while (!strm.avail_out); + if (result != Z_OK) { + inflateEnd(&strm); + return {}; + } + + output.append(tmpBuf, (BUFSIZE - strm.avail_out)); + strm.next_out = reinterpret_cast(tmpBuf); + strm.avail_out = BUFSIZE; + } - // clean up and return inflateEnd(&strm); - return true; + + if (ok) *ok = true; + return output; } diff --git a/src/base/utils/gzip.h b/src/base/utils/gzip.h index 79b16778c..4954a4023 100644 --- a/src/base/utils/gzip.h +++ b/src/base/utils/gzip.h @@ -37,7 +37,7 @@ namespace Utils namespace Gzip { QByteArray compress(const QByteArray &data, int level = 6, bool *ok = nullptr); - bool uncompress(QByteArray src, QByteArray &dest); + QByteArray decompress(const QByteArray &data, bool *ok = nullptr); } }