-rw-r--r-- | library/config.cpp | 264 |
1 files changed, 122 insertions, 142 deletions
diff --git a/library/config.cpp b/library/config.cpp index f68c336..e9cae4c 100644 --- a/library/config.cpp +++ b/library/config.cpp | |||
@@ -66,194 +66,174 @@ public: | |||
66 | ///////////////////////////////////////////////////////////////// | 66 | ///////////////////////////////////////////////////////////////// |
67 | ///////////////////////////////////////////////////////////////// | 67 | ///////////////////////////////////////////////////////////////// |
68 | 68 | ||
69 | #ifndef Q_OS_WIN32 | 69 | #ifndef Q_OS_WIN32 |
70 | 70 | ||
71 | //#define DEBUG_CONFIG_CACHE | 71 | //#define DEBUG_CONFIG_CACHE |
72 | |||
73 | const int CONFIG_CACHE_SIZE = 8192; | ||
74 | const int CONFIG_CACHE_TIMEOUT = 1000; | ||
75 | |||
76 | class ConfigData | 72 | class ConfigData |
77 | { | 73 | { |
78 | public: | 74 | public: |
79 | ConfigData(const ConfigData& o) : | 75 | ConfigData() {} |
80 | cfg(o.cfg), | 76 | ConfigData( const ConfigGroupMap& cf, const ConfigPrivate& pri, |
81 | priv(o.priv ? new ConfigPrivate(*o.priv) : 0), | 77 | struct stat sbuf ) |
82 | mtime(o.mtime), | 78 | : cfg( cf ), priv( pri ), mtime( sbuf.st_mtime ), |
83 | size(o.size), | 79 | size( sbuf.st_size ) |
84 | used(o.used) | 80 | { |
85 | { } | 81 | gettimeofday(&used, 0 ); |
86 | 82 | } | |
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 | 83 | ||
101 | ConfigGroupMap cfg; | 84 | ConfigGroupMap cfg; |
102 | ConfigPrivate *priv; // Owned by this object | 85 | ConfigPrivate priv; // Owned by this object |
103 | time_t mtime; | 86 | time_t mtime; |
104 | unsigned int size; | 87 | unsigned int size; |
105 | struct timeval used; | 88 | struct timeval used; |
106 | }; | 89 | }; |
107 | 90 | ||
108 | class ConfigCache : public QObject | ||
109 | { | ||
110 | public: | ||
111 | ConfigCache(); | ||
112 | 91 | ||
113 | void insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv); | 92 | class ConfigCache : public QObject { |
114 | bool find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv); | 93 | public: |
115 | void remove(const QString &filename); | 94 | static ConfigCache* instance(); |
116 | 95 | ||
96 | void insert( const QString& fileName, const ConfigGroupMap& cfg, | ||
97 | const ConfigPrivate *priv ); | ||
98 | bool find(const QString& fileName, ConfigGroupMap& cfg, | ||
99 | ConfigPrivate** priv ); | ||
117 | protected: | 100 | protected: |
118 | void timerEvent(QTimerEvent *); | 101 | void timerEvent( QTimerEvent* ); |
119 | 102 | ||
120 | private: | 103 | private: |
104 | ConfigCache(); | ||
105 | void remove( const QString& fileName ); | ||
121 | void removeLru(); | 106 | void removeLru(); |
122 | 107 | ||
123 | QMap<QString, ConfigData> configData; | 108 | private: |
124 | unsigned int totalsize; | 109 | QMap<QString, ConfigData> m_cached; |
125 | int tid; | 110 | unsigned int m_totalSize; |
111 | int m_tid; | ||
112 | private: | ||
113 | static ConfigCache* m_inst; | ||
114 | static const unsigned int CONFIG_CACHE_SIZE = 8192; | ||
115 | static const unsigned int CONFIG_CACHE_TIMEOUT = 1000; | ||
126 | }; | 116 | }; |
127 | 117 | ||
128 | ConfigCache::ConfigCache() : QObject(), totalsize(0), tid(0) | 118 | ConfigCache* ConfigCache::m_inst = 0; |
129 | { | 119 | /* |
130 | } | 120 | * get destroyed when qApp gets destroyed |
131 | 121 | */ | |
132 | void ConfigCache::insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv) | 122 | ConfigCache::ConfigCache() : QObject( qApp ), m_totalSize( 0 ), m_tid( 0 ) {} |
133 | { | 123 | ConfigCache* ConfigCache::instance() { |
134 | // use stat() rather than QFileInfo for speed. | 124 | if ( !m_inst ) |
135 | struct stat sbuf; | 125 | m_inst = new ConfigCache(); |
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 | 126 | ||
155 | if (totalsize > (uint)CONFIG_CACHE_SIZE) { | 127 | return m_inst; |
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 | } | 128 | } |
164 | 129 | ||
165 | bool ConfigCache::find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv) | 130 | void ConfigCache::remove( const QString& fileName ) { |
166 | { | 131 | QMap<QString, ConfigData>::Iterator it = m_cached.find( fileName ); |
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 | 132 | ||
190 | return FALSE; | 133 | if ( it == m_cached.end() ) |
191 | } | 134 | return; |
192 | 135 | ||
193 | void ConfigCache::remove(const QString &filename) | 136 | m_totalSize -= (*it).size; |
194 | { | 137 | m_cached.remove( it ); |
195 | QMap<QString, ConfigData>::Iterator it = configData.find(filename); | ||
196 | if (it != configData.end()) { | ||
197 | totalsize -= (*it).size; | ||
198 | configData.remove(it); | ||
199 | } | ||
200 | } | 138 | } |
201 | 139 | ||
202 | void ConfigCache::timerEvent(QTimerEvent *) | 140 | void ConfigCache::removeLru() { |
203 | { | 141 | QMap<QString, ConfigData>::Iterator it = m_cached.begin(); |
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; | 142 | QMap<QString, ConfigData>::Iterator lru = it; |
217 | ++it; | 143 | ++it; |
218 | for (; it != configData.end(); ++it) { | 144 | for (; it != m_cached.end(); ++it) |
219 | if ((*it).used.tv_sec < (*lru).used.tv_sec || | 145 | if ((*it).used.tv_sec < (*lru).used.tv_sec || |
220 | ((*it).used.tv_sec == (*lru).used.tv_sec && | 146 | ((*it).used.tv_sec == (*lru).used.tv_sec && |
221 | (*it).used.tv_usec < (*lru).used.tv_usec)) | 147 | (*it).used.tv_usec < (*lru).used.tv_usec)) |
222 | lru = it; | 148 | lru = it; |
223 | } | ||
224 | 149 | ||
225 | #ifdef DEBUG_CONFIG_CACHE | 150 | qWarning( "Removing item" ); |
226 | qDebug("Cache full, removing: %s", lru.key().latin1()); | 151 | m_totalSize -= (*lru).size; |
227 | #endif | 152 | m_cached.remove(lru); |
228 | totalsize -= (*lru).size; | 153 | } |
229 | configData.remove(lru); | 154 | |
155 | void ConfigCache::timerEvent( QTimerEvent* ) { | ||
156 | while ( m_totalSize > CONFIG_CACHE_SIZE ) | ||
157 | removeLru(); | ||
158 | |||
159 | killTimer(m_tid); | ||
160 | m_tid = 0; | ||
230 | } | 161 | } |
231 | 162 | ||
232 | static ConfigCache *qpe_configCache = 0; | 163 | void ConfigCache::insert( const QString& fileName, const ConfigGroupMap& cfg, |
164 | const ConfigPrivate* _priv ) { | ||
233 | 165 | ||
234 | #endif /* Q_OS_WIN32 */ | ||
235 | 166 | ||
167 | struct stat sbuf; | ||
168 | ::stat( QFile::encodeName(fileName), &sbuf ); | ||
169 | if ( static_cast<unsigned int>(sbuf.st_size) >= CONFIG_CACHE_SIZE>>1) | ||
170 | return; | ||
236 | 171 | ||
237 | // ========================================================================== | 172 | /* |
173 | * remove the old version and use the new one | ||
174 | */ | ||
175 | ConfigPrivate priv = _priv ? *_priv : ConfigPrivate(); | ||
176 | ConfigData data( cfg, priv, sbuf ); | ||
177 | m_totalSize += data.size; | ||
178 | |||
179 | remove( fileName ); | ||
180 | m_cached.insert( fileName, data ); | ||
181 | |||
182 | /* | ||
183 | * we've overcommited allocation, let us clean up | ||
184 | * soon | ||
185 | */ | ||
186 | if ( m_totalSize >= CONFIG_CACHE_SIZE ) | ||
187 | if ( !m_tid ) | ||
188 | m_tid = startTimer(CONFIG_CACHE_TIMEOUT); | ||
189 | } | ||
190 | |||
191 | bool ConfigCache::find( const QString& fileName, ConfigGroupMap& cfg, | ||
192 | ConfigPrivate **ppriv ) { | ||
193 | QMap<QString, ConfigData>::Iterator it = m_cached.find(fileName); | ||
194 | if (it != m_cached.end()) { | ||
195 | ConfigData &data = *it; | ||
196 | struct stat sbuf; | ||
197 | ::stat(QFile::encodeName( fileName ), &sbuf); | ||
198 | |||
199 | if (data.mtime == sbuf.st_mtime && (int)data.size == sbuf.st_size) { | ||
200 | cfg = data.cfg; | ||
201 | |||
202 | /* | ||
203 | * null pointer | ||
204 | */ | ||
205 | if ( *ppriv == 0 ) | ||
206 | *ppriv = new ConfigPrivate( data.priv ); | ||
207 | **ppriv = data.priv; | ||
208 | gettimeofday(&data.used, 0); | ||
209 | |||
210 | return true; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | return false; | ||
215 | } | ||
216 | |||
217 | #endif | ||
238 | 218 | ||
239 | 219 | ||
240 | /*! | 220 | /*! |
241 | \internal | 221 | \internal |
242 | */ | 222 | */ |
243 | QString Config::configFilename(const QString& name, Domain d) | 223 | QString Config::configFilename(const QString& name, Domain d) |
244 | { | 224 | { |
245 | switch (d) { | 225 | switch (d) { |
246 | case File: | 226 | case File: |
247 | return name; | 227 | return name; |
248 | case User: { | 228 | case User: { |
249 | QDir dir = (QString(getenv("HOME")) + "/Settings"); | 229 | QDir dir = (QString(getenv("HOME")) + "/Settings"); |
250 | if ( !dir.exists() ) | 230 | if ( !dir.exists() ) |
251 | mkdir(dir.path().local8Bit(),0700); | 231 | mkdir(dir.path().local8Bit(),0700); |
252 | return dir.path() + "/" + name + ".conf"; | 232 | return dir.path() + "/" + name + ".conf"; |
253 | } | 233 | } |
254 | } | 234 | } |
255 | return name; | 235 | return name; |
256 | } | 236 | } |
257 | 237 | ||
258 | /* This cannot be made public because of binary compat issues */ | 238 | /* This cannot be made public because of binary compat issues */ |
259 | void Config::read( QTextStream &s ) | 239 | void Config::read( QTextStream &s ) |
@@ -692,12 +672,14 @@ void Config::clearGroup() | |||
692 | 672 | ||
693 | /*! | 673 | /*! |
694 | \internal | 674 | \internal |
695 | */ | 675 | */ |
696 | void Config::write( const QString &fn ) | 676 | void Config::write( const QString &fn ) |
697 | { | 677 | { |
678 | QString oldGroup = git.key(); | ||
679 | |||
698 | QString strNewFile; | 680 | QString strNewFile; |
699 | if ( !fn.isEmpty() ) | 681 | if ( !fn.isEmpty() ) |
700 | filename = fn; | 682 | filename = fn; |
701 | strNewFile = filename + ".new"; | 683 | strNewFile = filename + ".new"; |
702 | 684 | ||
703 | QFile f( strNewFile ); | 685 | QFile f( strNewFile ); |
@@ -736,14 +718,14 @@ void Config::write( const QString &fn ) | |||
736 | filename.latin1() ); | 718 | filename.latin1() ); |
737 | QFile::remove( strNewFile ); | 719 | QFile::remove( strNewFile ); |
738 | return; | 720 | return; |
739 | } | 721 | } |
740 | 722 | ||
741 | #ifndef Q_OS_WIN32 | 723 | #ifndef Q_OS_WIN32 |
742 | if (qpe_configCache) | 724 | ConfigCache::instance()->insert( filename, groups, d ); |
743 | qpe_configCache->insert(filename, groups, d); | 725 | setGroup( oldGroup ); |
744 | #endif | 726 | #endif |
745 | changed = FALSE; | 727 | changed = FALSE; |
746 | } | 728 | } |
747 | 729 | ||
748 | /*! | 730 | /*! |
749 | Returns whether the Config is in a valid state. | 731 | Returns whether the Config is in a valid state. |
@@ -778,16 +760,14 @@ void Config::read() | |||
778 | git = groups.end(); | 760 | git = groups.end(); |
779 | return; | 761 | return; |
780 | } | 762 | } |
781 | } | 763 | } |
782 | 764 | ||
783 | #ifndef Q_OS_WIN32 | 765 | #ifndef Q_OS_WIN32 |
784 | if (!qpe_configCache) | ||
785 | qpe_configCache = new ConfigCache; | ||
786 | 766 | ||
787 | if (qpe_configCache->find(readFilename, groups, d)) { | 767 | if (ConfigCache::instance()->find(readFilename, groups, &d)) { |
788 | if ( d && d->multilang ) { | 768 | if ( d && d->multilang ) { |
789 | QStringList l = Global::languageList(); | 769 | QStringList l = Global::languageList(); |
790 | lang = l[0]; | 770 | lang = l[0]; |
791 | glang = l[1]; | 771 | glang = l[1]; |
792 | } | 772 | } |
793 | git = groups.begin(); | 773 | git = groups.begin(); |
@@ -809,13 +789,13 @@ void Config::read() | |||
809 | 789 | ||
810 | QTextStream s( &f ); | 790 | QTextStream s( &f ); |
811 | read( s ); | 791 | read( s ); |
812 | f.close(); | 792 | f.close(); |
813 | 793 | ||
814 | #ifndef Q_OS_WIN32 | 794 | #ifndef Q_OS_WIN32 |
815 | qpe_configCache->insert(readFilename, groups, d); | 795 | ConfigCache::instance()->insert(readFilename, groups, d); |
816 | #endif | 796 | #endif |
817 | } | 797 | } |
818 | 798 | ||
819 | /*! | 799 | /*! |
820 | \internal | 800 | \internal |
821 | */ | 801 | */ |