/* * (C) 2014,2015 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #ifndef BOTAN_ALGO_REGISTRY_H__ #define BOTAN_ALGO_REGISTRY_H__ #include #include #include #include #include namespace Botan { size_t static_provider_weight(const std::string& prov_name); template class Algo_Registry { public: typedef typename T::Spec Spec; typedef std::function maker_fn; static Algo_Registry& global_registry() { static Algo_Registry g_registry; return g_registry; } void add(const std::string& name, const std::string& provider, maker_fn fn) { std::unique_lock lock(m_mutex); if(!m_maker_fns[name][provider]) m_maker_fns[name][provider] = fn; } T* make(const std::string& spec_str) { const Spec spec(spec_str); return make(spec_str, ""); } T* make(const Spec& spec, const std::string& provider = "") { maker_fn maker = find_maker(spec, provider); try { return maker(spec); } catch(std::exception& e) { //return nullptr; // ?? throw std::runtime_error("Creating '" + spec.as_string() + "' failed: " + e.what()); } } class Add { public: Add(const std::string& basename, maker_fn fn, const std::string& provider = "builtin") { Algo_Registry::global_registry().add(basename, provider, fn); } Add(bool cond, const std::string& basename, maker_fn fn, const std::string& provider) { if(cond) Algo_Registry::global_registry().add(basename, provider, fn); } }; private: Algo_Registry() {} maker_fn find_maker(const Spec& spec, const std::string& provider) { const std::string basename = spec.algo_name(); std::unique_lock lock(m_mutex); auto providers = m_maker_fns.find(basename); if(providers != m_maker_fns.end() && !providers->second.empty()) { const std::map& prov = providers->second; if(provider != "") { // find one explicit provider requested by user, or fail auto i = prov.find(provider); if(i != prov.end()) return i->second; } else { if(prov.size() == 1) { return prov.begin()->second; } else if(prov.size() > 1) { // TODO choose best of available options (how?) //throw std::runtime_error("multiple choice not implemented"); return prov.begin()->second; } } } // Default result is a function producing a null pointer return [](const Spec&) { return nullptr; }; } std::mutex m_mutex; std::map> m_maker_fns; }; template T* make_a(const typename T::Spec& spec, const std::string provider = "") { return Algo_Registry::global_registry().make(spec, provider); } template T* make_a(const std::string& spec_str, const std::string provider = "") { typename T::Spec spec(spec_str); return make_a(spec, provider); } template T* make_new_T(const typename Algo_Registry::Spec&) { return new T; } template T* make_new_T_1len(const typename Algo_Registry::Spec& spec) { return new T(spec.arg_as_integer(0, DEF_VAL)); } template T* make_new_T_2len(const typename Algo_Registry::Spec& spec) { return new T(spec.arg_as_integer(0, DEF1), spec.arg_as_integer(1, DEF2)); } template T* make_new_T_1str(const typename Algo_Registry::Spec& spec, const std::string& def) { return new T(spec.arg(0, def)); } template T* make_new_T_1str_req(const typename Algo_Registry::Spec& spec) { return new T(spec.arg(0)); } template T* make_new_T_1X(const typename Algo_Registry::Spec& spec) { std::unique_ptr x(Algo_Registry::global_registry().make(spec.arg(0))); if(!x) throw std::runtime_error(spec.arg(0)); return new T(x.release()); } #define BOTAN_REGISTER_NAMED_T(T, namestr, type, maker) \ namespace { Algo_Registry::Add g_ ## type ## _reg(namestr, maker); } #define BOTAN_REGISTER_T(T, name, maker) \ namespace { Algo_Registry::Add g_ ## name ## _reg(#name, maker); } #define BOTAN_REGISTER_T_NOARGS(T, name) \ namespace { Algo_Registry::Add g_ ## name ## _reg(#name, make_new_T); } #define BOTAN_REGISTER_T_1LEN(T, name, def) \ namespace { Algo_Registry::Add g_ ## name ## _reg(#name, make_new_T_1len); } #define BOTAN_REGISTER_NAMED_T_NOARGS(T, type, name, provider) \ namespace { Algo_Registry::Add g_ ## type ## _reg(name, make_new_T, provider); } #define BOTAN_COND_REGISTER_NAMED_T_NOARGS(cond, T, type, name, provider) \ namespace { Algo_Registry::Add g_ ## type ## _reg(cond, name, make_new_T, provider); } #define BOTAN_REGISTER_NAMED_T_2LEN(T, type, name, provider, len1, len2) \ namespace { Algo_Registry::Add g_ ## type ## _reg(name, make_new_T_2len, provider); } // TODO move elsewhere: #define BOTAN_REGISTER_TRANSFORM(name, maker) BOTAN_REGISTER_T(Transform, name, maker) #define BOTAN_REGISTER_TRANSFORM_NOARGS(name) BOTAN_REGISTER_T_NOARGS(Transform, name) } #endif