diff options
Diffstat (limited to 'src/tools/qgcache.cpp')
-rw-r--r-- | src/tools/qgcache.cpp | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/src/tools/qgcache.cpp b/src/tools/qgcache.cpp new file mode 100644 index 000000000..2465f4a47 --- /dev/null +++ b/src/tools/qgcache.cpp @@ -0,0 +1,866 @@ +/**************************************************************************** +** +** Implementation of TQGCache and TQGCacheIterator classes +** +** Created : 950208 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the tools module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qgcache.h" +#include "qptrlist.h" +#include "qdict.h" +#include "qstring.h" + +/*! + \class TQGCache qgcache.h + \reentrant + \ingroup shared + \ingroup collection + \brief The TQGCache class is an internal class for implementing TQCache + template classes. + + \internal + + TQGCache is a strictly internal class that acts as a base class for the + \link collection.html collection classes\endlink TQCache and TQIntCache. +*/ + + +/***************************************************************************** + TQGCacheItem class (internal cache item) + *****************************************************************************/ + +struct TQCacheItem +{ + TQCacheItem( void *k, TQPtrCollection::Item d, int c, short p ) + : priority(p), skipPriority(p), cost(c), key(k), data(d), node(0) {} + short priority; + short skipPriority; + int cost; + void *key; + TQPtrCollection::Item data; + TQLNode *node; +}; + + +/***************************************************************************** + TQCList class (internal list of cache items) + *****************************************************************************/ + +class TQCList : private TQPtrList<TQCacheItem> +{ +friend class TQGCacheIterator; +friend class TQCListIt; +public: + TQCList() {} + ~TQCList(); + + void insert( TQCacheItem * ); // insert according to priority + void insert( int, TQCacheItem * ); + void take( TQCacheItem * ); + void reference( TQCacheItem * ); + + void setAutoDelete( bool del ) { TQPtrCollection::setAutoDelete(del); } + + bool removeFirst() { return TQPtrList<TQCacheItem>::removeFirst(); } + bool removeLast() { return TQPtrList<TQCacheItem>::removeLast(); } + + TQCacheItem *first() { return TQPtrList<TQCacheItem>::first(); } + TQCacheItem *last() { return TQPtrList<TQCacheItem>::last(); } + TQCacheItem *prev() { return TQPtrList<TQCacheItem>::prev(); } + TQCacheItem *next() { return TQPtrList<TQCacheItem>::next(); } + +#if defined(QT_DEBUG) + int inserts; // variables for statistics + int insertCosts; + int insertMisses; + int finds; + int hits; + int hitCosts; + int dumps; + int dumpCosts; +#endif +}; + + +TQCList::~TQCList() +{ +#if defined(QT_DEBUG) + Q_ASSERT( count() == 0 ); +#endif +} + + +void TQCList::insert( TQCacheItem *ci ) +{ + TQCacheItem *item = first(); + while( item && item->skipPriority > ci->priority ) { + item->skipPriority--; + item = next(); + } + if ( item ) + TQPtrList<TQCacheItem>::insert( at(), ci ); + else + append( ci ); +#if defined(QT_DEBUG) + Q_ASSERT( ci->node == 0 ); +#endif + ci->node = currentNode(); +} + +inline void TQCList::insert( int i, TQCacheItem *ci ) +{ + TQPtrList<TQCacheItem>::insert( i, ci ); +#if defined(QT_DEBUG) + Q_ASSERT( ci->node == 0 ); +#endif + ci->node = currentNode(); +} + + +void TQCList::take( TQCacheItem *ci ) +{ + if ( ci ) { +#if defined(QT_DEBUG) + Q_ASSERT( ci->node != 0 ); +#endif + takeNode( ci->node ); + ci->node = 0; + } +} + + +inline void TQCList::reference( TQCacheItem *ci ) +{ +#if defined(QT_DEBUG) + Q_ASSERT( ci != 0 && ci->node != 0 ); +#endif + ci->skipPriority = ci->priority; + relinkNode( ci->node ); // relink as first item +} + + +class TQCListIt: public TQPtrListIterator<TQCacheItem> +{ +public: + TQCListIt( const TQCList *p ): TQPtrListIterator<TQCacheItem>( *p ) {} + TQCListIt( const TQCListIt *p ): TQPtrListIterator<TQCacheItem>( *p ) {} +}; + + +/***************************************************************************** + TQCDict class (internal dictionary of cache items) + *****************************************************************************/ + +// +// Since we need to decide if the dictionary should use an int or const +// char * key (the "bool trivial" argument in the constructor below) +// we cannot use the macro/template dict, but inherit directly from TQGDict. +// + +class TQCDict : public TQGDict +{ +public: + TQCDict( uint size, uint kt, bool caseSensitive, bool copyKeys ) + : TQGDict( size, (KeyType)kt, caseSensitive, copyKeys ) {} + ~TQCDict(); + + void clear() { TQGDict::clear(); } + + TQCacheItem *find_string(const TQString &key) const + { return (TQCacheItem*)((TQCDict*)this)->look_string(key, 0, 0); } + TQCacheItem *find_ascii(const char *key) const + { return (TQCacheItem*)((TQCDict*)this)->look_ascii(key, 0, 0); } + TQCacheItem *find_int(long key) const + { return (TQCacheItem*)((TQCDict*)this)->look_int(key, 0, 0); } + + TQCacheItem *take_string(const TQString &key) + { return (TQCacheItem*)TQGDict::take_string(key); } + TQCacheItem *take_ascii(const char *key) + { return (TQCacheItem*)TQGDict::take_ascii(key); } + TQCacheItem *take_int(long key) + { return (TQCacheItem*)TQGDict::take_int(key); } + + bool insert_string( const TQString &key, const TQCacheItem *ci ) + { return TQGDict::look_string(key,(Item)ci,1)!=0;} + bool insert_ascii( const char *key, const TQCacheItem *ci ) + { return TQGDict::look_ascii(key,(Item)ci,1)!=0;} + bool insert_int( long key, const TQCacheItem *ci ) + { return TQGDict::look_int(key,(Item)ci,1)!=0;} + + bool remove_string( TQCacheItem *item ) + { return TQGDict::remove_string(*((TQString*)(item->key)),item); } + bool remove_ascii( TQCacheItem *item ) + { return TQGDict::remove_ascii((const char *)item->key,item); } + bool remove_int( TQCacheItem *item ) + { return TQGDict::remove_int((long)item->key,item);} + + void statistics() { TQGDict::statistics(); } + +private: + void deleteItem( void *item ) + { if ( del_item ) { TQCacheItem *d = (TQCacheItem*)item; delete d; } } +}; + +inline TQCDict::~TQCDict() +{ + clear(); +} + +/***************************************************************************** + TQGDict member functions + *****************************************************************************/ + +/*! + Constructs a cache. + The maximum cost of the cache is given by \a maxCost and the size by \a + size. The key type is \a kt which may be \c StringKey, \c AsciiKey, + \c IntKey or \c PtrKey. The case-sensitivity of lookups is set with + \a caseSensitive. Keys are copied if \a copyKeys is TRUE. +*/ + +TQGCache::TQGCache( int maxCost, uint size, KeyType kt, bool caseSensitive, + bool copyKeys ) +{ + keytype = kt; + lruList = new TQCList; + Q_CHECK_PTR( lruList ); + lruList->setAutoDelete( TRUE ); + copyk = ((keytype == AsciiKey) && copyKeys); + dict = new TQCDict( size, kt, caseSensitive, FALSE ); + Q_CHECK_PTR( dict ); + mCost = maxCost; + tCost = 0; +#if defined(QT_DEBUG) + lruList->inserts = 0; + lruList->insertCosts = 0; + lruList->insertMisses = 0; + lruList->finds = 0; + lruList->hits = 0; + lruList->hitCosts = 0; + lruList->dumps = 0; + lruList->dumpCosts = 0; +#endif +} + +/*! + Cannot copy a cache. +*/ + +TQGCache::TQGCache( const TQGCache & ) + : TQPtrCollection() +{ +#if defined(QT_CHECK_NULL) + qFatal( "TQGCache::TQGCache(TQGCache &): Cannot copy a cache" ); +#endif +} + +/*! + Removes all items from the cache and destroys it. +*/ + +TQGCache::~TQGCache() +{ + clear(); + delete dict; + delete lruList; +} + +/*! + Cannot assign a cache. +*/ + +TQGCache &TQGCache::operator=( const TQGCache & ) +{ +#if defined(QT_CHECK_NULL) + qFatal( "TQGCache::operator=: Cannot copy a cache" ); +#endif + return *this; +} + + +/*! + Returns the number of items in the cache. +*/ + +uint TQGCache::count() const +{ + return dict->count(); +} + +/*! + Returns the size of the hash array. +*/ + +uint TQGCache::size() const +{ + return dict->size(); +} + +/*! + \fn int TQGCache::maxCost() const + + Returns the maximum cache cost. +*/ + +/*! + \fn int TQGCache::totalCost() const + + Returns the total cache cost. +*/ + +/*! + Sets the maximum cache cost to \a maxCost. +*/ + +void TQGCache::setMaxCost( int maxCost ) +{ + if ( maxCost < tCost ) { + if ( !makeRoomFor(tCost - maxCost) ) // remove excess cost + return; + } + mCost = maxCost; +} + + +/*! + Inserts an item with data \a data into the cache using key \a key. + The item has cost \a cost and priority \a priority. + + \warning If this function returns FALSE, you must delete \a data + yourself. Additionally, be very careful about using \a data after + calling this function, as any other insertions into the cache, from + anywhere in the application, or within TQt itself, could cause the + data to be discarded from the cache, and the pointer to become + invalid. +*/ + +bool TQGCache::insert_string( const TQString &key, TQPtrCollection::Item data, + int cost, int priority) +{ + if ( tCost + cost > mCost ) { + if ( !makeRoomFor(tCost + cost - mCost, priority) ) { +#if defined(QT_DEBUG) + lruList->insertMisses++; +#endif + return FALSE; + } + } +#if defined(QT_DEBUG) + Q_ASSERT( keytype == StringKey ); + lruList->inserts++; + lruList->insertCosts += cost; +#endif + if ( priority < -32768 ) + priority = -32768; + else if ( priority > 32767 ) + priority = 32677; + TQCacheItem *ci = new TQCacheItem( new TQString(key), newItem(data), + cost, (short)priority ); + Q_CHECK_PTR( ci ); + lruList->insert( 0, ci ); + dict->insert_string( key, ci ); + tCost += cost; + return TRUE; +} + +bool TQGCache::insert_other( const char *key, TQPtrCollection::Item data, + int cost, int priority) +{ + if ( tCost + cost > mCost ) { + if ( !makeRoomFor(tCost + cost - mCost, priority) ) { +#if defined(QT_DEBUG) + lruList->insertMisses++; +#endif + return FALSE; + } + } +#if defined(QT_DEBUG) + Q_ASSERT( keytype != StringKey ); + lruList->inserts++; + lruList->insertCosts += cost; +#endif + if ( keytype == AsciiKey && copyk ) + key = qstrdup( key ); + if ( priority < -32768 ) + priority = -32768; + else if ( priority > 32767 ) + priority = 32677; + TQCacheItem *ci = new TQCacheItem( (void*)key, newItem(data), cost, + (short)priority ); + Q_CHECK_PTR( ci ); + lruList->insert( 0, ci ); + if ( keytype == AsciiKey ) + dict->insert_ascii( key, ci ); + else + dict->insert_int( (long)key, ci ); + tCost += cost; + return TRUE; +} + + +/*! + Removes the item with key \a key from the cache. Returns TRUE if the + item was removed; otherwise returns FALSE. +*/ + +bool TQGCache::remove_string( const TQString &key ) +{ + Item d = take_string( key ); + if ( d ) + deleteItem( d ); + return d != 0; +} + +bool TQGCache::remove_other( const char *key ) +{ + Item d = take_other( key ); + if ( d ) + deleteItem( d ); + return d != 0; +} + + +/*! + Takes the item with key \a key out of the cache. The item is not + deleted. If no item has this \a key 0 is returned. +*/ + +TQPtrCollection::Item TQGCache::take_string( const TQString &key ) +{ + TQCacheItem *ci = dict->take_string( key ); // take from dict + Item d; + if ( ci ) { + d = ci->data; + tCost -= ci->cost; + lruList->take( ci ); // take from list + delete (TQString*)ci->key; + delete ci; + } else { + d = 0; + } + return d; +} + +/*! + Takes the item with key \a key out of the cache. The item is not + deleted. If no item has this \a key 0 is returned. +*/ + +TQPtrCollection::Item TQGCache::take_other( const char *key ) +{ + TQCacheItem *ci; + if ( keytype == AsciiKey ) + ci = dict->take_ascii( key ); + else + ci = dict->take_int( (long)key ); + Item d; + if ( ci ) { + d = ci->data; + tCost -= ci->cost; + lruList->take( ci ); // take from list + if ( copyk ) + delete [] (char *)ci->key; + delete ci; + } else { + d = 0; + } + return d; +} + + +/*! + Clears the cache. +*/ + +void TQGCache::clear() +{ + TQCacheItem *ci; + while ( (ci = lruList->first()) ) { + switch ( keytype ) { + case StringKey: + dict->remove_string( ci ); + delete (TQString*)ci->key; + break; + case AsciiKey: + dict->remove_ascii( ci ); + if ( copyk ) + delete [] (char*)ci->key; + break; + case IntKey: + dict->remove_int( ci ); + break; + case PtrKey: // unused + break; + } + deleteItem( ci->data ); // delete data + lruList->removeFirst(); // remove from list + } + tCost = 0; +} + + +/*! + Finds an item for \a key in the cache and adds a reference if \a ref is TRUE. +*/ + +TQPtrCollection::Item TQGCache::find_string( const TQString &key, bool ref ) const +{ + TQCacheItem *ci = dict->find_string( key ); +#if defined(QT_DEBUG) + lruList->finds++; +#endif + if ( ci ) { +#if defined(QT_DEBUG) + lruList->hits++; + lruList->hitCosts += ci->cost; +#endif + if ( ref ) + lruList->reference( ci ); + return ci->data; + } + return 0; +} + + +/*! + Finds an item for \a key in the cache and adds a reference if \a ref is TRUE. +*/ + +TQPtrCollection::Item TQGCache::find_other( const char *key, bool ref ) const +{ + TQCacheItem *ci = keytype == AsciiKey ? dict->find_ascii(key) + : dict->find_int((long)key); +#if defined(QT_DEBUG) + lruList->finds++; +#endif + if ( ci ) { +#if defined(QT_DEBUG) + lruList->hits++; + lruList->hitCosts += ci->cost; +#endif + if ( ref ) + lruList->reference( ci ); + return ci->data; + } + return 0; +} + + +/*! + Allocates cache space for one or more items. +*/ + +bool TQGCache::makeRoomFor( int cost, int priority ) +{ + if ( cost > mCost ) // cannot make room for more + return FALSE; // than maximum cost + if ( priority == -1 ) + priority = 32767; + register TQCacheItem *ci = lruList->last(); + int cntCost = 0; + int dumps = 0; // number of items to dump + while ( cntCost < cost && ci && ci->skipPriority <= priority ) { + cntCost += ci->cost; + ci = lruList->prev(); + dumps++; + } + if ( cntCost < cost ) // can enough cost be dumped? + return FALSE; // no +#if defined(QT_DEBUG) + Q_ASSERT( dumps > 0 ); +#endif + while ( dumps-- ) { + ci = lruList->last(); +#if defined(QT_DEBUG) + lruList->dumps++; + lruList->dumpCosts += ci->cost; +#endif + switch ( keytype ) { + case StringKey: + dict->remove_string( ci ); + delete (TQString*)ci->key; + break; + case AsciiKey: + dict->remove_ascii( ci ); + if ( copyk ) + delete [] (char *)ci->key; + break; + case IntKey: + dict->remove_int( ci ); + break; + case PtrKey: // unused + break; + } + deleteItem( ci->data ); // delete data + lruList->removeLast(); // remove from list + } + tCost -= cntCost; + return TRUE; +} + + +/*! + Outputs debug statistics. +*/ + +void TQGCache::statistics() const +{ +#if defined(QT_DEBUG) + TQString line; + line.fill( '*', 80 ); + qDebug( line.ascii() ); + qDebug( "CACHE STATISTICS:" ); + qDebug( "cache contains %d item%s, with a total cost of %d", + count(), count() != 1 ? "s" : "", tCost ); + qDebug( "maximum cost is %d, cache is %d%% full.", + mCost, (200*tCost + mCost) / (mCost*2) ); + qDebug( "find() has been called %d time%s", + lruList->finds, lruList->finds != 1 ? "s" : "" ); + qDebug( "%d of these were hits, items found had a total cost of %d.", + lruList->hits,lruList->hitCosts ); + qDebug( "%d item%s %s been inserted with a total cost of %d.", + lruList->inserts,lruList->inserts != 1 ? "s" : "", + lruList->inserts != 1 ? "have" : "has", lruList->insertCosts ); + qDebug( "%d item%s %s too large or had too low priority to be inserted.", + lruList->insertMisses, lruList->insertMisses != 1 ? "s" : "", + lruList->insertMisses != 1 ? "were" : "was" ); + qDebug( "%d item%s %s been thrown away with a total cost of %d.", + lruList->dumps, lruList->dumps != 1 ? "s" : "", + lruList->dumps != 1 ? "have" : "has", lruList->dumpCosts ); + qDebug( "Statistics from internal dictionary class:" ); + dict->statistics(); + qDebug( line.ascii() ); +#endif +} + + +/***************************************************************************** + TQGCacheIterator member functions + *****************************************************************************/ + +/*! + \class TQGCacheIterator qgcache.h + \reentrant + \ingroup shared + \ingroup collection + \brief The TQGCacheIterator class is an internal class for implementing TQCacheIterator and + TQIntCacheIterator. + + \internal + + TQGCacheIterator is a strictly internal class that does the heavy work for + TQCacheIterator and TQIntCacheIterator. +*/ + +/*! + Constructs an iterator that operates on the cache \a c. +*/ + +TQGCacheIterator::TQGCacheIterator( const TQGCache &c ) +{ + it = new TQCListIt( c.lruList ); +#if defined(QT_DEBUG) + Q_ASSERT( it != 0 ); +#endif +} + +/*! + Constructs an iterator that operates on the same cache as \a ci. +*/ + +TQGCacheIterator::TQGCacheIterator( const TQGCacheIterator &ci ) +{ + it = new TQCListIt( ci.it ); +#if defined(QT_DEBUG) + Q_ASSERT( it != 0 ); +#endif +} + +/*! + Destroys the iterator. +*/ + +TQGCacheIterator::~TQGCacheIterator() +{ + delete it; +} + +/*! + Assigns the iterator \a ci to this cache iterator. +*/ + +TQGCacheIterator &TQGCacheIterator::operator=( const TQGCacheIterator &ci ) +{ + *it = *ci.it; + return *this; +} + +/*! + Returns the number of items in the cache. +*/ + +uint TQGCacheIterator::count() const +{ + return it->count(); +} + +/*! + Returns TRUE if the iterator points to the first item. +*/ + +bool TQGCacheIterator::atFirst() const +{ + return it->atFirst(); +} + +/*! + Returns TRUE if the iterator points to the last item. +*/ + +bool TQGCacheIterator::atLast() const +{ + return it->atLast(); +} + +/*! + Sets the list iterator to point to the first item in the cache. +*/ + +TQPtrCollection::Item TQGCacheIterator::toFirst() +{ + TQCacheItem *item = it->toFirst(); + return item ? item->data : 0; +} + +/*! + Sets the list iterator to point to the last item in the cache. +*/ + +TQPtrCollection::Item TQGCacheIterator::toLast() +{ + TQCacheItem *item = it->toLast(); + return item ? item->data : 0; +} + +/*! + Returns the current item. +*/ + +TQPtrCollection::Item TQGCacheIterator::get() const +{ + TQCacheItem *item = it->current(); + return item ? item->data : 0; +} + +/*! + Returns the key of the current item. +*/ + +TQString TQGCacheIterator::getKeyString() const +{ + TQCacheItem *item = it->current(); + return item ? *((TQString*)item->key) : TQString::null; +} + +/*! + Returns the key of the current item, as a \0-terminated C string. +*/ + +const char *TQGCacheIterator::getKeyAscii() const +{ + TQCacheItem *item = it->current(); + return item ? (const char *)item->key : 0; +} + +/*! + Returns the key of the current item, as a long. +*/ + +long TQGCacheIterator::getKeyInt() const +{ + TQCacheItem *item = it->current(); + return item ? (long)item->key : 0; +} + +/*! + Moves to the next item (postfix). +*/ + +TQPtrCollection::Item TQGCacheIterator::operator()() +{ + TQCacheItem *item = it->operator()(); + return item ? item->data : 0; +} + +/*! + Moves to the next item (prefix). +*/ + +TQPtrCollection::Item TQGCacheIterator::operator++() +{ + TQCacheItem *item = it->operator++(); + return item ? item->data : 0; +} + +/*! + Moves \a jump positions forward. +*/ + +TQPtrCollection::Item TQGCacheIterator::operator+=( uint jump ) +{ + TQCacheItem *item = it->operator+=(jump); + return item ? item->data : 0; +} + +/*! + Moves to the previous item (prefix). +*/ + +TQPtrCollection::Item TQGCacheIterator::operator--() +{ + TQCacheItem *item = it->operator--(); + return item ? item->data : 0; +} + +/*! + Moves \a jump positions backward. +*/ + +TQPtrCollection::Item TQGCacheIterator::operator-=( uint jump ) +{ + TQCacheItem *item = it->operator-=(jump); + return item ? item->data : 0; +} |