diff options
Diffstat (limited to 'src/lib/base/algo_registry.h')
-rw-r--r-- | src/lib/base/algo_registry.h | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/src/lib/base/algo_registry.h b/src/lib/base/algo_registry.h new file mode 100644 index 000000000..9582180bd --- /dev/null +++ b/src/lib/base/algo_registry.h @@ -0,0 +1,215 @@ +/* +* (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 <botan/types.h> +#include <functional> +#include <stdexcept> +#include <mutex> +#include <vector> +#include <map> +#include <unordered_map> + +namespace Botan { + +template<typename T> +class Algo_Registry + { + public: + typedef typename T::Spec Spec; + + typedef std::function<T* (const Spec&)> maker_fn; + + static Algo_Registry<T>& global_registry() + { + static Algo_Registry<T> g_registry; + return g_registry; + } + + void add(const std::string& name, const std::string& provider, maker_fn fn, byte pref) + { + std::unique_lock<std::mutex> lock(m_mutex); + m_algo_info[name].add_provider(provider, fn, pref); + } + + std::vector<std::string> providers_of(const Spec& spec) + { + std::unique_lock<std::mutex> lock(m_mutex); + auto i = m_algo_info.find(spec.algo_name()); + if(i != m_algo_info.end()) + return i->second.providers(); + return std::vector<std::string>(); + } + + T* make(const Spec& spec, const std::string& provider = "") + { + maker_fn maker = find_maker(spec, provider); + + try + { + return maker(spec); + } + catch(std::exception& e) + { + 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", byte pref = 128) + { + Algo_Registry<T>::global_registry().add(basename, provider, fn, pref); + } + + Add(bool cond, const std::string& basename, maker_fn fn, const std::string& provider, byte pref) + { + if(cond) + Algo_Registry<T>::global_registry().add(basename, provider, fn, pref); + } + }; + + private: + Algo_Registry() {} + + maker_fn find_maker(const Spec& spec, const std::string& provider) + { + std::unique_lock<std::mutex> lock(m_mutex); + return m_algo_info[spec.algo_name()].get_maker(provider); + } + + struct Algo_Info + { + public: + void add_provider(const std::string& provider, maker_fn fn, byte pref = 128) + { + if(m_maker_fns.count(provider) > 0) + throw std::runtime_error("Duplicated registration of '" + provider + "'"); + + m_maker_fns[provider] = std::make_pair(pref, fn); + } + + std::vector<std::string> providers() const + { + std::vector<std::string> v; + for(auto&& k : m_maker_fns) + v.push_back(k.first); + return v; + } + + void set_pref(const std::string& provider, byte val) + { + m_maker_fns[provider].first = val; + } + + maker_fn get_maker(const std::string& req_provider) + { + maker_fn null_result = [](const Spec&) { return nullptr; }; + + if(req_provider != "") + { + // find one explicit provider requested by user or fail + auto i = m_maker_fns.find(req_provider); + if(i != m_maker_fns.end()) + return i->second.second; + return null_result; + } + + size_t pref = 255; + maker_fn result = null_result; + + for(auto&& i : m_maker_fns) + { + if(i.second.first < pref) + { + pref = i.second.first; + result = i.second.second; + } + } + + return result; + } + private: + std::unordered_map<std::string, std::pair<byte, maker_fn>> m_maker_fns; // provider -> (pref, creator fn) + }; + + std::mutex m_mutex; + std::unordered_map<std::string, Algo_Info> m_algo_info; + }; + +template<typename T> T* +make_a(const typename T::Spec& spec, const std::string provider = "") + { + return Algo_Registry<T>::global_registry().make(spec, provider); + } + +template<typename T> std::vector<std::string> providers_of(const typename T::Spec& spec) + { + return Algo_Registry<T>::global_registry().providers_of(spec); + } + +template<typename T> T* +make_new_T(const typename Algo_Registry<T>::Spec&) { return new T; } + +template<typename T, size_t DEF_VAL> T* +make_new_T_1len(const typename Algo_Registry<T>::Spec& spec) + { + return new T(spec.arg_as_integer(0, DEF_VAL)); + } + +template<typename T, size_t DEF1, size_t DEF2> T* +make_new_T_2len(const typename Algo_Registry<T>::Spec& spec) + { + return new T(spec.arg_as_integer(0, DEF1), spec.arg_as_integer(1, DEF2)); + } + +template<typename T> T* +make_new_T_1str(const typename Algo_Registry<T>::Spec& spec, const std::string& def) + { + return new T(spec.arg(0, def)); + } + +template<typename T> T* +make_new_T_1str_req(const typename Algo_Registry<T>::Spec& spec) + { + return new T(spec.arg(0)); + } + +template<typename T, typename X> T* +make_new_T_1X(const typename Algo_Registry<T>::Spec& spec) + { + std::unique_ptr<X> x(Algo_Registry<X>::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<T>::Add g_ ## type ## _reg(namestr, maker); } +#define BOTAN_REGISTER_T(T, name, maker) \ + namespace { Algo_Registry<T>::Add g_ ## name ## _reg(#name, maker); } +#define BOTAN_REGISTER_T_NOARGS(T, name) \ + namespace { Algo_Registry<T>::Add g_ ## name ## _reg(#name, make_new_T<name>); } +#define BOTAN_REGISTER_T_1LEN(T, name, def) \ + namespace { Algo_Registry<T>::Add g_ ## name ## _reg(#name, make_new_T_1len<name, def>); } + +#define BOTAN_REGISTER_NAMED_T_NOARGS(T, type, name, provider) \ + namespace { Algo_Registry<T>::Add g_ ## type ## _reg(name, make_new_T<type>, provider); } +#define BOTAN_COND_REGISTER_NAMED_T_NOARGS(cond, T, type, name, provider, pref) \ + namespace { Algo_Registry<T>::Add g_ ## type ## _reg(cond, name, make_new_T<type>, provider, pref); } +#define BOTAN_REGISTER_NAMED_T_2LEN(T, type, name, provider, len1, len2) \ + namespace { Algo_Registry<T>::Add g_ ## type ## _reg(name, make_new_T_2len<type, len1, len2>, 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 |