summaryrefslogtreecommitdiffstats
path: root/quazip/quaziodevice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'quazip/quaziodevice.cpp')
-rw-r--r--quazip/quaziodevice.cpp283
1 files changed, 283 insertions, 0 deletions
diff --git a/quazip/quaziodevice.cpp b/quazip/quaziodevice.cpp
new file mode 100644
index 00000000..959ca0e8
--- /dev/null
+++ b/quazip/quaziodevice.cpp
@@ -0,0 +1,283 @@
+#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 <QFile>
+static QFile debug;
+#endif
+#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT
+#include <QFile>
+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;
+}