/*==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 . 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/pnUtSkipList.h * ***/ #ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSKIPLIST_H #error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSkipList.h included more than once" #endif #define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSKIPLIST_H /***************************************************************************** * * Macros * ***/ #define SKIPLIST(type, keyType, keyField, cmp) TSkipList< type, keyType, offsetof(type, keyField), cmp > #define SKIPLIST_NUMERIC(type, keyType, keyField) SKIPLIST(type, keyType, keyField, TSkipListNumericCmp) #define SKIPLIST_STRING(type, keyType, keyField) SKIPLIST(type, keyType, keyField, TSkipListStringCmp) #define SKIPLIST_STRINGI(type, keyType, keyField) SKIPLIST(type, keyType, keyField, TSkipListStringCmpI) /***************************************************************************** * * Typedefs * ***/ typedef void * SkipListTag; /***************************************************************************** * * Comparers * ***/ template class TSkipListNumericCmp { public: static bool Eq (const K & a, const K & b) { return a == b; } static bool Lt (const K & a, const K & b) { return a < b; } }; template class TSkipListStringCmp { public: static bool Eq (const K & a, const K & b) { return StrCmp(a, b, (unsigned)-1) == 0; } static bool Lt (const K & a, const K & b) { return StrCmp(a, b, (unsigned)-1) < 0; } }; template class TSkipListStringCmpI { public: static bool Eq (const K & a, const K & b) { return StrCmpI(a, b, (unsigned)-1) == 0; } static bool Lt (const K & a, const K & b) { return StrCmpI(a, b, (unsigned)-1) < 0; } }; /***************************************************************************** * * TSkipList * ***/ template class TSkipList { private: enum { kMaxLevels = 32 }; template struct TNode { const K * key; T * object; unsigned level; TNode * prev; TNode * next[1]; // variable size array }; typedef TNode Node; unsigned m_level; Node * m_head; Node * m_stop; unsigned m_randomBits; unsigned m_randomsLeft; Node * AllocNode (unsigned levels); void FreeNode (Node * node); unsigned RandomLevel (); public: inline TSkipList (); inline ~TSkipList (); inline void Clear (); inline void Delete (T * object); inline T * Find (const K & key, SkipListTag * tag = nil) const; inline T * FindNext (SkipListTag * tag) const; inline T * Head (SkipListTag * tag) const; inline T * Next (SkipListTag * tag) const; inline T * Prev (SkipListTag * tag) const; inline T * Tail (SkipListTag * tag) const; inline void Link (T * object); inline void Unlink (T * object); inline void Unlink (SkipListTag * tag); inline void UnlinkAll (); #ifdef HS_DEBUGGING inline void Print () const; #endif }; /***************************************************************************** * * TSkipList private member functions * ***/ //============================================================================ template typename TSkipList::TNode * TSkipList::AllocNode (unsigned level) { unsigned size = offsetof(Node, next) + (level + 1) * sizeof(Node); Node * node = (Node *)ALLOC(size); node->level = level; return node; } //============================================================================ template void TSkipList::FreeNode (TNode * node) { FREE(node); } //============================================================================ template unsigned TSkipList::RandomLevel () { unsigned level = 0; unsigned bits = 0; while (!bits) { bits = m_randomBits % 4; if (!bits) ++level; m_randomBits >>= 2; m_randomsLeft -= 2; if (!m_randomsLeft) { m_randomBits = RandUnsigned(); m_randomsLeft = 30; } } return level; } /***************************************************************************** * * TSkipList public member functions * ***/ //============================================================================ template TSkipList::TSkipList () { m_level = 0; m_head = AllocNode(kMaxLevels); m_stop = AllocNode(0); m_randomBits = RandUnsigned(); m_randomsLeft = 30; // Initialize header and stop skip node pointers m_stop->prev = m_head; m_stop->object = nil; m_stop->next[0] = nil; m_head->object = nil; for (unsigned index = 0; index < kMaxLevels; ++index) m_head->next[index] = m_stop; } //============================================================================ template TSkipList::~TSkipList () { UnlinkAll(); ASSERT(m_stop->prev == m_head); FreeNode(m_head); FreeNode(m_stop); } //============================================================================ template void TSkipList::Clear () { Node * ptr = m_head->next[0]; while (ptr != m_stop) { Node * next = ptr->next[0]; DEL(ptr->object); FreeNode(ptr); ptr = next; } m_stop->prev = m_head; for (unsigned index = 0; index < kMaxLevels; ++index) m_head->next[index] = m_stop; m_level = 0; } //============================================================================ template void TSkipList::Delete (T * object) { Unlink(object); DEL(object); } //============================================================================ template T * TSkipList::Find (const K & key, SkipListTag * tag) const { Node * node = m_head; m_stop->key = &key; for (int level = (int)m_level; level >= 0; --level) while (Cmp::Lt(*node->next[level]->key, key)) node = node->next[level]; node = node->next[0]; if (node != m_stop && Cmp::Eq(*node->key, *m_stop->key)) { if (tag) *tag = node; return node->object; } else { if (tag) *tag = nil; return nil; } } //============================================================================ template T * TSkipList::FindNext (SkipListTag * tag) const { Node * node = (Node *)*tag; m_stop->key = node->key; for (int level = (int)node->level; level >= 0; --level) while (Cmp::Lt(*node->next[level]->key, *m_stop->key)) node = node->next[level]; node = node->next[0]; if (node != m_stop && Cmp::Eq(*node->key, *m_stop->key)) { *tag = node; return node->object; } else { *tag = nil; return nil; } } //============================================================================ template T * TSkipList::Head (SkipListTag * tag) const { ASSERT(tag); Node * first = m_head->next[0]; if (first == m_stop) { *tag = nil; return nil; } *tag = first; return first->object; } //============================================================================ template T * TSkipList::Next (SkipListTag * tag) const { ASSERT(tag); Node * node = (Node *)*tag; ASSERT(node); if (node->next[0] == m_stop) { *tag = nil; return nil; } *tag = node->next[0]; return node->next[0]->object; } //============================================================================ template T * TSkipList::Prev (SkipListTag * tag) const { ASSERT(tag); Node * node = (Node *)*tag; ASSERT(node); if (node->prev == m_head) { *tag = nil; return nil; } *tag = node->prev; return node->prev->object; } //============================================================================ template T * TSkipList::Tail (SkipListTag * tag) const { ASSERT(tag); Node * last = m_stop->prev; if (last == m_head) { *tag = nil; return nil; } *tag = last; return last->object; } //============================================================================ template void TSkipList::Link (T * object) { const K * key = (const K *)((const byte *)object + keyOffset); // Find the node's insertion point m_stop->key = key; Node * update[kMaxLevels]; Node * node = m_head; for (int level = (int)m_level; level >= 0; --level) { while (Cmp::Lt(*node->next[level]->key, *key)) node = node->next[level]; update[level] = node; } node = node->next[0]; { // Select a level for the skip node unsigned newLevel = RandomLevel(); if (newLevel > m_level) { if (m_level < kMaxLevels - 1) { newLevel = ++m_level; update[newLevel] = m_head; } else newLevel = m_level; } // Create the node and insert it into the skip list Node * node = AllocNode(newLevel); node->key = key; node->object = object; for (unsigned level = newLevel; level >= 1; --level) { node->next[level] = update[level]->next[level]; update[level]->next[level] = node; } node->prev = update[0]; node->next[0] = update[0]->next[0]; update[0]->next[0]->prev = node; update[0]->next[0] = node; } } //============================================================================ template void TSkipList::Unlink (T * object) { const K * key = (const K *)((const byte *)object + keyOffset); Node * node = m_head; Node * update[kMaxLevels]; int level = m_level; for (;;) { // Find the node being unlinked m_stop->key = key; for (; level >= 0; --level) { while (Cmp::Lt(*node->next[level]->key, *key)) node = node->next[level]; update[level] = node; } node = node->next[0]; // Node wasn't found so do nothing if (*node->key != *key || node == m_stop) return; if (node->object == object) break; } // Update all links for (level = m_level; level >= 1; --level) { if (update[level]->next[level] != node) continue; update[level]->next[level] = node->next[level]; } ASSERT(update[0]->next[0] == node); node->next[0]->prev = update[0]; update[0]->next[0] = node->next[0]; // Update header while (m_level && m_head->next[m_level] == m_stop) --m_level; FreeNode(node); } //============================================================================ template void TSkipList::UnlinkAll () { Node * ptr = m_head->next[0]; while (ptr != m_stop) { Node * next = ptr->next[0]; FreeNode(ptr); ptr = next; } m_stop->prev = m_head; for (unsigned index = 0; index < kMaxLevels; ++index) m_head->next[index] = m_stop; m_level = 0; }