//////////////////////////////////////////////////////////////////////////////////////// // // 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 template Router::Router(KeyParam key,Data* const data,Code code) : hooks(NULL) { Add( key, Callback(data,code) ); } template template Router::Router(Data* const data,const Array& arr) : hooks(NULL) { Add( data, arr, sizeof(array(arr)) ); } template Router::~Router() { for (Hook* it = hooks; it; ) { Hook* const him = it; it = it->next; delete him; } } template Output Router::Hook::Invoke(Input input) { for (uint i=0; i < items.Size(); ++i) items[i]( input ); return main ? main( input ) : Output(0); } template void Router::Add(KeyParam key,const Callback& callback) { bool existing; Callback& item = items.GetSorted( key, existing ).callback; if (existing) { NST_ASSERT ( hooks && item.template CodePtr() == &Hook::Invoke && !item.template DataPtr()->main ); item.template DataPtr()->main = callback; } else { item = callback; } } template template void Router::Add(Data* const data,const Entry* list,const uint count) { items.Reserve( items.Size() + count ); for (const Entry* const end = list + count; list != end; ++list) Add( list->key, Callback(data,list->function) ); } template template void Router::HookRouter::Add(Data* const data,const HookEntry* list,const uint count) { for (const HookEntry* const end = list + count; list != end; ++list) router.AddHook( list->key, Callback(data,list->function) ); } template void Router::Set(KeyParam key,const Callback& callback) { if (Item* const item = items.FindSorted( key )) { if (item->callback.template CodePtr() == &Hook::Invoke) item->callback.template DataPtr()->main = callback; else item->callback = callback; } else { Add( key, callback ); } } template template void Router::Set(Data* const data,const Entry* list,const uint count) { for (const Entry* const end = list + count; list != end; ++list) Set( list->key, Callback(data,list->function) ); } template void Router::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::Invoke && item->callback.template DataPtr()->main == callback ) { item->callback.template DataPtr()->main.Reset(); } } } template void Router::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::Invoke && callback.template DataPtr()->main.VoidPtr() == data ) callback.template DataPtr()->main.Unset(); ++i; } } } template void Router::RemoveAll(const void* const data) { RemoveHooks( data ); Remove( data ); } template void Router::Defrag() { items.Defrag(); } template void Router::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::Invoke) { hook = callback.template DataPtr(); 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 uint Router::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 void Router::RemoveHook(KeyParam key,const typename Hook::Item& item) { if (Item* const mainItem = items.FindSorted( key )) { NST_ASSERT( mainItem->callback.template CodePtr() == &Hook::Invoke ); Hook* const hook = mainItem->callback.template DataPtr(); NST_ASSERT( hook ); if (typename Hook::Item* const hookItem = hook->items.FindSorted( item )) RemoveHook( mainItem, hook, hookItem ); } } template void Router::RemoveHooks(const void* const data) { for (uint i=0; i < items.Size(); ++i) { Item& mainItem = items[i]; if (mainItem.callback.template CodePtr() == &Hook::Invoke) { Hook* const hook = mainItem.callback.template DataPtr(); 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 Router::Callback& Router::operator [] (KeyParam key) { Callback& callback = items.AtSorted( key ).callback; if (callback.template CodePtr() == &Hook::Invoke) return callback.template DataPtr()->main; return callback; } template typename Router::Item& Router::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(this->At(pos))) Item( key ); } return *this->At(pos); } template typename Router::Item* Router::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 Router::Item& Router::Items::AtSorted(KeyParam key) { const uint pos = this->LowerBound( key ); NST_ASSERT( pos < this->Size() && this->At(pos)->key == key ); return *this->At(pos); } } }