summaryrefslogtreecommitdiffstats
path: root/kdecore/ksycocadict.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/ksycocadict.cpp')
-rw-r--r--kdecore/ksycocadict.cpp454
1 files changed, 454 insertions, 0 deletions
diff --git a/kdecore/ksycocadict.cpp b/kdecore/ksycocadict.cpp
new file mode 100644
index 000000000..cc7f87471
--- /dev/null
+++ b/kdecore/ksycocadict.cpp
@@ -0,0 +1,454 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "ksycocadict.h"
+#include "ksycocaentry.h"
+#include "ksycoca.h"
+
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <kdebug.h>
+#include <stdlib.h>
+
+namespace
+{
+struct string_entry {
+ string_entry(QString _key, KSycocaEntry *_payload)
+ { keyStr = _key; key = keyStr.unicode(); length = keyStr.length(); payload = _payload; hash = 0; }
+ uint hash;
+ int length;
+ const QChar *key;
+ QString keyStr;
+ KSycocaEntry *payload;
+};
+}
+
+template class QPtrList<string_entry>;
+
+class KSycocaDictStringList : public QPtrList<string_entry>
+{
+public:
+ KSycocaDictStringList();
+};
+
+KSycocaDictStringList::KSycocaDictStringList()
+{
+ setAutoDelete(true);
+}
+
+KSycocaDict::KSycocaDict()
+ : d(0), mStr(0), mOffset(0)
+{
+}
+
+KSycocaDict::KSycocaDict(QDataStream *str, int offset)
+ : d(0), mStr(str), mOffset(offset)
+{
+ Q_UINT32 test1, test2;
+ str->device()->at(offset);
+ (*str) >> test1 >> test2;
+ if ((test1 > 0x000fffff) || (test2 > 1024))
+ {
+ KSycoca::flagError();
+ mHashTableSize = 0;
+ mOffset = 0;
+ return;
+ }
+
+ str->device()->at(offset);
+ (*str) >> mHashTableSize;
+ (*str) >> mHashList;
+ mOffset = str->device()->at(); // Start of hashtable
+}
+
+KSycocaDict::~KSycocaDict()
+{
+ delete d;
+}
+
+void
+KSycocaDict::add(const QString &key, KSycocaEntry *payload)
+{
+ if (key.isEmpty()) return; // Not allowed (should never happen)
+ if (!payload) return; // Not allowed!
+ if (!d)
+ {
+ d = new KSycocaDictStringList();
+ }
+
+ string_entry *entry= new string_entry(key, payload);
+ d->append(entry);
+}
+
+void
+KSycocaDict::remove(const QString &key)
+{
+ if (d)
+ {
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ if (entry->keyStr == key)
+ {
+ d->remove();
+ break;
+ }
+ }
+ }
+}
+
+int
+KSycocaDict::find_string(const QString &key )
+{
+ //kdDebug(7011) << QString("KSycocaDict::find_string(%1)").arg(key) << endl;
+
+ if (!mStr || !mOffset)
+ {
+ kdError(7011) << "No database available!" << endl;
+ return 0;
+ }
+
+ if (mHashTableSize == 0)
+ return 0; // Unlikely to find anything :-]
+
+ // Read hash-table data
+ uint hash = hashKey(key) % mHashTableSize;
+ //kdDebug(7011) << QString("hash is %1").arg(hash) << endl;
+
+ uint off = mOffset+sizeof(Q_INT32)*hash;
+ //kdDebug(7011) << QString("off is %1").arg(off,8,16) << endl;
+ mStr->device()->at( off );
+
+ Q_INT32 offset;
+ (*mStr) >> offset;
+
+ //kdDebug(7011) << QString("offset is %1").arg(offset,8,16) << endl;
+ if (offset == 0)
+ return 0;
+
+ if (offset > 0)
+ return offset; // Positive ID
+
+ // Lookup duplicate list.
+ offset = -offset;
+
+ mStr->device()->at(offset);
+ //kdDebug(7011) << QString("Looking up duplicate list at %1").arg(offset,8,16) << endl;
+
+ while(true)
+ {
+ (*mStr) >> offset;
+ if (offset == 0) break;
+ QString dupkey;
+ (*mStr) >> dupkey;
+ //kdDebug(7011) << QString(">> %1 %2").arg(offset,8,16).arg(dupkey) << endl;
+ if (dupkey == key) return offset;
+ }
+ //kdWarning(7011) << "Not found!" << endl;
+
+ return 0;
+}
+
+uint
+KSycocaDict::count()
+{
+ if (!d) return 0;
+
+ return d->count();
+}
+
+void
+KSycocaDict::clear()
+{
+ delete d;
+ d = 0;
+}
+
+uint
+KSycocaDict::hashKey( const QString &key)
+{
+ int l = key.length();
+ register uint h = 0;
+
+ for(uint i = 0; i < mHashList.count(); i++)
+ {
+ int pos = mHashList[i];
+ if (pos < 0)
+ {
+ pos = -pos-1;
+ if (pos < l)
+ h = ((h * 13) + (key[l-pos].cell() % 29)) & 0x3ffffff;
+ }
+ else
+ {
+ pos = pos-1;
+ if (pos < l)
+ h = ((h * 13) + (key[pos].cell() % 29)) & 0x3ffffff;
+ }
+ }
+ return h;
+}
+
+//
+// Calculate the diversity of the strings at position 'pos'
+static int
+calcDiversity(KSycocaDictStringList *d, int pos, int sz)
+{
+ if (pos == 0) return 0;
+ bool *matrix = (bool *) calloc(sz, sizeof(bool));
+ uint usz = sz;
+
+ if (pos < 0)
+ {
+ pos = -pos-1;
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ register int l = entry->length;
+ if (pos < l && pos != 0)
+ {
+ register uint hash = ((entry->hash * 13) + (entry->key[l-pos].cell() % 29)) & 0x3ffffff;
+ matrix[ hash % usz ] = true;
+ }
+ }
+ }
+ else
+ {
+ pos = pos-1;
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ if (pos < entry->length)
+ {
+ register uint hash = ((entry->hash * 13) + (entry->key[pos].cell() % 29)) & 0x3ffffff;
+ matrix[ hash % usz ] = true;
+ }
+ }
+ }
+ int diversity = 0;
+ for(int i=0;i< sz;i++)
+ if (matrix[i]) diversity++;
+
+ free((void *) matrix);
+
+ return diversity;
+}
+
+//
+// Add the diversity of the strings at position 'pos'
+static void
+addDiversity(KSycocaDictStringList *d, int pos)
+{
+ if (pos == 0) return;
+ if (pos < 0)
+ {
+ pos = -pos-1;
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ register int l = entry->length;
+ if (pos < l)
+ entry->hash = ((entry->hash * 13) + (entry->key[l-pos].cell() % 29)) & 0x3fffffff;
+ }
+ }
+ else
+ {
+ pos = pos - 1;
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ if (pos < entry->length)
+ entry->hash = ((entry->hash * 13) + (entry->key[pos].cell() % 29)) & 0x3fffffff;
+ }
+ }
+}
+
+
+void
+KSycocaDict::save(QDataStream &str)
+{
+ if (count() == 0)
+ {
+ mHashTableSize = 0;
+ mHashList.clear();
+ str << mHashTableSize;
+ str << mHashList;
+ return;
+ }
+
+ mOffset = str.device()->at();
+
+ //kdDebug(7011) << QString("KSycocaDict: %1 entries.").arg(count()) << endl;
+
+ //kdDebug(7011) << "Calculating hash keys.." << endl;
+
+ int maxLength = 0;
+ //kdDebug(7011) << "Finding maximum string length" << endl;
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ entry->hash = 0;
+ if (entry->length > maxLength)
+ maxLength = entry->length;
+ }
+
+ //kdDebug(7011) << QString("Max string length = %1").arg(maxLength) << endl;
+
+ // use "almost prime" number for sz (to calculate diversity) and later
+ // for the table size of big tables
+ // int sz = d->count()*5-1;
+ register unsigned int sz = count()*4 + 1;
+ while(!(((sz % 3) && (sz % 5) && (sz % 7) && (sz % 11) && (sz % 13)))) sz+=2;
+
+ int maxDiv = 0;
+ int maxPos = 0;
+ int lastDiv = 0;
+
+ mHashList.clear();
+
+ // try to limit diversity scan by "predicting" positions
+ // with high diversity
+ int *oldvec=new int[maxLength*2+1];
+ for (int i=0; i<(maxLength*2+1); i++) oldvec[i]=0;
+ int mindiv=0;
+
+ while(true)
+ {
+ int divsum=0,divnum=0;
+
+ maxDiv = 0;
+ maxPos = 0;
+ for(int pos=-maxLength; pos <= maxLength; pos++)
+ {
+ // cut off
+ if (oldvec[pos+maxLength]<mindiv)
+ { oldvec[pos+maxLength]=0; continue; }
+
+ int diversity = calcDiversity(d, pos, sz);
+ if (diversity > maxDiv)
+ {
+ maxDiv = diversity;
+ maxPos = pos;
+ }
+ oldvec[pos+maxLength]=diversity;
+ divsum+=diversity; divnum++;
+ }
+ // arbitrary cut-off value 3/4 of average seems to work
+ if (divnum)
+ mindiv=(3*divsum)/(4*divnum);
+
+ if (maxDiv <= lastDiv)
+ break;
+ // qWarning("Max Div = %d at pos %d", maxDiv, maxPos);
+ lastDiv = maxDiv;
+ addDiversity(d, maxPos);
+ mHashList.append(maxPos);
+ }
+
+ delete [] oldvec;
+
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ entry->hash = hashKey(entry->keyStr);
+ }
+// fprintf(stderr, "Calculating minimum table size..\n");
+
+ mHashTableSize = sz;
+
+ struct hashtable_entry {
+ string_entry *entry;
+ QPtrList<string_entry> *duplicates;
+ int duplicate_offset;
+ };
+
+ hashtable_entry *hashTable = new hashtable_entry[ sz ];
+
+ //kdDebug(7011) << "Clearing hashtable..." << endl;
+ for (unsigned int i=0; i < sz; i++)
+ {
+ hashTable[i].entry = 0;
+ hashTable[i].duplicates = 0;
+ }
+
+ //kdDebug(7011) << "Filling hashtable..." << endl;
+ for(string_entry *entry=d->first(); entry; entry = d->next())
+ {
+ int hash = entry->hash % sz;
+ if (!hashTable[hash].entry)
+ { // First entry
+ hashTable[hash].entry = entry;
+ }
+ else
+ {
+ if (!hashTable[hash].duplicates)
+ { // Second entry, build duplicate list.
+ hashTable[hash].duplicates = new QPtrList<string_entry>();
+ hashTable[hash].duplicates->append(hashTable[hash].entry);
+ hashTable[hash].duplicate_offset = 0;
+ }
+ hashTable[hash].duplicates->append(entry);
+ }
+ }
+
+ str << mHashTableSize;
+ str << mHashList;
+
+ mOffset = str.device()->at(); // mOffset points to start of hashTable
+ //kdDebug(7011) << QString("Start of Hash Table, offset = %1").arg(mOffset,8,16) << endl;
+
+ for(int pass = 1; pass <= 2; pass++)
+ {
+ str.device()->at(mOffset);
+ //kdDebug(7011) << QString("Writing hash table (pass #%1)").arg(pass) << endl;
+ for(uint i=0; i < mHashTableSize; i++)
+ {
+ Q_INT32 tmpid;
+ if (!hashTable[i].entry)
+ tmpid = (Q_INT32) 0;
+ else if (!hashTable[i].duplicates)
+ tmpid = (Q_INT32) hashTable[i].entry->payload->offset(); // Positive ID
+ else
+ tmpid = (Q_INT32) -hashTable[i].duplicate_offset; // Negative ID
+ str << tmpid;
+ //kdDebug(7011) << QString("Hash table : %1").arg(tmpid,8,16) << endl;
+ }
+ //kdDebug(7011) << QString("End of Hash Table, offset = %1").arg(str.device()->at(),8,16) << endl;
+
+ //kdDebug(7011) << QString("Writing duplicate lists (pass #%1)").arg(pass) << endl;
+ for(uint i=0; i < mHashTableSize; i++)
+ {
+ if (hashTable[i].duplicates)
+ {
+ QPtrList<string_entry> *dups = hashTable[i].duplicates;
+ hashTable[i].duplicate_offset = str.device()->at();
+
+ /*kdDebug(7011) << QString("Duplicate lists: Offset = %1 list_size = %2") .arg(hashTable[i].duplicate_offset,8,16).arg(dups->count()) << endl;
+*/
+ for(string_entry *dup = dups->first(); dup; dup=dups->next())
+ {
+ str << (Q_INT32) dup->payload->offset(); // Positive ID
+ str << dup->keyStr; // Key (QString)
+ }
+ str << (Q_INT32) 0; // End of list marker (0)
+ }
+ }
+ //kdDebug(7011) << QString("End of Dict, offset = %1").arg(str.device()->at(),8,16) << endl;
+ }
+
+ //kdDebug(7011) << "Cleaning up hash table." << endl;
+ for(uint i=0; i < mHashTableSize; i++)
+ {
+ delete hashTable[i].duplicates;
+ }
+ delete [] hashTable;
+}
+