author | zecke <zecke> | 2004-12-26 13:32:10 (UTC) |
---|---|---|
committer | zecke <zecke> | 2004-12-26 13:32:10 (UTC) |
commit | d8f38f36ad533f93d46c8ff883c6b42f15c96c28 (patch) (unidiff) | |
tree | 27f14def0ffc5d2b94cbd8aad7afbb66d532151d /library | |
parent | 4f5d6b7aff824a8a0f692cbab98b5cfe933de933 (diff) | |
download | opie-d8f38f36ad533f93d46c8ff883c6b42f15c96c28.zip opie-d8f38f36ad533f93d46c8ff883c6b42f15c96c28.tar.gz opie-d8f38f36ad533f93d46c8ff883c6b42f15c96c28.tar.bz2 |
Config is now cached
-rw-r--r-- | library/config.cpp | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/library/config.cpp b/library/config.cpp index bdfcb3f..f68c336 100644 --- a/library/config.cpp +++ b/library/config.cpp | |||
@@ -14,34 +14,36 @@ | |||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | 14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. |
15 | ** | 15 | ** |
16 | ** Contact info@trolltech.com if any conditions of this licensing are | 16 | ** Contact info@trolltech.com if any conditions of this licensing are |
17 | ** not clear to you. | 17 | ** not clear to you. |
18 | ** | 18 | ** |
19 | **********************************************************************/ | 19 | **********************************************************************/ |
20 | 20 | ||
21 | #include <qdir.h> | 21 | #include <qdir.h> |
22 | #include <qmessagebox.h> | 22 | #include <qmessagebox.h> |
23 | #if QT_VERSION <= 230 && defined(QT_NO_CODECS) | 23 | #if QT_VERSION <= 230 && defined(QT_NO_CODECS) |
24 | #include <qtextcodec.h> | 24 | #include <qtextcodec.h> |
25 | #endif | 25 | #endif |
26 | #include <qtextstream.h> | 26 | #include <qtextstream.h> |
27 | 27 | ||
28 | #include <sys/stat.h> | 28 | #include <sys/stat.h> |
29 | #include <sys/types.h> | 29 | #include <sys/types.h> |
30 | #include <sys/time.h> | ||
30 | #include <fcntl.h> | 31 | #include <fcntl.h> |
31 | #include <stdlib.h> | 32 | #include <stdlib.h> |
33 | #include <time.h> | ||
32 | #include <unistd.h> | 34 | #include <unistd.h> |
33 | 35 | ||
34 | #define QTOPIA_INTERNAL_LANGLIST | 36 | #define QTOPIA_INTERNAL_LANGLIST |
35 | #include "config.h" | 37 | #include "config.h" |
36 | #include "global.h" | 38 | #include "global.h" |
37 | #include "qpeapplication.h" | 39 | #include "qpeapplication.h" |
38 | 40 | ||
39 | 41 | ||
40 | /* | 42 | /* |
41 | * Internal Class | 43 | * Internal Class |
42 | */ | 44 | */ |
43 | class ConfigPrivate { | 45 | class ConfigPrivate { |
44 | public: | 46 | public: |
45 | ConfigPrivate() : multilang(FALSE) {} | 47 | ConfigPrivate() : multilang(FALSE) {} |
46 | ConfigPrivate(const ConfigPrivate& o) : | 48 | ConfigPrivate(const ConfigPrivate& o) : |
47 | trfile(o.trfile), | 49 | trfile(o.trfile), |
@@ -51,32 +53,203 @@ public: | |||
51 | ConfigPrivate& operator=(const ConfigPrivate& o) | 53 | ConfigPrivate& operator=(const ConfigPrivate& o) |
52 | { | 54 | { |
53 | trfile = o.trfile; | 55 | trfile = o.trfile; |
54 | trcontext = o.trcontext; | 56 | trcontext = o.trcontext; |
55 | multilang = o.multilang; | 57 | multilang = o.multilang; |
56 | return *this; | 58 | return *this; |
57 | } | 59 | } |
58 | 60 | ||
59 | QString trfile; | 61 | QString trfile; |
60 | QCString trcontext; | 62 | QCString trcontext; |
61 | bool multilang; | 63 | bool multilang; |
62 | }; | 64 | }; |
63 | 65 | ||
64 | ///////////////////////////////////////////////////////////////// | 66 | ///////////////////////////////////////////////////////////////// |
65 | ///////////////////////////////////////////////////////////////// | 67 | ///////////////////////////////////////////////////////////////// |
66 | 68 | ||
69 | #ifndef Q_OS_WIN32 | ||
70 | |||
71 | //#define DEBUG_CONFIG_CACHE | ||
72 | |||
73 | const int CONFIG_CACHE_SIZE = 8192; | ||
74 | const int CONFIG_CACHE_TIMEOUT = 1000; | ||
75 | |||
76 | class ConfigData | ||
77 | { | ||
78 | public: | ||
79 | ConfigData(const ConfigData& o) : | ||
80 | cfg(o.cfg), | ||
81 | priv(o.priv ? new ConfigPrivate(*o.priv) : 0), | ||
82 | mtime(o.mtime), | ||
83 | size(o.size), | ||
84 | used(o.used) | ||
85 | { } | ||
86 | |||
87 | ConfigData& operator=(const ConfigData& o) | ||
88 | { | ||
89 | cfg = o.cfg; | ||
90 | delete priv; | ||
91 | priv = o.priv ? new ConfigPrivate(*o.priv) : 0; | ||
92 | mtime = o.mtime; | ||
93 | size = o.size; | ||
94 | used = o.used; | ||
95 | return *this; | ||
96 | } | ||
97 | |||
98 | ConfigData() : priv(0) {} | ||
99 | ~ConfigData() { delete priv; } | ||
100 | |||
101 | ConfigGroupMap cfg; | ||
102 | ConfigPrivate *priv; // Owned by this object | ||
103 | time_t mtime; | ||
104 | unsigned int size; | ||
105 | struct timeval used; | ||
106 | }; | ||
107 | |||
108 | class ConfigCache : public QObject | ||
109 | { | ||
110 | public: | ||
111 | ConfigCache(); | ||
112 | |||
113 | void insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv); | ||
114 | bool find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv); | ||
115 | void remove(const QString &filename); | ||
116 | |||
117 | protected: | ||
118 | void timerEvent(QTimerEvent *); | ||
119 | |||
120 | private: | ||
121 | void removeLru(); | ||
122 | |||
123 | QMap<QString, ConfigData> configData; | ||
124 | unsigned int totalsize; | ||
125 | int tid; | ||
126 | }; | ||
127 | |||
128 | ConfigCache::ConfigCache() : QObject(), totalsize(0), tid(0) | ||
129 | { | ||
130 | } | ||
131 | |||
132 | void ConfigCache::insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv) | ||
133 | { | ||
134 | // use stat() rather than QFileInfo for speed. | ||
135 | struct stat sbuf; | ||
136 | stat(filename.local8Bit().data(), &sbuf); | ||
137 | |||
138 | if (sbuf.st_size < CONFIG_CACHE_SIZE/2) { | ||
139 | ConfigData data; | ||
140 | data.cfg = cfg; | ||
141 | data.priv = priv ? new ConfigPrivate(*priv) : 0; | ||
142 | data.mtime = sbuf.st_mtime; | ||
143 | data.size = sbuf.st_size; | ||
144 | gettimeofday(&data.used, 0); | ||
145 | |||
146 | remove(filename); | ||
147 | configData.insert(filename, data); | ||
148 | |||
149 | totalsize += data.size; | ||
150 | #ifdef DEBUG_CONFIG_CACHE | ||
151 | qDebug("++++++ insert %s", filename.latin1()); | ||
152 | #endif | ||
153 | } | ||
154 | |||
155 | if (totalsize > (uint)CONFIG_CACHE_SIZE) { | ||
156 | // We'll delay deleting anything until later. | ||
157 | // This lets us grow quite large during some operations, | ||
158 | // but we'll be reduced to a decent size later. | ||
159 | // This works well with the main use case - app startup. | ||
160 | if (!tid) | ||
161 | tid = startTimer(CONFIG_CACHE_TIMEOUT); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | bool ConfigCache::find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv) | ||
166 | { | ||
167 | QMap<QString, ConfigData>::Iterator it = configData.find(filename); | ||
168 | if (it != configData.end()) { | ||
169 | ConfigData data = *it; | ||
170 | // use stat() rather than QFileInfo for speed. | ||
171 | struct stat sbuf; | ||
172 | stat(filename.local8Bit().data(), &sbuf); | ||
173 | |||
174 | if (data.mtime == sbuf.st_mtime && (int)data.size == sbuf.st_size) { | ||
175 | cfg = data.cfg; | ||
176 | delete priv; | ||
177 | priv = data.priv ? new ConfigPrivate(*data.priv) : 0; | ||
178 | gettimeofday(&data.used, 0); | ||
179 | #ifdef DEBUG_CONFIG_CACHE | ||
180 | qDebug("******* Cache hit: %s", filename.latin1()); | ||
181 | #endif | ||
182 | return TRUE; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | #ifdef DEBUG_CONFIG_CACHE | ||
187 | qDebug("------- Cache miss: %s", filename.latin1()); | ||
188 | #endif | ||
189 | |||
190 | return FALSE; | ||
191 | } | ||
192 | |||
193 | void ConfigCache::remove(const QString &filename) | ||
194 | { | ||
195 | QMap<QString, ConfigData>::Iterator it = configData.find(filename); | ||
196 | if (it != configData.end()) { | ||
197 | totalsize -= (*it).size; | ||
198 | configData.remove(it); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | void ConfigCache::timerEvent(QTimerEvent *) | ||
203 | { | ||
204 | #ifdef DEBUG_CONFIG_CACHE | ||
205 | qDebug( "cache size: %d", totalsize); | ||
206 | #endif | ||
207 | while (totalsize > (uint)CONFIG_CACHE_SIZE) | ||
208 | removeLru(); | ||
209 | killTimer(tid); | ||
210 | tid = 0; | ||
211 | } | ||
212 | |||
213 | void ConfigCache::removeLru() | ||
214 | { | ||
215 | QMap<QString, ConfigData>::Iterator it = configData.begin(); | ||
216 | QMap<QString, ConfigData>::Iterator lru = it; | ||
217 | ++it; | ||
218 | for (; it != configData.end(); ++it) { | ||
219 | if ((*it).used.tv_sec < (*lru).used.tv_sec || | ||
220 | ((*it).used.tv_sec == (*lru).used.tv_sec && | ||
221 | (*it).used.tv_usec < (*lru).used.tv_usec)) | ||
222 | lru = it; | ||
223 | } | ||
224 | |||
225 | #ifdef DEBUG_CONFIG_CACHE | ||
226 | qDebug("Cache full, removing: %s", lru.key().latin1()); | ||
227 | #endif | ||
228 | totalsize -= (*lru).size; | ||
229 | configData.remove(lru); | ||
230 | } | ||
231 | |||
232 | static ConfigCache *qpe_configCache = 0; | ||
233 | |||
234 | #endif /* Q_OS_WIN32 */ | ||
235 | |||
236 | |||
237 | // ========================================================================== | ||
238 | |||
239 | |||
67 | /*! | 240 | /*! |
68 | \internal | 241 | \internal |
69 | */ | 242 | */ |
70 | QString Config::configFilename(const QString& name, Domain d) | 243 | QString Config::configFilename(const QString& name, Domain d) |
71 | { | 244 | { |
72 | switch (d) { | 245 | switch (d) { |
73 | case File: | 246 | case File: |
74 | return name; | 247 | return name; |
75 | case User: { | 248 | case User: { |
76 | QDir dir = (QString(getenv("HOME")) + "/Settings"); | 249 | QDir dir = (QString(getenv("HOME")) + "/Settings"); |
77 | if ( !dir.exists() ) | 250 | if ( !dir.exists() ) |
78 | mkdir(dir.path().local8Bit(),0700); | 251 | mkdir(dir.path().local8Bit(),0700); |
79 | return dir.path() + "/" + name + ".conf"; | 252 | return dir.path() + "/" + name + ".conf"; |
80 | } | 253 | } |
81 | } | 254 | } |
82 | return name; | 255 | return name; |
@@ -552,32 +725,36 @@ void Config::write( const QString &fn ) | |||
552 | QMessageBox::critical( 0, QObject::tr("Out of Space"), | 725 | QMessageBox::critical( 0, QObject::tr("Out of Space"), |
553 | QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") ); | 726 | QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") ); |
554 | f.close(); | 727 | f.close(); |
555 | QFile::remove( strNewFile ); | 728 | QFile::remove( strNewFile ); |
556 | return; | 729 | return; |
557 | } | 730 | } |
558 | 731 | ||
559 | f.close(); | 732 | f.close(); |
560 | // now rename the file... | 733 | // now rename the file... |
561 | if ( rename( strNewFile, filename ) < 0 ) { | 734 | if ( rename( strNewFile, filename ) < 0 ) { |
562 | qWarning( "problem renaming the file %s to %s", strNewFile.latin1(), | 735 | qWarning( "problem renaming the file %s to %s", strNewFile.latin1(), |
563 | filename.latin1() ); | 736 | filename.latin1() ); |
564 | QFile::remove( strNewFile ); | 737 | QFile::remove( strNewFile ); |
565 | return; | 738 | return; |
566 | } | 739 | } |
567 | 740 | ||
741 | #ifndef Q_OS_WIN32 | ||
742 | if (qpe_configCache) | ||
743 | qpe_configCache->insert(filename, groups, d); | ||
744 | #endif | ||
568 | changed = FALSE; | 745 | changed = FALSE; |
569 | } | 746 | } |
570 | 747 | ||
571 | /*! | 748 | /*! |
572 | Returns whether the Config is in a valid state. | 749 | Returns whether the Config is in a valid state. |
573 | */ | 750 | */ |
574 | bool Config::isValid() const | 751 | bool Config::isValid() const |
575 | { | 752 | { |
576 | return groups.end() != git; | 753 | return groups.end() != git; |
577 | } | 754 | } |
578 | 755 | ||
579 | /*! | 756 | /*! |
580 | \internal | 757 | \internal |
581 | */ | 758 | */ |
582 | void Config::read() | 759 | void Config::read() |
583 | { | 760 | { |
@@ -590,48 +767,66 @@ void Config::read() | |||
590 | QFileInfo fi(filename); | 767 | QFileInfo fi(filename); |
591 | QString settingsDir = QDir::homeDirPath() + "/Settings"; | 768 | QString settingsDir = QDir::homeDirPath() + "/Settings"; |
592 | if (fi.dirPath(TRUE) == settingsDir) { | 769 | if (fi.dirPath(TRUE) == settingsDir) { |
593 | // User setting - see if there is a default in $OPIEDIR/etc/default/ | 770 | // User setting - see if there is a default in $OPIEDIR/etc/default/ |
594 | QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName(); | 771 | QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName(); |
595 | if (QFile::exists(dftlFile)) { | 772 | if (QFile::exists(dftlFile)) { |
596 | readFilename = dftlFile; | 773 | readFilename = dftlFile; |
597 | failed = FALSE; | 774 | failed = FALSE; |
598 | } | 775 | } |
599 | } | 776 | } |
600 | if (failed) { | 777 | if (failed) { |
601 | git = groups.end(); | 778 | git = groups.end(); |
602 | return; | 779 | return; |
603 | } | 780 | } |
604 | } | 781 | } |
605 | 782 | ||
783 | #ifndef Q_OS_WIN32 | ||
784 | if (!qpe_configCache) | ||
785 | qpe_configCache = new ConfigCache; | ||
786 | |||
787 | if (qpe_configCache->find(readFilename, groups, d)) { | ||
788 | if ( d && d->multilang ) { | ||
789 | QStringList l = Global::languageList(); | ||
790 | lang = l[0]; | ||
791 | glang = l[1]; | ||
792 | } | ||
793 | git = groups.begin(); | ||
794 | return; | ||
795 | } | ||
796 | #endif | ||
606 | 797 | ||
607 | QFile f( readFilename ); | 798 | QFile f( readFilename ); |
608 | if ( !f.open( IO_ReadOnly ) ) { | 799 | if ( !f.open( IO_ReadOnly ) ) { |
609 | git = groups.end(); | 800 | git = groups.end(); |
610 | return; | 801 | return; |
611 | } | 802 | } |
612 | 803 | ||
613 | if (f.getch()!='[') { | 804 | if (f.getch()!='[') { |
614 | git = groups.end(); | 805 | git = groups.end(); |
615 | return; | 806 | return; |
616 | } | 807 | } |
617 | f.ungetch('['); | 808 | f.ungetch('['); |
618 | 809 | ||
619 | QTextStream s( &f ); | 810 | QTextStream s( &f ); |
620 | read( s ); | 811 | read( s ); |
621 | f.close(); | 812 | f.close(); |
813 | |||
814 | #ifndef Q_OS_WIN32 | ||
815 | qpe_configCache->insert(readFilename, groups, d); | ||
816 | #endif | ||
622 | } | 817 | } |
623 | 818 | ||
624 | /*! | 819 | /*! |
625 | \internal | 820 | \internal |
626 | */ | 821 | */ |
627 | bool Config::parse( const QString &l ) | 822 | bool Config::parse( const QString &l ) |
628 | { | 823 | { |
629 | QString line = l.stripWhiteSpace(); | 824 | QString line = l.stripWhiteSpace(); |
630 | if ( line[ 0 ] == QChar( '[' ) ) { | 825 | if ( line[ 0 ] == QChar( '[' ) ) { |
631 | QString gname = line; | 826 | QString gname = line; |
632 | gname = gname.remove( 0, 1 ); | 827 | gname = gname.remove( 0, 1 ); |
633 | if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) ) | 828 | if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) ) |
634 | gname = gname.remove( gname.length() - 1, 1 ); | 829 | gname = gname.remove( gname.length() - 1, 1 ); |
635 | git = groups.insert( gname, ConfigGroup() ); | 830 | git = groups.insert( gname, ConfigGroup() ); |
636 | } else if ( !line.isEmpty() ) { | 831 | } else if ( !line.isEmpty() ) { |
637 | if ( git == groups.end() ) | 832 | if ( git == groups.end() ) |