/* * An algorithm cache (used by Algorithm_Factory) * (C) 2008-2009 Jack Lloyd * * Distributed under the terms of the Botan license */ #ifndef BOTAN_ALGORITHM_CACHE_TEMPLATE_H__ #define BOTAN_ALGORITHM_CACHE_TEMPLATE_H__ #include #include #include #include #include #include namespace Botan { /** * @param prov_name a provider name * @return weight for this provider */ u32bit static_provider_weight(const std::string& prov_name); /** * Algorithm_Cache (used by Algorithm_Factory) */ template class Algorithm_Cache { public: const T* get(const std::string& algo_spec, const std::string& pref_provider); /** * Add a new algorithm implementation to the cache */ void add(T* algo, const std::string& requested_name, const std::string& provider_name); /** * Set the preferred provider */ void set_preferred_provider(const std::string& algo_spec, const std::string& provider); /** * Return the list of providers of this algorithm */ std::vector providers_of(const std::string& algo_name); ~Algorithm_Cache(); private: typename std::map >::const_iterator find_algorithm(const std::string& algo_spec); std::mutex mutex; std::map aliases; std::map pref_providers; std::map > algorithms; }; /** * Look for an algorithm implementation in the cache, also checking aliases * Assumes object lock is held */ template typename std::map >::const_iterator Algorithm_Cache::find_algorithm(const std::string& algo_spec) { auto algo = algorithms.find(algo_spec); // Not found? Check if a known alias if(algo == algorithms.end()) { auto alias = aliases.find(algo_spec); if(alias != aliases.end()) algo = algorithms.find(alias->second); } return algo; } /** * Look for an algorithm implementation by a particular provider */ template const T* Algorithm_Cache::get(const std::string& algo_spec, const std::string& requested_provider) { std::lock_guard lock(mutex); auto algo = find_algorithm(algo_spec); if(algo == algorithms.end()) // algo not found at all (no providers) return 0; // If a provider is requested specifically, return it or fail entirely if(requested_provider != "") { auto prov = algo->second.find(requested_provider); if(prov != algo->second.end()) return prov->second; return 0; } const T* prototype = 0; std::string prototype_provider; u32bit prototype_prov_weight = 0; const std::string pref_provider = search_map(pref_providers, algo_spec); for(auto i = algo->second.begin(); i != algo->second.end(); ++i) { const std::string prov_name = i->first; const u32bit prov_weight = static_provider_weight(prov_name); // preferred prov exists, return immediately if(prov_name == pref_provider) return i->second; if(prototype == 0 || prov_weight > prototype_prov_weight) { prototype = i->second; prototype_provider = i->first; prototype_prov_weight = prov_weight; } } return prototype; } /** * Add an implementation to the cache */ template void Algorithm_Cache::add(T* algo, const std::string& requested_name, const std::string& provider) { if(!algo) return; std::lock_guard lock(mutex); delete algorithms[algo->name()][provider]; algorithms[algo->name()][provider] = algo; if(algo->name() != requested_name && aliases.find(requested_name) == aliases.end()) { aliases[requested_name] = algo->name(); } } /** * Find the providers of this algo (if any) */ template std::vector Algorithm_Cache::providers_of(const std::string& algo_name) { std::lock_guard lock(mutex); std::vector providers; auto algo = find_algorithm(algo_name); if(algo != algorithms.end()) { auto provider = algo->second.begin(); while(provider != algo->second.end()) { providers.push_back(provider->first); ++provider; } } return providers; } /** * Set the preferred provider for an algorithm */ template void Algorithm_Cache::set_preferred_provider(const std::string& algo_spec, const std::string& provider) { std::lock_guard lock(mutex); pref_providers[algo_spec] = provider; } /** * Algorithm_Cache Destructor */ template Algorithm_Cache::~Algorithm_Cache() { auto algo = algorithms.begin(); while(algo != algorithms.end()) { auto provider = algo->second.begin(); while(provider != algo->second.end()) { delete provider->second; ++provider; } ++algo; } } } #endif