// 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
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
kInt32T,
kInt64T,
kUInt,
+ kUInt8T,
kUInt32T,
kUInt64T,
kSizeT,
kDouble,
kCompactionStyle,
kCompactionPri,
- kSliceTransform,
kCompressionType,
- kComparator,
- kCompactionFilter,
- kCompactionFilterFactory,
kCompactionStopStyle,
- kMergeOperator,
- kMemTableRepFactory,
- kFilterPolicy,
- kFlushBlockPolicyFactory,
kChecksumType,
kEncodingType,
kEnv,
kVector,
kConfigurable,
kCustomizable,
+ kEncodedString,
+ kTemperature,
+ kArray,
kUnknown,
};
kStringNameOnly = 0x8000, // The option serializes to a name only
};
-inline OptionTypeFlags operator|(const OptionTypeFlags &a,
- const OptionTypeFlags &b) {
+inline OptionTypeFlags operator|(const OptionTypeFlags& a,
+ const OptionTypeFlags& b) {
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 OptionTypeFlags& a,
+ const OptionTypeFlags& b) {
return static_cast<OptionTypeFlags>(static_cast<uint32_t>(a) &
static_cast<uint32_t>(b));
}
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,
// @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.
// @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
// 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.
// - 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
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>
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
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>
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
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>
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
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>
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 {
}
}
+ 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));
}
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>
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);
}
}
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);
}
}
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
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
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.
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.
// @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
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_;
// 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
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 &&
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;
}
if (result.find("=") != std::string::npos) {
*value = "{" + result + "}";
+ } else if (printed > 1 && result.at(0) == '{') {
+ *value = "{" + result + "}";
} else {
*value = result;
}
// @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,