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