1 // Copyright (c) 2011-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 #include <functional>
8 #include <memory>
9 #include <unordered_map>
10 
11 #include "options/configurable_helper.h"
12 #include "rocksdb/convenience.h"
13 #include "rocksdb/customizable.h"
14 #include "rocksdb/status.h"
15 #include "rocksdb/utilities/object_registry.h"
16 
17 namespace ROCKSDB_NAMESPACE {
18 template <typename T>
19 using SharedFactoryFunc =
20     std::function<bool(const std::string&, std::shared_ptr<T>*)>;
21 
22 template <typename T>
23 using UniqueFactoryFunc =
24     std::function<bool(const std::string&, std::unique_ptr<T>*)>;
25 
26 template <typename T>
27 using StaticFactoryFunc = std::function<bool(const std::string&, T**)>;
28 
29 // Creates a new shared Customizable object based on the input parameters.
30 // This method parses the input value to determine the type of instance to
31 // create. If there is an existing instance (in result) and it is the same type
32 // as the object being created, the existing configuration is stored and used as
33 // the default for the new object.
34 //
35 // The value parameter specified the instance class of the object to create.
36 // If it is a simple string (e.g. BlockBasedTable), then the instance will be
37 // created using the default settings.  If the value is a set of name-value
38 // pairs, then the "id" value is used to determine the instance to create and
39 // the remaining parameters are used to configure the object.  Id name-value
40 // pairs are specified, there must be an "id=value" pairing or an error will
41 // result.
42 //
43 // The config_options parameter controls the process and how errors are
44 // returned. If ignore_unknown_options=true, unknown values are ignored during
45 // the configuration If ignore_unsupported_options=true, unknown instance types
46 // are ignored If invoke_prepare_options=true, the resulting instance wll be
47 // initialized (via PrepareOptions
48 //
49 // @param config_options Controls how the instance is created and errors are
50 // handled
51 // @param value Either the simple name of the instance to create, or a set of
52 // name-value pairs to
53 //              create and initailzie the object
54 // @param func  Optional function to call to attempt to create an instance
55 // @param result The newly created instance.
56 template <typename T>
LoadSharedObject(const ConfigOptions & config_options,const std::string & value,const SharedFactoryFunc<T> & func,std::shared_ptr<T> * result)57 static Status LoadSharedObject(const ConfigOptions& config_options,
58                                const std::string& value,
59                                const SharedFactoryFunc<T>& func,
60                                std::shared_ptr<T>* result) {
61   std::string id;
62   std::unordered_map<std::string, std::string> opt_map;
63   Status status =
64       ConfigurableHelper::GetOptionsMap(value, result->get(), &id, &opt_map);
65   if (!status.ok()) {  // GetOptionsMap failed
66     return status;
67   }
68   std::string curr_opts;
69 #ifndef ROCKSDB_LITE
70   if (result->get() != nullptr && result->get()->GetId() == id) {
71     // Try to get the existing options, ignoring any errors
72     ConfigOptions embedded = config_options;
73     embedded.delimiter = ";";
74     result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError();
75   }
76 #endif
77   if (func == nullptr || !func(id, result)) {  // No factory, or it failed
78     if (value.empty()) {
79       // No Id and no options.  Clear the object
80       result->reset();
81       return Status::OK();
82     } else if (id.empty()) {  // We have no Id but have options.  Not good
83       return Status::NotSupported("Cannot reset object ", id);
84     } else {
85 #ifndef ROCKSDB_LITE
86       status = config_options.registry->NewSharedObject(id, result);
87 #else
88       status = Status::NotSupported("Cannot load object in LITE mode ", id);
89 #endif
90       if (!status.ok()) {
91         if (config_options.ignore_unsupported_options &&
92             status.IsNotSupported()) {
93           return Status::OK();
94         } else {
95           return status;
96         }
97       }
98     }
99   }
100   return ConfigurableHelper::ConfigureNewObject(config_options, result->get(),
101                                                 id, curr_opts, opt_map);
102 }
103 
104 // Creates a new unique customizable instance object based on the input
105 // parameters.
106 // @see LoadSharedObject for more information on the inner workings of this
107 // method.
108 //
109 // @param config_options Controls how the instance is created and errors are
110 // handled
111 // @param value Either the simple name of the instance to create, or a set of
112 // name-value pairs to
113 //              create and initailzie the object
114 // @param func  Optional function to call to attempt to create an instance
115 // @param result The newly created instance.
116 template <typename T>
LoadUniqueObject(const ConfigOptions & config_options,const std::string & value,const UniqueFactoryFunc<T> & func,std::unique_ptr<T> * result)117 static Status LoadUniqueObject(const ConfigOptions& config_options,
118                                const std::string& value,
119                                const UniqueFactoryFunc<T>& func,
120                                std::unique_ptr<T>* result) {
121   std::string id;
122   std::unordered_map<std::string, std::string> opt_map;
123   Status status =
124       ConfigurableHelper::GetOptionsMap(value, result->get(), &id, &opt_map);
125   if (!status.ok()) {  // GetOptionsMap failed
126     return status;
127   }
128   std::string curr_opts;
129 #ifndef ROCKSDB_LITE
130   if (result->get() != nullptr && result->get()->GetId() == id) {
131     // Try to get the existing options, ignoring any errors
132     ConfigOptions embedded = config_options;
133     embedded.delimiter = ";";
134     result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError();
135   }
136 #endif
137   if (func == nullptr || !func(id, result)) {  // No factory, or it failed
138     if (value.empty()) {
139       // No Id and no options.  Clear the object
140       result->reset();
141       return Status::OK();
142     } else if (id.empty()) {  // We have no Id but have options.  Not good
143       return Status::NotSupported("Cannot reset object ", id);
144     } else {
145 #ifndef ROCKSDB_LITE
146       status = config_options.registry->NewUniqueObject(id, result);
147 #else
148       status = Status::NotSupported("Cannot load object in LITE mode ", id);
149 #endif  // ROCKSDB_LITE
150       if (!status.ok()) {
151         if (config_options.ignore_unsupported_options &&
152             status.IsNotSupported()) {
153           return Status::OK();
154         } else {
155           return status;
156         }
157       }
158     }
159   }
160   return ConfigurableHelper::ConfigureNewObject(config_options, result->get(),
161                                                 id, curr_opts, opt_map);
162 }
163 // Creates a new static (raw pointer) customizable instance object based on the
164 // input parameters.
165 // @see LoadSharedObject for more information on the inner workings of this
166 // method.
167 //
168 // @param config_options Controls how the instance is created and errors are
169 // handled
170 // @param value Either the simple name of the instance to create, or a set of
171 // name-value pairs to
172 //              create and initailzie the object
173 // @param func  Optional function to call to attempt to create an instance
174 // @param result The newly created instance.
175 template <typename T>
LoadStaticObject(const ConfigOptions & config_options,const std::string & value,const StaticFactoryFunc<T> & func,T ** result)176 static Status LoadStaticObject(const ConfigOptions& config_options,
177                                const std::string& value,
178                                const StaticFactoryFunc<T>& func, T** result) {
179   std::string id;
180   std::unordered_map<std::string, std::string> opt_map;
181   Status status =
182       ConfigurableHelper::GetOptionsMap(value, *result, &id, &opt_map);
183   if (!status.ok()) {  // GetOptionsMap failed
184     return status;
185   }
186   std::string curr_opts;
187 #ifndef ROCKSDB_LITE
188   if (*result != nullptr && (*result)->GetId() == id) {
189     // Try to get the existing options, ignoring any errors
190     ConfigOptions embedded = config_options;
191     embedded.delimiter = ";";
192     (*result)->GetOptionString(embedded, &curr_opts).PermitUncheckedError();
193   }
194 #endif
195   if (func == nullptr || !func(id, result)) {  // No factory, or it failed
196     if (value.empty()) {
197       // No Id and no options.  Clear the object
198       *result = nullptr;
199       return Status::OK();
200     } else if (id.empty()) {  // We have no Id but have options.  Not good
201       return Status::NotSupported("Cannot reset object ", id);
202     } else {
203 #ifndef ROCKSDB_LITE
204       status = config_options.registry->NewStaticObject(id, result);
205 #else
206       status = Status::NotSupported("Cannot load object in LITE mode ", id);
207 #endif  // ROCKSDB_LITE
208       if (!status.ok()) {
209         if (config_options.ignore_unsupported_options &&
210             status.IsNotSupported()) {
211           return Status::OK();
212         } else {
213           return status;
214         }
215       }
216     }
217   }
218   return ConfigurableHelper::ConfigureNewObject(config_options, *result, id,
219                                                 curr_opts, opt_map);
220 }
221 }  // namespace ROCKSDB_NAMESPACE
222