#include "quaziodevice.h" #define QUAZIO_INBUFSIZE 4096 #define QUAZIO_OUTBUFSIZE 4096 class QuaZIODevicePrivate { friend class QuaZIODevice; QuaZIODevicePrivate(QIODevice *io); ~QuaZIODevicePrivate(); QIODevice *io; z_stream zins; z_stream zouts; char *inBuf; int inBufPos; int inBufSize; char *outBuf; int outBufPos; int outBufSize; bool zBufError; int doFlush(QString &error); }; QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io): io(io), inBuf(NULL), inBufPos(0), inBufSize(0), outBuf(NULL), outBufPos(0), outBufSize(0), zBufError(false) { zins.zalloc = (alloc_func) NULL; zins.zfree = (free_func) NULL; zins.opaque = NULL; zouts.zalloc = (alloc_func) NULL; zouts.zfree = (free_func) NULL; zouts.opaque = NULL; inBuf = new char[QUAZIO_INBUFSIZE]; outBuf = new char[QUAZIO_OUTBUFSIZE]; #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.setFileName("debug.out"); debug.open(QIODevice::WriteOnly); #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.setFileName("debug.in"); indebug.open(QIODevice::WriteOnly); #endif } QuaZIODevicePrivate::~QuaZIODevicePrivate() { #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.close(); #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.close(); #endif if (inBuf != NULL) delete[] inBuf; if (outBuf != NULL) delete[] outBuf; } int QuaZIODevicePrivate::doFlush(QString &error) { int flushed = 0; while (outBufPos < outBufSize) { int more = io->write(outBuf + outBufPos, outBufSize - outBufPos); if (more == -1) { error = io->errorString(); return -1; } if (more == 0) break; outBufPos += more; flushed += more; } if (outBufPos == outBufSize) { outBufPos = outBufSize = 0; } return flushed; } // #define QUAZIP_ZIODEVICE_DEBUG_OUTPUT // #define QUAZIP_ZIODEVICE_DEBUG_INPUT #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT #include static QFile debug; #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT #include static QFile indebug; #endif QuaZIODevice::QuaZIODevice(QIODevice *io, QObject *parent): QIODevice(parent), d(new QuaZIODevicePrivate(io)) { connect(io, SIGNAL(readyRead()), SIGNAL(readyRead())); } QuaZIODevice::~QuaZIODevice() { if (isOpen()) close(); delete d; } QIODevice *QuaZIODevice::getIoDevice() const { return d->io; } bool QuaZIODevice::open(QIODevice::OpenMode mode) { if ((mode & QIODevice::ReadOnly) != 0) { if (inflateInit(&d->zins) != Z_OK) { setErrorString(d->zins.msg); return false; } } if ((mode & QIODevice::WriteOnly) != 0) { if (deflateInit(&d->zouts, Z_DEFAULT_COMPRESSION) != Z_OK) { setErrorString(d->zouts.msg); return false; } } return QIODevice::open(mode); } void QuaZIODevice::close() { if ((openMode() & QIODevice::ReadOnly) != 0) { if (inflateEnd(&d->zins) != Z_OK) { setErrorString(d->zins.msg); } } if ((openMode() & QIODevice::WriteOnly) != 0) { flush(); if (deflateEnd(&d->zouts) != Z_OK) { setErrorString(d->zouts.msg); } } QIODevice::close(); } qint64 QuaZIODevice::readData(char *data, qint64 maxSize) { int read = 0; while (read < maxSize) { if (d->inBufPos == d->inBufSize) { d->inBufPos = 0; d->inBufSize = d->io->read(d->inBuf, QUAZIO_INBUFSIZE); if (d->inBufSize == -1) { d->inBufSize = 0; setErrorString(d->io->errorString()); return -1; } if (d->inBufSize == 0) break; } while (read < maxSize && d->inBufPos < d->inBufSize) { d->zins.next_in = (Bytef *) (d->inBuf + d->inBufPos); d->zins.avail_in = d->inBufSize - d->inBufPos; d->zins.next_out = (Bytef *) (data + read); d->zins.avail_out = (uInt) (maxSize - read); // hope it's less than 2GB int more = 0; switch (inflate(&d->zins, Z_SYNC_FLUSH)) { case Z_OK: read = (char *) d->zins.next_out - data; d->inBufPos = (char *) d->zins.next_in - d->inBuf; break; case Z_STREAM_END: read = (char *) d->zins.next_out - data; d->inBufPos = (char *) d->zins.next_in - d->inBuf; return read; case Z_BUF_ERROR: // this should never happen, but just in case if (!d->zBufError) { qWarning("Z_BUF_ERROR detected with %d/%d in/out, weird", d->zins.avail_in, d->zins.avail_out); d->zBufError = true; } memmove(d->inBuf, d->inBuf + d->inBufPos, d->inBufSize - d->inBufPos); d->inBufSize -= d->inBufPos; d->inBufPos = 0; more = d->io->read(d->inBuf + d->inBufSize, QUAZIO_INBUFSIZE - d->inBufSize); if (more == -1) { setErrorString(d->io->errorString()); return -1; } if (more == 0) return read; d->inBufSize += more; break; default: setErrorString(QString::fromLocal8Bit(d->zins.msg)); return -1; } } } #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.write(data, read); #endif return read; } qint64 QuaZIODevice::writeData(const char *data, qint64 maxSize) { int written = 0; QString error; if (d->doFlush(error) == -1) { setErrorString(error); return -1; } while (written < maxSize) { // there is some data waiting in the output buffer if (d->outBufPos < d->outBufSize) return written; d->zouts.next_in = (Bytef *) (data + written); d->zouts.avail_in = (uInt) (maxSize - written); // hope it's less than 2GB d->zouts.next_out = (Bytef *) d->outBuf; d->zouts.avail_out = QUAZIO_OUTBUFSIZE; switch (deflate(&d->zouts, Z_NO_FLUSH)) { case Z_OK: written = (char *) d->zouts.next_in - data; d->outBufSize = (char *) d->zouts.next_out - d->outBuf; break; default: setErrorString(QString::fromLocal8Bit(d->zouts.msg)); return -1; } if (d->doFlush(error) == -1) { setErrorString(error); return -1; } } #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.write(data, written); #endif return written; } bool QuaZIODevice::flush() { QString error; if (d->doFlush(error) < 0) { setErrorString(error); return false; } // can't flush buffer, some data is still waiting if (d->outBufPos < d->outBufSize) return true; Bytef c = 0; d->zouts.next_in = &c; // fake input buffer d->zouts.avail_in = 0; // of zero size do { d->zouts.next_out = (Bytef *) d->outBuf; d->zouts.avail_out = QUAZIO_OUTBUFSIZE; switch (deflate(&d->zouts, Z_SYNC_FLUSH)) { case Z_OK: d->outBufSize = (char *) d->zouts.next_out - d->outBuf; if (d->doFlush(error) < 0) { setErrorString(error); return false; } if (d->outBufPos < d->outBufSize) return true; break; case Z_BUF_ERROR: // nothing to write? return true; default: setErrorString(QString::fromLocal8Bit(d->zouts.msg)); return false; } } while (d->zouts.avail_out == 0); return true; } bool QuaZIODevice::isSequential() const { return true; }