/*==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 .
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
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/pnUtPriQ.h
*
***/
#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPRIQ_H
#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPriQ.h included more than once"
#endif
#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPRIQ_H
/****************************************************************************
*
* Macros
*
***/
#define PRIORITY_TIME(class) TPriorityTime< class >
#define PRIORITY_NUMERIC(class,type) TPriorityNumeric< class,type >
#define PRIQ(class,priority) TPriorityQueue< class,priority >
#define PRIQDECL(class,priority,field) TPriorityQueueDecl< class,priority,offsetof(class,field) >
#define PRIQDYN(class,priority) TPriorityQueueDyn< class,priority >
/****************************************************************************
*
* class TPriorityQueue
*
***/
template
class TBasePriority;
template
class TPriorityQueue {
public:
TPriorityQueue ();
~TPriorityQueue ();
C * const & operator[] (unsigned index) const;
void Clear ();
unsigned Count () const;
C * Delete (C * object);
C * Dequeue ();
void Enqueue (C * object);
C * const * Ptr () const;
C * Root () const;
C * const * Term () const;
void UnlinkAll ();
public:
// Intentionally unimplemented
TPriorityQueue (TPriorityQueue const &);
TPriorityQueue const & operator= (TPriorityQueue const &);
protected:
void SetLinkOffset (int offset);
private:
unsigned IndexChild (unsigned index) const;
unsigned IndexParent (unsigned index) const;
void Link (unsigned index);
P * Priority (C * object);
P const * Priority (C const * object) const;
void Remove (unsigned index);
void Unlink (unsigned index);
enum { LINK_OFFSET_UNINIT = 0xdddddddd };
int m_linkOffset;
ARRAY(C *) m_array;
friend class TBasePriority;
};
//===========================================================================
template
inline C * const & TPriorityQueue::operator[] (unsigned index) const {
return m_array[index];
}
//===========================================================================
template
inline TPriorityQueue::TPriorityQueue () :
m_linkOffset(LINK_OFFSET_UNINIT) {
}
//===========================================================================
template
inline TPriorityQueue::~TPriorityQueue () {
UnlinkAll();
}
//===========================================================================
template
inline void TPriorityQueue::Clear () {
// Deleting an object could cause other objects in the queue to be deleted
// so we can't make any assumptions about indices or counts of items in the array
while (C * head = Dequeue())
DEL(head);
m_array.Clear();
}
//===========================================================================
template
inline unsigned TPriorityQueue::Count () const {
return m_array.Count();
}
//===========================================================================
template
C * TPriorityQueue::Delete (C * object) {
// get the object's priority queue and position
P * priority = Priority(object);
const TPriorityQueue * queue = priority->GetLink();
unsigned index = priority->GetIndex();
// delete the object
DEL(object);
// return the next object in that queue
if (queue && (index < queue->Count()))
return (*queue)[index];
else
return nil;
}
//===========================================================================
template
C * TPriorityQueue::Dequeue () {
if (!m_array.Count())
return nil;
C * value = m_array[0];
Remove(0);
return value;
}
//===========================================================================
template
void TPriorityQueue::Enqueue (C * object) {
P * priority = Priority(object);
// Verify that the object is not already linked into a priority queue.
// The original implementation of this function silently refused to
// enqueue at a new priority if the object was already in this queue.
// Since this behavior requires callers to check whether the object is
// already enqueued, we now simply assert that.
ASSERT(!priority->IsLinked());
unsigned index = m_array.Add(object);
unsigned parent = IndexParent(index);
// shift value toward root
while (index && priority->IsPriorityHigher(*Priority(m_array[parent]))) {
m_array[index] = m_array[parent];
Link(index);
index = parent;
parent = IndexParent(index);
}
// assign and link the new value
m_array[index] = object;
Link(index);
}
//===========================================================================
template
inline unsigned TPriorityQueue::IndexChild (unsigned index) const {
return (index << 1) + 1;
}
//===========================================================================
template
inline unsigned TPriorityQueue::IndexParent (unsigned index) const {
return (index - 1) >> 1;
}
//===========================================================================
template
inline void TPriorityQueue::Link (unsigned index) {
Priority(m_array[index])->Link(this, index);
}
//===========================================================================
template
inline P * TPriorityQueue::Priority (C * object) {
ASSERT(m_linkOffset != LINK_OFFSET_UNINIT);
return (P *)((byte *)object + m_linkOffset);
}
//===========================================================================
template
inline P const * TPriorityQueue::Priority (C const * object) const {
ASSERT(m_linkOffset != LINK_OFFSET_UNINIT);
return (P const *)((byte const *)object + m_linkOffset);
}
//===========================================================================
template
inline C * const * TPriorityQueue::Ptr () const {
return m_array.Ptr();
}
//===========================================================================
template
void TPriorityQueue::Remove (unsigned index) {
// reset the priority link fields
Unlink(index);
// save the terminal leaf node
C * value = m_array.Pop();
P * priority = Priority(value);
const unsigned count = m_array.Count();
if (count == index)
return;
// rebalance upwards from the position of the deleted entry
unsigned parent;
unsigned entry = index;
if (entry && priority->IsPriorityHigher(*Priority(m_array[parent = IndexParent(entry)]))) {
do {
m_array[entry] = m_array[parent];
Link(entry);
entry = parent;
} while (entry && priority->IsPriorityHigher(*Priority(m_array[parent = IndexParent(entry)])));
m_array[entry] = value;
Link(entry);
entry = index;
value = m_array[index];
priority = Priority(value);
}
// rebalance downwards from the position of the deleted entry
for (;;) {
unsigned child = IndexChild(entry);
if (child >= count)
break;
unsigned sibling = child + 1;
if ( (sibling < count) &&
(Priority(m_array[sibling])->IsPriorityHigher(*Priority(m_array[child]))) )
child = sibling;
if (priority->IsPriorityHigher(*Priority(m_array[child])))
break;
m_array[entry] = m_array[child];
Link(entry);
entry = child;
}
m_array[entry] = value;
Link(entry);
}
//===========================================================================
template
inline C * TPriorityQueue::Root () const {
return m_array.Count() ? m_array[0] : nil;
}
//===========================================================================
template
inline void TPriorityQueue::SetLinkOffset (int offset) {
ASSERT(m_linkOffset == LINK_OFFSET_UNINIT);
m_linkOffset = offset;
}
//===========================================================================
template
inline C * const * TPriorityQueue::Term () const {
return m_array.Term();
}
//===========================================================================
template
inline void TPriorityQueue::Unlink (unsigned index) {
Priority(m_array[index])->Link(nil, 0);
}
//===========================================================================
template
inline void TPriorityQueue::UnlinkAll () {
for (unsigned loop = m_array.Count(); loop--; )
Unlink(loop);
m_array.ZeroCount();
}
/****************************************************************************
*
* TPriorityQueueDecl
*
***/
template
class TPriorityQueueDecl : public TPriorityQueue {
public:
TPriorityQueueDecl () { this->SetLinkOffset(linkOffset); }
};
/****************************************************************************
*
* TPriorityQueueDyn
*
***/
template
class TPriorityQueueDyn : public TPriorityQueue {
public:
void Initialize (int linkOffset) { this->SetLinkOffset(linkOffset); }
};
/****************************************************************************
*
* class TBasePriority
*
***/
template
class TBasePriority {
public:
TBasePriority () : m_queue(nil), m_index(0) { }
virtual ~TBasePriority () { Unlink(); }
void Unlink () { if (m_queue) m_queue->Remove(m_index); }
bool IsLinked () const { return m_queue != nil; }
public:
TBasePriority (const TBasePriority &);
const TBasePriority & operator= (const TBasePriority &);
protected:
void Relink ();
private:
void Link (TPriorityQueue * queue, unsigned index);
const TPriorityQueue * GetLink () const { return m_queue; }
unsigned GetIndex () const { return m_index; }
private:
TPriorityQueue * m_queue;
unsigned m_index;
friend class TPriorityQueue;
};
//===========================================================================
template
inline void TBasePriority::Link (TPriorityQueue * queue, unsigned index) {
m_queue = queue;
m_index = index;
}
//===========================================================================
template
void TBasePriority::Relink () {
// cache m_queue, since m_queue->Remove() will set it to nil
TPriorityQueue * queue = m_queue;
if (!queue)
return;
C * object = (*queue)[m_index];
queue->Remove(m_index);
queue->Enqueue(object);
}
/****************************************************************************
*
* class TPriorityNumeric
*
***/
template
class TPriorityNumeric : public TBasePriority< C, TPriorityNumeric > {
public:
TPriorityNumeric () : m_value(0) { }
TPriorityNumeric (T value) : m_value(value) { }
void Set (T value) {
if (value == m_value)
return;
m_value = value;
this->Relink();
}
T Get () const {
return m_value;
}
bool IsPriorityHigher (const TPriorityNumeric & source) {
return m_value > source.m_value;
}
bool IsPriorityHigher (T value) const {
return m_value > value;
}
private:
T m_value;
};
/****************************************************************************
*
* class TPriorityTime
*
***/
template
class TPriorityTime : public TBasePriority< C, TPriorityTime > {
public:
TPriorityTime () : m_time(0) { }
TPriorityTime (unsigned time) : m_time(time) { }
void Set (unsigned time) {
if (m_time == time)
return;
m_time = time;
this->Relink();
}
unsigned Get () const {
return m_time;
}
bool IsPriorityHigher (const TPriorityTime & source) const {
return (int)(m_time - source.m_time) < 0;
}
bool IsPriorityHigher (unsigned time) const {
return (int)(m_time - time) < 0;
}
private:
unsigned m_time;
};