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 }