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), expiring(false),
15 directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') +
16 QLatin1String(name) + QLatin1Char('/');
17 #ifndef QT_NO_DEBUG_OUTPUT
23 LocalCache::~LocalCache() {
24 #ifndef QT_NO_DEBUG_OUTPUT
29 QByteArray LocalCache::hash(const QByteArray &s) {
30 QCryptographicHash hash(QCryptographicHash::Sha1);
32 const QByteArray h = QByteArray::number(*(qlonglong *)hash.result().constData(), 36);
33 static const char sep('/');
35 p.reserve(h.length() + 2);
40 p.append(h.constData() + 2, strlen(h.constData()) - 2); // p.append(h.mid(2));
44 bool LocalCache::isCached(const QString &path) {
45 bool cached = (QFile::exists(path) &&
47 QDateTime::currentDateTime().toTime_t() - QFileInfo(path).created().toTime_t() <
49 #ifndef QT_NO_DEBUG_OUTPUT
50 if (!cached) misses++;
55 QByteArray LocalCache::value(const QByteArray &key) {
56 const QString path = cachePath(key);
57 if (!isCached(path)) return QByteArray();
60 if (!file.open(QIODevice::ReadOnly)) {
61 qWarning() << __PRETTY_FUNCTION__ << file.fileName() << file.errorString();
62 #ifndef QT_NO_DEBUG_OUTPUT
67 #ifndef QT_NO_DEBUG_OUTPUT
70 return file.readAll();
73 void LocalCache::insert(const QByteArray &key, const QByteArray &value) {
74 const QueueItem item = {key, value};
75 insertQueue.append(item);
76 QTimer::singleShot(0, [this]() {
77 if (insertQueue.isEmpty()) return;
78 for (const auto &item : insertQueue) {
79 const QString path = cachePath(item.key);
80 const QString parentDir = path.left(path.lastIndexOf('/'));
81 if (!QFile::exists(parentDir)) {
82 QDir().mkpath(parentDir);
85 if (!file.open(QIODevice::WriteOnly)) {
86 qWarning() << "Cannot create" << path;
89 file.write(item.value);
91 if (size > 0) size += item.value.size();
95 // expire cache every n inserts
96 if (maxSize > 0 && ++insertCount % 100 == 0) {
97 if (size == 0 || size > maxSize) size = expire();
102 bool LocalCache::clear() {
103 #ifndef QT_NO_DEBUG_OUTPUT
109 return QDir(directory).removeRecursively();
112 QString LocalCache::cachePath(const QByteArray &key) const {
113 return directory + QLatin1String(key.constData());
116 qint64 LocalCache::expire() {
117 if (expiring) return size;
120 QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot;
121 QDirIterator it(directory, filters, QDirIterator::Subdirectories);
123 QMultiMap<QDateTime, QString> cacheItems;
124 qint64 totalSize = 0;
125 while (it.hasNext()) {
126 QString path = it.next();
127 QFileInfo info = it.fileInfo();
128 cacheItems.insert(info.created(), path);
129 totalSize += info.size();
130 qApp->processEvents();
133 int removedFiles = 0;
134 qint64 goal = (maxSize * 9) / 10;
135 auto i = cacheItems.constBegin();
136 while (i != cacheItems.constEnd()) {
137 if (totalSize < goal) break;
138 QString name = i.value();
140 qint64 size = file.size();
145 qApp->processEvents();
147 #ifndef QT_NO_DEBUG_OUTPUT
149 if (removedFiles > 0) {
150 qDebug() << "Removed:" << removedFiles << "Kept:" << cacheItems.count() - removedFiles
151 << "New Size:" << totalSize;
160 #ifndef QT_NO_DEBUG_OUTPUT
161 void LocalCache::debugStats() {
162 int total = hits + misses;
164 qDebug() << "Cache:" << name << '\n'
165 << "Inserts:" << insertCount << '\n'
166 << "Requests:" << total << '\n'
167 << "Hits:" << hits << (hits * 100) / total << "%\n"
168 << "Misses:" << misses << (misses * 100) / total << "%";