add more spacing
[personal-kdebase.git] / runtime / phonon / kded-module / hardwaredatabase.cpp
blob7846656c163a9d35c5d2a272a3892a8b84f0faf6
1 /* This file is part of the KDE project
2 Copyright (C) 2007 Matthias Kretz <kretz@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 or version 3 as published by the Free Software
7 Foundation.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
21 #include "hardwaredatabase.h"
22 #include <kcomponentdata.h>
23 #include <kdebug.h>
24 #include <kconfig.h>
25 #include <kconfiggroup.h>
26 #include <kglobal.h>
27 #include <ksavefile.h>
28 #include <kstandarddirs.h>
29 #include <QtCore/QCache>
30 #include <QtCore/QList>
31 #include <QtCore/QVarLengthArray>
32 #include <QtCore/QVector>
33 #include <QtCore/QDataStream>
34 #include <QtCore/QFile>
35 #include <QtCore/QFileInfo>
36 #include <QtCore/QSet>
37 #include <QtCore/QString>
38 #include <QtCore/QTimerEvent>
40 static const char CACHE_MAGIC[] = "PHwdbC";
41 static const quint32 CACHE_VERSION = 2;
42 static const quint32 HEADER_LENGTH = 14;
44 namespace PS
46 namespace HardwareDatabase
49 class HardwareDatabasePrivate : public QObject
51 public:
52 HardwareDatabasePrivate();
53 void createCache(const QString &dbFileName, const QString &cacheFileName);
54 bool validCacheHeader(QDataStream &cacheStream);
55 Entry *readEntry(const QString &uniqueId);
57 QCache<QString, Entry> entryCache;
59 protected:
60 void timerEvent(QTimerEvent *);
62 private:
63 int m_timerId;
64 QFile *m_cacheFile;
65 QString m_fileName;
68 K_GLOBAL_STATIC(HardwareDatabasePrivate, s_globalDB)
70 HardwareDatabasePrivate::HardwareDatabasePrivate()
71 : m_timerId(-1), m_cacheFile(0)
73 const QString dbFileName = KStandardDirs::locate("data", QLatin1String("libphonon/hardwaredatabase"));
74 if (dbFileName.isEmpty()) {
75 // no database, we're useless
76 return;
78 const QString cacheFileName =
79 KGlobal::mainComponent().dirs()->saveLocation("cache", QLatin1String("libphonon/"))
80 + QLatin1String("hardwaredatabase");
81 const QFileInfo dbFileInfo(dbFileName);
82 const QFileInfo cacheFileInfo(cacheFileName);
83 if (!cacheFileInfo.exists() || cacheFileInfo.lastModified() < dbFileInfo.lastModified()) {
84 // update cache file
85 createCache(dbFileName, cacheFileName);
86 } else {
87 m_cacheFile = new QFile(cacheFileName);
88 m_cacheFile->open(QIODevice::ReadOnly);
89 m_timerId = startTimer(0);
90 QDataStream cacheStream(m_cacheFile);
91 if (!validCacheHeader(cacheStream)) {
92 m_cacheFile->close();
93 delete m_cacheFile;
94 m_cacheFile = 0;
95 createCache(dbFileName, cacheFileName);
98 m_fileName = cacheFileName;
101 struct BucketEntry
103 BucketEntry() {}
104 BucketEntry(const uint &a, const quint32 &b) : hash(a), offset(b) {}
105 uint hash;
106 quint32 offset;
109 QDataStream &operator<<(QDataStream &s, const BucketEntry &e)
111 return s << e.hash << e.offset;
114 QDataStream &operator>>(QDataStream &s, BucketEntry &e)
116 return s >> e.hash >> e.offset;
119 void HardwareDatabasePrivate::createCache(const QString &dbFileName, const QString &cacheFileName)
121 KSaveFile cacheFile(cacheFileName);
122 QString name;
123 QString iconName;
124 int pref;
125 quint8 isAdvanced;
127 const KConfig dbFile(dbFileName, KConfig::CascadeConfig);
128 const bool opened = cacheFile.open(); // QIODevice::WriteOnly
129 Q_ASSERT(opened);
130 QDataStream cacheStream(&cacheFile);
131 cacheStream.writeRawData(CACHE_MAGIC, 6);
132 cacheStream << CACHE_VERSION << cacheStream.version() << quint32(0) << quint32(0);
133 QStringList allIds = dbFile.groupList();
134 QHash<uint, quint32> offsets;
135 offsets.reserve(allIds.count());
136 foreach (const QString &uniqueId, allIds) {
137 offsets.insertMulti(qHash(uniqueId), cacheFile.pos());
138 const KConfigGroup group = dbFile.group(uniqueId);
139 name = group.readEntry("name", QString());
140 iconName = group.readEntry("icon", QString());
141 pref = group.readEntry("initialPreference", 0);
142 if (group.hasKey("isAdvancedDevice")) {
143 isAdvanced = group.readEntry("isAdvancedDevice", false) ? 1 : 0;
144 } else {
145 isAdvanced = 2;
147 cacheStream << uniqueId << name << iconName << pref << isAdvanced;
149 //offsets.squeeze();
150 const quint32 hashTableBuckets = offsets.capacity();
151 const quint32 hashTableOffset = cacheFile.pos();
152 QVector<QList<BucketEntry> > bucketContents(hashTableBuckets);
154 QHashIterator<uint, quint32> it(offsets);
155 while (it.hasNext()) {
156 it.next();
157 const uint &h = it.key();
158 bucketContents[h % hashTableBuckets] << BucketEntry(h, it.value());
160 offsets.clear();
162 for (quint32 i = 0; i < hashTableBuckets; ++i) {
163 cacheStream << quint32(0);
165 QVarLengthArray<quint32, 4099> bucketOffsets(hashTableBuckets);
166 for (quint32 i = 0; i < hashTableBuckets; ++i) {
167 if (bucketContents[i].isEmpty()) {
168 bucketOffsets[i] = 0;
169 } else {
170 bucketOffsets[i] = cacheFile.pos();
171 cacheStream << bucketContents[i];
174 bucketContents.clear();
175 cacheFile.seek(hashTableOffset);
176 for (quint32 i = 0; i < hashTableBuckets; ++i) {
177 cacheStream << bucketOffsets[i];
179 cacheFile.seek(HEADER_LENGTH);
180 cacheStream << hashTableOffset << hashTableBuckets;
181 cacheFile.close();
184 bool HardwareDatabasePrivate::validCacheHeader(QDataStream &cacheStream)
186 char magic[6];
187 quint32 version;
188 int datastreamVersion;
189 const int read = cacheStream.readRawData(magic, 6);
190 cacheStream >> version >> datastreamVersion;
191 return (read == 6 && 0 == strncmp(magic, CACHE_MAGIC, 6) && version == CACHE_VERSION && datastreamVersion == cacheStream.version());
194 Entry *HardwareDatabasePrivate::readEntry(const QString &uniqueId)
196 QDataStream cacheStream;
197 if (m_cacheFile) {
198 if (m_cacheFile->seek(HEADER_LENGTH)) {
199 cacheStream.setDevice(m_cacheFile);
200 } else {
201 delete m_cacheFile;
202 m_cacheFile = 0;
205 if (!m_cacheFile) {
206 m_cacheFile = new QFile(m_fileName);
207 m_cacheFile->open(QIODevice::ReadOnly);
208 m_timerId = startTimer(0);
209 cacheStream.setDevice(m_cacheFile);
210 if (!validCacheHeader(cacheStream)) {
211 return 0;
214 quint32 hashTableOffset;
215 quint32 hashTableBuckets;
216 cacheStream >> hashTableOffset >> hashTableBuckets;
217 const uint h = qHash(uniqueId);
218 m_cacheFile->seek(hashTableOffset + (h % hashTableBuckets) * sizeof(quint32));
219 quint32 offset;
220 cacheStream >> offset;
221 //kDebug(601) << hashTableOffset << hashTableBuckets << uniqueId << h << offset;
222 if (0 == offset) {
223 return 0;
225 m_cacheFile->seek(offset);
226 QList<BucketEntry> bucket;
227 cacheStream >> bucket;
229 QString readUdi;
230 QString name;
231 QString iconName;
232 int pref;
233 quint8 isAdvanced;
235 foreach (const BucketEntry &entry, bucket) {
236 if (entry.hash == h) {
237 m_cacheFile->seek(entry.offset);
238 cacheStream >> readUdi;
239 if (readUdi == uniqueId) {
240 cacheStream >> name >> iconName >> pref >> isAdvanced;
241 Entry *e = new Entry(name, iconName, pref, isAdvanced);
242 s_globalDB->entryCache.insert(uniqueId, e);
243 return e;
248 return 0;
251 void HardwareDatabasePrivate::timerEvent(QTimerEvent *e)
253 if (e->timerId() == m_timerId) {
254 delete m_cacheFile;
255 m_cacheFile = 0;
256 killTimer(m_timerId);
257 } else {
258 QObject::timerEvent(e);
262 bool contains(const QString &uniqueId)
264 return (s_globalDB->entryCache[uniqueId] || s_globalDB->readEntry(uniqueId));
267 Entry entryFor(const QString &uniqueId)
269 Entry *e = s_globalDB->entryCache[uniqueId];
270 if (e) {
271 return *e;
273 e = s_globalDB->readEntry(uniqueId);
274 if (e) {
275 return *e;
277 return Entry();
280 } // namespace HardwareDatabase
281 } // namespace PS