summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--logic/GZip.cpp76
-rw-r--r--logic/GZip.h1
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/tst_GZip.cpp55
4 files changed, 119 insertions, 14 deletions
diff --git a/logic/GZip.cpp b/logic/GZip.cpp
index 5207f3f0..d38d06e2 100644
--- a/logic/GZip.cpp
+++ b/logic/GZip.cpp
@@ -2,12 +2,6 @@
#include <zlib.h>
#include <QByteArray>
-// HACK: workaround for terrible macro crap on Windows
-int wrap_inflate (z_streamp strm, int flush)
-{
- return inflate(strm, flush);
-}
-
bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
{
if (compressedBytes.size() == 0)
@@ -17,16 +11,13 @@ bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompresse
}
unsigned uncompLength = compressedBytes.size();
- unsigned half_length = compressedBytes.size() / 2;
uncompressedBytes.clear();
uncompressedBytes.resize(uncompLength);
z_stream strm;
+ memset(&strm, 0, sizeof(strm));
strm.next_in = (Bytef *)compressedBytes.data();
strm.avail_in = compressedBytes.size();
- strm.total_out = 0;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
bool done = false;
@@ -35,20 +26,22 @@ bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompresse
return false;
}
+ int err = Z_OK;
+
while (!done)
{
// If our output buffer is too small
if (strm.total_out >= uncompLength)
{
- uncompressedBytes.resize(uncompLength + half_length);
- uncompLength += half_length;
+ uncompressedBytes.resize(uncompLength * 2);
+ uncompLength *= 2;
}
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
strm.avail_out = uncompLength - strm.total_out;
// Inflate another chunk.
- int err = wrap_inflate(&strm, Z_SYNC_FLUSH);
+ err = inflate(&strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END)
done = true;
else if (err != Z_OK)
@@ -57,7 +50,7 @@ bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompresse
}
}
- if (inflateEnd(&strm) != Z_OK)
+ if (inflateEnd(&strm) != Z_OK || !done)
{
return false;
}
@@ -65,3 +58,58 @@ bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompresse
uncompressedBytes.resize(strm.total_out);
return true;
}
+
+bool GZip::compress(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
+{
+ if (uncompressedBytes.size() == 0)
+ {
+ compressedBytes = uncompressedBytes;
+ return true;
+ }
+
+ unsigned compLength = std::min(uncompressedBytes.size(), 16);
+ compressedBytes.clear();
+ compressedBytes.resize(compLength);
+
+ z_stream zs;
+ memset(&zs, 0, sizeof(zs));
+
+ if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
+ {
+ return false;
+ }
+
+ zs.next_in = (Bytef*)uncompressedBytes.data();
+ zs.avail_in = uncompressedBytes.size();
+
+ int ret;
+ compressedBytes.resize(uncompressedBytes.size());
+
+ unsigned offset = 0;
+ unsigned temp = 0;
+ do
+ {
+ auto remaining = compressedBytes.size() - offset;
+ if(remaining < 1)
+ {
+ compressedBytes.resize(compressedBytes.size() * 2);
+ }
+ zs.next_out = (Bytef *) (compressedBytes.data() + offset);
+ temp = zs.avail_out = compressedBytes.size() - offset;
+ ret = deflate(&zs, Z_FINISH);
+ offset += temp - zs.avail_out;
+ } while (ret == Z_OK);
+
+ compressedBytes.resize(offset);
+
+ if (deflateEnd(&zs) != Z_OK)
+ {
+ return false;
+ }
+
+ if (ret != Z_STREAM_END)
+ {
+ return false;
+ }
+ return true;
+} \ No newline at end of file
diff --git a/logic/GZip.h b/logic/GZip.h
index 30f4f9b5..51be5ca1 100644
--- a/logic/GZip.h
+++ b/logic/GZip.h
@@ -7,5 +7,6 @@ class MULTIMC_LOGIC_EXPORT GZip
{
public:
static bool decompress(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
+ static bool compress(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
};
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d6487a65..31537c75 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -31,6 +31,7 @@ add_unit_test(UpdateChecker tst_UpdateChecker.cpp)
add_unit_test(DownloadTask tst_DownloadTask.cpp)
add_unit_test(filematchers tst_filematchers.cpp)
add_unit_test(Resource tst_Resource.cpp)
+add_unit_test(GZip tst_GZip.cpp)
# Tests END #
diff --git a/tests/tst_GZip.cpp b/tests/tst_GZip.cpp
new file mode 100644
index 00000000..23c749ef
--- /dev/null
+++ b/tests/tst_GZip.cpp
@@ -0,0 +1,55 @@
+#include <QTest>
+#include "TestUtil.h"
+
+#include "GZip.h"
+#include <random>
+
+void fib(int &prev, int &cur)
+{
+ auto ret = prev + cur;
+ prev = cur;
+ cur = ret;
+}
+
+class GZipTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+
+ void test_Through()
+ {
+ // test up to 10 MB
+ static const int size = 10 * 1024 * 1024;
+ QByteArray random;
+ QByteArray compressed;
+ QByteArray decompressed;
+ std::default_random_engine eng((std::random_device())());
+ std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max());
+
+ // initialize random buffer
+ for(int i = 0; i < size; i++)
+ {
+ random.append((char)idis(eng));
+ }
+
+ // initialize fibonacci
+ int prev = 1;
+ int cur = 1;
+
+ // test if fibonacci long random buffers pass through GZip
+ do
+ {
+ QByteArray copy = random;
+ copy.resize(cur);
+ QVERIFY(GZip::compress(copy, compressed));
+ QVERIFY(GZip::decompress(compressed, decompressed));
+ QCOMPARE(decompressed, copy);
+ fib(prev, cur);
+ } while (cur < size);
+ }
+};
+
+QTEST_GUILESS_MAIN(GZipTest)
+
+#include "tst_GZip.moc"