summaryrefslogtreecommitdiff
path: root/library
authorzecke <zecke>2005-02-06 21:39:24 (UTC)
committer zecke <zecke>2005-02-06 21:39:24 (UTC)
commitec33239c6edd9927fe2f82953fa48dec47d19567 (patch) (side-by-side diff)
treed546238a10bf19641198f6f4eea32ac942a7116c /library
parentf1a89d7deff682ea52f34a7d160eaa69886aa340 (diff)
downloadopie-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
Diffstat (limited to 'library') (more/less context) (show whitespace changes)
-rw-r--r--library/config.cpp238
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];