keepassxc

Форк
0
/
qtiocompressor.cpp 
622 строки · 20.4 Кб
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 LICENSE.NOKIA-LGPL-EXCEPTION
26
** in this 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-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.
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

50
typedef Bytef ZlibByte;
51
typedef uInt ZlibSize;
52

53
class QtIOCompressorPrivate {
54
    QtIOCompressor *q_ptr;
55
    Q_DECLARE_PUBLIC(QtIOCompressor)
56
public:
57
    enum State {
58
        // Read state
59
        NotReadFirstByte,
60
        InStream,
61
        EndOfStream,
62
        // Write state
63
        NoBytesWritten,
64
        BytesWritten,
65
        // Common
66
        Closed,
67
        Error
68
    };
69

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);
75

76
    QIODevice *device;
77
    bool manageDevice;
78
    z_stream zlibStream;
79
    const int compressionLevel;
80
    const ZlibSize bufferSize;
81
    ZlibByte *buffer;
82
    State state;
83
    QtIOCompressor::StreamFormat streamFormat;
84
};
85

86
/*!
87
    \internal
88
*/
89
QtIOCompressorPrivate::QtIOCompressorPrivate(QtIOCompressor *q_ptr, QIODevice *device, int compressionLevel, int bufferSize)
90
:q_ptr(q_ptr)
91
,device(device)
92
,compressionLevel(compressionLevel)
93
,bufferSize(bufferSize)
94
,buffer(new ZlibByte[bufferSize])
95
,state(Closed)
96
,streamFormat(QtIOCompressor::ZlibFormat)
97
{
98
    // Use default zlib memory management.
99
    zlibStream.zalloc = Z_NULL;
100
    zlibStream.zfree = Z_NULL;
101
    zlibStream.opaque = Z_NULL;
102
}
103

104
/*!
105
    \internal
106
*/
107
QtIOCompressorPrivate::~QtIOCompressorPrivate()
108
{
109
    delete[] buffer;
110
}
111

112
/*!
113
    \internal
114
    Flushes the zlib stream.
115
*/
116
void QtIOCompressorPrivate::flushZlib(int flushMode)
117
{
118
    // No input.
119
    zlibStream.next_in = nullptr;
120
    zlibStream.avail_in = 0;
121
    int status;
122
    do {
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);
129
            return;
130
        }
131

132
        ZlibSize outputSize = bufferSize - zlibStream.avail_out;
133

134
        // Try to write data from the buffer to to the underlying device, return on failure.
135
        if (!writeBytes(buffer, outputSize))
136
            return;
137

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));
141

142
    if (flushMode == Z_FINISH)
143
        Q_ASSERT(status == Z_STREAM_END);
144
    else
145
        Q_ASSERT(status == Z_OK);
146
}
147

148
/*!
149
    \internal
150
    Writes outputSize bytes from buffer to the underlying device.
151
*/
152
bool QtIOCompressorPrivate::writeBytes(ZlibByte *buffer, ZlibSize outputSize)
153
{
154
    Q_Q(QtIOCompressor);
155
    ZlibSize totalBytesWritten = 0;
156
    // Loop until all bytes are written to the underlying device.
157
    do {
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());
161
            return false;
162
        }
163
        totalBytesWritten += bytesWritten;
164
    } while (totalBytesWritten != outputSize);
165

166
    // put up a flag so that the device will be flushed on close.
167
    state = BytesWritten;
168
    return true;
169
}
170

171
/*!
172
    \internal
173
    Sets the error string to errorMessage + zlib error string for zlibErrorCode
174
*/
175
void QtIOCompressorPrivate::setZlibError(const QString &errorMessage, int zlibErrorCode)
176
{
177
    Q_Q(QtIOCompressor);
178
    // Watch out, zlibErrorString may be null.
179
    const char * const zlibErrorString = zError(zlibErrorCode);
180
    QString errorString;
181
    if (zlibErrorString)
182
        errorString = errorMessage + zlibErrorString;
183
    else
184
        errorString = errorMessage  + " Unknown error, code " + QString::number(zlibErrorCode);
185

186
    q->setErrorString(errorString);
187
}
188

189
/*! \class QtIOCompressor
190
    \brief The QtIOCompressor class is a QIODevice that compresses data streams.
191

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
197
    decompressed.
198

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.
202

203
    Usage examples:
204
    Writing compressed data to a file:
205
    \code
206
        QFile file("foo");
207
        QtIOCompressor compressor(&file);
208
        compressor.open(QIODevice::WriteOnly);
209
        compressor.write(QByteArray() << "The quick brown fox");
210
        compressor.close();
211
    \endcode
212

213
    Reading compressed data from a file:
214
    \code
215
        QFile file("foo");
216
        QtIOCompressor compressor(&file);
217
        compressor.open(QIODevice::ReadOnly);
218
        const QByteArray text = compressor.readAll();
219
        compressor.close();
220
    \endcode
221

222
    QtIOCompressor can also read and write compressed data in
223
    different compressed formats, ref. StreamFormat. Use
224
    setStreamFormat() before open() to select format.
225
*/
226

