#include #include #include #include "../shared/ZLibStats.h" #include "../shared/ZLibMalloc.h" #include "GZip.h" // http://tools.ietf.org/html/rfc1952 Information on the gzip format. std::string gZipCompress(std::string input, GZipOptions options) { int level; switch (options) { case gzoFastest: level = Z_BEST_SPEED; break; case gzoSmallest: level = Z_BEST_COMPRESSION; break; case gzoNormal: default: level = Z_DEFAULT_COMPRESSION; break; } z_stream_s zlibBuffer; zlibBuffer.zalloc = zlibMalloc; zlibBuffer.zfree = zlibFree; zlibBuffer.opaque = Z_NULL; // This is requesting all of the defaults except for two things. The level, // of course, is an input to this fuction. But the 31 is a actually doing // two things. It is requesting the default value for "window bits". But // it is also requesting a GZIP header, rather than the default header. int zresult = deflateInit2(&zlibBuffer, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); assert((zresult == Z_OK) && "deflateInit2()"); // It seems that deflateBound() only works with the default headers. When // I request the gzip headers (and trailers) instead, this value is too // low. So I add the complete size of the gzip headers and trailers. I // could probably make this 8 less, since, the default headers and trailers // take 8 bytes. But since none of this is documented, I thought it was // better to be safe and request the extra space. const int HEADER_SIZE = 18; const uint64_t sizeHint = deflateBound(&zlibBuffer, input.size()) + HEADER_SIZE + 1; char *data = new char[sizeHint]; zlibBuffer.next_in = (Bytef *)input.data(); zlibBuffer.avail_in = input.size(); zlibBuffer.total_in = 0; zlibBuffer.next_out = (Bytef *)data; zlibBuffer.avail_out = sizeHint; zlibBuffer.total_out = 0; zresult = deflate(&zlibBuffer, Z_FINISH); assert((zresult == Z_STREAM_END) && "deflate"); assert((zlibBuffer.avail_in == 0) && "Not all was consumed!"); // We asked for one more byte than the maximum we should have needed. So we // should have at least one byte free. assert((zlibBuffer.avail_out >= 1) && "Output buffer full!"); const uint64_t bodySize = zlibBuffer.total_out; std::string result(data, bodySize); delete[] data; zresult = deflateEnd(&zlibBuffer); assert((zresult == Z_OK) && "deflateEnd"); ZLibStats::beforeCompression(input.size()); ZLibStats::afterCompression(result.size()); return result; } #ifdef __UNIT_TEST_GZIP__ // g++ -Wall -ggdb -D__UNIT_TEST_GZIP__ -lz GZip.C ../shared/MiscSupport.C ../shared/FixedMalloc.C ../shared/ZLibMalloc.C #include int main(char **, int) { FILE *file = fopen("/tmp/gzip_unit_test.txt.gz", "wb"); if (!file) { perror("fopen()"); return 3; } const std::string compressed = gZipCompress("Hello World!\n", gzoFastest); fwrite(compressed.data(), 1, compressed.size(), file); fclose(file); } #endif