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