227
/*!
228
    \enum QtIOCompressor::StreamFormat
229
    This enum specifies which stream format to use.
230

231
    \value ZlibFormat: This is the default and has the smallest overhead.
232

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.
236

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.
242

243
    \sa setStreamFormat()
244
*/
245

246
/*!
247
    Constructs a QtIOCompressor using the given \a device as the underlying device.
248

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.
251

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.
255
*/
256
QtIOCompressor::QtIOCompressor(QIODevice *device, int compressionLevel, int bufferSize)
257
:d_ptr(new QtIOCompressorPrivate(this, device, compressionLevel, bufferSize))
258
{}
259

260
/*!
261
    Destroys the QtIOCompressor, closing it if necessary.
262
*/
263
QtIOCompressor::~QtIOCompressor()
264
{
265
    Q_D(QtIOCompressor);
266
    close();
267
    delete d;
268
}
269

270
/*!
271
    Sets the format on the compressed stream to \a format.
272

273
    \sa QtIOCompressor::StreamFormat
274
*/
275
void QtIOCompressor::setStreamFormat(StreamFormat format)
276
{
277
    Q_D(QtIOCompressor);
278

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",
283
                 ZLIB_VERSION);
284

285
    d->streamFormat = format;
286
}
287

288
/*!
289
    Returns the format set on the compressed stream.
290
    \sa QtIOCompressor::StreamFormat
291
*/
292
QtIOCompressor::StreamFormat QtIOCompressor::streamFormat() const
293
{
294
    Q_D(const QtIOCompressor);
295
    return d->streamFormat;
296
}
297

298
/*!
299
    Returns true if the zlib library in use supports the gzip format, false otherwise.
300
*/
301
bool QtIOCompressor::isGzipSupported()
302
{
303
    return checkGzipSupport(zlibVersion());
304
}
305

306
/*!
307
    \reimp
308
*/
309
bool QtIOCompressor::isSequential() const
310
{
311
    return true;
312
}
313

314
/*!
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.
317

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.
320

321
    If the underlying device is already opened, its openmode must be compatible with \a mode.
322

323
    Returns true on success, false on error.
324

325
    \sa close()
326
*/
327
bool QtIOCompressor::open(OpenMode mode)
328
{
329
    Q_D(QtIOCompressor);
330
    if (isOpen()) {
331
        qWarning("QtIOCompressor::open: device already open");
332
        return false;
333
    }
334

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");
342
        return false;
343
    }
344

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");
351
            return false;
352
        } else if (write && !(deviceMode & WriteOnly)) {
353
            qWarning("QtIOCompressor::open: underlying device must be open in one of the ReadOnly or WriteOnly modes");
354
            return false;
355
        }
356

357
    // If the underlying device is closed, open it.
358
    } else {
359
        d->manageDevice = true;
360
        if (d->device->open(mode) == false) {
361
            setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error opening underlying device: ") + d->device->errorString());
362
            return false;
363
        }
364
    }
365

366
    // Initialize zlib for deflating or inflating.
367

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.
374
    int windowBits;
375
    switch (d->streamFormat) {
376
    case QtIOCompressor::GzipFormat:
377
        windowBits = 31;
378
        break;
379
    case QtIOCompressor::RawZipFormat:
380
        windowBits = -15;
381
        break;
382
    default:
383
        windowBits = 15;
384
    }
385

386
    int status;
387
    if (read) {
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);
393
        } else {
394
            if (checkGzipSupport(zlibVersion()) == false) {
395
                setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor::open", "The gzip format not supported in this version of zlib."));
396
                return false;
397
            }
398

399
            status = inflateInit2(&d->zlibStream, windowBits);
400
        }
401
    } else {
402
        d->state = QtIOCompressorPrivate::NoBytesWritten;
403
        if (d->streamFormat == QtIOCompressor::ZlibFormat)
404
            status = deflateInit(&d->zlibStream, d->compressionLevel);
405
        else
406
            status = deflateInit2(&d->zlibStream, d->compressionLevel, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY);
407
    }
408

409
    // Handle error.
410
    if (status != Z_OK) {
411
        d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor::open", "Internal zlib error: "), status);
412
        return false;
413
    }
414
    return QIODevice::open(mode);
415
}
416

417
/*!
418
     Closes the QtIOCompressor, and also the underlying device if it was opened by QtIOCompressor.
419
    \sa open()
420
*/
421
void QtIOCompressor::close()
422
{
423
    Q_D(QtIOCompressor);
424
    if (isOpen() == false)
425
        return;
426

427
    // Flush and close the zlib stream.
428
    if (openMode() & ReadOnly) {
429
        d->state = QtIOCompressorPrivate::NotReadFirstByte;
430
        inflateEnd(&d->zlibStream);
431
    } else {
432
        if (d->state == QtIOCompressorPrivate::BytesWritten) { // Only flush if we have written anything.
433
            d->state = QtIOCompressorPrivate::NoBytesWritten;
434
            d->flushZlib(Z_FINISH);
435
        }
436
        deflateEnd(&d->zlibStream);
437
    }
438

439
    // Close the underlying device if we are managing it.
440
    if (d->manageDevice)
441
        d->device->close();
442

443
    QIODevice::close();
444
}
445

