diff --git a/src/widgets/accessmanagerreply_p.h b/src/widgets/accessmanagerreply_p.h --- a/src/widgets/accessmanagerreply_p.h +++ b/src/widgets/accessmanagerreply_p.h @@ -96,6 +96,7 @@ private: QByteArray m_data; + qint64 m_offset; bool m_metaDataRead; bool m_ignoreContentDisposition; bool m_emitReadyReadOnMetaDataChange; diff --git a/src/widgets/accessmanagerreply_p.cpp b/src/widgets/accessmanagerreply_p.cpp --- a/src/widgets/accessmanagerreply_p.cpp +++ b/src/widgets/accessmanagerreply_p.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #define QL1S(x) QLatin1String(x) @@ -46,6 +47,7 @@ bool emitReadyReadOnMetaDataChange, QObject *parent) : QNetworkReply(parent), + m_offset(0), m_metaDataRead(false), m_ignoreContentDisposition(false), m_emitReadyReadOnMetaDataChange(emitReadyReadOnMetaDataChange), @@ -84,6 +86,7 @@ QObject *parent) : QNetworkReply(parent), m_data(data), + m_offset(0), m_ignoreContentDisposition(false), m_emitReadyReadOnMetaDataChange(false) { @@ -106,7 +109,8 @@ QNetworkReply::NetworkError errorCode, const QString &errorMessage, QObject *parent) - : QNetworkReply(parent) + : QNetworkReply(parent), + m_offset(0) { setRequest(request); setOpenMode(QIODevice::ReadOnly); @@ -131,21 +135,31 @@ } m_kioJob.clear(); m_data.clear(); + m_offset = 0; m_metaDataRead = false; } qint64 AccessManagerReply::bytesAvailable() const { - return (QNetworkReply::bytesAvailable() + m_data.length()); + return (QNetworkReply::bytesAvailable() + m_data.length() - m_offset); } qint64 AccessManagerReply::readData(char *data, qint64 maxSize) { - const qint64 length = qMin(qint64(m_data.length()), maxSize); + const qint64 length = qMin(qint64(m_data.length() - m_offset), maxSize); + + if (length <= 0) { + return 0; + } - if (length) { - memcpy(data, m_data.constData(), length); - m_data.remove(0, length); + memcpy(data, m_data.constData() + m_offset, length); + m_offset += length; + // Remove the stale data before m_offset only if it grows to half + // of the total size, to avoid calling the expensive .remove function. + // If there's no data left, it's a trivial remove. + if (m_data.length() == m_offset || m_offset * 2 >= m_data.length()) { + m_data.remove(0, m_offset); + m_offset = 0; } return length; @@ -395,10 +409,32 @@ void AccessManagerReply::slotData(KIO::Job *kioJob, const QByteArray &data) { Q_UNUSED(kioJob); - m_data += data; - if (!data.isEmpty()) { - emit readyRead(); + if (data.isEmpty()) { + return; } + + qint64 newSizeWithOffset = m_data.size() + data.size(); + if (newSizeWithOffset <= m_data.capacity()) { + // Already enough space + } else if (newSizeWithOffset - m_offset <= m_data.capacity()) { + // We get enough space with ::remove. + m_data.remove(0, m_offset); + m_offset = 0; + } else { + // We have to resize the array, which implies an expensive memmove. + // Do it ourselves to save m_offset bytes. + QByteArray newData; + // Leave some free space to avoid that every slotData call results in + // a reallocation. qNextPowerOfTwo is what QByteArray does internally. + newData.reserve(qNextPowerOfTwo(newSizeWithOffset - m_offset)); + newData.append(m_data.constData() + m_offset, m_data.size() - m_offset); + m_data = newData; + m_offset = 0; + } + + m_data += data; + + emit readyRead(); } void AccessManagerReply::slotMimeType(KIO::Job *kioJob, const QString &mimeType)