]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/options/options_helper.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / options / options_helper.cc
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 #include "options/options_helper.h"
6
7 #include <cassert>
8 #include <cctype>
9 #include <cstdlib>
10 #include <unordered_set>
11 #include <vector>
12
13 #include "options/cf_options.h"
14 #include "options/db_options.h"
15 #include "rocksdb/cache.h"
16 #include "rocksdb/compaction_filter.h"
17 #include "rocksdb/convenience.h"
18 #include "rocksdb/filter_policy.h"
19 #include "rocksdb/flush_block_policy.h"
20 #include "rocksdb/memtablerep.h"
21 #include "rocksdb/merge_operator.h"
22 #include "rocksdb/options.h"
23 #include "rocksdb/rate_limiter.h"
24 #include "rocksdb/slice_transform.h"
25 #include "rocksdb/table.h"
26 #include "rocksdb/utilities/object_registry.h"
27 #include "rocksdb/utilities/options_type.h"
28 #include "util/string_util.h"
29
30 namespace ROCKSDB_NAMESPACE {
31 Status ValidateOptions(const DBOptions& db_opts,
32 const ColumnFamilyOptions& cf_opts) {
33 Status s;
34 #ifndef ROCKSDB_LITE
35 auto db_cfg = DBOptionsAsConfigurable(db_opts);
36 auto cf_cfg = CFOptionsAsConfigurable(cf_opts);
37 s = db_cfg->ValidateOptions(db_opts, cf_opts);
38 if (s.ok()) s = cf_cfg->ValidateOptions(db_opts, cf_opts);
39 #else
40 s = cf_opts.table_factory->ValidateOptions(db_opts, cf_opts);
41 #endif
42 return s;
43 }
44
45 DBOptions BuildDBOptions(const ImmutableDBOptions& immutable_db_options,
46 const MutableDBOptions& mutable_db_options) {
47 DBOptions options;
48
49 options.create_if_missing = immutable_db_options.create_if_missing;
50 options.create_missing_column_families =
51 immutable_db_options.create_missing_column_families;
52 options.error_if_exists = immutable_db_options.error_if_exists;
53 options.paranoid_checks = immutable_db_options.paranoid_checks;
54 options.track_and_verify_wals_in_manifest =
55 immutable_db_options.track_and_verify_wals_in_manifest;
56 options.env = immutable_db_options.env;
57 options.rate_limiter = immutable_db_options.rate_limiter;
58 options.sst_file_manager = immutable_db_options.sst_file_manager;
59 options.info_log = immutable_db_options.info_log;
60 options.info_log_level = immutable_db_options.info_log_level;
61 options.max_open_files = mutable_db_options.max_open_files;
62 options.max_file_opening_threads =
63 immutable_db_options.max_file_opening_threads;
64 options.max_total_wal_size = mutable_db_options.max_total_wal_size;
65 options.statistics = immutable_db_options.statistics;
66 options.use_fsync = immutable_db_options.use_fsync;
67 options.db_paths = immutable_db_options.db_paths;
68 options.db_log_dir = immutable_db_options.db_log_dir;
69 options.wal_dir = immutable_db_options.wal_dir;
70 options.delete_obsolete_files_period_micros =
71 mutable_db_options.delete_obsolete_files_period_micros;
72 options.max_background_jobs = mutable_db_options.max_background_jobs;
73 options.base_background_compactions =
74 mutable_db_options.base_background_compactions;
75 options.max_background_compactions =
76 mutable_db_options.max_background_compactions;
77 options.bytes_per_sync = mutable_db_options.bytes_per_sync;
78 options.wal_bytes_per_sync = mutable_db_options.wal_bytes_per_sync;
79 options.strict_bytes_per_sync = mutable_db_options.strict_bytes_per_sync;
80 options.max_subcompactions = mutable_db_options.max_subcompactions;
81 options.max_background_flushes = mutable_db_options.max_background_flushes;
82 options.max_log_file_size = immutable_db_options.max_log_file_size;
83 options.log_file_time_to_roll = immutable_db_options.log_file_time_to_roll;
84 options.keep_log_file_num = immutable_db_options.keep_log_file_num;
85 options.recycle_log_file_num = immutable_db_options.recycle_log_file_num;
86 options.max_manifest_file_size = immutable_db_options.max_manifest_file_size;
87 options.table_cache_numshardbits =
88 immutable_db_options.table_cache_numshardbits;
89 options.WAL_ttl_seconds = immutable_db_options.wal_ttl_seconds;
90 options.WAL_size_limit_MB = immutable_db_options.wal_size_limit_mb;
91 options.manifest_preallocation_size =
92 immutable_db_options.manifest_preallocation_size;
93 options.allow_mmap_reads = immutable_db_options.allow_mmap_reads;
94 options.allow_mmap_writes = immutable_db_options.allow_mmap_writes;
95 options.use_direct_reads = immutable_db_options.use_direct_reads;
96 options.use_direct_io_for_flush_and_compaction =
97 immutable_db_options.use_direct_io_for_flush_and_compaction;
98 options.allow_fallocate = immutable_db_options.allow_fallocate;
99 options.is_fd_close_on_exec = immutable_db_options.is_fd_close_on_exec;
100 options.stats_dump_period_sec = mutable_db_options.stats_dump_period_sec;
101 options.stats_persist_period_sec =
102 mutable_db_options.stats_persist_period_sec;
103 options.persist_stats_to_disk = immutable_db_options.persist_stats_to_disk;
104 options.stats_history_buffer_size =
105 mutable_db_options.stats_history_buffer_size;
106 options.advise_random_on_open = immutable_db_options.advise_random_on_open;
107 options.db_write_buffer_size = immutable_db_options.db_write_buffer_size;
108 options.write_buffer_manager = immutable_db_options.write_buffer_manager;
109 options.access_hint_on_compaction_start =
110 immutable_db_options.access_hint_on_compaction_start;
111 options.new_table_reader_for_compaction_inputs =
112 immutable_db_options.new_table_reader_for_compaction_inputs;
113 options.compaction_readahead_size =
114 mutable_db_options.compaction_readahead_size;
115 options.random_access_max_buffer_size =
116 immutable_db_options.random_access_max_buffer_size;
117 options.writable_file_max_buffer_size =
118 mutable_db_options.writable_file_max_buffer_size;
119 options.use_adaptive_mutex = immutable_db_options.use_adaptive_mutex;
120 options.listeners = immutable_db_options.listeners;
121 options.enable_thread_tracking = immutable_db_options.enable_thread_tracking;
122 options.delayed_write_rate = mutable_db_options.delayed_write_rate;
123 options.enable_pipelined_write = immutable_db_options.enable_pipelined_write;
124 options.unordered_write = immutable_db_options.unordered_write;
125 options.allow_concurrent_memtable_write =
126 immutable_db_options.allow_concurrent_memtable_write;
127 options.enable_write_thread_adaptive_yield =
128 immutable_db_options.enable_write_thread_adaptive_yield;
129 options.max_write_batch_group_size_bytes =
130 immutable_db_options.max_write_batch_group_size_bytes;
131 options.write_thread_max_yield_usec =
132 immutable_db_options.write_thread_max_yield_usec;
133 options.write_thread_slow_yield_usec =
134 immutable_db_options.write_thread_slow_yield_usec;
135 options.skip_stats_update_on_db_open =
136 immutable_db_options.skip_stats_update_on_db_open;
137 options.skip_checking_sst_file_sizes_on_db_open =
138 immutable_db_options.skip_checking_sst_file_sizes_on_db_open;
139 options.wal_recovery_mode = immutable_db_options.wal_recovery_mode;
140 options.allow_2pc = immutable_db_options.allow_2pc;
141 options.row_cache = immutable_db_options.row_cache;
142 #ifndef ROCKSDB_LITE
143 options.wal_filter = immutable_db_options.wal_filter;
144 #endif // ROCKSDB_LITE
145 options.fail_if_options_file_error =
146 immutable_db_options.fail_if_options_file_error;
147 options.dump_malloc_stats = immutable_db_options.dump_malloc_stats;
148 options.avoid_flush_during_recovery =
149 immutable_db_options.avoid_flush_during_recovery;
150 options.avoid_flush_during_shutdown =
151 mutable_db_options.avoid_flush_during_shutdown;
152 options.allow_ingest_behind =
153 immutable_db_options.allow_ingest_behind;
154 options.preserve_deletes =
155 immutable_db_options.preserve_deletes;
156 options.two_write_queues = immutable_db_options.two_write_queues;
157 options.manual_wal_flush = immutable_db_options.manual_wal_flush;
158 options.atomic_flush = immutable_db_options.atomic_flush;
159 options.avoid_unnecessary_blocking_io =
160 immutable_db_options.avoid_unnecessary_blocking_io;
161 options.log_readahead_size = immutable_db_options.log_readahead_size;
162 options.file_checksum_gen_factory =
163 immutable_db_options.file_checksum_gen_factory;
164 options.best_efforts_recovery = immutable_db_options.best_efforts_recovery;
165 options.max_bgerror_resume_count =
166 immutable_db_options.max_bgerror_resume_count;
167 options.bgerror_resume_retry_interval =
168 immutable_db_options.bgerror_resume_retry_interval;
169 options.db_host_id = immutable_db_options.db_host_id;
170 options.allow_data_in_errors = immutable_db_options.allow_data_in_errors;
171 return options;
172 }
173
174 ColumnFamilyOptions BuildColumnFamilyOptions(
175 const ColumnFamilyOptions& options,
176 const MutableCFOptions& mutable_cf_options) {
177 ColumnFamilyOptions cf_opts(options);
178
179 // Memtable related options
180 cf_opts.write_buffer_size = mutable_cf_options.write_buffer_size;
181 cf_opts.max_write_buffer_number = mutable_cf_options.max_write_buffer_number;
182 cf_opts.arena_block_size = mutable_cf_options.arena_block_size;
183 cf_opts.memtable_prefix_bloom_size_ratio =
184 mutable_cf_options.memtable_prefix_bloom_size_ratio;
185 cf_opts.memtable_whole_key_filtering =
186 mutable_cf_options.memtable_whole_key_filtering;
187 cf_opts.memtable_huge_page_size = mutable_cf_options.memtable_huge_page_size;
188 cf_opts.max_successive_merges = mutable_cf_options.max_successive_merges;
189 cf_opts.inplace_update_num_locks =
190 mutable_cf_options.inplace_update_num_locks;
191 cf_opts.prefix_extractor = mutable_cf_options.prefix_extractor;
192
193 // Compaction related options
194 cf_opts.disable_auto_compactions =
195 mutable_cf_options.disable_auto_compactions;
196 cf_opts.soft_pending_compaction_bytes_limit =
197 mutable_cf_options.soft_pending_compaction_bytes_limit;
198 cf_opts.hard_pending_compaction_bytes_limit =
199 mutable_cf_options.hard_pending_compaction_bytes_limit;
200 cf_opts.level0_file_num_compaction_trigger =
201 mutable_cf_options.level0_file_num_compaction_trigger;
202 cf_opts.level0_slowdown_writes_trigger =
203 mutable_cf_options.level0_slowdown_writes_trigger;
204 cf_opts.level0_stop_writes_trigger =
205 mutable_cf_options.level0_stop_writes_trigger;
206 cf_opts.max_compaction_bytes = mutable_cf_options.max_compaction_bytes;
207 cf_opts.target_file_size_base = mutable_cf_options.target_file_size_base;
208 cf_opts.target_file_size_multiplier =
209 mutable_cf_options.target_file_size_multiplier;
210 cf_opts.max_bytes_for_level_base =
211 mutable_cf_options.max_bytes_for_level_base;
212 cf_opts.max_bytes_for_level_multiplier =
213 mutable_cf_options.max_bytes_for_level_multiplier;
214 cf_opts.ttl = mutable_cf_options.ttl;
215 cf_opts.periodic_compaction_seconds =
216 mutable_cf_options.periodic_compaction_seconds;
217
218 cf_opts.max_bytes_for_level_multiplier_additional.clear();
219 for (auto value :
220 mutable_cf_options.max_bytes_for_level_multiplier_additional) {
221 cf_opts.max_bytes_for_level_multiplier_additional.emplace_back(value);
222 }
223
224 cf_opts.compaction_options_fifo = mutable_cf_options.compaction_options_fifo;
225 cf_opts.compaction_options_universal =
226 mutable_cf_options.compaction_options_universal;
227
228 // Blob file related options
229 cf_opts.enable_blob_files = mutable_cf_options.enable_blob_files;
230 cf_opts.min_blob_size = mutable_cf_options.min_blob_size;
231 cf_opts.blob_file_size = mutable_cf_options.blob_file_size;
232 cf_opts.blob_compression_type = mutable_cf_options.blob_compression_type;
233 cf_opts.enable_blob_garbage_collection =
234 mutable_cf_options.enable_blob_garbage_collection;
235 cf_opts.blob_garbage_collection_age_cutoff =
236 mutable_cf_options.blob_garbage_collection_age_cutoff;
237
238 // Misc options
239 cf_opts.max_sequential_skip_in_iterations =
240 mutable_cf_options.max_sequential_skip_in_iterations;
241 cf_opts.check_flush_compaction_key_order =
242 mutable_cf_options.check_flush_compaction_key_order;
243 cf_opts.paranoid_file_checks = mutable_cf_options.paranoid_file_checks;
244 cf_opts.report_bg_io_stats = mutable_cf_options.report_bg_io_stats;
245 cf_opts.compression = mutable_cf_options.compression;
246 cf_opts.compression_opts = mutable_cf_options.compression_opts;
247 cf_opts.bottommost_compression = mutable_cf_options.bottommost_compression;
248 cf_opts.bottommost_compression_opts =
249 mutable_cf_options.bottommost_compression_opts;
250 cf_opts.sample_for_compression = mutable_cf_options.sample_for_compression;
251
252 cf_opts.table_factory = options.table_factory;
253 // TODO(yhchiang): find some way to handle the following derived options
254 // * max_file_size
255
256 return cf_opts;
257 }
258
259 std::map<CompactionStyle, std::string>
260 OptionsHelper::compaction_style_to_string = {
261 {kCompactionStyleLevel, "kCompactionStyleLevel"},
262 {kCompactionStyleUniversal, "kCompactionStyleUniversal"},
263 {kCompactionStyleFIFO, "kCompactionStyleFIFO"},
264 {kCompactionStyleNone, "kCompactionStyleNone"}};
265
266 std::map<CompactionPri, std::string> OptionsHelper::compaction_pri_to_string = {
267 {kByCompensatedSize, "kByCompensatedSize"},
268 {kOldestLargestSeqFirst, "kOldestLargestSeqFirst"},
269 {kOldestSmallestSeqFirst, "kOldestSmallestSeqFirst"},
270 {kMinOverlappingRatio, "kMinOverlappingRatio"}};
271
272 std::map<CompactionStopStyle, std::string>
273 OptionsHelper::compaction_stop_style_to_string = {
274 {kCompactionStopStyleSimilarSize, "kCompactionStopStyleSimilarSize"},
275 {kCompactionStopStyleTotalSize, "kCompactionStopStyleTotalSize"}};
276
277 std::unordered_map<std::string, ChecksumType>
278 OptionsHelper::checksum_type_string_map = {{"kNoChecksum", kNoChecksum},
279 {"kCRC32c", kCRC32c},
280 {"kxxHash", kxxHash},
281 {"kxxHash64", kxxHash64}};
282
283 std::unordered_map<std::string, CompressionType>
284 OptionsHelper::compression_type_string_map = {
285 {"kNoCompression", kNoCompression},
286 {"kSnappyCompression", kSnappyCompression},
287 {"kZlibCompression", kZlibCompression},
288 {"kBZip2Compression", kBZip2Compression},
289 {"kLZ4Compression", kLZ4Compression},
290 {"kLZ4HCCompression", kLZ4HCCompression},
291 {"kXpressCompression", kXpressCompression},
292 {"kZSTD", kZSTD},
293 {"kZSTDNotFinalCompression", kZSTDNotFinalCompression},
294 {"kDisableCompressionOption", kDisableCompressionOption}};
295
296 std::vector<CompressionType> GetSupportedCompressions() {
297 std::vector<CompressionType> supported_compressions;
298 for (const auto& comp_to_name : OptionsHelper::compression_type_string_map) {
299 CompressionType t = comp_to_name.second;
300 if (t != kDisableCompressionOption && CompressionTypeSupported(t)) {
301 supported_compressions.push_back(t);
302 }
303 }
304 return supported_compressions;
305 }
306
307 std::vector<CompressionType> GetSupportedDictCompressions() {
308 std::vector<CompressionType> dict_compression_types;
309 for (const auto& comp_to_name : OptionsHelper::compression_type_string_map) {
310 CompressionType t = comp_to_name.second;
311 if (t != kDisableCompressionOption && DictCompressionTypeSupported(t)) {
312 dict_compression_types.push_back(t);
313 }
314 }
315 return dict_compression_types;
316 }
317
318 #ifndef ROCKSDB_LITE
319 bool ParseSliceTransformHelper(
320 const std::string& kFixedPrefixName, const std::string& kCappedPrefixName,
321 const std::string& value,
322 std::shared_ptr<const SliceTransform>* slice_transform) {
323 const char* no_op_name = "rocksdb.Noop";
324 size_t no_op_length = strlen(no_op_name);
325 auto& pe_value = value;
326 if (pe_value.size() > kFixedPrefixName.size() &&
327 pe_value.compare(0, kFixedPrefixName.size(), kFixedPrefixName) == 0) {
328 int prefix_length = ParseInt(trim(value.substr(kFixedPrefixName.size())));
329 slice_transform->reset(NewFixedPrefixTransform(prefix_length));
330 } else if (pe_value.size() > kCappedPrefixName.size() &&
331 pe_value.compare(0, kCappedPrefixName.size(), kCappedPrefixName) ==
332 0) {
333 int prefix_length =
334 ParseInt(trim(pe_value.substr(kCappedPrefixName.size())));
335 slice_transform->reset(NewCappedPrefixTransform(prefix_length));
336 } else if (pe_value.size() == no_op_length &&
337 pe_value.compare(0, no_op_length, no_op_name) == 0) {
338 const SliceTransform* no_op_transform = NewNoopTransform();
339 slice_transform->reset(no_op_transform);
340 } else if (value == kNullptrString) {
341 slice_transform->reset();
342 } else {
343 return false;
344 }
345
346 return true;
347 }
348
349 bool ParseSliceTransform(
350 const std::string& value,
351 std::shared_ptr<const SliceTransform>* slice_transform) {
352 // While we normally don't convert the string representation of a
353 // pointer-typed option into its instance, here we do so for backward
354 // compatibility as we allow this action in SetOption().
355
356 // TODO(yhchiang): A possible better place for these serialization /
357 // deserialization is inside the class definition of pointer-typed
358 // option itself, but this requires a bigger change of public API.
359 bool result =
360 ParseSliceTransformHelper("fixed:", "capped:", value, slice_transform);
361 if (result) {
362 return result;
363 }
364 result = ParseSliceTransformHelper(
365 "rocksdb.FixedPrefix.", "rocksdb.CappedPrefix.", value, slice_transform);
366 if (result) {
367 return result;
368 }
369 // TODO(yhchiang): we can further support other default
370 // SliceTransforms here.
371 return false;
372 }
373
374 static bool ParseOptionHelper(char* opt_address, const OptionType& opt_type,
375 const std::string& value) {
376 switch (opt_type) {
377 case OptionType::kBoolean:
378 *reinterpret_cast<bool*>(opt_address) = ParseBoolean("", value);
379 break;
380 case OptionType::kInt:
381 *reinterpret_cast<int*>(opt_address) = ParseInt(value);
382 break;
383 case OptionType::kInt32T:
384 *reinterpret_cast<int32_t*>(opt_address) = ParseInt32(value);
385 break;
386 case OptionType::kInt64T:
387 PutUnaligned(reinterpret_cast<int64_t*>(opt_address), ParseInt64(value));
388 break;
389 case OptionType::kUInt:
390 *reinterpret_cast<unsigned int*>(opt_address) = ParseUint32(value);
391 break;
392 case OptionType::kUInt32T:
393 *reinterpret_cast<uint32_t*>(opt_address) = ParseUint32(value);
394 break;
395 case OptionType::kUInt64T:
396 PutUnaligned(reinterpret_cast<uint64_t*>(opt_address), ParseUint64(value));
397 break;
398 case OptionType::kSizeT:
399 PutUnaligned(reinterpret_cast<size_t*>(opt_address), ParseSizeT(value));
400 break;
401 case OptionType::kString:
402 *reinterpret_cast<std::string*>(opt_address) = value;
403 break;
404 case OptionType::kDouble:
405 *reinterpret_cast<double*>(opt_address) = ParseDouble(value);
406 break;
407 case OptionType::kCompactionStyle:
408 return ParseEnum<CompactionStyle>(
409 compaction_style_string_map, value,
410 reinterpret_cast<CompactionStyle*>(opt_address));
411 case OptionType::kCompactionPri:
412 return ParseEnum<CompactionPri>(
413 compaction_pri_string_map, value,
414 reinterpret_cast<CompactionPri*>(opt_address));
415 case OptionType::kCompressionType:
416 return ParseEnum<CompressionType>(
417 compression_type_string_map, value,
418 reinterpret_cast<CompressionType*>(opt_address));
419 case OptionType::kSliceTransform:
420 return ParseSliceTransform(
421 value, reinterpret_cast<std::shared_ptr<const SliceTransform>*>(
422 opt_address));
423 case OptionType::kChecksumType:
424 return ParseEnum<ChecksumType>(
425 checksum_type_string_map, value,
426 reinterpret_cast<ChecksumType*>(opt_address));
427 case OptionType::kEncodingType:
428 return ParseEnum<EncodingType>(
429 encoding_type_string_map, value,
430 reinterpret_cast<EncodingType*>(opt_address));
431 case OptionType::kCompactionStopStyle:
432 return ParseEnum<CompactionStopStyle>(
433 compaction_stop_style_string_map, value,
434 reinterpret_cast<CompactionStopStyle*>(opt_address));
435 default:
436 return false;
437 }
438 return true;
439 }
440
441 bool SerializeSingleOptionHelper(const char* opt_address,
442 const OptionType opt_type,
443 std::string* value) {
444
445 assert(value);
446 switch (opt_type) {
447 case OptionType::kBoolean:
448 *value = *(reinterpret_cast<const bool*>(opt_address)) ? "true" : "false";
449 break;
450 case OptionType::kInt:
451 *value = ToString(*(reinterpret_cast<const int*>(opt_address)));
452 break;
453 case OptionType::kInt32T:
454 *value = ToString(*(reinterpret_cast<const int32_t*>(opt_address)));
455 break;
456 case OptionType::kInt64T:
457 {
458 int64_t v;
459 GetUnaligned(reinterpret_cast<const int64_t*>(opt_address), &v);
460 *value = ToString(v);
461 }
462 break;
463 case OptionType::kUInt:
464 *value = ToString(*(reinterpret_cast<const unsigned int*>(opt_address)));
465 break;
466 case OptionType::kUInt32T:
467 *value = ToString(*(reinterpret_cast<const uint32_t*>(opt_address)));
468 break;
469 case OptionType::kUInt64T:
470 {
471 uint64_t v;
472 GetUnaligned(reinterpret_cast<const uint64_t*>(opt_address), &v);
473 *value = ToString(v);
474 }
475 break;
476 case OptionType::kSizeT:
477 {
478 size_t v;
479 GetUnaligned(reinterpret_cast<const size_t*>(opt_address), &v);
480 *value = ToString(v);
481 }
482 break;
483 case OptionType::kDouble:
484 *value = ToString(*(reinterpret_cast<const double*>(opt_address)));
485 break;
486 case OptionType::kString:
487 *value = EscapeOptionString(
488 *(reinterpret_cast<const std::string*>(opt_address)));
489 break;
490 case OptionType::kCompactionStyle:
491 return SerializeEnum<CompactionStyle>(
492 compaction_style_string_map,
493 *(reinterpret_cast<const CompactionStyle*>(opt_address)), value);
494 case OptionType::kCompactionPri:
495 return SerializeEnum<CompactionPri>(
496 compaction_pri_string_map,
497 *(reinterpret_cast<const CompactionPri*>(opt_address)), value);
498 case OptionType::kCompressionType:
499 return SerializeEnum<CompressionType>(
500 compression_type_string_map,
501 *(reinterpret_cast<const CompressionType*>(opt_address)), value);
502 case OptionType::kSliceTransform: {
503 const auto* slice_transform_ptr =
504 reinterpret_cast<const std::shared_ptr<const SliceTransform>*>(
505 opt_address);
506 *value = slice_transform_ptr->get() ? slice_transform_ptr->get()->Name()
507 : kNullptrString;
508 break;
509 }
510 case OptionType::kComparator: {
511 // it's a const pointer of const Comparator*
512 const auto* ptr = reinterpret_cast<const Comparator* const*>(opt_address);
513 // Since the user-specified comparator will be wrapped by
514 // InternalKeyComparator, we should persist the user-specified one
515 // instead of InternalKeyComparator.
516 if (*ptr == nullptr) {
517 *value = kNullptrString;
518 } else {
519 const Comparator* root_comp = (*ptr)->GetRootComparator();
520 if (root_comp == nullptr) {
521 root_comp = (*ptr);
522 }
523 *value = root_comp->Name();
524 }
525 break;
526 }
527 case OptionType::kCompactionFilter: {
528 // it's a const pointer of const CompactionFilter*
529 const auto* ptr =
530 reinterpret_cast<const CompactionFilter* const*>(opt_address);
531 *value = *ptr ? (*ptr)->Name() : kNullptrString;
532 break;
533 }
534 case OptionType::kCompactionFilterFactory: {
535 const auto* ptr =
536 reinterpret_cast<const std::shared_ptr<CompactionFilterFactory>*>(
537 opt_address);
538 *value = ptr->get() ? ptr->get()->Name() : kNullptrString;
539 break;
540 }
541 case OptionType::kMemTableRepFactory: {
542 const auto* ptr =
543 reinterpret_cast<const std::shared_ptr<MemTableRepFactory>*>(
544 opt_address);
545 *value = ptr->get() ? ptr->get()->Name() : kNullptrString;
546 break;
547 }
548 case OptionType::kMergeOperator: {
549 const auto* ptr =
550 reinterpret_cast<const std::shared_ptr<MergeOperator>*>(opt_address);
551 *value = ptr->get() ? ptr->get()->Name() : kNullptrString;
552 break;
553 }
554 case OptionType::kFilterPolicy: {
555 const auto* ptr =
556 reinterpret_cast<const std::shared_ptr<FilterPolicy>*>(opt_address);
557 *value = ptr->get() ? ptr->get()->Name() : kNullptrString;
558 break;
559 }
560 case OptionType::kChecksumType:
561 return SerializeEnum<ChecksumType>(
562 checksum_type_string_map,
563 *reinterpret_cast<const ChecksumType*>(opt_address), value);
564 case OptionType::kFlushBlockPolicyFactory: {
565 const auto* ptr =
566 reinterpret_cast<const std::shared_ptr<FlushBlockPolicyFactory>*>(
567 opt_address);
568 *value = ptr->get() ? ptr->get()->Name() : kNullptrString;
569 break;
570 }
571 case OptionType::kEncodingType:
572 return SerializeEnum<EncodingType>(
573 encoding_type_string_map,
574 *reinterpret_cast<const EncodingType*>(opt_address), value);
575 case OptionType::kCompactionStopStyle:
576 return SerializeEnum<CompactionStopStyle>(
577 compaction_stop_style_string_map,
578 *reinterpret_cast<const CompactionStopStyle*>(opt_address), value);
579 default:
580 return false;
581 }
582 return true;
583 }
584
585 template <typename T>
586 Status ConfigureFromMap(
587 const ConfigOptions& config_options,
588 const std::unordered_map<std::string, std::string>& opt_map,
589 const std::string& option_name, Configurable* config, T* new_opts) {
590 Status s = config->ConfigureFromMap(config_options, opt_map);
591 if (s.ok()) {
592 *new_opts = *(config->GetOptions<T>(option_name));
593 }
594 return s;
595 }
596
597 Status GetMutableOptionsFromStrings(
598 const MutableCFOptions& base_options,
599 const std::unordered_map<std::string, std::string>& options_map,
600 Logger* /*info_log*/, MutableCFOptions* new_options) {
601 assert(new_options);
602 *new_options = base_options;
603 ConfigOptions config_options;
604 const auto config = CFOptionsAsConfigurable(base_options);
605 return ConfigureFromMap<MutableCFOptions>(config_options, options_map,
606 MutableCFOptions::kName(),
607 config.get(), new_options);
608 }
609
610 Status GetMutableDBOptionsFromStrings(
611 const MutableDBOptions& base_options,
612 const std::unordered_map<std::string, std::string>& options_map,
613 MutableDBOptions* new_options) {
614 assert(new_options);
615 *new_options = base_options;
616 ConfigOptions config_options;
617
618 auto config = DBOptionsAsConfigurable(base_options);
619 return ConfigureFromMap<MutableDBOptions>(config_options, options_map,
620 MutableDBOptions::kName(),
621 config.get(), new_options);
622 }
623
624 Status StringToMap(const std::string& opts_str,
625 std::unordered_map<std::string, std::string>* opts_map) {
626 assert(opts_map);
627 // Example:
628 // opts_str = "write_buffer_size=1024;max_write_buffer_number=2;"
629 // "nested_opt={opt1=1;opt2=2};max_bytes_for_level_base=100"
630 size_t pos = 0;
631 std::string opts = trim(opts_str);
632 // If the input string starts and ends with "{...}", strip off the brackets
633 while (opts.size() > 2 && opts[0] == '{' && opts[opts.size() - 1] == '}') {
634 opts = trim(opts.substr(1, opts.size() - 2));
635 }
636
637 while (pos < opts.size()) {
638 size_t eq_pos = opts.find('=', pos);
639 if (eq_pos == std::string::npos) {
640 return Status::InvalidArgument("Mismatched key value pair, '=' expected");
641 }
642 std::string key = trim(opts.substr(pos, eq_pos - pos));
643 if (key.empty()) {
644 return Status::InvalidArgument("Empty key found");
645 }
646
647 std::string value;
648 Status s = OptionTypeInfo::NextToken(opts, ';', eq_pos + 1, &pos, &value);
649 if (!s.ok()) {
650 return s;
651 } else {
652 (*opts_map)[key] = value;
653 if (pos == std::string::npos) {
654 break;
655 } else {
656 pos++;
657 }
658 }
659 }
660
661 return Status::OK();
662 }
663
664 Status GetStringFromMutableDBOptions(const ConfigOptions& config_options,
665 const MutableDBOptions& mutable_opts,
666 std::string* opt_string) {
667 auto config = DBOptionsAsConfigurable(mutable_opts);
668 return config->GetOptionString(config_options, opt_string);
669 }
670
671 Status GetStringFromDBOptions(std::string* opt_string,
672 const DBOptions& db_options,
673 const std::string& delimiter) {
674 ConfigOptions config_options;
675 config_options.delimiter = delimiter;
676 return GetStringFromDBOptions(config_options, db_options, opt_string);
677 }
678
679 Status GetStringFromDBOptions(const ConfigOptions& config_options,
680 const DBOptions& db_options,
681 std::string* opt_string) {
682 assert(opt_string);
683 opt_string->clear();
684 auto config = DBOptionsAsConfigurable(db_options);
685 return config->GetOptionString(config_options, opt_string);
686 }
687
688 Status GetStringFromMutableCFOptions(const ConfigOptions& config_options,
689 const MutableCFOptions& mutable_opts,
690 std::string* opt_string) {
691 assert(opt_string);
692 opt_string->clear();
693 const auto config = CFOptionsAsConfigurable(mutable_opts);
694 return config->GetOptionString(config_options, opt_string);
695 }
696
697 Status GetStringFromColumnFamilyOptions(std::string* opt_string,
698 const ColumnFamilyOptions& cf_options,
699 const std::string& delimiter) {
700 ConfigOptions config_options;
701 config_options.delimiter = delimiter;
702 return GetStringFromColumnFamilyOptions(config_options, cf_options,
703 opt_string);
704 }
705
706 Status GetStringFromColumnFamilyOptions(const ConfigOptions& config_options,
707 const ColumnFamilyOptions& cf_options,
708 std::string* opt_string) {
709 const auto config = CFOptionsAsConfigurable(cf_options);
710 return config->GetOptionString(config_options, opt_string);
711 }
712
713 Status GetStringFromCompressionType(std::string* compression_str,
714 CompressionType compression_type) {
715 bool ok = SerializeEnum<CompressionType>(compression_type_string_map,
716 compression_type, compression_str);
717 if (ok) {
718 return Status::OK();
719 } else {
720 return Status::InvalidArgument("Invalid compression types");
721 }
722 }
723
724 Status GetColumnFamilyOptionsFromMap(
725 const ColumnFamilyOptions& base_options,
726 const std::unordered_map<std::string, std::string>& opts_map,
727 ColumnFamilyOptions* new_options, bool input_strings_escaped,
728 bool ignore_unknown_options) {
729 ConfigOptions config_options;
730 config_options.ignore_unknown_options = ignore_unknown_options;
731 config_options.input_strings_escaped = input_strings_escaped;
732 return GetColumnFamilyOptionsFromMap(config_options, base_options, opts_map,
733 new_options);
734 }
735
736 Status GetColumnFamilyOptionsFromMap(
737 const ConfigOptions& config_options,
738 const ColumnFamilyOptions& base_options,
739 const std::unordered_map<std::string, std::string>& opts_map,
740 ColumnFamilyOptions* new_options) {
741 assert(new_options);
742
743 *new_options = base_options;
744
745 const auto config = CFOptionsAsConfigurable(base_options);
746 Status s = ConfigureFromMap<ColumnFamilyOptions>(
747 config_options, opts_map, OptionsHelper::kCFOptionsName, config.get(),
748 new_options);
749 // Translate any errors (NotFound, NotSupported, to InvalidArgument
750 if (s.ok() || s.IsInvalidArgument()) {
751 return s;
752 } else {
753 return Status::InvalidArgument(s.getState());
754 }
755 }
756
757 Status GetColumnFamilyOptionsFromString(
758 const ColumnFamilyOptions& base_options,
759 const std::string& opts_str,
760 ColumnFamilyOptions* new_options) {
761 ConfigOptions config_options;
762 config_options.input_strings_escaped = false;
763 config_options.ignore_unknown_options = false;
764 return GetColumnFamilyOptionsFromString(config_options, base_options,
765 opts_str, new_options);
766 }
767
768 Status GetColumnFamilyOptionsFromString(const ConfigOptions& config_options,
769 const ColumnFamilyOptions& base_options,
770 const std::string& opts_str,
771 ColumnFamilyOptions* new_options) {
772 std::unordered_map<std::string, std::string> opts_map;
773 Status s = StringToMap(opts_str, &opts_map);
774 if (!s.ok()) {
775 *new_options = base_options;
776 return s;
777 }
778 return GetColumnFamilyOptionsFromMap(config_options, base_options, opts_map,
779 new_options);
780 }
781
782 Status GetDBOptionsFromMap(
783 const DBOptions& base_options,
784 const std::unordered_map<std::string, std::string>& opts_map,
785 DBOptions* new_options, bool input_strings_escaped,
786 bool ignore_unknown_options) {
787 ConfigOptions config_options;
788 config_options.input_strings_escaped = input_strings_escaped;
789 config_options.ignore_unknown_options = ignore_unknown_options;
790 return GetDBOptionsFromMap(config_options, base_options, opts_map,
791 new_options);
792 }
793
794 Status GetDBOptionsFromMap(
795 const ConfigOptions& config_options, const DBOptions& base_options,
796 const std::unordered_map<std::string, std::string>& opts_map,
797 DBOptions* new_options) {
798 assert(new_options);
799 *new_options = base_options;
800 auto config = DBOptionsAsConfigurable(base_options);
801 Status s = ConfigureFromMap<DBOptions>(config_options, opts_map,
802 OptionsHelper::kDBOptionsName,
803 config.get(), new_options);
804 // Translate any errors (NotFound, NotSupported, to InvalidArgument
805 if (s.ok() || s.IsInvalidArgument()) {
806 return s;
807 } else {
808 return Status::InvalidArgument(s.getState());
809 }
810 }
811
812 Status GetDBOptionsFromString(const DBOptions& base_options,
813 const std::string& opts_str,
814 DBOptions* new_options) {
815 ConfigOptions config_options;
816 config_options.input_strings_escaped = false;
817 config_options.ignore_unknown_options = false;
818
819 return GetDBOptionsFromString(config_options, base_options, opts_str,
820 new_options);
821 }
822
823 Status GetDBOptionsFromString(const ConfigOptions& config_options,
824 const DBOptions& base_options,
825 const std::string& opts_str,
826 DBOptions* new_options) {
827 std::unordered_map<std::string, std::string> opts_map;
828 Status s = StringToMap(opts_str, &opts_map);
829 if (!s.ok()) {
830 *new_options = base_options;
831 return s;
832 }
833 return GetDBOptionsFromMap(config_options, base_options, opts_map,
834 new_options);
835 }
836
837 Status GetOptionsFromString(const Options& base_options,
838 const std::string& opts_str, Options* new_options) {
839 ConfigOptions config_options;
840 config_options.input_strings_escaped = false;
841 config_options.ignore_unknown_options = false;
842
843 return GetOptionsFromString(config_options, base_options, opts_str,
844 new_options);
845 }
846
847 Status GetOptionsFromString(const ConfigOptions& config_options,
848 const Options& base_options,
849 const std::string& opts_str, Options* new_options) {
850 ColumnFamilyOptions new_cf_options;
851 std::unordered_map<std::string, std::string> unused_opts;
852 std::unordered_map<std::string, std::string> opts_map;
853
854 *new_options = base_options;
855 Status s = StringToMap(opts_str, &opts_map);
856 if (!s.ok()) {
857 return s;
858 }
859 auto config = DBOptionsAsConfigurable(base_options);
860 s = config->ConfigureFromMap(config_options, opts_map, &unused_opts);
861
862 if (s.ok()) {
863 DBOptions* new_db_options =
864 config->GetOptions<DBOptions>(OptionsHelper::kDBOptionsName);
865 if (!unused_opts.empty()) {
866 s = GetColumnFamilyOptionsFromMap(config_options, base_options,
867 unused_opts, &new_cf_options);
868 if (s.ok()) {
869 *new_options = Options(*new_db_options, new_cf_options);
870 }
871 } else {
872 *new_options = Options(*new_db_options, base_options);
873 }
874 }
875 // Translate any errors (NotFound, NotSupported, to InvalidArgument
876 if (s.ok() || s.IsInvalidArgument()) {
877 return s;
878 } else {
879 return Status::InvalidArgument(s.getState());
880 }
881 }
882
883 std::unordered_map<std::string, EncodingType>
884 OptionsHelper::encoding_type_string_map = {{"kPlain", kPlain},
885 {"kPrefix", kPrefix}};
886
887 std::unordered_map<std::string, CompactionStyle>
888 OptionsHelper::compaction_style_string_map = {
889 {"kCompactionStyleLevel", kCompactionStyleLevel},
890 {"kCompactionStyleUniversal", kCompactionStyleUniversal},
891 {"kCompactionStyleFIFO", kCompactionStyleFIFO},
892 {"kCompactionStyleNone", kCompactionStyleNone}};
893
894 std::unordered_map<std::string, CompactionPri>
895 OptionsHelper::compaction_pri_string_map = {
896 {"kByCompensatedSize", kByCompensatedSize},
897 {"kOldestLargestSeqFirst", kOldestLargestSeqFirst},
898 {"kOldestSmallestSeqFirst", kOldestSmallestSeqFirst},
899 {"kMinOverlappingRatio", kMinOverlappingRatio}};
900
901 std::unordered_map<std::string, CompactionStopStyle>
902 OptionsHelper::compaction_stop_style_string_map = {
903 {"kCompactionStopStyleSimilarSize", kCompactionStopStyleSimilarSize},
904 {"kCompactionStopStyleTotalSize", kCompactionStopStyleTotalSize}};
905
906 Status OptionTypeInfo::NextToken(const std::string& opts, char delimiter,
907 size_t pos, size_t* end, std::string* token) {
908 while (pos < opts.size() && isspace(opts[pos])) {
909 ++pos;
910 }
911 // Empty value at the end
912 if (pos >= opts.size()) {
913 *token = "";
914 *end = std::string::npos;
915 return Status::OK();
916 } else if (opts[pos] == '{') {
917 int count = 1;
918 size_t brace_pos = pos + 1;
919 while (brace_pos < opts.size()) {
920 if (opts[brace_pos] == '{') {
921 ++count;
922 } else if (opts[brace_pos] == '}') {
923 --count;
924 if (count == 0) {
925 break;
926 }
927 }
928 ++brace_pos;
929 }
930 // found the matching closing brace
931 if (count == 0) {
932 *token = trim(opts.substr(pos + 1, brace_pos - pos - 1));
933 // skip all whitespace and move to the next delimiter
934 // brace_pos points to the next position after the matching '}'
935 pos = brace_pos + 1;
936 while (pos < opts.size() && isspace(opts[pos])) {
937 ++pos;
938 }
939 if (pos < opts.size() && opts[pos] != delimiter) {
940 return Status::InvalidArgument("Unexpected chars after nested options");
941 }
942 *end = pos;
943 } else {
944 return Status::InvalidArgument(
945 "Mismatched curly braces for nested options");
946 }
947 } else {
948 *end = opts.find(delimiter, pos);
949 if (*end == std::string::npos) {
950 // It either ends with a trailing semi-colon or the last key-value pair
951 *token = trim(opts.substr(pos));
952 } else {
953 *token = trim(opts.substr(pos, *end - pos));
954 }
955 }
956 return Status::OK();
957 }
958
959 Status OptionTypeInfo::Parse(const ConfigOptions& config_options,
960 const std::string& opt_name,
961 const std::string& value, void* opt_ptr) const {
962 if (IsDeprecated()) {
963 return Status::OK();
964 }
965 try {
966 char* opt_addr = reinterpret_cast<char*>(opt_ptr) + offset_;
967 const std::string& opt_value = config_options.input_strings_escaped
968 ? UnescapeOptionString(value)
969 : value;
970
971 if (opt_addr == nullptr) {
972 return Status::NotFound("Could not find option", opt_name);
973 } else if (parse_func_ != nullptr) {
974 ConfigOptions copy = config_options;
975 copy.invoke_prepare_options = false;
976 return parse_func_(copy, opt_name, opt_value, opt_addr);
977 } else if (ParseOptionHelper(opt_addr, type_, opt_value)) {
978 return Status::OK();
979 } else if (IsConfigurable()) {
980 // The option is <config>.<name>
981 Configurable* config = AsRawPointer<Configurable>(opt_ptr);
982 if (opt_value.empty()) {
983 return Status::OK();
984 } else if (config == nullptr) {
985 return Status::NotFound("Could not find configurable: ", opt_name);
986 } else {
987 ConfigOptions copy = config_options;
988 copy.ignore_unknown_options = false;
989 copy.invoke_prepare_options = false;
990 if (opt_value.find("=") != std::string::npos) {
991 return config->ConfigureFromString(copy, opt_value);
992 } else {
993 return config->ConfigureOption(copy, opt_name, opt_value);
994 }
995 }
996 } else if (IsByName()) {
997 return Status::NotSupported("Deserializing the option " + opt_name +
998 " is not supported");
999 } else {
1000 return Status::InvalidArgument("Error parsing:", opt_name);
1001 }
1002 } catch (std::exception& e) {
1003 return Status::InvalidArgument("Error parsing " + opt_name + ":" +
1004 std::string(e.what()));
1005 }
1006 }
1007
1008 Status OptionTypeInfo::ParseStruct(
1009 const ConfigOptions& config_options, const std::string& struct_name,
1010 const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
1011 const std::string& opt_name, const std::string& opt_value, char* opt_addr) {
1012 assert(struct_map);
1013 Status status;
1014 if (opt_name == struct_name || EndsWith(opt_name, "." + struct_name)) {
1015 // This option represents the entire struct
1016 std::unordered_map<std::string, std::string> opt_map;
1017 status = StringToMap(opt_value, &opt_map);
1018 for (const auto& map_iter : opt_map) {
1019 if (!status.ok()) {
1020 break;
1021 }
1022 const auto iter = struct_map->find(map_iter.first);
1023 if (iter != struct_map->end()) {
1024 status = iter->second.Parse(config_options, map_iter.first,
1025 map_iter.second, opt_addr);
1026 } else {
1027 status = Status::InvalidArgument("Unrecognized option",
1028 struct_name + "." + map_iter.first);
1029 }
1030 }
1031 } else if (StartsWith(opt_name, struct_name + ".")) {
1032 // This option represents a nested field in the struct (e.g, struct.field)
1033 std::string elem_name;
1034 const auto opt_info =
1035 Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
1036 if (opt_info != nullptr) {
1037 status = opt_info->Parse(config_options, elem_name, opt_value, opt_addr);
1038 } else {
1039 status = Status::InvalidArgument("Unrecognized option", opt_name);
1040 }
1041 } else {
1042 // This option represents a field in the struct (e.g. field)
1043 std::string elem_name;
1044 const auto opt_info = Find(opt_name, *struct_map, &elem_name);
1045 if (opt_info != nullptr) {
1046 status = opt_info->Parse(config_options, elem_name, opt_value, opt_addr);
1047 } else {
1048 status = Status::InvalidArgument("Unrecognized option",
1049 struct_name + "." + opt_name);
1050 }
1051 }
1052 return status;
1053 }
1054
1055 Status OptionTypeInfo::Serialize(const ConfigOptions& config_options,
1056 const std::string& opt_name,
1057 const void* const opt_ptr,
1058 std::string* opt_value) const {
1059 // If the option is no longer used in rocksdb and marked as deprecated,
1060 // we skip it in the serialization.
1061 const char* opt_addr = reinterpret_cast<const char*>(opt_ptr) + offset_;
1062 if (opt_addr == nullptr || IsDeprecated()) {
1063 return Status::OK();
1064 } else if (IsEnabled(OptionTypeFlags::kDontSerialize)) {
1065 return Status::NotSupported("Cannot serialize option: ", opt_name);
1066 } else if (serialize_func_ != nullptr) {
1067 return serialize_func_(config_options, opt_name, opt_addr, opt_value);
1068 } else if (SerializeSingleOptionHelper(opt_addr, type_, opt_value)) {
1069 return Status::OK();
1070 } else if (IsCustomizable()) {
1071 const Customizable* custom = AsRawPointer<Customizable>(opt_ptr);
1072 if (custom == nullptr) {
1073 *opt_value = kNullptrString;
1074 } else if (IsEnabled(OptionTypeFlags::kStringNameOnly) &&
1075 !config_options.IsDetailed()) {
1076 *opt_value = custom->GetId();
1077 } else {
1078 ConfigOptions embedded = config_options;
1079 embedded.delimiter = ";";
1080 *opt_value = custom->ToString(embedded);
1081 }
1082 return Status::OK();
1083 } else if (IsConfigurable()) {
1084 const Configurable* config = AsRawPointer<Configurable>(opt_ptr);
1085 if (config != nullptr) {
1086 ConfigOptions embedded = config_options;
1087 embedded.delimiter = ";";
1088 *opt_value = config->ToString(embedded);
1089 }
1090 return Status::OK();
1091 } else {
1092 return Status::InvalidArgument("Cannot serialize option: ", opt_name);
1093 }
1094 }
1095
1096 Status OptionTypeInfo::SerializeStruct(
1097 const ConfigOptions& config_options, const std::string& struct_name,
1098 const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
1099 const std::string& opt_name, const char* opt_addr, std::string* value) {
1100 assert(struct_map);
1101 Status status;
1102 if (EndsWith(opt_name, struct_name)) {
1103 // We are going to write the struct as "{ prop1=value1; prop2=value2;}.
1104 // Set the delimiter to ";" so that the everything will be on one line.
1105 ConfigOptions embedded = config_options;
1106 embedded.delimiter = ";";
1107
1108 // This option represents the entire struct
1109 std::string result;
1110 for (const auto& iter : *struct_map) {
1111 std::string single;
1112 const auto& opt_info = iter.second;
1113 if (opt_info.ShouldSerialize()) {
1114 status = opt_info.Serialize(embedded, iter.first, opt_addr, &single);
1115 if (!status.ok()) {
1116 return status;
1117 } else {
1118 result.append(iter.first + "=" + single + embedded.delimiter);
1119 }
1120 }
1121 }
1122 *value = "{" + result + "}";
1123 } else if (StartsWith(opt_name, struct_name + ".")) {
1124 // This option represents a nested field in the struct (e.g, struct.field)
1125 std::string elem_name;
1126 const auto opt_info =
1127 Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
1128 if (opt_info != nullptr) {
1129 status = opt_info->Serialize(config_options, elem_name, opt_addr, value);
1130 } else {
1131 status = Status::InvalidArgument("Unrecognized option", opt_name);
1132 }
1133 } else {
1134 // This option represents a field in the struct (e.g. field)
1135 std::string elem_name;
1136 const auto opt_info = Find(opt_name, *struct_map, &elem_name);
1137 if (opt_info == nullptr) {
1138 status = Status::InvalidArgument("Unrecognized option", opt_name);
1139 } else if (opt_info->ShouldSerialize()) {
1140 status = opt_info->Serialize(config_options, opt_name + "." + elem_name,
1141 opt_addr, value);
1142 }
1143 }
1144 return status;
1145 }
1146
1147 template <typename T>
1148 bool IsOptionEqual(const char* offset1, const char* offset2) {
1149 return (*reinterpret_cast<const T*>(offset1) ==
1150 *reinterpret_cast<const T*>(offset2));
1151 }
1152
1153 static bool AreEqualDoubles(const double a, const double b) {
1154 return (fabs(a - b) < 0.00001);
1155 }
1156
1157 static bool AreOptionsEqual(OptionType type, const char* this_offset,
1158 const char* that_offset) {
1159 switch (type) {
1160 case OptionType::kBoolean:
1161 return IsOptionEqual<bool>(this_offset, that_offset);
1162 case OptionType::kInt:
1163 return IsOptionEqual<int>(this_offset, that_offset);
1164 case OptionType::kUInt:
1165 return IsOptionEqual<unsigned int>(this_offset, that_offset);
1166 case OptionType::kInt32T:
1167 return IsOptionEqual<int32_t>(this_offset, that_offset);
1168 case OptionType::kInt64T: {
1169 int64_t v1, v2;
1170 GetUnaligned(reinterpret_cast<const int64_t*>(this_offset), &v1);
1171 GetUnaligned(reinterpret_cast<const int64_t*>(that_offset), &v2);
1172 return (v1 == v2);
1173 }
1174 case OptionType::kUInt32T:
1175 return IsOptionEqual<uint32_t>(this_offset, that_offset);
1176 case OptionType::kUInt64T: {
1177 uint64_t v1, v2;
1178 GetUnaligned(reinterpret_cast<const uint64_t*>(this_offset), &v1);
1179 GetUnaligned(reinterpret_cast<const uint64_t*>(that_offset), &v2);
1180 return (v1 == v2);
1181 }
1182 case OptionType::kSizeT: {
1183 size_t v1, v2;
1184 GetUnaligned(reinterpret_cast<const size_t*>(this_offset), &v1);
1185 GetUnaligned(reinterpret_cast<const size_t*>(that_offset), &v2);
1186 return (v1 == v2);
1187 }
1188 case OptionType::kString:
1189 return IsOptionEqual<std::string>(this_offset, that_offset);
1190 case OptionType::kDouble:
1191 return AreEqualDoubles(*reinterpret_cast<const double*>(this_offset),
1192 *reinterpret_cast<const double*>(that_offset));
1193 case OptionType::kCompactionStyle:
1194 return IsOptionEqual<CompactionStyle>(this_offset, that_offset);
1195 case OptionType::kCompactionStopStyle:
1196 return IsOptionEqual<CompactionStopStyle>(this_offset, that_offset);
1197 case OptionType::kCompactionPri:
1198 return IsOptionEqual<CompactionPri>(this_offset, that_offset);
1199 case OptionType::kCompressionType:
1200 return IsOptionEqual<CompressionType>(this_offset, that_offset);
1201 case OptionType::kChecksumType:
1202 return IsOptionEqual<ChecksumType>(this_offset, that_offset);
1203 case OptionType::kEncodingType:
1204 return IsOptionEqual<EncodingType>(this_offset, that_offset);
1205 default:
1206 return false;
1207 } // End switch
1208 }
1209
1210 bool OptionTypeInfo::AreEqual(const ConfigOptions& config_options,
1211 const std::string& opt_name,
1212 const void* const this_ptr,
1213 const void* const that_ptr,
1214 std::string* mismatch) const {
1215 auto level = GetSanityLevel();
1216 if (!config_options.IsCheckEnabled(level)) {
1217 return true; // If the sanity level is not being checked, skip it
1218 }
1219 const auto this_addr = reinterpret_cast<const char*>(this_ptr) + offset_;
1220 const auto that_addr = reinterpret_cast<const char*>(that_ptr) + offset_;
1221 if (this_addr == nullptr || that_addr == nullptr) {
1222 if (this_addr == that_addr) {
1223 return true;
1224 }
1225 } else if (equals_func_ != nullptr) {
1226 if (equals_func_(config_options, opt_name, this_addr, that_addr,
1227 mismatch)) {
1228 return true;
1229 }
1230 } else if (AreOptionsEqual(type_, this_addr, that_addr)) {
1231 return true;
1232 } else if (IsConfigurable()) {
1233 const auto* this_config = AsRawPointer<Configurable>(this_ptr);
1234 const auto* that_config = AsRawPointer<Configurable>(that_ptr);
1235 if (this_config == that_config) {
1236 return true;
1237 } else if (this_config != nullptr && that_config != nullptr) {
1238 std::string bad_name;
1239 bool matches;
1240 if (level < config_options.sanity_level) {
1241 ConfigOptions copy = config_options;
1242 copy.sanity_level = level;
1243 matches = this_config->AreEquivalent(copy, that_config, &bad_name);
1244 } else {
1245 matches =
1246 this_config->AreEquivalent(config_options, that_config, &bad_name);
1247 }
1248 if (!matches) {
1249 *mismatch = opt_name + "." + bad_name;
1250 }
1251 return matches;
1252 }
1253 }
1254 if (mismatch->empty()) {
1255 *mismatch = opt_name;
1256 }
1257 return false;
1258 }
1259
1260 bool OptionTypeInfo::StructsAreEqual(
1261 const ConfigOptions& config_options, const std::string& struct_name,
1262 const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
1263 const std::string& opt_name, const char* this_addr, const char* that_addr,
1264 std::string* mismatch) {
1265 assert(struct_map);
1266 bool matches = true;
1267 std::string result;
1268 if (EndsWith(opt_name, struct_name)) {
1269 // This option represents the entire struct
1270 for (const auto& iter : *struct_map) {
1271 const auto& opt_info = iter.second;
1272
1273 matches = opt_info.AreEqual(config_options, iter.first, this_addr,
1274 that_addr, &result);
1275 if (!matches) {
1276 *mismatch = struct_name + "." + result;
1277 return false;
1278 }
1279 }
1280 } else if (StartsWith(opt_name, struct_name + ".")) {
1281 // This option represents a nested field in the struct (e.g, struct.field)
1282 std::string elem_name;
1283 const auto opt_info =
1284 Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
1285 assert(opt_info);
1286 if (opt_info == nullptr) {
1287 *mismatch = opt_name;
1288 matches = false;
1289 } else if (!opt_info->AreEqual(config_options, elem_name, this_addr,
1290 that_addr, &result)) {
1291 matches = false;
1292 *mismatch = struct_name + "." + result;
1293 }
1294 } else {
1295 // This option represents a field in the struct (e.g. field)
1296 std::string elem_name;
1297 const auto opt_info = Find(opt_name, *struct_map, &elem_name);
1298 assert(opt_info);
1299 if (opt_info == nullptr) {
1300 *mismatch = struct_name + "." + opt_name;
1301 matches = false;
1302 } else if (!opt_info->AreEqual(config_options, elem_name, this_addr,
1303 that_addr, &result)) {
1304 matches = false;
1305 *mismatch = struct_name + "." + result;
1306 }
1307 }
1308 return matches;
1309 }
1310
1311 bool MatchesOptionsTypeFromMap(
1312 const ConfigOptions& config_options,
1313 const std::unordered_map<std::string, OptionTypeInfo>& type_map,
1314 const void* const this_ptr, const void* const that_ptr,
1315 std::string* mismatch) {
1316 for (auto& pair : type_map) {
1317 // We skip checking deprecated variables as they might
1318 // contain random values since they might not be initialized
1319 if (config_options.IsCheckEnabled(pair.second.GetSanityLevel())) {
1320 if (!pair.second.AreEqual(config_options, pair.first, this_ptr, that_ptr,
1321 mismatch) &&
1322 !pair.second.AreEqualByName(config_options, pair.first, this_ptr,
1323 that_ptr)) {
1324 return false;
1325 }
1326 }
1327 }
1328 return true;
1329 }
1330
1331 bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
1332 const std::string& opt_name,
1333 const void* const this_ptr,
1334 const void* const that_ptr) const {
1335 if (IsByName()) {
1336 std::string that_value;
1337 if (Serialize(config_options, opt_name, that_ptr, &that_value).ok()) {
1338 return AreEqualByName(config_options, opt_name, this_ptr, that_value);
1339 }
1340 }
1341 return false;
1342 }
1343
1344 bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
1345 const std::string& opt_name,
1346 const void* const opt_ptr,
1347 const std::string& that_value) const {
1348 std::string this_value;
1349 if (!IsByName()) {
1350 return false;
1351 } else if (!Serialize(config_options, opt_name, opt_ptr, &this_value).ok()) {
1352 return false;
1353 } else if (IsEnabled(OptionVerificationType::kByNameAllowFromNull)) {
1354 if (that_value == kNullptrString) {
1355 return true;
1356 }
1357 } else if (IsEnabled(OptionVerificationType::kByNameAllowNull)) {
1358 if (that_value == kNullptrString) {
1359 return true;
1360 }
1361 }
1362 return (this_value == that_value);
1363 }
1364
1365 const OptionTypeInfo* OptionTypeInfo::Find(
1366 const std::string& opt_name,
1367 const std::unordered_map<std::string, OptionTypeInfo>& opt_map,
1368 std::string* elem_name) {
1369 const auto iter = opt_map.find(opt_name); // Look up the value in the map
1370 if (iter != opt_map.end()) { // Found the option in the map
1371 *elem_name = opt_name; // Return the name
1372 return &(iter->second); // Return the contents of the iterator
1373 } else {
1374 auto idx = opt_name.find("."); // Look for a separator
1375 if (idx > 0 && idx != std::string::npos) { // We found a separator
1376 auto siter =
1377 opt_map.find(opt_name.substr(0, idx)); // Look for the short name
1378 if (siter != opt_map.end()) { // We found the short name
1379 if (siter->second.IsStruct() || // If the object is a struct
1380 siter->second.IsConfigurable()) { // or a Configurable
1381 *elem_name = opt_name.substr(idx + 1); // Return the rest
1382 return &(siter->second); // Return the contents of the iterator
1383 }
1384 }
1385 }
1386 }
1387 return nullptr;
1388 }
1389 #endif // !ROCKSDB_LITE
1390
1391 } // namespace ROCKSDB_NAMESPACE