#ifndef _PTRLIST_H #define _PTRLIST_H #include //#include #include #include //#include #ifdef _DEBUG extern int ptrlist_totalnitems; #endif // Disable the "identifier was truncated to '255' characters..." warning. #pragma warning( disable : 4786 ) /* a generic pointer list template. only takes up 12 bytes when empty. auto-grows the array as necessary, NEVER shrinks it unless you removeAll() or equivalent Use, PtrList, never PtrListRoot */ // yes, this really should be an enum or something #define PTRLIST_POS_LAST -1 // 4k each, leaving 16 bytes for MALLOC overhead //#define DEF_PTRLIST_INCREMENT (4096/sizeof(void*)-16) // in average, 4k doubles the VM working set of a skin, 128bytes (32 elements) seems most optimal #define DEF_PTRLIST_INCREMENT (128) class __foreach; // base class, to share as much code as possible class NOVTABLE PtrListRoot { friend class __foreach; protected: PtrListRoot(int initial_size = 0); PtrListRoot(const PtrListRoot *from); virtual ~PtrListRoot(); void copyFrom(const PtrListRoot *from); void appendFrom(const PtrListRoot *from); void setMinimumSize(int nslots); // expand to at least this many slots int getNumItems() const; int size() const { return nitems; } bool empty() { return nitems == 0; } void *enumItem(int n) const; void moveItem(int from, int to); int removeItem(void *item); void removeEveryItem(void *item); void removeByPos(int pos); void removeLastItem(); void removeAll(); void freeAll(); void purge(); // gross-ass linear search to find index of item // note that PtrListSorted provides a binary search version int searchItem(void *item) const; #if 0//fuct // speedy binary search. although it'll be fuct if it's not sorted right int bsearchItem(void *item) const; #endif void *addItem(void *item, int pos, int inc); void *setItem(void *item, int pos); // replace what's in the slot with the new value void reverse(); void **getItemList() const { return items; } // try to avoid! this is inline to make q() fast private: #undef verify // for Mac void verify(); int nitems, nslots; void **items; }; // now we add the methods that refer specifically to the pointer type template class PtrList : public PtrListRoot { friend class __foreach; public: PtrList(int initial_size = 0) {} PtrList(const PtrList &r) { copyFrom(&r); } PtrList(const PtrList *r) { copyFrom(r); } ~PtrList() {} // copy another PtrList // deletes previous contents void copyFrom(const PtrList *from) { PtrListRoot::copyFrom(from); } // append contents of another PtrList to the end of this one // preserves previous contents void appendFrom(const PtrList *from) { PtrListRoot::appendFrom(from); } // adding // expand freelist to at least this many slots, even if 0 items in list void setMinimumSize(int nslots) { PtrListRoot::setMinimumSize(nslots); } // provide a public addItem for the pointer type T *addItem(const T *item, int pos = PTRLIST_POS_LAST, int inc = DEF_PTRLIST_INCREMENT) { return static_cast(PtrListRoot::addItem(const_cast(item), pos, inc)); } void push_back(const T* item) { return addItem(item); } // replace what's in the slot with the new value T *setItem(const T *item, int pos) { return static_cast(PtrListRoot::setItem(const_cast(item), pos)); } // reverse the order of the list in place void reverse() { PtrListRoot::reverse(); } // enumerating // returns # of items in list int getNumItems() const { return PtrListRoot::getNumItems(); } // basic list enumerator. returns NULL for out of bounds T *enumItem(int n) const { return static_cast(PtrListRoot::enumItem(n)); } T *operator[](int n) const { return enumItem(n); } // this will safely return NULL if 0 items due to enumItems's boundscheck T *getFirst() const { return enumItem(0); } T *getLast() const { return enumItem(getNumItems() - 1); } // this is a NON-BOUNDS-CHECKING lookup T *q(int n) { return static_cast(getItemList()[n]); } // gross-ass linear search to find index of item // note that PtrListSorted provides a binary search version int searchItem(T *item) const { return PtrListRoot::searchItem(item); } int haveItem(T *item) const { return searchItem(item) >= 0; } // deleteing // removes first instance of a pointer in list, returns how many are left int removeItem(T *item) { return PtrListRoot::removeItem(item); } //DEPRECATED int delItem(T *item) { return removeItem(item); } // removes all instances of this pointer void removeEveryItem(const T *item) { PtrListRoot::removeEveryItem(const_cast(item)); } // removes pointer at specified position regardless of value void removeByPos(int pos) { PtrListRoot::removeByPos(pos); } // DEPRECATED void delByPos(int pos) { removeByPos(pos); } // removes last item void removeLastItem() { PtrListRoot::removeLastItem(); } // removes all entries, also deletes memory space void removeAll() { PtrListRoot::removeAll(); } // removes all entries, calling FREE on the pointers void freeAll() { PtrListRoot::freeAll(); } // removes all entries, calling delete on the pointers void deleteAll() { int i, nitems = getNumItems(); for (i = 0; i < nitems; i++) delete enumItem(i); removeAll(); } // removes all entries, calling delete on the pointers // FG>this version removes each entry as it deletes it so if // one of the object uses this list in its destructor, it will // still work. It is MUCH slower than deleteAll tho. void deleteAllSafe() { //CUT ASSERT(!(nitems != 0 && items == NULL)); while (getNumItems()) { T *i = enumItem(0); delete i; removeItem(i); } } void deleteItem(int item) { if (item < getNumItems()) { deleteItem(enumItem(item)); } } void deleteItem(T *item) { delete item; removeItem(item); } void moveItem(int from, int to) { PtrListRoot::moveItem(from, to); } static T *castFor(void *ptr) { return static_cast(ptr); } using PtrListRoot::purge; protected: T **getItemList() { return reinterpret_cast(PtrListRoot::getItemList()); } }; class NotSorted { public: // comparator for searching -- override static int compareAttrib(const wchar_t *attrib, void *item) { return 0; } // comparator for sorting -- override , -1 p1 < p2, 0 eq, 1 p1 > p2 static int compareItem(void *p1, void* p2) { return CMP3(p1, p2); } }; //template class NoSort { // static void _sort(T **, int) {} //}; // a base class to sort the pointers // you must implement the comparisons (C) and the sort algorithm (S) template < class T, class C, class S > class PtrListSorted : public PtrList { public: PtrListSorted(int initial_size = 0) : PtrList(initial_size) { need_sorting = 0; auto_sort = true; dups_low = dups_hi = dups_pos = 0; } void copyFrom(const PtrList *from) { PtrList::copyFrom(from); need_sorting = 1; if (auto_sort) sort(); } T *addItem(T *item, int pos = PTRLIST_POS_LAST, int inc = DEF_PTRLIST_INCREMENT) { #if 1 // check for appending in sorted order if (pos == PTRLIST_POS_LAST && !need_sorting && auto_sort) { int n = PtrList::getNumItems(); if (n > 0 && C::compareItem(item, q(n - 1)) < 0) need_sorting = 1; } else #endif need_sorting = 1; return PtrList::addItem(item, pos, inc); } void sort(bool force_sort = false) { if (need_sorting || force_sort) S::_sort(PtrList::getItemList(), PtrList::getNumItems()); need_sorting = 0; } T *enumItem(int n) { // NOT const since we might call sort() if (auto_sort) sort(); return PtrList::enumItem(n); } T *operator[](int n) { return PtrListSorted::enumItem(n); } T *q(int n) { if (auto_sort) sort(); return static_cast(PtrList::getItemList()[n]); } T *findItem(const wchar_t *attrib, int *pos = NULL) { ASSERTPR(!(!auto_sort && need_sorting), "must call sort() first if auto-sorting is disabled"); sort(); #if 1 // do binary search if (PtrList::getNumItems() == 0) return NULL; int bot = 0, top = PtrList::getNumItems() - 1, mid; for (int c = 0; c < PtrList::getNumItems() + 1; c++) { if (bot > top) return NULL; mid = (bot + top) / 2; int r = C::compareAttrib(attrib, PtrList::getItemList()[mid]); if (r == 0) { if (pos != NULL) *pos = mid; return PtrList::getItemList()[mid]; } if (r < 0) { top = mid - 1; } else { bot = mid + 1; } } ASSERTPR(0, "binary search fucked up"); #else // re-enable this in case of fuckup for (int i = 0; i < nitems; i++) { if (C::compareAttrib(attrib, static_cast(items[i])) == 0) return static_castitems[i]; } #endif return NULL; } T *findItem(T *attrib, int *pos = NULL) { return findItem((const wchar_t *)attrib, pos); } int beginEnumDups(const char *attrib) { int pos; findItem(attrib, &pos); if (pos < 0) return -1; dups_hi = pos; dups_low = pos; int i; for (i = pos - 1;i >= 0;i--) { if (C::compareAttrib(attrib, static_cast(enumItem(i))) == 0) break; dups_low = i; } for (i = pos + 1;i < PtrList::getNumItems();i++) { if (C::compareAttrib(attrib, static_cast(enumItem(i))) == 0) break; dups_hi = i; } dups_pos = dups_low; return dups_pos; } int getNextDup() { // returns -1 when done if (dups_pos >= dups_hi) return -1; return ++dups_pos; } #if 0 // replace search with binary search int searchItem(T *item) const { ASSERTPR(!(!auto_sort && need_sorting), "must call sort() first if auto-sorting is disabled"); sort(); return bsearchItem(item); } #endif void setAutoSort(bool as) { auto_sort = as; } bool getAutoSort() const { return auto_sort; } void removeDups() { ASSERTPR(!(!auto_sort && need_sorting), "must call sort() first if auto-sorting is disabled"); sort(); for (int i = 1; i < PtrList::getNumItems(); i++) { if (C::compareItem(enumItem(i - 1), enumItem(i)) == 0) { PtrList::delByPos(i); i--; } } } private: int need_sorting; bool auto_sort; int dups_low, dups_hi, dups_pos; }; // quicksort -- you still need to override the compare fns template class QuickSorted { public: static void _sort(T **items, int nitems) { if (items == NULL || nitems <= 1) return ; Qsort(items, 0, nitems - 1); } private: static void swapItem(T **items, int a, int b) { // no bounds checking! T *tmp = items[a]; items[a] = items[b]; items[b] = tmp; } static void Qsort(T **items, int lo0, int hi0) { int lo = lo0, hi = hi0; if (hi0 > lo0) { T *mid = items[(lo0 + hi0) / 2]; while (lo <= hi) { while ((lo < hi0) && (C::compareItem(items[lo], mid) < 0)) lo++; while ((hi > lo0) && (C::compareItem(items[hi], mid) > 0)) hi--; if (lo <= hi) { swapItem(items, lo, hi); lo++; hi--; } } if (lo0 < hi) Qsort(items, lo0, hi); if (lo < hi0) Qsort(items, lo, hi0); } } }; // easy way to specify quicksorting, just data type and comparison class template class PtrListQuickSorted : public PtrListSorted > { public: PtrListQuickSorted(int initial_size = 0) : PtrListSorted >(initial_size) {} }; // easy way to get a list sorted by pointer val class SortByPtrVal { public: static int compareItem(void *p1, void *p2) { return CMP3(p1, p2); } static int compareAttrib(const wchar_t *attrib, void *item) { return CMP3((void *)attrib, item); } }; template class PtrListQuickSortedByPtrVal : public PtrListQuickSorted {}; // this class automatically inserts at the correct position, so // the binary searches are very fast if you need to insert and search often (no need to sort) template < class T, class C > class PtrListInsertSorted : public PtrList { public: PtrListInsertSorted() : last_insert_pos(0) { disable_sort = 0; } T *addItem(T *item) { int numItems = PtrList::getNumItems(); if (numItems == 0) { last_insert_pos = 0; return PtrList::addItem(item); } int insertpoint = -1; if (!disable_sort) { int bot = 0, top = numItems - 1, mid; // benski> // optimization based on profiler info. Too many string compares! // Most of the use of this comes from GuiObjectWnd's constructor (and derived classes) // so I've changed GuiObjectWnd to add things in alphabetical order. // Before we start the binary search, we'll check the new item against the LAST item inserted // Most of the time, we'll finish the insert in O(1) // Even if we fail, we mitigate the loss somewhat by limiting the binary search if (last_insert_pos >= numItems) // the list may have shrunk since last time last_insert_pos = numItems - 1; int quickTest = C::compareItem(item, PtrList::getItemList()[last_insert_pos]); if (quickTest == 0) // right on the money.. we'll go ahead and insert ourselves next return PtrList::addItem(item, last_insert_pos); if (quickTest > 0) // ok we go after the last inserted item (good), but we need to make sure we go before the next one { last_insert_pos++; if (last_insert_pos == numItems) // we're at the end? cool... return PtrList::addItem(item, PTRLIST_POS_LAST); quickTest = C::compareItem(item, PtrList::getItemList()[last_insert_pos]); // test against the next item if (quickTest <= 0) // and we're not bigger than the next one... perfect! return PtrList::addItem(item, last_insert_pos); else // too bad bot = last_insert_pos; // help out the binary search ... We're at least bigger than everything before last_insert_pos } else // ok our optimization failed, but we can still help out the binary search top = last_insert_pos - 1; // we're at least smaller than everything before last_insert_pos // end optimization code for (int c = 0; c < numItems + 1; c++) { if (bot > top) { // insert here insertpoint = bot; break; } mid = (bot + top) / 2; int r = C::compareItem(item, PtrList::getItemList()[mid]); if (r == 0) { // insert here insertpoint = mid; break; } if (r < 0) { top = mid - 1; } else { bot = mid + 1; } } last_insert_pos = insertpoint; ASSERTPR(insertpoint != -1, "insertsort/binary search fucked up"); } else // no sorting { last_insert_pos = numItems; insertpoint = PTRLIST_POS_LAST; } return PtrList::addItem(item, insertpoint); } T *getInsertionPoint(T *item, int *pos) { if (PtrList::getNumItems() == 0) { if (pos) *pos = 0; return NULL; } int bot = 0, top = PtrList::getNumItems() - 1, mid; int insertpoint = -1; if (!disable_sort ) { for (int c = 0; c < PtrList::getNumItems() + 1; c++) { if (bot > top) { // insert here insertpoint = bot; break; } mid = (bot + top) / 2; int r = C::compareItem(item, PtrList::getItemList()[mid]); if (r == 0) { // insert here insertpoint = mid; break; } if (r < 0) { top = mid - 1; } else { bot = mid + 1; } } ASSERTPR(insertpoint != -1, "insertsort/binary search fucked up"); } else insertpoint = PTRLIST_POS_LAST; if (pos) *pos = insertpoint; return PtrList::enumItem(insertpoint); } T *findItem(const wchar_t *attrib, int *pos = NULL) { if (isSorted()) { // binary search if (PtrList::getNumItems() == 0) return NULL; int bot = 0, top = PtrList::getNumItems() - 1, mid; for (int c = 0; c < PtrList::getNumItems() + 1; c++) { if (bot > top) return NULL; mid = (bot + top) / 2; int r = C::compareAttrib(attrib, PtrList::getItemList()[mid]); if (r == 0) { if (pos != NULL) *pos = mid; return PtrList::getItemList()[mid]; } if (r < 0) { top = mid - 1; } else { bot = mid + 1; } } ASSERTPR(0, "binary search fucked up"); } else { // linear search for (int i = 0; i < PtrList::getNumItems(); i++) { int r = C::compareAttrib(attrib, PtrList::getItemList()[i]); if (r == 0) { if (pos != NULL) *pos = i; return PtrList::getItemList()[i]; } } } return NULL; } T *findItem(T *attrib, int *pos = NULL) { return findItem((const wchar_t *)attrib, pos); } void setSorted(int dosort) { disable_sort = !dosort; } int isSorted() { return !disable_sort; } int disable_sort; int last_insert_pos; }; // this list allows you to have multiple items with same attrib and adds findLastItem so you can // sort on more than just one item. this can be used to make autosorting lists of overriding items // which you can add and remove at will. template class PtrListQuickMultiSorted : public PtrListQuickSorted { public: PtrListQuickMultiSorted(int initial_size = 0) : PtrListQuickSorted(initial_size) {} T *findLastItem(const wchar_t *attrib, int *pos = NULL) { PtrListQuickSorted::sort(); int p = 0; int fp = 0; T *item = PtrListQuickSorted::findItem(attrib, &fp); if (!item) return NULL; p = fp; for(;;) { p++; if (p >= PtrListQuickSorted::getNumItems()) break; T* i = PtrListQuickSorted::enumItem(p); if (!C::compareAttrib(attrib, i)) { fp = p; item = i; } else break; } if (pos != NULL) *pos = fp; return item; } }; //same thing but Insert sorted. use this one if you insert and search items often (no need to sort on findItem) template class PtrListInsertMultiSorted : public PtrListInsertSorted { public: PtrListInsertMultiSorted() : PtrListInsertSorted() {} T *findLastItem(const wchar_t *attrib, int *pos = NULL) { //sort(); int p = 0; int fp = 0; T *item = PtrListInsertSorted::findItem(attrib, &fp); if (!item) return NULL; p = fp; for (;;) { p++; if (p >= PtrListInsertSorted::getNumItems()) break; T* i = PtrListInsertSorted::enumItem(p); if (!C::compareAttrib(attrib, i)) { fp = p; item = i; } else break; } if (pos != NULL) *pos = fp; return item; } }; #include #endif