File indexing completed on 2024-05-05 12:06:53
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org> 0004 // 0005 0006 0007 // Own 0008 #include "DiscCache.h" 0009 0010 // Qt 0011 #include <QtGlobal> 0012 #include <QFile> 0013 #include <QDirIterator> 0014 #include <QDataStream> 0015 0016 using namespace Marble; 0017 0018 static QString indexFileName( const QString &cacheDirectory ) 0019 { 0020 return cacheDirectory + QLatin1String("/cache_index.idx"); 0021 } 0022 0023 DiscCache::DiscCache( const QString &cacheDirectory ) 0024 : m_CacheDirectory( cacheDirectory ), 0025 m_CacheLimit( 300 * 1024 * 1024 ), 0026 m_CurrentCacheSize( 0 ) 0027 { 0028 Q_ASSERT( !m_CacheDirectory.isEmpty() && "Passed empty cache directory!" ); 0029 0030 QFile file( indexFileName( m_CacheDirectory ) ); 0031 0032 if ( file.exists() ) { 0033 if ( file.open( QIODevice::ReadOnly ) ) { 0034 QDataStream s( &file ); 0035 s.setVersion( 8 ); 0036 0037 s >> m_CacheLimit; 0038 s >> m_CurrentCacheSize; 0039 s >> m_Entries; 0040 0041 } else { 0042 qWarning( "Unable to open cache directory %s", qPrintable( m_CacheDirectory ) ); 0043 } 0044 } 0045 } 0046 0047 DiscCache::~DiscCache() 0048 { 0049 QFile file( indexFileName( m_CacheDirectory ) ); 0050 0051 if ( file.open( QIODevice::WriteOnly ) ) { 0052 QDataStream s( &file ); 0053 s.setVersion( 8 ); 0054 0055 s << m_CacheLimit; 0056 s << m_CurrentCacheSize; 0057 s << m_Entries; 0058 } 0059 0060 file.close(); 0061 } 0062 0063 quint64 DiscCache::cacheLimit() const 0064 { 0065 return m_CacheLimit; 0066 } 0067 0068 void DiscCache::clear() 0069 { 0070 QDirIterator it( m_CacheDirectory ); 0071 0072 // Remove all files from cache directory 0073 while ( it.hasNext() ) { 0074 it.next(); 0075 0076 if ( it.fileName() == indexFileName( m_CacheDirectory ) ) // skip index file 0077 continue; 0078 0079 QFile::remove( it.fileName() ); 0080 } 0081 0082 // Delete entries 0083 m_Entries.clear(); 0084 0085 // Reset current cache size 0086 m_CurrentCacheSize = 0; 0087 } 0088 0089 bool DiscCache::exists( const QString &key ) const 0090 { 0091 return m_Entries.contains( key ); 0092 } 0093 0094 bool DiscCache::find( const QString &key, QByteArray &data ) 0095 { 0096 // Return error if we don't know this key 0097 if ( !m_Entries.contains( key ) ) 0098 return false; 0099 0100 // If we can open the file, load all data and update access timestamp 0101 QFile file( keyToFileName( key ) ); 0102 if ( file.open( QIODevice::ReadOnly ) ) { 0103 data = file.readAll(); 0104 0105 m_Entries[ key ].first = QDateTime::currentDateTime(); 0106 return true; 0107 } 0108 0109 return false; 0110 } 0111 0112 bool DiscCache::insert( const QString &key, const QByteArray &data ) 0113 { 0114 // If we can't open/create a file for this entry signal an error 0115 QFile file( keyToFileName( key ) ); 0116 if ( !file.open( QIODevice::WriteOnly ) ) 0117 return false; 0118 0119 // If we overwrite an existing entry, subtract the size first 0120 if ( m_Entries.contains( key ) ) 0121 m_CurrentCacheSize -= m_Entries.value( key ).second; 0122 0123 // Store the data on disc 0124 file.write( data ); 0125 0126 // Create/Overwrite with a new entry 0127 m_Entries.insert( key, QPair<QDateTime, quint64>(QDateTime::currentDateTime(), data.length()) ); 0128 0129 // Add the size of the new entry 0130 m_CurrentCacheSize += data.length(); 0131 0132 cleanup(); 0133 0134 return true; 0135 } 0136 0137 void DiscCache::remove( const QString &key ) 0138 { 0139 // Do nothing if we don't know the key 0140 if ( !m_Entries.contains( key ) ) 0141 return; 0142 0143 // If we can't remove the file we don't remove 0144 // the entry to prevent inconsistency 0145 if ( !QFile::remove( keyToFileName( key ) ) ) 0146 return; 0147 0148 // Subtract from current size 0149 m_CurrentCacheSize -= m_Entries.value( key ).second; 0150 0151 // Finally remove entry 0152 m_Entries.remove( key ); 0153 } 0154 0155 void DiscCache::setCacheLimit( quint64 n ) 0156 { 0157 m_CacheLimit = n; 0158 0159 cleanup(); 0160 } 0161 0162 QString DiscCache::keyToFileName( const QString &key ) const 0163 { 0164 QString fileName( key ); 0165 fileName.replace(QLatin1Char('/'), QLatin1Char('_')); 0166 0167 return m_CacheDirectory + QLatin1Char('/') + fileName; 0168 } 0169 0170 void DiscCache::cleanup() 0171 { 0172 // Calculate 5% of our current cache limit 0173 quint64 fivePercent = quint64( m_CacheLimit * 0.05 ); 0174 0175 while ( m_CurrentCacheSize > (m_CacheLimit - fivePercent) ) { 0176 QDateTime oldestDate( QDateTime::currentDateTime() ); 0177 QString oldestKey; 0178 0179 QMapIterator<QString, QPair<QDateTime, quint64> > it( m_Entries ); 0180 while ( it.hasNext() ) { 0181 it.next(); 0182 0183 if ( it.value().first < oldestDate ) { 0184 oldestDate = it.value().first; 0185 oldestKey = it.key(); 0186 } 0187 } 0188 0189 if ( !oldestKey.isEmpty() ) { 0190 // We found the oldest key, so using remove() to 0191 // remove it from cache 0192 remove( oldestKey ); 0193 } 0194 } 0195 }