Marble

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

KDE's Doxygen guidelines are available online.