1 // Copyright (c) 2016-present, Facebook, Inc.  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 #pragma once
7 
8 #ifndef ROCKSDB_LITE
9 
10 #include <functional>
11 #include <memory>
12 #include <regex>
13 #include <string>
14 #include <unordered_map>
15 #include <vector>
16 #include "rocksdb/status.h"
17 
18 namespace ROCKSDB_NAMESPACE {
19 class Logger;
20 // Returns a new T when called with a string. Populates the std::unique_ptr
21 // argument if granting ownership to caller.
22 template <typename T>
23 using FactoryFunc =
24     std::function<T*(const std::string&, std::unique_ptr<T>*, std::string*)>;
25 
26 class ObjectLibrary {
27  public:
28   // Base class for an Entry in the Registry.
29   class Entry {
30    public:
~Entry()31     virtual ~Entry() {}
Entry(const std::string & name)32     Entry(const std::string& name) : name_(std::move(name)) {}
33 
34     // Checks to see if the target matches this entry
matches(const std::string & target)35     virtual bool matches(const std::string& target) const {
36       return name_ == target;
37     }
Name()38     const std::string& Name() const { return name_; }
39 
40    private:
41     const std::string name_;  // The name of the Entry
42   };                          // End class Entry
43 
44   // An Entry containing a FactoryFunc for creating new Objects
45   template <typename T>
46   class FactoryEntry : public Entry {
47    public:
FactoryEntry(const std::string & name,FactoryFunc<T> f)48     FactoryEntry(const std::string& name, FactoryFunc<T> f)
49         : Entry(name), pattern_(std::move(name)), factory_(std::move(f)) {}
~FactoryEntry()50     ~FactoryEntry() override {}
matches(const std::string & target)51     bool matches(const std::string& target) const override {
52       return std::regex_match(target, pattern_);
53     }
54     // Creates a new T object.
NewFactoryObject(const std::string & target,std::unique_ptr<T> * guard,std::string * msg)55     T* NewFactoryObject(const std::string& target, std::unique_ptr<T>* guard,
56                         std::string* msg) const {
57       return factory_(target, guard, msg);
58     }
59 
60    private:
61     std::regex pattern_;  // The pattern for this entry
62     FactoryFunc<T> factory_;
63   };  // End class FactoryEntry
64  public:
65   // Finds the entry matching the input name and type
66   const Entry* FindEntry(const std::string& type,
67                          const std::string& name) const;
68   void Dump(Logger* logger) const;
69 
70   // Registers the factory with the library for the pattern.
71   // If the pattern matches, the factory may be used to create a new object.
72   template <typename T>
Register(const std::string & pattern,const FactoryFunc<T> & factory)73   const FactoryFunc<T>& Register(const std::string& pattern,
74                                  const FactoryFunc<T>& factory) {
75     std::unique_ptr<Entry> entry(new FactoryEntry<T>(pattern, factory));
76     AddEntry(T::Type(), entry);
77     return factory;
78   }
79   // Returns the default ObjectLibrary
80   static std::shared_ptr<ObjectLibrary>& Default();
81 
82  private:
83   // Adds the input entry to the list for the given type
84   void AddEntry(const std::string& type, std::unique_ptr<Entry>& entry);
85 
86   // ** FactoryFunctions for this loader, organized by type
87   std::unordered_map<std::string, std::vector<std::unique_ptr<Entry>>> entries_;
88 };
89 
90 // The ObjectRegistry is used to register objects that can be created by a
91 // name/pattern at run-time where the specific implementation of the object may
92 // not be known in advance.
93 class ObjectRegistry {
94  public:
95   static std::shared_ptr<ObjectRegistry> NewInstance();
96 
97   ObjectRegistry();
98 
AddLibrary(const std::shared_ptr<ObjectLibrary> & library)99   void AddLibrary(const std::shared_ptr<ObjectLibrary>& library) {
100     libraries_.emplace_back(library);
101   }
102 
103   // Creates a new T using the factory function that was registered with a
104   // pattern that matches the provided "target" string according to
105   // std::regex_match.
106   //
107   // If no registered functions match, returns nullptr. If multiple functions
108   // match, the factory function used is unspecified.
109   //
110   // Populates res_guard with result pointer if caller is granted ownership.
111   template <typename T>
NewObject(const std::string & target,std::unique_ptr<T> * guard,std::string * errmsg)112   T* NewObject(const std::string& target, std::unique_ptr<T>* guard,
113                std::string* errmsg) {
114     guard->reset();
115     const auto* basic = FindEntry(T::Type(), target);
116     if (basic != nullptr) {
117       const auto* factory =
118           static_cast<const ObjectLibrary::FactoryEntry<T>*>(basic);
119       return factory->NewFactoryObject(target, guard, errmsg);
120     } else {
121       *errmsg = std::string("Could not load ") + T::Type();
122       return nullptr;
123     }
124   }
125 
126   // Creates a new unique T using the input factory functions.
127   // Returns OK if a new unique T was successfully created
128   // Returns NotFound if the type/target could not be created
129   // Returns InvalidArgument if the factory return an unguarded object
130   //                      (meaning it cannot be managed by a unique ptr)
131   template <typename T>
NewUniqueObject(const std::string & target,std::unique_ptr<T> * result)132   Status NewUniqueObject(const std::string& target,
133                          std::unique_ptr<T>* result) {
134     std::string errmsg;
135     T* ptr = NewObject(target, result, &errmsg);
136     if (ptr == nullptr) {
137       return Status::NotFound(errmsg, target);
138     } else if (*result) {
139       return Status::OK();
140     } else {
141       return Status::InvalidArgument(std::string("Cannot make a unique ") +
142                                          T::Type() + " from unguarded one ",
143                                      target);
144     }
145   }
146 
147   // Creates a new shared T using the input factory functions.
148   // Returns OK if a new shared T was successfully created
149   // Returns NotFound if the type/target could not be created
150   // Returns InvalidArgument if the factory return an unguarded object
151   //                      (meaning it cannot be managed by a shared ptr)
152   template <typename T>
NewSharedObject(const std::string & target,std::shared_ptr<T> * result)153   Status NewSharedObject(const std::string& target,
154                          std::shared_ptr<T>* result) {
155     std::string errmsg;
156     std::unique_ptr<T> guard;
157     T* ptr = NewObject(target, &guard, &errmsg);
158     if (ptr == nullptr) {
159       return Status::NotFound(errmsg, target);
160     } else if (guard) {
161       result->reset(guard.release());
162       return Status::OK();
163     } else {
164       return Status::InvalidArgument(std::string("Cannot make a shared ") +
165                                          T::Type() + " from unguarded one ",
166                                      target);
167     }
168   }
169 
170   // Creates a new static T using the input factory functions.
171   // Returns OK if a new static T was successfully created
172   // Returns NotFound if the type/target could not be created
173   // Returns InvalidArgument if the factory return a guarded object
174   //                      (meaning it is managed by a unique ptr)
175   template <typename T>
NewStaticObject(const std::string & target,T ** result)176   Status NewStaticObject(const std::string& target, T** result) {
177     std::string errmsg;
178     std::unique_ptr<T> guard;
179     T* ptr = NewObject(target, &guard, &errmsg);
180     if (ptr == nullptr) {
181       return Status::NotFound(errmsg, target);
182     } else if (guard.get()) {
183       return Status::InvalidArgument(std::string("Cannot make a static ") +
184                                          T::Type() + " from a guarded one ",
185                                      target);
186     } else {
187       *result = ptr;
188       return Status::OK();
189     }
190   }
191 
192   // Dump the contents of the registry to the logger
193   void Dump(Logger* logger) const;
194 
195  private:
196   const ObjectLibrary::Entry* FindEntry(const std::string& type,
197                                         const std::string& name) const;
198 
199   // The set of libraries to search for factories for this registry.
200   // The libraries are searched in reverse order (back to front) when
201   // searching for entries.
202   std::vector<std::shared_ptr<ObjectLibrary>> libraries_;
203 };
204 }  // namespace ROCKSDB_NAMESPACE
205 #endif  // ROCKSDB_LITE
206