]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/rocksdb/include/rocksdb/utilities/options_type.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / include / rocksdb / utilities / options_type.h
index 36e1e09f97b0d0b0a2e010d15ac4318788fb7ae2..cd340ed596780753a8a10b132ddaedfdab222899 100644 (file)
@@ -2,6 +2,15 @@
 //  This source code is licensed under both the GPLv2 (found in the
 //  COPYING file in the root directory) and Apache 2.0 License
 //  (found in the LICENSE.Apache file in the root directory).
+//
+// The OptionTypeInfo and related classes provide a framework for
+// configuring and validating RocksDB classes via the Options framework.
+// This file is part of the public API to allow developers who wish to
+// write their own extensions and plugins to take use the Options
+// framework in their custom implementations.
+//
+// See https://github.com/facebook/rocksdb/wiki/RocksDB-Configurable-Objects
+// for more information on how to develop and use custom extensions
 
 #pragma once
 
@@ -15,6 +24,8 @@
 
 namespace ROCKSDB_NAMESPACE {
 class OptionTypeInfo;
+struct ColumnFamilyOptions;
+struct DBOptions;
 
 // The underlying "class/type" of the option.
 // This enum is used to determine how the option should
@@ -25,6 +36,7 @@ enum class OptionType {
   kInt32T,
   kInt64T,
   kUInt,
+  kUInt8T,
   kUInt32T,
   kUInt64T,
   kSizeT,
@@ -32,16 +44,8 @@ enum class OptionType {
   kDouble,
   kCompactionStyle,
   kCompactionPri,
-  kSliceTransform,
   kCompressionType,
-  kComparator,
-  kCompactionFilter,
-  kCompactionFilterFactory,
   kCompactionStopStyle,
-  kMergeOperator,
-  kMemTableRepFactory,
-  kFilterPolicy,
-  kFlushBlockPolicyFactory,
   kChecksumType,
   kEncodingType,
   kEnv,
@@ -50,6 +54,9 @@ enum class OptionType {
   kVector,
   kConfigurable,
   kCustomizable,
+  kEncodedString,
+  kTemperature,
+  kArray,
   kUnknown,
 };
 
@@ -104,14 +111,14 @@ enum class OptionTypeFlags : uint32_t {
   kStringNameOnly = 0x8000,  // The option serializes to a name only
 };
 
-inline OptionTypeFlags operator|(const OptionTypeFlags &a,
-                                 const OptionTypeFlags &b) {
+inline OptionTypeFlags operator|(const OptionTypeFlagsa,
+                                 const OptionTypeFlagsb) {
   return static_cast<OptionTypeFlags>(static_cast<uint32_t>(a) |
                                       static_cast<uint32_t>(b));
 }
 
-inline OptionTypeFlags operator&(const OptionTypeFlags &a,
-                                 const OptionTypeFlags &b) {
+inline OptionTypeFlags operator&(const OptionTypeFlagsa,
+                                 const OptionTypeFlagsb) {
   return static_cast<OptionTypeFlags>(static_cast<uint32_t>(a) &
                                       static_cast<uint32_t>(b));
 }
@@ -149,6 +156,24 @@ bool SerializeEnum(const std::unordered_map<std::string, T>& type_map,
   return false;
 }
 
+template <typename T, size_t kSize>
+Status ParseArray(const ConfigOptions& config_options,
+                  const OptionTypeInfo& elem_info, char separator,
+                  const std::string& name, const std::string& value,
+                  std::array<T, kSize>* result);
+
+template <typename T, size_t kSize>
+Status SerializeArray(const ConfigOptions& config_options,
+                      const OptionTypeInfo& elem_info, char separator,
+                      const std::string& name, const std::array<T, kSize>& vec,
+                      std::string* value);
+
+template <typename T, size_t kSize>
+bool ArraysAreEqual(const ConfigOptions& config_options,
+                    const OptionTypeInfo& elem_info, const std::string& name,
+                    const std::array<T, kSize>& array1,
+                    const std::array<T, kSize>& array2, std::string* mismatch);
+
 template <typename T>
 Status ParseVector(const ConfigOptions& config_options,
                    const OptionTypeInfo& elem_info, char separator,
@@ -176,7 +201,7 @@ bool VectorsAreEqual(const ConfigOptions& config_options,
 // @param addr  Pointer to the object
 using ParseFunc = std::function<Status(
     const ConfigOptions& /*opts*/, const std::string& /*name*/,
-    const std::string& /*value*/, char* /*addr*/)>;
+    const std::string& /*value*/, void* /*addr*/)>;
 
 // Function for converting an option "addr" into its string representation.
 // On success, Status::OK is returned and value is the serialized form.
@@ -187,7 +212,7 @@ using ParseFunc = std::function<Status(
 // @param value The result of the serialization.
 using SerializeFunc = std::function<Status(
     const ConfigOptions& /*opts*/, const std::string& /*name*/,
-    const char* /*addr*/, std::string* /*value*/)>;
+    const void* /*addr*/, std::string* /*value*/)>;
 
 // Function for comparing two option values
 // If they are not equal, updates "mismatch" with the name of the bad option
@@ -199,7 +224,17 @@ using SerializeFunc = std::function<Status(
 // first differs
 using EqualsFunc = std::function<bool(
     const ConfigOptions& /*opts*/, const std::string& /*name*/,
-    const char* /*addr1*/, const char* /*addr2*/, std::string* mismatch)>;
+    const void* /*addr1*/, const void* /*addr2*/, std::string* mismatch)>;
+
+// Function for preparing/initializing an option.
+using PrepareFunc =
+    std::function<Status(const ConfigOptions& /*opts*/,
+                         const std::string& /*name*/, void* /*addr*/)>;
+
+// Function for validating an option.
+using ValidateFunc = std::function<Status(
+    const DBOptions& /*db_opts*/, const ColumnFamilyOptions& /*cf_opts*/,
+    const std::string& /*name*/, const void* /*addr*/)>;
 
 // A struct for storing constant option information such as option name,
 // option type, and offset.
@@ -255,55 +290,59 @@ class OptionTypeInfo {
   // - Create a static map of string values to the corresponding enum value
   // - Call this method passing the static map in as a parameter.
   // Note that it is not necessary to add a new OptionType or make any
-  // other changes -- the returned object handles parsing, serialiation, and
+  // other changes -- the returned object handles parsing, serialization, and
   // comparisons.
   //
   // @param offset The offset in the option object for this enum
   // @param map The string to enum mapping for this enum
   template <typename T>
   static OptionTypeInfo Enum(
-      int offset, const std::unordered_map<std::string, T>* const map) {
-    return OptionTypeInfo(
-        offset, OptionType::kEnum, OptionVerificationType::kNormal,
-        OptionTypeFlags::kNone,
+      int offset, const std::unordered_map<std::string, T>* const map,
+      OptionTypeFlags flags = OptionTypeFlags::kNone) {
+    OptionTypeInfo info(offset, OptionType::kEnum,
+                        OptionVerificationType::kNormal, flags);
+    info.SetParseFunc(
         // Uses the map argument to convert the input string into
         // its corresponding enum value.  If value is found in the map,
         // addr is updated to the corresponding map entry.
         // @return OK if the value is found in the map
         // @return InvalidArgument if the value is not found in the map
         [map](const ConfigOptions&, const std::string& name,
-              const std::string& value, char* addr) {
+              const std::string& value, void* addr) {
           if (map == nullptr) {
             return Status::NotSupported("No enum mapping ", name);
-          } else if (ParseEnum<T>(*map, value, reinterpret_cast<T*>(addr))) {
+          } else if (ParseEnum<T>(*map, value, static_cast<T*>(addr))) {
             return Status::OK();
           } else {
             return Status::InvalidArgument("No mapping for enum ", name);
           }
-        },
+        });
+    info.SetSerializeFunc(
         // Uses the map argument to convert the input enum into
         // its corresponding string value.  If enum value is found in the map,
         // value is updated to the corresponding string value in the map.
         // @return OK if the enum is found in the map
         // @return InvalidArgument if the enum is not found in the map
-        [map](const ConfigOptions&, const std::string& name, const char* addr,
+        [map](const ConfigOptions&, const std::string& name, const void* addr,
               std::string* value) {
           if (map == nullptr) {
             return Status::NotSupported("No enum mapping ", name);
-          } else if (SerializeEnum<T>(*map, (*reinterpret_cast<const T*>(addr)),
+          } else if (SerializeEnum<T>(*map, (*static_cast<const T*>(addr)),
                                       value)) {
             return Status::OK();
           } else {
             return Status::InvalidArgument("No mapping for enum ", name);
           }
-        },
+        });
+    info.SetEqualsFunc(
         // Casts addr1 and addr2 to the enum type and returns true if
         // they are equal, false otherwise.
-        [](const ConfigOptions&, const std::string&, const char* addr1,
-           const char* addr2, std::string*) {
-          return (*reinterpret_cast<const T*>(addr1) ==
-                  *reinterpret_cast<const T*>(addr2));
+        [](const ConfigOptions&, const std::string&, const void* addr1,
+           const void* addr2, std::string*) {
+          return (*static_cast<const T*>(addr1) ==
+                  *static_cast<const T*>(addr2));
         });
+    return info;
   }  // End OptionTypeInfo::Enum
 
   // Creates an OptionTypeInfo for a Struct type.  Structs have a
@@ -332,48 +371,72 @@ class OptionTypeInfo {
       const std::string& struct_name,
       const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
       int offset, OptionVerificationType verification, OptionTypeFlags flags) {
-    return OptionTypeInfo(
-        offset, OptionType::kStruct, verification, flags,
+    OptionTypeInfo info(offset, OptionType::kStruct, verification, flags);
+    info.SetParseFunc(
         // Parses the struct and updates the fields at addr
         [struct_name, struct_map](const ConfigOptions& opts,
                                   const std::string& name,
-                                  const std::string& value, char* addr) {
+                                  const std::string& value, void* addr) {
           return ParseStruct(opts, struct_name, struct_map, name, value, addr);
-        },
+        });
+    info.SetSerializeFunc(
         // Serializes the struct options into value
         [struct_name, struct_map](const ConfigOptions& opts,
-                                  const std::string& name, const char* addr,
+                                  const std::string& name, const void* addr,
                                   std::string* value) {
           return SerializeStruct(opts, struct_name, struct_map, name, addr,
                                  value);
-        },
+        });
+    info.SetEqualsFunc(
         // Compares the struct fields of addr1 and addr2 for equality
         [struct_name, struct_map](const ConfigOptions& opts,
-                                  const std::string& name, const char* addr1,
-                                  const char* addr2, std::string* mismatch) {
+                                  const std::string& name, const void* addr1,
+                                  const void* addr2, std::string* mismatch) {
           return StructsAreEqual(opts, struct_name, struct_map, name, addr1,
                                  addr2, mismatch);
         });
+    return info;
   }
   static OptionTypeInfo Struct(
       const std::string& struct_name,
       const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
       int offset, OptionVerificationType verification, OptionTypeFlags flags,
       const ParseFunc& parse_func) {
-    return OptionTypeInfo(
-        offset, OptionType::kStruct, verification, flags, parse_func,
-        [struct_name, struct_map](const ConfigOptions& opts,
-                                  const std::string& name, const char* addr,
-                                  std::string* value) {
-          return SerializeStruct(opts, struct_name, struct_map, name, addr,
-                                 value);
-        },
-        [struct_name, struct_map](const ConfigOptions& opts,
-                                  const std::string& name, const char* addr1,
-                                  const char* addr2, std::string* mismatch) {
-          return StructsAreEqual(opts, struct_name, struct_map, name, addr1,
-                                 addr2, mismatch);
-        });
+    OptionTypeInfo info(
+        Struct(struct_name, struct_map, offset, verification, flags));
+    return info.SetParseFunc(parse_func);
+  }
+
+  template <typename T, size_t kSize>
+  static OptionTypeInfo Array(int _offset, OptionVerificationType _verification,
+                              OptionTypeFlags _flags,
+                              const OptionTypeInfo& elem_info,
+                              char separator = ':') {
+    OptionTypeInfo info(_offset, OptionType::kArray, _verification, _flags);
+    info.SetParseFunc([elem_info, separator](
+                          const ConfigOptions& opts, const std::string& name,
+                          const std::string& value, void* addr) {
+      auto result = static_cast<std::array<T, kSize>*>(addr);
+      return ParseArray<T, kSize>(opts, elem_info, separator, name, value,
+                                  result);
+    });
+    info.SetSerializeFunc([elem_info, separator](const ConfigOptions& opts,
+                                                 const std::string& name,
+                                                 const void* addr,
+                                                 std::string* value) {
+      const auto& array = *(static_cast<const std::array<T, kSize>*>(addr));
+      return SerializeArray<T, kSize>(opts, elem_info, separator, name, array,
+                                      value);
+    });
+    info.SetEqualsFunc([elem_info](const ConfigOptions& opts,
+                                   const std::string& name, const void* addr1,
+                                   const void* addr2, std::string* mismatch) {
+      const auto& array1 = *(static_cast<const std::array<T, kSize>*>(addr1));
+      const auto& array2 = *(static_cast<const std::array<T, kSize>*>(addr2));
+      return ArraysAreEqual<T, kSize>(opts, elem_info, name, array1, array2,
+                                      mismatch);
+    });
+    return info;
   }
 
   template <typename T>
@@ -382,30 +445,28 @@ class OptionTypeInfo {
                                OptionTypeFlags _flags,
                                const OptionTypeInfo& elem_info,
                                char separator = ':') {
-    return OptionTypeInfo(
-        _offset, OptionType::kVector, _verification, _flags,
-        [elem_info, separator](const ConfigOptions& opts,
-                               const std::string& name,
-                               const std::string& value, char* addr) {
-          auto result = reinterpret_cast<std::vector<T>*>(addr);
-          return ParseVector<T>(opts, elem_info, separator, name, value,
-                                result);
-        },
-        [elem_info, separator](const ConfigOptions& opts,
-                               const std::string& name, const char* addr,
-                               std::string* value) {
-          const auto& vec = *(reinterpret_cast<const std::vector<T>*>(addr));
-          return SerializeVector<T>(opts, elem_info, separator, name, vec,
-                                    value);
-        },
-        [elem_info](const ConfigOptions& opts, const std::string& name,
-                    const char* addr1, const char* addr2,
-                    std::string* mismatch) {
-          const auto& vec1 = *(reinterpret_cast<const std::vector<T>*>(addr1));
-          const auto& vec2 = *(reinterpret_cast<const std::vector<T>*>(addr2));
-          return VectorsAreEqual<T>(opts, elem_info, name, vec1, vec2,
-                                    mismatch);
-        });
+    OptionTypeInfo info(_offset, OptionType::kVector, _verification, _flags);
+    info.SetParseFunc([elem_info, separator](
+                          const ConfigOptions& opts, const std::string& name,
+                          const std::string& value, void* addr) {
+      auto result = static_cast<std::vector<T>*>(addr);
+      return ParseVector<T>(opts, elem_info, separator, name, value, result);
+    });
+    info.SetSerializeFunc([elem_info, separator](const ConfigOptions& opts,
+                                                 const std::string& name,
+                                                 const void* addr,
+                                                 std::string* value) {
+      const auto& vec = *(static_cast<const std::vector<T>*>(addr));
+      return SerializeVector<T>(opts, elem_info, separator, name, vec, value);
+    });
+    info.SetEqualsFunc([elem_info](const ConfigOptions& opts,
+                                   const std::string& name, const void* addr1,
+                                   const void* addr2, std::string* mismatch) {
+      const auto& vec1 = *(static_cast<const std::vector<T>*>(addr1));
+      const auto& vec2 = *(static_cast<const std::vector<T>*>(addr2));
+      return VectorsAreEqual<T>(opts, elem_info, name, vec1, vec2, mismatch);
+    });
+    return info;
   }
 
   // Create a new std::shared_ptr<Customizable> OptionTypeInfo
@@ -421,7 +482,19 @@ class OptionTypeInfo {
   static OptionTypeInfo AsCustomSharedPtr(int offset,
                                           OptionVerificationType ovt,
                                           OptionTypeFlags flags) {
-    return AsCustomSharedPtr<T>(offset, ovt, flags, nullptr, nullptr);
+    OptionTypeInfo info(offset, OptionType::kCustomizable, ovt,
+                        flags | OptionTypeFlags::kShared);
+    return info.SetParseFunc([](const ConfigOptions& opts,
+                                const std::string& name,
+                                const std::string& value, void* addr) {
+      auto* shared = static_cast<std::shared_ptr<T>*>(addr);
+      if (name == kIdPropName() && value.empty()) {
+        shared->reset();
+        return Status::OK();
+      } else {
+        return T::CreateFromString(opts, value, shared);
+      }
+    });
   }
 
   template <typename T>
@@ -430,15 +503,10 @@ class OptionTypeInfo {
                                           OptionTypeFlags flags,
                                           const SerializeFunc& serialize_func,
                                           const EqualsFunc& equals_func) {
-    return OptionTypeInfo(
-        offset, OptionType::kCustomizable, ovt,
-        flags | OptionTypeFlags::kShared,
-        [](const ConfigOptions& opts, const std::string&,
-           const std::string& value, char* addr) {
-          auto* shared = reinterpret_cast<std::shared_ptr<T>*>(addr);
-          return T::CreateFromString(opts, value, shared);
-        },
-        serialize_func, equals_func);
+    OptionTypeInfo info(AsCustomSharedPtr<T>(offset, ovt, flags));
+    info.SetSerializeFunc(serialize_func);
+    info.SetEqualsFunc(equals_func);
+    return info;
   }
 
   // Create a new std::unique_ptr<Customizable> OptionTypeInfo
@@ -454,7 +522,19 @@ class OptionTypeInfo {
   static OptionTypeInfo AsCustomUniquePtr(int offset,
                                           OptionVerificationType ovt,
                                           OptionTypeFlags flags) {
-    return AsCustomUniquePtr<T>(offset, ovt, flags, nullptr, nullptr);
+    OptionTypeInfo info(offset, OptionType::kCustomizable, ovt,
+                        flags | OptionTypeFlags::kUnique);
+    return info.SetParseFunc([](const ConfigOptions& opts,
+                                const std::string& name,
+                                const std::string& value, void* addr) {
+      auto* unique = static_cast<std::unique_ptr<T>*>(addr);
+      if (name == kIdPropName() && value.empty()) {
+        unique->reset();
+        return Status::OK();
+      } else {
+        return T::CreateFromString(opts, value, unique);
+      }
+    });
   }
 
   template <typename T>
@@ -463,15 +543,10 @@ class OptionTypeInfo {
                                           OptionTypeFlags flags,
                                           const SerializeFunc& serialize_func,
                                           const EqualsFunc& equals_func) {
-    return OptionTypeInfo(
-        offset, OptionType::kCustomizable, ovt,
-        flags | OptionTypeFlags::kUnique,
-        [](const ConfigOptions& opts, const std::string&,
-           const std::string& value, char* addr) {
-          auto* unique = reinterpret_cast<std::unique_ptr<T>*>(addr);
-          return T::CreateFromString(opts, value, unique);
-        },
-        serialize_func, equals_func);
+    OptionTypeInfo info(AsCustomUniquePtr<T>(offset, ovt, flags));
+    info.SetSerializeFunc(serialize_func);
+    info.SetEqualsFunc(equals_func);
+    return info;
   }
 
   // Create a new Customizable* OptionTypeInfo
@@ -486,7 +561,19 @@ class OptionTypeInfo {
   template <typename T>
   static OptionTypeInfo AsCustomRawPtr(int offset, OptionVerificationType ovt,
                                        OptionTypeFlags flags) {
-    return AsCustomRawPtr<T>(offset, ovt, flags, nullptr, nullptr);
+    OptionTypeInfo info(offset, OptionType::kCustomizable, ovt,
+                        flags | OptionTypeFlags::kRawPointer);
+    return info.SetParseFunc([](const ConfigOptions& opts,
+                                const std::string& name,
+                                const std::string& value, void* addr) {
+      auto** pointer = static_cast<T**>(addr);
+      if (name == kIdPropName() && value.empty()) {
+        *pointer = nullptr;
+        return Status::OK();
+      } else {
+        return T::CreateFromString(opts, value, pointer);
+      }
+    });
   }
 
   template <typename T>
@@ -494,19 +581,45 @@ class OptionTypeInfo {
                                        OptionTypeFlags flags,
                                        const SerializeFunc& serialize_func,
                                        const EqualsFunc& equals_func) {
-    return OptionTypeInfo(
-        offset, OptionType::kCustomizable, ovt,
-        flags | OptionTypeFlags::kRawPointer,
-        [](const ConfigOptions& opts, const std::string&,
-           const std::string& value, char* addr) {
-          auto** pointer = reinterpret_cast<T**>(addr);
-          return T::CreateFromString(opts, value, pointer);
-        },
-        serialize_func, equals_func);
+    OptionTypeInfo info(AsCustomRawPtr<T>(offset, ovt, flags));
+    info.SetSerializeFunc(serialize_func);
+    info.SetEqualsFunc(equals_func);
+    return info;
+  }
+
+  OptionTypeInfo& SetParseFunc(const ParseFunc& f) {
+    parse_func_ = f;
+    return *this;
+  }
+
+  OptionTypeInfo& SetSerializeFunc(const SerializeFunc& f) {
+    serialize_func_ = f;
+    return *this;
+  }
+  OptionTypeInfo& SetEqualsFunc(const EqualsFunc& f) {
+    equals_func_ = f;
+    return *this;
+  }
+
+  OptionTypeInfo& SetPrepareFunc(const PrepareFunc& f) {
+    prepare_func_ = f;
+    return *this;
+  }
+
+  OptionTypeInfo& SetValidateFunc(const ValidateFunc& f) {
+    validate_func_ = f;
+    return *this;
   }
 
   bool IsEnabled(OptionTypeFlags otf) const { return (flags_ & otf) == otf; }
 
+  bool IsEditable(const ConfigOptions& opts) const {
+    if (opts.mutable_options_only) {
+      return IsMutable();
+    } else {
+      return true;
+    }
+  }
   bool IsMutable() const { return IsEnabled(OptionTypeFlags::kMutable); }
 
   bool IsDeprecated() const {
@@ -552,11 +665,30 @@ class OptionTypeInfo {
     }
   }
 
+  bool ShouldPrepare() const {
+    if (IsDeprecated() || IsAlias()) {
+      return false;
+    } else if (IsEnabled(OptionTypeFlags::kDontPrepare)) {
+      return false;
+    } else {
+      return (prepare_func_ != nullptr || IsConfigurable());
+    }
+  }
+
+  bool ShouldValidate() const {
+    if (IsDeprecated() || IsAlias()) {
+      return false;
+    } else {
+      return (validate_func_ != nullptr || IsConfigurable());
+    }
+  }
+
   // Returns true if the option is allowed to be null.
   // Options can be null if the verification type is allow from null
   // or if the flags specify allow null.
   bool CanBeNull() const {
     return (IsEnabled(OptionTypeFlags::kAllowNull) ||
+            IsEnabled(OptionVerificationType::kByNameAllowNull) ||
             IsEnabled(OptionVerificationType::kByNameAllowFromNull));
   }
 
@@ -581,6 +713,26 @@ class OptionTypeInfo {
 
   bool IsCustomizable() const { return (type_ == OptionType::kCustomizable); }
 
+  inline const void* GetOffset(const void* base) const {
+    return static_cast<const char*>(base) + offset_;
+  }
+
+  inline void* GetOffset(void* base) const {
+    return static_cast<char*>(base) + offset_;
+  }
+
+  template <typename T>
+  const T* GetOffsetAs(const void* base) const {
+    const void* addr = GetOffset(base);
+    return static_cast<const T*>(addr);
+  }
+
+  template <typename T>
+  T* GetOffsetAs(void* base) const {
+    void* addr = GetOffset(base);
+    return static_cast<T*>(addr);
+  }
+
   // Returns the underlying pointer for the type at base_addr
   // The value returned is the underlying "raw" pointer, offset from base.
   template <typename T>
@@ -588,20 +740,17 @@ class OptionTypeInfo {
     if (base_addr == nullptr) {
       return nullptr;
     }
-    const auto opt_addr = reinterpret_cast<const char*>(base_addr) + offset_;
     if (IsUniquePtr()) {
-      const std::unique_ptr<T>* ptr =
-          reinterpret_cast<const std::unique_ptr<T>*>(opt_addr);
+      const auto ptr = GetOffsetAs<std::unique_ptr<T>>(base_addr);
       return ptr->get();
     } else if (IsSharedPtr()) {
-      const std::shared_ptr<T>* ptr =
-          reinterpret_cast<const std::shared_ptr<T>*>(opt_addr);
+      const auto ptr = GetOffsetAs<std::shared_ptr<T>>(base_addr);
       return ptr->get();
     } else if (IsRawPtr()) {
-      const T* const* ptr = reinterpret_cast<const T* const*>(opt_addr);
+      const T* const* ptr = GetOffsetAs<T* const>(base_addr);
       return *ptr;
     } else {
-      return reinterpret_cast<const T*>(opt_addr);
+      return GetOffsetAs<T>(base_addr);
     }
   }
 
@@ -612,18 +761,17 @@ class OptionTypeInfo {
     if (base_addr == nullptr) {
       return nullptr;
     }
-    auto opt_addr = reinterpret_cast<char*>(base_addr) + offset_;
     if (IsUniquePtr()) {
-      std::unique_ptr<T>* ptr = reinterpret_cast<std::unique_ptr<T>*>(opt_addr);
+      auto ptr = GetOffsetAs<std::unique_ptr<T>>(base_addr);
       return ptr->get();
     } else if (IsSharedPtr()) {
-      std::shared_ptr<T>* ptr = reinterpret_cast<std::shared_ptr<T>*>(opt_addr);
+      auto ptr = GetOffsetAs<std::shared_ptr<T>>(base_addr);
       return ptr->get();
     } else if (IsRawPtr()) {
-      T** ptr = reinterpret_cast<T**>(opt_addr);
+      auto ptr = GetOffsetAs<T*>(base_addr);
       return *ptr;
     } else {
-      return reinterpret_cast<T*>(opt_addr);
+      return GetOffsetAs<T>(base_addr);
     }
   }
 
@@ -657,6 +805,42 @@ class OptionTypeInfo {
                       const std::string& opt_name, const void* const this_ptr,
                       const std::string& that_value) const;
 
+  Status Prepare(const ConfigOptions& config_options, const std::string& name,
+                 void* opt_ptr) const;
+  Status Validate(const DBOptions& db_opts, const ColumnFamilyOptions& cf_opts,
+                  const std::string& name, const void* opt_ptr) const;
+
+  // Parses the input opts_map according to the type_map for the opt_addr
+  // For each name-value pair in opts_map, find the corresponding name in
+  // type_map If the name is found:
+  //    - set the corresponding value in opt_addr, returning the status on
+  //    failure;
+  // If the name is not found:
+  //    - If unused is specified, add the name-value to unused and continue
+  //    - If ingore_unknown_options is false, return NotFound
+  // Returns OK if all options were either:
+  //    - Successfully set
+  //    - options were not found and ignore_unknown_options=true
+  //    - options were not found and unused was specified
+  // Note that this method is much less sophisticated than the comparable
+  // Configurable::Configure methods.  For example, on error, there is no
+  // attempt to return opt_addr to the initial state.  Additionally, there
+  // is no effort to initialize (Configurable::PrepareOptions) the object
+  // on success.  This method should typically only be used for simpler,
+  // standalone structures and not those that contain shared and embedded
+  // objects.
+  static Status ParseType(
+      const ConfigOptions& config_options, const std::string& opts_str,
+      const std::unordered_map<std::string, OptionTypeInfo>& type_map,
+      void* opt_addr,
+      std::unordered_map<std::string, std::string>* unused = nullptr);
+  static Status ParseType(
+      const ConfigOptions& config_options,
+      const std::unordered_map<std::string, std::string>& opts_map,
+      const std::unordered_map<std::string, OptionTypeInfo>& type_map,
+      void* opt_addr,
+      std::unordered_map<std::string, std::string>* unused = nullptr);
+
   // Parses the input value according to the map for the struct at opt_addr
   // struct_name is the name of the struct option as registered
   // opt_name is the name of the option being evaluated.  This may
@@ -665,7 +849,15 @@ class OptionTypeInfo {
   static Status ParseStruct(
       const ConfigOptions& config_options, const std::string& struct_name,
       const std::unordered_map<std::string, OptionTypeInfo>* map,
-      const std::string& opt_name, const std::string& value, char* opt_addr);
+      const std::string& opt_name, const std::string& value, void* opt_addr);
+
+  // Serializes the values from opt_addr using the rules in type_map.
+  // Returns the serialized form in result.
+  // Returns OK on success or non-OK if some option could not be serialized.
+  static Status SerializeType(
+      const ConfigOptions& config_options,
+      const std::unordered_map<std::string, OptionTypeInfo>& type_map,
+      const void* opt_addr, std::string* value);
 
   // Serializes the input addr according to the map for the struct to value.
   // struct_name is the name of the struct option as registered
@@ -674,7 +866,16 @@ class OptionTypeInfo {
   static Status SerializeStruct(
       const ConfigOptions& config_options, const std::string& struct_name,
       const std::unordered_map<std::string, OptionTypeInfo>* map,
-      const std::string& opt_name, const char* opt_addr, std::string* value);
+      const std::string& opt_name, const void* opt_addr, std::string* value);
+
+  // Compares the values in this_addr and that_addr using the rules in type_map.
+  // If the values are equal, returns true
+  // If the values are not equal, returns false and sets mismatch to the name
+  // of the first value that did not match.
+  static bool TypesAreEqual(
+      const ConfigOptions& config_options,
+      const std::unordered_map<std::string, OptionTypeInfo>& map,
+      const void* this_addr, const void* that_addr, std::string* mismatch);
 
   // Compares the input offsets according to the map for the struct and returns
   // true if they are equivalent, false otherwise.
@@ -684,8 +885,8 @@ class OptionTypeInfo {
   static bool StructsAreEqual(
       const ConfigOptions& config_options, const std::string& struct_name,
       const std::unordered_map<std::string, OptionTypeInfo>* map,
-      const std::string& opt_name, const char* this_offset,
-      const char* that_offset, std::string* mismatch);
+      const std::string& opt_name, const void* this_offset,
+      const void* that_offset, std::string* mismatch);
 
   // Finds the entry for the opt_name in the opt_map, returning
   // nullptr if not found.
@@ -711,7 +912,7 @@ class OptionTypeInfo {
   // @param opts The string in which to find the next token
   // @param delimiter The delimiter between tokens
   // @param start     The position in opts to start looking for the token
-  // @parem ed        Returns the end position in opts of the token
+  // @param ed        Returns the end position in opts of the token
   // @param token     Returns the token
   // @returns OK if a token was found
   // @return InvalidArgument if the braces mismatch
@@ -721,6 +922,9 @@ class OptionTypeInfo {
   static Status NextToken(const std::string& opts, char delimiter, size_t start,
                           size_t* end, std::string* token);
 
+  constexpr static const char* kIdPropName() { return "id"; }
+  constexpr static const char* kIdPropSuffix() { return ".id"; }
+
  private:
   int offset_;
 
@@ -733,11 +937,151 @@ class OptionTypeInfo {
   // The optional function to match two option values
   EqualsFunc equals_func_;
 
+  PrepareFunc prepare_func_;
+  ValidateFunc validate_func_;
   OptionType type_;
   OptionVerificationType verification_;
   OptionTypeFlags flags_;
 };
 
+// Parses the input value into elements of the result array, which has fixed
+// array size. For example, if the value=1:2:3 and elem_info parses integers,
+// the result array will be {1,2,3}. Array size is defined in the OptionTypeInfo
+// the input value has to match with that.
+// @param config_options Controls how the option value is parsed.
+// @param elem_info Controls how individual tokens in value are parsed
+// @param separator Character separating tokens in values (':' in the above
+// example)
+// @param name      The name associated with this array option
+// @param value     The input string to parse into tokens
+// @param result    Returns the results of parsing value into its elements.
+// @return OK if the value was successfully parse
+// @return InvalidArgument if the value is improperly formed or element number
+//                          doesn't match array size defined in OptionTypeInfo
+//                          or if the token could not be parsed
+// @return NotFound         If the tokenized value contains unknown options for
+// its type
+template <typename T, size_t kSize>
+Status ParseArray(const ConfigOptions& config_options,
+                  const OptionTypeInfo& elem_info, char separator,
+                  const std::string& name, const std::string& value,
+                  std::array<T, kSize>* result) {
+  Status status;
+
+  ConfigOptions copy = config_options;
+  copy.ignore_unsupported_options = false;
+  size_t i = 0, start = 0, end = 0;
+  for (; status.ok() && i < kSize && start < value.size() &&
+         end != std::string::npos;
+       i++, start = end + 1) {
+    std::string token;
+    status = OptionTypeInfo::NextToken(value, separator, start, &end, &token);
+    if (status.ok()) {
+      status = elem_info.Parse(copy, name, token, &((*result)[i]));
+      if (config_options.ignore_unsupported_options &&
+          status.IsNotSupported()) {
+        // If we were ignoring unsupported options and this one should be
+        // ignored, ignore it by setting the status to OK
+        status = Status::OK();
+      }
+    }
+  }
+  if (!status.ok()) {
+    return status;
+  }
+  // make sure the element number matches the array size
+  if (i < kSize) {
+    return Status::InvalidArgument(
+        "Serialized value has less elements than array size", name);
+  }
+  if (start < value.size() && end != std::string::npos) {
+    return Status::InvalidArgument(
+        "Serialized value has more elements than array size", name);
+  }
+  return status;
+}
+
+// Serializes the fixed size input array into its output value.  Elements are
+// separated by the separator character.  This element will convert all of the
+// elements in array into their serialized form, using elem_info to perform the
+// serialization.
+// For example, if the array contains the integers 1,2,3 and elem_info
+// serializes the output would be 1:2:3 for separator ":".
+// @param config_options Controls how the option value is serialized.
+// @param elem_info Controls how individual tokens in value are serialized
+// @param separator Character separating tokens in value (':' in the above
+// example)
+// @param name      The name associated with this array option
+// @param array     The input array to serialize
+// @param value     The output string of serialized options
+// @return OK if the value was successfully parse
+// @return InvalidArgument if the value is improperly formed or if the token
+//                          could not be parsed
+// @return NotFound        If the tokenized value contains unknown options for
+//                          its type
+template <typename T, size_t kSize>
+Status SerializeArray(const ConfigOptions& config_options,
+                      const OptionTypeInfo& elem_info, char separator,
+                      const std::string& name,
+                      const std::array<T, kSize>& array, std::string* value) {
+  std::string result;
+  ConfigOptions embedded = config_options;
+  embedded.delimiter = ";";
+  int printed = 0;
+  for (const auto& elem : array) {
+    std::string elem_str;
+    Status s = elem_info.Serialize(embedded, name, &elem, &elem_str);
+    if (!s.ok()) {
+      return s;
+    } else if (!elem_str.empty()) {
+      if (printed++ > 0) {
+        result += separator;
+      }
+      // If the element contains embedded separators, put it inside of brackets
+      if (elem_str.find(separator) != std::string::npos) {
+        result += "{" + elem_str + "}";
+      } else {
+        result += elem_str;
+      }
+    }
+  }
+  if (result.find("=") != std::string::npos) {
+    *value = "{" + result + "}";
+  } else if (printed > 1 && result.at(0) == '{') {
+    *value = "{" + result + "}";
+  } else {
+    *value = result;
+  }
+  return Status::OK();
+}
+
+// Compares the input arrays array1 and array2 for equality
+// Elements of the array are compared one by one using elem_info to perform the
+// comparison.
+//
+// @param config_options Controls how the arrays are compared.
+// @param elem_info  Controls how individual elements in the arrays are compared
+// @param name          The name associated with this array option
+// @param array1,array2 The arrays to compare.
+// @param mismatch      If the arrays are not equivalent, mismatch will point to
+//                       the first element of the comparison that did not match.
+// @return true         If vec1 and vec2 are "equal", false otherwise
+template <typename T, size_t kSize>
+bool ArraysAreEqual(const ConfigOptions& config_options,
+                    const OptionTypeInfo& elem_info, const std::string& name,
+                    const std::array<T, kSize>& array1,
+                    const std::array<T, kSize>& array2, std::string* mismatch) {
+  assert(array1.size() == kSize);
+  assert(array2.size() == kSize);
+  for (size_t i = 0; i < kSize; ++i) {
+    if (!elem_info.AreEqual(config_options, name, &array1[i], &array2[i],
+                            mismatch)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 // Parses the input value into elements of the result vector.  This method
 // will break the input value into the individual tokens (based on the
 // separator), where each of those tokens will be parsed based on the rules of
@@ -775,8 +1119,7 @@ Status ParseVector(const ConfigOptions& config_options,
     status = OptionTypeInfo::NextToken(value, separator, start, &end, &token);
     if (status.ok()) {
       T elem;
-      status =
-          elem_info.Parse(copy, name, token, reinterpret_cast<char*>(&elem));
+      status = elem_info.Parse(copy, name, token, &elem);
       if (status.ok()) {
         result->emplace_back(elem);
       } else if (config_options.ignore_unsupported_options &&
@@ -816,18 +1159,18 @@ Status SerializeVector(const ConfigOptions& config_options,
   std::string result;
   ConfigOptions embedded = config_options;
   embedded.delimiter = ";";
-  for (size_t i = 0; i < vec.size(); ++i) {
+  int printed = 0;
+  for (const auto& elem : vec) {
     std::string elem_str;
-    Status s = elem_info.Serialize(
-        embedded, name, reinterpret_cast<const char*>(&vec[i]), &elem_str);
+    Status s = elem_info.Serialize(embedded, name, &elem, &elem_str);
     if (!s.ok()) {
       return s;
-    } else {
-      if (i > 0) {
+    } else if (!elem_str.empty()) {
+      if (printed++ > 0) {
         result += separator;
       }
       // If the element contains embedded separators, put it inside of brackets
-      if (result.find(separator) != std::string::npos) {
+      if (elem_str.find(separator) != std::string::npos) {
         result += "{" + elem_str + "}";
       } else {
         result += elem_str;
@@ -836,6 +1179,8 @@ Status SerializeVector(const ConfigOptions& config_options,
   }
   if (result.find("=") != std::string::npos) {
     *value = "{" + result + "}";
+  } else if (printed > 1 && result.at(0) == '{') {
+    *value = "{" + result + "}";
   } else {
     *value = result;
   }
@@ -852,7 +1197,7 @@ Status SerializeVector(const ConfigOptions& config_options,
 // @param vec1,vec2 The vectors to compare.
 // @param mismatch  If the vectors are not equivalent, mismatch will point to
 // the first
-//                  element of the comparison tht did not match.
+//                  element of the comparison that did not match.
 // @return true     If vec1 and vec2 are "equal", false otherwise
 template <typename T>
 bool VectorsAreEqual(const ConfigOptions& config_options,