284 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#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;
 | 
						|
}
 |