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