X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ceph%2Fsrc%2Frocksdb%2Finclude%2Frocksdb%2Futilities%2Foptions_type.h;fp=ceph%2Fsrc%2Frocksdb%2Finclude%2Frocksdb%2Futilities%2Foptions_type.h;h=cd340ed596780753a8a10b132ddaedfdab222899;hb=1e59de90020f1d8d374046ef9cca56ccd4e806e2;hp=36e1e09f97b0d0b0a2e010d15ac4318788fb7ae2;hpb=bd41e436e25044e8e83156060a37c23cb661c364;p=ceph.git diff --git a/ceph/src/rocksdb/include/rocksdb/utilities/options_type.h b/ceph/src/rocksdb/include/rocksdb/utilities/options_type.h index 36e1e09f9..cd340ed59 100644 --- a/ceph/src/rocksdb/include/rocksdb/utilities/options_type.h +++ b/ceph/src/rocksdb/include/rocksdb/utilities/options_type.h @@ -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 OptionTypeFlags& a, + const OptionTypeFlags& b) { return static_cast(static_cast(a) | static_cast(b)); } -inline OptionTypeFlags operator&(const OptionTypeFlags &a, - const OptionTypeFlags &b) { +inline OptionTypeFlags operator&(const OptionTypeFlags& a, + const OptionTypeFlags& b) { return static_cast(static_cast(a) & static_cast(b)); } @@ -149,6 +156,24 @@ bool SerializeEnum(const std::unordered_map& type_map, return false; } +template +Status ParseArray(const ConfigOptions& config_options, + const OptionTypeInfo& elem_info, char separator, + const std::string& name, const std::string& value, + std::array* result); + +template +Status SerializeArray(const ConfigOptions& config_options, + const OptionTypeInfo& elem_info, char separator, + const std::string& name, const std::array& vec, + std::string* value); + +template +bool ArraysAreEqual(const ConfigOptions& config_options, + const OptionTypeInfo& elem_info, const std::string& name, + const std::array& array1, + const std::array& array2, std::string* mismatch); + template 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; + 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; + 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; + const void* /*addr1*/, const void* /*addr2*/, std::string* mismatch)>; + +// Function for preparing/initializing an option. +using PrepareFunc = + std::function; + +// Function for validating an option. +using ValidateFunc = std::function; // 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 static OptionTypeInfo Enum( - int offset, const std::unordered_map* const map) { - return OptionTypeInfo( - offset, OptionType::kEnum, OptionVerificationType::kNormal, - OptionTypeFlags::kNone, + int offset, const std::unordered_map* 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(*map, value, reinterpret_cast(addr))) { + } else if (ParseEnum(*map, value, static_cast(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(*map, (*reinterpret_cast(addr)), + } else if (SerializeEnum(*map, (*static_cast(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(addr1) == - *reinterpret_cast(addr2)); + [](const ConfigOptions&, const std::string&, const void* addr1, + const void* addr2, std::string*) { + return (*static_cast(addr1) == + *static_cast(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* 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* 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 + 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*>(addr); + return ParseArray(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*>(addr)); + return SerializeArray(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*>(addr1)); + const auto& array2 = *(static_cast*>(addr2)); + return ArraysAreEqual(opts, elem_info, name, array1, array2, + mismatch); + }); + return info; } template @@ -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*>(addr); - return ParseVector(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*>(addr)); - return SerializeVector(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*>(addr1)); - const auto& vec2 = *(reinterpret_cast*>(addr2)); - return VectorsAreEqual(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*>(addr); + return ParseVector(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*>(addr)); + return SerializeVector(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*>(addr1)); + const auto& vec2 = *(static_cast*>(addr2)); + return VectorsAreEqual(opts, elem_info, name, vec1, vec2, mismatch); + }); + return info; } // Create a new std::shared_ptr OptionTypeInfo @@ -421,7 +482,19 @@ class OptionTypeInfo { static OptionTypeInfo AsCustomSharedPtr(int offset, OptionVerificationType ovt, OptionTypeFlags flags) { - return AsCustomSharedPtr(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*>(addr); + if (name == kIdPropName() && value.empty()) { + shared->reset(); + return Status::OK(); + } else { + return T::CreateFromString(opts, value, shared); + } + }); } template @@ -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*>(addr); - return T::CreateFromString(opts, value, shared); - }, - serialize_func, equals_func); + OptionTypeInfo info(AsCustomSharedPtr(offset, ovt, flags)); + info.SetSerializeFunc(serialize_func); + info.SetEqualsFunc(equals_func); + return info; } // Create a new std::unique_ptr OptionTypeInfo @@ -454,7 +522,19 @@ class OptionTypeInfo { static OptionTypeInfo AsCustomUniquePtr(int offset, OptionVerificationType ovt, OptionTypeFlags flags) { - return AsCustomUniquePtr(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*>(addr); + if (name == kIdPropName() && value.empty()) { + unique->reset(); + return Status::OK(); + } else { + return T::CreateFromString(opts, value, unique); + } + }); } template @@ -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*>(addr); - return T::CreateFromString(opts, value, unique); - }, - serialize_func, equals_func); + OptionTypeInfo info(AsCustomUniquePtr(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 static OptionTypeInfo AsCustomRawPtr(int offset, OptionVerificationType ovt, OptionTypeFlags flags) { - return AsCustomRawPtr(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(addr); + if (name == kIdPropName() && value.empty()) { + *pointer = nullptr; + return Status::OK(); + } else { + return T::CreateFromString(opts, value, pointer); + } + }); } template @@ -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(addr); - return T::CreateFromString(opts, value, pointer); - }, - serialize_func, equals_func); + OptionTypeInfo info(AsCustomRawPtr(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(base) + offset_; + } + + inline void* GetOffset(void* base) const { + return static_cast(base) + offset_; + } + + template + const T* GetOffsetAs(const void* base) const { + const void* addr = GetOffset(base); + return static_cast(addr); + } + + template + T* GetOffsetAs(void* base) const { + void* addr = GetOffset(base); + return static_cast(addr); + } + // Returns the underlying pointer for the type at base_addr // The value returned is the underlying "raw" pointer, offset from base. template @@ -588,20 +740,17 @@ class OptionTypeInfo { if (base_addr == nullptr) { return nullptr; } - const auto opt_addr = reinterpret_cast(base_addr) + offset_; if (IsUniquePtr()) { - const std::unique_ptr* ptr = - reinterpret_cast*>(opt_addr); + const auto ptr = GetOffsetAs>(base_addr); return ptr->get(); } else if (IsSharedPtr()) { - const std::shared_ptr* ptr = - reinterpret_cast*>(opt_addr); + const auto ptr = GetOffsetAs>(base_addr); return ptr->get(); } else if (IsRawPtr()) { - const T* const* ptr = reinterpret_cast(opt_addr); + const T* const* ptr = GetOffsetAs(base_addr); return *ptr; } else { - return reinterpret_cast(opt_addr); + return GetOffsetAs(base_addr); } } @@ -612,18 +761,17 @@ class OptionTypeInfo { if (base_addr == nullptr) { return nullptr; } - auto opt_addr = reinterpret_cast(base_addr) + offset_; if (IsUniquePtr()) { - std::unique_ptr* ptr = reinterpret_cast*>(opt_addr); + auto ptr = GetOffsetAs>(base_addr); return ptr->get(); } else if (IsSharedPtr()) { - std::shared_ptr* ptr = reinterpret_cast*>(opt_addr); + auto ptr = GetOffsetAs>(base_addr); return ptr->get(); } else if (IsRawPtr()) { - T** ptr = reinterpret_cast(opt_addr); + auto ptr = GetOffsetAs(base_addr); return *ptr; } else { - return reinterpret_cast(opt_addr); + return GetOffsetAs(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& type_map, + void* opt_addr, + std::unordered_map* unused = nullptr); + static Status ParseType( + const ConfigOptions& config_options, + const std::unordered_map& opts_map, + const std::unordered_map& type_map, + void* opt_addr, + std::unordered_map* 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* 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& 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* 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& 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* 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 +Status ParseArray(const ConfigOptions& config_options, + const OptionTypeInfo& elem_info, char separator, + const std::string& name, const std::string& value, + std::array* 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 +Status SerializeArray(const ConfigOptions& config_options, + const OptionTypeInfo& elem_info, char separator, + const std::string& name, + const std::array& 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 +bool ArraysAreEqual(const ConfigOptions& config_options, + const OptionTypeInfo& elem_info, const std::string& name, + const std::array& array1, + const std::array& 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(&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(&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 bool VectorsAreEqual(const ConfigOptions& config_options,