Fix for UBSan build
[platform/upstream/doxygen.git] / qtools / qgcache.cpp
1 /****************************************************************************
2 ** 
3 **
4 ** Implementation of QGCache and QGCacheIterator classes
5 **
6 ** Created : 950208
7 **
8 ** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
9 **
10 ** This file is part of the tools module of the Qt GUI Toolkit.
11 **
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.
15 **
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.
20 **
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.
24 **
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.
27 **
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.
32 **
33 ** Contact info@trolltech.com if any conditions of this licensing are
34 ** not clear to you.
35 **
36 **********************************************************************/
37
38 #include "qgcache.h"
39 #include "qlist.h"
40 #include "qdict.h"
41 #include "qstring.h"
42
43
44 // NOT REVISED
45 /*!
46   \class QGCache qgcache.h
47
48   \brief The QGCache class is an internal class for implementing QCache template classes.
49
50   QGCache is a strictly internal class that acts as a base class for the
51   \link collection.html collection classes\endlink QCache and QIntCache.
52 */
53
54
55 /*****************************************************************************
56   QGCacheItem class (internal cache item)
57  *****************************************************************************/
58
59 struct QCacheItem
60 {
61     QCacheItem( void *k, QCollection::Item d, int c, short p )
62         : priority(p), skipPriority(p), cost(c), key(k), data(d), node(0) {}
63     short       priority;
64     short       skipPriority;
65     int         cost;
66     void       *key;
67     QCollection::Item data;
68     QLNode     *node;
69 };
70
71
72 /*****************************************************************************
73   QCList class (internal list of cache items)
74  *****************************************************************************/
75
76 class QCList : private QList<QCacheItem>
77 {
78 friend class QGCacheIterator;
79 friend class QCListIt;
80 public:
81     QCList()    {}
82    ~QCList();
83
84     void        insert( QCacheItem * );         // insert according to priority
85     void        insert( int, QCacheItem * );
86     void        take( QCacheItem * );
87     void        reference( QCacheItem * );
88
89     void        setAutoDelete( bool del ) { QCollection::setAutoDelete(del); }
90
91     bool        removeFirst()   { return QList<QCacheItem>::removeFirst(); }
92     bool        removeLast()    { return QList<QCacheItem>::removeLast(); }
93
94     QCacheItem *first()         { return QList<QCacheItem>::first(); }
95     QCacheItem *last()          { return QList<QCacheItem>::last(); }
96     QCacheItem *prev()          { return QList<QCacheItem>::prev(); }
97     QCacheItem *next()          { return QList<QCacheItem>::next(); }
98
99 #if defined(DEBUG)
100     int         inserts;                        // variables for statistics
101     int         insertCosts;
102     int         insertMisses;
103     int         finds;
104     int         hits;
105     int         hitCosts;
106     int         dumps;
107     int         dumpCosts;
108 #endif
109 };
110
111
112 QCList::~QCList()
113 {
114 #if defined(DEBUG)
115     ASSERT( count() == 0 );
116 #endif
117 }
118
119
120 void QCList::insert( QCacheItem *ci )
121 {
122     QCacheItem *item = first();
123     while( item && item->skipPriority > ci->priority ) {
124         item->skipPriority--;
125         item = next();
126     }
127     if ( item )
128         QList<QCacheItem>::insert( at(), ci );
129     else
130         append( ci );
131 #if defined(DEBUG)
132     ASSERT( ci->node == 0 );
133 #endif
134     ci->node = currentNode();
135 }
136
137 inline void QCList::insert( int i, QCacheItem *ci )
138 {
139     QList<QCacheItem>::insert( i, ci );
140 #if defined(DEBUG)
141     ASSERT( ci->node == 0 );
142 #endif
143     ci->node = currentNode();
144 }
145
146
147 void QCList::take( QCacheItem *ci )
148 {
149     if ( ci ) {
150 #if defined(DEBUG)
151         ASSERT( ci->node != 0 );
152 #endif
153         takeNode( ci->node );
154         ci->node = 0;
155     }
156 }
157
158
159 inline void QCList::reference( QCacheItem *ci )
160 {
161 #if defined(DEBUG)
162     ASSERT( ci != 0 && ci->node != 0 );
163 #endif
164     ci->skipPriority = ci->priority;
165     relinkNode( ci->node );                     // relink as first item
166 }
167
168
169 class QCListIt: public QListIterator<QCacheItem>
170 {
171 public:
172     QCListIt( const QCList *p ): QListIterator<QCacheItem>( *p ) {}
173     QCListIt( const QCListIt *p ): QListIterator<QCacheItem>( *p ) {}
174 };
175
176
177 /*****************************************************************************
178   QCDict class (internal dictionary of cache items)
179  *****************************************************************************/
180
181 //
182 // Since we need to decide if the dictionary should use an int or const
183 // char * key (the "bool trivial" argument in the constructor below)
184 // we cannot use the macro/template dict, but inherit directly from QGDict.
185 //
186
187 class QCDict : public QGDict
188 {
189 public:
190     QCDict( uint size, uint kt, bool caseSensitive, bool copyKeys )
191         : QGDict( size, (KeyType)kt, caseSensitive, copyKeys ) {}
192
193     QCacheItem *find_string(const QString &key) const
194         { return (QCacheItem*)((QCDict*)this)->look_string(key, 0, 0); }
195     QCacheItem *find_ascii(const char *key) const
196         { return (QCacheItem*)((QCDict*)this)->look_ascii(key, 0, 0); }
197     QCacheItem *find_int(long key) const
198         { return (QCacheItem*)((QCDict*)this)->look_int(key, 0, 0); }
199
200     QCacheItem *take_string(const QString &key)
201         { return (QCacheItem*)QGDict::take_string(key); }
202     QCacheItem *take_ascii(const char *key)
203         { return (QCacheItem*)QGDict::take_ascii(key); }
204     QCacheItem *take_int(long key)
205         { return (QCacheItem*)QGDict::take_int(key); }
206
207     bool  insert_string( const QString &key, const QCacheItem *ci )
208         { return QGDict::look_string(key,(Item)ci,1)!=0;}
209     bool  insert_ascii( const char *key, const QCacheItem *ci )
210         { return QGDict::look_ascii(key,(Item)ci,1)!=0;}
211     bool  insert_int( long key, const QCacheItem *ci )
212         { return QGDict::look_int(key,(Item)ci,1)!=0;}
213
214     bool  remove_string( QCacheItem *item )
215         { return QGDict::remove_string(*((QString*)(item->key)),item); }
216     bool  remove_ascii( QCacheItem *item )
217         { return QGDict::remove_ascii((const char *)item->key,item); }
218     bool  remove_int( QCacheItem *item )
219         { return QGDict::remove_int((long)item->key,item);}
220
221     void  statistics()                  { QGDict::statistics(); }
222 };
223
224
225 /*****************************************************************************
226   QGDict member functions
227  *****************************************************************************/
228
229 /*!
230   \internal
231   Constructs a cache.
232 */
233
234 QGCache::QGCache( int maxCost, uint size, KeyType kt, bool caseSensitive,
235                   bool copyKeys )
236 {
237     keytype = kt;
238     lruList = new QCList;
239     CHECK_PTR( lruList );
240     lruList->setAutoDelete( TRUE );
241     copyk   = ((keytype == AsciiKey) && copyKeys);
242     dict    = new QCDict( size, kt, caseSensitive, FALSE );
243     CHECK_PTR( dict );
244     mCost   = maxCost;
245     tCost   = 0;
246 #if defined(DEBUG)
247     lruList->inserts      = 0;
248     lruList->insertCosts  = 0;
249     lruList->insertMisses = 0;
250     lruList->finds        = 0;
251     lruList->hits         = 0;
252     lruList->hitCosts     = 0;
253     lruList->dumps        = 0;
254     lruList->dumpCosts    = 0;
255 #endif
256 }
257
258 /*!
259   \internal
260   Cannot copy a cache.
261 */
262
263 QGCache::QGCache( const QGCache & )
264     : QCollection()
265 {
266 #if defined(CHECK_NULL)
267     qFatal( "QGCache::QGCache(QGCache &): Cannot copy a cache" );
268 #endif
269 }
270
271 /*!
272   \internal
273   Removes all items from the cache and destroys it.
274 */
275
276 QGCache::~QGCache()
277 {
278     clear();                                    // delete everything first
279     delete dict;
280     delete lruList;
281 }
282
283 /*!
284   \internal
285   Cannot assign a cache.
286 */
287
288 QGCache &QGCache::operator=( const QGCache & )
289 {
290 #if defined(CHECK_NULL)
291     qFatal( "QGCache::operator=: Cannot copy a cache" );
292 #endif
293     return *this;                               // satisfy the compiler
294 }
295
296
297 /*!
298   \fn uint QGCache::count() const
299   \internal
300   Returns the number of items in the cache.
301 */
302
303 /*!
304   \fn uint QGCache::size() const
305   \internal
306   Returns the size of the hash array.
307 */
308
309 /*!
310   \fn int QGCache::maxCost() const
311   \internal
312   Returns the maximum cache cost.
313 */
314
315 /*!
316   \fn int QGCache::totalCost() const
317   \internal
318   Returns the total cache cost.
319 */
320
321 /*!
322   \internal
323   Sets the maximum cache cost.
324 */
325
326 void QGCache::setMaxCost( int maxCost )
327 {
328     if ( maxCost < tCost ) {
329         if ( !makeRoomFor(tCost - maxCost) )    // remove excess cost
330             return;
331     }
332     mCost = maxCost;
333 }
334
335
336 /*!
337   \internal
338   Inserts an item into the cache.
339
340   \warning If this function returns FALSE, you must delete \a data
341   yourself.  Additionally, be very careful about using \a data after
342   calling this function, as any other insertions into the cache, from
343   anywhere in the application, or within Qt itself, could cause the
344   data to be discarded from the cache, and the pointer to become
345   invalid.
346 */
347
348 bool QGCache::insert_string( const QString &key, QCollection::Item data,
349                              int cost, int priority)
350 {
351     if ( tCost + cost > mCost ) {
352         if ( !makeRoomFor(tCost + cost - mCost, priority) ) {
353 #if defined(DEBUG)
354             lruList->insertMisses++;
355 #endif
356             return FALSE;
357         }
358     }
359 #if defined(DEBUG)
360     ASSERT( keytype == StringKey );
361     lruList->inserts++;
362     lruList->insertCosts += cost;
363 #endif
364     if ( priority < -32768 )
365         priority = -32768;
366     else if ( priority > 32767 )
367         priority = 32677;
368     QCacheItem *ci = new QCacheItem( new QString(key), newItem(data),
369                                      cost, (short)priority );
370     CHECK_PTR( ci );
371     lruList->insert( 0, ci );
372     dict->insert_string( key, ci );
373     tCost += cost;
374     return TRUE;
375 }
376
377
378 /*! \internal */
379
380 bool QGCache::insert_other( const char *key, QCollection::Item data,
381                             int cost, int priority)
382 {
383     if ( tCost + cost > mCost ) {
384         if ( !makeRoomFor(tCost + cost - mCost, priority) ) {
385 #if defined(DEBUG)
386             lruList->insertMisses++;
387 #endif
388             return FALSE;
389         }
390     }
391 #if defined(DEBUG)
392     ASSERT( keytype != StringKey );
393     lruList->inserts++;
394     lruList->insertCosts += cost;
395 #endif
396     if ( keytype == AsciiKey && copyk )
397         key = qstrdup( key );
398     if ( priority < -32768 )
399         priority = -32768;
400     else if ( priority > 32767 )
401         priority = 32677;
402     QCacheItem *ci = new QCacheItem( (void*)key, newItem(data), cost,
403                                      (short)priority );
404     CHECK_PTR( ci );
405     lruList->insert( 0, ci );
406     if ( keytype == AsciiKey )
407         dict->insert_ascii( key, ci );
408     else
409         dict->insert_int( (long)key, ci );
410     tCost += cost;
411     return TRUE;
412 }
413
414
415 /*!
416   \internal
417   Removes an item from the cache.
418 */
419
420 bool QGCache::remove_string( const QString &key )
421 {
422     Item d = take_string( key );
423     if ( d )
424         deleteItem( d );
425     return d != 0;
426 }
427
428
429 /*! \internal */
430
431 bool QGCache::remove_other( const char *key )
432 {
433     Item d = take_other( key );
434     if ( d )
435         deleteItem( d );
436     return d != 0;
437 }
438
439
440 /*!
441   \internal
442   Takes an item out of the cache (no delete).
443 */
444
445 QCollection::Item QGCache::take_string( const QString &key )
446 {
447     QCacheItem *ci = dict->take_string( key );  // take from dict
448     Item d;
449     if ( ci ) {
450         d = ci->data;
451         tCost -= ci->cost;
452         lruList->take( ci );                    // take from list
453         delete (QString*)ci->key;
454         delete ci;
455     } else {
456         d = 0;
457     }
458     return d;
459 }
460
461 /*!
462   \internal
463   Takes an item out of the cache (no delete).
464 */
465
466 QCollection::Item QGCache::take_other( const char *key )
467 {
468     QCacheItem *ci;
469     if ( keytype == AsciiKey )
470         ci = dict->take_ascii( key );
471     else
472         ci = dict->take_int( (long)key );
473     Item d;
474     if ( ci ) {
475         d = ci->data;
476         tCost -= ci->cost;
477         lruList->take( ci );                    // take from list
478         if ( copyk )
479             delete [] (char *)ci->key;
480         delete ci;
481     } else {
482         d = 0;
483     }
484     return d;
485 }
486
487
488 /*!
489   \internal
490   Clears the cache.
491 */
492
493 void QGCache::clear()
494 {
495     QCacheItem *ci;
496     while ( (ci = lruList->first()) ) {
497         switch ( keytype ) {
498             case StringKey:
499                 dict->remove_string( ci );
500                 delete (QString*)ci->key;
501                 break;
502             case AsciiKey:
503                 dict->remove_ascii( ci );
504                 if ( copyk )
505                     delete [] (char*)ci->key;
506                 break;
507             case IntKey:
508                 dict->remove_int( ci );
509                 break;
510             case PtrKey:                        // unused
511                 break;
512         }
513         deleteItem( ci->data );                 // delete data
514         lruList->removeFirst();                 // remove from list
515     }
516     tCost = 0;
517 }
518
519
520 /*!
521   \internal
522   Finds an item in the cache.
523 */
524
525 QCollection::Item QGCache::find_string( const QString &key, bool ref ) const
526 {
527     QCacheItem *ci = dict->find_string( key );
528 #if defined(DEBUG)
529     lruList->finds++;
530 #endif
531     if ( ci ) {
532 #if defined(DEBUG)
533         lruList->hits++;
534         lruList->hitCosts += ci->cost;
535 #endif
536         if ( ref )
537             lruList->reference( ci );
538         return ci->data;
539     }
540     return 0;
541 }
542
543
544 /*!
545   \internal
546   Finds an item in the cache.
547 */
548
549 QCollection::Item QGCache::find_other( const char *key, bool ref ) const
550 {
551     QCacheItem *ci = keytype == AsciiKey ? dict->find_ascii(key)
552                                          : dict->find_int((long)key);
553 #if defined(DEBUG)
554     lruList->finds++;
555 #endif
556     if ( ci ) {
557 #if defined(DEBUG)
558         lruList->hits++;
559         lruList->hitCosts += ci->cost;
560 #endif
561         if ( ref )
562             lruList->reference( ci );
563         return ci->data;
564     }
565     return 0;
566 }
567
568
569 /*!
570   \internal
571   Allocates cache space for one or more items.
572 */
573
574 bool QGCache::makeRoomFor( int cost, int priority )
575 {
576     if ( cost > mCost )                         // cannot make room for more
577         return FALSE;                           //   than maximum cost
578     if ( priority == -1 )
579         priority = 32767;
580     register QCacheItem *ci = lruList->last();
581     int cntCost = 0;
582     int dumps   = 0;                            // number of items to dump
583     while ( cntCost < cost && ci && ci->skipPriority <= priority ) {
584         cntCost += ci->cost;
585         ci       = lruList->prev();
586         dumps++;
587     }
588     if ( cntCost < cost )                       // can enough cost be dumped?
589         return FALSE;                           // no
590 #if defined(DEBUG)
591     ASSERT( dumps > 0 );
592 #endif
593     while ( dumps-- ) {
594         ci = lruList->last();
595 #if defined(DEBUG)
596         lruList->dumps++;
597         lruList->dumpCosts += ci->cost;
598 #endif
599         switch ( keytype ) {
600             case StringKey:
601                 dict->remove_string( ci );
602                 delete (QString*)ci->key;
603                 break;
604             case AsciiKey:
605                 dict->remove_ascii( ci );
606                 if ( copyk )
607                     delete [] (char *)ci->key;
608                 break;
609             case IntKey:
610                 dict->remove_int( ci );
611                 break;
612             case PtrKey:                        // unused
613                 break;
614         }
615         deleteItem( ci->data );                 // delete data
616         lruList->removeLast();                  // remove from list
617     }
618     tCost -= cntCost;
619     return TRUE;
620 }
621
622
623 /*!
624   \internal
625   Outputs debug statistics.
626 */
627
628 void QGCache::statistics() const
629 {
630 #if defined(DEBUG)
631     QString line;
632     line.fill( '*', 80 );
633     qDebug( "%s",line.ascii() );
634     qDebug( "CACHE STATISTICS:" );
635     qDebug( "cache contains %d item%s, with a total cost of %d",
636            count(), count() != 1 ? "s" : "", tCost );
637     qDebug( "maximum cost is %d, cache is %d%% full.",
638            mCost, (200*tCost + mCost) / (mCost*2) );
639     qDebug( "find() has been called %d time%s",
640            lruList->finds, lruList->finds != 1 ? "s" : "" );
641     qDebug( "%d of these were hits, items found had a total cost of %d.",
642            lruList->hits,lruList->hitCosts );
643     qDebug( "%d item%s %s been inserted with a total cost of %d.",
644            lruList->inserts,lruList->inserts != 1 ? "s" : "",
645            lruList->inserts != 1 ? "have" : "has", lruList->insertCosts );
646     qDebug( "%d item%s %s too large or had too low priority to be inserted.",
647            lruList->insertMisses, lruList->insertMisses != 1 ? "s" : "",
648            lruList->insertMisses != 1 ? "were" : "was" );
649     qDebug( "%d item%s %s been thrown away with a total cost of %d.",
650            lruList->dumps, lruList->dumps != 1 ? "s" : "",
651            lruList->dumps != 1 ? "have" : "has", lruList->dumpCosts );
652     qDebug( "Statistics from internal dictionary class:" );
653     dict->statistics();
654     qDebug( "%s",line.ascii() );
655 #endif
656 }
657
658 int QGCache::hits() const
659 {
660   return lruList->hits;
661 }
662
663 int QGCache::misses() const
664 {
665   return lruList->finds - lruList->hits;
666 }
667
668
669 /*****************************************************************************
670   QGCacheIterator member functions
671  *****************************************************************************/
672
673 /*!
674   \class QGCacheIterator qgcache.h
675
676   \brief An internal class for implementing QCacheIterator and QIntCacheIterator.
677
678   QGCacheIterator is a strictly internal class that does the heavy work for
679   QCacheIterator and QIntCacheIterator.
680 */
681
682 /*!
683   \internal
684   Constructs an iterator that operates on the cache \e c.
685 */
686
687 QGCacheIterator::QGCacheIterator( const QGCache &c )
688 {
689     it = new QCListIt( c.lruList );
690 #if defined(DEBUG)
691     ASSERT( it != 0 );
692 #endif
693 }
694
695 /*!
696   \internal
697   Constructs an iterator that operates on the same cache as \e ci.
698 */
699
700 QGCacheIterator::QGCacheIterator( const QGCacheIterator &ci )
701 {
702     it = new QCListIt( ci.it );
703 #if defined(DEBUG)
704     ASSERT( it != 0 );
705 #endif
706 }
707
708 /*!
709   \internal
710   Destroys the iterator.
711 */
712
713 QGCacheIterator::~QGCacheIterator()
714 {
715     delete it;
716 }
717
718 /*!
719   \internal
720   Assigns the iterator \e ci to this cache iterator.
721 */
722
723 QGCacheIterator &QGCacheIterator::operator=( const QGCacheIterator &ci )
724 {
725     *it = *ci.it;
726     return *this;
727 }
728
729 /*!
730   \internal
731   Returns the number of items in the cache.
732 */
733
734 uint QGCacheIterator::count() const
735 {
736     return it->count();
737 }
738
739 /*!
740   \internal
741   Returns TRUE if the iterator points to the first item.
742 */
743
744 bool  QGCacheIterator::atFirst() const
745 {
746     return it->atFirst();
747 }
748
749 /*!
750   \internal
751   Returns TRUE if the iterator points to the last item.
752 */
753
754 bool QGCacheIterator::atLast() const
755 {
756     return it->atLast();
757 }
758
759 /*!
760   \internal
761   Sets the list iterator to point to the first item in the cache.
762 */
763
764 QCollection::Item QGCacheIterator::toFirst()
765 {
766     QCacheItem *item = it->toFirst();
767     return item ? item->data : 0;
768 }
769
770 /*!
771   \internal
772   Sets the list iterator to point to the last item in the cache.
773 */
774
775 QCollection::Item QGCacheIterator::toLast()
776 {
777     QCacheItem *item = it->toLast();
778     return item ? item->data : 0;
779 }
780
781 /*!
782   \internal
783   Returns the current item.
784 */
785
786 QCollection::Item QGCacheIterator::get() const
787 {
788     QCacheItem *item = it->current();
789     return item ? item->data : 0;
790 }
791
792 /*!
793   \internal
794   Returns the key of the current item.
795 */
796
797 QString QGCacheIterator::getKeyString() const
798 {
799     QCacheItem *item = it->current();
800     return item ? *((QString*)item->key) : QString::null;
801 }
802
803 /*!
804   \internal
805   Returns the key of the current item, as a \0-terminated C string.
806 */
807
808 const char *QGCacheIterator::getKeyAscii() const
809 {
810     QCacheItem *item = it->current();
811     return item ? (const char *)item->key : 0;
812 }
813
814 /*!
815   \internal
816   Returns the key of the current item, as a long.
817 */
818
819 long QGCacheIterator::getKeyInt() const
820 {
821     QCacheItem *item = it->current();
822     return item ? (long)item->key : 0;
823 }
824
825 /*!
826   \internal
827   Moves to the next item (postfix).
828 */
829
830 QCollection::Item QGCacheIterator::operator()()
831 {
832     QCacheItem *item = it->operator()();
833     return item ? item->data : 0;
834 }
835
836 /*!
837   \internal
838   Moves to the next item (prefix).
839 */
840
841 QCollection::Item QGCacheIterator::operator++()
842 {
843     QCacheItem *item = it->operator++();
844     return item ? item->data : 0;
845 }
846
847 /*!
848   \internal
849   Moves \e jumps positions forward.
850 */
851
852 QCollection::Item QGCacheIterator::operator+=( uint jump )
853 {
854     QCacheItem *item = it->operator+=(jump);
855     return item ? item->data : 0;
856 }
857
858 /*!
859   \internal
860   Moves to the previous item (prefix).
861 */
862
863 QCollection::Item QGCacheIterator::operator--()
864 {
865     QCacheItem *item = it->operator--();
866     return item ? item->data : 0;
867 }
868
869 /*!
870   \internal
871   Moves \e jumps positions backward.
872 */
873
874 QCollection::Item QGCacheIterator::operator-=( uint jump )
875 {
876     QCacheItem *item = it->operator-=(jump);
877     return item ? item->data : 0;
878 }