summaryrefslogtreecommitdiffstats
path: root/src/network/access/qdecompresshelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qdecompresshelper.cpp')
-rw-r--r--src/network/access/qdecompresshelper.cpp94
1 files changed, 77 insertions, 17 deletions
diff --git a/src/network/access/qdecompresshelper.cpp b/src/network/access/qdecompresshelper.cpp
index 451684bf1b..f3227efe8a 100644
--- a/src/network/access/qdecompresshelper.cpp
+++ b/src/network/access/qdecompresshelper.cpp
@@ -237,7 +237,13 @@ void QDecompressHelper::setCountingBytesEnabled(bool shouldCount)
qint64 QDecompressHelper::uncompressedSize() const
{
Q_ASSERT(countDecompressed);
- return uncompressedBytes;
+ // Use the 'totalUncompressedBytes' from the countHelper if it exceeds the amount of bytes
+ // that we know about.
+ auto totalUncompressed =
+ countHelper && countHelper->totalUncompressedBytes > totalUncompressedBytes
+ ? countHelper->totalUncompressedBytes
+ : totalUncompressedBytes;
+ return totalUncompressed - totalBytesRead;
}
/*!
@@ -262,10 +268,9 @@ void QDecompressHelper::feed(QByteArray &&data)
{
Q_ASSERT(contentEncoding != None);
totalCompressedBytes += data.size();
- if (!countInternal(data))
+ compressedDataBuffer.append(std::move(data));
+ if (!countInternal(compressedDataBuffer[compressedDataBuffer.bufferCount() - 1]))
clear(); // If our counting brother failed then so will we :|
- else
- compressedDataBuffer.append(std::move(data));
}
/*!
@@ -276,10 +281,9 @@ void QDecompressHelper::feed(const QByteDataBuffer &buffer)
{
Q_ASSERT(contentEncoding != None);
totalCompressedBytes += buffer.byteAmount();
+ compressedDataBuffer.append(buffer);
if (!countInternal(buffer))
clear(); // If our counting brother failed then so will we :|
- else
- compressedDataBuffer.append(buffer);
}
/*!
@@ -290,10 +294,10 @@ void QDecompressHelper::feed(QByteDataBuffer &&buffer)
{
Q_ASSERT(contentEncoding != None);
totalCompressedBytes += buffer.byteAmount();
- if (!countInternal(buffer))
+ const QByteDataBuffer copy(buffer);
+ compressedDataBuffer.append(std::move(buffer));
+ if (!countInternal(copy))
clear(); // If our counting brother failed then so will we :|
- else
- compressedDataBuffer.append(std::move(buffer));
}
/*!
@@ -303,19 +307,34 @@ void QDecompressHelper::feed(QByteDataBuffer &&buffer)
This lets us know the final size, unfortunately at the cost of
increased computation.
- Potential @future improvement:
- Decompress XX MiB/KiB before starting the count.
- For smaller files the extra decompression can then be avoided.
+ To save on some of the computation we will store the data until
+ we reach \c MaxDecompressedDataBufferSize stored. In this case the
+ "penalty" is completely removed from users who read the data on
+ readyRead rather than waiting for it all to be received. And
+ any file smaller than \c MaxDecompressedDataBufferSize will
+ avoid this issue as well.
*/
bool QDecompressHelper::countInternal()
{
Q_ASSERT(countDecompressed);
+ while (hasDataInternal()
+ && decompressedDataBuffer.byteAmount() < MaxDecompressedDataBufferSize) {
+ const qsizetype toRead = 256 * 1024;
+ QByteArray buffer(toRead, Qt::Uninitialized);
+ qsizetype bytesRead = readInternal(buffer.data(), buffer.size());
+ if (bytesRead == -1)
+ return false;
+ buffer.truncate(bytesRead);
+ decompressedDataBuffer.append(std::move(buffer));
+ }
+ if (!hasDataInternal())
+ return true; // handled all the data so far, just return
+
while (countHelper->hasData()) {
std::array<char, 1024> temp;
qsizetype bytesRead = countHelper->read(temp.data(), temp.size());
if (bytesRead == -1)
return false;
- uncompressedBytes += bytesRead;
}
return true;
}
@@ -358,13 +377,45 @@ bool QDecompressHelper::countInternal(const QByteDataBuffer &buffer)
qsizetype QDecompressHelper::read(char *data, qsizetype maxSize)
{
+ if (maxSize <= 0)
+ return 0;
+
if (!isValid())
return -1;
- qsizetype bytesRead = -1;
if (!hasData())
return 0;
+ qsizetype cachedRead = 0;
+ if (!decompressedDataBuffer.isEmpty()) {
+ cachedRead = decompressedDataBuffer.read(data, maxSize);
+ data += cachedRead;
+ maxSize -= cachedRead;
+ }
+
+ qsizetype bytesRead = readInternal(data, maxSize);
+ if (bytesRead == -1)
+ return -1;
+ totalBytesRead += bytesRead + cachedRead;
+ return bytesRead + cachedRead;
+}
+
+/*!
+ \internal
+ Like read() but without attempting to read the
+ cached/already-decompressed data.
+*/
+qsizetype QDecompressHelper::readInternal(char *data, qsizetype maxSize)
+{
+ Q_ASSERT(isValid());
+
+ if (maxSize <= 0)
+ return 0;
+
+ if (!hasDataInternal())
+ return 0;
+
+ qsizetype bytesRead = -1;
switch (contentEncoding) {
case None:
Q_UNREACHABLE();
@@ -382,8 +433,6 @@ qsizetype QDecompressHelper::read(char *data, qsizetype maxSize)
}
if (bytesRead == -1)
clear();
- else if (countDecompressed)
- uncompressedBytes -= bytesRead;
totalUncompressedBytes += bytesRead;
if (isPotentialArchiveBomb())
@@ -450,6 +499,16 @@ bool QDecompressHelper::isPotentialArchiveBomb() const
*/
bool QDecompressHelper::hasData() const
{
+ return hasDataInternal() || !decompressedDataBuffer.isEmpty();
+}
+
+/*!
+ \internal
+ Like hasData() but internally the buffer of decompressed data is
+ not interesting.
+*/
+bool QDecompressHelper::hasDataInternal() const
+{
return encodedBytesAvailable() || decoderHasData;
}
@@ -497,11 +556,12 @@ void QDecompressHelper::clear()
contentEncoding = None;
compressedDataBuffer.clear();
+ decompressedDataBuffer.clear();
decoderHasData = false;
countDecompressed = false;
countHelper.reset();
- uncompressedBytes = 0;
+ totalBytesRead = 0;
totalUncompressedBytes = 0;
totalCompressedBytes = 0;
}