]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
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). | |
1e59de90 TL |
5 | // |
6 | // The OptionTypeInfo and related classes provide a framework for | |
7 | // configuring and validating RocksDB classes via the Options framework. | |
8 | // This file is part of the public API to allow developers who wish to | |
9 | // write their own extensions and plugins to take use the Options | |
10 | // framework in their custom implementations. | |
11 | // | |
12 | // See https://github.com/facebook/rocksdb/wiki/RocksDB-Configurable-Objects | |
13 | // for more information on how to develop and use custom extensions | |
20effc67 TL |
14 | |
15 | #pragma once | |
16 | ||
17 | #include <functional> | |
18 | #include <memory> | |
19 | #include <unordered_map> | |
20 | ||
21 | #include "rocksdb/convenience.h" | |
22 | #include "rocksdb/rocksdb_namespace.h" | |
23 | #include "rocksdb/status.h" | |
24 | ||
25 | namespace ROCKSDB_NAMESPACE { | |
26 | class OptionTypeInfo; | |
1e59de90 TL |
27 | struct ColumnFamilyOptions; |
28 | struct DBOptions; | |
20effc67 TL |
29 | |
30 | // The underlying "class/type" of the option. | |
31 | // This enum is used to determine how the option should | |
32 | // be converted to/from strings and compared. | |
33 | enum class OptionType { | |
34 | kBoolean, | |
35 | kInt, | |
36 | kInt32T, | |
37 | kInt64T, | |
38 | kUInt, | |
1e59de90 | 39 | kUInt8T, |
20effc67 TL |
40 | kUInt32T, |
41 | kUInt64T, | |
42 | kSizeT, | |
43 | kString, | |
44 | kDouble, | |
45 | kCompactionStyle, | |
46 | kCompactionPri, | |
20effc67 | 47 | kCompressionType, |
20effc67 | 48 | kCompactionStopStyle, |
20effc67 TL |
49 | kChecksumType, |
50 | kEncodingType, | |
51 | kEnv, | |
52 | kEnum, | |
53 | kStruct, | |
54 | kVector, | |
55 | kConfigurable, | |
56 | kCustomizable, | |
1e59de90 TL |
57 | kEncodedString, |
58 | kTemperature, | |
59 | kArray, | |
20effc67 TL |
60 | kUnknown, |
61 | }; | |
62 | ||
63 | enum class OptionVerificationType { | |
64 | kNormal, | |
65 | kByName, // The option is pointer typed so we can only verify | |
66 | // based on it's name. | |
67 | kByNameAllowNull, // Same as kByName, but it also allows the case | |
68 | // where one of them is a nullptr. | |
69 | kByNameAllowFromNull, // Same as kByName, but it also allows the case | |
70 | // where the old option is nullptr. | |
71 | kDeprecated, // The option is no longer used in rocksdb. The RocksDB | |
72 | // OptionsParser will still accept this option if it | |
73 | // happen to exists in some Options file. However, | |
74 | // the parser will not include it in serialization | |
75 | // and verification processes. | |
76 | kAlias, // This option represents is a name/shortcut for | |
77 | // another option and should not be written or verified | |
78 | // independently | |
79 | }; | |
80 | ||
81 | // A set of modifier flags used to alter how an option is evaluated or | |
82 | // processed. These flags can be combined together (e.g. kMutable | kShared). | |
83 | // The kCompare flags can be used to control if/when options are compared. | |
84 | // If kCompareNever is set, two related options would never be compared (always | |
85 | // equal) If kCompareExact is set, the options will only be compared if the | |
86 | // sanity mode | |
87 | // is exact | |
88 | // kMutable means the option can be changed after it is prepared | |
89 | // kShared means the option is contained in a std::shared_ptr | |
90 | // kUnique means the option is contained in a std::uniqued_ptr | |
91 | // kRawPointer means the option is a raw pointer value. | |
92 | // kAllowNull means that an option is allowed to be null for verification | |
93 | // purposes. | |
94 | // kDontSerialize means this option should not be serialized and included in | |
95 | // the string representation. | |
96 | // kDontPrepare means do not call PrepareOptions for this pointer value. | |
97 | enum class OptionTypeFlags : uint32_t { | |
98 | kNone = 0x00, // No flags | |
99 | kCompareDefault = 0x0, | |
100 | kCompareNever = ConfigOptions::kSanityLevelNone, | |
101 | kCompareLoose = ConfigOptions::kSanityLevelLooselyCompatible, | |
102 | kCompareExact = ConfigOptions::kSanityLevelExactMatch, | |
103 | ||
104 | kMutable = 0x0100, // Option is mutable | |
105 | kRawPointer = 0x0200, // The option is stored as a raw pointer | |
106 | kShared = 0x0400, // The option is stored as a shared_ptr | |
107 | kUnique = 0x0800, // The option is stored as a unique_ptr | |
108 | kAllowNull = 0x1000, // The option can be null | |
109 | kDontSerialize = 0x2000, // Don't serialize the option | |
110 | kDontPrepare = 0x4000, // Don't prepare or sanitize this option | |
111 | kStringNameOnly = 0x8000, // The option serializes to a name only | |
112 | }; | |
113 | ||
1e59de90 TL |
114 | inline OptionTypeFlags operator|(const OptionTypeFlags& a, |
115 | const OptionTypeFlags& b) { | |
20effc67 TL |
116 | return static_cast<OptionTypeFlags>(static_cast<uint32_t>(a) | |
117 | static_cast<uint32_t>(b)); | |
118 | } | |
119 | ||
1e59de90 TL |
120 | inline OptionTypeFlags operator&(const OptionTypeFlags& a, |
121 | const OptionTypeFlags& b) { | |
20effc67 TL |
122 | return static_cast<OptionTypeFlags>(static_cast<uint32_t>(a) & |
123 | static_cast<uint32_t>(b)); | |
124 | } | |
125 | ||
126 | // Converts an string into its enumerated value. | |
127 | // @param type_map Mapping between strings and enum values | |
128 | // @param type The string representation of the enum | |
129 | // @param value Returns the enum value represented by the string | |
130 | // @return true if the string was found in the enum map, false otherwise. | |
131 | template <typename T> | |
132 | bool ParseEnum(const std::unordered_map<std::string, T>& type_map, | |
133 | const std::string& type, T* value) { | |
134 | auto iter = type_map.find(type); | |
135 | if (iter != type_map.end()) { | |
136 | *value = iter->second; | |
137 | return true; | |
138 | } | |
139 | return false; | |
140 | } | |
141 | ||
142 | // Converts an enum into its string representation. | |
143 | // @param type_map Mapping between strings and enum values | |
144 | // @param type The enum | |
145 | // @param value Returned as the string representation of the enum | |
146 | // @return true if the enum was found in the enum map, false otherwise. | |
147 | template <typename T> | |
148 | bool SerializeEnum(const std::unordered_map<std::string, T>& type_map, | |
149 | const T& type, std::string* value) { | |
150 | for (const auto& pair : type_map) { | |
151 | if (pair.second == type) { | |
152 | *value = pair.first; | |
153 | return true; | |
154 | } | |
155 | } | |
156 | return false; | |
157 | } | |
158 | ||
1e59de90 TL |
159 | template <typename T, size_t kSize> |
160 | Status ParseArray(const ConfigOptions& config_options, | |
161 | const OptionTypeInfo& elem_info, char separator, | |
162 | const std::string& name, const std::string& value, | |
163 | std::array<T, kSize>* result); | |
164 | ||
165 | template <typename T, size_t kSize> | |
166 | Status SerializeArray(const ConfigOptions& config_options, | |
167 | const OptionTypeInfo& elem_info, char separator, | |
168 | const std::string& name, const std::array<T, kSize>& vec, | |
169 | std::string* value); | |
170 | ||
171 | template <typename T, size_t kSize> | |
172 | bool ArraysAreEqual(const ConfigOptions& config_options, | |
173 | const OptionTypeInfo& elem_info, const std::string& name, | |
174 | const std::array<T, kSize>& array1, | |
175 | const std::array<T, kSize>& array2, std::string* mismatch); | |
176 | ||
20effc67 TL |
177 | template <typename T> |
178 | Status ParseVector(const ConfigOptions& config_options, | |
179 | const OptionTypeInfo& elem_info, char separator, | |
180 | const std::string& name, const std::string& value, | |
181 | std::vector<T>* result); | |
182 | ||
183 | template <typename T> | |
184 | Status SerializeVector(const ConfigOptions& config_options, | |
185 | const OptionTypeInfo& elem_info, char separator, | |
186 | const std::string& name, const std::vector<T>& vec, | |
187 | std::string* value); | |
188 | template <typename T> | |
189 | bool VectorsAreEqual(const ConfigOptions& config_options, | |
190 | const OptionTypeInfo& elem_info, const std::string& name, | |
191 | const std::vector<T>& vec1, const std::vector<T>& vec2, | |
192 | std::string* mismatch); | |
193 | ||
194 | // Function for converting a option string value into its underlying | |
195 | // representation in "addr" | |
196 | // On success, Status::OK is returned and addr is set to the parsed form | |
197 | // On failure, a non-OK status is returned | |
198 | // @param opts The ConfigOptions controlling how the value is parsed | |
199 | // @param name The name of the options being parsed | |
200 | // @param value The string representation of the option | |
201 | // @param addr Pointer to the object | |
202 | using ParseFunc = std::function<Status( | |
203 | const ConfigOptions& /*opts*/, const std::string& /*name*/, | |
1e59de90 | 204 | const std::string& /*value*/, void* /*addr*/)>; |
20effc67 TL |
205 | |
206 | // Function for converting an option "addr" into its string representation. | |
207 | // On success, Status::OK is returned and value is the serialized form. | |
208 | // On failure, a non-OK status is returned | |
209 | // @param opts The ConfigOptions controlling how the values are serialized | |
210 | // @param name The name of the options being serialized | |
211 | // @param addr Pointer to the value being serialized | |
212 | // @param value The result of the serialization. | |
213 | using SerializeFunc = std::function<Status( | |
214 | const ConfigOptions& /*opts*/, const std::string& /*name*/, | |
1e59de90 | 215 | const void* /*addr*/, std::string* /*value*/)>; |
20effc67 TL |
216 | |
217 | // Function for comparing two option values | |
218 | // If they are not equal, updates "mismatch" with the name of the bad option | |
219 | // @param opts The ConfigOptions controlling how the values are compared | |
220 | // @param name The name of the options being compared | |
221 | // @param addr1 The first address to compare | |
222 | // @param addr2 The address to compare to | |
223 | // @param mismatch If the values are not equal, the name of the option that | |
224 | // first differs | |
225 | using EqualsFunc = std::function<bool( | |
226 | const ConfigOptions& /*opts*/, const std::string& /*name*/, | |
1e59de90 TL |
227 | const void* /*addr1*/, const void* /*addr2*/, std::string* mismatch)>; |
228 | ||
229 | // Function for preparing/initializing an option. | |
230 | using PrepareFunc = | |
231 | std::function<Status(const ConfigOptions& /*opts*/, | |
232 | const std::string& /*name*/, void* /*addr*/)>; | |
233 | ||
234 | // Function for validating an option. | |
235 | using ValidateFunc = std::function<Status( | |
236 | const DBOptions& /*db_opts*/, const ColumnFamilyOptions& /*cf_opts*/, | |
237 | const std::string& /*name*/, const void* /*addr*/)>; | |
20effc67 TL |
238 | |
239 | // A struct for storing constant option information such as option name, | |
240 | // option type, and offset. | |
241 | class OptionTypeInfo { | |
242 | public: | |
243 | // A simple "normal", non-mutable Type "type" at offset | |
244 | OptionTypeInfo(int offset, OptionType type) | |
245 | : offset_(offset), | |
246 | parse_func_(nullptr), | |
247 | serialize_func_(nullptr), | |
248 | equals_func_(nullptr), | |
249 | type_(type), | |
250 | verification_(OptionVerificationType::kNormal), | |
251 | flags_(OptionTypeFlags::kNone) {} | |
252 | ||
253 | OptionTypeInfo(int offset, OptionType type, | |
254 | OptionVerificationType verification, OptionTypeFlags flags) | |
255 | : offset_(offset), | |
256 | parse_func_(nullptr), | |
257 | serialize_func_(nullptr), | |
258 | equals_func_(nullptr), | |
259 | type_(type), | |
260 | verification_(verification), | |
261 | flags_(flags) {} | |
262 | ||
263 | OptionTypeInfo(int offset, OptionType type, | |
264 | OptionVerificationType verification, OptionTypeFlags flags, | |
265 | const ParseFunc& parse_func) | |
266 | : offset_(offset), | |
267 | parse_func_(parse_func), | |
268 | serialize_func_(nullptr), | |
269 | equals_func_(nullptr), | |
270 | type_(type), | |
271 | verification_(verification), | |
272 | flags_(flags) {} | |
273 | ||
274 | OptionTypeInfo(int offset, OptionType type, | |
275 | OptionVerificationType verification, OptionTypeFlags flags, | |
276 | const ParseFunc& parse_func, | |
277 | const SerializeFunc& serialize_func, | |
278 | const EqualsFunc& equals_func) | |
279 | : offset_(offset), | |
280 | parse_func_(parse_func), | |
281 | serialize_func_(serialize_func), | |
282 | equals_func_(equals_func), | |
283 | type_(type), | |
284 | verification_(verification), | |
285 | flags_(flags) {} | |
286 | ||
287 | // Creates an OptionTypeInfo for an enum type. Enums use an additional | |
288 | // map to convert the enums to/from their string representation. | |
289 | // To create an OptionTypeInfo that is an Enum, one should: | |
290 | // - Create a static map of string values to the corresponding enum value | |
291 | // - Call this method passing the static map in as a parameter. | |
292 | // Note that it is not necessary to add a new OptionType or make any | |
1e59de90 | 293 | // other changes -- the returned object handles parsing, serialization, and |
20effc67 TL |
294 | // comparisons. |
295 | // | |
296 | // @param offset The offset in the option object for this enum | |
297 | // @param map The string to enum mapping for this enum | |
298 | template <typename T> | |
299 | static OptionTypeInfo Enum( | |
1e59de90 TL |
300 | int offset, const std::unordered_map<std::string, T>* const map, |
301 | OptionTypeFlags flags = OptionTypeFlags::kNone) { | |
302 | OptionTypeInfo info(offset, OptionType::kEnum, | |
303 | OptionVerificationType::kNormal, flags); | |
304 | info.SetParseFunc( | |
20effc67 TL |
305 | // Uses the map argument to convert the input string into |
306 | // its corresponding enum value. If value is found in the map, | |
307 | // addr is updated to the corresponding map entry. | |
308 | // @return OK if the value is found in the map | |
309 | // @return InvalidArgument if the value is not found in the map | |
310 | [map](const ConfigOptions&, const std::string& name, | |
1e59de90 | 311 | const std::string& value, void* addr) { |
20effc67 TL |
312 | if (map == nullptr) { |
313 | return Status::NotSupported("No enum mapping ", name); | |
1e59de90 | 314 | } else if (ParseEnum<T>(*map, value, static_cast<T*>(addr))) { |
20effc67 TL |
315 | return Status::OK(); |
316 | } else { | |
317 | return Status::InvalidArgument("No mapping for enum ", name); | |
318 | } | |
1e59de90 TL |
319 | }); |
320 | info.SetSerializeFunc( | |
20effc67 TL |
321 | // Uses the map argument to convert the input enum into |
322 | // its corresponding string value. If enum value is found in the map, | |
323 | // value is updated to the corresponding string value in the map. | |
324 | // @return OK if the enum is found in the map | |
325 | // @return InvalidArgument if the enum is not found in the map | |
1e59de90 | 326 | [map](const ConfigOptions&, const std::string& name, const void* addr, |
20effc67 TL |
327 | std::string* value) { |
328 | if (map == nullptr) { | |
329 | return Status::NotSupported("No enum mapping ", name); | |
1e59de90 | 330 | } else if (SerializeEnum<T>(*map, (*static_cast<const T*>(addr)), |
20effc67 TL |
331 | value)) { |
332 | return Status::OK(); | |
333 | } else { | |
334 | return Status::InvalidArgument("No mapping for enum ", name); | |
335 | } | |
1e59de90 TL |
336 | }); |
337 | info.SetEqualsFunc( | |
20effc67 TL |
338 | // Casts addr1 and addr2 to the enum type and returns true if |
339 | // they are equal, false otherwise. | |
1e59de90 TL |
340 | [](const ConfigOptions&, const std::string&, const void* addr1, |
341 | const void* addr2, std::string*) { | |
342 | return (*static_cast<const T*>(addr1) == | |
343 | *static_cast<const T*>(addr2)); | |
20effc67 | 344 | }); |
1e59de90 | 345 | return info; |
20effc67 TL |
346 | } // End OptionTypeInfo::Enum |
347 | ||
348 | // Creates an OptionTypeInfo for a Struct type. Structs have a | |
349 | // map of string-OptionTypeInfo associated with them that describes how | |
350 | // to process the object for parsing, serializing, and matching. | |
351 | // Structs also have a struct_name, which is the name of the object | |
352 | // as registered in the parent map. | |
353 | // When processing a struct, the option name can be specified as: | |
354 | // - <struct_name> Meaning to process the entire struct. | |
355 | // - <struct_name.field> Meaning to process the single field | |
356 | // - <field> Process the single fields | |
357 | // The CompactionOptionsFIFO, CompactionOptionsUniversal, and LRUCacheOptions | |
358 | // are all examples of Struct options. | |
359 | // | |
360 | // To create an OptionTypeInfo that is a Struct, one should: | |
361 | // - Create a static map of string-OptionTypeInfo corresponding to the | |
362 | // properties of the object that can be set via the options. | |
363 | // - Call this method passing the name and map in as parameters. | |
364 | // Note that it is not necessary to add a new OptionType or make any | |
365 | // other changes -- the returned object handles parsing, serialization, and | |
366 | // comparisons. | |
367 | // | |
368 | // @param offset The offset in the option object for this enum | |
369 | // @param map The string to enum mapping for this enum | |
370 | static OptionTypeInfo Struct( | |
371 | const std::string& struct_name, | |
372 | const std::unordered_map<std::string, OptionTypeInfo>* struct_map, | |
373 | int offset, OptionVerificationType verification, OptionTypeFlags flags) { | |
1e59de90 TL |
374 | OptionTypeInfo info(offset, OptionType::kStruct, verification, flags); |
375 | info.SetParseFunc( | |
20effc67 TL |
376 | // Parses the struct and updates the fields at addr |
377 | [struct_name, struct_map](const ConfigOptions& opts, | |
378 | const std::string& name, | |
1e59de90 | 379 | const std::string& value, void* addr) { |
20effc67 | 380 | return ParseStruct(opts, struct_name, struct_map, name, value, addr); |
1e59de90 TL |
381 | }); |
382 | info.SetSerializeFunc( | |
20effc67 TL |
383 | // Serializes the struct options into value |
384 | [struct_name, struct_map](const ConfigOptions& opts, | |
1e59de90 | 385 | const std::string& name, const void* addr, |
20effc67 TL |
386 | std::string* value) { |
387 | return SerializeStruct(opts, struct_name, struct_map, name, addr, | |
388 | value); | |
1e59de90 TL |
389 | }); |
390 | info.SetEqualsFunc( | |
20effc67 TL |
391 | // Compares the struct fields of addr1 and addr2 for equality |
392 | [struct_name, struct_map](const ConfigOptions& opts, | |
1e59de90 TL |
393 | const std::string& name, const void* addr1, |
394 | const void* addr2, std::string* mismatch) { | |
20effc67 TL |
395 | return StructsAreEqual(opts, struct_name, struct_map, name, addr1, |
396 | addr2, mismatch); | |
397 | }); | |
1e59de90 | 398 | return info; |
20effc67 TL |
399 | } |
400 | static OptionTypeInfo Struct( | |
401 | const std::string& struct_name, | |
402 | const std::unordered_map<std::string, OptionTypeInfo>* struct_map, | |
403 | int offset, OptionVerificationType verification, OptionTypeFlags flags, | |
404 | const ParseFunc& parse_func) { | |
1e59de90 TL |
405 | OptionTypeInfo info( |
406 | Struct(struct_name, struct_map, offset, verification, flags)); | |
407 | return info.SetParseFunc(parse_func); | |
408 | } | |
409 | ||
410 | template <typename T, size_t kSize> | |
411 | static OptionTypeInfo Array(int _offset, OptionVerificationType _verification, | |
412 | OptionTypeFlags _flags, | |
413 | const OptionTypeInfo& elem_info, | |
414 | char separator = ':') { | |
415 | OptionTypeInfo info(_offset, OptionType::kArray, _verification, _flags); | |
416 | info.SetParseFunc([elem_info, separator]( | |
417 | const ConfigOptions& opts, const std::string& name, | |
418 | const std::string& value, void* addr) { | |
419 | auto result = static_cast<std::array<T, kSize>*>(addr); | |
420 | return ParseArray<T, kSize>(opts, elem_info, separator, name, value, | |
421 | result); | |
422 | }); | |
423 | info.SetSerializeFunc([elem_info, separator](const ConfigOptions& opts, | |
424 | const std::string& name, | |
425 | const void* addr, | |
426 | std::string* value) { | |
427 | const auto& array = *(static_cast<const std::array<T, kSize>*>(addr)); | |
428 | return SerializeArray<T, kSize>(opts, elem_info, separator, name, array, | |
429 | value); | |
430 | }); | |
431 | info.SetEqualsFunc([elem_info](const ConfigOptions& opts, | |
432 | const std::string& name, const void* addr1, | |
433 | const void* addr2, std::string* mismatch) { | |
434 | const auto& array1 = *(static_cast<const std::array<T, kSize>*>(addr1)); | |
435 | const auto& array2 = *(static_cast<const std::array<T, kSize>*>(addr2)); | |
436 | return ArraysAreEqual<T, kSize>(opts, elem_info, name, array1, array2, | |
437 | mismatch); | |
438 | }); | |
439 | return info; | |
20effc67 TL |
440 | } |
441 | ||
442 | template <typename T> | |
443 | static OptionTypeInfo Vector(int _offset, | |
444 | OptionVerificationType _verification, | |
445 | OptionTypeFlags _flags, | |
446 | const OptionTypeInfo& elem_info, | |
447 | char separator = ':') { | |
1e59de90 TL |
448 | OptionTypeInfo info(_offset, OptionType::kVector, _verification, _flags); |
449 | info.SetParseFunc([elem_info, separator]( | |
450 | const ConfigOptions& opts, const std::string& name, | |
451 | const std::string& value, void* addr) { | |
452 | auto result = static_cast<std::vector<T>*>(addr); | |
453 | return ParseVector<T>(opts, elem_info, separator, name, value, result); | |
454 | }); | |
455 | info.SetSerializeFunc([elem_info, separator](const ConfigOptions& opts, | |
456 | const std::string& name, | |
457 | const void* addr, | |
458 | std::string* value) { | |
459 | const auto& vec = *(static_cast<const std::vector<T>*>(addr)); | |
460 | return SerializeVector<T>(opts, elem_info, separator, name, vec, value); | |
461 | }); | |
462 | info.SetEqualsFunc([elem_info](const ConfigOptions& opts, | |
463 | const std::string& name, const void* addr1, | |
464 | const void* addr2, std::string* mismatch) { | |
465 | const auto& vec1 = *(static_cast<const std::vector<T>*>(addr1)); | |
466 | const auto& vec2 = *(static_cast<const std::vector<T>*>(addr2)); | |
467 | return VectorsAreEqual<T>(opts, elem_info, name, vec1, vec2, mismatch); | |
468 | }); | |
469 | return info; | |
20effc67 TL |
470 | } |
471 | ||
472 | // Create a new std::shared_ptr<Customizable> OptionTypeInfo | |
473 | // This function will call the T::CreateFromString method to create a new | |
474 | // std::shared_ptr<T> object. | |
475 | // | |
476 | // @param offset The offset for the Customizable from the base pointer | |
477 | // @param ovt How to verify this option | |
478 | // @param flags, Extra flags specifying the behavior of this option | |
479 | // @param _sfunc Optional function for serializing this option | |
480 | // @param _efunc Optional function for comparing this option | |
481 | template <typename T> | |
482 | static OptionTypeInfo AsCustomSharedPtr(int offset, | |
483 | OptionVerificationType ovt, | |
484 | OptionTypeFlags flags) { | |
1e59de90 TL |
485 | OptionTypeInfo info(offset, OptionType::kCustomizable, ovt, |
486 | flags | OptionTypeFlags::kShared); | |
487 | return info.SetParseFunc([](const ConfigOptions& opts, | |
488 | const std::string& name, | |
489 | const std::string& value, void* addr) { | |
490 | auto* shared = static_cast<std::shared_ptr<T>*>(addr); | |
491 | if (name == kIdPropName() && value.empty()) { | |
492 | shared->reset(); | |
493 | return Status::OK(); | |
494 | } else { | |
495 | return T::CreateFromString(opts, value, shared); | |
496 | } | |
497 | }); | |
20effc67 TL |
498 | } |
499 | ||
500 | template <typename T> | |
501 | static OptionTypeInfo AsCustomSharedPtr(int offset, | |
502 | OptionVerificationType ovt, | |
503 | OptionTypeFlags flags, | |
504 | const SerializeFunc& serialize_func, | |
505 | const EqualsFunc& equals_func) { | |
1e59de90 TL |
506 | OptionTypeInfo info(AsCustomSharedPtr<T>(offset, ovt, flags)); |
507 | info.SetSerializeFunc(serialize_func); | |
508 | info.SetEqualsFunc(equals_func); | |
509 | return info; | |
20effc67 TL |
510 | } |
511 | ||
512 | // Create a new std::unique_ptr<Customizable> OptionTypeInfo | |
513 | // This function will call the T::CreateFromString method to create a new | |
514 | // std::unique_ptr<T> object. | |
515 | // | |
516 | // @param offset The offset for the Customizable from the base pointer | |
517 | // @param ovt How to verify this option | |
518 | // @param flags, Extra flags specifying the behavior of this option | |
519 | // @param _sfunc Optional function for serializing this option | |
520 | // @param _efunc Optional function for comparing this option | |
521 | template <typename T> | |
522 | static OptionTypeInfo AsCustomUniquePtr(int offset, | |
523 | OptionVerificationType ovt, | |
524 | OptionTypeFlags flags) { | |
1e59de90 TL |
525 | OptionTypeInfo info(offset, OptionType::kCustomizable, ovt, |
526 | flags | OptionTypeFlags::kUnique); | |
527 | return info.SetParseFunc([](const ConfigOptions& opts, | |
528 | const std::string& name, | |
529 | const std::string& value, void* addr) { | |
530 | auto* unique = static_cast<std::unique_ptr<T>*>(addr); | |
531 | if (name == kIdPropName() && value.empty()) { | |
532 | unique->reset(); | |
533 | return Status::OK(); | |
534 | } else { | |
535 | return T::CreateFromString(opts, value, unique); | |
536 | } | |
537 | }); | |
20effc67 TL |
538 | } |
539 | ||
540 | template <typename T> | |
541 | static OptionTypeInfo AsCustomUniquePtr(int offset, | |
542 | OptionVerificationType ovt, | |
543 | OptionTypeFlags flags, | |
544 | const SerializeFunc& serialize_func, | |
545 | const EqualsFunc& equals_func) { | |
1e59de90 TL |
546 | OptionTypeInfo info(AsCustomUniquePtr<T>(offset, ovt, flags)); |
547 | info.SetSerializeFunc(serialize_func); | |
548 | info.SetEqualsFunc(equals_func); | |
549 | return info; | |
20effc67 TL |
550 | } |
551 | ||
552 | // Create a new Customizable* OptionTypeInfo | |
553 | // This function will call the T::CreateFromString method to create a new | |
554 | // T object. | |
555 | // | |
556 | // @param _offset The offset for the Customizable from the base pointer | |
557 | // @param ovt How to verify this option | |
558 | // @param flags, Extra flags specifying the behavior of this option | |
559 | // @param _sfunc Optional function for serializing this option | |
560 | // @param _efunc Optional function for comparing this option | |
561 | template <typename T> | |
562 | static OptionTypeInfo AsCustomRawPtr(int offset, OptionVerificationType ovt, | |
563 | OptionTypeFlags flags) { | |
1e59de90 TL |
564 | OptionTypeInfo info(offset, OptionType::kCustomizable, ovt, |
565 | flags | OptionTypeFlags::kRawPointer); | |
566 | return info.SetParseFunc([](const ConfigOptions& opts, | |
567 | const std::string& name, | |
568 | const std::string& value, void* addr) { | |
569 | auto** pointer = static_cast<T**>(addr); | |
570 | if (name == kIdPropName() && value.empty()) { | |
571 | *pointer = nullptr; | |
572 | return Status::OK(); | |
573 | } else { | |
574 | return T::CreateFromString(opts, value, pointer); | |
575 | } | |
576 | }); | |
20effc67 TL |
577 | } |
578 | ||
579 | template <typename T> | |
580 | static OptionTypeInfo AsCustomRawPtr(int offset, OptionVerificationType ovt, | |
581 | OptionTypeFlags flags, | |
582 | const SerializeFunc& serialize_func, | |
583 | const EqualsFunc& equals_func) { | |
1e59de90 TL |
584 | OptionTypeInfo info(AsCustomRawPtr<T>(offset, ovt, flags)); |
585 | info.SetSerializeFunc(serialize_func); | |
586 | info.SetEqualsFunc(equals_func); | |
587 | return info; | |
588 | } | |
589 | ||
590 | OptionTypeInfo& SetParseFunc(const ParseFunc& f) { | |
591 | parse_func_ = f; | |
592 | return *this; | |
593 | } | |
594 | ||
595 | OptionTypeInfo& SetSerializeFunc(const SerializeFunc& f) { | |
596 | serialize_func_ = f; | |
597 | return *this; | |
598 | } | |
599 | OptionTypeInfo& SetEqualsFunc(const EqualsFunc& f) { | |
600 | equals_func_ = f; | |
601 | return *this; | |
602 | } | |
603 | ||
604 | OptionTypeInfo& SetPrepareFunc(const PrepareFunc& f) { | |
605 | prepare_func_ = f; | |
606 | return *this; | |
607 | } | |
608 | ||
609 | OptionTypeInfo& SetValidateFunc(const ValidateFunc& f) { | |
610 | validate_func_ = f; | |
611 | return *this; | |
20effc67 TL |
612 | } |
613 | ||
614 | bool IsEnabled(OptionTypeFlags otf) const { return (flags_ & otf) == otf; } | |
615 | ||
1e59de90 TL |
616 | bool IsEditable(const ConfigOptions& opts) const { |
617 | if (opts.mutable_options_only) { | |
618 | return IsMutable(); | |
619 | } else { | |
620 | return true; | |
621 | } | |
622 | } | |
20effc67 TL |
623 | bool IsMutable() const { return IsEnabled(OptionTypeFlags::kMutable); } |
624 | ||
625 | bool IsDeprecated() const { | |
626 | return IsEnabled(OptionVerificationType::kDeprecated); | |
627 | } | |
628 | ||
629 | // Returns true if the option is marked as an Alias. | |
630 | // Aliases are valid options that are parsed but are not converted to strings | |
631 | // or compared. | |
632 | bool IsAlias() const { return IsEnabled(OptionVerificationType::kAlias); } | |
633 | ||
634 | bool IsEnabled(OptionVerificationType ovf) const { | |
635 | return verification_ == ovf; | |
636 | } | |
637 | ||
638 | // Returns the sanity level for comparing the option. | |
639 | // If the options should not be compared, returns None | |
640 | // If the option has a compare flag, returns it. | |
641 | // Otherwise, returns "exact" | |
642 | ConfigOptions::SanityLevel GetSanityLevel() const { | |
643 | if (IsDeprecated() || IsAlias()) { | |
644 | return ConfigOptions::SanityLevel::kSanityLevelNone; | |
645 | } else { | |
646 | auto match = (flags_ & OptionTypeFlags::kCompareExact); | |
647 | if (match == OptionTypeFlags::kCompareDefault) { | |
648 | return ConfigOptions::SanityLevel::kSanityLevelExactMatch; | |
649 | } else { | |
650 | return (ConfigOptions::SanityLevel)match; | |
651 | } | |
652 | } | |
653 | } | |
654 | ||
655 | // Returns true if the option should be serialized. | |
656 | // Options should be serialized if the are not deprecated, aliases, | |
657 | // or marked as "Don't Serialize". | |
658 | bool ShouldSerialize() const { | |
659 | if (IsDeprecated() || IsAlias()) { | |
660 | return false; | |
661 | } else if (IsEnabled(OptionTypeFlags::kDontSerialize)) { | |
662 | return false; | |
663 | } else { | |
664 | return true; | |
665 | } | |
666 | } | |
667 | ||
1e59de90 TL |
668 | bool ShouldPrepare() const { |
669 | if (IsDeprecated() || IsAlias()) { | |
670 | return false; | |
671 | } else if (IsEnabled(OptionTypeFlags::kDontPrepare)) { | |
672 | return false; | |
673 | } else { | |
674 | return (prepare_func_ != nullptr || IsConfigurable()); | |
675 | } | |
676 | } | |
677 | ||
678 | bool ShouldValidate() const { | |
679 | if (IsDeprecated() || IsAlias()) { | |
680 | return false; | |
681 | } else { | |
682 | return (validate_func_ != nullptr || IsConfigurable()); | |
683 | } | |
684 | } | |
685 | ||
20effc67 TL |
686 | // Returns true if the option is allowed to be null. |
687 | // Options can be null if the verification type is allow from null | |
688 | // or if the flags specify allow null. | |
689 | bool CanBeNull() const { | |
690 | return (IsEnabled(OptionTypeFlags::kAllowNull) || | |
1e59de90 | 691 | IsEnabled(OptionVerificationType::kByNameAllowNull) || |
20effc67 TL |
692 | IsEnabled(OptionVerificationType::kByNameAllowFromNull)); |
693 | } | |
694 | ||
695 | bool IsSharedPtr() const { return IsEnabled(OptionTypeFlags::kShared); } | |
696 | ||
697 | bool IsUniquePtr() const { return IsEnabled(OptionTypeFlags::kUnique); } | |
698 | ||
699 | bool IsRawPtr() const { return IsEnabled(OptionTypeFlags::kRawPointer); } | |
700 | ||
701 | bool IsByName() const { | |
702 | return (verification_ == OptionVerificationType::kByName || | |
703 | verification_ == OptionVerificationType::kByNameAllowNull || | |
704 | verification_ == OptionVerificationType::kByNameAllowFromNull); | |
705 | } | |
706 | ||
707 | bool IsStruct() const { return (type_ == OptionType::kStruct); } | |
708 | ||
709 | bool IsConfigurable() const { | |
710 | return (type_ == OptionType::kConfigurable || | |
711 | type_ == OptionType::kCustomizable); | |
712 | } | |
713 | ||
714 | bool IsCustomizable() const { return (type_ == OptionType::kCustomizable); } | |
715 | ||
1e59de90 TL |
716 | inline const void* GetOffset(const void* base) const { |
717 | return static_cast<const char*>(base) + offset_; | |
718 | } | |
719 | ||
720 | inline void* GetOffset(void* base) const { | |
721 | return static_cast<char*>(base) + offset_; | |
722 | } | |
723 | ||
724 | template <typename T> | |
725 | const T* GetOffsetAs(const void* base) const { | |
726 | const void* addr = GetOffset(base); | |
727 | return static_cast<const T*>(addr); | |
728 | } | |
729 | ||
730 | template <typename T> | |
731 | T* GetOffsetAs(void* base) const { | |
732 | void* addr = GetOffset(base); | |
733 | return static_cast<T*>(addr); | |
734 | } | |
735 | ||
20effc67 TL |
736 | // Returns the underlying pointer for the type at base_addr |
737 | // The value returned is the underlying "raw" pointer, offset from base. | |
738 | template <typename T> | |
739 | const T* AsRawPointer(const void* const base_addr) const { | |
740 | if (base_addr == nullptr) { | |
741 | return nullptr; | |
742 | } | |
20effc67 | 743 | if (IsUniquePtr()) { |
1e59de90 | 744 | const auto ptr = GetOffsetAs<std::unique_ptr<T>>(base_addr); |
20effc67 TL |
745 | return ptr->get(); |
746 | } else if (IsSharedPtr()) { | |
1e59de90 | 747 | const auto ptr = GetOffsetAs<std::shared_ptr<T>>(base_addr); |
20effc67 TL |
748 | return ptr->get(); |
749 | } else if (IsRawPtr()) { | |
1e59de90 | 750 | const T* const* ptr = GetOffsetAs<T* const>(base_addr); |
20effc67 TL |
751 | return *ptr; |
752 | } else { | |
1e59de90 | 753 | return GetOffsetAs<T>(base_addr); |
20effc67 TL |
754 | } |
755 | } | |
756 | ||
757 | // Returns the underlying pointer for the type at base_addr | |
758 | // The value returned is the underlying "raw" pointer, offset from base. | |
759 | template <typename T> | |
760 | T* AsRawPointer(void* base_addr) const { | |
761 | if (base_addr == nullptr) { | |
762 | return nullptr; | |
763 | } | |
20effc67 | 764 | if (IsUniquePtr()) { |
1e59de90 | 765 | auto ptr = GetOffsetAs<std::unique_ptr<T>>(base_addr); |
20effc67 TL |
766 | return ptr->get(); |
767 | } else if (IsSharedPtr()) { | |
1e59de90 | 768 | auto ptr = GetOffsetAs<std::shared_ptr<T>>(base_addr); |
20effc67 TL |
769 | return ptr->get(); |
770 | } else if (IsRawPtr()) { | |
1e59de90 | 771 | auto ptr = GetOffsetAs<T*>(base_addr); |
20effc67 TL |
772 | return *ptr; |
773 | } else { | |
1e59de90 | 774 | return GetOffsetAs<T>(base_addr); |
20effc67 TL |
775 | } |
776 | } | |
777 | ||
778 | // Parses the option in "opt_value" according to the rules of this class | |
779 | // and updates the value at "opt_ptr". | |
780 | // On success, Status::OK() is returned. On failure: | |
781 | // NotFound means the opt_name is not valid for this option | |
782 | // NotSupported means we do not know how to parse the value for this option | |
783 | // InvalidArgument means the opt_value is not valid for this option. | |
784 | Status Parse(const ConfigOptions& config_options, const std::string& opt_name, | |
785 | const std::string& opt_value, void* const opt_ptr) const; | |
786 | ||
787 | // Serializes the option in "opt_addr" according to the rules of this class | |
788 | // into the value at "opt_value". | |
789 | Status Serialize(const ConfigOptions& config_options, | |
790 | const std::string& opt_name, const void* const opt_ptr, | |
791 | std::string* opt_value) const; | |
792 | ||
793 | // Compares the "addr1" and "addr2" values according to the rules of this | |
794 | // class and returns true if they match. On a failed match, mismatch is the | |
795 | // name of the option that failed to match. | |
796 | bool AreEqual(const ConfigOptions& config_options, | |
797 | const std::string& opt_name, const void* const addr1, | |
798 | const void* const addr2, std::string* mismatch) const; | |
799 | ||
800 | // Used to override the match rules for "ByName" options. | |
801 | bool AreEqualByName(const ConfigOptions& config_options, | |
802 | const std::string& opt_name, const void* const this_ptr, | |
803 | const void* const that_ptr) const; | |
804 | bool AreEqualByName(const ConfigOptions& config_options, | |
805 | const std::string& opt_name, const void* const this_ptr, | |
806 | const std::string& that_value) const; | |
807 | ||
1e59de90 TL |
808 | Status Prepare(const ConfigOptions& config_options, const std::string& name, |
809 | void* opt_ptr) const; | |
810 | Status Validate(const DBOptions& db_opts, const ColumnFamilyOptions& cf_opts, | |
811 | const std::string& name, const void* opt_ptr) const; | |
812 | ||
813 | // Parses the input opts_map according to the type_map for the opt_addr | |
814 | // For each name-value pair in opts_map, find the corresponding name in | |
815 | // type_map If the name is found: | |
816 | // - set the corresponding value in opt_addr, returning the status on | |
817 | // failure; | |
818 | // If the name is not found: | |
819 | // - If unused is specified, add the name-value to unused and continue | |
820 | // - If ingore_unknown_options is false, return NotFound | |
821 | // Returns OK if all options were either: | |
822 | // - Successfully set | |
823 | // - options were not found and ignore_unknown_options=true | |
824 | // - options were not found and unused was specified | |
825 | // Note that this method is much less sophisticated than the comparable | |
826 | // Configurable::Configure methods. For example, on error, there is no | |
827 | // attempt to return opt_addr to the initial state. Additionally, there | |
828 | // is no effort to initialize (Configurable::PrepareOptions) the object | |
829 | // on success. This method should typically only be used for simpler, | |
830 | // standalone structures and not those that contain shared and embedded | |
831 | // objects. | |
832 | static Status ParseType( | |
833 | const ConfigOptions& config_options, const std::string& opts_str, | |
834 | const std::unordered_map<std::string, OptionTypeInfo>& type_map, | |
835 | void* opt_addr, | |
836 | std::unordered_map<std::string, std::string>* unused = nullptr); | |
837 | static Status ParseType( | |
838 | const ConfigOptions& config_options, | |
839 | const std::unordered_map<std::string, std::string>& opts_map, | |
840 | const std::unordered_map<std::string, OptionTypeInfo>& type_map, | |
841 | void* opt_addr, | |
842 | std::unordered_map<std::string, std::string>* unused = nullptr); | |
843 | ||
20effc67 TL |
844 | // Parses the input value according to the map for the struct at opt_addr |
845 | // struct_name is the name of the struct option as registered | |
846 | // opt_name is the name of the option being evaluated. This may | |
847 | // be the whole struct or a sub-element of it, based on struct_name and | |
848 | // opt_name. | |
849 | static Status ParseStruct( | |
850 | const ConfigOptions& config_options, const std::string& struct_name, | |
851 | const std::unordered_map<std::string, OptionTypeInfo>* map, | |
1e59de90 TL |
852 | const std::string& opt_name, const std::string& value, void* opt_addr); |
853 | ||
854 | // Serializes the values from opt_addr using the rules in type_map. | |
855 | // Returns the serialized form in result. | |
856 | // Returns OK on success or non-OK if some option could not be serialized. | |
857 | static Status SerializeType( | |
858 | const ConfigOptions& config_options, | |
859 | const std::unordered_map<std::string, OptionTypeInfo>& type_map, | |
860 | const void* opt_addr, std::string* value); | |
20effc67 TL |
861 | |
862 | // Serializes the input addr according to the map for the struct to value. | |
863 | // struct_name is the name of the struct option as registered | |
864 | // opt_name is the name of the option being evaluated. This may | |
865 | // be the whole struct or a sub-element of it | |
866 | static Status SerializeStruct( | |
867 | const ConfigOptions& config_options, const std::string& struct_name, | |
868 | const std::unordered_map<std::string, OptionTypeInfo>* map, | |
1e59de90 TL |
869 | const std::string& opt_name, const void* opt_addr, std::string* value); |
870 | ||
871 | // Compares the values in this_addr and that_addr using the rules in type_map. | |
872 | // If the values are equal, returns true | |
873 | // If the values are not equal, returns false and sets mismatch to the name | |
874 | // of the first value that did not match. | |
875 | static bool TypesAreEqual( | |
876 | const ConfigOptions& config_options, | |
877 | const std::unordered_map<std::string, OptionTypeInfo>& map, | |
878 | const void* this_addr, const void* that_addr, std::string* mismatch); | |
20effc67 TL |
879 | |
880 | // Compares the input offsets according to the map for the struct and returns | |
881 | // true if they are equivalent, false otherwise. | |
882 | // struct_name is the name of the struct option as registered | |
883 | // opt_name is the name of the option being evaluated. This may | |
884 | // be the whole struct or a sub-element of it | |
885 | static bool StructsAreEqual( | |
886 | const ConfigOptions& config_options, const std::string& struct_name, | |
887 | const std::unordered_map<std::string, OptionTypeInfo>* map, | |
1e59de90 TL |
888 | const std::string& opt_name, const void* this_offset, |
889 | const void* that_offset, std::string* mismatch); | |
20effc67 TL |
890 | |
891 | // Finds the entry for the opt_name in the opt_map, returning | |
892 | // nullptr if not found. | |
893 | // If found, elem_name will be the name of option to find. | |
894 | // This may be opt_name, or a substring of opt_name. | |
895 | // For "simple" options, opt_name will be equal to elem_name. Given the | |
896 | // opt_name "opt", elem_name will equal "opt". | |
897 | // For "embedded" options (like structs), elem_name may be opt_name | |
898 | // or a field within the opt_name. For example, given the struct "struct", | |
899 | // and opt_name of "struct.field", elem_name will be "field" | |
900 | static const OptionTypeInfo* Find( | |
901 | const std::string& opt_name, | |
902 | const std::unordered_map<std::string, OptionTypeInfo>& opt_map, | |
903 | std::string* elem_name); | |
904 | ||
905 | // Returns the next token marked by the delimiter from "opts" after start in | |
906 | // token and updates end to point to where that token stops. Delimiters inside | |
907 | // of braces are ignored. Returns OK if a token is found and an error if the | |
908 | // input opts string is mis-formatted. | |
909 | // Given "a=AA;b=BB;" start=2 and delimiter=";", token is "AA" and end points | |
910 | // to "b" Given "{a=A;b=B}", the token would be "a=A;b=B" | |
911 | // | |
912 | // @param opts The string in which to find the next token | |
913 | // @param delimiter The delimiter between tokens | |
914 | // @param start The position in opts to start looking for the token | |
1e59de90 | 915 | // @param ed Returns the end position in opts of the token |
20effc67 TL |
916 | // @param token Returns the token |
917 | // @returns OK if a token was found | |
918 | // @return InvalidArgument if the braces mismatch | |
919 | // (e.g. "{a={b=c;}" ) -- missing closing brace | |
920 | // @return InvalidArgument if an expected delimiter is not found | |
921 | // e.g. "{a=b}c=d;" -- missing delimiter before "c" | |
922 | static Status NextToken(const std::string& opts, char delimiter, size_t start, | |
923 | size_t* end, std::string* token); | |
924 | ||
1e59de90 TL |
925 | constexpr static const char* kIdPropName() { return "id"; } |
926 | constexpr static const char* kIdPropSuffix() { return ".id"; } | |
927 | ||
20effc67 TL |
928 | private: |
929 | int offset_; | |
930 | ||
931 | // The optional function to convert a string to its representation | |
932 | ParseFunc parse_func_; | |
933 | ||
934 | // The optional function to convert a value to its string representation | |
935 | SerializeFunc serialize_func_; | |
936 | ||
937 | // The optional function to match two option values | |
938 | EqualsFunc equals_func_; | |
939 | ||
1e59de90 TL |
940 | PrepareFunc prepare_func_; |
941 | ValidateFunc validate_func_; | |
20effc67 TL |
942 | OptionType type_; |
943 | OptionVerificationType verification_; | |
944 | OptionTypeFlags flags_; | |
945 | }; | |
946 | ||
1e59de90 TL |
947 | // Parses the input value into elements of the result array, which has fixed |
948 | // array size. For example, if the value=1:2:3 and elem_info parses integers, | |
949 | // the result array will be {1,2,3}. Array size is defined in the OptionTypeInfo | |
950 | // the input value has to match with that. | |
951 | // @param config_options Controls how the option value is parsed. | |
952 | // @param elem_info Controls how individual tokens in value are parsed | |
953 | // @param separator Character separating tokens in values (':' in the above | |
954 | // example) | |
955 | // @param name The name associated with this array option | |
956 | // @param value The input string to parse into tokens | |
957 | // @param result Returns the results of parsing value into its elements. | |
958 | // @return OK if the value was successfully parse | |
959 | // @return InvalidArgument if the value is improperly formed or element number | |
960 | // doesn't match array size defined in OptionTypeInfo | |
961 | // or if the token could not be parsed | |
962 | // @return NotFound If the tokenized value contains unknown options for | |
963 | // its type | |
964 | template <typename T, size_t kSize> | |
965 | Status ParseArray(const ConfigOptions& config_options, | |
966 | const OptionTypeInfo& elem_info, char separator, | |
967 | const std::string& name, const std::string& value, | |
968 | std::array<T, kSize>* result) { | |
969 | Status status; | |
970 | ||
971 | ConfigOptions copy = config_options; | |
972 | copy.ignore_unsupported_options = false; | |
973 | size_t i = 0, start = 0, end = 0; | |
974 | for (; status.ok() && i < kSize && start < value.size() && | |
975 | end != std::string::npos; | |
976 | i++, start = end + 1) { | |
977 | std::string token; | |
978 | status = OptionTypeInfo::NextToken(value, separator, start, &end, &token); | |
979 | if (status.ok()) { | |
980 | status = elem_info.Parse(copy, name, token, &((*result)[i])); | |
981 | if (config_options.ignore_unsupported_options && | |
982 | status.IsNotSupported()) { | |
983 | // If we were ignoring unsupported options and this one should be | |
984 | // ignored, ignore it by setting the status to OK | |
985 | status = Status::OK(); | |
986 | } | |
987 | } | |
988 | } | |
989 | if (!status.ok()) { | |
990 | return status; | |
991 | } | |
992 | // make sure the element number matches the array size | |
993 | if (i < kSize) { | |
994 | return Status::InvalidArgument( | |
995 | "Serialized value has less elements than array size", name); | |
996 | } | |
997 | if (start < value.size() && end != std::string::npos) { | |
998 | return Status::InvalidArgument( | |
999 | "Serialized value has more elements than array size", name); | |
1000 | } | |
1001 | return status; | |
1002 | } | |
1003 | ||
1004 | // Serializes the fixed size input array into its output value. Elements are | |
1005 | // separated by the separator character. This element will convert all of the | |
1006 | // elements in array into their serialized form, using elem_info to perform the | |
1007 | // serialization. | |
1008 | // For example, if the array contains the integers 1,2,3 and elem_info | |
1009 | // serializes the output would be 1:2:3 for separator ":". | |
1010 | // @param config_options Controls how the option value is serialized. | |
1011 | // @param elem_info Controls how individual tokens in value are serialized | |
1012 | // @param separator Character separating tokens in value (':' in the above | |
1013 | // example) | |
1014 | // @param name The name associated with this array option | |
1015 | // @param array The input array to serialize | |
1016 | // @param value The output string of serialized options | |
1017 | // @return OK if the value was successfully parse | |
1018 | // @return InvalidArgument if the value is improperly formed or if the token | |
1019 | // could not be parsed | |
1020 | // @return NotFound If the tokenized value contains unknown options for | |
1021 | // its type | |
1022 | template <typename T, size_t kSize> | |
1023 | Status SerializeArray(const ConfigOptions& config_options, | |
1024 | const OptionTypeInfo& elem_info, char separator, | |
1025 | const std::string& name, | |
1026 | const std::array<T, kSize>& array, std::string* value) { | |
1027 | std::string result; | |
1028 | ConfigOptions embedded = config_options; | |
1029 | embedded.delimiter = ";"; | |
1030 | int printed = 0; | |
1031 | for (const auto& elem : array) { | |
1032 | std::string elem_str; | |
1033 | Status s = elem_info.Serialize(embedded, name, &elem, &elem_str); | |
1034 | if (!s.ok()) { | |
1035 | return s; | |
1036 | } else if (!elem_str.empty()) { | |
1037 | if (printed++ > 0) { | |
1038 | result += separator; | |
1039 | } | |
1040 | // If the element contains embedded separators, put it inside of brackets | |
1041 | if (elem_str.find(separator) != std::string::npos) { | |
1042 | result += "{" + elem_str + "}"; | |
1043 | } else { | |
1044 | result += elem_str; | |
1045 | } | |
1046 | } | |
1047 | } | |
1048 | if (result.find("=") != std::string::npos) { | |
1049 | *value = "{" + result + "}"; | |
1050 | } else if (printed > 1 && result.at(0) == '{') { | |
1051 | *value = "{" + result + "}"; | |
1052 | } else { | |
1053 | *value = result; | |
1054 | } | |
1055 | return Status::OK(); | |
1056 | } | |
1057 | ||
1058 | // Compares the input arrays array1 and array2 for equality | |
1059 | // Elements of the array are compared one by one using elem_info to perform the | |
1060 | // comparison. | |
1061 | // | |
1062 | // @param config_options Controls how the arrays are compared. | |
1063 | // @param elem_info Controls how individual elements in the arrays are compared | |
1064 | // @param name The name associated with this array option | |
1065 | // @param array1,array2 The arrays to compare. | |
1066 | // @param mismatch If the arrays are not equivalent, mismatch will point to | |
1067 | // the first element of the comparison that did not match. | |
1068 | // @return true If vec1 and vec2 are "equal", false otherwise | |
1069 | template <typename T, size_t kSize> | |
1070 | bool ArraysAreEqual(const ConfigOptions& config_options, | |
1071 | const OptionTypeInfo& elem_info, const std::string& name, | |
1072 | const std::array<T, kSize>& array1, | |
1073 | const std::array<T, kSize>& array2, std::string* mismatch) { | |
1074 | assert(array1.size() == kSize); | |
1075 | assert(array2.size() == kSize); | |
1076 | for (size_t i = 0; i < kSize; ++i) { | |
1077 | if (!elem_info.AreEqual(config_options, name, &array1[i], &array2[i], | |
1078 | mismatch)) { | |
1079 | return false; | |
1080 | } | |
1081 | } | |
1082 | return true; | |
1083 | } | |
1084 | ||
20effc67 TL |
1085 | // Parses the input value into elements of the result vector. This method |
1086 | // will break the input value into the individual tokens (based on the | |
1087 | // separator), where each of those tokens will be parsed based on the rules of | |
1088 | // elem_info. The result vector will be populated with elements based on the | |
1089 | // input tokens. For example, if the value=1:2:3:4:5 and elem_info parses | |
1090 | // integers, the result vector will contain the integers 1,2,3,4,5 | |
1091 | // @param config_options Controls how the option value is parsed. | |
1092 | // @param elem_info Controls how individual tokens in value are parsed | |
1093 | // @param separator Character separating tokens in values (':' in the above | |
1094 | // example) | |
1095 | // @param name The name associated with this vector option | |
1096 | // @param value The input string to parse into tokens | |
1097 | // @param result Returns the results of parsing value into its elements. | |
1098 | // @return OK if the value was successfully parse | |
1099 | // @return InvalidArgument if the value is improperly formed or if the token | |
1100 | // could not be parsed | |
1101 | // @return NotFound If the tokenized value contains unknown options for | |
1102 | // its type | |
1103 | template <typename T> | |
1104 | Status ParseVector(const ConfigOptions& config_options, | |
1105 | const OptionTypeInfo& elem_info, char separator, | |
1106 | const std::string& name, const std::string& value, | |
1107 | std::vector<T>* result) { | |
1108 | result->clear(); | |
1109 | Status status; | |
1110 | ||
1111 | // Turn off ignore_unknown_objects so we can tell if the returned | |
1112 | // object is valid or not. | |
1113 | ConfigOptions copy = config_options; | |
1114 | copy.ignore_unsupported_options = false; | |
1115 | for (size_t start = 0, end = 0; | |
1116 | status.ok() && start < value.size() && end != std::string::npos; | |
1117 | start = end + 1) { | |
1118 | std::string token; | |
1119 | status = OptionTypeInfo::NextToken(value, separator, start, &end, &token); | |
1120 | if (status.ok()) { | |
1121 | T elem; | |
1e59de90 | 1122 | status = elem_info.Parse(copy, name, token, &elem); |
20effc67 TL |
1123 | if (status.ok()) { |
1124 | result->emplace_back(elem); | |
1125 | } else if (config_options.ignore_unsupported_options && | |
1126 | status.IsNotSupported()) { | |
1127 | // If we were ignoring unsupported options and this one should be | |
1128 | // ignored, ignore it by setting the status to OK | |
1129 | status = Status::OK(); | |
1130 | } | |
1131 | } | |
1132 | } | |
1133 | return status; | |
1134 | } | |
1135 | ||
1136 | // Serializes the input vector into its output value. Elements are | |
1137 | // separated by the separator character. This element will convert all of the | |
1138 | // elements in vec into their serialized form, using elem_info to perform the | |
1139 | // serialization. | |
1140 | // For example, if the vec contains the integers 1,2,3,4,5 and elem_info | |
1141 | // serializes the output would be 1:2:3:4:5 for separator ":". | |
1142 | // @param config_options Controls how the option value is serialized. | |
1143 | // @param elem_info Controls how individual tokens in value are serialized | |
1144 | // @param separator Character separating tokens in value (':' in the above | |
1145 | // example) | |
1146 | // @param name The name associated with this vector option | |
1147 | // @param vec The input vector to serialize | |
1148 | // @param value The output string of serialized options | |
1149 | // @return OK if the value was successfully parse | |
1150 | // @return InvalidArgument if the value is improperly formed or if the token | |
1151 | // could not be parsed | |
1152 | // @return NotFound If the tokenized value contains unknown options for | |
1153 | // its type | |
1154 | template <typename T> | |
1155 | Status SerializeVector(const ConfigOptions& config_options, | |
1156 | const OptionTypeInfo& elem_info, char separator, | |
1157 | const std::string& name, const std::vector<T>& vec, | |
1158 | std::string* value) { | |
1159 | std::string result; | |
1160 | ConfigOptions embedded = config_options; | |
1161 | embedded.delimiter = ";"; | |
1e59de90 TL |
1162 | int printed = 0; |
1163 | for (const auto& elem : vec) { | |
20effc67 | 1164 | std::string elem_str; |
1e59de90 | 1165 | Status s = elem_info.Serialize(embedded, name, &elem, &elem_str); |
20effc67 TL |
1166 | if (!s.ok()) { |
1167 | return s; | |
1e59de90 TL |
1168 | } else if (!elem_str.empty()) { |
1169 | if (printed++ > 0) { | |
20effc67 TL |
1170 | result += separator; |
1171 | } | |
1172 | // If the element contains embedded separators, put it inside of brackets | |
1e59de90 | 1173 | if (elem_str.find(separator) != std::string::npos) { |
20effc67 TL |
1174 | result += "{" + elem_str + "}"; |
1175 | } else { | |
1176 | result += elem_str; | |
1177 | } | |
1178 | } | |
1179 | } | |
1180 | if (result.find("=") != std::string::npos) { | |
1181 | *value = "{" + result + "}"; | |
1e59de90 TL |
1182 | } else if (printed > 1 && result.at(0) == '{') { |
1183 | *value = "{" + result + "}"; | |
20effc67 TL |
1184 | } else { |
1185 | *value = result; | |
1186 | } | |
1187 | return Status::OK(); | |
1188 | } | |
1189 | ||
1190 | // Compares the input vectors vec1 and vec2 for equality | |
1191 | // If the vectors are the same size, elements of the vectors are compared one by | |
1192 | // one using elem_info to perform the comparison. | |
1193 | // | |
1194 | // @param config_options Controls how the vectors are compared. | |
1195 | // @param elem_info Controls how individual elements in the vectors are compared | |
1196 | // @param name The name associated with this vector option | |
1197 | // @param vec1,vec2 The vectors to compare. | |
1198 | // @param mismatch If the vectors are not equivalent, mismatch will point to | |
1199 | // the first | |
1e59de90 | 1200 | // element of the comparison that did not match. |
20effc67 TL |
1201 | // @return true If vec1 and vec2 are "equal", false otherwise |
1202 | template <typename T> | |
1203 | bool VectorsAreEqual(const ConfigOptions& config_options, | |
1204 | const OptionTypeInfo& elem_info, const std::string& name, | |
1205 | const std::vector<T>& vec1, const std::vector<T>& vec2, | |
1206 | std::string* mismatch) { | |
1207 | if (vec1.size() != vec2.size()) { | |
1208 | *mismatch = name; | |
1209 | return false; | |
1210 | } else { | |
1211 | for (size_t i = 0; i < vec1.size(); ++i) { | |
1212 | if (!elem_info.AreEqual( | |
1213 | config_options, name, reinterpret_cast<const char*>(&vec1[i]), | |
1214 | reinterpret_cast<const char*>(&vec2[i]), mismatch)) { | |
1215 | return false; | |
1216 | } | |
1217 | } | |
1218 | return true; | |
1219 | } | |
1220 | } | |
1221 | } // namespace ROCKSDB_NAMESPACE |