/src/rocksdb/utilities/object_registry.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. |
2 | | // This source code is licensed under both the GPLv2 (found in the |
3 | | // COPYING file in the root directory) and Apache 2.0 License |
4 | | // (found in the LICENSE.Apache file in the root directory). |
5 | | |
6 | | #include "rocksdb/utilities/object_registry.h" |
7 | | |
8 | | #include <cctype> |
9 | | |
10 | | #include "logging/logging.h" |
11 | | #include "port/lang.h" |
12 | | #include "rocksdb/customizable.h" |
13 | | #include "rocksdb/env.h" |
14 | | #include "util/string_util.h" |
15 | | |
16 | | namespace ROCKSDB_NAMESPACE { |
17 | | namespace { |
18 | 0 | bool MatchesInteger(const std::string &target, size_t start, size_t pos) { |
19 | | // If it is numeric, everything up to the match must be a number |
20 | 0 | int digits = 0; |
21 | 0 | if (target[start] == '-') { |
22 | 0 | start++; // Allow negative numbers |
23 | 0 | } |
24 | 0 | while (start < pos) { |
25 | 0 | if (!isdigit(target[start++])) { |
26 | 0 | return false; |
27 | 0 | } else { |
28 | 0 | digits++; |
29 | 0 | } |
30 | 0 | } |
31 | 0 | return (digits > 0); |
32 | 0 | } |
33 | | |
34 | 0 | bool MatchesDecimal(const std::string &target, size_t start, size_t pos) { |
35 | 0 | int digits = 0; |
36 | 0 | if (target[start] == '-') { |
37 | 0 | start++; // Allow negative numbers |
38 | 0 | } |
39 | 0 | for (bool point = false; start < pos; start++) { |
40 | 0 | if (target[start] == '.') { |
41 | 0 | if (point) { |
42 | 0 | return false; |
43 | 0 | } else { |
44 | 0 | point = true; |
45 | 0 | } |
46 | 0 | } else if (!isdigit(target[start])) { |
47 | 0 | return false; |
48 | 0 | } else { |
49 | 0 | digits++; |
50 | 0 | } |
51 | 0 | } |
52 | 0 | return (digits > 0); |
53 | 0 | } |
54 | | } // namespace |
55 | | |
56 | | size_t ObjectLibrary::PatternEntry::MatchSeparatorAt( |
57 | | size_t start, Quantifier mode, const std::string &target, size_t tlen, |
58 | 0 | const std::string &separator) const { |
59 | 0 | size_t slen = separator.size(); |
60 | | // See if there is enough space. If so, find the separator |
61 | 0 | if (tlen < start + slen) { |
62 | 0 | return std::string::npos; // not enough space left |
63 | 0 | } else if (mode == kMatchExact) { |
64 | | // Exact mode means the next thing we are looking for is the separator |
65 | 0 | if (target.compare(start, slen, separator) != 0) { |
66 | 0 | return std::string::npos; |
67 | 0 | } else { |
68 | 0 | return start + slen; // Found the separator, return where we found it |
69 | 0 | } |
70 | 0 | } else { |
71 | 0 | auto pos = start + 1; |
72 | 0 | if (!separator.empty()) { |
73 | 0 | pos = target.find(separator, pos); |
74 | 0 | } |
75 | 0 | if (pos == std::string::npos) { |
76 | 0 | return pos; |
77 | 0 | } else if (mode == kMatchInteger) { |
78 | 0 | if (!MatchesInteger(target, start, pos)) { |
79 | 0 | return std::string::npos; |
80 | 0 | } |
81 | 0 | } else if (mode == kMatchDecimal) { |
82 | 0 | if (!MatchesDecimal(target, start, pos)) { |
83 | 0 | return std::string::npos; |
84 | 0 | } |
85 | 0 | } |
86 | 0 | return pos + slen; |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | bool ObjectLibrary::PatternEntry::MatchesTarget(const std::string &name, |
91 | | size_t nlen, |
92 | | const std::string &target, |
93 | 265k | size_t tlen) const { |
94 | 265k | if (separators_.empty()) { |
95 | 151k | assert(optional_); // If there are no separators, it must be only a name |
96 | 151k | return nlen == tlen && name == target; |
97 | 151k | } else if (nlen == tlen) { // The lengths are the same |
98 | 37.8k | return optional_ && name == target; |
99 | 75.7k | } else if (tlen < nlen + slength_) { |
100 | | // The target is not long enough |
101 | 37.8k | return false; |
102 | 37.8k | } else if (target.compare(0, nlen, name) != 0) { |
103 | 37.8k | return false; // Target does not start with name |
104 | 37.8k | } else { |
105 | | // Loop through all of the separators one at a time matching them. |
106 | | // Note that we first match the separator and then its quantifiers. |
107 | | // Since we expect the separator first, we start with an exact match |
108 | | // Subsequent matches will use the quantifier of the previous separator |
109 | 0 | size_t start = nlen; |
110 | 0 | auto mode = kMatchExact; |
111 | 0 | for (size_t idx = 0; idx < separators_.size(); ++idx) { |
112 | 0 | const auto &separator = separators_[idx]; |
113 | 0 | start = MatchSeparatorAt(start, mode, target, tlen, separator.first); |
114 | 0 | if (start == std::string::npos) { |
115 | 0 | return false; |
116 | 0 | } else { |
117 | 0 | mode = separator.second; |
118 | 0 | } |
119 | 0 | } |
120 | | // We have matched all of the separators. Now check that what is left |
121 | | // unmatched in the target is acceptable. |
122 | 0 | if (mode == kMatchExact) { |
123 | 0 | return (start == tlen); |
124 | 0 | } else if (start > tlen || (start == tlen && mode != kMatchZeroOrMore)) { |
125 | 0 | return false; |
126 | 0 | } else if (mode == kMatchInteger) { |
127 | 0 | return MatchesInteger(target, start, tlen); |
128 | 0 | } else if (mode == kMatchDecimal) { |
129 | 0 | return MatchesDecimal(target, start, tlen); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | return true; |
133 | 265k | } |
134 | | |
135 | 227k | bool ObjectLibrary::PatternEntry::Matches(const std::string &target) const { |
136 | 227k | auto tlen = target.size(); |
137 | 227k | if (MatchesTarget(name_, nlength_, target, tlen)) { |
138 | 189k | return true; |
139 | 189k | } else if (!names_.empty()) { |
140 | 37.8k | for (const auto &alt : names_) { |
141 | 37.8k | if (MatchesTarget(alt, alt.size(), target, tlen)) { |
142 | 0 | return true; |
143 | 0 | } |
144 | 37.8k | } |
145 | 37.8k | } |
146 | 37.8k | return false; |
147 | 227k | } |
148 | | |
149 | 3 | size_t ObjectLibrary::GetFactoryCount(size_t *types) const { |
150 | 3 | std::unique_lock<std::mutex> lock(mu_); |
151 | 3 | *types = factories_.size(); |
152 | 3 | size_t factories = 0; |
153 | 21 | for (const auto &e : factories_) { |
154 | 21 | factories += e.second.size(); |
155 | 21 | } |
156 | 3 | return factories; |
157 | 3 | } |
158 | | |
159 | 0 | size_t ObjectLibrary::GetFactoryCount(const std::string &type) const { |
160 | 0 | std::unique_lock<std::mutex> lock(mu_); |
161 | 0 | auto iter = factories_.find(type); |
162 | 0 | if (iter != factories_.end()) { |
163 | 0 | return iter->second.size(); |
164 | 0 | } else { |
165 | 0 | return 0; |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | void ObjectLibrary::GetFactoryNames(const std::string &type, |
170 | 0 | std::vector<std::string> *names) const { |
171 | 0 | assert(names); |
172 | 0 | std::unique_lock<std::mutex> lock(mu_); |
173 | 0 | auto iter = factories_.find(type); |
174 | 0 | if (iter != factories_.end()) { |
175 | 0 | for (const auto &f : iter->second) { |
176 | 0 | names->push_back(f->Name()); |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | void ObjectLibrary::GetFactoryTypes( |
182 | 0 | std::unordered_set<std::string> *types) const { |
183 | 0 | assert(types); |
184 | 0 | std::unique_lock<std::mutex> lock(mu_); |
185 | 0 | for (const auto &iter : factories_) { |
186 | 0 | types->insert(iter.first); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | 0 | void ObjectLibrary::Dump(Logger *logger) const { |
191 | 0 | std::unique_lock<std::mutex> lock(mu_); |
192 | 0 | if (logger != nullptr && !factories_.empty()) { |
193 | 0 | ROCKS_LOG_HEADER(logger, " Registered Library: %s\n", id_.c_str()); |
194 | 0 | for (const auto &iter : factories_) { |
195 | 0 | ROCKS_LOG_HEADER(logger, " Registered factories for type[%s] ", |
196 | 0 | iter.first.c_str()); |
197 | 0 | bool printed_one = false; |
198 | 0 | for (const auto &e : iter.second) { |
199 | 0 | ROCKS_LOG_HEADER(logger, "%c %s", (printed_one) ? ',' : ':', e->Name()); |
200 | 0 | printed_one = true; |
201 | 0 | } |
202 | 0 | } |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | // Returns the Default singleton instance of the ObjectLibrary |
207 | | // This instance will contain most of the "standard" registered objects |
208 | 12 | std::shared_ptr<ObjectLibrary> &ObjectLibrary::Default() { |
209 | | // Use avoid destruction here so the default ObjectLibrary will not be |
210 | | // statically destroyed and long-lived. |
211 | 12 | STATIC_AVOID_DESTRUCTION(std::shared_ptr<ObjectLibrary>, instance) |
212 | 12 | (std::make_shared<ObjectLibrary>("default")); |
213 | 12 | return instance; |
214 | 12 | } |
215 | | |
216 | 1 | ObjectRegistry::ObjectRegistry(const std::shared_ptr<ObjectLibrary> &library) { |
217 | 1 | libraries_.push_back(library); |
218 | 1 | for (const auto &b : builtins_) { |
219 | 0 | RegisterPlugin(b.first, b.second); |
220 | 0 | } |
221 | 1 | } |
222 | | |
223 | 52.2k | std::shared_ptr<ObjectRegistry> ObjectRegistry::Default() { |
224 | | // Use avoid destruction here so the default ObjectRegistry will not be |
225 | | // statically destroyed and long-lived. |
226 | 52.2k | STATIC_AVOID_DESTRUCTION(std::shared_ptr<ObjectRegistry>, instance) |
227 | 52.2k | (std::make_shared<ObjectRegistry>(ObjectLibrary::Default())); |
228 | 52.2k | return instance; |
229 | 52.2k | } |
230 | | |
231 | 52.2k | std::shared_ptr<ObjectRegistry> ObjectRegistry::NewInstance() { |
232 | 52.2k | return std::make_shared<ObjectRegistry>(Default()); |
233 | 52.2k | } |
234 | | |
235 | | std::shared_ptr<ObjectRegistry> ObjectRegistry::NewInstance( |
236 | 0 | const std::shared_ptr<ObjectRegistry> &parent) { |
237 | 0 | return std::make_shared<ObjectRegistry>(parent); |
238 | 0 | } |
239 | | |
240 | | Status ObjectRegistry::SetManagedObject( |
241 | | const std::string &type, const std::string &id, |
242 | 0 | const std::shared_ptr<Customizable> &object) { |
243 | 0 | std::string object_key = ToManagedObjectKey(type, id); |
244 | 0 | std::shared_ptr<Customizable> curr; |
245 | 0 | if (parent_ != nullptr) { |
246 | 0 | curr = parent_->GetManagedObject(type, id); |
247 | 0 | } |
248 | 0 | if (curr == nullptr) { |
249 | | // We did not find the object in any parent. Update in the current |
250 | 0 | std::unique_lock<std::mutex> lock(objects_mutex_); |
251 | 0 | auto iter = managed_objects_.find(object_key); |
252 | 0 | if (iter != managed_objects_.end()) { // The object exists |
253 | 0 | curr = iter->second.lock(); |
254 | 0 | if (curr != nullptr && curr != object) { |
255 | 0 | return Status::InvalidArgument("Object already exists: ", object_key); |
256 | 0 | } else { |
257 | 0 | iter->second = object; |
258 | 0 | } |
259 | 0 | } else { |
260 | | // The object does not exist. Add it |
261 | 0 | managed_objects_[object_key] = object; |
262 | 0 | } |
263 | 0 | } else if (curr != object) { |
264 | 0 | return Status::InvalidArgument("Object already exists: ", object_key); |
265 | 0 | } |
266 | 0 | return Status::OK(); |
267 | 0 | } |
268 | | |
269 | | std::shared_ptr<Customizable> ObjectRegistry::GetManagedObject( |
270 | 0 | const std::string &type, const std::string &id) const { |
271 | 0 | { |
272 | 0 | std::unique_lock<std::mutex> lock(objects_mutex_); |
273 | 0 | auto iter = managed_objects_.find(ToManagedObjectKey(type, id)); |
274 | 0 | if (iter != managed_objects_.end()) { |
275 | 0 | return iter->second.lock(); |
276 | 0 | } |
277 | 0 | } |
278 | 0 | if (parent_ != nullptr) { |
279 | 0 | return parent_->GetManagedObject(type, id); |
280 | 0 | } else { |
281 | 0 | return nullptr; |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | | Status ObjectRegistry::ListManagedObjects( |
286 | | const std::string &type, const std::string &name, |
287 | 0 | std::vector<std::shared_ptr<Customizable>> *results) const { |
288 | 0 | { |
289 | 0 | std::string key = ToManagedObjectKey(type, name); |
290 | 0 | std::unique_lock<std::mutex> lock(objects_mutex_); |
291 | 0 | for (auto iter = managed_objects_.lower_bound(key); |
292 | 0 | iter != managed_objects_.end() && StartsWith(iter->first, key); |
293 | 0 | ++iter) { |
294 | 0 | auto shared = iter->second.lock(); |
295 | 0 | if (shared != nullptr) { |
296 | 0 | if (name.empty() || shared->IsInstanceOf(name)) { |
297 | 0 | results->emplace_back(shared); |
298 | 0 | } |
299 | 0 | } |
300 | 0 | } |
301 | 0 | } |
302 | 0 | if (parent_ != nullptr) { |
303 | 0 | return parent_->ListManagedObjects(type, name, results); |
304 | 0 | } else { |
305 | 0 | return Status::OK(); |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | // Returns the number of registered types for this registry. |
310 | | // If specified (not-null), types is updated to include the names of the |
311 | | // registered types. |
312 | 0 | size_t ObjectRegistry::GetFactoryCount(const std::string &type) const { |
313 | 0 | size_t count = 0; |
314 | 0 | if (parent_ != nullptr) { |
315 | 0 | count = parent_->GetFactoryCount(type); |
316 | 0 | } |
317 | 0 | std::unique_lock<std::mutex> lock(library_mutex_); |
318 | 0 | for (const auto &library : libraries_) { |
319 | 0 | count += library->GetFactoryCount(type); |
320 | 0 | } |
321 | 0 | return count; |
322 | 0 | } |
323 | | |
324 | | void ObjectRegistry::GetFactoryNames(const std::string &type, |
325 | 0 | std::vector<std::string> *names) const { |
326 | 0 | assert(names); |
327 | 0 | names->clear(); |
328 | 0 | if (parent_ != nullptr) { |
329 | 0 | parent_->GetFactoryNames(type, names); |
330 | 0 | } |
331 | 0 | std::unique_lock<std::mutex> lock(library_mutex_); |
332 | 0 | for (const auto &library : libraries_) { |
333 | 0 | library->GetFactoryNames(type, names); |
334 | 0 | } |
335 | 0 | } |
336 | | |
337 | | void ObjectRegistry::GetFactoryTypes( |
338 | 0 | std::unordered_set<std::string> *types) const { |
339 | 0 | assert(types); |
340 | 0 | if (parent_ != nullptr) { |
341 | 0 | parent_->GetFactoryTypes(types); |
342 | 0 | } |
343 | 0 | std::unique_lock<std::mutex> lock(library_mutex_); |
344 | 0 | for (const auto &library : libraries_) { |
345 | 0 | library->GetFactoryTypes(types); |
346 | 0 | } |
347 | 0 | } |
348 | | |
349 | 0 | void ObjectRegistry::Dump(Logger *logger) const { |
350 | 0 | if (logger != nullptr) { |
351 | 0 | std::unique_lock<std::mutex> lock(library_mutex_); |
352 | 0 | if (!plugins_.empty()) { |
353 | 0 | ROCKS_LOG_HEADER(logger, " Registered Plugins:"); |
354 | 0 | bool printed_one = false; |
355 | 0 | for (const auto &plugin : plugins_) { |
356 | 0 | ROCKS_LOG_HEADER(logger, "%s%s", (printed_one) ? ", " : " ", |
357 | 0 | plugin.c_str()); |
358 | 0 | printed_one = true; |
359 | 0 | } |
360 | 0 | ROCKS_LOG_HEADER(logger, "\n"); |
361 | 0 | } |
362 | 0 | for (auto iter = libraries_.crbegin(); iter != libraries_.crend(); ++iter) { |
363 | 0 | iter->get()->Dump(logger); |
364 | 0 | } |
365 | 0 | } |
366 | 0 | if (parent_ != nullptr) { |
367 | 0 | parent_->Dump(logger); |
368 | 0 | } |
369 | 0 | } |
370 | | |
371 | | int ObjectRegistry::RegisterPlugin(const std::string &name, |
372 | 0 | const RegistrarFunc &func) { |
373 | 0 | if (!name.empty() && func != nullptr) { |
374 | 0 | plugins_.push_back(name); |
375 | 0 | return AddLibrary(name)->Register(func, name); |
376 | 0 | } else { |
377 | 0 | return -1; |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | | } // namespace ROCKSDB_NAMESPACE |