#include "ptrlist.h" #include // if this symbol is enabled, do sanity checking in a bunch of methods //#define DO_VERIFY_PTRLIST #if defined(ASSERTS_ENABLED) && defined(DO_VERIFY_PTRLIST) #define VERIFY_PTRLIST verify() #else #define VERIFY_PTRLIST #endif #ifdef _DEBUG int ptrlist_totalnitems = 0; #endif PtrListRoot::PtrListRoot( int initial_size ) { nitems = nslots = 0; items = NULL; //FG>why? we might as well save initial_size*4 bytes if nobody inserts anything in the list, no? //BU No. initial_size defaults to 0 and therefore no memory is actually //allocated in a typical construction. But, sometimes you want to start with //a specific size, generally when you know the exact size you want, //so that you can avoid REALLOCs when you initially populate the list. //FG>Ah, makes sense. setMinimumSize( initial_size ); } PtrListRoot::PtrListRoot( const PtrListRoot *from ) { nitems = nslots = 0; items = NULL; copyFrom( from ); } PtrListRoot::~PtrListRoot() { removeAll(); } void PtrListRoot::copyFrom( const PtrListRoot *from ) { ASSERT( from != NULL ); removeAll(); appendFrom( from ); VERIFY_PTRLIST; } void PtrListRoot::appendFrom( const PtrListRoot *from ) { ASSERT( from != NULL ); int n = from->getNumItems(); if ( n <= 0 ) return; void **mem = from->getItemList(); ASSERT( mem != NULL ); // can't be NULL if getNumItems() >= 1 setMinimumSize( getNumItems() + n ); MEMCPY( items + getNumItems(), from->getItemList(), sizeof( void * ) * n ); nitems += n; #ifdef _DEBUG ptrlist_totalnitems += n; #endif VERIFY_PTRLIST; } void PtrListRoot::setMinimumSize( int _nslots ) { ASSERT( _nslots >= 0 ); #ifdef DECREASE_PTRLISTS_SIZE if ( _nslots + 2 * DEF_PTRLIST_INCREMENT < nslots ) { nslots = ( ( _nslots / DEF_PTRLIST_INCREMENT ) + 2 ) * DEF_PTRLIST_INCREMENT; items = (void **) REALLOC( items, sizeof( void * ) * nslots ); } #endif if ( _nslots == 0 || _nslots <= nslots ) return; nslots = _nslots; if ( items ) items = (void **) REALLOC( items, sizeof( void * ) * nslots ); else items = (void **) MALLOC( sizeof( void * ) * nslots ); VERIFY_PTRLIST; } int PtrListRoot::getNumItems() const { return nitems; } void *PtrListRoot::enumItem( int n ) const { if ( items == NULL || n < 0 || n >= nitems ) return NULL; return items[ n ]; } void *PtrListRoot::addItem( void *item, int pos, int increment ) { ASSERT( increment > 0 ); ASSERTPR( item != NULL, "can't put NULLs into ptrlists" ); ASSERT( nitems <= nslots ); #ifdef DECREASE_PTRLISTS_SIZE // expand or shrink as necessary if ( items == NULL || nslots == nitems ) setMinimumSize( nslots + increment ); else setMinimumSize( nitems ); #else // expand if necessary if ( items == NULL || nslots == nitems ) setMinimumSize( nslots + increment ); #endif items[ nitems++ ] = item; if ( pos != PTRLIST_POS_LAST ) moveItem( nitems - 1, pos ); VERIFY_PTRLIST; #ifdef _DEBUG ptrlist_totalnitems++; #endif return item; } // replace what's in the slot with the new value void *PtrListRoot::setItem( void *item, int pos ) { ASSERT( nitems >= pos ); items[ pos ] = item; VERIFY_PTRLIST; return item; } void PtrListRoot::reverse() { if ( nitems <= 1 ) return; MemBlock block( nitems, items ); void **blockmem = block.getMemory(); for ( int i = 0, j = nitems - 1; j >= 0; i++, j-- ) { items[ i ] = blockmem[ j ]; } } void PtrListRoot::moveItem( int from, int to ) { if ( from == to ) return; void *ptr = items[ from ]; if ( nitems > from + 1 ) // if moving from the end, there is nothing to shift behind our src position { //IMPORTANT note for future ports: This assumes MEMCPY accepts overlapping segments. MEMCPY( &items[ from ], &items[ from + 1 ], ( nitems - from ) * sizeof( void * ) ); } if ( to > from ) to--; if ( nitems > to ) // if moving to the end, there is nothing to shift behind our target position MEMCPY( &items[ to + 1 ], &items[ to ], ( nitems - to - 1 ) * sizeof( void * ) ); items[ to ] = ptr; VERIFY_PTRLIST; } int PtrListRoot::removeItem( void *item ) { int c = 0; if ( item == NULL || items == NULL || nitems <= 0 ) return 0; // count occurences of item in items, remember the first one void **p = items; int first = -1; for ( int i = 0; i < nitems; i++, p++ ) { if ( *p == item ) { if ( c++ == 0 ) first = i; } } // if we found one, remove it if ( c > 0 ) { removeByPos( first ); // delByPos is faaast c--; // decrease count } VERIFY_PTRLIST; return c; // returns how many occurences of this item left } void PtrListRoot::removeEveryItem( void *item ) { while ( removeItem( item ) ); VERIFY_PTRLIST; } void PtrListRoot::removeByPos( int pos ) { if ( pos < 0 || pos >= nitems ) return; //JC --nitems; #ifdef _DEBUG ptrlist_totalnitems--; #endif if ( pos == nitems ) return; // last one? nothing to copy over MEMCPY( items + pos, items + ( pos + 1 ), sizeof( void * ) * ( nitems - pos ) ); // CT:not (nitems-(pos+1)) as nitems has been decremented earlier #ifdef DECREASE_PTRLISTS_SIZE // shrink if necessary setMinimumSize( nitems ); #endif VERIFY_PTRLIST; } void PtrListRoot::removeLastItem() { if ( nitems == 0 || items == NULL ) return; nitems--; // hee hee #ifdef _DEBUG ptrlist_totalnitems--; #endif #ifdef DECREASE_PTRLISTS_SIZE // shrink if necessary setMinimumSize( nitems ); #endif VERIFY_PTRLIST; } void PtrListRoot::removeAll() { FREE( items ); items = NULL; #ifdef _DEBUG ptrlist_totalnitems -= nitems; #endif #ifdef DECREASE_PTRLISTS_SIZE // shrink if necessary setMinimumSize( nitems ); #endif nitems = 0; nslots = 0; VERIFY_PTRLIST; } void PtrListRoot::freeAll() { for ( int i = 0; i < nitems; i++ ) //JC if ( items ) FREE( items[ i ] ); removeAll(); VERIFY_PTRLIST; } int PtrListRoot::searchItem( void *item ) const { for ( int i = 0; i < nitems; i++ ) if ( items[ i ] == item ) return i; return -1; } void PtrListRoot::verify() { #ifdef DO_VERIFY_PTRLIST ASSERT( nitems >= 0 ); ASSERT( nslots >= 0 ); ASSERT( nitems <= nslots ); #endif } #if 0 int PtrListRoot::bsearchItem( void *item ) const { // do binary search if ( nitems == 0 || items == NULL ) return -1; int bot = 0, top = nitems - 1, mid; for ( int c = 0; c < nitems + 16; c++ ) { if ( bot > top ) return -1; mid = ( bot + top ) / 2; if ( item == items[ mid ] ) return mid; if ( item < items[ mid ] ) top = mid - 1; else bot = mid + 1; } ASSERTPR( 0, "binary search fucked up" ); return -1; } #endif void PtrListRoot::purge() { ASSERT( nitems == 0 ); if ( items != NULL ) { FREE( items ); items = NULL; } }