GH-1227 add GZip compress function and a unit test fo GZip
This commit is contained in:
		| @@ -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; | ||||
| } | ||||
| @@ -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); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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 # | ||||
|  | ||||
|   | ||||
							
								
								
									
										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" | ||||
		Reference in New Issue
	
	Block a user