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 **********************************************************************/
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 QList<QGDictIterator>
69 QGDItList() : QList<QGDictIterator>() {}
70 QGDItList( const QGDItList &list ) : QList<QGDictIterator>(list) {}
71 ~QGDItList() { clear(); }
72 QGDItList &operator=(const QGDItList &list)
73 { return (QGDItList&)QList<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" );
132 register const char *k = 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
157 #ifndef QT_NO_DATASTREAM
160 Reads a collection/dictionary item from the stream \e s and returns a
161 reference to the stream.
163 The default implementation sets \e item to 0.
168 QDataStream& QGDict::read( QDataStream &s, QCollection::Item &item )
175 Writes a collection/dictionary item to the stream \e s and returns a
176 reference to the stream.
181 QDataStream& QGDict::write( QDataStream &s, QCollection::Item ) const
185 #endif //QT_NO_DATASTREAM
187 /*****************************************************************************
188 QGDict member functions
189 *****************************************************************************/
193 Constructs a dictionary.
196 QGDict::QGDict( uint len, KeyType kt, bool caseSensitive, bool copyKeys )
198 init( len, kt, caseSensitive, copyKeys );
202 void QGDict::init( uint len, KeyType kt, bool caseSensitive, bool copyKeys )
204 vec = new QBaseBucket *[vlen = len]; // allocate hash table
206 memset( (char*)vec, 0, vlen*sizeof(QBaseBucket*) );
209 // The caseSensitive and copyKey options don't make sense for
211 switch ( (keytype = (uint)kt) ) {
213 cases = caseSensitive;
217 cases = caseSensitive;
230 Constructs a copy of \e dict.
233 QGDict::QGDict( const QGDict & dict )
234 : QCollection( dict )
236 init( dict.vlen, (KeyType)dict.keytype, dict.cases, dict.copyk );
237 QGDictIterator it( dict );
238 while ( it.get() ) { // copy from other dict
241 look_string( it.getKeyString(), it.get(), op_insert );
244 look_ascii( it.getKeyAscii(), it.get(), op_insert );
247 look_int( it.getKeyInt(), it.get(), op_insert );
250 look_ptr( it.getKeyPtr(), it.get(), op_insert );
260 Removes all items from the dictionary and destroys it.
265 clear(); // delete everything
267 if ( !iterators ) // no iterators for this dict
269 QGDictIterator *i = iterators->first();
270 while ( i ) { // notify all iterators that
271 i->dict = 0; // this dict is deleted
272 i = iterators->next();
280 Assigns \e dict to this dictionary.
283 QGDict &QGDict::operator=( const QGDict &dict )
286 QGDictIterator it( dict );
287 while ( it.get() ) { // copy from other dict
290 look_string( it.getKeyString(), it.get(), op_insert );
293 look_ascii( it.getKeyAscii(), it.get(), op_insert );
296 look_int( it.getKeyInt(), it.get(), op_insert );
299 look_ptr( it.getKeyPtr(), it.get(), op_insert );
308 /*! \fn QCollection::Item QGDictIterator::get() const
314 /*! \fn QString QGDictIterator::getKeyString() const
320 /*! \fn const char * QGDictIterator::getKeyAscii() const
326 /*! \fn void * QGDictIterator::getKeyPtr() const
332 /*! \fn long QGDictIterator::getKeyInt() const
339 \fn uint QGDict::count() const
341 Returns the number of items in the dictionary.
345 \fn uint QGDict::size() const
347 Returns the size of the hash array.
353 The do-it-all function; op is one of op_find, op_insert, op_replace
356 QCollection::Item QGDict::look_string( const QString &key, QCollection::Item d, int op )
359 int index = hashKeyString(key) % vlen;
360 if ( op == op_find ) { // find
362 for ( n=(QStringBucket*)vec[index]; n;
363 n=(QStringBucket*)n->getNext() ) {
364 if ( key == n->getKey() )
365 return n->getData(); // item found
368 QString k = key.lower();
369 for ( n=(QStringBucket*)vec[index]; n;
370 n=(QStringBucket*)n->getNext() ) {
371 if ( k == n->getKey().lower() )
372 return n->getData(); // item found
375 return 0; // not found
377 if ( op == op_replace ) { // replace
378 if ( vec[index] != 0 ) // maybe something there
379 remove_string( key );
381 // op_insert or op_replace
382 n = new QStringBucket(key,newItem(d),vec[index]);
384 #if defined(CHECK_NULL)
385 if ( n->getData() == 0 )
386 qWarning( "QDict: Cannot insert null item" );
396 QCollection::Item QGDict::look_ascii( const char *key, QCollection::Item d, int op )
399 int index = hashKeyAscii(key) % vlen;
400 if ( op == op_find ) { // find
402 for ( n=(QAsciiBucket*)vec[index]; n;
403 n=(QAsciiBucket*)n->getNext() ) {
404 if ( qstrcmp(n->getKey(),key) == 0 )
405 return n->getData(); // item found
408 for ( n=(QAsciiBucket*)vec[index]; n;
409 n=(QAsciiBucket*)n->getNext() ) {
410 if ( qstricmp(n->getKey(),key) == 0 )
411 return n->getData(); // item found
414 return 0; // not found
416 if ( op == op_replace ) { // replace
417 if ( vec[index] != 0 ) // maybe something there
420 // op_insert or op_replace
421 n = new QAsciiBucket(copyk ? qstrdup(key) : key,newItem(d),vec[index]);
423 #if defined(CHECK_NULL)
424 if ( n->getData() == 0 )
425 qWarning( "QAsciiDict: Cannot insert null item" );
435 QCollection::Item QGDict::look_int( long key, QCollection::Item d, int op )
438 int index = (int)((ulong)key % vlen); // simple hash
439 if ( op == op_find ) { // find
440 for ( n=(QIntBucket*)vec[index]; n;
441 n=(QIntBucket*)n->getNext() ) {
442 if ( n->getKey() == key )
443 return n->getData(); // item found
445 return 0; // not found
447 if ( op == op_replace ) { // replace
448 if ( vec[index] != 0 ) // maybe something there
451 // op_insert or op_replace
452 n = new QIntBucket(key,newItem(d),vec[index]);
454 #if defined(CHECK_NULL)
455 if ( n->getData() == 0 )
456 qWarning( "QIntDict: Cannot insert null item" );
466 QCollection::Item QGDict::look_ptr( void *key, QCollection::Item d, int op )
469 int index = (int)((ulong)key % vlen); // simple hash
470 if ( op == op_find ) { // find
471 for ( n=(QPtrBucket*)vec[index]; n;
472 n=(QPtrBucket*)n->getNext() ) {
473 if ( n->getKey() == key )
474 return n->getData(); // item found
476 return 0; // not found
478 if ( op == op_replace ) { // replace
479 if ( vec[index] != 0 ) // maybe something there
482 // op_insert or op_replace
483 n = new QPtrBucket(key,newItem(d),vec[index]);
485 #if defined(CHECK_NULL)
486 if ( n->getData() == 0 )
487 qWarning( "QPtrDict: Cannot insert null item" );
497 Changes the size of the hashtable.
498 The contents of the dictionary are preserved,
499 but all iterators on the dictionary become invalid.
501 void QGDict::resize( uint newsize )
503 // Save old information
504 QBaseBucket **old_vec = vec;
505 uint old_vlen = vlen;
506 bool old_copyk = copyk;
508 vec = new QBaseBucket *[vlen = newsize];
510 memset( (char*)vec, 0, vlen*sizeof(QBaseBucket*) );
514 // Reinsert every item from vec, deleting vec as we go
515 for ( uint index = 0; index < old_vlen; index++ ) {
519 QStringBucket *n=(QStringBucket *)old_vec[index];
521 look_string( n->getKey(), n->getData(), op_insert );
522 QStringBucket *t=(QStringBucket *)n->getNext();
530 QAsciiBucket *n=(QAsciiBucket *)old_vec[index];
532 look_ascii( n->getKey(), n->getData(), op_insert );
533 QAsciiBucket *t=(QAsciiBucket *)n->getNext();
541 QIntBucket *n=(QIntBucket *)old_vec[index];
543 look_int( n->getKey(), n->getData(), op_insert );
544 QIntBucket *t=(QIntBucket *)n->getNext();
552 QPtrBucket *n=(QPtrBucket *)old_vec[index];
554 look_ptr( n->getKey(), n->getData(), op_insert );
555 QPtrBucket *t=(QPtrBucket *)n->getNext();
568 // Invalidate all iterators, since order is lost
569 if ( iterators && iterators->count() ) {
570 QGDictIterator *i = iterators->first();
573 i = iterators->next();
580 Unlinks the bucket with the specified key (and specified data pointer,
584 void QGDict::unlink_common( int index, QBaseBucket *node, QBaseBucket *prev )
586 if ( iterators && iterators->count() ) { // update iterators
587 QGDictIterator *i = iterators->first();
588 while ( i ) { // invalidate all iterators
589 if ( i->curNode == node ) // referring to pending node
591 i = iterators->next();
594 if ( prev ) // unlink node
595 prev->setNext( node->getNext() );
597 vec[index] = node->getNext();
601 QStringBucket *QGDict::unlink_string( const QString &key, QCollection::Item d )
603 if ( numItems == 0 ) // nothing in dictionary
606 QStringBucket *prev = 0;
607 int index = hashKeyString(key) % vlen;
609 for ( n=(QStringBucket*)vec[index]; n;
610 n=(QStringBucket*)n->getNext() ) {
611 bool found = (key == n->getKey());
613 found = (n->getData() == d);
615 unlink_common(index,n,prev);
621 QString k = key.lower();
622 for ( n=(QStringBucket*)vec[index]; n;
623 n=(QStringBucket*)n->getNext() ) {
624 bool found = (k == n->getKey().lower());
626 found = (n->getData() == d);
628 unlink_common(index,n,prev);
637 QAsciiBucket *QGDict::unlink_ascii( const char *key, QCollection::Item d )
639 if ( numItems == 0 ) // nothing in dictionary
642 QAsciiBucket *prev = 0;
643 int index = hashKeyAscii(key) % vlen;
644 for ( n=(QAsciiBucket *)vec[index]; n; n=(QAsciiBucket *)n->getNext() ) {
645 bool found = (cases ? qstrcmp(n->getKey(),key)
646 : qstricmp(n->getKey(),key)) == 0;
648 found = (n->getData() == d);
650 unlink_common(index,n,prev);
658 QIntBucket *QGDict::unlink_int( long key, QCollection::Item d )
660 if ( numItems == 0 ) // nothing in dictionary
663 QIntBucket *prev = 0;
664 int index = (int)((ulong)key % vlen);
665 for ( n=(QIntBucket *)vec[index]; n; n=(QIntBucket *)n->getNext() ) {
666 bool found = (n->getKey() == key);
668 found = (n->getData() == d);
670 unlink_common(index,n,prev);
678 QPtrBucket *QGDict::unlink_ptr( void *key, QCollection::Item d )
680 if ( numItems == 0 ) // nothing in dictionary
683 QPtrBucket *prev = 0;
684 int index = (int)((ulong)key % vlen);
685 for ( n=(QPtrBucket *)vec[index]; n; n=(QPtrBucket *)n->getNext() ) {
686 bool found = (n->getKey() == key);
688 found = (n->getData() == d);
690 unlink_common(index,n,prev);
701 Removes the item with the specified key. If item is non-null,
702 the remove will match the \a item as well (used to remove an
703 item when several items have the same key).
706 bool QGDict::remove_string( const QString &key, QCollection::Item item )
708 QStringBucket *n = unlink_string( key, item );
710 deleteItem( n->getData() );
721 bool QGDict::remove_ascii( const char *key, QCollection::Item item )
723 QAsciiBucket *n = unlink_ascii( key, item );
726 delete [] (char *)n->getKey();
727 deleteItem( n->getData() );
736 bool QGDict::remove_int( long key, QCollection::Item item )
738 QIntBucket *n = unlink_int( key, item );
740 deleteItem( n->getData() );
749 bool QGDict::remove_ptr( void *key, QCollection::Item item )
751 QPtrBucket *n = unlink_ptr( key, item );
753 deleteItem( n->getData() );
762 QCollection::Item QGDict::take_string( const QString &key )
764 QStringBucket *n = unlink_string( key );
778 QCollection::Item QGDict::take_ascii( const char *key )
780 QAsciiBucket *n = unlink_ascii( key );
784 delete [] (char *)n->getKey();
796 QCollection::Item QGDict::take_int( long key )
798 QIntBucket *n = unlink_int( key );
812 QCollection::Item QGDict::take_ptr( void *key )
814 QPtrBucket *n = unlink_ptr( key );
828 Removes all items from the dictionary.
835 numItems = 0; // disable remove() function
836 for ( uint j=0; j<vlen; j++ ) { // destroy hash table
841 QStringBucket *n=(QStringBucket *)vec[j];
843 QStringBucket *next = (QStringBucket*)n->getNext();
844 deleteItem( n->getData() );
852 QAsciiBucket *n=(QAsciiBucket *)vec[j];
854 QAsciiBucket *next = (QAsciiBucket*)n->getNext();
856 delete [] (char *)n->getKey();
857 deleteItem( n->getData() );
865 QIntBucket *n=(QIntBucket *)vec[j];
867 QIntBucket *next = (QIntBucket*)n->getNext();
868 deleteItem( n->getData() );
876 QPtrBucket *n=(QPtrBucket *)vec[j];
878 QPtrBucket *next = (QPtrBucket*)n->getNext();
879 deleteItem( n->getData() );
886 vec[j] = 0; // detach list of buckets
889 if ( iterators && iterators->count() ) { // invalidate all iterators
890 QGDictIterator *i = iterators->first();
893 i = iterators->next();
901 Outputs debug statistics.
904 void QGDict::statistics() const
908 line.fill( '-', 60 );
910 qDebug( "%s",line.ascii() );
911 qDebug( "DICTIONARY STATISTICS:" );
912 if ( count() == 0 ) {
914 qDebug( "%s", line.ascii() );
918 ideal = (float)count()/(2.0*size())*(count()+2.0*size()-1);
921 QBaseBucket *n = vec[i];
923 while ( n ) { // count number of buckets
927 real = real + (double)b * ((double)b+1.0)/2.0;
938 qDebug( "Array size = %d", size() );
939 qDebug( "# items = %d", count() );
940 qDebug( "Real dist = %g", real );
941 qDebug( "Rand dist = %g", ideal );
942 qDebug( "Real/Rand = %g", real/ideal );
943 qDebug( "%s",line.ascii() );
948 /*****************************************************************************
949 QGDict stream functions
950 *****************************************************************************/
951 #ifndef QT_NO_DATASTREAM
952 QDataStream &operator>>( QDataStream &s, QGDict &dict )
954 return dict.read( s );
957 QDataStream &operator<<( QDataStream &s, const QGDict &dict )
959 return dict.write( s );
962 #if defined(_CC_DEC_) && defined(__alpha) && (__DECCXX_VER >= 50190001)
963 #pragma message disable narrowptr
968 Reads a dictionary from the stream \e s.
971 QDataStream &QGDict::read( QDataStream &s )
974 s >> num; // read number of items
975 clear(); // clear dict
976 while ( num-- ) { // read all items
984 look_string( k, d, op_insert );
992 look_ascii( k, d, op_insert );
1002 look_int( (long)k, d, op_insert );
1010 // ### cannot insert 0 - this renders the thing
1011 // useless since all pointers are written as 0,
1012 // but hey, serializing pointers? can it be done
1015 look_ptr( (void *)k, d, op_insert );
1025 Writes the dictionary to the stream \e s.
1028 QDataStream& QGDict::write( QDataStream &s ) const
1030 s << count(); // write number of items
1032 while ( i<size() ) {
1033 QBaseBucket *n = vec[i];
1034 while ( n ) { // write all buckets
1035 switch ( keytype ) {
1037 s << ((QStringBucket*)n)->getKey();
1040 s << ((QAsciiBucket*)n)->getKey();
1043 s << (Q_UINT32)((QIntBucket*)n)->getKey();
1046 s << (Q_UINT32)0; // ### cannot serialize a pointer
1049 write( s, n->getData() ); // write data
1056 #endif //QT_NO_DATASTREAM
1058 /*****************************************************************************
1059 QGDictIterator member functions
1060 *****************************************************************************/
1063 \class QGDictIterator qgdict.h
1064 \brief An internal class for implementing QDictIterator and QIntDictIterator.
1066 QGDictIterator is a strictly internal class that does the heavy work for
1067 QDictIterator and QIntDictIterator.
1072 Constructs an iterator that operates on the dictionary \e d.
1075 QGDictIterator::QGDictIterator( const QGDict &d )
1077 dict = (QGDict *)&d; // get reference to dict
1078 toFirst(); // set to first noe
1079 if ( !dict->iterators ) {
1080 dict->iterators = new QGDItList; // create iterator list
1081 CHECK_PTR( dict->iterators );
1083 dict->iterators->append( this ); // attach iterator to dict
1088 Constructs a copy of the iterator \e it.
1091 QGDictIterator::QGDictIterator( const QGDictIterator &it )
1094 curNode = it.curNode;
1095 curIndex = it.curIndex;
1097 dict->iterators->append( this ); // attach iterator to dict
1102 Assigns a copy of the iterator \e it and returns a reference to this
1106 QGDictIterator &QGDictIterator::operator=( const QGDictIterator &it )
1108 if ( dict ) // detach from old dict
1109 dict->iterators->removeRef( this );
1111 curNode = it.curNode;
1112 curIndex = it.curIndex;
1114 dict->iterators->append( this ); // attach to new list
1120 Destroys the iterator.
1123 QGDictIterator::~QGDictIterator()
1125 if ( dict ) // detach iterator from dict
1126 dict->iterators->removeRef( this );
1132 Sets the iterator to point to the first item in the dictionary.
1135 QCollection::Item QGDictIterator::toFirst()
1138 #if defined(CHECK_NULL)
1139 qWarning( "QGDictIterator::toFirst: Dictionary has been deleted" );
1143 if ( dict->count() == 0 ) { // empty dictionary
1147 register uint i = 0;
1148 register QBaseBucket **v = dict->vec;
1151 curNode = dict->vec[i];
1153 return curNode->getData();
1159 Moves to the next item (postfix).
1162 QCollection::Item QGDictIterator::operator()()
1165 #if defined(CHECK_NULL)
1166 qWarning( "QGDictIterator::operator(): Dictionary has been deleted" );
1172 QCollection::Item d = curNode->getData();
1179 Moves to the next item (prefix).
1182 QCollection::Item QGDictIterator::operator++()
1185 #if defined(CHECK_NULL)
1186 qWarning( "QGDictIterator::operator++: Dictionary has been deleted" );
1192 curNode = curNode->getNext();
1193 if ( !curNode ) { // no next bucket
1194 register uint i = curIndex + 1; // look from next vec element
1195 register QBaseBucket **v = &dict->vec[i];
1196 while ( i < dict->size() && !(*v++) )
1198 if ( i == dict->size() ) { // nothing found
1202 curNode = dict->vec[i];
1205 return curNode->getData();
1210 Moves \e jumps positions forward.
1213 QCollection::Item QGDictIterator::operator+=( uint jumps )
1215 while ( curNode && jumps-- )
1217 return curNode ? curNode->getData() : 0;