author | zecke <zecke> | 2005-02-06 21:39:24 (UTC) |
---|---|---|
committer | zecke <zecke> | 2005-02-06 21:39:24 (UTC) |
commit | ec33239c6edd9927fe2f82953fa48dec47d19567 (patch) (side-by-side diff) | |
tree | d546238a10bf19641198f6f4eea32ac942a7116c /library | |
parent | f1a89d7deff682ea52f34a7d160eaa69886aa340 (diff) | |
download | opie-ec33239c6edd9927fe2f82953fa48dec47d19567.zip opie-ec33239c6edd9927fe2f82953fa48dec47d19567.tar.gz opie-ec33239c6edd9927fe2f82953fa48dec47d19567.tar.bz2 |
Rewrite Trolltechs Cache implementation.
-Update the used timestamp on access
-Do not play silly games with ConfigPrivate pointers
copying them is enough
In write save the old config group and restore it after
writing it
-rw-r--r-- | library/config.cpp | 238 |
1 files changed, 109 insertions, 129 deletions
diff --git a/library/config.cpp b/library/config.cpp index f68c336..e9cae4c 100644 --- a/library/config.cpp +++ b/library/config.cpp @@ -24,262 +24,242 @@ #include <qtextcodec.h> #endif #include <qtextstream.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/time.h> #include <fcntl.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #define QTOPIA_INTERNAL_LANGLIST #include "config.h" #include "global.h" #include "qpeapplication.h" /* * Internal Class */ class ConfigPrivate { public: ConfigPrivate() : multilang(FALSE) {} ConfigPrivate(const ConfigPrivate& o) : trfile(o.trfile), trcontext(o.trcontext), multilang(o.multilang) {} ConfigPrivate& operator=(const ConfigPrivate& o) { trfile = o.trfile; trcontext = o.trcontext; multilang = o.multilang; return *this; } QString trfile; QCString trcontext; bool multilang; }; ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// #ifndef Q_OS_WIN32 //#define DEBUG_CONFIG_CACHE - -const int CONFIG_CACHE_SIZE = 8192; -const int CONFIG_CACHE_TIMEOUT = 1000; - class ConfigData { public: - ConfigData(const ConfigData& o) : - cfg(o.cfg), - priv(o.priv ? new ConfigPrivate(*o.priv) : 0), - mtime(o.mtime), - size(o.size), - used(o.used) - { } - - ConfigData& operator=(const ConfigData& o) + ConfigData() {} + ConfigData( const ConfigGroupMap& cf, const ConfigPrivate& pri, + struct stat sbuf ) + : cfg( cf ), priv( pri ), mtime( sbuf.st_mtime ), + size( sbuf.st_size ) { - cfg = o.cfg; - delete priv; - priv = o.priv ? new ConfigPrivate(*o.priv) : 0; - mtime = o.mtime; - size = o.size; - used = o.used; - return *this; + gettimeofday(&used, 0 ); } - ConfigData() : priv(0) {} - ~ConfigData() { delete priv; } - ConfigGroupMap cfg; - ConfigPrivate *priv; // Owned by this object + ConfigPrivate priv; // Owned by this object time_t mtime; unsigned int size; struct timeval used; }; -class ConfigCache : public QObject -{ -public: - ConfigCache(); - void insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv); - bool find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv); - void remove(const QString &filename); +class ConfigCache : public QObject { +public: + static ConfigCache* instance(); + void insert( const QString& fileName, const ConfigGroupMap& cfg, + const ConfigPrivate *priv ); + bool find(const QString& fileName, ConfigGroupMap& cfg, + ConfigPrivate** priv ); protected: void timerEvent(QTimerEvent *); private: + ConfigCache(); + void remove( const QString& fileName ); void removeLru(); - QMap<QString, ConfigData> configData; - unsigned int totalsize; - int tid; +private: + QMap<QString, ConfigData> m_cached; + unsigned int m_totalSize; + int m_tid; +private: + static ConfigCache* m_inst; + static const unsigned int CONFIG_CACHE_SIZE = 8192; + static const unsigned int CONFIG_CACHE_TIMEOUT = 1000; }; -ConfigCache::ConfigCache() : QObject(), totalsize(0), tid(0) -{ +ConfigCache* ConfigCache::m_inst = 0; +/* + * get destroyed when qApp gets destroyed + */ +ConfigCache::ConfigCache() : QObject( qApp ), m_totalSize( 0 ), m_tid( 0 ) {} +ConfigCache* ConfigCache::instance() { + if ( !m_inst ) + m_inst = new ConfigCache(); + + return m_inst; } -void ConfigCache::insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv) -{ - // use stat() rather than QFileInfo for speed. - struct stat sbuf; - stat(filename.local8Bit().data(), &sbuf); - - if (sbuf.st_size < CONFIG_CACHE_SIZE/2) { - ConfigData data; - data.cfg = cfg; - data.priv = priv ? new ConfigPrivate(*priv) : 0; - data.mtime = sbuf.st_mtime; - data.size = sbuf.st_size; - gettimeofday(&data.used, 0); +void ConfigCache::remove( const QString& fileName ) { + QMap<QString, ConfigData>::Iterator it = m_cached.find( fileName ); - remove(filename); - configData.insert(filename, data); + if ( it == m_cached.end() ) + return; - totalsize += data.size; -#ifdef DEBUG_CONFIG_CACHE - qDebug("++++++ insert %s", filename.latin1()); -#endif + m_totalSize -= (*it).size; + m_cached.remove( it ); } - if (totalsize > (uint)CONFIG_CACHE_SIZE) { - // We'll delay deleting anything until later. - // This lets us grow quite large during some operations, - // but we'll be reduced to a decent size later. - // This works well with the main use case - app startup. - if (!tid) - tid = startTimer(CONFIG_CACHE_TIMEOUT); - } +void ConfigCache::removeLru() { + QMap<QString, ConfigData>::Iterator it = m_cached.begin(); + QMap<QString, ConfigData>::Iterator lru = it; + ++it; + for (; it != m_cached.end(); ++it) + if ((*it).used.tv_sec < (*lru).used.tv_sec || + ((*it).used.tv_sec == (*lru).used.tv_sec && + (*it).used.tv_usec < (*lru).used.tv_usec)) + lru = it; + + qWarning( "Removing item" ); + m_totalSize -= (*lru).size; + m_cached.remove(lru); } -bool ConfigCache::find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv) -{ - QMap<QString, ConfigData>::Iterator it = configData.find(filename); - if (it != configData.end()) { - ConfigData data = *it; - // use stat() rather than QFileInfo for speed. - struct stat sbuf; - stat(filename.local8Bit().data(), &sbuf); +void ConfigCache::timerEvent( QTimerEvent* ) { + while ( m_totalSize > CONFIG_CACHE_SIZE ) + removeLru(); - if (data.mtime == sbuf.st_mtime && (int)data.size == sbuf.st_size) { - cfg = data.cfg; - delete priv; - priv = data.priv ? new ConfigPrivate(*data.priv) : 0; - gettimeofday(&data.used, 0); -#ifdef DEBUG_CONFIG_CACHE - qDebug("******* Cache hit: %s", filename.latin1()); -#endif - return TRUE; - } + killTimer(m_tid); + m_tid = 0; } -#ifdef DEBUG_CONFIG_CACHE - qDebug("------- Cache miss: %s", filename.latin1()); -#endif +void ConfigCache::insert( const QString& fileName, const ConfigGroupMap& cfg, + const ConfigPrivate* _priv ) { - return FALSE; -} -void ConfigCache::remove(const QString &filename) -{ - QMap<QString, ConfigData>::Iterator it = configData.find(filename); - if (it != configData.end()) { - totalsize -= (*it).size; - configData.remove(it); - } -} + struct stat sbuf; + ::stat( QFile::encodeName(fileName), &sbuf ); + if ( static_cast<unsigned int>(sbuf.st_size) >= CONFIG_CACHE_SIZE>>1) + return; -void ConfigCache::timerEvent(QTimerEvent *) -{ -#ifdef DEBUG_CONFIG_CACHE - qDebug( "cache size: %d", totalsize); -#endif - while (totalsize > (uint)CONFIG_CACHE_SIZE) - removeLru(); - killTimer(tid); - tid = 0; -} + /* + * remove the old version and use the new one + */ + ConfigPrivate priv = _priv ? *_priv : ConfigPrivate(); + ConfigData data( cfg, priv, sbuf ); + m_totalSize += data.size; -void ConfigCache::removeLru() -{ - QMap<QString, ConfigData>::Iterator it = configData.begin(); - QMap<QString, ConfigData>::Iterator lru = it; - ++it; - for (; it != configData.end(); ++it) { - if ((*it).used.tv_sec < (*lru).used.tv_sec || - ((*it).used.tv_sec == (*lru).used.tv_sec && - (*it).used.tv_usec < (*lru).used.tv_usec)) - lru = it; - } + remove( fileName ); + m_cached.insert( fileName, data ); -#ifdef DEBUG_CONFIG_CACHE - qDebug("Cache full, removing: %s", lru.key().latin1()); -#endif - totalsize -= (*lru).size; - configData.remove(lru); + /* + * we've overcommited allocation, let us clean up + * soon + */ + if ( m_totalSize >= CONFIG_CACHE_SIZE ) + if ( !m_tid ) + m_tid = startTimer(CONFIG_CACHE_TIMEOUT); } -static ConfigCache *qpe_configCache = 0; +bool ConfigCache::find( const QString& fileName, ConfigGroupMap& cfg, + ConfigPrivate **ppriv ) { + QMap<QString, ConfigData>::Iterator it = m_cached.find(fileName); + if (it != m_cached.end()) { + ConfigData &data = *it; + struct stat sbuf; + ::stat(QFile::encodeName( fileName ), &sbuf); -#endif /* Q_OS_WIN32 */ + if (data.mtime == sbuf.st_mtime && (int)data.size == sbuf.st_size) { + cfg = data.cfg; + /* + * null pointer + */ + if ( *ppriv == 0 ) + *ppriv = new ConfigPrivate( data.priv ); + **ppriv = data.priv; + gettimeofday(&data.used, 0); -// ========================================================================== + return true; + } + } + + return false; +} + +#endif /*! \internal */ QString Config::configFilename(const QString& name, Domain d) { switch (d) { case File: return name; case User: { QDir dir = (QString(getenv("HOME")) + "/Settings"); if ( !dir.exists() ) mkdir(dir.path().local8Bit(),0700); return dir.path() + "/" + name + ".conf"; } } return name; } /* This cannot be made public because of binary compat issues */ void Config::read( QTextStream &s ) { #if QT_VERSION <= 230 && defined(QT_NO_CODECS) // The below should work, but doesn't in Qt 2.3.0 s.setCodec( QTextCodec::codecForMib( 106 ) ); #else s.setEncoding( QTextStream::UnicodeUTF8 ); #endif QStringList list = QStringList::split('\n', s.read() ); for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { if ( !parse( *it ) ) { git = groups.end(); return; } } } /*! \class Config config.h \brief The Config class provides for saving application cofniguration state. You should keep a Config in existence only while you do not want others to be able to change the state. There is no locking currently, but there may be in the future. */ @@ -650,214 +630,214 @@ int Config::readNumEntry( const QString &key, int deflt ) */ bool Config::readBoolEntry( const QString &key, bool deflt ) { QString s = readEntry( key ); if ( s.isEmpty() ) return deflt; else return (bool)s.toInt(); } /*! \fn QStringList Config::readListEntry( const QString &key, const QChar &sep ) const Reads a string list entry stored with \a key, and with \a sep as the separator. */ /*! \internal For compatibility, non-const version. */ QStringList Config::readListEntry( const QString &key, const QChar &sep ) { QString s = readEntry( key ); if ( s.isEmpty() ) return QStringList(); else return QStringList::split( sep, s ); } /*! Removes all entries from the current group. */ void Config::clearGroup() { if ( git == groups.end() ) { qWarning( "no group set" ); return; } if ( !(*git).isEmpty() ) { ( *git ).clear(); changed = TRUE; } } /*! \internal */ void Config::write( const QString &fn ) { + QString oldGroup = git.key(); + QString strNewFile; if ( !fn.isEmpty() ) filename = fn; strNewFile = filename + ".new"; QFile f( strNewFile ); if ( !f.open( IO_WriteOnly|IO_Raw ) ) { qWarning( "could not open for writing `%s'", strNewFile.latin1() ); git = groups.end(); return; } QString str; QCString cstr; QMap< QString, ConfigGroup >::Iterator g_it = groups.begin(); for ( ; g_it != groups.end(); ++g_it ) { str += "[" + g_it.key() + "]\n"; ConfigGroup::Iterator e_it = ( *g_it ).begin(); for ( ; e_it != ( *g_it ).end(); ++e_it ) str += e_it.key() + " = " + *e_it + "\n"; } cstr = str.utf8(); int total_length; total_length = f.writeBlock( cstr.data(), cstr.length() ); if ( total_length != int(cstr.length()) ) { QMessageBox::critical( 0, QObject::tr("Out of Space"), QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") ); f.close(); QFile::remove( strNewFile ); return; } f.close(); // now rename the file... if ( rename( strNewFile, filename ) < 0 ) { qWarning( "problem renaming the file %s to %s", strNewFile.latin1(), filename.latin1() ); QFile::remove( strNewFile ); return; } #ifndef Q_OS_WIN32 - if (qpe_configCache) - qpe_configCache->insert(filename, groups, d); + ConfigCache::instance()->insert( filename, groups, d ); + setGroup( oldGroup ); #endif changed = FALSE; } /*! Returns whether the Config is in a valid state. */ bool Config::isValid() const { return groups.end() != git; } /*! \internal */ void Config::read() { changed = FALSE; QString readFilename(filename); if ( !QFile::exists(filename) ) { bool failed = TRUE; QFileInfo fi(filename); QString settingsDir = QDir::homeDirPath() + "/Settings"; if (fi.dirPath(TRUE) == settingsDir) { // User setting - see if there is a default in $OPIEDIR/etc/default/ QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName(); if (QFile::exists(dftlFile)) { readFilename = dftlFile; failed = FALSE; } } if (failed) { git = groups.end(); return; } } #ifndef Q_OS_WIN32 - if (!qpe_configCache) - qpe_configCache = new ConfigCache; - if (qpe_configCache->find(readFilename, groups, d)) { + if (ConfigCache::instance()->find(readFilename, groups, &d)) { if ( d && d->multilang ) { QStringList l = Global::languageList(); lang = l[0]; glang = l[1]; } git = groups.begin(); return; } #endif QFile f( readFilename ); if ( !f.open( IO_ReadOnly ) ) { git = groups.end(); return; } if (f.getch()!='[') { git = groups.end(); return; } f.ungetch('['); QTextStream s( &f ); read( s ); f.close(); #ifndef Q_OS_WIN32 - qpe_configCache->insert(readFilename, groups, d); + ConfigCache::instance()->insert(readFilename, groups, d); #endif } /*! \internal */ bool Config::parse( const QString &l ) { QString line = l.stripWhiteSpace(); if ( line[ 0 ] == QChar( '[' ) ) { QString gname = line; gname = gname.remove( 0, 1 ); if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) ) gname = gname.remove( gname.length() - 1, 1 ); git = groups.insert( gname, ConfigGroup() ); } else if ( !line.isEmpty() ) { if ( git == groups.end() ) return FALSE; int eq = line.find( '=' ); if ( eq == -1 ) return FALSE; QString key = line.left(eq).stripWhiteSpace(); QString value = line.mid(eq+1).stripWhiteSpace(); if ( git.key() == "Translation" ) { if ( key == "File" ) { if ( !d ) d = new ConfigPrivate; d->trfile = value; } else if ( key == "Context" ) { if ( !d ) d = new ConfigPrivate; d->trcontext = value.latin1(); } else if ( key.startsWith("Comment") ) { return TRUE; // ignore comment for ts file } else { return FALSE; // Unrecognized } } int kl = key.length(); if ( kl > 1 && key[kl-1] == ']' && key[kl-2] != '[' ) { // Old-style translation (inefficient) if ( !d ) d = new ConfigPrivate; if ( !d->multilang ) { QStringList l = Global::languageList(); lang = l[0]; |