diff --git a/src/libCom/cxxTemplates/resourceLib.h b/src/libCom/cxxTemplates/resourceLib.h index 2acdf2426..c301795d8 100644 --- a/src/libCom/cxxTemplates/resourceLib.h +++ b/src/libCom/cxxTemplates/resourceLib.h @@ -78,7 +78,7 @@ template class resTableIter; // located with a hash key of type ID. // // NOTES: -// 1) class T _must_ derive from class ID and also from class tsSLNode +// 1) class T must derive from class ID and also from class tsSLNode // // 2) If the "resTable::show (unsigned level)" member function is called then // class T must also implement a "show (unsigned level)" member function which @@ -93,19 +93,7 @@ template class resTableIter; // // ID to hash index convert (see examples below) // resTableIndex hash (unsigned nBitsHashIndex) const; // -// 4) Classes of type ID must provide the following member functions -// (which will usually be static const inline for improved performance). -// They determine the minimum and maximum number of elements in the hash -// table. If minIndexBitWidth() == maxIndexBitWidth() then the hash table -// size is determined at compile time -// -// inline static const unsigned maxIndexBitWidth (); -// inline static const unsigned minIndexBitWidth (); -// -// max number of hash table elements = 1 << maxIndexBitWidth(); -// min number of hash table elements = 1 << minIndexBitWidth(); -// -// 5) Storage for identifier of type ID must persist until the item of type +// 4) Storage for identifier of type ID must persist until the item of type // T is deleted from the resTable // template @@ -123,24 +111,23 @@ public: void traverse ( void (T::*pCB)() ); void traverseConst ( void (T::*pCB)() const ) const; unsigned numEntriesInstalled () const; - // - // exceptions thrown - // - class epicsShareClass dynamicMemoryAllocationFailed {}; - class epicsShareClass sizeExceedsMaxIndexWidth {}; + void setTableSize ( const unsigned newTableSize ); private: tsSLList < T > * pTable; unsigned nextSplitIndex; unsigned hashIxMask; unsigned hashIxSplitMask; + unsigned nBitsHashIxSplitMask; + unsigned logBaseTwoTableSize; unsigned nInUse; resTableIndex hash ( const ID & idIn ) const; T * find ( tsSLList & list, const ID & idIn ) const; - T * findDelete ( tsSLList & list, const ID & idIn ); void splitBucket (); unsigned tableSize () const; + bool setTableSizePrivate ( unsigned logBaseTwoTableSize ); resTable ( const resTable & ); resTable & operator = ( const resTable & ); + static unsigned resTableBitMask ( const unsigned nBits ); friend class resTableIter; }; @@ -156,9 +143,9 @@ public: T * next (); T * operator () (); private: - tsSLIter iter; - unsigned index; - const resTable &table; + tsSLIter iter; + unsigned index; + const resTable &table; }; // @@ -194,8 +181,6 @@ public: bool operator == (const intId &idIn) const; resTableIndex hash () const; const T getId() const; - static const unsigned maxIndexBitWidth (); - static const unsigned minIndexBitWidth (); protected: T id; }; @@ -219,9 +204,11 @@ class chronIntIdResTable : public resTable { public: chronIntIdResTable (); virtual ~chronIntIdResTable (); - void add (ITEM &item); + void add ( ITEM & item ); private: unsigned allocId; + chronIntIdResTable ( const chronIntIdResTable & ); + chronIntIdResTable & operator = ( const chronIntIdResTable & ); }; // @@ -236,6 +223,7 @@ public: chronIntIdRes (); private: void setId (unsigned newId); + chronIntIdRes (const chronIntIdRes & ); }; // @@ -252,12 +240,6 @@ public: bool operator == (const stringId &idIn) const; const char * resourceName() const; // return the pointer to the string void show (unsigned level) const; - static const unsigned maxIndexBitWidth (); - static const unsigned minIndexBitWidth (); - // - // exceptions - // - class epicsShareClass dynamicMemoryAllocationFailed {}; private: stringId & operator = ( const stringId & ); stringId ( const stringId &); @@ -274,21 +256,15 @@ private: // resTable::resTable () // template -resTable::resTable () : - nextSplitIndex ( 0 ), - hashIxMask ( ( 1 << ID::minIndexBitWidth() ) - 1 ), - hashIxSplitMask ( ( 1 << ( ID::minIndexBitWidth() + 1 ) ) - 1 ), - nInUse ( 0 ) +inline resTable::resTable () : + pTable ( 0 ), nextSplitIndex ( 0 ), hashIxMask ( 0 ), + hashIxSplitMask ( 0 ), nBitsHashIxSplitMask ( 0 ), + logBaseTwoTableSize ( 0 ), nInUse ( 0 ) {} + +template +inline unsigned resTable::resTableBitMask ( const unsigned nBits ) { - unsigned newTableSize = this->hashIxSplitMask + 1; - this->pTable = ( tsSLList * ) - operator new ( newTableSize * sizeof ( tsSLList ) ); - if ( ! this->pTable ) { - throwWithLocation ( dynamicMemoryAllocationFailed () ); - } - for ( unsigned i = 0u; i < newTableSize; i++ ) { - new ( &this->pTable[i] ) tsSLList; - } + return ( 1 << nBits ) - 1; } // @@ -297,22 +273,48 @@ resTable::resTable () : // remove a res from the resTable // template -inline T * resTable::remove ( const ID &idIn ) +T * resTable::remove ( const ID &idIn ) { - tsSLList &list = this->pTable[ this->hash(idIn) ]; - return this->findDelete ( list, idIn ); + if ( this->pTable ) { + // search list for idIn and remove the first match + tsSLList & list = this->pTable [ this->hash(idIn) ]; + tsSLIter pItem = list.firstIter (); + T *pPrev = 0; + while ( pItem.valid () ) { + const ID & id = *pItem; + if ( id == idIn ) { + if ( pPrev ) { + list.remove ( *pPrev ); + } + else { + list.get (); + } + this->nInUse--; + break; + } + pPrev = pItem.pointer (); + pItem++; + } + return pItem.pointer (); + } + else { + return 0; + } } // // resTable::lookup () // -// find an res in the resTable -// template inline T * resTable::lookup ( const ID &idIn ) const { - tsSLList &list = this->pTable[this->hash(idIn)]; - return this->find (list, idIn); + if ( this->pTable ) { + tsSLList & list = this->pTable [ this->hash ( idIn ) ]; + return this->find ( list, idIn ); + } + else { + return 0; + } } // @@ -334,15 +336,15 @@ inline resTableIndex resTable::hash ( const ID & idIn ) const // template void resTable::show ( unsigned level ) const -{ - unsigned N = this->tableSize (); +{ + const unsigned N = this->tableSize (); printf ( "%u bucket hash table with %u items of type %s installed\n", N, this->nInUse, typeid(T).name() ); - { + if ( N ) { tsSLList * pList = this->pTable; - while ( pList < &this->pTable[N] ) { + while ( pList < & this->pTable[N] ) { tsSLIter pItem = pList->firstIter (); while ( pItem.valid () ) { tsSLIter pNext = pItem; @@ -355,12 +357,11 @@ void resTable::show ( unsigned level ) const } if ( level >=1u ) { - tsSLList *pList = this->pTable; double X = 0.0; double XX = 0.0; unsigned maxEntries = 0u; for ( unsigned i = 0u; i < N; i++ ) { - tsSLIter pItem = pList[i].firstIter (); + tsSLIter pItem = this->pTable[i].firstIter (); unsigned count = 0; while ( pItem.valid () ) { if ( level >= 3u ) { @@ -389,14 +390,34 @@ void resTable::show ( unsigned level ) const } } +// self test template void resTable::verify () const { - unsigned N = this->tableSize (); + const unsigned N = this->tableSize (); + + if ( this->pTable ) { + assert ( this->nextSplitIndex <= this->hashIxMask + 1 ); + assert ( this->hashIxMask ); + assert ( this->hashIxMask == this->hashIxSplitMask >> 1 ); + assert ( this->hashIxSplitMask ); + assert ( this->nBitsHashIxSplitMask ); + assert ( resTableBitMask ( this->nBitsHashIxSplitMask ) + == this->hashIxSplitMask ); + assert ( this->logBaseTwoTableSize ); + assert ( this->nBitsHashIxSplitMask <= this->logBaseTwoTableSize ); + } + else { + assert ( this->nextSplitIndex == 0 ); + assert ( this->hashIxMask == 0 ); + assert ( this->hashIxSplitMask == 0 ); + assert ( this->nBitsHashIxSplitMask == 0 ); + assert ( this->logBaseTwoTableSize == 0 ); + } + unsigned total = 0u; - tsSLList *pList = this->pTable; for ( unsigned i = 0u; i < N; i++ ) { - tsSLIter pItem = pList[i].firstIter (); + tsSLIter pItem = this->pTable[i].firstIter (); unsigned count = 0; while ( pItem.valid () ) { resTableIndex index = this->hash ( *pItem ); @@ -416,17 +437,15 @@ void resTable::verify () const template void resTable::traverse ( void (T::*pCB)() ) { - tsSLList * pList = this->pTable; - unsigned N = this->tableSize (); - while ( pList < &this->pTable[N] ) { - tsSLIter pItem = pList->firstIter (); + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + tsSLIter pItem = this->pTable[i].firstIter (); while ( pItem.valid () ) { tsSLIter pNext = pItem; pNext++; ( pItem.pointer ()->*pCB ) (); pItem = pNext; } - pList++; } } @@ -436,19 +455,16 @@ void resTable::traverse ( void (T::*pCB)() ) template void resTable::traverseConst ( void (T::*pCB)() const ) const { - const tsSLList *pList; - - pList = this->pTable; - unsigned N = this->tableSize (); - while ( pList < &this->pTable[N] ) { - tsSLIterConst pItem = pList->firstIter (); + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + const tsSLList < T > & table = this->pTable[i]; + tsSLIterConst pItem = table.firstIter (); while ( pItem.valid () ) { tsSLIterConst pNext = pItem; pNext++; ( pItem.pointer ()->*pCB ) (); pItem = pNext; } - pList++; } } @@ -459,9 +475,95 @@ inline unsigned resTable::numEntriesInstalled () const } template -unsigned resTable::tableSize () const +inline unsigned resTable::tableSize () const { - return ( this->hashIxMask + 1 ) + this->nextSplitIndex; + if ( this->pTable ) { + return ( this->hashIxMask + 1 ) + this->nextSplitIndex; + } + else { + return 0; + } +} + +// it will be more efficent to call this once prior to installing +// the first entry +template +void resTable::setTableSize ( const unsigned newTableSize ) +{ + if ( newTableSize == 0u ) { + return; + } + + // + // count the number of bits in newTableSize and round up + // to the next power of two + // + unsigned newMask = newTableSize - 1; + unsigned nbits; + for ( nbits = 0; nbits < sizeof (newTableSize) * CHAR_BIT; nbits++ ) { + unsigned nBitsMask = resTableBitMask ( nbits ); + if ( ( newMask & ~nBitsMask ) == 0){ + break; + } + } + setTableSizePrivate ( nbits ); +} + +template +bool resTable::setTableSizePrivate ( unsigned logBaseTwoTableSize ) +{ + // dont shrink + if ( this->logBaseTwoTableSize >= logBaseTwoTableSize ) { + return true; + } + + // dont allow ridiculously small tables + if ( logBaseTwoTableSize < 4 ) { + logBaseTwoTableSize = 4; + } + + const unsigned newTableSize = 1 << logBaseTwoTableSize; + const unsigned oldTableSize = this->pTable ? 1 << this->logBaseTwoTableSize : 0; + const unsigned oldTableOccupiedSize = this->tableSize (); + + tsSLList * pNewTable = ( tsSLList * ) + operator new ( newTableSize * sizeof ( tsSLList ), std::nothrow ); + if ( ! pNewTable ) { + if ( ! this->pTable ) { + throw std::bad_alloc(); + } + return false; + } + + // run the constructors using placement new + unsigned i; + for ( i = 0u; i < oldTableOccupiedSize; i++ ) { + new ( &pNewTable[i] ) tsSLList ( this->pTable[i] ); + } + for ( i = oldTableOccupiedSize; i < newTableSize; i++ ) { + new ( &pNewTable[i] ) tsSLList; + } + // Run the destructors explicitly. Currently this destructor is a noop. + // The Tornado II compiler and RedHat 6.2 will not compile ~tsSLList() but + // since its a NOOP we can find an ugly workaround +# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) + for ( i = 0; i < oldTableSize; i++ ) { + this->pTable[i].~tsSLList(); + } +# endif + + if ( ! this->pTable ) { + this->hashIxSplitMask = resTableBitMask ( logBaseTwoTableSize ); + this->nBitsHashIxSplitMask = logBaseTwoTableSize; + this->hashIxMask = this->hashIxSplitMask >> 1; + this->nextSplitIndex = 0; + } + + operator delete ( this->pTable ); + this->pTable = pNewTable; + this->logBaseTwoTableSize = logBaseTwoTableSize; + + return true; } template @@ -471,34 +573,13 @@ void resTable::splitBucket () // (this results in only a memcpy overhead, but // no hashing or entry redistribution) if ( this->nextSplitIndex > this->hashIxMask ) { - unsigned oldTableSize = this->hashIxSplitMask + 1; - unsigned newTableSize = oldTableSize * 2; - tsSLList *pNewTable = ( tsSLList * ) - operator new ( newTableSize * sizeof ( tsSLList ), std::nothrow ); - if ( ! pNewTable ) { + bool success = this->setTableSizePrivate ( this->nBitsHashIxSplitMask + 1 ); + if ( ! success ) { return; } - unsigned oldTableOccupiedSize = ( this->hashIxMask + 1 ) + this->nextSplitIndex; - // run the constructors using placement new - unsigned i; - for ( i = 0u; i < oldTableOccupiedSize; i++ ) { - new ( &pNewTable[i] ) tsSLList ( this->pTable[i] ); - } - for ( i = oldTableOccupiedSize; i < newTableSize; i++ ) { - new ( &pNewTable[i] ) tsSLList; - } - // Run the destructors explicitly. Currently this destructor is a noop. - // The Tornado II compiler and RedHat 6.2 will not compile ~tsSLList() but - // since its a NOOP we can find an ugly workaround :-( -# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) - for ( i = 0; i < oldTableSize; i++ ) { - this->pTable[i].~tsSLList(); - } -# endif - operator delete ( this->pTable ); - this->pTable = pNewTable; - this->hashIxMask = this->hashIxSplitMask; - this->hashIxSplitMask = newTableSize - 1; + this->nBitsHashIxSplitMask += 1; + this->hashIxSplitMask = resTableBitMask ( this->nBitsHashIxSplitMask ); + this->hashIxMask = this->hashIxSplitMask >> 1; this->nextSplitIndex = 0; } @@ -507,9 +588,8 @@ void resTable::splitBucket () this->nextSplitIndex++; T *pItem = tmp.get(); while ( pItem ) { - resTableIndex index = this->hash(*pItem); - tsSLList &list = this->pTable[index]; - list.add ( *pItem ); + resTableIndex index = this->hash ( *pItem ); + this->pTable[index].add ( *pItem ); pItem = tmp.get(); } } @@ -517,13 +597,18 @@ void resTable::splitBucket () // // add a res to the resTable // -// (bad status on failure) -// template int resTable::add ( T &res ) { - if ( this->nInUse > this->tableSize() ) { + if ( ! this->pTable ) { + this->setTableSizePrivate ( 10 ); + } + else if ( this->nInUse >= this->tableSize() ) { this->splitBucket (); + tsSLList &list = this->pTable[this->hash(res)]; + if ( this->find ( list, res ) != 0 ) { + return -1; + } } tsSLList &list = this->pTable[this->hash(res)]; if ( this->find ( list, res ) != 0 ) { @@ -556,40 +641,6 @@ T *resTable::find ( tsSLList &list, const ID &idIn ) const return pItem.pointer (); } -// -// findDelete -// searches from where the iterator points to the -// end of the list for idIn -// -// iterator points to the item found upon return -// (or NULL if nothing matching was found) -// -// removes the item if it finds it -// -template -T *resTable::findDelete ( tsSLList &list, const ID &idIn ) -{ - tsSLIter pItem = list.firstIter (); - T *pPrev = 0; - - while ( pItem.valid () ) { - const ID &id = *pItem; - if ( id == idIn ) { - if ( pPrev ) { - list.remove ( *pPrev ); - } - else { - list.get (); - } - this->nInUse--; - break; - } - pPrev = pItem.pointer (); - pItem++; - } - return pItem.pointer (); -} - // // ~resTable::resTable() // @@ -627,7 +678,9 @@ inline resTable & resTable::operator = ( const resTable & ) // template inline resTableIter::resTableIter (const resTable &tableIn) : - iter ( tableIn.pTable[0].firstIter () ), index (1), table ( tableIn ) {} + iter ( tableIn.pTable ? tableIn.pTable[0].firstIter () : + tsSLList::invalidIter() ), + index (1), table ( tableIn ) {} // // resTableIter::next () @@ -676,6 +729,17 @@ template inline chronIntIdResTable::chronIntIdResTable () : resTable (), allocId(1u) {} +template +inline chronIntIdResTable::chronIntIdResTable ( const chronIntIdResTable & ) : + resTable (), allocId(1u) {} + +template +inline chronIntIdResTable & chronIntIdResTable:: + operator = ( const chronIntIdResTable & ) +{ + return *this; +} + // // chronIntIdResTable::~chronIntIdResTable() // (not inline because it is virtual) @@ -752,25 +816,6 @@ inline const T intId::getId () const // X aCC return this->id; } -// -// const unsigned intId::minIndexBitWidth () -// -template -inline const unsigned intId::minIndexBitWidth () // X aCC 361 -{ - return MIN_INDEX_WIDTH; -} - - -// -// const unsigned intId::maxIndexBitWidth () -// -template -inline const unsigned intId::maxIndexBitWidth () // X aCC 361 -{ - return sizeof (resTableIndex) * CHAR_BIT; -} - // // integerHash() // @@ -845,22 +890,6 @@ inline const char * stringId::resourceName () const static const unsigned stringIdMinIndexWidth = CHAR_BIT; static const unsigned stringIdMaxIndexWidth = sizeof ( unsigned ); -// -// const unsigned stringId::minIndexBitWidth () -// -inline const unsigned stringId::minIndexBitWidth () -{ - return stringIdMinIndexWidth; -} - -// -// const unsigned stringId::maxIndexBitWidth () -// -inline const unsigned stringId::maxIndexBitWidth () -{ - return stringIdMaxIndexWidth; -} - #ifdef instantiateRecourceLib // @@ -877,7 +906,7 @@ stringId::stringId (const char * idIn, allocationType typeIn) : memcpy ((void *)this->pStr, idIn, nChars); } else { - throwWithLocation ( dynamicMemoryAllocationFailed () ); + throw std::bad_alloc(); } } else {