/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined( INCLUDED_CONTAINER_CACHE_H ) #define INCLUDED_CONTAINER_CACHE_H #include #include "container/hashtable.h" #include "memory/allocator.h" template class DefaultCreationPolicy { public: Type* construct( const Parameter& parameter ){ return New().scalar( parameter ); } void destroy( Type* p ){ Delete().scalar( p ); } }; template class SharedValue { typedef Type value_type; typedef value_type* pointer; typedef value_type& reference; std::size_t m_count; pointer m_value; public: SharedValue() : m_count( 0 ), m_value( 0 ){ } ~SharedValue(){ ASSERT_MESSAGE( m_count == 0, "destroying a referenced object\n" ); } void set( pointer value ){ m_value = value; } pointer get(){ return m_value; } std::size_t increment(){ return ++m_count; } std::size_t decrement(){ ASSERT_MESSAGE( !empty(), "destroying a non-existent object\n" ); return --m_count; } std::size_t count(){ return m_count; } bool empty(){ return m_count == 0; } reference operator*() const { ASSERT_NOTNULL( m_value ); return *m_value; } pointer operator->() const { return &( operator*() ); } }; /// \brief Caches values that are uniquely identified by a key. /// /// - Automatically removes objects that are no longer referenced. /// /// \param Key Uniquely identifies each element. /// \param Cached The type to be cached. Must define a constructor that accepts \c Key. /// \param CreationPolicy Must define 'Cached* construct(const Key&)' and 'void destroy(Cached*)'. The lifetime of the \c Key passed to 'construct' is guaranteed to be longer than the subsequent matched call to 'destroy'. template, typename CreationPolicy = DefaultCreationPolicy > class HashedCache : public CreationPolicy { typedef SharedValue Element; typedef HashTable map_type; map_type m_map; public: explicit HashedCache( const CreationPolicy& creation = CreationPolicy() ) : CreationPolicy( creation ), m_map( 256 ){ } ~HashedCache(){ ASSERT_MESSAGE( empty(), "HashedCache::~HashedCache: not empty" ); } typedef typename map_type::iterator iterator; typedef typename map_type::value_type value_type; iterator begin(){ return m_map.begin(); } iterator end(){ return m_map.end(); } bool empty() const { return m_map.empty(); } iterator find( const Key& key ){ return m_map.find( key ); } void capture( iterator i ){ ( *i ).value.increment(); } void release( iterator i ){ if ( ( *i ).value.decrement() == 0 ) { CreationPolicy::destroy( ( *i ).value.get() ); m_map.erase( i ); } } #if 1 Element& capture( const Key& key ){ #if 0 Element& elem = m_map[key]; if ( elem.increment() == 1 ) { elem.set( CreationPolicy::construct( key ) ); } return elem; #else iterator i = m_map.insert( key, Element() ); if ( ( *i ).value.increment() == 1 ) { ( *i ).value.set( CreationPolicy::construct( ( *i ).key ) ); } return ( *i ).value; #endif } #else value_type& capture( const Key& key ){ iterator i = m_map.find( key ); if ( i == m_map.end() ) { i = m_map.insert( key, Element() ); ( *i ).value.set( CreationPolicy::construct( ( *i ).key ) ); } ( *i ).value.increment(); return ( *i ); } #endif void release( const Key& key ){ iterator i = m_map.find( key ); ASSERT_MESSAGE( i != m_map.end(), "releasing a non-existent object\n" ); release( i ); } void clear(){ m_map.clear(); } }; #endif