1
/****************************************************************************
3
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
5
** Contact: Nokia Corporation (qt-info@nokia.com)
7
** This file is part of a Qt Solutions component.
10
** Licensees holding valid Qt Commercial licenses may use this file in
11
** accordance with the Qt Solutions Commercial License Agreement provided
12
** with the Software or, alternatively, in accordance with the terms
13
** contained in a written agreement between you and Nokia.
15
** GNU Lesser General Public License Usage
16
** Alternatively, this file may be used under the terms of the GNU Lesser
17
** General Public License version 2.1 as published by the Free Software
18
** Foundation and appearing in the file LICENSE.LGPL included in the
19
** packaging of this file. Please review the following information to
20
** ensure the GNU Lesser General Public License version 2.1 requirements
21
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
** In addition, as a special exception, Nokia gives you certain
24
** additional rights. These rights are described in the Nokia Qt LGPL
25
** Exception version 1.1, included in the file LICENSE.NOKIA-LGPL-EXCEPTION
28
** GNU General Public License Usage
29
** Alternatively, this file may be used under the terms of the GNU
30
** General Public License version 3.0 as published by the Free Software
31
** Foundation and appearing in the file LICENSE.GPL-3 included in the
32
** packaging of this file. Please review the following information to
33
** ensure the GNU General Public License version 3.0 requirements will be
34
** met: http://www.gnu.org/copyleft/gpl.html.
36
** Please note Third Party Software included with Qt Solutions may impose
37
** additional restrictions and it is the user's responsibility to ensure
38
** that they have met the licensing requirements of the GPL, LGPL, or Qt
39
** Solutions Commercial license and the relevant license of the Third
40
** Party Software they are using.
42
** If you are unsure which license is appropriate for your use, please
43
** contact Nokia at qt-info@nokia.com.
45
****************************************************************************/
47
#include "qtiocompressor.h"
50
typedef Bytef ZlibByte;
53
class QtIOCompressorPrivate {
54
QtIOCompressor *q_ptr;
55
Q_DECLARE_PUBLIC(QtIOCompressor)
70
QtIOCompressorPrivate(QtIOCompressor *q_ptr, QIODevice *device, int compressionLevel, int bufferSize);
71
~QtIOCompressorPrivate();
72
void flushZlib(int flushMode);
73
bool writeBytes(ZlibByte *buffer, ZlibSize outputSize);
74
void setZlibError(const QString &errorMessage, int zlibErrorCode);
79
const int compressionLevel;
80
const ZlibSize bufferSize;
83
QtIOCompressor::StreamFormat streamFormat;
89
QtIOCompressorPrivate::QtIOCompressorPrivate(QtIOCompressor *q_ptr, QIODevice *device, int compressionLevel, int bufferSize)
92
,compressionLevel(compressionLevel)
93
,bufferSize(bufferSize)
94
,buffer(new ZlibByte[bufferSize])
96
,streamFormat(QtIOCompressor::ZlibFormat)
98
// Use default zlib memory management.
99
zlibStream.zalloc = Z_NULL;
100
zlibStream.zfree = Z_NULL;
101
zlibStream.opaque = Z_NULL;
107
QtIOCompressorPrivate::~QtIOCompressorPrivate()
114
Flushes the zlib stream.
116
void QtIOCompressorPrivate::flushZlib(int flushMode)
119
zlibStream.next_in = nullptr;
120
zlibStream.avail_in = 0;
123
zlibStream.next_out = buffer;
124
zlibStream.avail_out = bufferSize;
125
status = deflate(&zlibStream, flushMode);
126
if (status != Z_OK && status != Z_STREAM_END) {
127
state = QtIOCompressorPrivate::Error;
128
setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when compressing: "), status);
132
ZlibSize outputSize = bufferSize - zlibStream.avail_out;
134
// Try to write data from the buffer to to the underlying device, return on failure.
135
if (!writeBytes(buffer, outputSize))
138
// If the mode is Z_FINISH we must loop until we get Z_STREAM_END,
139
// else we loop as long as zlib is able to fill the output buffer.
140
} while ((flushMode == Z_FINISH && status != Z_STREAM_END) || (flushMode != Z_FINISH && zlibStream.avail_out == 0));
142
if (flushMode == Z_FINISH)
143
Q_ASSERT(status == Z_STREAM_END);
145
Q_ASSERT(status == Z_OK);
150
Writes outputSize bytes from buffer to the underlying device.
152
bool QtIOCompressorPrivate::writeBytes(ZlibByte *buffer, ZlibSize outputSize)
155
ZlibSize totalBytesWritten = 0;
156
// Loop until all bytes are written to the underlying device.
158
const qint64 bytesWritten = device->write(reinterpret_cast<char *>(buffer), outputSize);
159
if (bytesWritten == -1) {
160
q->setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error writing to underlying device: ") + device->errorString());
163
totalBytesWritten += bytesWritten;
164
} while (totalBytesWritten != outputSize);
166
// put up a flag so that the device will be flushed on close.
167
state = BytesWritten;
173
Sets the error string to errorMessage + zlib error string for zlibErrorCode
175
void QtIOCompressorPrivate::setZlibError(const QString &errorMessage, int zlibErrorCode)
178
// Watch out, zlibErrorString may be null.
179
const char * const zlibErrorString = zError(zlibErrorCode);
182
errorString = errorMessage + zlibErrorString;
184
errorString = errorMessage + " Unknown error, code " + QString::number(zlibErrorCode);
186
q->setErrorString(errorString);
189
/*! \class QtIOCompressor
190
\brief The QtIOCompressor class is a QIODevice that compresses data streams.
192
A QtIOCompressor object is constructed with a pointer to an
193
underlying QIODevice. Data written to the QtIOCompressor object
194
will be compressed before it is written to the underlying
195
QIODevice. Similarly, if you read from the QtIOCompressor object,
196
the data will be read from the underlying device and then
199
QtIOCompressor is a sequential device, which means that it does
200
not support seeks or random access. Internally, QtIOCompressor
201
uses the zlib library to compress and uncompress data.
204
Writing compressed data to a file:
207
QtIOCompressor compressor(&file);
208
compressor.open(QIODevice::WriteOnly);
209
compressor.write(QByteArray() << "The quick brown fox");
213
Reading compressed data from a file:
216
QtIOCompressor compressor(&file);
217
compressor.open(QIODevice::ReadOnly);
218
const QByteArray text = compressor.readAll();
222
QtIOCompressor can also read and write compressed data in
223
different compressed formats, ref. StreamFormat. Use
224
setStreamFormat() before open() to select format.
228
\enum QtIOCompressor::StreamFormat
229
This enum specifies which stream format to use.
231
\value ZlibFormat: This is the default and has the smallest overhead.
233
\value GzipFormat: This format is compatible with the gzip file
234
format, but has more overhead than ZlibFormat. Note: requires zlib
235
version 1.2.x or higher at runtime.
237
\value RawZipFormat: This is compatible with the most common
238
compression method of the data blocks contained in ZIP
239
archives. Note: ZIP file headers are not read or generated, so
240
setting this format, by itself, does not let QtIOCompressor read
241
or write ZIP files. Ref. the ziplist example program.
243
\sa setStreamFormat()
247
Constructs a QtIOCompressor using the given \a device as the underlying device.
249
The allowed value range for \a compressionLevel is 0 to 9, where 0 means no compression
250
and 9 means maximum compression. The default value is 6.
252
\a bufferSize specifies the size of the internal buffer used when reading from and writing to the
253
underlying device. The default value is 65KB. Using a larger value allows for faster compression and
254
decompression at the expense of memory usage.
256
QtIOCompressor::QtIOCompressor(QIODevice *device, int compressionLevel, int bufferSize)
257
:d_ptr(new QtIOCompressorPrivate(this, device, compressionLevel, bufferSize))
261
Destroys the QtIOCompressor, closing it if necessary.
263
QtIOCompressor::~QtIOCompressor()
271
Sets the format on the compressed stream to \a format.
273
\sa QtIOCompressor::StreamFormat
275
void QtIOCompressor::setStreamFormat(StreamFormat format)
279
// Print a waning if the compile-time version of zlib does not support gzip.
280
if (format == GzipFormat && checkGzipSupport(ZLIB_VERSION) == false)
281
qWarning("QtIOCompressor::setStreamFormat: zlib 1.2.x or higher is "
282
"required to use the gzip format. Current version is: %s",
285
d->streamFormat = format;
289
Returns the format set on the compressed stream.
290
\sa QtIOCompressor::StreamFormat
292
QtIOCompressor::StreamFormat QtIOCompressor::streamFormat() const
294
Q_D(const QtIOCompressor);
295
return d->streamFormat;
299
Returns true if the zlib library in use supports the gzip format, false otherwise.
301
bool QtIOCompressor::isGzipSupported()
303
return checkGzipSupport(zlibVersion());
309
bool QtIOCompressor::isSequential() const
315
Opens the QtIOCompressor in \a mode. Only ReadOnly and WriteOnly is supported.
316
This function will return false if you try to open in other modes.
318
If the underlying device is not opened, this function will open it in a suitable mode. If this happens
319
the device will also be closed when close() is called.
321
If the underlying device is already opened, its openmode must be compatible with \a mode.
323
Returns true on success, false on error.
327
bool QtIOCompressor::open(OpenMode mode)
331
qWarning("QtIOCompressor::open: device already open");
335
// Check for correct mode: ReadOnly xor WriteOnly
336
const bool read = (mode & ReadOnly);
337
const bool write = (mode & WriteOnly);
338
const bool both = (read && write);
339
const bool neither = !(read || write);
340
if (both || neither) {
341
qWarning("QtIOCompressor::open: QtIOCompressor can only be opened in the ReadOnly or WriteOnly modes");
345
// If the underlying device is open, check that is it opened in a compatible mode.
346
if (d->device->isOpen()) {
347
d->manageDevice = false;
348
const OpenMode deviceMode = d->device->openMode();
349
if (read && !(deviceMode & ReadOnly)) {
350
qWarning("QtIOCompressor::open: underlying device must be open in one of the ReadOnly or WriteOnly modes");
352
} else if (write && !(deviceMode & WriteOnly)) {
353
qWarning("QtIOCompressor::open: underlying device must be open in one of the ReadOnly or WriteOnly modes");
357
// If the underlying device is closed, open it.
359
d->manageDevice = true;
360
if (d->device->open(mode) == false) {
361
setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error opening underlying device: ") + d->device->errorString());
366
// Initialize zlib for deflating or inflating.
368
// The second argument to inflate/deflateInit2 is the windowBits parameter,
369
// which also controls what kind of compression stream headers to use.
370
// The default value for this is 15. Passing a value greater than 15
371
// enables gzip headers and then subtracts 16 form the windowBits value.
372
// (So passing 31 gives gzip headers and 15 windowBits). Passing a negative
373
// value selects no headers hand then negates the windowBits argument.
375
switch (d->streamFormat) {
376
case QtIOCompressor::GzipFormat:
379
case QtIOCompressor::RawZipFormat:
388
d->state = QtIOCompressorPrivate::NotReadFirstByte;
389
d->zlibStream.avail_in = 0;
390
d->zlibStream.next_in = nullptr;
391
if (d->streamFormat == QtIOCompressor::ZlibFormat) {
392
status = inflateInit(&d->zlibStream);
394
if (checkGzipSupport(zlibVersion()) == false) {
395
setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor::open", "The gzip format not supported in this version of zlib."));
399
status = inflateInit2(&d->zlibStream, windowBits);
402
d->state = QtIOCompressorPrivate::NoBytesWritten;
403
if (d->streamFormat == QtIOCompressor::ZlibFormat)
404
status = deflateInit(&d->zlibStream, d->compressionLevel);
406
status = deflateInit2(&d->zlibStream, d->compressionLevel, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY);
410
if (status != Z_OK) {
411
d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor::open", "Internal zlib error: "), status);
414
return QIODevice::open(mode);
418
Closes the QtIOCompressor, and also the underlying device if it was opened by QtIOCompressor.
421
void QtIOCompressor::close()
424
if (isOpen() == false)
427
// Flush and close the zlib stream.
428
if (openMode() & ReadOnly) {
429
d->state = QtIOCompressorPrivate::NotReadFirstByte;
430
inflateEnd(&d->zlibStream);
432
if (d->state == QtIOCompressorPrivate::BytesWritten) { // Only flush if we have written anything.
433
d->state = QtIOCompressorPrivate::NoBytesWritten;
434
d->flushZlib(Z_FINISH);
436
deflateEnd(&d->zlibStream);
439
// Close the underlying device if we are managing it.
447
Flushes the internal buffer.
449
Each time you call flush, all data written to the QtIOCompressor is compressed and written to the
450
underlying device. Calling this function can reduce the compression ratio. The underlying device
453
Calling this function when QtIOCompressor is in ReadOnly mode has no effect.
455
void QtIOCompressor::flush()
458
if (isOpen() == false || openMode() & ReadOnly)
461
d->flushZlib(Z_SYNC_FLUSH);
465
Returns 1 if there might be data available for reading, or 0 if there is no data available.
467
There is unfortunately no way of knowing how much data there is available when dealing with compressed streams.
469
Also, since the remaining compressed data might be a part of the meta-data that ends the compressed stream (and
470
therefore will yield no uncompressed data), you cannot assume that a read after getting a 1 from this function will return data.
472
qint64 QtIOCompressor::bytesAvailable() const
474
Q_D(const QtIOCompressor);
475
if ((openMode() & ReadOnly) == false)
481
case QtIOCompressorPrivate::NotReadFirstByte:
482
numBytes = d->device->bytesAvailable();
484
case QtIOCompressorPrivate::InStream:
487
case QtIOCompressorPrivate::EndOfStream:
488
case QtIOCompressorPrivate::Error:
494
numBytes += QIODevice::bytesAvailable();
504
Reads and decompresses data from the underlying device.
506
qint64 QtIOCompressor::readData(char *data, qint64 maxSize)
510
if (d->state == QtIOCompressorPrivate::EndOfStream)
513
if (d->state == QtIOCompressorPrivate::Error)
516
// We are going to try to fill the data buffer
517
d->zlibStream.next_out = reinterpret_cast<ZlibByte *>(data);
518
d->zlibStream.avail_out = maxSize;
522
// Read data if if the input buffer is empty. There could be data in the buffer
523
// from a previous readData call.
524
if (d->zlibStream.avail_in == 0) {
525
qint64 bytesAvailable = d->device->read(reinterpret_cast<char *>(d->buffer), d->bufferSize);
526
d->zlibStream.next_in = d->buffer;
527
d->zlibStream.avail_in = bytesAvailable;
529
if (bytesAvailable == -1) {
530
d->state = QtIOCompressorPrivate::Error;
531
setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error reading data from underlying device: ") + d->device->errorString());
535
if (d->state != QtIOCompressorPrivate::InStream) {
536
// If we are not in a stream and get 0 bytes, we are probably trying to read from an empty device.
537
if(bytesAvailable == 0)
539
else if (bytesAvailable > 0)
540
d->state = QtIOCompressorPrivate::InStream;
545
status = inflate(&d->zlibStream, Z_SYNC_FLUSH);
550
d->state = QtIOCompressorPrivate::Error;
551
d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when decompressing: "), status);
553
case Z_BUF_ERROR: // No more input and zlib can not provide more output - Not an error, we can try to read again when we have more input.
556
// Loop util data buffer is full or we reach the end of the input stream.
557
} while (d->zlibStream.avail_out != 0 && status != Z_STREAM_END);
559
if (status == Z_STREAM_END) {
560
d->state = QtIOCompressorPrivate::EndOfStream;
562
// Unget any data left in the read buffer.
563
for (int i = d->zlibStream.avail_in; i >= 0; --i)
564
d->device->ungetChar(*reinterpret_cast<char *>(d->zlibStream.next_in + i));
567
const ZlibSize outputSize = maxSize - d->zlibStream.avail_out;
574
Compresses and writes data to the underlying device.
576
qint64 QtIOCompressor::writeData(const char *data, qint64 maxSize)
581
d->zlibStream.next_in = reinterpret_cast<ZlibByte *>(const_cast<char *>(data));
582
d->zlibStream.avail_in = maxSize;
584
if (d->state == QtIOCompressorPrivate::Error)
588
d->zlibStream.next_out = d->buffer;
589
d->zlibStream.avail_out = d->bufferSize;
590
const int status = deflate(&d->zlibStream, Z_NO_FLUSH);
591
if (status != Z_OK) {
592
d->state = QtIOCompressorPrivate::Error;
593
d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when compressing: "), status);
597
ZlibSize outputSize = d->bufferSize - d->zlibStream.avail_out;
599
// Try to write data from the buffer to to the underlying device, return -1 on failure.
600
if (d->writeBytes(d->buffer, outputSize) == false)
603
} while (d->zlibStream.avail_out == 0); // run until output is not full.
604
Q_ASSERT(d->zlibStream.avail_in == 0);
611
Checks if the run-time zlib version is 1.2.x or higher.
613
bool QtIOCompressor::checkGzipSupport(const char * const versionString)
615
if (strlen(versionString) < 3)
618
if (versionString[0] == '0' || (versionString[0] == '1' && (versionString[2] == '0' || versionString[2] == '1' )))