446
/*!
447
    Flushes the internal buffer.
448

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
451
    is not flushed.
452

453
    Calling this function when QtIOCompressor is in ReadOnly mode has no effect.
454
*/
455
void QtIOCompressor::flush()
456
{
457
    Q_D(QtIOCompressor);
458
    if (isOpen() == false || openMode() & ReadOnly)
459
        return;
460

461
    d->flushZlib(Z_SYNC_FLUSH);
462
}
463

464
/*!
465
    Returns 1 if there might be data available for reading, or 0 if there is no data available.
466

467
    There is unfortunately no way of knowing how much data there is available when dealing with compressed streams.
468

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.
471
*/
472
qint64 QtIOCompressor::bytesAvailable() const
473
{
474
    Q_D(const QtIOCompressor);
475
    if ((openMode() & ReadOnly) == false)
476
        return 0;
477

478
    int numBytes = 0;
479

480
    switch (d->state) {
481
        case QtIOCompressorPrivate::NotReadFirstByte:
482
            numBytes = d->device->bytesAvailable();
483
        break;
484
        case QtIOCompressorPrivate::InStream:
485
            numBytes = 1;
486
        break;
487
        case QtIOCompressorPrivate::EndOfStream:
488
        case QtIOCompressorPrivate::Error:
489
        default:
490
            numBytes = 0;
491
        break;
492
    };
493

494
    numBytes += QIODevice::bytesAvailable();
495

496
    if (numBytes > 0)
497
        return 1;
498
    else
499
        return 0;
500
}
501

502
/*!
503
    \internal
504
    Reads and decompresses data from the underlying device.
505
*/
506
qint64 QtIOCompressor::readData(char *data, qint64 maxSize)
507
{
508
    Q_D(QtIOCompressor);
509

510
    if (d->state == QtIOCompressorPrivate::EndOfStream)
511
        return 0;
512

513
    if (d->state == QtIOCompressorPrivate::Error)
514
        return -1;
515

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;
519

520
    int status;
521
    do {
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;
528

529
            if (bytesAvailable == -1) {
530
                d->state = QtIOCompressorPrivate::Error;
531
                setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error reading data from underlying device: ") + d->device->errorString());
532
                return -1;
533
            }
534

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)
538
                    return 0;
539
                else if (bytesAvailable > 0)
540
                    d->state = QtIOCompressorPrivate::InStream;
541
            }
542
        }
543

544
        // Decompress.
545
        status = inflate(&d->zlibStream, Z_SYNC_FLUSH);
546
        switch (status) {
547
            case Z_NEED_DICT:
548
            case Z_DATA_ERROR:
549
            case Z_MEM_ERROR:
550
                d->state = QtIOCompressorPrivate::Error;
551
                d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when decompressing: "), status);
552
                return -1;
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.
554
                return 0;
555
        }
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);
558

559
    if (status == Z_STREAM_END) {
560
        d->state = QtIOCompressorPrivate::EndOfStream;
561

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));
565
    }
566

567
    const ZlibSize outputSize = maxSize - d->zlibStream.avail_out;
568
    return outputSize;
569
}
570

571

572
/*!
573
    \internal
574
    Compresses and writes data to the underlying device.
575
*/
576
qint64 QtIOCompressor::writeData(const char *data, qint64 maxSize)
577
{
578
    if (maxSize < 1)
579
        return 0;
580
    Q_D(QtIOCompressor);
581
    d->zlibStream.next_in = reinterpret_cast<ZlibByte *>(const_cast<char *>(data));
582
    d->zlibStream.avail_in = maxSize;
583

584
    if (d->state == QtIOCompressorPrivate::Error)
585
        return -1;
586

587
    do {
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);
594
            return -1;
595
        }
596

597
        ZlibSize outputSize = d->bufferSize - d->zlibStream.avail_out;
598

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)
601
            return -1;
602

603
    } while (d->zlibStream.avail_out == 0); // run until output is not full.
604
    Q_ASSERT(d->zlibStream.avail_in == 0);
605

606
    return maxSize;
607
}
608

609
/*
610
    \internal
611
    Checks if the run-time zlib version is 1.2.x or higher.
612
*/
613
bool QtIOCompressor::checkGzipSupport(const char * const versionString)
614
{
615
    if (strlen(versionString) < 3)
616
        return false;
617

618
    if (versionString[0] == '0' || (versionString[0] == '1' && (versionString[2] == '0' || versionString[2]  == '1' )))
619
        return false;
620

621
    return true;
622
}
623

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.