]> git.sur5r.net Git - minitube/blob - lib/http/src/localcache.cpp
Update upstream source from tag 'upstream/3.6.1'
[minitube] / lib / http / src / localcache.cpp
1 #include "localcache.h"
2
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);
9     return instance;
10 }
11
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
17     hits = 0;
18     misses = 0;
19 #endif
20 }
21
22 LocalCache::~LocalCache() {
23 #ifndef QT_NO_DEBUG_OUTPUT
24     debugStats();
25 #endif
26 }
27
28 QByteArray LocalCache::hash(const QByteArray &s) {
29     QCryptographicHash hash(QCryptographicHash::Sha1);
30     hash.addData(s);
31     const QByteArray h = QByteArray::number(*(qlonglong *)hash.result().constData(), 36);
32     static const char sep('/');
33     QByteArray p;
34     p.reserve(h.length() + 2);
35     p.append(h.at(0));
36     p.append(sep);
37     p.append(h.at(1));
38     p.append(sep);
39     p.append(h.constData() + 2, strlen(h.constData()) - 2); // p.append(h.mid(2));
40     return p;
41 }
42
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++;
49 #endif
50     return cached;
51 }
52
53 QByteArray LocalCache::value(const QByteArray &key) {
54     const QString path = cachePath(key);
55     if (!isCached(path)) return QByteArray();
56
57     QFile file(path);
58     if (!file.open(QIODevice::ReadOnly)) {
59         qWarning() << file.fileName() << file.errorString();
60 #ifndef QT_NO_DEBUG_OUTPUT
61         misses++;
62 #endif
63         return QByteArray();
64     }
65 #ifndef QT_NO_DEBUG_OUTPUT
66     hits++;
67 #endif
68     return file.readAll();
69 }
70
71 QByteArray LocalCache::possiblyStaleValue(const QByteArray &key) {
72     const QString path = cachePath(key);
73     QFile file(path);
74     if (!file.open(QIODevice::ReadOnly)) {
75 #ifndef QT_NO_DEBUG_OUTPUT
76         misses++;
77 #endif
78         return QByteArray();
79     }
80 #ifndef QT_NO_DEBUG_OUTPUT
81     hits++;
82 #endif
83     return file.readAll();
84 }
85
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);
92     }
93     QFile file(path);
94     if (!file.open(QIODevice::WriteOnly)) {
95         qWarning() << "Cannot create" << path;
96         return;
97     }
98     file.write(value);
99     file.close();
100     if (size > 0) size += value.size();
101
102     // expire cache every n inserts
103     if (maxSize > 0 && ++insertCount % 100 == 0) {
104         if (size == 0 || size > maxSize) expire();
105     }
106 }
107
108 void LocalCache::clear() {
109 #ifndef QT_NO_DEBUG_OUTPUT
110     hits = 0;
111     misses = 0;
112 #endif
113     size = 0;
114     insertCount = 0;
115     mutex.lock();
116     QDir(directory).removeRecursively();
117     mutex.unlock();
118 }
119
120 QString LocalCache::cachePath(const QByteArray &key) const {
121     return directory + QLatin1String(key);
122 }
123
124 void LocalCache::expire() {
125     if (!mutex.tryLock()) return;
126
127     QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot;
128     QDirIterator it(directory, filters, QDirIterator::Subdirectories);
129
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();
138     }
139
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();
146         QFile file(name);
147         qint64 size = file.size();
148         file.remove();
149         totalSize -= size;
150         ++removedFiles;
151         ++i;
152         qApp->processEvents();
153     }
154 #ifndef QT_NO_DEBUG_OUTPUT
155     debugStats();
156     if (removedFiles > 0) {
157         qDebug() << "Removed:" << removedFiles << "Kept:" << cacheItems.count() - removedFiles
158                  << "New Size:" << totalSize;
159     }
160 #endif
161
162     size = totalSize;
163     mutex.unlock();
164 }
165
166 #ifndef QT_NO_DEBUG_OUTPUT
167 void LocalCache::debugStats() {
168     int total = hits + misses;
169     if (total > 0) {
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 << "%";
175     }
176 }
177 #endif