Marble

DiscCache.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org>
4//
5
6// Own
7#include "DiscCache.h"
8
9// Qt
10#include <QDataStream>
11#include <QDirIterator>
12#include <QFile>
13#include <QtGlobal>
14
15using namespace Marble;
16
17static QString indexFileName(const QString &cacheDirectory)
18{
19 return cacheDirectory + QLatin1StringView("/cache_index.idx");
20}
21
22DiscCache::DiscCache(const QString &cacheDirectory)
23 : m_CacheDirectory(cacheDirectory)
24 , m_CacheLimit(300 * 1024 * 1024)
25 , m_CurrentCacheSize(0)
26{
27 Q_ASSERT(!m_CacheDirectory.isEmpty() && "Passed empty cache directory!");
28
29 QFile file(indexFileName(m_CacheDirectory));
30
31 if (file.exists()) {
32 if (file.open(QIODevice::ReadOnly)) {
33 QDataStream s(&file);
34 s.setVersion(8);
35
36 s >> m_CacheLimit;
37 s >> m_CurrentCacheSize;
38 s >> m_Entries;
39
40 } else {
41 qWarning("Unable to open cache directory %s", qPrintable(m_CacheDirectory));
42 }
43 }
44}
45
46DiscCache::~DiscCache()
47{
48 QFile file(indexFileName(m_CacheDirectory));
49
50 if (file.open(QIODevice::WriteOnly)) {
51 QDataStream s(&file);
52 s.setVersion(8);
53
54 s << m_CacheLimit;
55 s << m_CurrentCacheSize;
56 s << m_Entries;
57 }
58
59 file.close();
60}
61
62quint64 DiscCache::cacheLimit() const
63{
64 return m_CacheLimit;
65}
66
67void DiscCache::clear()
68{
69 QDirIterator it(m_CacheDirectory);
70
71 // Remove all files from cache directory
72 while (it.hasNext()) {
73 it.next();
74
75 if (it.fileName() == indexFileName(m_CacheDirectory)) // skip index file
76 continue;
77
78 QFile::remove(it.fileName());
79 }
80
81 // Delete entries
82 m_Entries.clear();
83
84 // Reset current cache size
85 m_CurrentCacheSize = 0;
86}
87
88bool DiscCache::exists(const QString &key) const
89{
90 return m_Entries.contains(key);
91}
92
93bool DiscCache::find(const QString &key, QByteArray &data)
94{
95 // Return error if we don't know this key
96 if (!m_Entries.contains(key))
97 return false;
98
99 // If we can open the file, load all data and update access timestamp
100 QFile file(keyToFileName(key));
101 if (file.open(QIODevice::ReadOnly)) {
102 data = file.readAll();
103
104 m_Entries[key].first = QDateTime::currentDateTime();
105 return true;
106 }
107
108 return false;
109}
110
111bool DiscCache::insert(const QString &key, const QByteArray &data)
112{
113 // If we can't open/create a file for this entry signal an error
114 QFile file(keyToFileName(key));
115 if (!file.open(QIODevice::WriteOnly))
116 return false;
117
118 // If we overwrite an existing entry, subtract the size first
119 if (m_Entries.contains(key))
120 m_CurrentCacheSize -= m_Entries.value(key).second;
121
122 // Store the data on disc
123 file.write(data);
124
125 // Create/Overwrite with a new entry
126 m_Entries.insert(key, QPair<QDateTime, quint64>(QDateTime::currentDateTime(), data.length()));
127
128 // Add the size of the new entry
129 m_CurrentCacheSize += data.length();
130
131 cleanup();
132
133 return true;
134}
135
136void DiscCache::remove(const QString &key)
137{
138 // Do nothing if we don't know the key
139 if (!m_Entries.contains(key))
140 return;
141
142 // If we can't remove the file we don't remove
143 // the entry to prevent inconsistency
144 if (!QFile::remove(keyToFileName(key)))
145 return;
146
147 // Subtract from current size
148 m_CurrentCacheSize -= m_Entries.value(key).second;
149
150 // Finally remove entry
151 m_Entries.remove(key);
152}
153
154void DiscCache::setCacheLimit(quint64 n)
155{
156 m_CacheLimit = n;
157
158 cleanup();
159}
160
161QString DiscCache::keyToFileName(const QString &key) const
162{
163 QString fileName(key);
164 fileName.replace(QLatin1Char('/'), QLatin1Char('_'));
165
166 return m_CacheDirectory + QLatin1Char('/') + fileName;
167}
168
169void DiscCache::cleanup()
170{
171 // Calculate 5% of our current cache limit
172 auto fivePercent = quint64(m_CacheLimit * 0.05);
173
174 while (m_CurrentCacheSize > (m_CacheLimit - fivePercent)) {
176 QString oldestKey;
177
179 while (it.hasNext()) {
180 it.next();
181
182 if (it.value().first < oldestDate) {
183 oldestDate = it.value().first;
184 oldestKey = it.key();
185 }
186 }
187
188 if (!oldestKey.isEmpty()) {
189 // We found the oldest key, so using remove() to
190 // remove it from cache
191 remove(oldestKey);
192 }
193 }
194}
Binds a QML item to a specific geodetic location in screen coordinates.
qsizetype length() const const
QDateTime currentDateTime()
bool remove()
void clear()
bool contains(const Key &key) const const
T & first()
iterator insert(const Key &key, const T &value)
size_type remove(const Key &key)
T value(const Key &key, const T &defaultValue) const const
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.