diff options
Diffstat (limited to 'Src/Wasabi/bfc/ptrlist.h')
-rw-r--r-- | Src/Wasabi/bfc/ptrlist.h | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/Src/Wasabi/bfc/ptrlist.h b/Src/Wasabi/bfc/ptrlist.h new file mode 100644 index 00000000..9d8d310d --- /dev/null +++ b/Src/Wasabi/bfc/ptrlist.h @@ -0,0 +1,777 @@ +#ifndef _PTRLIST_H +#define _PTRLIST_H + +#include <bfc/std_math.h> +//#include <bfc/memblock.h> +#include <bfc/bfc_assert.h> + +#include <bfc/platform/platform.h> + +//#include <wasabicfg.h> + +#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<typename>, 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 T> +class PtrList : public PtrListRoot +{ + friend class __foreach; +public: + PtrList(int initial_size = 0) {} + PtrList(const PtrList<T> &r) { copyFrom(&r); } + PtrList(const PtrList<T> *r) { copyFrom(r); } + ~PtrList() {} + + // copy another PtrList + // deletes previous contents + void copyFrom(const PtrList<T> *from) { PtrListRoot::copyFrom(from); } + + // append contents of another PtrList to the end of this one + // preserves previous contents + void appendFrom(const PtrList<T> *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<T *>(PtrListRoot::addItem(const_cast<T*>(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<T *>(PtrListRoot::setItem(const_cast<T*>(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<T *>(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<T*>(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<T*>(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<T*>(ptr); } + + using PtrListRoot::purge; + +protected: + T **getItemList() + { + return reinterpret_cast<T **>(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 T, class C> 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<T> +{ +public: + PtrListSorted(int initial_size = 0) : PtrList<T>(initial_size) + { + need_sorting = 0; + auto_sort = true; + dups_low = dups_hi = dups_pos = 0; + } + + void copyFrom(const PtrList<T> *from) + { + PtrList<T>::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<T>::getNumItems(); + if (n > 0 && C::compareItem(item, q(n - 1)) < 0) need_sorting = 1; + } + else +#endif + need_sorting = 1; + return PtrList<T>::addItem(item, pos, inc); + } + + void sort(bool force_sort = false) + { + if (need_sorting || force_sort) + S::_sort(PtrList<T>::getItemList(), PtrList<T>::getNumItems()); + need_sorting = 0; + } + + T *enumItem(int n) + { // NOT const since we might call sort() + if (auto_sort) sort(); + return PtrList<T>::enumItem(n); + } +T *operator[](int n) { return PtrListSorted<T, C, S>::enumItem(n); } + + T *q(int n) + { + if (auto_sort) sort(); + return static_cast<T*>(PtrList<T>::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<T>::getNumItems() == 0) return NULL; + + int bot = 0, top = PtrList<T>::getNumItems() - 1, mid; + + for (int c = 0; c < PtrList<T>::getNumItems() + 1; c++) + { + if (bot > top) return NULL; + mid = (bot + top) / 2; + int r = C::compareAttrib(attrib, PtrList<T>::getItemList()[mid]); + if (r == 0) + { + if (pos != NULL) *pos = mid; + return PtrList<T>::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<T *>(items[i])) == 0) + return static_cast<T *>items[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<T *>(enumItem(i))) == 0) + break; + + dups_low = i; + } + + for (i = pos + 1;i < PtrList<T>::getNumItems();i++) + { + if (C::compareAttrib(attrib, static_cast<T *>(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<T>::getNumItems(); i++) + { + if (C::compareItem(enumItem(i - 1), enumItem(i)) == 0) + { + PtrList<T>::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 T, class C> +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 T, class C> class PtrListQuickSorted : public PtrListSorted<T, C, QuickSorted<T, C> > +{ +public: + PtrListQuickSorted(int initial_size = 0) : PtrListSorted<T, C, QuickSorted<T, C> >(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 T> class PtrListQuickSortedByPtrVal : public PtrListQuickSorted<T, SortByPtrVal > {}; + + +// 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<T> +{ +public: + PtrListInsertSorted() : last_insert_pos(0) { disable_sort = 0; } + + T *addItem(T *item) + { + int numItems = PtrList<T>::getNumItems(); + if (numItems == 0) + { + last_insert_pos = 0; + + return PtrList<T>::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<T>::getItemList()[last_insert_pos]); + + if (quickTest == 0) // right on the money.. we'll go ahead and insert ourselves next + return PtrList<T>::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<T>::addItem(item, PTRLIST_POS_LAST); + + quickTest = C::compareItem(item, PtrList<T>::getItemList()[last_insert_pos]); // test against the next item + + if (quickTest <= 0) // and we're not bigger than the next one... perfect! + return PtrList<T>::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<T>::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<T>::addItem(item, insertpoint); + } + T *getInsertionPoint(T *item, int *pos) + { + if (PtrList<T>::getNumItems() == 0) + { + if (pos) + *pos = 0; + + return NULL; + } + + int bot = 0, top = PtrList<T>::getNumItems() - 1, mid; + + int insertpoint = -1; + if (!disable_sort ) + { + for (int c = 0; c < PtrList<T>::getNumItems() + 1; c++) + { + if (bot > top) + { + // insert here + insertpoint = bot; + break; + } + mid = (bot + top) / 2; + int r = C::compareItem(item, PtrList<T>::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<T>::enumItem(insertpoint); + } + T *findItem(const wchar_t *attrib, int *pos = NULL) + { + if (isSorted()) + { + // binary search + if (PtrList<T>::getNumItems() == 0) + return NULL; + + int bot = 0, top = PtrList<T>::getNumItems() - 1, mid; + + for (int c = 0; c < PtrList<T>::getNumItems() + 1; c++) + { + if (bot > top) + return NULL; + + mid = (bot + top) / 2; + int r = C::compareAttrib(attrib, PtrList<T>::getItemList()[mid]); + + if (r == 0) + { + if (pos != NULL) + *pos = mid; + + return PtrList<T>::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<T>::getNumItems(); i++) + { + int r = C::compareAttrib(attrib, PtrList<T>::getItemList()[i]); + if (r == 0) + { + if (pos != NULL) + *pos = i; + + return PtrList<T>::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 T, class C> class PtrListQuickMultiSorted : public PtrListQuickSorted<T, C> +{ +public: + PtrListQuickMultiSorted(int initial_size = 0) : PtrListQuickSorted<T, C>(initial_size) {} + T *findLastItem(const wchar_t *attrib, int *pos = NULL) + { + PtrListQuickSorted<T, C>::sort(); + int p = 0; + int fp = 0; + T *item = PtrListQuickSorted<T, C>::findItem(attrib, &fp); + + if (!item) + return NULL; + + p = fp; + + for(;;) + { + p++; + + if (p >= PtrListQuickSorted<T, C>::getNumItems()) + break; + + T* i = PtrListQuickSorted<T, C>::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 T, class C> class PtrListInsertMultiSorted : public PtrListInsertSorted<T, C> +{ +public: + PtrListInsertMultiSorted() : PtrListInsertSorted<T, C>() {} + T *findLastItem(const wchar_t *attrib, int *pos = NULL) + { + //sort(); + int p = 0; + int fp = 0; + T *item = PtrListInsertSorted<T, C>::findItem(attrib, &fp); + + if (!item) + return NULL; + + p = fp; + + for (;;) + { + p++; + + if (p >= PtrListInsertSorted<T, C>::getNumItems()) + break; + + T* i = PtrListInsertSorted<T, C>::enumItem(p); + + if (!C::compareAttrib(attrib, i)) + { + fp = p; + item = i; + } + else + break; + } + + if (pos != NULL) + *pos = fp; + + return item; + } +}; + + +#include <bfc/foreach.h> + +#endif |