GH-1227 add GZip compress function and a unit test fo GZip
This commit is contained in:
parent
cfd5976471
commit
e38cc1d480
@ -2,12 +2,6 @@
|
|||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <QByteArray>
|
#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)
|
bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
|
||||||
{
|
{
|
||||||
if (compressedBytes.size() == 0)
|
if (compressedBytes.size() == 0)
|
||||||
@ -17,16 +11,13 @@ bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompresse
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned uncompLength = compressedBytes.size();
|
unsigned uncompLength = compressedBytes.size();
|
||||||
unsigned half_length = compressedBytes.size() / 2;
|
|
||||||
uncompressedBytes.clear();
|
uncompressedBytes.clear();
|
||||||
uncompressedBytes.resize(uncompLength);
|
uncompressedBytes.resize(uncompLength);
|
||||||
|
|
||||||
z_stream strm;
|
z_stream strm;
|
||||||
|
memset(&strm, 0, sizeof(strm));
|
||||||
strm.next_in = (Bytef *)compressedBytes.data();
|
strm.next_in = (Bytef *)compressedBytes.data();
|
||||||
strm.avail_in = compressedBytes.size();
|
strm.avail_in = compressedBytes.size();
|
||||||
strm.total_out = 0;
|
|
||||||
strm.zalloc = Z_NULL;
|
|
||||||
strm.zfree = Z_NULL;
|
|
||||||
|
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
|
||||||
@ -35,20 +26,22 @@ bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompresse
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int err = Z_OK;
|
||||||
|
|
||||||
while (!done)
|
while (!done)
|
||||||
{
|
{
|
||||||
// If our output buffer is too small
|
// If our output buffer is too small
|
||||||
if (strm.total_out >= uncompLength)
|
if (strm.total_out >= uncompLength)
|
||||||
{
|
{
|
||||||
uncompressedBytes.resize(uncompLength + half_length);
|
uncompressedBytes.resize(uncompLength * 2);
|
||||||
uncompLength += half_length;
|
uncompLength *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
||||||
strm.avail_out = uncompLength - strm.total_out;
|
strm.avail_out = uncompLength - strm.total_out;
|
||||||
|
|
||||||
// Inflate another chunk.
|
// Inflate another chunk.
|
||||||
int err = wrap_inflate(&strm, Z_SYNC_FLUSH);
|
err = inflate(&strm, Z_SYNC_FLUSH);
|
||||||
if (err == Z_STREAM_END)
|
if (err == Z_STREAM_END)
|
||||||
done = true;
|
done = true;
|
||||||
else if (err != Z_OK)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
@ -65,3 +58,58 @@ bool GZip::decompress(const QByteArray &compressedBytes, QByteArray &uncompresse
|
|||||||
uncompressedBytes.resize(strm.total_out);
|
uncompressedBytes.resize(strm.total_out);
|
||||||
return true;
|
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;
|
||||||
|
}
|
@ -7,5 +7,6 @@ class MULTIMC_LOGIC_EXPORT GZip
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool decompress(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
|
static bool decompress(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
|
||||||
|
static bool compress(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ add_unit_test(UpdateChecker tst_UpdateChecker.cpp)
|
|||||||
add_unit_test(DownloadTask tst_DownloadTask.cpp)
|
add_unit_test(DownloadTask tst_DownloadTask.cpp)
|
||||||
add_unit_test(filematchers tst_filematchers.cpp)
|
add_unit_test(filematchers tst_filematchers.cpp)
|
||||||
add_unit_test(Resource tst_Resource.cpp)
|
add_unit_test(Resource tst_Resource.cpp)
|
||||||
|
add_unit_test(GZip tst_GZip.cpp)
|
||||||
|
|
||||||
# Tests END #
|
# Tests END #
|
||||||
|
|
||||||
|
55
tests/tst_GZip.cpp
Normal file
55
tests/tst_GZip.cpp
Normal file
@ -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"
|
Loading…
Reference in New Issue
Block a user