/// \file /// \brief \b [Internal] Ordered Channel Heap . This is a heap where you add to it on multiple ordered channels, with each channel having a different weight. /// /// This file is part of RakNet Copyright 2003 Kevin Jenkins. /// /// Usage of RakNet is subject to the appropriate license agreement. /// Creative Commons Licensees are subject to the /// license found at /// http://creativecommons.org/licenses/by-nc/2.5/ /// Single application licensees are subject to the license found at /// http://www.rakkarsoft.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free /// Software Foundation; either version 2 of the License, or (at your /// option) any later version. #ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H #define __RAKNET_ORDERED_CHANNEL_HEAP_H #include "DS_Heap.h" #include "DS_Map.h" #include "DS_Queue.h" #include "Export.h" #include #include "Rand.h" /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. namespace DataStructures { template > class RAK_DLL_EXPORT OrderedChannelHeap { public: static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(channel_key_type(),channel_key_type());} OrderedChannelHeap(); ~OrderedChannelHeap(); void Push(const channel_key_type &channelID, const heap_data_type &data); void PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data); heap_data_type Pop(const unsigned startingIndex=0); heap_data_type Peek(const unsigned startingIndex) const; void AddChannel(const channel_key_type &channelID, const double weight); void RemoveChannel(channel_key_type channelID); void Clear(void); heap_data_type& operator[] ( const unsigned int position ) const; unsigned ChannelSize(const channel_key_type &channelID); unsigned Size(void) const; struct QueueAndWeight { DataStructures::Queue randResultQueue; double weight; bool signalDeletion; }; struct HeapChannelAndData { HeapChannelAndData() {} HeapChannelAndData(const channel_key_type &_channel, const heap_data_type &_data) : data(_data), channel(_channel) {} heap_data_type data; channel_key_type channel; }; protected: DataStructures::Map map; DataStructures::Heap heap; void GreatestRandResult(void); }; template OrderedChannelHeap::OrderedChannelHeap() { } template OrderedChannelHeap::~OrderedChannelHeap() { Clear(); } template void OrderedChannelHeap::Push(const channel_key_type &channelID, const heap_data_type &data) { PushAtHead(MAX_UNSIGNED_LONG, channelID, data); } template void OrderedChannelHeap::GreatestRandResult(void) { double greatest; unsigned i; greatest=0.0; for (i=0; i < map.Size(); i++) { if (map[i]->randResultQueue.Size() && map[i]->randResultQueue[0]>greatest) greatest=map[i]->randResultQueue[0]; } return greatest; } template void OrderedChannelHeap::PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data) { // If an assert hits here then this is an unknown channel. Call AddChannel first. QueueAndWeight *queueAndWeight=map.Get(channelID); double maxRange, minRange, rnd; if (queueAndWeight->randResultQueue.Size()==0) { // Set maxRange to the greatest random number waiting to be returned, rather than 1.0 necessarily // This is so weights are scaled similarly among channels. For example, if the head weight for a used channel was .25 // and then we added another channel, the new channel would need to choose between .25 and 0 // If we chose between 1.0 and 0, it would be 1/.25 (4x) more likely to be at the head of the heap than it should be maxRange=GreatestRandResult(); if (maxRange==0.0) maxRange=1.0; minRange=0.0; } else if (index >= queueAndWeight->randResultQueue.Size()) { maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; minRange=0.0; } else { if (index==0) { maxRange=GreatestRandResult(); if (maxRange==queueAndWeight->randResultQueue[0]) maxRange=1.0; } else if (index >= queueAndWeight->randResultQueue.Size()) maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; else maxRange=queueAndWeight->randResultQueue[index-1]*.99999999; minRange=maxRange=queueAndWeight->randResultQueue[index]*1.00000001; } #ifdef _DEBUG assert(maxRange!=0.0); #endif rnd=frandomMT() * (maxRange - minRange); if (rnd==0.0) rnd=maxRange/2.0; if (index >= queueAndWeight->randResultQueue.Size()) queueAndWeight->randResultQueue.Push(rnd); else queueAndWeight->randResultQueue.PushAtHead(rnd, index); heap.Push(rnd*queueAndWeight->weight, HeapChannelAndData(channelID, data)); } template heap_data_type OrderedChannelHeap::Pop(const unsigned startingIndex) { assert(startingIndex < heap.Size()); QueueAndWeight *queueAndWeight=map.Get(heap[startingIndex].channel); if (startingIndex!=0) { // Ugly - have to count in the heap how many nodes have the same channel, so we know where to delete from in the queue unsigned indiceCount=0; unsigned i; for (i=0; i < startingIndex; i++) if (channel_key_comparison_func(heap[i].channel,heap[startingIndex].channel)==0) indiceCount++; queueAndWeight->randResultQueue.Del(indiceCount); } else { // TODO - ordered channel heap uses progressively lower values as items are inserted. But this won't give relative ordering among channels. I have to renormalize after every pop. queueAndWeight->randResultQueue.Pop(); } // Try to remove the channel after every pop, because doing so is not valid while there are elements in the list. if (queueAndWeight->signalDeletion) RemoveChannel(heap[startingIndex].channel); return heap.Pop(startingIndex).data; } template heap_data_type OrderedChannelHeap::Peek(const unsigned startingIndex) const { HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex); return heapChannelAndData.data; } template void OrderedChannelHeap::AddChannel(const channel_key_type &channelID, const double weight) { QueueAndWeight *qaw = new QueueAndWeight; qaw->weight=weight; qaw->signalDeletion=false; map.SetNew(channelID, qaw); } template void OrderedChannelHeap::RemoveChannel(channel_key_type channelID) { if (map.Has(channelID)) { unsigned i; i=map.GetIndexAtKey(channelID); if (map[i]->randResultQueue.Size()==0) { delete map[i]; map.RemoveAtIndex(i); } else { // Signal this channel for deletion later, because the heap has nodes with this channel right now map[i]->signalDeletion=true; } } } template unsigned OrderedChannelHeap::Size(void) const { return heap.Size(); } template heap_data_type& OrderedChannelHeap::operator[]( const unsigned int position ) const { return heap[position].data; } template unsigned OrderedChannelHeap::ChannelSize(const channel_key_type &channelID) { QueueAndWeight *queueAndWeight=map.Get(channelID); return queueAndWeight->randResultQueue.Size(); } template void OrderedChannelHeap::Clear(void) { unsigned i; for (i=0; i < map.Size(); i++) delete map[i]; map.Clear(); heap.Clear(); } } #endif