]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
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). | |
7c673cae FG |
5 | #include "options/options_helper.h" |
6 | ||
7 | #include <cassert> | |
8 | #include <cctype> | |
9 | #include <cstdlib> | |
10 | #include <unordered_set> | |
11 | #include <vector> | |
f67539c2 | 12 | |
20effc67 TL |
13 | #include "options/cf_options.h" |
14 | #include "options/db_options.h" | |
7c673cae FG |
15 | #include "rocksdb/cache.h" |
16 | #include "rocksdb/compaction_filter.h" | |
17 | #include "rocksdb/convenience.h" | |
18 | #include "rocksdb/filter_policy.h" | |
20effc67 | 19 | #include "rocksdb/flush_block_policy.h" |
7c673cae FG |
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" | |
494da23a | 26 | #include "rocksdb/utilities/object_registry.h" |
20effc67 | 27 | #include "rocksdb/utilities/options_type.h" |
7c673cae FG |
28 | #include "util/string_util.h" |
29 | ||
f67539c2 | 30 | namespace ROCKSDB_NAMESPACE { |
20effc67 TL |
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 | } | |
7c673cae FG |
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; | |
20effc67 TL |
54 | options.track_and_verify_wals_in_manifest = |
55 | immutable_db_options.track_and_verify_wals_in_manifest; | |
7c673cae FG |
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; | |
11fdf7f2 | 61 | options.max_open_files = mutable_db_options.max_open_files; |
7c673cae FG |
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; | |
11fdf7f2 | 72 | options.max_background_jobs = mutable_db_options.max_background_jobs; |
7c673cae FG |
73 | options.base_background_compactions = |
74 | mutable_db_options.base_background_compactions; | |
75 | options.max_background_compactions = | |
76 | mutable_db_options.max_background_compactions; | |
11fdf7f2 TL |
77 | options.bytes_per_sync = mutable_db_options.bytes_per_sync; |
78 | options.wal_bytes_per_sync = mutable_db_options.wal_bytes_per_sync; | |
f67539c2 | 79 | options.strict_bytes_per_sync = mutable_db_options.strict_bytes_per_sync; |
20effc67 TL |
80 | options.max_subcompactions = mutable_db_options.max_subcompactions; |
81 | options.max_background_flushes = mutable_db_options.max_background_flushes; | |
7c673cae FG |
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; | |
494da23a TL |
101 | options.stats_persist_period_sec = |
102 | mutable_db_options.stats_persist_period_sec; | |
f67539c2 | 103 | options.persist_stats_to_disk = immutable_db_options.persist_stats_to_disk; |
494da23a TL |
104 | options.stats_history_buffer_size = |
105 | mutable_db_options.stats_history_buffer_size; | |
7c673cae FG |
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 = | |
11fdf7f2 | 114 | mutable_db_options.compaction_readahead_size; |
7c673cae FG |
115 | options.random_access_max_buffer_size = |
116 | immutable_db_options.random_access_max_buffer_size; | |
117 | options.writable_file_max_buffer_size = | |
11fdf7f2 | 118 | mutable_db_options.writable_file_max_buffer_size; |
7c673cae | 119 | options.use_adaptive_mutex = immutable_db_options.use_adaptive_mutex; |
7c673cae FG |
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; | |
11fdf7f2 | 123 | options.enable_pipelined_write = immutable_db_options.enable_pipelined_write; |
f67539c2 | 124 | options.unordered_write = immutable_db_options.unordered_write; |
7c673cae FG |
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; | |
f67539c2 TL |
129 | options.max_write_batch_group_size_bytes = |
130 | immutable_db_options.max_write_batch_group_size_bytes; | |
7c673cae FG |
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; | |
f67539c2 TL |
137 | options.skip_checking_sst_file_sizes_on_db_open = |
138 | immutable_db_options.skip_checking_sst_file_sizes_on_db_open; | |
7c673cae FG |
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; | |
11fdf7f2 TL |
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; | |
494da23a TL |
158 | options.atomic_flush = immutable_db_options.atomic_flush; |
159 | options.avoid_unnecessary_blocking_io = | |
160 | immutable_db_options.avoid_unnecessary_blocking_io; | |
f67539c2 | 161 | options.log_readahead_size = immutable_db_options.log_readahead_size; |
20effc67 TL |
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; | |
7c673cae FG |
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; | |
494da23a TL |
185 | cf_opts.memtable_whole_key_filtering = |
186 | mutable_cf_options.memtable_whole_key_filtering; | |
7c673cae FG |
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; | |
11fdf7f2 | 191 | cf_opts.prefix_extractor = mutable_cf_options.prefix_extractor; |
7c673cae FG |
192 | |
193 | // Compaction related options | |
194 | cf_opts.disable_auto_compactions = | |
195 | mutable_cf_options.disable_auto_compactions; | |
11fdf7f2 TL |
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; | |
7c673cae FG |
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; | |
11fdf7f2 | 214 | cf_opts.ttl = mutable_cf_options.ttl; |
f67539c2 TL |
215 | cf_opts.periodic_compaction_seconds = |
216 | mutable_cf_options.periodic_compaction_seconds; | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | ||
20effc67 TL |
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 | ||
7c673cae FG |
238 | // Misc options |
239 | cf_opts.max_sequential_skip_in_iterations = | |
240 | mutable_cf_options.max_sequential_skip_in_iterations; | |
20effc67 TL |
241 | cf_opts.check_flush_compaction_key_order = |
242 | mutable_cf_options.check_flush_compaction_key_order; | |
7c673cae FG |
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; | |
20effc67 TL |
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; | |
494da23a | 250 | cf_opts.sample_for_compression = mutable_cf_options.sample_for_compression; |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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}, | |
494da23a TL |
280 | {"kxxHash", kxxHash}, |
281 | {"kxxHash64", kxxHash64}}; | |
11fdf7f2 TL |
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}}; | |
7c673cae | 295 | |
20effc67 TL |
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); | |
7c673cae FG |
302 | } |
303 | } | |
20effc67 | 304 | return supported_compressions; |
11fdf7f2 TL |
305 | } |
306 | ||
20effc67 TL |
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); | |
11fdf7f2 | 313 | } |
11fdf7f2 | 314 | } |
20effc67 | 315 | return dict_compression_types; |
11fdf7f2 | 316 | } |
11fdf7f2 | 317 | |
20effc67 | 318 | #ifndef ROCKSDB_LITE |
7c673cae FG |
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) { | |
11fdf7f2 TL |
323 | const char* no_op_name = "rocksdb.Noop"; |
324 | size_t no_op_length = strlen(no_op_name); | |
7c673cae FG |
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)); | |
11fdf7f2 TL |
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); | |
7c673cae FG |
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 | ||
20effc67 TL |
374 | static bool ParseOptionHelper(char* opt_address, const OptionType& opt_type, |
375 | const std::string& value) { | |
7c673cae FG |
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; | |
494da23a TL |
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; | |
7c673cae FG |
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)); | |
7c673cae FG |
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)); | |
7c673cae FG |
427 | case OptionType::kEncodingType: |
428 | return ParseEnum<EncodingType>( | |
429 | encoding_type_string_map, value, | |
430 | reinterpret_cast<EncodingType*>(opt_address)); | |
11fdf7f2 TL |
431 | case OptionType::kCompactionStopStyle: |
432 | return ParseEnum<CompactionStopStyle>( | |
433 | compaction_stop_style_string_map, value, | |
434 | reinterpret_cast<CompactionStopStyle*>(opt_address)); | |
7c673cae FG |
435 | default: |
436 | return false; | |
437 | } | |
438 | return true; | |
439 | } | |
440 | ||
7c673cae FG |
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; | |
494da23a TL |
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; | |
7c673cae FG |
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); | |
7c673cae FG |
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 | } | |
7c673cae FG |
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. | |
11fdf7f2 TL |
516 | if (*ptr == nullptr) { |
517 | *value = kNullptrString; | |
7c673cae | 518 | } else { |
11fdf7f2 TL |
519 | const Comparator* root_comp = (*ptr)->GetRootComparator(); |
520 | if (root_comp == nullptr) { | |
521 | root_comp = (*ptr); | |
522 | } | |
523 | *value = root_comp->Name(); | |
7c673cae FG |
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); | |
7c673cae FG |
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); | |
11fdf7f2 TL |
575 | case OptionType::kCompactionStopStyle: |
576 | return SerializeEnum<CompactionStopStyle>( | |
577 | compaction_stop_style_string_map, | |
578 | *reinterpret_cast<const CompactionStopStyle*>(opt_address), value); | |
7c673cae FG |
579 | default: |
580 | return false; | |
581 | } | |
582 | return true; | |
583 | } | |
584 | ||
20effc67 TL |
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 | ||
7c673cae FG |
597 | Status GetMutableOptionsFromStrings( |
598 | const MutableCFOptions& base_options, | |
599 | const std::unordered_map<std::string, std::string>& options_map, | |
20effc67 | 600 | Logger* /*info_log*/, MutableCFOptions* new_options) { |
7c673cae FG |
601 | assert(new_options); |
602 | *new_options = base_options; | |
20effc67 TL |
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); | |
7c673cae FG |
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; | |
20effc67 TL |
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); | |
7c673cae FG |
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); | |
20effc67 TL |
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 | ||
7c673cae FG |
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 | ||
20effc67 TL |
647 | std::string value; |
648 | Status s = OptionTypeInfo::NextToken(opts, ';', eq_pos + 1, &pos, &value); | |
649 | if (!s.ok()) { | |
650 | return s; | |
7c673cae | 651 | } else { |
20effc67 TL |
652 | (*opts_map)[key] = value; |
653 | if (pos == std::string::npos) { | |
7c673cae FG |
654 | break; |
655 | } else { | |
20effc67 | 656 | pos++; |
7c673cae | 657 | } |
7c673cae FG |
658 | } |
659 | } | |
660 | ||
661 | return Status::OK(); | |
662 | } | |
663 | ||
20effc67 TL |
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); | |
7c673cae FG |
669 | } |
670 | ||
20effc67 TL |
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); | |
7c673cae FG |
677 | } |
678 | ||
20effc67 TL |
679 | Status GetStringFromDBOptions(const ConfigOptions& config_options, |
680 | const DBOptions& db_options, | |
681 | std::string* opt_string) { | |
7c673cae FG |
682 | assert(opt_string); |
683 | opt_string->clear(); | |
20effc67 TL |
684 | auto config = DBOptionsAsConfigurable(db_options); |
685 | return config->GetOptionString(config_options, opt_string); | |
7c673cae FG |
686 | } |
687 | ||
20effc67 TL |
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); | |
7c673cae FG |
695 | } |
696 | ||
697 | Status GetStringFromColumnFamilyOptions(std::string* opt_string, | |
698 | const ColumnFamilyOptions& cf_options, | |
699 | const std::string& delimiter) { | |
20effc67 TL |
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); | |
7c673cae FG |
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 | ||
7c673cae FG |
724 | Status GetColumnFamilyOptionsFromMap( |
725 | const ColumnFamilyOptions& base_options, | |
726 | const std::unordered_map<std::string, std::string>& opts_map, | |
11fdf7f2 TL |
727 | ColumnFamilyOptions* new_options, bool input_strings_escaped, |
728 | bool ignore_unknown_options) { | |
20effc67 TL |
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); | |
7c673cae FG |
734 | } |
735 | ||
20effc67 TL |
736 | Status GetColumnFamilyOptionsFromMap( |
737 | const ConfigOptions& config_options, | |
7c673cae FG |
738 | const ColumnFamilyOptions& base_options, |
739 | const std::unordered_map<std::string, std::string>& opts_map, | |
20effc67 | 740 | ColumnFamilyOptions* new_options) { |
7c673cae | 741 | assert(new_options); |
20effc67 | 742 | |
7c673cae | 743 | *new_options = base_options; |
20effc67 TL |
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()); | |
7c673cae | 754 | } |
7c673cae FG |
755 | } |
756 | ||
757 | Status GetColumnFamilyOptionsFromString( | |
758 | const ColumnFamilyOptions& base_options, | |
759 | const std::string& opts_str, | |
760 | ColumnFamilyOptions* new_options) { | |
20effc67 TL |
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) { | |
7c673cae FG |
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 | } | |
20effc67 TL |
778 | return GetColumnFamilyOptionsFromMap(config_options, base_options, opts_map, |
779 | new_options); | |
7c673cae FG |
780 | } |
781 | ||
782 | Status GetDBOptionsFromMap( | |
783 | const DBOptions& base_options, | |
784 | const std::unordered_map<std::string, std::string>& opts_map, | |
11fdf7f2 TL |
785 | DBOptions* new_options, bool input_strings_escaped, |
786 | bool ignore_unknown_options) { | |
20effc67 TL |
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); | |
7c673cae FG |
792 | } |
793 | ||
20effc67 TL |
794 | Status GetDBOptionsFromMap( |
795 | const ConfigOptions& config_options, const DBOptions& base_options, | |
7c673cae | 796 | const std::unordered_map<std::string, std::string>& opts_map, |
20effc67 | 797 | DBOptions* new_options) { |
7c673cae FG |
798 | assert(new_options); |
799 | *new_options = base_options; | |
20effc67 TL |
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()); | |
7c673cae | 809 | } |
7c673cae FG |
810 | } |
811 | ||
20effc67 TL |
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) { | |
7c673cae FG |
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 | } | |
20effc67 TL |
833 | return GetDBOptionsFromMap(config_options, base_options, opts_map, |
834 | new_options); | |
7c673cae FG |
835 | } |
836 | ||
837 | Status GetOptionsFromString(const Options& base_options, | |
838 | const std::string& opts_str, Options* new_options) { | |
20effc67 TL |
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; | |
7c673cae | 852 | std::unordered_map<std::string, std::string> opts_map; |
20effc67 TL |
853 | |
854 | *new_options = base_options; | |
7c673cae FG |
855 | Status s = StringToMap(opts_str, &opts_map); |
856 | if (!s.ok()) { | |
857 | return s; | |
858 | } | |
20effc67 TL |
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 | } | |
7c673cae | 871 | } else { |
20effc67 | 872 | *new_options = Options(*new_db_options, base_options); |
7c673cae FG |
873 | } |
874 | } | |
20effc67 TL |
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()); | |
7c673cae | 880 | } |
7c673cae FG |
881 | } |
882 | ||
11fdf7f2 TL |
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 | ||
20effc67 TL |
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 | } | |
11fdf7f2 | 1094 | } |
20effc67 TL |
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; | |
11fdf7f2 | 1145 | } |
20effc67 TL |
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)); | |
11fdf7f2 | 1151 | } |
20effc67 TL |
1152 | |
1153 | static bool AreEqualDoubles(const double a, const double b) { | |
1154 | return (fabs(a - b) < 0.00001); | |
11fdf7f2 | 1155 | } |
20effc67 TL |
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 | |
11fdf7f2 TL |
1208 | } |
1209 | ||
20effc67 TL |
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 | } | |
11fdf7f2 | 1259 | |
20effc67 TL |
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 | } | |
11fdf7f2 | 1310 | |
20effc67 TL |
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 | } | |
11fdf7f2 | 1364 | |
20effc67 TL |
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 | } | |
7c673cae FG |
1389 | #endif // !ROCKSDB_LITE |
1390 | ||
f67539c2 | 1391 | } // namespace ROCKSDB_NAMESPACE |