1 /****************************************************************************
4 ** Implementation of QGDict and QGDictIterator classes
8 ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
10 ** This file is part of the tools module of the Qt GUI Toolkit.
12 ** This file may be distributed under the terms of the Q Public License
13 ** as defined by Trolltech AS of Norway and appearing in the file
14 ** LICENSE.QPL included in the packaging of this file.
16 ** This file may be distributed and/or modified under the terms of the
17 ** GNU General Public License version 2 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.GPL included in the
19 ** packaging of this file.
21 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22 ** licenses may use this file in accordance with the Qt Commercial License
23 ** Agreement provided with the Software.
25 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29 ** information about Qt Commercial License Agreements.
30 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
31 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
33 ** Contact info@trolltech.com if any conditions of this licensing are
36 **********************************************************************/
39 #include "qinternallist.h"
41 #include "qdatastream.h"
46 \class QGDict qgdict.h
47 \brief The QGDict class is an internal class for implementing QDict template classes.
49 QGDict is a strictly internal class that acts as a base class for the
50 \link collection.html collection classes\endlink QDict and QIntDict.
52 QGDict has some virtual functions that can be reimplemented to customize
55 <li> read() reads a collection/dictionary item from a QDataStream.
56 <li> write() writes a collection/dictionary item to a QDataStream.
58 Normally, you do not have to reimplement any of these functions.
61 static const int op_find = 0;
62 static const int op_insert = 1;
63 static const int op_replace = 2;
66 class QGDItList : public QInternalList<QGDictIterator>
69 QGDItList() : QInternalList<QGDictIterator>() {}
70 QGDItList( const QGDItList &list ) : QInternalList<QGDictIterator>(list) {}
71 ~QGDItList() { clear(); }
72 QGDItList &operator=(const QGDItList &list)
73 { return (QGDItList&)QInternalList<QGDictIterator>::operator=(list); }
77 /*****************************************************************************
78 Default implementation of special and virtual functions
79 *****************************************************************************/
83 Returns the hash key for \e key, when key is a string.
86 int QGDict::hashKeyString( const QString &key )
88 #if defined(CHECK_NULL)
90 qWarning( "QGDict::hashStringKey: Invalid null key" );
95 int len = key.length();
96 const QChar *p = key.unicode();
97 if ( cases ) { // case sensitive
98 for ( i=0; i<len; i++ ) {
99 h = (h<<4) + p[i].cell();
100 if ( (g = h & 0xf0000000) )
104 } else { // case insensitive
105 for ( i=0; i<len; i++ ) {
106 h = (h<<4) + p[i].lower().cell();
107 if ( (g = h & 0xf0000000) )
113 if ( index < 0 ) // adjust index to table size
120 Returns the hash key for \a key, which is a C string.
123 int QGDict::hashKeyAscii( const char *key )
125 #if defined(CHECK_NULL)
128 qWarning( "QGDict::hashAsciiKey: Invalid null key" );
135 if ( cases ) { // case sensitive
138 if ( (g = h & 0xf0000000) )
142 } else { // case insensitive
144 h = (h<<4) + tolower(*k);
145 if ( (g = h & 0xf0000000) )
152 if ( index < 0 ) // adjust index to table size
158 int QGDict::hashKeyAscii( const char *key )
160 #if defined(CHECK_NULL)
163 qWarning( "QGDict::hashAsciiKey: Invalid null key" );
167 unsigned int hash = 5381;
169 // use djb2 by Dan Bernstein
172 while ((c=*key++)) hash = ((hash<<5)+hash)+c;
176 while ((c=*key++)) hash = ((hash<<5)+hash)+tolower(c);
179 return index<0 ? -index : index;
183 #ifndef QT_NO_DATASTREAM
186 Reads a collection/dictionary item from the stream \e s and returns a
187 reference to the stream.
189 The default implementation sets \e item to 0.
194 QDataStream& QGDict::read( QDataStream &s, QCollection::Item &item )
201 Writes a collection/dictionary item to the stream \e s and returns a
202 reference to the stream.
207 QDataStream& QGDict::write( QDataStream &s, QCollection::Item ) const
211 #endif //QT_NO_DATASTREAM
213 /*****************************************************************************
214 QGDict member functions
215 *****************************************************************************/
219 Constructs a dictionary.
222 QGDict::QGDict( uint len, KeyType kt, bool caseSensitive, bool copyKeys )
224 init( len, kt, caseSensitive, copyKeys );
228 void QGDict::init( uint len, KeyType kt, bool caseSensitive, bool copyKeys )
230 vec = new QBaseBucket *[vlen = len]; // allocate hash table
232 memset( (char*)vec, 0, vlen*sizeof(QBaseBucket*) );
235 // The caseSensitive and copyKey options don't make sense for
237 switch ( (keytype = (uint)kt) ) {
239 cases = caseSensitive;
243 cases = caseSensitive;
256 Constructs a copy of \e dict.
259 QGDict::QGDict( const QGDict & dict )
260 : QCollection( dict )
262 init( dict.vlen, (KeyType)dict.keytype, dict.cases, dict.copyk );
263 QGDictIterator it( dict );
264 while ( it.get() ) { // copy from other dict
267 look_string( it.getKeyString(), it.get(), op_insert );
270 look_ascii( it.getKeyAscii(), it.get(), op_insert );
273 look_int( it.getKeyInt(), it.get(), op_insert );
276 look_ptr( it.getKeyPtr(), it.get(), op_insert );
286 Removes all items from the dictionary and destroys it.
291 clear(); // delete everything
293 if ( !iterators ) // no iterators for this dict
295 QGDictIterator *i = iterators->first();
296 while ( i ) { // notify all iterators that
297 i->dict = 0; // this dict is deleted
298 i = iterators->next();
306 Assigns \e dict to this dictionary.
309 QGDict &QGDict::operator=( const QGDict &dict )
312 QGDictIterator it( dict );
313 while ( it.get() ) { // copy from other dict
316 look_string( it.getKeyString(), it.get(), op_insert );
319 look_ascii( it.getKeyAscii(), it.get(), op_insert );
322 look_int( it.getKeyInt(), it.get(), op_insert );
325 look_ptr( it.getKeyPtr(), it.get(), op_insert );
334 /*! \fn QCollection::Item QGDictIterator::get() const
340 /*! \fn QString QGDictIterator::getKeyString() const
346 /*! \fn const char * QGDictIterator::getKeyAscii() const
352 /*! \fn void * QGDictIterator::getKeyPtr() const
358 /*! \fn long QGDictIterator::getKeyInt() const
365 \fn uint QGDict::count() const
367 Returns the number of items in the dictionary.
371 \fn uint QGDict::size() const
373 Returns the size of the hash array.
379 The do-it-all function; op is one of op_find, op_insert, op_replace
382 QCollection::Item QGDict::look_string( const QString &key, QCollection::Item d, int op )
385 int index = hashKeyString(key) % vlen;
386 if ( op == op_find ) { // find
388 for ( n=(QStringBucket*)vec[index]; n;
389 n=(QStringBucket*)n->getNext() ) {
390 if ( key == n->getKey() )
391 return n->getData(); // item found
394 QString k = key.lower();
395 for ( n=(QStringBucket*)vec[index]; n;
396 n=(QStringBucket*)n->getNext() ) {
397 if ( k == n->getKey().lower() )
398 return n->getData(); // item found
401 return 0; // not found
403 if ( op == op_replace ) { // replace
404 if ( vec[index] != 0 ) // maybe something there
405 remove_string( key );
407 // op_insert or op_replace
408 n = new QStringBucket(key,newItem(d),vec[index]);
410 #if defined(CHECK_NULL)
411 if ( n->getData() == 0 )
412 qWarning( "QDict: Cannot insert null item" );
422 QCollection::Item QGDict::look_ascii( const char *key, QCollection::Item d, int op )
425 int index = hashKeyAscii(key) % vlen;
426 if ( op == op_find ) { // find
428 for ( n=(QAsciiBucket*)vec[index]; n;
429 n=(QAsciiBucket*)n->getNext() ) {
430 if ( qstrcmp(n->getKey(),key) == 0 )
431 return n->getData(); // item found
434 for ( n=(QAsciiBucket*)vec[index]; n;
435 n=(QAsciiBucket*)n->getNext() ) {
436 if ( qstricmp(n->getKey(),key) == 0 )
437 return n->getData(); // item found
440 return 0; // not found
442 if ( op == op_replace ) { // replace
443 if ( vec[index] != 0 ) // maybe something there
446 // op_insert or op_replace
447 n = new QAsciiBucket(copyk ? qstrdup(key) : key,newItem(d),vec[index]);
449 #if defined(CHECK_NULL)
450 if ( n->getData() == 0 )
451 qWarning( "QAsciiDict: Cannot insert null item" );
461 QCollection::Item QGDict::look_int( long key, QCollection::Item d, int op )
464 int index = (int)((ulong)key % vlen); // simple hash
465 if ( op == op_find ) { // find
466 for ( n=(QIntBucket*)vec[index]; n;
467 n=(QIntBucket*)n->getNext() ) {
468 if ( n->getKey() == key )
469 return n->getData(); // item found
471 return 0; // not found
473 if ( op == op_replace ) { // replace
474 if ( vec[index] != 0 ) // maybe something there
477 // op_insert or op_replace
478 n = new QIntBucket(key,newItem(d),vec[index]);
480 #if defined(CHECK_NULL)
481 if ( n->getData() == 0 )
482 qWarning( "QIntDict: Cannot insert null item" );
492 QCollection::Item QGDict::look_ptr( void *key, QCollection::Item d, int op )
495 int index = (int)((uintptr_t)key % vlen); // simple hash
496 if ( op == op_find ) { // find
497 for ( n=(QPtrBucket*)vec[index]; n;
498 n=(QPtrBucket*)n->getNext() ) {
499 if ( n->getKey() == key )
500 return n->getData(); // item found
502 return 0; // not found
504 if ( op == op_replace ) { // replace
505 if ( vec[index] != 0 ) // maybe something there
508 // op_insert or op_replace
509 n = new QPtrBucket(key,newItem(d),vec[index]);
511 #if defined(CHECK_NULL)
512 if ( n->getData() == 0 )
513 qWarning( "QPtrDict: Cannot insert null item" );
523 Changes the size of the hashtable.
524 The contents of the dictionary are preserved,
525 but all iterators on the dictionary become invalid.
527 void QGDict::resize( uint newsize )
529 // Save old information
530 QBaseBucket **old_vec = vec;
531 uint old_vlen = vlen;
532 bool old_copyk = copyk;
534 vec = new QBaseBucket *[vlen = newsize];
536 memset( (char*)vec, 0, vlen*sizeof(QBaseBucket*) );
540 // Reinsert every item from vec, deleting vec as we go
541 for ( uint index = 0; index < old_vlen; index++ ) {
545 QStringBucket *n=(QStringBucket *)old_vec[index];
547 look_string( n->getKey(), n->getData(), op_insert );
548 QStringBucket *t=(QStringBucket *)n->getNext();
556 QAsciiBucket *n=(QAsciiBucket *)old_vec[index];
558 look_ascii( n->getKey(), n->getData(), op_insert );
559 QAsciiBucket *t=(QAsciiBucket *)n->getNext();
567 QIntBucket *n=(QIntBucket *)old_vec[index];
569 look_int( n->getKey(), n->getData(), op_insert );
570 QIntBucket *t=(QIntBucket *)n->getNext();
578 QPtrBucket *n=(QPtrBucket *)old_vec[index];
580 look_ptr( n->getKey(), n->getData(), op_insert );
581 QPtrBucket *t=(QPtrBucket *)n->getNext();
594 // Invalidate all iterators, since order is lost
595 if ( iterators && iterators->count() ) {
596 QGDictIterator *i = iterators->first();
599 i = iterators->next();
606 Unlinks the bucket with the specified key (and specified data pointer,
610 void QGDict::unlink_common( int index, QBaseBucket *node, QBaseBucket *prev )
612 if ( iterators && iterators->count() ) { // update iterators
613 QGDictIterator *i = iterators->first();
614 while ( i ) { // invalidate all iterators
615 if ( i->curNode == node ) // referring to pending node
617 i = iterators->next();
620 if ( prev ) // unlink node
621 prev->setNext( node->getNext() );
623 vec[index] = node->getNext();
627 QStringBucket *QGDict::unlink_string( const QString &key, QCollection::Item d )
629 if ( numItems == 0 ) // nothing in dictionary
632 QStringBucket *prev = 0;
633 int index = hashKeyString(key) % vlen;
635 for ( n=(QStringBucket*)vec[index]; n;
636 n=(QStringBucket*)n->getNext() ) {
637 bool found = (key == n->getKey());
639 found = (n->getData() == d);
641 unlink_common(index,n,prev);
647 QString k = key.lower();
648 for ( n=(QStringBucket*)vec[index]; n;
649 n=(QStringBucket*)n->getNext() ) {
650 bool found = (k == n->getKey().lower());
652 found = (n->getData() == d);
654 unlink_common(index,n,prev);
663 QAsciiBucket *QGDict::unlink_ascii( const char *key, QCollection::Item d )
665 if ( numItems == 0 ) // nothing in dictionary
668 QAsciiBucket *prev = 0;
669 int index = hashKeyAscii(key) % vlen;
670 for ( n=(QAsciiBucket *)vec[index]; n; n=(QAsciiBucket *)n->getNext() ) {
671 bool found = (cases ? qstrcmp(n->getKey(),key)
672 : qstricmp(n->getKey(),key)) == 0;
674 found = (n->getData() == d);
676 unlink_common(index,n,prev);
684 QIntBucket *QGDict::unlink_int( long key, QCollection::Item d )
686 if ( numItems == 0 ) // nothing in dictionary
689 QIntBucket *prev = 0;
690 int index = (int)((ulong)key % vlen);
691 for ( n=(QIntBucket *)vec[index]; n; n=(QIntBucket *)n->getNext() ) {
692 bool found = (n->getKey() == key);
694 found = (n->getData() == d);
696 unlink_common(index,n,prev);
704 QPtrBucket *QGDict::unlink_ptr( void *key, QCollection::Item d )
706 if ( numItems == 0 ) // nothing in dictionary
709 QPtrBucket *prev = 0;
710 int index = (int)((uintptr_t)key % vlen);
711 for ( n=(QPtrBucket *)vec[index]; n; n=(QPtrBucket *)n->getNext() ) {
712 bool found = (n->getKey() == key);
714 found = (n->getData() == d);
716 unlink_common(index,n,prev);
727 Removes the item with the specified key. If item is non-null,
728 the remove will match the \a item as well (used to remove an
729 item when several items have the same key).
732 bool QGDict::remove_string( const QString &key, QCollection::Item item )
734 QStringBucket *n = unlink_string( key, item );
736 deleteItem( n->getData() );
747 bool QGDict::remove_ascii( const char *key, QCollection::Item item )
749 QAsciiBucket *n = unlink_ascii( key, item );
752 delete [] (char *)n->getKey();
753 deleteItem( n->getData() );
762 bool QGDict::remove_int( long key, QCollection::Item item )
764 QIntBucket *n = unlink_int( key, item );
766 deleteItem( n->getData() );
775 bool QGDict::remove_ptr( void *key, QCollection::Item item )
777 QPtrBucket *n = unlink_ptr( key, item );
779 deleteItem( n->getData() );
788 QCollection::Item QGDict::take_string( const QString &key )
790 QStringBucket *n = unlink_string( key );
804 QCollection::Item QGDict::take_ascii( const char *key )
806 QAsciiBucket *n = unlink_ascii( key );
810 delete [] (char *)n->getKey();
822 QCollection::Item QGDict::take_int( long key )
824 QIntBucket *n = unlink_int( key );
838 QCollection::Item QGDict::take_ptr( void *key )
840 QPtrBucket *n = unlink_ptr( key );
854 Removes all items from the dictionary.
861 numItems = 0; // disable remove() function
862 for ( uint j=0; j<vlen; j++ ) { // destroy hash table
867 QStringBucket *n=(QStringBucket *)vec[j];
869 QStringBucket *next = (QStringBucket*)n->getNext();
870 deleteItem( n->getData() );
878 QAsciiBucket *n=(QAsciiBucket *)vec[j];
880 QAsciiBucket *next = (QAsciiBucket*)n->getNext();
882 delete [] (char *)n->getKey();
883 deleteItem( n->getData() );
891 QIntBucket *n=(QIntBucket *)vec[j];
893 QIntBucket *next = (QIntBucket*)n->getNext();
894 deleteItem( n->getData() );
902 QPtrBucket *n=(QPtrBucket *)vec[j];
904 QPtrBucket *next = (QPtrBucket*)n->getNext();
905 deleteItem( n->getData() );
912 vec[j] = 0; // detach list of buckets
915 if ( iterators && iterators->count() ) { // invalidate all iterators
916 QGDictIterator *i = iterators->first();
919 i = iterators->next();
927 Outputs debug statistics.
930 void QGDict::statistics() const
934 line.fill( '-', 60 );
936 qDebug( "%s",line.ascii() );
937 qDebug( "DICTIONARY STATISTICS:" );
938 if ( count() == 0 ) {
940 qDebug( "%s", line.ascii() );
944 ideal = (float)count()/(2.0*size())*(count()+2.0*size()-1);
947 QBaseBucket *n = vec[i];
949 while ( n ) { // count number of buckets
953 real = real + (double)b * ((double)b+1.0)/2.0;
964 qDebug( "Array size = %d", size() );
965 qDebug( "# items = %d", count() );
966 qDebug( "Real dist = %g", real );
967 qDebug( "Rand dist = %g", ideal );
968 qDebug( "Real/Rand = %g", real/ideal );
969 qDebug( "%s",line.ascii() );
974 /*****************************************************************************
975 QGDict stream functions
976 *****************************************************************************/
977 #ifndef QT_NO_DATASTREAM
978 QDataStream &operator>>( QDataStream &s, QGDict &dict )
980 return dict.read( s );
983 QDataStream &operator<<( QDataStream &s, const QGDict &dict )
985 return dict.write( s );
988 #if defined(_CC_DEC_) && defined(__alpha) && (__DECCXX_VER >= 50190001)
989 #pragma message disable narrowptr
994 Reads a dictionary from the stream \e s.
997 QDataStream &QGDict::read( QDataStream &s )
1000 s >> num; // read number of items
1001 clear(); // clear dict
1002 while ( num-- ) { // read all items
1004 switch ( keytype ) {
1010 look_string( k, d, op_insert );
1018 look_ascii( k, d, op_insert );
1028 look_int( k, d, op_insert );
1036 // ### cannot insert 0 - this renders the thing
1037 // useless since all pointers are written as 0,
1038 // but hey, serializing pointers? can it be done
1041 look_ptr( (void *)(uintptr_t)k, d, op_insert );
1051 Writes the dictionary to the stream \e s.
1054 QDataStream& QGDict::write( QDataStream &s ) const
1056 s << count(); // write number of items
1058 while ( i<size() ) {
1059 QBaseBucket *n = vec[i];
1060 while ( n ) { // write all buckets
1061 switch ( keytype ) {
1063 s << ((QStringBucket*)n)->getKey();
1066 s << ((QAsciiBucket*)n)->getKey();
1069 s << (Q_UINT32)((QIntBucket*)n)->getKey();
1072 s << (Q_UINT32)0; // ### cannot serialize a pointer
1075 write( s, n->getData() ); // write data
1082 #endif //QT_NO_DATASTREAM
1084 /*****************************************************************************
1085 QGDictIterator member functions
1086 *****************************************************************************/
1089 \class QGDictIterator qgdict.h
1090 \brief An internal class for implementing QDictIterator and QIntDictIterator.
1092 QGDictIterator is a strictly internal class that does the heavy work for
1093 QDictIterator and QIntDictIterator.
1098 Constructs an iterator that operates on the dictionary \e d.
1101 QGDictIterator::QGDictIterator( const QGDict &d )
1103 dict = (QGDict *)&d; // get reference to dict
1104 toFirst(); // set to first node
1105 if ( !dict->iterators ) {
1106 dict->iterators = new QGDItList; // create iterator list
1107 CHECK_PTR( dict->iterators );
1109 dict->iterators->append( this ); // attach iterator to dict
1114 Constructs a copy of the iterator \e it.
1117 QGDictIterator::QGDictIterator( const QGDictIterator &it )
1120 curNode = it.curNode;
1121 curIndex = it.curIndex;
1123 dict->iterators->append( this ); // attach iterator to dict
1128 Assigns a copy of the iterator \e it and returns a reference to this
1132 QGDictIterator &QGDictIterator::operator=( const QGDictIterator &it )
1134 if ( dict ) // detach from old dict
1135 dict->iterators->removeRef( this );
1137 curNode = it.curNode;
1138 curIndex = it.curIndex;
1140 dict->iterators->append( this ); // attach to new list
1146 Destroys the iterator.
1149 QGDictIterator::~QGDictIterator()
1151 if ( dict ) // detach iterator from dict
1152 dict->iterators->removeRef( this );
1158 Sets the iterator to point to the first item in the dictionary.
1161 QCollection::Item QGDictIterator::toFirst()
1164 #if defined(CHECK_NULL)
1165 qWarning( "QGDictIterator::toFirst: Dictionary has been deleted" );
1169 if ( dict->count() == 0 ) { // empty dictionary
1174 QBaseBucket **v = dict->vec;
1177 curNode = dict->vec[i];
1179 return curNode->getData();
1185 Moves to the next item (postfix).
1188 QCollection::Item QGDictIterator::operator()()
1191 #if defined(CHECK_NULL)
1192 qWarning( "QGDictIterator::operator(): Dictionary has been deleted" );
1198 QCollection::Item d = curNode->getData();
1205 Moves to the next item (prefix).
1208 QCollection::Item QGDictIterator::operator++()
1211 #if defined(CHECK_NULL)
1212 qWarning( "QGDictIterator::operator++: Dictionary has been deleted" );
1218 curNode = curNode->getNext();
1219 if ( !curNode ) { // no next bucket
1220 uint i = curIndex + 1; // look from next vec element
1221 QBaseBucket **v = &dict->vec[i];
1222 while ( i < dict->size() && !(*v++) )
1224 if ( i == dict->size() ) { // nothing found
1228 curNode = dict->vec[i];
1231 return curNode->getData();
1236 Moves \e jumps positions forward.
1239 QCollection::Item QGDictIterator::operator+=( uint jumps )
1241 while ( curNode && jumps-- )
1243 return curNode ? curNode->getData() : 0;