summaryrefslogtreecommitdiffstats
path: root/src/network/access/qnetworkdiskcache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qnetworkdiskcache.cpp')
-rw-r--r--src/network/access/qnetworkdiskcache.cpp210
1 files changed, 76 insertions, 134 deletions
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index 1ee075cb71..c883a61886 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNETWORKDISKCACHE_DEBUG
@@ -48,20 +12,23 @@
#include <qdir.h>
#include <qdatastream.h>
#include <qdatetime.h>
-#include <qdiriterator.h>
+#include <qdirlisting.h>
#include <qurl.h>
#include <qcryptographichash.h>
#include <qdebug.h>
-#define CACHE_POSTFIX QLatin1String(".d")
-#define PREPARED_SLASH QLatin1String("prepared/")
+#include <memory>
+
+#define CACHE_POSTFIX ".d"_L1
#define CACHE_VERSION 8
-#define DATA_DIR QLatin1String("data")
+#define DATA_DIR "data"_L1
#define MAX_COMPRESSION_SIZE (1024 * 1024 * 3)
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QNetworkDiskCache
\since 4.5
@@ -145,10 +112,10 @@ void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
d->cacheDirectory = cacheDir;
QDir dir(d->cacheDirectory);
d->cacheDirectory = dir.absolutePath();
- if (!d->cacheDirectory.endsWith(QLatin1Char('/')))
- d->cacheDirectory += QLatin1Char('/');
+ if (!d->cacheDirectory.endsWith(u'/'))
+ d->cacheDirectory += u'/';
- d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + QLatin1Char('/');
+ d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + u'/';
d->prepareLayout();
}
@@ -196,7 +163,7 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
break;
}
}
- QScopedPointer<QCacheItem> cacheItem(new QCacheItem);
+ std::unique_ptr<QCacheItem> cacheItem = std::make_unique<QCacheItem>();
cacheItem->metaData = metaData;
QIODevice *device = nullptr;
@@ -204,13 +171,9 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
cacheItem->data.open(QBuffer::ReadWrite);
device = &(cacheItem->data);
} else {
- QString templateName = d->tmpCacheFileName();
- QT_TRY {
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- } QT_CATCH(...) {
- cacheItem->file = nullptr;
- }
- if (!cacheItem->file || !cacheItem->file->open()) {
+ QString fileName = d->cacheFileName(cacheItem->metaData.url());
+ cacheItem->file = new(std::nothrow) QSaveFile(fileName, &cacheItem->data);
+ if (!cacheItem->file || !cacheItem->file->open(QFileDevice::WriteOnly)) {
qWarning("QNetworkDiskCache::prepare() unable to open temporary file");
cacheItem.reset();
return nullptr;
@@ -218,7 +181,7 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
cacheItem->writeHeader(cacheItem->file);
device = cacheItem->file;
}
- d->inserting[device] = cacheItem.take();
+ d->inserting[device] = cacheItem.release();
return device;
}
@@ -250,7 +213,6 @@ void QNetworkDiskCache::insert(QIODevice *device)
void QNetworkDiskCachePrivate::prepareLayout()
{
QDir helper;
- helper.mkpath(cacheDirectory + PREPARED_SLASH);
//Create directory and subdirectories 0-F
helper.mkpath(dataDirectory);
@@ -271,19 +233,16 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
Q_ASSERT(!fileName.isEmpty());
if (QFile::exists(fileName)) {
- if (!QFile::remove(fileName)) {
+ if (!removeFile(fileName)) {
qWarning() << "QNetworkDiskCache: couldn't remove the cache file " << fileName;
return;
}
}
- if (currentCacheSize > 0)
- currentCacheSize += 1024 + cacheItem->size();
currentCacheSize = q->expire();
if (!cacheItem->file) {
- QString templateName = tmpCacheFileName();
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- if (cacheItem->file->open()) {
+ cacheItem->file = new QSaveFile(fileName, &cacheItem->data);
+ if (cacheItem->file->open(QFileDevice::WriteOnly)) {
cacheItem->writeHeader(cacheItem->file);
cacheItem->writeCompressedData(cacheItem->file);
}
@@ -291,13 +250,15 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
if (cacheItem->file
&& cacheItem->file->isOpen()
- && cacheItem->file->error() == QFile::NoError) {
- cacheItem->file->setAutoRemove(false);
- // ### use atomic rename rather then remove & rename
- if (cacheItem->file->rename(fileName))
- currentCacheSize += cacheItem->file->size();
- else
- cacheItem->file->setAutoRemove(true);
+ && cacheItem->file->error() == QFileDevice::NoError) {
+ // We have to call size() here instead of inside the if-body because
+ // commit() invalidates the file-engine, and size() will create a new
+ // one, pointing at an empty filename.
+ qint64 size = cacheItem->file->size();
+ if (cacheItem->file->commit())
+ currentCacheSize += size;
+ // Delete and unset the QSaveFile, it's invalid now.
+ delete std::exchange(cacheItem->file, nullptr);
}
if (cacheItem->metaData.url() == lastItem.metaData.url())
lastItem.reset();
@@ -395,7 +356,7 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
qDebug() << "QNetworkDiskCache::data()" << url;
#endif
Q_D(QNetworkDiskCache);
- QScopedPointer<QBuffer> buffer;
+ std::unique_ptr<QBuffer> buffer;
if (!url.isValid())
return nullptr;
if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
@@ -417,22 +378,11 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
buffer->setData(d->lastItem.data.data());
} else {
buffer.reset(new QBuffer);
- // ### verify that QFile uses the fd size and not the file name
- qint64 size = file->size() - file->pos();
- const uchar *p = nullptr;
-#if !defined(Q_OS_INTEGRITY)
- p = file->map(file->pos(), size);
-#endif
- if (p) {
- buffer->setData((const char *)p, size);
- file.take()->setParent(buffer.data());
- } else {
- buffer->setData(file->readAll());
- }
+ buffer->setData(file->readAll());
}
}
buffer->open(QBuffer::ReadOnly);
- return buffer.take();
+ return buffer.release();
}
/*!
@@ -527,47 +477,45 @@ qint64 QNetworkDiskCache::expire()
// close file handle to prevent "in use" error when QFile::remove() is called
d->lastItem.reset();
- QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
+ const QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QMultiMap<QDateTime, QString> cacheItems;
+ struct CacheItem
+ {
+ std::chrono::milliseconds msecs;
+ QString path;
+ qint64 size = 0;
+ };
+ std::vector<CacheItem> cacheItems;
qint64 totalSize = 0;
- while (it.hasNext()) {
- QString path = it.next();
- QFileInfo info = it.fileInfo();
- QString fileName = info.fileName();
- if (fileName.endsWith(CACHE_POSTFIX)) {
- const QDateTime birthTime = info.fileTime(QFile::FileBirthTime);
- cacheItems.insert(birthTime.isValid() ? birthTime
- : info.fileTime(QFile::FileMetadataChangeTime), path);
- totalSize += info.size();
- }
+ using F = QDirListing::IteratorFlag;
+ for (const auto &dirEntry : QDirListing(cacheDirectory(), filters, F::Recursive)) {
+ if (!dirEntry.fileName().endsWith(CACHE_POSTFIX))
+ continue;
+
+ const QFileInfo &info = dirEntry.fileInfo();
+ QDateTime fileTime = info.birthTime(QTimeZone::UTC);
+ if (!fileTime.isValid())
+ fileTime = info.metadataChangeTime(QTimeZone::UTC);
+ const std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
+ const qint64 size = info.size();
+ cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
+ totalSize += size;
}
- int removedFiles = 0;
- qint64 goal = (maximumCacheSize() * 9) / 10;
- QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
- while (i != cacheItems.constEnd()) {
- if (totalSize < goal)
- break;
- QString name = i.value();
- QFile file(name);
-
- if (name.contains(PREPARED_SLASH)) {
- for (QCacheItem *item : qAsConst(d->inserting)) {
- if (item && item->file && item->file->fileName() == name) {
- delete item->file;
- item->file = nullptr;
- break;
- }
- }
- }
+ const qint64 goal = (maximumCacheSize() * 9) / 10;
+ if (totalSize < goal)
+ return totalSize; // Nothing to do
- qint64 size = file.size();
- file.remove();
- totalSize -= size;
+ auto byFileTime = [&](const auto &a, const auto &b) { return a.msecs < b.msecs; };
+ std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
+
+ [[maybe_unused]] int removedFiles = 0; // used under QNETWORKDISKCACHE_DEBUG
+ for (const CacheItem &cached : cacheItems) {
+ QFile::remove(cached.path);
++removedFiles;
- ++i;
+ totalSize -= cached.size;
+ if (totalSize < goal)
+ break;
}
#if defined(QNETWORKDISKCACHE_DEBUG)
if (removedFiles > 0) {
@@ -603,24 +551,16 @@ QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
cleanUrl.setPassword(QString());
cleanUrl.setFragment(QString());
- QCryptographicHash hash(QCryptographicHash::Sha1);
- hash.addData(cleanUrl.toEncoded());
+ const QByteArray hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
// convert sha1 to base36 form and return first 8 bytes for use as string
- const QByteArray id = QByteArray::number(*(qlonglong*)hash.result().constData(), 36).left(8);
- // generates <one-char subdir>/<8-char filname.d>
- uint code = (uint)id.at(id.length()-1) % 16;
- QString pathFragment = QString::number(code, 16) + QLatin1Char('/')
- + QLatin1String(id) + CACHE_POSTFIX;
+ const QByteArray id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
+ // generates <one-char subdir>/<8-char filename.d>
+ uint code = (uint)id.at(id.size()-1) % 16;
+ QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) + CACHE_POSTFIX;
return pathFragment;
}
-QString QNetworkDiskCachePrivate::tmpCacheFileName() const
-{
- //The subdirectory is presumed to be already read for use.
- return cacheDirectory + PREPARED_SLASH + QLatin1String("XXXXXX") + CACHE_POSTFIX;
-}
-
/*!
Generates fully qualified path of cached resource from a URL.
*/
@@ -671,7 +611,7 @@ enum
CurrentCacheVersion = CACHE_VERSION
};
-void QCacheItem::writeHeader(QFile *device) const
+void QCacheItem::writeHeader(QFileDevice *device) const
{
QDataStream out(device);
@@ -683,7 +623,7 @@ void QCacheItem::writeHeader(QFile *device) const
out << compressed;
}
-void QCacheItem::writeCompressedData(QFile *device) const
+void QCacheItem::writeCompressedData(QFileDevice *device) const
{
QDataStream out(device);
@@ -694,7 +634,7 @@ void QCacheItem::writeCompressedData(QFile *device) const
Returns \c false if the file is a cache file,
but is an older version and should be removed otherwise true.
*/
-bool QCacheItem::read(QFile *device, bool readData)
+bool QCacheItem::read(QFileDevice *device, bool readData)
{
reset();
@@ -733,7 +673,9 @@ bool QCacheItem::read(QFile *device, bool readData)
if (!device->fileName().endsWith(expectedFilename))
return false;
- return metaData.isValid();
+ return metaData.isValid() && !metaData.rawHeaders().isEmpty();
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkdiskcache.cpp"