Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qdecompresshelper.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/private/qbytearray_p.h>
7#include <QtCore/qiodevice.h>
8#include <QtCore/qcoreapplication.h>
9
10#include <limits>
11#include <zlib.h>
12
13#if QT_CONFIG(brotli)
14# include <brotli/decode.h>
15#endif
16
17#if QT_CONFIG(zstd)
18# include <zstd.h>
19#endif
20
21#include <array>
22
24namespace {
26{
27 char name[8];
29};
30
32#if QT_CONFIG(zstd)
34#endif
35#if QT_CONFIG(brotli)
37#endif
38 { "gzip", QDecompressHelper::GZip },
39 { "deflate", QDecompressHelper::Deflate },
40};
41
43{
44 for (const auto &mapping : contentEncodingMapping) {
45 if (ce.compare(QByteArrayView(mapping.name, strlen(mapping.name)), Qt::CaseInsensitive) == 0)
46 return mapping.encoding;
47 }
49}
50
51z_stream *toZlibPointer(void *ptr)
52{
53 return static_cast<z_stream_s *>(ptr);
54}
55
56#if QT_CONFIG(brotli)
57BrotliDecoderState *toBrotliPointer(void *ptr)
58{
59 return static_cast<BrotliDecoderState *>(ptr);
60}
61#endif
62
63#if QT_CONFIG(zstd)
64ZSTD_DStream *toZstandardPointer(void *ptr)
65{
66 return static_cast<ZSTD_DStream *>(ptr);
67}
68#endif
69}
70
72{
73 return encodingFromByteArray(encoding) != QDecompressHelper::None;
74}
75
77{
78 static QByteArrayList accepted = []() {
80 list.reserve(sizeof(contentEncodingMapping) / sizeof(contentEncodingMapping[0]));
81 for (const auto &mapping : contentEncodingMapping) {
82 list << QByteArray(mapping.name);
83 }
84 return list;
85 }();
86 return accepted;
87}
88
90{
91 clear();
92}
93
95{
96 Q_ASSERT(contentEncoding == QDecompressHelper::None);
97 if (contentEncoding != QDecompressHelper::None) {
98 qWarning("Encoding is already set.");
99 // This isn't an error, so it doesn't set errorStr, it's just wrong usage.
100 return false;
101 }
102 ContentEncoding ce = encodingFromByteArray(encoding);
103 if (ce == None) {
104 errorStr = QCoreApplication::translate("QHttp", "Unsupported content encoding: %1")
105 .arg(QLatin1String(encoding));
106 return false;
107 }
108 errorStr = QString(); // clear error
109 return setEncoding(ce);
110}
111
112bool QDecompressHelper::setEncoding(ContentEncoding ce)
113{
114 Q_ASSERT(contentEncoding == None);
115 contentEncoding = ce;
116 switch (contentEncoding) {
117 case None:
118 Q_UNREACHABLE();
119 break;
120 case Deflate:
121 case GZip: {
122 z_stream *inflateStream = new z_stream;
123 memset(inflateStream, 0, sizeof(z_stream));
124 // "windowBits can also be greater than 15 for optional gzip decoding.
125 // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
126 // http://www.zlib.net/manual.html
127 if (inflateInit2(inflateStream, MAX_WBITS + 32) != Z_OK) {
128 delete inflateStream;
129 inflateStream = nullptr;
130 }
131 decoderPointer = inflateStream;
132 break;
133 }
134 case Brotli:
135#if QT_CONFIG(brotli)
136 decoderPointer = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
137#else
138 Q_UNREACHABLE();
139#endif
140 break;
141 case Zstandard:
142#if QT_CONFIG(zstd)
143 decoderPointer = ZSTD_createDStream();
144#else
145 Q_UNREACHABLE();
146#endif
147 break;
148 }
149 if (!decoderPointer) {
150 errorStr = QCoreApplication::translate("QHttp",
151 "Failed to initialize the compression decoder.");
152 contentEncoding = QDecompressHelper::None;
153 return false;
154 }
155 return true;
156}
157
167{
168 return countDecompressed;
169}
170
185{
186 // These are a best-effort check to ensure that no data has already been processed before this
187 // gets enabled
188 Q_ASSERT(compressedDataBuffer.byteAmount() == 0);
189 Q_ASSERT(contentEncoding == None);
190 countDecompressed = shouldCount;
191}
192
207{
208 Q_ASSERT(countDecompressed);
209 // Use the 'totalUncompressedBytes' from the countHelper if it exceeds the amount of bytes
210 // that we know about.
211 auto totalUncompressed =
212 countHelper && countHelper->totalUncompressedBytes > totalUncompressedBytes
213 ? countHelper->totalUncompressedBytes
214 : totalUncompressedBytes;
215 return totalUncompressed - totalBytesRead;
216}
217
223{
224 return feed(QByteArray(data));
225}
226
237{
238 Q_ASSERT(contentEncoding != None);
239 totalCompressedBytes += data.size();
240 compressedDataBuffer.append(std::move(data));
241 if (!countInternal(compressedDataBuffer[compressedDataBuffer.bufferCount() - 1]))
242 clear(); // If our counting brother failed then so will we :|
243}
244
250{
251 Q_ASSERT(contentEncoding != None);
252 totalCompressedBytes += buffer.byteAmount();
253 compressedDataBuffer.append(buffer);
254 if (!countInternal(buffer))
255 clear(); // If our counting brother failed then so will we :|
256}
257
263{
264 Q_ASSERT(contentEncoding != None);
265 totalCompressedBytes += buffer.byteAmount();
267 compressedDataBuffer.append(std::move(buffer));
268 if (!countInternal(copy))
269 clear(); // If our counting brother failed then so will we :|
270}
271
286bool QDecompressHelper::countInternal()
287{
288 Q_ASSERT(countDecompressed);
289 while (hasDataInternal()
290 && decompressedDataBuffer.byteAmount() < MaxDecompressedDataBufferSize) {
291 const qsizetype toRead = 256 * 1024;
293 qsizetype bytesRead = readInternal(buffer.data(), buffer.size());
294 if (bytesRead == -1)
295 return false;
296 buffer.truncate(bytesRead);
297 decompressedDataBuffer.append(std::move(buffer));
298 }
299 if (!hasDataInternal())
300 return true; // handled all the data so far, just return
301
302 while (countHelper->hasData()) {
303 std::array<char, 1024> temp;
304 qsizetype bytesRead = countHelper->read(temp.data(), temp.size());
305 if (bytesRead == -1)
306 return false;
307 }
308 return true;
309}
310
315bool QDecompressHelper::countInternal(const QByteArray &data)
316{
317 if (countDecompressed) {
318 if (!countHelper) {
319 countHelper = std::make_unique<QDecompressHelper>();
320 countHelper->setDecompressedSafetyCheckThreshold(archiveBombCheckThreshold);
321 countHelper->setEncoding(contentEncoding);
322 }
323 countHelper->feed(data);
324 return countInternal();
325 }
326 return true;
327}
328
333bool QDecompressHelper::countInternal(const QByteDataBuffer &buffer)
334{
335 if (countDecompressed) {
336 if (!countHelper) {
337 countHelper = std::make_unique<QDecompressHelper>();
338 countHelper->setDecompressedSafetyCheckThreshold(archiveBombCheckThreshold);
339 countHelper->setEncoding(contentEncoding);
340 }
341 countHelper->feed(buffer);
342 return countInternal();
343 }
344 return true;
345}
346
348{
349 if (maxSize <= 0)
350 return 0;
351
352 if (!isValid())
353 return -1;
354
355 if (!hasData())
356 return 0;
357
358 qsizetype cachedRead = 0;
359 if (!decompressedDataBuffer.isEmpty()) {
360 cachedRead = decompressedDataBuffer.read(data, maxSize);
361 data += cachedRead;
362 maxSize -= cachedRead;
363 }
364
365 qsizetype bytesRead = readInternal(data, maxSize);
366 if (bytesRead == -1)
367 return -1;
368 totalBytesRead += bytesRead + cachedRead;
369 return bytesRead + cachedRead;
370}
371
377qsizetype QDecompressHelper::readInternal(char *data, qsizetype maxSize)
378{
379 Q_ASSERT(isValid());
380
381 if (maxSize <= 0)
382 return 0;
383
384 if (!hasDataInternal())
385 return 0;
386
387 qsizetype bytesRead = -1;
388 switch (contentEncoding) {
389 case None:
390 Q_UNREACHABLE();
391 break;
392 case Deflate:
393 case GZip:
394 bytesRead = readZLib(data, maxSize);
395 break;
396 case Brotli:
397 bytesRead = readBrotli(data, maxSize);
398 break;
399 case Zstandard:
400 bytesRead = readZstandard(data, maxSize);
401 break;
402 }
403 if (bytesRead == -1)
404 clear();
405
406 totalUncompressedBytes += bytesRead;
407 if (isPotentialArchiveBomb()) {
409 "QHttp",
410 "The decompressed output exceeds the limits specified by "
411 "QNetworkRequest::decompressedSafetyCheckThreshold()");
412 return -1;
413 }
414
415 return bytesRead;
416}
417
425{
426 if (threshold == -1)
427 threshold = std::numeric_limits<qint64>::max();
428 archiveBombCheckThreshold = threshold;
429}
430
431bool QDecompressHelper::isPotentialArchiveBomb() const
432{
433 if (totalCompressedBytes == 0)
434 return false;
435
436 if (totalUncompressedBytes <= archiveBombCheckThreshold)
437 return false;
438
439 // Some protection against malicious or corrupted compressed files that expand far more than
440 // is reasonable.
441 double ratio = double(totalUncompressedBytes) / double(totalCompressedBytes);
442 switch (contentEncoding) {
443 case None:
444 Q_UNREACHABLE();
445 break;
446 case Deflate:
447 case GZip:
448 // This value is mentioned in docs for
449 // QNetworkRequest::setMinimumArchiveBombSize, keep synchronized
450 if (ratio > 40) {
451 return true;
452 }
453 break;
454 case Brotli:
455 case Zstandard:
456 // This value is mentioned in docs for
457 // QNetworkRequest::setMinimumArchiveBombSize, keep synchronized
458 if (ratio > 100) {
459 return true;
460 }
461 break;
462 }
463 return false;
464}
465
475{
476 return hasDataInternal() || !decompressedDataBuffer.isEmpty();
477}
478
484bool QDecompressHelper::hasDataInternal() const
485{
486 return encodedBytesAvailable() || decoderHasData;
487}
488
489qint64 QDecompressHelper::encodedBytesAvailable() const
490{
491 return compressedDataBuffer.byteAmount();
492}
493
502{
503 return contentEncoding != None;
504}
505
513{
514 return errorStr;
515}
516
518{
519 switch (contentEncoding) {
520 case None:
521 break;
522 case Deflate:
523 case GZip: {
524 z_stream *inflateStream = toZlibPointer(decoderPointer);
525 if (inflateStream)
526 inflateEnd(inflateStream);
527 delete inflateStream;
528 break;
529 }
530 case Brotli: {
531#if QT_CONFIG(brotli)
532 BrotliDecoderState *brotliDecoderState = toBrotliPointer(decoderPointer);
533 if (brotliDecoderState)
534 BrotliDecoderDestroyInstance(brotliDecoderState);
535#endif
536 break;
537 }
538 case Zstandard: {
539#if QT_CONFIG(zstd)
540 ZSTD_DStream *zstdStream = toZstandardPointer(decoderPointer);
541 if (zstdStream)
542 ZSTD_freeDStream(zstdStream);
543#endif
544 break;
545 }
546 }
547 decoderPointer = nullptr;
548 contentEncoding = None;
549
550 compressedDataBuffer.clear();
551 decompressedDataBuffer.clear();
552 decoderHasData = false;
553
554 countDecompressed = false;
555 countHelper.reset();
556 totalBytesRead = 0;
557 totalUncompressedBytes = 0;
558 totalCompressedBytes = 0;
559
560 errorStr.clear();
561}
562
563qsizetype QDecompressHelper::readZLib(char *data, const qsizetype maxSize)
564{
565 bool triedRawDeflate = false;
566
567 z_stream *inflateStream = toZlibPointer(decoderPointer);
568 static const size_t zlibMaxSize =
569 size_t(std::numeric_limits<decltype(inflateStream->avail_in)>::max());
570
571 QByteArrayView input = compressedDataBuffer.readPointer();
572 if (size_t(input.size()) > zlibMaxSize)
573 input = input.sliced(zlibMaxSize);
574
575 inflateStream->avail_in = input.size();
576 inflateStream->next_in = reinterpret_cast<Bytef *>(const_cast<char *>(input.data()));
577
578 bool bigMaxSize = (zlibMaxSize < size_t(maxSize));
579 qsizetype adjustedAvailableOut = bigMaxSize ? qsizetype(zlibMaxSize) : maxSize;
580 inflateStream->avail_out = adjustedAvailableOut;
581 inflateStream->next_out = reinterpret_cast<Bytef *>(data);
582
583 qsizetype bytesDecoded = 0;
584 do {
585 auto previous_avail_out = inflateStream->avail_out;
586 int ret = inflate(inflateStream, Z_NO_FLUSH);
587 // All negative return codes are errors, in the context of HTTP compression, Z_NEED_DICT is
588 // also an error.
589 // in the case where we get Z_DATA_ERROR this could be because we received raw deflate
590 // compressed data.
591 if (ret == Z_DATA_ERROR && !triedRawDeflate) {
592 inflateEnd(inflateStream);
593 triedRawDeflate = true;
594 inflateStream->zalloc = Z_NULL;
595 inflateStream->zfree = Z_NULL;
596 inflateStream->opaque = Z_NULL;
597 inflateStream->avail_in = 0;
598 inflateStream->next_in = Z_NULL;
599 int ret = inflateInit2(inflateStream, -MAX_WBITS);
600 if (ret != Z_OK) {
601 return -1;
602 } else {
603 inflateStream->avail_in = input.size();
604 inflateStream->next_in =
605 reinterpret_cast<Bytef *>(const_cast<char *>(input.data()));
606 continue;
607 }
608 } else if (ret < 0 || ret == Z_NEED_DICT) {
609 return -1;
610 }
611 bytesDecoded += qsizetype(previous_avail_out - inflateStream->avail_out);
612 if (ret == Z_STREAM_END) {
613
614 // If there's more data after the stream then this is probably composed of multiple
615 // streams.
616 if (inflateStream->avail_in != 0) {
617 inflateEnd(inflateStream);
618 Bytef *next_in = inflateStream->next_in;
619 uInt avail_in = inflateStream->avail_in;
620 inflateStream->zalloc = Z_NULL;
621 inflateStream->zfree = Z_NULL;
622 inflateStream->opaque = Z_NULL;
623 if (inflateInit2(inflateStream, MAX_WBITS + 32) != Z_OK) {
624 delete inflateStream;
625 decoderPointer = nullptr;
626 // Failed to reinitialize, so we'll just return what we have
627 compressedDataBuffer.advanceReadPointer(input.size() - avail_in);
628 return bytesDecoded;
629 } else {
630 inflateStream->next_in = next_in;
631 inflateStream->avail_in = avail_in;
632 // Keep going to handle the other cases below
633 }
634 } else {
635 // No extra data, stream is at the end. We're done.
636 compressedDataBuffer.advanceReadPointer(input.size());
637 return bytesDecoded;
638 }
639 }
640
641 if (bigMaxSize && inflateStream->avail_out == 0) {
642 // Need to adjust the next_out and avail_out parameters since we reached the end
643 // of the current range
644 bigMaxSize = (zlibMaxSize < size_t(maxSize - bytesDecoded));
645 inflateStream->avail_out = bigMaxSize ? qsizetype(zlibMaxSize) : maxSize - bytesDecoded;
646 inflateStream->next_out = reinterpret_cast<Bytef *>(data + bytesDecoded);
647 }
648
649 if (inflateStream->avail_in == 0 && inflateStream->avail_out > 0) {
650 // Grab the next input!
651 compressedDataBuffer.advanceReadPointer(input.size());
652 input = compressedDataBuffer.readPointer();
653 if (size_t(input.size()) > zlibMaxSize)
654 input = input.sliced(zlibMaxSize);
655 inflateStream->avail_in = input.size();
656 inflateStream->next_in =
657 reinterpret_cast<Bytef *>(const_cast<char *>(input.data()));
658 }
659 } while (inflateStream->avail_out > 0 && inflateStream->avail_in > 0);
660
661 compressedDataBuffer.advanceReadPointer(input.size() - inflateStream->avail_in);
662
663 return bytesDecoded;
664}
665
666qsizetype QDecompressHelper::readBrotli(char *data, const qsizetype maxSize)
667{
668#if !QT_CONFIG(brotli)
669 Q_UNUSED(data);
670 Q_UNUSED(maxSize);
671 Q_UNREACHABLE();
672#else
673 qint64 bytesDecoded = 0;
674
675 BrotliDecoderState *brotliDecoderState = toBrotliPointer(decoderPointer);
676
677 while (decoderHasData && bytesDecoded < maxSize) {
678 Q_ASSERT(brotliUnconsumedDataPtr || BrotliDecoderHasMoreOutput(brotliDecoderState));
679 if (brotliUnconsumedDataPtr) {
680 Q_ASSERT(brotliUnconsumedAmount);
681 size_t toRead = std::min(size_t(maxSize - bytesDecoded), brotliUnconsumedAmount);
682 memcpy(data + bytesDecoded, brotliUnconsumedDataPtr, toRead);
683 bytesDecoded += toRead;
684 brotliUnconsumedAmount -= toRead;
685 brotliUnconsumedDataPtr += toRead;
686 if (brotliUnconsumedAmount == 0) {
687 brotliUnconsumedDataPtr = nullptr;
688 decoderHasData = false;
689 }
690 }
691 if (BrotliDecoderHasMoreOutput(brotliDecoderState) == BROTLI_TRUE) {
692 brotliUnconsumedDataPtr =
693 BrotliDecoderTakeOutput(brotliDecoderState, &brotliUnconsumedAmount);
694 decoderHasData = true;
695 }
696 }
697 if (bytesDecoded == maxSize)
698 return bytesDecoded;
699 Q_ASSERT(bytesDecoded < maxSize);
700
701 QByteArrayView input = compressedDataBuffer.readPointer();
702 const uint8_t *encodedPtr = reinterpret_cast<const uint8_t *>(input.data());
703 size_t encodedBytesRemaining = input.size();
704
705 uint8_t *decodedPtr = reinterpret_cast<uint8_t *>(data + bytesDecoded);
706 size_t unusedDecodedSize = size_t(maxSize - bytesDecoded);
707 while (unusedDecodedSize > 0) {
708 auto previousUnusedDecodedSize = unusedDecodedSize;
709 BrotliDecoderResult result = BrotliDecoderDecompressStream(
710 brotliDecoderState, &encodedBytesRemaining, &encodedPtr, &unusedDecodedSize,
711 &decodedPtr, nullptr);
712 bytesDecoded += previousUnusedDecodedSize - unusedDecodedSize;
713
714 switch (result) {
715 case BROTLI_DECODER_RESULT_ERROR:
716 errorStr = QLatin1String("Brotli error: %1")
717 .arg(QString::fromUtf8(BrotliDecoderErrorString(
718 BrotliDecoderGetErrorCode(brotliDecoderState))));
719 return -1;
720 case BROTLI_DECODER_RESULT_SUCCESS:
721 BrotliDecoderDestroyInstance(brotliDecoderState);
722 decoderPointer = nullptr;
723 compressedDataBuffer.clear();
724 return bytesDecoded;
725 case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
726 compressedDataBuffer.advanceReadPointer(input.size());
727 input = compressedDataBuffer.readPointer();
728 if (!input.isEmpty()) {
729 encodedPtr = reinterpret_cast<const uint8_t *>(input.constData());
730 encodedBytesRemaining = input.size();
731 break;
732 }
733 return bytesDecoded;
734 case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
735 // Some data is leftover inside the brotli decoder, remember for next time
736 decoderHasData = BrotliDecoderHasMoreOutput(brotliDecoderState);
737 Q_ASSERT(unusedDecodedSize == 0);
738 break;
739 }
740 }
741 compressedDataBuffer.advanceReadPointer(input.size() - encodedBytesRemaining);
742 return bytesDecoded;
743#endif
744}
745
746qsizetype QDecompressHelper::readZstandard(char *data, const qsizetype maxSize)
747{
748#if !QT_CONFIG(zstd)
749 Q_UNUSED(data);
750 Q_UNUSED(maxSize);
751 Q_UNREACHABLE();
752#else
753 ZSTD_DStream *zstdStream = toZstandardPointer(decoderPointer);
754
755 QByteArrayView input = compressedDataBuffer.readPointer();
756 ZSTD_inBuffer inBuf { input.data(), size_t(input.size()), 0 };
757
758 ZSTD_outBuffer outBuf { data, size_t(maxSize), 0 };
759
760 qsizetype bytesDecoded = 0;
761 while (outBuf.pos < outBuf.size && (inBuf.pos < inBuf.size || decoderHasData)) {
762 size_t retValue = ZSTD_decompressStream(zstdStream, &outBuf, &inBuf);
763 if (ZSTD_isError(retValue)) {
764 errorStr = QLatin1String("ZStandard error: %1")
765 .arg(QString::fromUtf8(ZSTD_getErrorName(retValue)));
766 return -1;
767 } else {
768 decoderHasData = false;
769 bytesDecoded = outBuf.pos;
770 // if pos == size then there may be data left over in internal buffers
771 if (outBuf.pos == outBuf.size) {
772 decoderHasData = true;
773 } else if (inBuf.pos == inBuf.size) {
774 compressedDataBuffer.advanceReadPointer(input.size());
775 input = compressedDataBuffer.readPointer();
776 inBuf = { input.constData(), size_t(input.size()), 0 };
777 }
778 }
779 }
780 compressedDataBuffer.advanceReadPointer(inBuf.pos);
781 return bytesDecoded;
782#endif
783}
784
\inmodule QtCore
constexpr QByteArrayView sliced(qsizetype pos) const
constexpr qsizetype size() const noexcept
constexpr const_pointer data() const noexcept
constexpr const_pointer constData() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
bool isEmpty() const
void append(const QByteDataBuffer &other)
Definition qbytedata_p.h:48
void advanceReadPointer(qint64 distance)
QByteArrayView readPointer() const
qsizetype bufferCount() const
QByteArray read()
qint64 byteAmount() const
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
void setCountingBytesEnabled(bool shouldCount)
void feed(const QByteArray &data)
qint64 uncompressedSize() const
static QByteArrayList acceptedEncoding()
bool setEncoding(const QByteArray &contentEncoding)
qsizetype read(char *data, qsizetype maxSize)
void setDecompressedSafetyCheckThreshold(qint64 threshold)
static bool isSupportedEncoding(const QByteArray &encoding)
QString errorString() const
QString arg(Args &&...args) const
void reserve(qsizetype size)
Definition qlist.h:746
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
Combined button and popup list for selecting options.
constexpr ContentEncodingMapping contentEncodingMapping[]
QDecompressHelper::ContentEncoding encodingFromByteArray(const QByteArray &ce) noexcept
z_stream * toZlibPointer(void *ptr)
@ CaseInsensitive
constexpr Initialization Uninitialized
static jboolean copy(JNIEnv *, jobject)
#define qWarning
Definition qlogging.h:162
return ret
static ControlElement< T > * ptr(QWidget *widget)
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLuint64EXT * result
[6]
GLenum GLenum GLenum GLenum mapping
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
Definition qzip.cpp:92
QList< int > list
[14]
QDecompressHelper::ContentEncoding encoding