1 #include "localcache.h"
3 LocalCache *LocalCache::instance(const char *name) {
4 static QMap<QByteArray, LocalCache *> instances;
5 auto i = instances.constFind(QByteArray::fromRawData(name, strlen(name)));
6 if (i != instances.constEnd()) return i.value();
7 LocalCache *instance = new LocalCache(name);
8 instances.insert(instance->getName(), instance);
12 LocalCache::LocalCache(const QByteArray &name)
13 : name(name), maxSeconds(86400 * 30), maxSize(1024 * 1024 * 100), size(0), insertCount(0) {
14 directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') +
15 QLatin1String(name) + QLatin1Char('/');
16 #ifndef QT_NO_DEBUG_OUTPUT
22 LocalCache::~LocalCache() {
23 #ifndef QT_NO_DEBUG_OUTPUT
28 QByteArray LocalCache::hash(const QByteArray &s) {
29 QCryptographicHash hash(QCryptographicHash::Sha1);
31 const QByteArray h = QByteArray::number(*(qlonglong *)hash.result().constData(), 36);
32 static const char sep('/');
34 p.reserve(h.length() + 2);
39 p.append(h.constData() + 2, strlen(h.constData()) - 2); // p.append(h.mid(2));
43 bool LocalCache::isCached(const QString &path) {
44 bool cached = QFile::exists(path) &&
45 (maxSeconds == 0 || QFileInfo(path).birthTime().secsTo(
46 QDateTime::currentDateTimeUtc()) < maxSeconds);
47 #ifndef QT_NO_DEBUG_OUTPUT
48 if (!cached) misses++;
53 QByteArray LocalCache::value(const QByteArray &key) {
54 const QString path = cachePath(key);
55 if (!isCached(path)) return QByteArray();
58 if (!file.open(QIODevice::ReadOnly)) {
59 qWarning() << file.fileName() << file.errorString();
60 #ifndef QT_NO_DEBUG_OUTPUT
65 #ifndef QT_NO_DEBUG_OUTPUT
68 return file.readAll();
71 QByteArray LocalCache::possiblyStaleValue(const QByteArray &key) {
72 const QString path = cachePath(key);
74 if (!file.open(QIODevice::ReadOnly)) {
75 #ifndef QT_NO_DEBUG_OUTPUT
80 #ifndef QT_NO_DEBUG_OUTPUT
83 return file.readAll();
86 void LocalCache::insert(const QByteArray &key, const QByteArray &value) {
87 qDebug() << "Inserting" << key;
88 const QString path = cachePath(key);
89 const QString parentDir = path.left(path.lastIndexOf(QLatin1Char('/')));
90 if (!QFile::exists(parentDir)) {
91 QDir().mkpath(parentDir);
94 if (!file.open(QIODevice::WriteOnly)) {
95 qWarning() << "Cannot create" << path;
100 if (size > 0) size += value.size();
102 // expire cache every n inserts
103 if (maxSize > 0 && ++insertCount % 100 == 0) {
104 if (size == 0 || size > maxSize) expire();
108 void LocalCache::clear() {
109 #ifndef QT_NO_DEBUG_OUTPUT
116 QDir(directory).removeRecursively();
120 QString LocalCache::cachePath(const QByteArray &key) const {
121 return directory + QLatin1String(key);
124 void LocalCache::expire() {
125 if (!mutex.tryLock()) return;
127 QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot;
128 QDirIterator it(directory, filters, QDirIterator::Subdirectories);
130 QMultiMap<QDateTime, QString> cacheItems;
131 qint64 totalSize = 0;
132 while (it.hasNext()) {
133 QString path = it.next();
134 QFileInfo info = it.fileInfo();
135 cacheItems.insert(info.birthTime(), path);
136 totalSize += info.size();
137 qApp->processEvents();
140 int removedFiles = 0;
141 qint64 goal = (maxSize * 9) / 10;
142 auto i = cacheItems.constBegin();
143 while (i != cacheItems.constEnd()) {
144 if (totalSize < goal) break;
145 QString name = i.value();
147 qint64 size = file.size();
152 qApp->processEvents();
154 #ifndef QT_NO_DEBUG_OUTPUT
156 if (removedFiles > 0) {
157 qDebug() << "Removed:" << removedFiles << "Kept:" << cacheItems.count() - removedFiles
158 << "New Size:" << totalSize;
166 #ifndef QT_NO_DEBUG_OUTPUT
167 void LocalCache::debugStats() {
168 int total = hits + misses;
170 qDebug() << "Cache:" << name << '\n'
171 << "Inserts:" << insertCount << '\n'
172 << "Requests:" << total << '\n'
173 << "Hits:" << hits << (hits * 100) / total << "%\n"
174 << "Misses:" << misses << (misses * 100) / total << "%";