1 /**
2 * An algorithm cache (used by AlgorithmFactory)
3 * 
4 * Copyright:
5 * (C) 2008-2009,2011 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.algo_factory.algo_cache;
12 
13 import botan.constants;
14 import botan.utils.types;
15 import memutils.hashmap;
16 /**
17 * Params:
18 *  prov_name = a provider name
19 * 
20 * Returns: weight for this provider
21 */
22 ubyte staticProviderWeight(in string prov_name)
23 {
24     /*
25     * Prefer asm over D, but prefer anything over OpenSSL or GNU MP; to use
26     * them, set the provider explicitly for the algorithms you want
27     */    
28     if (prov_name == "aes_isa") return 8;
29     if (prov_name == "simd") return 7;
30     if (prov_name == "asm") return 6;
31     
32     if (prov_name == "core") return 5;
33     
34     if (prov_name == "openssl") return 9;
35     if (prov_name == "gmp") return 1;
36     
37     return 0; // other/unknown
38 }
39 
40 
41 /**
42 * AlgorithmCache (used by AlgorithmFactory)
43 */
44 final class AlgorithmCache(T)
45 {
46 public:
47     /**
48     * Look for an algorithm implementation by a particular provider
49     * 
50     * Params:
51     *  algo_spec = names the requested algorithm
52     *  requested_provider = suggests a preferred provider
53     * 
54     * Returns: prototype object, or NULL
55     */
56     const(T) get(string algo_spec, string requested_provider) const
57     {
58         auto algo_providers = findAlgorithm(algo_spec);
59         // logTrace("Searching ", algo_spec, " in algo length: ", m_algorithms.length);
60         if (algo_providers.length == 0) // algo not found at all (no providers)
61             return null;
62         
63         // If a provider is requested specifically, return it or fail entirely
64         if (requested_provider != "")
65         {
66             return get(algo_providers, requested_provider).instance;
67         }
68 
69         T prototype = null;
70         string prototype_provider;
71         size_t prototype_prov_weight = 0;
72         
73         const string pref_provider = m_pref_providers.get(algo_spec);
74         auto prov_match = get(algo_providers, pref_provider);
75         if (prov_match.instance)
76             return prov_match.instance;
77 
78         foreach(ref CachedAlgorithm ca; *algo_providers) 
79         {
80             const ubyte prov_weight = staticProviderWeight(ca.provider);
81             
82             if (prototype is null || prov_weight > prototype_prov_weight)
83             {
84                 prototype = cast(T)(ca.instance);
85                 prototype_provider = ca.provider;
86                 prototype_prov_weight = prov_weight;
87             }
88         }
89 
90         // logTrace("Returning provider: ", prototype_provider);
91         return cast(const)prototype;
92     }
93 
94     /**
95     * Add a new algorithm implementation to the cache
96     * 
97     * Params:
98     *  algo = the algorithm prototype object
99     *  requested_name = how this name will be requested
100     *  provider = the name of the provider of this prototype
101     */
102     void add(T algo,
103              in string requested_name,
104              in string provider)
105     {
106         //logTrace("Start adding ", requested_name, " provider ", provider);
107         if (!algo) {
108             logError("Tried adding null algorithm");
109             return;
110         }
111 
112         // Add alias if not exists
113         if (algo.name != requested_name && m_aliases.get(requested_name) == null)
114         {
115             m_aliases[requested_name] = algo.name;
116         }
117 
118         // default init
119         {
120 	        Algorithm algo_cache = get(m_algorithms, algo.name);
121 	        if (!algo_cache.name) 
122 	        { 
123 	        	m_algorithms ~= Algorithm(algo.name, Array!CachedAlgorithm());
124 	        }
125 	    }
126 	    // add if not exists
127 	    {
128 	        Algorithm algo_cache = get(m_algorithms, algo.name);
129 	        if (get(algo_cache.providers, provider).instance is null) {
130 	        	algo_cache.providers ~= CachedAlgorithm(provider, algo);
131 			} else algo.destroy();
132 		}
133     }
134 
135 
136     /**
137     * Set the preferred provider for an algorithm
138     * 
139     * Params:
140     *  algo_spec = names the algorithm
141     *  provider = names the preferred provider
142     */
143     void setPreferredProvider(in string algo_spec,
144                               in string provider)
145     {        
146         m_pref_providers[algo_spec] = provider;
147     }
148 
149     /**
150     * Find the providers of this algo (if any)
151     * 
152     * Params:
153     *  algo_name = names the algorithm
154     * 
155     * Returns: list of providers of this algorithm
156     */
157     Vector!string providersOf(in string algo_name)
158     {
159         Vector!string providers;
160         string algo = m_aliases.get(algo_name);
161 		Algorithm algo_cache;
162 		{
163 			algo_cache = get(m_algorithms, algo);
164 	        if (algo_cache.providers.length == 0)
165 	            algo = algo_name;
166 		}
167 		{
168 			algo_cache = get(m_algorithms, algo);
169 	        if (get(m_algorithms, algo).providers.length == 0) {
170 	            return Vector!string();
171 	        }
172 		}
173 		auto arr = algo_cache.providers;
174         foreach(ref CachedAlgorithm ca; *arr) {
175             providers.pushBack(ca.provider);
176         }
177         return providers.move();
178     }
179 
180     /**
181     * Clear the cache
182     */
183     void clearCache()
184     {
185         foreach (ref Algorithm algo; m_algorithms)
186         {
187         	auto providers = algo.providers;
188             foreach (ref CachedAlgorithm ca; *providers) {
189                 if (ca.instance) destroy(ca.instance);
190             }
191         }
192 
193         m_algorithms.destroy();
194     }
195 
196     ~this() { clearCache(); }
197 private:
198 
199     /*
200     * Look for an algorithm implementation in the cache, also checking aliases
201     * Assumes object lock is held
202     */
203     Array!CachedAlgorithm findAlgorithm(in string algo_spec) const
204     {
205         Algorithm algo = get(m_algorithms, algo_spec);
206         // Not found? Check if a known alias
207         if (!algo.name)
208         {
209             string _alias = m_aliases.get(algo_spec);
210 
211             if (_alias) {
212                 return get(m_algorithms, _alias).providers;
213             }
214             else {
215                 return Array!CachedAlgorithm();
216             }
217         }
218         return algo.providers;
219     }
220 
221     HashMap!(string, string) m_aliases;
222     HashMap!(string, string) m_pref_providers;
223 
224     Vector!Algorithm m_algorithms;
225 
226 private:
227     struct Algorithm {
228     	string name;
229     	Array!CachedAlgorithm providers;
230     }
231 
232 	struct CachedAlgorithm {
233 		string provider;
234 		T instance;
235 	}
236 
237 	CachedAlgorithm get(Array!CachedAlgorithm arr, string provider) const {
238 		foreach (ref CachedAlgorithm ca; *arr) {
239 			if (provider == ca.provider)
240 				return ca;
241 		}
242 		return CachedAlgorithm.init;
243 	}
244 
245 	Algorithm get(inout ref Vector!Algorithm arr, inout string name) const {
246 		foreach (ref Algorithm algo; arr) {
247 			if (name == algo.name)
248 				return algo;
249 		}
250 		return Algorithm.init;
251 	}
252 
253 }
254 
255 shared(int) threads;
256 static this() {
257     import core.atomic;
258     atomicOp!"+="(threads, 1);
259     logTrace("Starting, Threads: ", atomicLoad(threads));
260 }
261 
262 static ~this() {
263     import core.atomic;
264     atomicOp!"-="(threads, 1);
265     logTrace("Closing, Threads: ", atomicLoad(threads));
266 }