1 /****************************************************************************
2 **
3 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of a Qt Solutions component.
8 **
9 ** Commercial Usage
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.
14 **
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.
22 **
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 LGPL_EXCEPTION.txt in this
26 ** package.
27 **
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 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.
35 **
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.
41 **
42 ** If you are unsure which license is appropriate for your use, please
43 ** contact Nokia at qt-info@nokia.com.
44 **
45 ****************************************************************************/
46
47 #include "qtiocompressor.h"
48 #include "zlib.h"
49 #include <QDebug>
50
51 typedef Bytef ZlibByte;
52 typedef uInt ZlibSize;
53
54 class QtIOCompressorPrivate {
55 QtIOCompressor *q_ptr;
56 Q_DECLARE_PUBLIC(QtIOCompressor)
57 public:
58 enum State {
59 // Read state
60 NotReadFirstByte,
61 InStream,
62 EndOfStream,
63 // Write state
64 NoBytesWritten,
65 BytesWritten,
66 // Common
67 Closed,
68 Error
69 };
70
71 QtIOCompressorPrivate(QtIOCompressor *q_ptr, QIODevice *device, int compressionLevel, int bufferSize);
72 ~QtIOCompressorPrivate();
73 void flushZlib(int flushMode);
74 bool writeBytes(ZlibByte *buffer, ZlibSize outputSize);
75 void setZlibError(const QString &erroMessage, int zlibErrorCode);
76
77 QIODevice *device;
78 bool manageDevice;
79 z_stream zlibStream;
80 const int compressionLevel;
81 const ZlibSize bufferSize;
82 ZlibByte *buffer;
83 State state;
84 QtIOCompressor::StreamFormat streamFormat;
85 };
86
87 /*!
88 \internal
89 */
QtIOCompressorPrivate(QtIOCompressor * q_ptr,QIODevice * device,int compressionLevel,int bufferSize)90 QtIOCompressorPrivate::QtIOCompressorPrivate(QtIOCompressor *q_ptr, QIODevice *device, int compressionLevel, int bufferSize)
91 :q_ptr(q_ptr)
92 ,device(device)
93 ,compressionLevel(compressionLevel)
94 ,bufferSize(bufferSize)
95 ,buffer(new ZlibByte[bufferSize])
96 ,state(Closed)
97 ,streamFormat(QtIOCompressor::ZlibFormat)
98 {
99 // Use default zlib memory management.
100 zlibStream.zalloc = Z_NULL;
101 zlibStream.zfree = Z_NULL;
102 zlibStream.opaque = Z_NULL;
103 }
104
105 /*!
106 \internal
107 */
~QtIOCompressorPrivate()108 QtIOCompressorPrivate::~QtIOCompressorPrivate()
109 {
110 delete[] buffer;
111 }
112
113 /*!
114 \internal
115 Flushes the zlib stream.
116 */
flushZlib(int flushMode)117 void QtIOCompressorPrivate::flushZlib(int flushMode)
118 {
119 // No input.
120 zlibStream.next_in = nullptr;
121 zlibStream.avail_in = 0;
122 int status;
123 do {
124 zlibStream.next_out = buffer;
125 zlibStream.avail_out = bufferSize;
126 status = deflate(&zlibStream, flushMode);
127 if (status != Z_OK && status != Z_STREAM_END) {
128 state = QtIOCompressorPrivate::Error;
129 setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when compressing: "), status);
130 return;
131 }
132
133 ZlibSize outputSize = bufferSize - zlibStream.avail_out;
134
135 // Try to write data from the buffer to to the underlying device, return on failure.
136 if (!writeBytes(buffer, outputSize))
137 return;
138
139 // If the mode is Z_FNISH we must loop until we get Z_STREAM_END,
140 // else we loop as long as zlib is able to fill the output buffer.
141 } while ((flushMode == Z_FINISH && status != Z_STREAM_END) || (flushMode != Z_FINISH && zlibStream.avail_out == 0));
142
143 if (flushMode == Z_FINISH)
144 Q_ASSERT(status == Z_STREAM_END);
145 else
146 Q_ASSERT(status == Z_OK);
147 }
148
149 /*!
150 \internal
151 Writes outputSize bytes from buffer to the inderlying device.
152 */
writeBytes(ZlibByte * buffer,ZlibSize outputSize)153 bool QtIOCompressorPrivate::writeBytes(ZlibByte *buffer, ZlibSize outputSize)
154 {
155 Q_Q(QtIOCompressor);
156 ZlibSize totalBytesWritten = 0;
157 // Loop until all bytes are written to the underlying device.
158 do {
159 const qint64 bytesWritten = device->write(reinterpret_cast<char *>(buffer), outputSize);
160 if (bytesWritten == -1) {
161 q->setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error writing to underlying device: ") + device->errorString());
162 return false;
163 }
164 totalBytesWritten += bytesWritten;
165 } while (totalBytesWritten != outputSize);
166
167 // put up a flag so that the device will be flushed on close.
168 state = BytesWritten;
169 return true;
170 }
171
172 /*!
173 \internal
174 Sets the error string to errorMessage + zlib error string for zlibErrorCode
175 */
setZlibError(const QString & errorMessage,int zlibErrorCode)176 void QtIOCompressorPrivate::setZlibError(const QString &errorMessage, int zlibErrorCode)
177 {
178 Q_Q(QtIOCompressor);
179 // Watch out, zlibErrorString may be null.
180 const char * const zlibErrorString = zError(zlibErrorCode);
181 QString errorString;
182 if (zlibErrorString)
183 errorString = errorMessage + zlibErrorString;
184 else
185 errorString = errorMessage + " Unknown error, code " + QString::number(zlibErrorCode);
186
187 q->setErrorString(errorString);
188 }
189
190 /*! \class QtIOCompressor
191 \brief The QtIOCompressor class is a QIODevice that compresses data streams.
192
193 A QtIOCompressor object is constructed with a pointer to an
194 underlying QIODevice. Data written to the QtIOCompressor object
195 will be compressed before it is written to the underlying
196 QIODevice. Similary, if you read from the QtIOCompressor object,
197 the data will be read from the underlying device and then
198 decompressed.
199
200 QtIOCompressor is a sequential device, which means that it does
201 not support seeks or random access. Internally, QtIOCompressor
202 uses the zlib library to compress and uncompress data.
203
204 Usage examples:
205 Writing compressed data to a file:
206 \code
207 QFile file("foo");
208 QtIOCompressor compressor(&file);
209 compressor.open(QIODevice::WriteOnly);
210 compressor.write(QByteArray() << "The quick brown fox");
211 compressor.close();
212 \endcode
213
214 Reading compressed data from a file:
215 \code
216 QFile file("foo");
217 QtIOCompressor compressor(&file);
218 compressor.open(QIODevice::ReadOnly);
219 const QByteArray text = compressor.readAll();
220 compressor.close();
221 \endcode
222
223 QtIOCompressor can also read and write compressed data in
224 different compressed formats, ref. StreamFormat. Use
225 setStreamFormat() before open() to select format.
226 */
227
228 /*!
229 \enum QtIOCompressor::StreamFormat
230 This enum specifies which stream format to use.
231
232 \value ZlibFormat: This is the default and has the smallest overhead.
233
234 \value GzipFormat: This format is compatible with the gzip file
235 format, but has more overhead than ZlibFormat. Note: requires zlib
236 version 1.2.x or higher at runtime.
237
238 \value RawZipFormat: This is compatible with the most common
239 compression method of the data blocks contained in ZIP
240 archives. Note: ZIP file headers are not read or generated, so
241 setting this format, by itself, does not let QtIOCompressor read
242 or write ZIP files. Ref. the ziplist example program.
243
244 \sa setStreamFormat()
245 */
246
247 /*!
248 Constructs a QtIOCompressor using the given \a device as the underlying device.
249
250 The allowed value range for \a compressionLevel is 0 to 9, where 0 means no compression
251 and 9 means maximum compression. The default value is 6.
252
253 \a bufferSize specifies the size of the internal buffer used when reading from and writing to the
254 underlying device. The default value is 65KB. Using a larger value allows for faster compression and
255 deompression at the expense of memory usage.
256 */
QtIOCompressor(QIODevice * device,int compressionLevel,int bufferSize)257 QtIOCompressor::QtIOCompressor(QIODevice *device, int compressionLevel, int bufferSize)
258 :d_ptr(new QtIOCompressorPrivate(this, device, compressionLevel, bufferSize))
259 {}
260
261 /*!
262 Destroys the QtIOCompressor, closing it if neccesary.
263 */
~QtIOCompressor()264 QtIOCompressor::~QtIOCompressor()
265 {
266 Q_D(QtIOCompressor);
267 close();
268 delete d;
269 }
270
271 /*!
272 Sets the format on the compressed stream to \a format.
273
274 \sa QtIOCompressor::StreamFormat
275 */
setStreamFormat(StreamFormat format)276 void QtIOCompressor::setStreamFormat(StreamFormat format)
277 {
278 Q_D(QtIOCompressor);
279
280 // Print a waning if the compile-time version of zlib does not support gzip.
281 if (format == GzipFormat && checkGzipSupport(ZLIB_VERSION) == false)
282 qWarning("QtIOCompressor::setStreamFormat: zlib 1.2.x or higher is "
283 "required to use the gzip format. Current version is: %s",
284 ZLIB_VERSION);
285
286 d->streamFormat = format;
287 }
288
289 /*!
290 Returns the format set on the compressed stream.
291 \sa QtIOCompressor::StreamFormat
292 */
streamFormat() const293 QtIOCompressor::StreamFormat QtIOCompressor::streamFormat() const
294 {
295 Q_D(const QtIOCompressor);
296 return d->streamFormat;
297 }
298
299 /*!
300 Returns true if the zlib library in use supports the gzip format, false otherwise.
301 */
isGzipSupported()302 bool QtIOCompressor::isGzipSupported()
303 {
304 return checkGzipSupport(zlibVersion());
305 }
306
307 /*!
308 \reimp
309 */
isSequential() const310 bool QtIOCompressor::isSequential() const
311 {
312 return true;
313 }
314
315 /*!
316 Opens the QtIOCompressor in \a mode. Only ReadOnly and WriteOnly is supported.
317 This functon will return false if you try to open in other modes.
318
319 If the underlying device is not opened, this function will open it in a suitable mode. If this happens
320 the device will also be closed when close() is called.
321
322 If the underlying device is already opened, its openmode must be compatable with \a mode.
323
324 Returns true on success, false on error.
325
326 \sa close()
327 */
open(OpenMode mode)328 bool QtIOCompressor::open(OpenMode mode)
329 {
330 Q_D(QtIOCompressor);
331 if (isOpen()) {
332 qWarning("QtIOCompressor::open: device already open");
333 return false;
334 }
335
336 // Check for correct mode: ReadOnly xor WriteOnly
337 const bool read = (bool)(mode & ReadOnly);
338 const bool write = (bool)(mode & WriteOnly);
339 const bool both = (read && write);
340 const bool neither = !(read || write);
341 if (both || neither) {
342 qWarning("QtIOCompressor::open: QtIOCompressor can only be opened in the ReadOnly or WriteOnly modes");
343 return false;
344 }
345
346 // If the underlying device is open, check that is it opened in a compatible mode.
347 if (d->device->isOpen()) {
348 d->manageDevice = false;
349 const OpenMode deviceMode = d->device->openMode();
350 if (read && !(deviceMode & ReadOnly)) {
351 qWarning("QtIOCompressor::open: underlying device must be open in one of the ReadOnly or WriteOnly modes");
352 return false;
353 } else if (write && !(deviceMode & WriteOnly)) {
354 qWarning("QtIOCompressor::open: underlying device must be open in one of the ReadOnly or WriteOnly modes");
355 return false;
356 }
357
358 // If the underlying device is closed, open it.
359 } else {
360 d->manageDevice = true;
361 if (d->device->open(mode) == false) {
362 setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error opening underlying device: ") + d->device->errorString());
363 return false;
364 }
365 }
366
367 // Initialize zlib for deflating or inflating.
368
369 // The second argument to inflate/deflateInit2 is the windowBits parameter,
370 // which also controls what kind of compression stream headers to use.
371 // The default value for this is 15. Passing a value greater than 15
372 // enables gzip headers and then subtracts 16 form the windowBits value.
373 // (So passing 31 gives gzip headers and 15 windowBits). Passing a negative
374 // value selects no headers hand then negates the windowBits argument.
375 int windowBits;
376 switch (d->streamFormat) {
377 case QtIOCompressor::GzipFormat:
378 windowBits = 31;
379 break;
380 case QtIOCompressor::RawZipFormat:
381 windowBits = -15;
382 break;
383 default:
384 windowBits = 15;
385 }
386
387 int status;
388 if (read) {
389 d->state = QtIOCompressorPrivate::NotReadFirstByte;
390 d->zlibStream.avail_in = 0;
391 d->zlibStream.next_in = nullptr;
392 if (d->streamFormat == QtIOCompressor::ZlibFormat) {
393 status = inflateInit(&d->zlibStream);
394 } else {
395 if (checkGzipSupport(zlibVersion()) == false) {
396 setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor::open", "The gzip format not supported in this version of zlib."));
397 return false;
398 }
399
400 status = inflateInit2(&d->zlibStream, windowBits);
401 }
402 } else {
403 d->state = QtIOCompressorPrivate::NoBytesWritten;
404 if (d->streamFormat == QtIOCompressor::ZlibFormat)
405 status = deflateInit(&d->zlibStream, d->compressionLevel);
406 else
407 status = deflateInit2(&d->zlibStream, d->compressionLevel, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY);
408 }
409
410 // Handle error.
411 if (status != Z_OK) {
412 d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor::open", "Internal zlib error: "), status);
413 return false;
414 }
415 return QIODevice::open(mode);
416 }
417
418 /*!
419 Closes the QtIOCompressor, and also the underlying device if it was opened by QtIOCompressor.
420 \sa open()
421 */
close()422 void QtIOCompressor::close()
423 {
424 Q_D(QtIOCompressor);
425 if (isOpen() == false)
426 return;
427
428 // Flush and close the zlib stream.
429 if (openMode() & ReadOnly) {
430 d->state = QtIOCompressorPrivate::NotReadFirstByte;
431 inflateEnd(&d->zlibStream);
432 } else {
433 if (d->state == QtIOCompressorPrivate::BytesWritten) { // Only flush if we have written anything.
434 d->state = QtIOCompressorPrivate::NoBytesWritten;
435 d->flushZlib(Z_FINISH);
436 }
437 deflateEnd(&d->zlibStream);
438 }
439
440 // Close the underlying device if we are managing it.
441 if (d->manageDevice)
442 d->device->close();
443
444 QIODevice::close();
445 }
446
447 /*!
448 Flushes the internal buffer.
449
450 Each time you call flush, all data written to the QtIOCompressor is compressed and written to the
451 underlying device. Calling this function can reduce the compression ratio. The underlying device
452 is not flushed.
453
454 Calling this function when QtIOCompressor is in ReadOnly mode has no effect.
455 */
flush()456 void QtIOCompressor::flush()
457 {
458 Q_D(QtIOCompressor);
459 if (isOpen() == false || openMode() & ReadOnly)
460 return;
461
462 d->flushZlib(Z_SYNC_FLUSH);
463 }
464
465 /*!
466 Returns 1 if there might be data available for reading, or 0 if there is no data available.
467
468 There is unfortunately no way of knowing how much data there is available when dealing with compressed streams.
469
470 Also, since the remaining compressed data might be a part of the meta-data that ends the compressed stream (and
471 therefore will yield no uncompressed data), you cannot assume that a read after getting a 1 from this function will return data.
472 */
bytesAvailable() const473 qint64 QtIOCompressor::bytesAvailable() const
474 {
475 Q_D(const QtIOCompressor);
476 if ((openMode() & ReadOnly) == false)
477 return 0;
478
479 int numBytes = 0;
480
481 switch (d->state) {
482 case QtIOCompressorPrivate::NotReadFirstByte:
483 numBytes = d->device->bytesAvailable();
484 break;
485 case QtIOCompressorPrivate::InStream:
486 numBytes = 1;
487 break;
488 case QtIOCompressorPrivate::EndOfStream:
489 case QtIOCompressorPrivate::Error:
490 default:
491 numBytes = 0;
492 break;
493 };
494
495 numBytes += QIODevice::bytesAvailable();
496
497 if (numBytes > 0)
498 return 1;
499 else
500 return 0;
501 }
502
503 /*!
504 \internal
505 Reads and decompresses data from the underlying device.
506 */
readData(char * data,qint64 maxSize)507 qint64 QtIOCompressor::readData(char *data, qint64 maxSize)
508 {
509 Q_D(QtIOCompressor);
510
511 if (d->state == QtIOCompressorPrivate::EndOfStream)
512 return 0;
513
514 if (d->state == QtIOCompressorPrivate::Error)
515 return -1;
516
517 // We are ging to try to fill the data buffer
518 d->zlibStream.next_out = reinterpret_cast<ZlibByte *>(data);
519 d->zlibStream.avail_out = maxSize;
520
521 int status;
522 do {
523 // Read data if if the input buffer is empty. There could be data in the buffer
524 // from a previous readData call.
525 if (d->zlibStream.avail_in == 0) {
526 qint64 bytesAvalible = d->device->read(reinterpret_cast<char *>(d->buffer), d->bufferSize);
527 d->zlibStream.next_in = d->buffer;
528 d->zlibStream.avail_in = bytesAvalible;
529
530 if (bytesAvalible == -1) {
531 d->state = QtIOCompressorPrivate::Error;
532 setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error reading data from underlying device: ") + d->device->errorString());
533 return -1;
534 }
535
536 if (d->state != QtIOCompressorPrivate::InStream) {
537 // If we are not in a stream and get 0 bytes, we are probably trying to read from an empty device.
538 if(bytesAvalible == 0)
539 return 0;
540 else if (bytesAvalible > 0)
541 d->state = QtIOCompressorPrivate::InStream;
542 }
543 }
544
545 // Decompress.
546 status = inflate(&d->zlibStream, Z_SYNC_FLUSH);
547 switch (status) {
548 case Z_NEED_DICT:
549 case Z_DATA_ERROR:
550 case Z_MEM_ERROR:
551 d->state = QtIOCompressorPrivate::Error;
552 d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when decompressing: "), status);
553 return -1;
554 case Z_BUF_ERROR: // No more input and zlib can not privide more output - Not an error, we can try to read again when we have more input.
555 return 0;
556 break;
557 }
558 // Loop util data buffer is full or we reach the end of the input stream.
559 } while (d->zlibStream.avail_out != 0 && status != Z_STREAM_END);
560
561 if (status == Z_STREAM_END) {
562 d->state = QtIOCompressorPrivate::EndOfStream;
563
564 // Unget any data left in the read buffer.
565 for (int i = d->zlibStream.avail_in; i >= 0; --i)
566 d->device->ungetChar(*reinterpret_cast<char *>(d->zlibStream.next_in + i));
567 }
568
569 const ZlibSize outputSize = maxSize - d->zlibStream.avail_out;
570 return outputSize;
571 }
572
573
574 /*!
575 \internal
576 Compresses and writes data to the underlying device.
577 */
writeData(const char * data,qint64 maxSize)578 qint64 QtIOCompressor::writeData(const char *data, qint64 maxSize)
579 {
580 if (maxSize < 1)
581 return 0;
582 Q_D(QtIOCompressor);
583 d->zlibStream.next_in = reinterpret_cast<ZlibByte *>(const_cast<char *>(data));
584 d->zlibStream.avail_in = maxSize;
585
586 if (d->state == QtIOCompressorPrivate::Error)
587 return -1;
588
589 do {
590 d->zlibStream.next_out = d->buffer;
591 d->zlibStream.avail_out = d->bufferSize;
592 const int status = deflate(&d->zlibStream, Z_NO_FLUSH);
593 if (status != Z_OK) {
594 d->state = QtIOCompressorPrivate::Error;
595 d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when compressing: "), status);
596 return -1;
597 }
598
599 ZlibSize outputSize = d->bufferSize - d->zlibStream.avail_out;
600
601 // Try to write data from the buffer to to the underlying device, return -1 on failure.
602 if (d->writeBytes(d->buffer, outputSize) == false)
603 return -1;
604
605 } while (d->zlibStream.avail_out == 0); // run until output is not full.
606 Q_ASSERT(d->zlibStream.avail_in == 0);
607
608 return maxSize;
609 }
610
611 /*
612 \internal
613 Checks if the run-time zlib version is 1.2.x or higher.
614 */
checkGzipSupport(const char * const versionString)615 bool QtIOCompressor::checkGzipSupport(const char * const versionString)
616 {
617 if (strlen(versionString) < 3)
618 return false;
619
620 if (versionString[0] == '0' || (versionString[0] == '1' && (versionString[2] == '0' || versionString[2] == '1' )))
621 return false;
622
623 return true;
624 }
625
626 #include "moc_qtiocompressor.cpp"
627