nestopia/source/win32/NstCollectionRouter.inl

350 lines
9.1 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia 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 2 of the License, or
// (at your option) any later version.
//
// Nestopia 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 Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
namespace Nestopia
{
namespace Collection
{
template<typename Output,typename Input,typename Key> template<typename Data,typename Code>
Router<Output,Input,Key>::Router(KeyParam key,Data* const data,Code code)
: hooks(NULL)
{
Add( key, Callback(data,code) );
}
template<typename Output,typename Input,typename Key> template<typename Data,typename Array>
Router<Output,Input,Key>::Router(Data* const data,const Array& arr)
: hooks(NULL)
{
Add( data, arr, sizeof(array(arr)) );
}
template<typename Output,typename Input,typename Key>
Router<Output,Input,Key>::~Router()
{
for (Hook* it = hooks; it; )
{
Hook* const him = it;
it = it->next;
delete him;
}
}
template<typename Output,typename Input,typename Key>
Output Router<Output,Input,Key>::Hook::Invoke(Input input)
{
for (uint i=0; i < items.Size(); ++i)
items[i]( input );
return main ? main( input ) : Output(0);
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::Add(KeyParam key,const Callback& callback)
{
bool existing;
Callback& item = items.GetSorted( key, existing ).callback;
if (existing)
{
NST_ASSERT
(
hooks &&
item.template CodePtr<Hook>() == &Hook::Invoke &&
!item.template DataPtr<Hook>()->main
);
item.template DataPtr<Hook>()->main = callback;
}
else
{
item = callback;
}
}
template<typename Output,typename Input,typename Key> template<typename Data>
void Router<Output,Input,Key>::Add(Data* const data,const Entry<Data>* list,const uint count)
{
items.Reserve( items.Size() + count );
for (const Entry<Data>* const end = list + count; list != end; ++list)
Add( list->key, Callback(data,list->function) );
}
template<typename Output,typename Input,typename Key> template<typename Data>
void Router<Output,Input,Key>::HookRouter::Add(Data* const data,const HookEntry<Data>* list,const uint count)
{
for (const HookEntry<Data>* const end = list + count; list != end; ++list)
router.AddHook( list->key, Callback(data,list->function) );
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::Set(KeyParam key,const Callback& callback)
{
if (Item* const item = items.FindSorted( key ))
{
if (item->callback.template CodePtr<Hook>() == &Hook::Invoke)
item->callback.template DataPtr<Hook>()->main = callback;
else
item->callback = callback;
}
else
{
Add( key, callback );
}
}
template<typename Output,typename Input,typename Key> template<typename Data>
void Router<Output,Input,Key>::Set(Data* const data,const Entry<Data>* list,const uint count)
{
for (const Entry<Data>* const end = list + count; list != end; ++list)
Set( list->key, Callback(data,list->function) );
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::Remove(KeyParam key,const Callback& callback)
{
if (Item* const item = items.FindSorted( key ))
{
if (item->callback == callback)
{
items.Erase( item );
}
else if
(
item->callback.template CodePtr<Hook>() == &Hook::Invoke &&
item->callback.template DataPtr<Hook>()->main == callback
)
{
item->callback.template DataPtr<Hook>()->main.Reset();
}
}
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::Remove(const void* const data)
{
for (uint i=0; i < items.Size(); )
{
const Callback& callback = items[i].callback;
if (callback.VoidPtr() == data)
{
items.Erase( items.At(i) );
}
else
{
if
(
callback.template CodePtr<Hook>() == &Hook::Invoke &&
callback.template DataPtr<Hook>()->main.VoidPtr() == data
)
callback.template DataPtr<Hook>()->main.Unset();
++i;
}
}
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::RemoveAll(const void* const data)
{
RemoveHooks( data );
Remove( data );
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::Defrag()
{
items.Defrag();
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::AddHook(KeyParam key,const typename Hook::Item& newItem)
{
Hook* hook;
bool existing;
Callback& callback = items.GetSorted( key, existing ).callback;
if (existing && callback.template CodePtr<Hook>() == &Hook::Invoke)
{
hook = callback.template DataPtr<Hook>();
NST_ASSERT( hook && hook->items.Size() );
}
else
{
NST_ASSERT( !existing || callback );
hook = new Hook;
if (Hook* it = hooks)
{
while (it->next)
it = it->next;
it->next = hook;
}
else
{
hooks = hook;
}
if (existing)
hook->main = callback;
callback.Set( hook, &Hook::Invoke );
}
hook->items.PushBack( newItem );
}
template<typename Output,typename Input,typename Key>
uint Router<Output,Input,Key>::RemoveHook(Item* const mainItem,Hook* const hook,typename Hook::Item* const hookItem)
{
NST_ASSERT( mainItem );
hook->items.Erase( hookItem );
if (hook->items.Size())
return 0;
uint result;
if (hook->main)
{
result = 1;
mainItem->callback = hook->main;
}
else
{
result = 2;
items.Erase( mainItem );
}
if (hooks == hook)
{
hooks = hook->next;
}
else
{
Hook* it = hooks;
while (it->next != hook)
it = it->next;
it->next = hook->next;
}
delete hook;
return result;
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::RemoveHook(KeyParam key,const typename Hook::Item& item)
{
if (Item* const mainItem = items.FindSorted( key ))
{
NST_ASSERT( mainItem->callback.template CodePtr<Hook>() == &Hook::Invoke );
Hook* const hook = mainItem->callback.template DataPtr<Hook>();
NST_ASSERT( hook );
if (typename Hook::Item* const hookItem = hook->items.FindSorted( item ))
RemoveHook( mainItem, hook, hookItem );
}
}
template<typename Output,typename Input,typename Key>
void Router<Output,Input,Key>::RemoveHooks(const void* const data)
{
for (uint i=0; i < items.Size(); ++i)
{
Item& mainItem = items[i];
if (mainItem.callback.template CodePtr<Hook>() == &Hook::Invoke)
{
Hook* const hook = mainItem.callback.template DataPtr<Hook>();
NST_ASSERT( hook );
for (uint j=0; j < hook->items.Size(); )
{
typename Hook::Item& hookItem = hook->items[j];
if (hookItem.VoidPtr() != data)
{
++j;
}
else if (const uint result = RemoveHook( &mainItem, hook, &hookItem ))
{
i -= (result == 2);
break;
}
}
}
}
}
template<typename Output,typename Input,typename Key>
typename Router<Output,Input,Key>::Callback& Router<Output,Input,Key>::operator [] (KeyParam key)
{
Callback& callback = items.AtSorted( key ).callback;
if (callback.template CodePtr<Hook>() == &Hook::Invoke)
return callback.template DataPtr<Hook>()->main;
return callback;
}
template<typename Output,typename Input,typename Key>
typename Router<Output,Input,Key>::Item& Router<Output,Input,Key>::Items::GetSorted(KeyParam key,bool& existing)
{
const uint pos = this->LowerBound( key );
existing = (pos != this->Size() && this->At(pos)->key == key);
if (!existing)
{
this->Insert( this->At(pos), NULL, 1 );
new (static_cast<void*>(this->At(pos))) Item( key );
}
return *this->At(pos);
}
template<typename Output,typename Input,typename Key>
typename Router<Output,Input,Key>::Item* Router<Output,Input,Key>::Items::FindSorted(KeyParam key)
{
const uint pos = this->LowerBound( key );
return pos != this->Size() && this->At(pos)->key == key ? this->At(pos) : NULL;
}
template<typename Output,typename Input,typename Key>
typename Router<Output,Input,Key>::Item& Router<Output,Input,Key>::Items::AtSorted(KeyParam key)
{
const uint pos = this->LowerBound( key );
NST_ASSERT( pos < this->Size() && this->At(pos)->key == key );
return *this->At(pos);
}
}
}