File indexing completed on 2024-09-15 11:58:02
0001 /* 0002 * This file is part of the DOM implementation for KDE. 0003 * 0004 * Copyright (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Library General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Library General Public License 0017 * along with this library; see the file COPYING.LIB. If not, write to 0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 * 0021 */ 0022 0023 #ifndef _MultiMap_h_ 0024 #define _MultiMap_h_ 0025 0026 #include <assert.h> 0027 #include <stdlib.h> 0028 0029 #include <QHash> 0030 #include <QList> 0031 #include <QSet> 0032 0033 template<class T> class MultiMapPtrList; 0034 0035 template<class K, class T> 0036 class KMultiMap 0037 { 0038 public: 0039 typedef QSet<T *> Set; 0040 typedef QHash<K *, Set *> Map; 0041 typedef MultiMapPtrList<T *> List; 0042 0043 KMultiMap() {} 0044 ~KMultiMap() 0045 { 0046 qDeleteAll(map); 0047 map.clear(); 0048 } 0049 0050 void insert(K *key, T *element) 0051 { 0052 Set *set = map.value(key); 0053 if (!set) { 0054 set = new Set(); 0055 map.insert(key, set); 0056 } 0057 if (!set->contains(element)) { 0058 set->insert(element); 0059 } 0060 } 0061 void remove(K *key, T *element) 0062 { 0063 Set *set = map.value(key); 0064 if (set) { 0065 set->remove(element); 0066 if (set->isEmpty()) { 0067 map.remove(key); 0068 delete set; 0069 } 0070 } 0071 } 0072 void remove(K *key) 0073 { 0074 Set *set = map.value(key); 0075 if (set) { 0076 map.remove(key); 0077 delete set; 0078 } 0079 } 0080 Set *find(K *key) 0081 { 0082 return map.value(key); 0083 } 0084 private: 0085 Map map; 0086 }; 0087 0088 static inline unsigned int stupidHash(void *ptr) 0089 { 0090 unsigned long val = (unsigned long)ptr; 0091 // remove alignment and multiply by a prime unlikely to be a factor of size 0092 val = (val >> 4) * 1237; 0093 return val; 0094 } 0095 0096 #define START_PTRLIST_SIZE 4 0097 #define MAX_PTRLIST_SIZE 27 0098 0099 class PtrListEntry 0100 { 0101 public: 0102 PtrListEntry(unsigned int log_size) : count(0), log_size(log_size), search(log_size), next(0) 0103 { 0104 // entry = new T* [size]; 0105 assert(log_size < MAX_PTRLIST_SIZE); 0106 entry = (void **)calloc((1 << log_size), sizeof(void *)); 0107 } 0108 ~PtrListEntry() 0109 { 0110 // delete[] entry; 0111 free(entry); 0112 } 0113 bool insert(void *e) 0114 { 0115 unsigned int t_size = size(); 0116 if (count == t_size) { 0117 return false; 0118 } 0119 unsigned int hash = stupidHash(e); 0120 void **firstFree = 0; 0121 // Only let elements be placed 'search' spots from their hash 0122 for (unsigned int j = 0; j < search; j++) { 0123 unsigned int i = (hash + j) & (t_size - 1); // modulo size 0124 // We need check to all hashes in 'search' to garuantee uniqueness 0125 if (entry[i] == 0) { 0126 if (!firstFree) { 0127 firstFree = entry + i; 0128 } 0129 } else if (entry[i] == e) { 0130 return true; 0131 } 0132 } 0133 if (firstFree) { 0134 *firstFree = e; 0135 count++; 0136 return true; 0137 } 0138 // We had more than 'search' collisions 0139 if (count < (t_size / 3) * 2) { 0140 // only 2/3 full => increase search 0141 unsigned int s = search * 2; 0142 if (s >= t_size) { 0143 s = t_size; 0144 } 0145 search = s; 0146 return insert(e); 0147 } 0148 return false; 0149 } 0150 // Insert another smaller set into this one 0151 // Is only garuantied to succede when this PtrList is new 0152 void insert(PtrListEntry *listEntry) 0153 { 0154 assert(size() >= listEntry->count * 2); 0155 unsigned int old_size = 1U << listEntry->log_size; 0156 for (unsigned int i = 0; i < old_size; i++) { 0157 bool s = true; 0158 void *e = listEntry->entry[i]; 0159 if (e) { 0160 s = insert(e); 0161 } 0162 assert(s); 0163 } 0164 } 0165 bool remove(void *e) 0166 { 0167 if (count == 0) { 0168 return false; 0169 } 0170 unsigned int size = (1U << log_size); 0171 unsigned int hash = stupidHash(e); 0172 // Elements are at most placed 'search' spots from their hash 0173 for (unsigned int j = 0; j < search; j++) { 0174 unsigned int i = (hash + j) & (size - 1); // modulo size 0175 if (entry[i] == e) { 0176 entry[i] = 0; 0177 count--; 0178 return true; 0179 } 0180 } 0181 return false; 0182 } 0183 bool contains(void *e) 0184 { 0185 if (count == 0) { 0186 return false; 0187 } 0188 unsigned int t_size = size(); 0189 unsigned int hash = stupidHash(e); 0190 // Elements are at most placed 'search' spots from their hash 0191 for (unsigned int j = 0; j < search; j++) { 0192 unsigned int i = (hash + j) & (t_size - 1); // modulo size 0193 if (entry[i] == e) { 0194 return true; 0195 } 0196 } 0197 return false; 0198 } 0199 void *at(unsigned int i) const 0200 { 0201 assert(i < (1U << log_size)); 0202 return entry[i]; 0203 } 0204 bool isEmpty() const 0205 { 0206 return count == 0; 0207 } 0208 bool isFull() const 0209 { 0210 return count == size(); 0211 } 0212 unsigned int size() const 0213 { 0214 return (1U << log_size); 0215 } 0216 0217 unsigned int count; 0218 const unsigned short log_size; 0219 unsigned short search; 0220 PtrListEntry *next; 0221 void **entry; 0222 }; 0223 0224 // An unsorted and unique PtrList that is implement as a linked list of hash-sets 0225 // Optimized for fast insert and fast lookup 0226 template<class T> 0227 class MultiMapPtrList 0228 { 0229 public: 0230 MultiMapPtrList(unsigned int init_size = 16) : m_first(0), m_current(0), m_pos(0) 0231 { 0232 assert(init_size > 0); 0233 unsigned int s = init_size - 1; 0234 unsigned int log_size = 0; 0235 while (s > 0) { 0236 log_size++; 0237 s = s >> 1; 0238 } 0239 m_first = new PtrListEntry(log_size); 0240 } 0241 MultiMapPtrList(const MultiMapPtrList &ptrList) : m_first(0), m_current(0), m_pos(0) 0242 { 0243 unsigned int t_count = ptrList.count(); 0244 unsigned int log_size = 0; 0245 while (t_count > 0) { 0246 log_size++; 0247 t_count = t_count >> 1; 0248 } 0249 // At least as large as the largest ptrListEntry in the original 0250 if (t_count < ptrList.m_first->log_size) { 0251 log_size = ptrList.m_first->log_size; 0252 } 0253 m_first = new PtrListEntry(log_size); 0254 0255 PtrListEntry *t_current = ptrList.m_first; 0256 while (t_current) { 0257 unsigned int t_size = t_current->size(); 0258 for (unsigned int i = 0; i < t_size; i++) { 0259 void *e = t_current->at(i); 0260 if (e != 0) { 0261 bool t = m_first->insert(e); 0262 if (!t) { 0263 // Make a new, but keep the size 0264 PtrListEntry *t_new = new PtrListEntry(log_size); 0265 t_new->insert(e); 0266 t_new->next = m_first; 0267 m_first = t_new; 0268 } 0269 } 0270 } 0271 t_current = t_current->next; 0272 } 0273 } 0274 ~MultiMapPtrList() 0275 { 0276 PtrListEntry *t_next, *t_current = m_first; 0277 while (t_current) { 0278 t_next = t_current->next; 0279 delete t_current; 0280 t_current = t_next; 0281 } 0282 } 0283 void append(T *e) 0284 { 0285 PtrListEntry *t_last = 0, *t_current = m_first; 0286 int count = 0; 0287 while (t_current) { 0288 if (t_current->insert(e)) { 0289 return; 0290 } 0291 t_last = t_current; 0292 t_current = t_current->next; 0293 count++; 0294 } 0295 // Create new hash-set 0296 unsigned int newsize = m_first->log_size + 1; 0297 if (newsize > MAX_PTRLIST_SIZE) { 0298 newsize = MAX_PTRLIST_SIZE; 0299 } 0300 t_current = new PtrListEntry(newsize); 0301 bool t = t_current->insert(e); 0302 assert(t); 0303 // Prepend it to the list, for insert effeciency 0304 t_current->next = m_first; 0305 m_first = t_current; 0306 // ### rehash some of the smaller sets 0307 /* 0308 if (count > 4) { 0309 // rehash the last in the new 0310 t_current->insert(t_last); 0311 }*/ 0312 } 0313 void remove(T *e) 0314 { 0315 PtrListEntry *t_next, *t_last = 0, *t_current = m_first; 0316 // Remove has to check every PtrEntry. 0317 while (t_current) { 0318 t_next = t_current->next; 0319 if (t_current->remove(e) && t_current->isEmpty()) { 0320 if (t_last) { 0321 t_last->next = t_current->next; 0322 } else { 0323 assert(m_first == t_current); 0324 m_first = t_current->next; 0325 } 0326 delete t_current; 0327 } else { 0328 t_last = t_current; 0329 } 0330 t_current = t_next; 0331 } 0332 } 0333 bool contains(T *e) 0334 { 0335 PtrListEntry *t_current = m_first; 0336 while (t_current) { 0337 if (t_current->contains(e)) { 0338 return true; 0339 } 0340 t_current = t_current->next; 0341 } 0342 return false; 0343 } 0344 bool isEmpty() 0345 { 0346 if (!m_first) { 0347 return true; 0348 } 0349 PtrListEntry *t_current = m_first; 0350 while (t_current) { 0351 if (!t_current->isEmpty()) { 0352 return false; 0353 } 0354 t_current = t_current->next; 0355 } 0356 return true; 0357 } 0358 unsigned int count() const 0359 { 0360 unsigned int t_count = 0; 0361 PtrListEntry *t_current = m_first; 0362 while (t_current) { 0363 t_count += t_current->count; 0364 t_current = t_current->next; 0365 } 0366 return t_count; 0367 } 0368 // Iterator functions: 0369 T *first() 0370 { 0371 m_current = m_first; 0372 m_pos = 0; 0373 // skip holes 0374 if (m_current && !m_current->at(m_pos)) { 0375 return next(); 0376 } else { 0377 return current(); 0378 } 0379 } 0380 T *current() 0381 { 0382 if (!m_current) { 0383 return (T *)0; 0384 } else { 0385 return (T *)m_current->at(m_pos); 0386 } 0387 } 0388 T *next() 0389 { 0390 if (!m_current) { 0391 return (T *)0; 0392 } 0393 m_pos++; 0394 if (m_pos >= m_current->size()) { 0395 m_current = m_current->next; 0396 m_pos = 0; 0397 } 0398 // skip holes 0399 if (m_current && !m_current->at(m_pos)) { 0400 return next(); 0401 } else { 0402 return current(); 0403 } 0404 } 0405 private: 0406 PtrListEntry *m_first; 0407 // iteration: 0408 PtrListEntry *m_current; 0409 unsigned int m_pos; 0410 }; 0411 0412 #undef START_PTRLIST_SIZE 0413 #undef MAX_PTRLIST_SIZE 0414 #endif