You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

631 lines
19 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/*****************************************************************************
*
* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h
*
***/
#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTLIST_H
#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h included more than once"
#endif
#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTLIST_H
/****************************************************************************
*
* Constants
*
***/
enum ELinkType {
kListUnlinked,
kListLinkAfter,
kListLinkBefore,
kListHead = kListLinkAfter,
kListTail = kListLinkBefore
};
/****************************************************************************
*
* Macros
*
***/
#define LINK(class) TLink< class >
#define LIST(class) TList< class >
#define LISTDECL(class,field) TListDecl< class, offsetof(class,field) >
#define LISTDYN(class) TListDyn< class >
/****************************************************************************
*
* Forward declarations
*
***/
template<class T>
class TLink;
template<class T>
class TList;
/****************************************************************************
*
* CBaseLink
*
***/
class CBaseLink {
friend class CBaseList;
inline static bool TermCheck (byte * ptr);
inline static byte * TermMark (byte * ptr);
inline static byte * TermUnmarkAlways (byte * ptr);
protected:
CBaseLink * volatile m_prevLink;
byte * volatile m_next;
inline int CalcLinkOffset () const;
inline void InitializeLinks ();
inline void InitializeLinksWithOffset (int linkOffset);
inline void InsertAfter (byte * node, CBaseLink * prevLink, int linkOffset);
inline void InsertBefore (byte * node, CBaseLink * nextLink);
inline byte * Next () const;
inline byte * NextIgnoreTerm () const;
inline byte * NextUnchecked () const;
inline CBaseLink * NextLink () const;
inline CBaseLink * NextLink (int linkOffset) const;
inline byte * Prev () const;
inline void UnlinkFromNeighbors ();
public:
inline CBaseLink ();
inline CBaseLink (const CBaseLink & source);
inline ~CBaseLink ();
inline CBaseLink & operator= (const CBaseLink & source);
inline bool IsLinked () const;
inline void Unlink ();
};
//===========================================================================
CBaseLink::CBaseLink () {
InitializeLinks();
}
//===========================================================================
CBaseLink::CBaseLink (const CBaseLink & source) {
#ifdef HS_DEBUGGING
if (source.IsLinked())
FATAL("No copy constructor");
#endif
InitializeLinks();
}
//===========================================================================
CBaseLink::~CBaseLink () {
UnlinkFromNeighbors();
}
//===========================================================================
CBaseLink & CBaseLink::operator= (const CBaseLink & source) {
#ifdef HS_DEBUGGING
FATAL("No assignment operator");
#endif
return *this;
}
//===========================================================================
int CBaseLink::CalcLinkOffset () const {
return (int)((byte *)this - m_prevLink->NextIgnoreTerm());
}
//===========================================================================
void CBaseLink::InitializeLinks () {
ASSERT(!((unsigned_ptr)this & 3));
m_prevLink = this;
m_next = TermMark((byte *)this);
}
//===========================================================================
void CBaseLink::InitializeLinksWithOffset (int linkOffset) {
m_prevLink = this;
m_next = TermMark((byte *)this - linkOffset);
}
//===========================================================================
void CBaseLink::InsertAfter (byte * node, CBaseLink * prevLink, int linkOffset) {
UnlinkFromNeighbors();
m_prevLink = prevLink;
m_next = prevLink->m_next;
prevLink->NextLink(linkOffset)->m_prevLink = this;
prevLink->m_next = node;
}
//===========================================================================
void CBaseLink::InsertBefore (byte * node, CBaseLink * nextLink) {
UnlinkFromNeighbors();
m_prevLink = nextLink->m_prevLink;
m_next = m_prevLink->m_next;
nextLink->m_prevLink->m_next = node;
nextLink->m_prevLink = this;
}
//===========================================================================
bool CBaseLink::IsLinked () const {
return (m_prevLink != this);
}
//===========================================================================
byte * CBaseLink::Next () const {
return TermCheck(m_next) ? nil : m_next;
}
//===========================================================================
byte * CBaseLink::NextIgnoreTerm () const {
return TermUnmarkAlways(m_next);
}
//===========================================================================
byte * CBaseLink::NextUnchecked () const {
return m_next;
}
//===========================================================================
CBaseLink * CBaseLink::NextLink () const {
return (CBaseLink *)(NextIgnoreTerm() + CalcLinkOffset());
}
//===========================================================================
CBaseLink * CBaseLink::NextLink (int linkOffset) const {
ASSERT(linkOffset == CalcLinkOffset());
return (CBaseLink *)(NextIgnoreTerm() + linkOffset);
}
//===========================================================================
byte * CBaseLink::Prev () const {
return m_prevLink->m_prevLink->Next();
}
//===========================================================================
bool CBaseLink::TermCheck (byte * ptr) {
return (unsigned_ptr)ptr & 1;
}
//===========================================================================
byte * CBaseLink::TermMark (byte * ptr) {
// Converts an unmarked pointer to a marked pointer
ASSERT(!TermCheck(ptr));
return (byte *)((unsigned_ptr)ptr + 1);
}
//===========================================================================
byte * CBaseLink::TermUnmarkAlways (byte * ptr) {
// Returns an unmarked pointer regardless of whether the source pointer
// was marked on unmarked
return (byte *)((unsigned_ptr)ptr & ~1);
}
//===========================================================================
void CBaseLink::Unlink () {
UnlinkFromNeighbors();
InitializeLinks();
}
//===========================================================================
void CBaseLink::UnlinkFromNeighbors () {
NextLink()->m_prevLink = m_prevLink;
m_prevLink->m_next = m_next;
}
/****************************************************************************
*
* TLink
*
***/
template<class T>
class TLink : public CBaseLink {
public:
inline T * Next ();
inline const T * Next () const;
inline T * NextUnchecked ();
inline const T * NextUnchecked () const;
inline T * Prev ();
inline const T * Prev () const;
};
//===========================================================================
template<class T>
T * TLink<T>::Next () {
return (T *)CBaseLink::Next();
}
//===========================================================================
template<class T>
const T * TLink<T>::Next () const {
return (const T *)CBaseLink::Next();
}
//===========================================================================
template<class T>
T * TLink<T>::NextUnchecked () {
return (T *)CBaseLink::NextUnchecked();
}
//===========================================================================
template<class T>
const T * TLink<T>::NextUnchecked () const {
return (const T *)CBaseLink::NextUnchecked();
}
//===========================================================================
template<class T>
T * TLink<T>::Prev () {
return (T *)CBaseLink::Prev();
}
//===========================================================================
template<class T>
const T * TLink<T>::Prev () const {
return (const T *)CBaseLink::Prev();
}
/****************************************************************************
*
* CBaseList
*
***/
class CBaseList {
private:
enum { LINK_OFFSET_UNINIT = 0xDDDDDDDD };
int m_linkOffset;
CBaseLink m_terminator;
protected:
inline CBaseLink * GetLink (const byte * node) const;
inline byte * Head () const;
inline bool IsLinked (const byte * node) const;
inline void Link (byte * node, ELinkType linkType, byte * existingNode);
void Link (CBaseList * list, byte * afterNode, byte * beforeNode, ELinkType linkType, byte * existingNode);
inline byte * Next (const byte * node) const;
inline byte * NextUnchecked (const byte * node) const;
inline byte * Prev (byte * node) const;
inline byte * Tail () const;
inline void Unlink (byte * node);
public:
inline CBaseList ();
inline CBaseList (const CBaseList & source);
inline ~CBaseList ();
inline CBaseList & operator= (const CBaseList & source);
inline void SetLinkOffset (int linkOffset);
void UnlinkAll ();
};
//===========================================================================
CBaseList::CBaseList () {
m_linkOffset = LINK_OFFSET_UNINIT;
}
//===========================================================================
CBaseList::CBaseList (const CBaseList & source) {
m_linkOffset = LINK_OFFSET_UNINIT;
}
//===========================================================================
CBaseList::~CBaseList () {
if (m_terminator.IsLinked())
UnlinkAll();
}
//===========================================================================
CBaseList & CBaseList::operator= (const CBaseList & source) {
return *this;
}
//===========================================================================
CBaseLink * CBaseList::GetLink (const byte * node) const {
return (CBaseLink *)(node + m_linkOffset);
}
//===========================================================================
byte * CBaseList::Head () const {
return m_terminator.Next();
}
//===========================================================================
bool CBaseList::IsLinked (const byte * node) const {
ASSERT(node);
return GetLink(node)->IsLinked();
}
//===========================================================================
void CBaseList::Link (byte * node, ELinkType linkType, byte * existingNode) {
ASSERT(node != existingNode);
ASSERT(m_linkOffset != LINK_OFFSET_UNINIT);
ASSERT((linkType == kListLinkAfter) || (linkType == kListLinkBefore));
if (linkType == kListLinkAfter)
GetLink(node)->InsertAfter(node, existingNode ? GetLink(existingNode) : &m_terminator, m_linkOffset);
else
GetLink(node)->InsertBefore(node, existingNode ? GetLink(existingNode) : &m_terminator);
}
//===========================================================================
byte * CBaseList::Next (const byte * node) const {
ASSERT(node);
return GetLink(node)->Next();
}
//===========================================================================
byte * CBaseList::NextUnchecked (const byte * node) const {
ASSERT(node);
return GetLink(node)->NextUnchecked();
}
//===========================================================================
byte * CBaseList::Prev (byte * node) const {
ASSERT(node);
return GetLink(node)->Prev();
}
//===========================================================================
void CBaseList::SetLinkOffset (int linkOffset) {
ASSERT(!Head());
m_linkOffset = linkOffset;
m_terminator.InitializeLinksWithOffset(linkOffset);
}
//===========================================================================
byte * CBaseList::Tail () const {
return m_terminator.Prev();
}
//===========================================================================
void CBaseList::Unlink (byte * node) {
ASSERT(node);
GetLink(node)->Unlink();
}
/****************************************************************************
*
* TList
*
***/
template<class T>
class TList : public CBaseList {
private:
inline T * NewFlags (unsigned flags, ELinkType linkType, T * existingNode, const char file[], int line);
public:
inline void Clear ();
inline void Delete (T * node);
inline T * Head ();
inline const T * Head () const;
inline bool IsLinked (const T * node) const;
inline void Link (T * node, ELinkType linkType = kListTail, T * existingNode = nil);
inline void Link (TList<T> * list, ELinkType linkType = kListTail, T * existingNode = nil);
inline void Link (TList<T> * list, T * afterNode, T * beforeNode, ELinkType linkType = kListTail, T * existingNode = nil);
inline T * New (ELinkType linkType = kListTail, T * existingNode = nil, const char file[] = nil, int line = 0);
inline T * NewZero (ELinkType linkType = kListTail, T * existingNode = nil, const char file[] = nil, int line = 0);
inline T * Next (const T * node);
inline const T * Next (const T * node) const;
inline T * NextUnchecked (const T * node);
inline const T * NextUnchecked (const T * node) const;
inline T * Prev (const T * node);
inline const T * Prev (const T * node) const;
inline T * Tail ();
inline const T * Tail () const;
inline void Unlink (T * node);
};
//===========================================================================
template<class T>
void TList<T>::Clear () {
for (T * curr; (curr = Head()) != nil; Delete(curr))
;
}
//===========================================================================
template<class T>
void TList<T>::Delete (T * node) {
DEL(node);
}
//===========================================================================
template<class T>
T * TList<T>::Head () {
return (T *)CBaseList::Head();
}
//===========================================================================
template<class T>
const T * TList<T>::Head () const {
return (const T *)CBaseList::Head();
}
//===========================================================================
template<class T>
bool TList<T>::IsLinked (const T * node) const {
return CBaseList::IsLinked((const byte *)node);
}
//===========================================================================
template<class T>
void TList<T>::Link (T * node, ELinkType linkType, T * existingNode) {
CBaseList::Link((byte *)node, linkType, (byte *)existingNode);
}
//===========================================================================
template<class T>
void TList<T>::Link (TList<T> * list, ELinkType linkType, T * existingNode) {
CBaseList::Link(list, nil, nil, linkType, (byte *)existingNode);
}
//===========================================================================
template<class T>
void TList<T>::Link (TList<T> * list, T * afterNode, T * beforeNode, ELinkType linkType, T * existingNode) {
CBaseList::Link(list, (byte *)afterNode, (byte *)beforeNode, linkType, (byte *)existingNode);
}
//===========================================================================
template<class T>
inline T * TList<T>::Next (const T * node) {
return (T *)CBaseList::Next((byte *)node);
}
//===========================================================================
template<class T>
inline const T * TList<T>::Next (const T * node) const {
return (const T *)CBaseList::Next((byte *)node);
}
//===========================================================================
template<class T>
inline T * TList<T>::NextUnchecked (const T * node) {
return (T *)CBaseList::NextUnchecked((byte *)node);
}
//===========================================================================
template<class T>
inline const T * TList<T>::NextUnchecked (const T * node) const {
return (const T *)CBaseList::NextUnchecked((byte *)node);
}
//===========================================================================
template<class T>
inline T * TList<T>::New (ELinkType linkType, T * existingNode, const char file[], int line) {
return NewFlags(0, linkType, existingNode, file, line);
}
//===========================================================================
template<class T>
inline T * TList<T>::NewFlags (unsigned flags, ELinkType linkType, T * existingNode, const char file[], int line) {
if (!file) {
file = __FILE__;
line = __LINE__;
}
T * node = new(MemAlloc(sizeof(T), flags, file, line)) T;
if (linkType != kListUnlinked)
Link(node, linkType, existingNode);
return node;
}
//===========================================================================
template<class T>
inline T * TList<T>::NewZero (ELinkType linkType, T * existingNode, const char file[], int line) {
return NewFlags(0, linkType, existingNode, file, line);
}
//===========================================================================
template<class T>
inline T * TList<T>::Prev (const T * node) {
return (T *)CBaseList::Prev((byte *)node);
}
//===========================================================================
template<class T>
inline const T * TList<T>::Prev (const T * node) const {
return (const T *)CBaseList::Prev((byte *)node);
}
//===========================================================================
template<class T>
inline T * TList<T>::Tail () {
return (T *)CBaseList::Tail();
}
//===========================================================================
template<class T>
inline const T * TList<T>::Tail () const {
return (const T *)CBaseList::Tail();
}
//===========================================================================
template<class T>
inline void TList<T>::Unlink (T * node) {
CBaseList::Unlink((byte *)node);
}
/****************************************************************************
*
* TListDecl
*
***/
template<class T, int linkOffset>
class TListDecl : public TList<T> {
public:
inline TListDecl ();
};
//===========================================================================
template<class T, int linkOffset>
TListDecl<T,linkOffset>::TListDecl () {
this->SetLinkOffset(linkOffset);
}
/****************************************************************************
*
* TListDyn
*
***/
template<class T>
class TListDyn : public TList<T> {
public:
void Initialize (int linkOffset);
};
//===========================================================================
template<class T>
void TListDyn<T>::Initialize (int linkOffset) {
this->SetLinkOffset(linkOffset);
}