/*==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 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 () { SetLinkOffset(linkOffset); } }; /**************************************************************************** * * TPriorityQueueDyn * ***/ template class TPriorityQueueDyn : public TPriorityQueue { public: void Initialize (int linkOffset) { 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 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; 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; 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; };