:: printable version ::

Compressing and decompressing GZip with Qt

Windows users might be thinking "hey there's a typo! it's Zip and not GZip". If this is the case, or if you are thinking if you could manage to rewrite Qt 4 using a perl oneliner, please close your emacs embedded browser and have a life.
If you are dreaming about being Winona Ryder's puppet and if you don't spend your whole wage on thinkgeek, read on ;-)

I came out with this post after writing an svgz (that's gzip compressed svg files, in case you are wondering) plugin for Qt's icon engine (check it out! ;-).

Qt is statically linked to the zlib library, so I was wondering why QtSvg doesn't support compressed svgs. I have submitted a suggestion to the trolls and they will consider this feature. I am pretty sure we will see it soon as it takes only a few lines of code to decompress a gzip file. And here is a brief example!

The source code you see has been taken from my Qt Zip/Unzip classes so it might look familiar to you.
The only relevant change is the windowBits parameter passed to inflateInit2. Zip files need to be parsed as a "raw" stream, so the parameter has to be negative. To process a gzip stream, we neet to set it to MAX_WBITS + 16.

Here is quick example. You can download a trivial main routine that calls this method: qgz.tar.bz2

bool deflate(const QString& filename) { QFile file(filename);
// Open input file, open output file/buffer, etc.
QDataStream dev(&outputFile); quint64 compressedSize = file.size();
// UNZIP_READ_BUFFER is the same as in unzip.cpp (256*1024)
uInt rep = compressedSize / UNZIP_READ_BUFFER; uInt rem = compressedSize % UNZIP_READ_BUFFER; uInt cur = 0; qint64 read; quint64 tot = 0; char buffer1[UNZIP_READ_BUFFER]; char buffer2[UNZIP_READ_BUFFER];
/* Allocate inflate state */
z_stream zstr; zstr.zalloc = Z_NULL; zstr.zfree = Z_NULL; zstr.opaque = Z_NULL; zstr.next_in = Z_NULL; zstr.avail_in = 0; int zret;
/* windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR. If a gzip stream is being decoded, strm->adler is a crc32 instead of an adler32. */
if ( (zret = inflateInit2_(&zstr, MAX_WBITS + 16, ZLIB_VERSION, sizeof(z_stream))) != Z_OK ) { qDebug("Failed to initialize zlib"); return false; } int szDecomp;
// Decompress until deflate stream ends or end of file
do { read = file.read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem); if (read == 0) break; if (read < 0) { (void)inflateEnd(&zstr); qDebug("Read error"); return false; } cur++; tot += read; zstr.avail_in = (uInt) read; zstr.next_in = (Bytef*) buffer1;
// Run inflate() on input until output buffer not full
do { zstr.avail_out = UNZIP_READ_BUFFER; zstr.next_out = (Bytef*) buffer2;; zret = inflate(&zstr, Z_NO_FLUSH); switch (zret) { case Z_NEED_DICT: case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd(&zstr); qDebug("zlib failed to decode file"); return false; default: ; } szDecomp = UNZIP_READ_BUFFER - zstr.avail_out; if (dev.writeRawData(buffer2, szDecomp) != szDecomp) { inflateEnd(&zstr); qDebug("Write error"); return false; } } while (zstr.avail_out == 0); } while (zret != Z_STREAM_END); inflateEnd(&zstr); return true;
// Success! Now turn off the pc and have a life :)

Quite easy, isn't it? We changed only the window flag from the unzip code and there's no header to be parsed.
Now you might be wondering if we could make it even easier. Well, we could still use zlib's gz* methods, but this way we would no longer be able to use Qt's resource system.
Nevertheless, here is an example:

QByteArray deflateFile(const QString& fileName) { gzFile gzDoc; char buff[4097]; int i; QByteArray fname = fileName.toLatin1(); gzDoc = gzopen(fname.constData(), "rb"); if (gzDoc == NULL) { qWarning("Cannot open file '%s', possibly not a valid gzip file.", qPrintable(fileName)); return QByteArray(); } QByteArray data; while ((i = gzread(gzDoc, &buff, 4096)) > 0) { buff[i] = '\0'; data.append(buff); } gzclose(gzDoc); return data; }

GZip compression should be quite similar. I had no need to implement it so I didn't really care, but I suppose you will only have to play with the windowBits parameter in deflateInit2().

:: index :: top ::