605 lines
12 KiB
C++
605 lines
12 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#ifndef __NLIST__
|
|
#define __NLIST__
|
|
|
|
|
|
#include "alloc/ialloc.h"
|
|
#include "assert/nassert.h"
|
|
|
|
|
|
namespace GS {
|
|
|
|
/*
|
|
@short List.
|
|
@author Emmanuel Julien (ejulien@gsworks.fr)
|
|
*/
|
|
template <class T> class List
|
|
{
|
|
public:
|
|
|
|
//----------------------------------------------------------------------
|
|
class Item
|
|
{
|
|
friend class List <T>;
|
|
|
|
T o;
|
|
Item *p, *n;
|
|
|
|
public:
|
|
|
|
NPLACEMENT_NEW(ListItem)
|
|
|
|
/// Return the previous list link.
|
|
inline Item *Previous() const { return p; }
|
|
/// Return the next list link.
|
|
inline Item *Next() const { return n; }
|
|
|
|
/// Retrieve a reference to the item object.
|
|
inline T &Object() { return o; }
|
|
|
|
Item(const T &obj)
|
|
{
|
|
o = obj;
|
|
p = n = 0;
|
|
}
|
|
};
|
|
//----------------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------
|
|
class Iterator
|
|
{
|
|
Item *c, *n;
|
|
|
|
public:
|
|
|
|
inline void operator++()
|
|
{
|
|
if ((c = c ? n : 0) != 0)
|
|
n = c->n;
|
|
}
|
|
|
|
inline bool IsOver() const { return c ? false : true; }
|
|
inline Item *Next() const { return n; }
|
|
|
|
inline Item *GetItem() const { return c; }
|
|
inline T &Object() { return c->Object(); }
|
|
|
|
/// Only use when using a pointer type.
|
|
inline T ObjectPtr() { return c ? c->Object() : 0; }
|
|
|
|
void Reset(Item *start = 0)
|
|
{
|
|
c = start;
|
|
n = c ? c->Next() : 0;
|
|
}
|
|
Iterator(Item *start = 0)
|
|
{ Reset(start); }
|
|
};
|
|
//----------------------------------------------------------------------
|
|
|
|
protected:
|
|
|
|
uint count;
|
|
Item *root, *last;
|
|
|
|
public:
|
|
|
|
/// Get item count in list.
|
|
inline uint GetCount() const { return count; }
|
|
/// Get root item.
|
|
inline Item *GetRoot() const { return root; }
|
|
/// Get last item.
|
|
inline Item *GetLast() const { return last; }
|
|
|
|
/// Clone list.
|
|
void Clone(List <T> &clone_list) const
|
|
{
|
|
clone_list.Clear();
|
|
for (Item *_i = root; _i; _i = _i->Next())
|
|
clone_list.Add(_i->Object());
|
|
}
|
|
|
|
/// Get item from position.
|
|
Item *ItemAt(uint n) const
|
|
{
|
|
if (n > count)
|
|
return 0;
|
|
|
|
Item *p = root;
|
|
while (n--)
|
|
p = p->n;
|
|
|
|
return p;
|
|
}
|
|
inline T &ObjectAt(uint n) const
|
|
{ return ItemAt(n)->Object(); }
|
|
inline T &operator[] (uint n) const
|
|
{ return ItemAt(n)->Object(); }
|
|
|
|
/// Get index of a given item.
|
|
int Index(const T &o) const
|
|
{
|
|
int pos = 0;
|
|
for (Item *p = root; p; p = p->n)
|
|
{
|
|
if (p->Object() == o)
|
|
return pos;
|
|
pos++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// Check whether a given item belongs to this list or not.
|
|
bool Belongs(Item *i) const
|
|
{
|
|
for (Item *s = root; s; s = s->n)
|
|
if (s == i)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// Insert item after a given reference item (defaults to last).
|
|
virtual Item *Append(const T &o, Item *rfr = 0)
|
|
{
|
|
Item *i = new Item(o);
|
|
if (!i)
|
|
return 0;
|
|
|
|
if (!rfr)
|
|
{
|
|
i->p = last;
|
|
if (last)
|
|
last->n = i;
|
|
else
|
|
root = i;
|
|
last = i;
|
|
}
|
|
else
|
|
{
|
|
if (rfr->n)
|
|
rfr->n->p = i;
|
|
i->n = rfr->n;
|
|
rfr->n = i;
|
|
i->p = rfr;
|
|
|
|
if (last == rfr) // Update last.
|
|
last = i;
|
|
}
|
|
|
|
count++;
|
|
return i;
|
|
}
|
|
|
|
/// Insert item before a given reference item (defaults to root).
|
|
virtual Item *Prepend(const T &o, Item *rfr = 0)
|
|
{
|
|
Item *i = new Item(o);
|
|
if (!i)
|
|
return 0;
|
|
|
|
if (!rfr)
|
|
{
|
|
i->n = root;
|
|
if (root)
|
|
root->p = i;
|
|
else
|
|
last = i;
|
|
root = i;
|
|
}
|
|
else
|
|
{
|
|
if (rfr->p)
|
|
rfr->p->n = i;
|
|
i->p = rfr->p;
|
|
rfr->p = i;
|
|
i->n = rfr;
|
|
|
|
if (root == rfr) // Update root.
|
|
root = i;
|
|
}
|
|
|
|
count++;
|
|
return i;
|
|
}
|
|
|
|
/// Insert a value at a given position.
|
|
Item *Insert(const T &v, uint at)
|
|
{
|
|
Item *rfr = ItemAt(at);
|
|
return rfr ? Prepend(v, rfr) : 0;
|
|
}
|
|
|
|
/// Add item to list.
|
|
Item *Add(const T &o, bool append, bool allow_duplicate)
|
|
{
|
|
if (!allow_duplicate)
|
|
if (Item *i = Find(o))
|
|
return i;
|
|
return append ? Append(o) : Prepend(o);
|
|
}
|
|
/// Add item to list.
|
|
Item *Add(const T &o)
|
|
{ return Append(o); }
|
|
|
|
/// Add item to list.
|
|
List <T> &operator << (const T &o)
|
|
{
|
|
Add(o);
|
|
return *this;
|
|
}
|
|
|
|
/// Find item by reference to object.
|
|
Item *Find(const T &o) const
|
|
{
|
|
for (Item *p = root; p; p = p->n)
|
|
if (p->Object() == o)
|
|
return p;
|
|
return 0;
|
|
}
|
|
|
|
/// Find item by object value.
|
|
Item *FindByValue(const T &o) const
|
|
{
|
|
for (Item *p = root; p; p = p->n)
|
|
if (*p->Object() == *o)
|
|
return p;
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
@short Subtract two given lists by object value.
|
|
@warning The resulting list holds pointers to the original objects.
|
|
*/
|
|
static List <T> *SubtractByValue(const List <T> &what, const List <T> &from)
|
|
{
|
|
List <T> *list = new List <T>;
|
|
if (list)
|
|
for (Item *f_p = from.root; f_p; f_p = f_p->n)
|
|
if (!what.FindByValue(f_p->Object()))
|
|
list->Add(f_p->Object());
|
|
return list;
|
|
}
|
|
|
|
/*!
|
|
@short Subtract two given lists by object address.
|
|
@warning The resulting list holds pointers to the original objects.
|
|
*/
|
|
static List <T> *SubtractByAddress(const List <T> &what, const List <T> &from)
|
|
{
|
|
List <T> *list = new List <T>;
|
|
if (list)
|
|
for (Item *f_p = from.root; f_p; f_p = f_p->n)
|
|
if (!what.Find(f_p->Object()))
|
|
list->Add(f_p->Object());
|
|
return list;
|
|
}
|
|
|
|
/// Filter out linked-list item function.
|
|
template <typename F> void FilterOut(F filter)
|
|
{
|
|
for (Item *c = GetRoot(); c; )
|
|
{
|
|
Item *n = c->Next();
|
|
if (filter(c->Object()))
|
|
Remove(c);
|
|
c = n;
|
|
}
|
|
}
|
|
|
|
/// Build a new list from select items.
|
|
template <typename F, typename P> uint Select(List <T> &out, F filter, const P &filter_param) const
|
|
{
|
|
out.Clear();
|
|
for (Item *c = GetRoot(); c; )
|
|
if (filter(c->Object(), filter_param))
|
|
out.Add(c->Object());
|
|
return out.GetCount();
|
|
}
|
|
|
|
/// Sort linked-list function.
|
|
template <typename C> void Sort(C compare)
|
|
{
|
|
for (bool swapped = true; swapped; )
|
|
{
|
|
swapped = false;
|
|
|
|
for (Item *c = GetRoot(); c; )
|
|
{
|
|
Item *n = c->Next();
|
|
if (!n)
|
|
break;
|
|
|
|
if (compare(c->Object(), n->Object()) < 0)
|
|
{
|
|
swapped = true;
|
|
|
|
c->n = n->n;
|
|
n->n = c;
|
|
n->p = c->p;
|
|
c->p = n;
|
|
|
|
if (n->p)
|
|
n->p->n = n;
|
|
else root = n;
|
|
|
|
if (c->n)
|
|
c->n->p = c;
|
|
else last = c;
|
|
}
|
|
else
|
|
c = n;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Merge sort linked-list function.
|
|
template <typename C> void MergeSort(C compare)
|
|
{
|
|
Item *list = root, *tail = 0;
|
|
if (!list)
|
|
return;
|
|
|
|
for (int insize = 1; ; insize *= 2)
|
|
{
|
|
Item *p = list;
|
|
list = 0;
|
|
tail = 0;
|
|
|
|
int merge_count = 0; // Count number of merges we do in this pass.
|
|
|
|
while (p)
|
|
{
|
|
merge_count++;
|
|
|
|
// Step along from p.
|
|
Item *q = p;
|
|
int psize = 0;
|
|
for ( ; q && (psize < insize); ++psize)
|
|
q = q->Next();
|
|
|
|
// If q hasn't fallen off end, we have two lists to merge.
|
|
int qsize = insize;
|
|
|
|
// Now we have two lists, merge them.
|
|
while (psize > 0 || (qsize > 0 && q))
|
|
{
|
|
Item *e;
|
|
|
|
if (!psize) // p is empty, e must come from q.
|
|
{ e = q; q = q->Next(); qsize--; }
|
|
else if (!qsize || !q) // q is empty, e must come from p.
|
|
{ e = p; p = p->Next(); psize--; }
|
|
else if (compare(p->Object(), q->Object()) <= 0) // First element of p is lower (or same), e must come from p.
|
|
{ e = p; p = p->Next(); psize--; }
|
|
else // First element of q is lower; e must come from q.
|
|
{ e = q; q = q->Next(); qsize--; }
|
|
|
|
// Add the next element to the merged list.
|
|
if (tail)
|
|
tail->n = e;
|
|
else list = e;
|
|
|
|
e->p = tail;
|
|
tail = e;
|
|
}
|
|
|
|
// Now p has stepped `insize' places along, and q has too.
|
|
p = q;
|
|
}
|
|
|
|
tail->n = 0;
|
|
|
|
// If we have done only one merge, we're done.
|
|
if (merge_count <= 1)
|
|
break;
|
|
}
|
|
|
|
root = list;
|
|
last = tail;
|
|
}
|
|
|
|
/// Extract object from list.
|
|
Item *ExtractItem(const T &o)
|
|
{
|
|
Item*i = Find(o);
|
|
return i ? ExtractItem(i) : 0;
|
|
}
|
|
|
|
/// Extract item from list.
|
|
Item *ExtractItem(Item *i)
|
|
{
|
|
if (!i)
|
|
return 0;
|
|
|
|
__ASSERT__(Belongs(i));
|
|
|
|
if (i->p)
|
|
i->p->n = i->n;
|
|
else
|
|
{
|
|
if (i->n)
|
|
i->n->p = 0;
|
|
root = i->n;
|
|
}
|
|
|
|
if (i->n)
|
|
i->n->p = i->p;
|
|
else
|
|
{
|
|
if (i->p)
|
|
i->p->n = 0;
|
|
last = i->p;
|
|
}
|
|
|
|
i->p = i->n = 0;
|
|
|
|
--count;
|
|
return i;
|
|
}
|
|
/// Extract item at position.
|
|
Item *ExtractAt(uint n)
|
|
{
|
|
Item *i = ItemAt(n);
|
|
return i ? ExtractItem(i) : 0;
|
|
}
|
|
|
|
/// Remove item from list.
|
|
virtual bool Remove(Item *i)
|
|
{
|
|
if (ExtractItem(i) == 0)
|
|
return false;
|
|
|
|
delete i;
|
|
return true;
|
|
}
|
|
|
|
/// Remove item from list.
|
|
virtual bool Remove(const T &o)
|
|
{
|
|
return Remove(Find(o));
|
|
}
|
|
|
|
/// Remove item at position.
|
|
bool RemoveAt(uint n)
|
|
{
|
|
Item *i = ItemAt(n);
|
|
return i ? Remove(i) : false;
|
|
}
|
|
|
|
/// Remove all items from the list.
|
|
virtual void Clear()
|
|
{
|
|
for (Item *p = root, *n; p; p = n)
|
|
{
|
|
n = p->n;
|
|
delete p;
|
|
}
|
|
|
|
count = 0;
|
|
root = last = 0;
|
|
}
|
|
|
|
List()
|
|
{
|
|
count = 0;
|
|
root = last = 0;
|
|
}
|
|
virtual ~List()
|
|
{ Clear(); }
|
|
};
|
|
|
|
/*
|
|
@short Auto linked-list.
|
|
@author Emmanuel Julien (ejulien@gsworks.fr)
|
|
*/
|
|
template <class T> struct AutoList : public List <T>
|
|
{
|
|
virtual bool Remove(class List <T> ::Item *i)
|
|
{
|
|
T o = i->Object();
|
|
if (!List <T> ::Remove(i))
|
|
return false;
|
|
delete o;
|
|
return true;
|
|
}
|
|
virtual bool Remove(const T &o)
|
|
{
|
|
class List <T> ::Item *i = this->Find(o);
|
|
return i ? this->Remove(i) : false;
|
|
}
|
|
|
|
virtual void Clear()
|
|
{
|
|
for (class List <T> ::Item *p = this->root; p; p = p->Next())
|
|
delete p->Object();
|
|
List <T> ::Clear();
|
|
}
|
|
virtual ~AutoList()
|
|
{ this->Clear(); }
|
|
};
|
|
|
|
/*
|
|
@short Shared object linked-list.
|
|
@author Emmanuel Julien (ejulien@gsworks.fr)
|
|
*/
|
|
template <class T> struct SharedList : public List <T>
|
|
{
|
|
virtual class List <T> ::Item *Append(const T &o, class List <T> ::Item *rfr = 0)
|
|
{
|
|
class List <T> ::Item *i = List <T> ::Append(o, rfr);
|
|
if (i)
|
|
o->AddRef();
|
|
return i;
|
|
}
|
|
virtual class List <T> ::Item *Prepend(const T &o, class List <T> ::Item *rfr = 0)
|
|
{
|
|
class List <T> ::Item *i = List <T> ::Prepend(o, rfr);
|
|
if (i)
|
|
o->AddRef();
|
|
return i;
|
|
}
|
|
|
|
virtual bool Remove(class List <T> ::Item *i)
|
|
{
|
|
T o = i->Object();
|
|
if (!List <T> ::Remove(i))
|
|
return false;
|
|
o->RemoveRef();
|
|
return true;
|
|
}
|
|
virtual bool Remove(const T &o)
|
|
{
|
|
class List <T> ::Item *i = this->Find(o);
|
|
return i ? this->Remove(i) : false;
|
|
}
|
|
|
|
virtual void Clear()
|
|
{
|
|
for (class List <T> ::Item *p = this->root; p; p = p->Next())
|
|
p->Object()->RemoveRef();
|
|
List <T> ::Clear();
|
|
}
|
|
virtual ~SharedList()
|
|
{ Clear(); }
|
|
};
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Delete all list entries.
|
|
#define ListDeleteAllPtr(T, L) { class GS::List <T> ::Item *t; while ((t = (L).GetRoot()) != 0) { T _o = t->Object(); (L).Remove(t); delete(_o); } }
|
|
// Iterate over a list of pointers.
|
|
#define ListForeachPtr(T, V, L) \
|
|
for (class GS::List <T> ::Iterator iterator((L).GetRoot()); T V = iterator.ObjectPtr(); ++iterator)
|
|
// Iterate over a list of objects.
|
|
#define ListForeach(T, V, L) \
|
|
for (class GS::List <T> ::Iterator V((L).GetRoot()); V.IsOver() == false; ++V)
|
|
|
|
/// Find item by using a template identification class.
|
|
template <typename T, typename F, typename P> T ListFindEx(const List <T> &list, F filter, const P &what)
|
|
{
|
|
for (class List <T> ::Item *p = list.GetRoot(); p; p = p->Next())
|
|
if (filter(p->Object(), what))
|
|
return p->Object();
|
|
return 0;
|
|
}
|
|
/// Remove all items with a reference count of 1 from a shared list.
|
|
template <class T> uint PurgeSharedList(SharedList <T *> &list)
|
|
{
|
|
uint c = list.GetCount();
|
|
ListForeachPtr(T *, t, list)
|
|
if (t->GetRefCount() == 1)
|
|
list.Remove(iterator.GetItem());
|
|
return c - list.GetCount();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
} // GS
|
|
|
|
|
|
#endif // __NLIST__
|