/* * Copyright 2018 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ #define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ #include #include #include #include #include #include #include "absl/types/optional.h" // Field trial parser functionality. Provides funcitonality to parse field trial // argument strings in key:value format. Each parameter is described using // key:value, parameters are separated with a ,. Values can't include the comma // character, since there's no quote facility. For most types, white space is // ignored. Parameters are declared with a given type for which an // implementation of ParseTypedParameter should be provided. The // ParseTypedParameter implementation is given whatever is between the : and the // ,. If the key is provided without : a FieldTrialOptional will use nullopt. // Example string: "my_optional,my_int:3,my_string:hello" // For further description of usage and behavior, see the examples in the unit // tests. namespace webrtc { class FieldTrialParameterInterface { public: virtual ~FieldTrialParameterInterface(); std::string key() const { return key_; } protected: // Protected to allow implementations to provide assignment and copy. FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default; FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) = default; explicit FieldTrialParameterInterface(std::string key); friend void ParseFieldTrial( std::initializer_list fields, std::string raw_string); void MarkAsUsed() { used_ = true; } virtual bool Parse(absl::optional str_value) = 0; virtual void ParseDone() {} std::vector sub_parameters_; private: std::string key_; bool used_ = false; }; // ParseFieldTrial function parses the given string and fills the given fields // with extracted values if available. void ParseFieldTrial( std::initializer_list fields, std::string raw_string); // Specialize this in code file for custom types. Should return absl::nullopt if // the given string cannot be properly parsed. template absl::optional ParseTypedParameter(std::string); // This class uses the ParseTypedParameter function to implement a parameter // implementation with an enforced default value. template class FieldTrialParameter : public FieldTrialParameterInterface { public: FieldTrialParameter(std::string key, T default_value) : FieldTrialParameterInterface(key), value_(default_value) {} T Get() const { return value_; } operator T() const { return Get(); } const T* operator->() const { return &value_; } void SetForTest(T value) { value_ = value; } protected: bool Parse(absl::optional str_value) override { if (str_value) { absl::optional value = ParseTypedParameter(*str_value); if (value.has_value()) { value_ = value.value(); return true; } } return false; } private: T value_; }; // This class uses the ParseTypedParameter function to implement a parameter // implementation with an enforced default value and a range constraint. Values // outside the configured range will be ignored. template class FieldTrialConstrained : public FieldTrialParameterInterface { public: FieldTrialConstrained(std::string key, T default_value, absl::optional lower_limit, absl::optional upper_limit) : FieldTrialParameterInterface(key), value_(default_value), lower_limit_(lower_limit), upper_limit_(upper_limit) {} T Get() const { return value_; } operator T() const { return Get(); } const T* operator->() const { return &value_; } protected: bool Parse(absl::optional str_value) override { if (str_value) { absl::optional value = ParseTypedParameter(*str_value); if (value && (!lower_limit_ || *value >= *lower_limit_) && (!upper_limit_ || *value <= *upper_limit_)) { value_ = *value; return true; } } return false; } private: T value_; absl::optional lower_limit_; absl::optional upper_limit_; }; class AbstractFieldTrialEnum : public FieldTrialParameterInterface { public: AbstractFieldTrialEnum(std::string key, int default_value, std::map mapping); ~AbstractFieldTrialEnum() override; AbstractFieldTrialEnum(const AbstractFieldTrialEnum&); protected: bool Parse(absl::optional str_value) override; protected: int value_; std::map enum_mapping_; std::set valid_values_; }; // The FieldTrialEnum class can be used to quickly define a parser for a // specific enum. It handles values provided as integers and as strings if a // mapping is provided. template class FieldTrialEnum : public AbstractFieldTrialEnum { public: FieldTrialEnum(std::string key, T default_value, std::map mapping) : AbstractFieldTrialEnum(key, static_cast(default_value), ToIntMap(mapping)) {} T Get() const { return static_cast(value_); } operator T() const { return Get(); } private: static std::map ToIntMap(std::map mapping) { std::map res; for (const auto& it : mapping) res[it.first] = static_cast(it.second); return res; } }; // This class uses the ParseTypedParameter function to implement an optional // parameter implementation that can default to absl::nullopt. template class FieldTrialOptional : public FieldTrialParameterInterface { public: explicit FieldTrialOptional(std::string key) : FieldTrialParameterInterface(key) {} FieldTrialOptional(std::string key, absl::optional default_value) : FieldTrialParameterInterface(key), value_(default_value) {} absl::optional GetOptional() const { return value_; } const T& Value() const { return value_.value(); } const T& operator*() const { return value_.value(); } const T* operator->() const { return &value_.value(); } explicit operator bool() const { return value_.has_value(); } protected: bool Parse(absl::optional str_value) override { if (str_value) { absl::optional value = ParseTypedParameter(*str_value); if (!value.has_value()) return false; value_ = value.value(); } else { value_ = absl::nullopt; } return true; } private: absl::optional value_; }; // Equivalent to a FieldTrialParameter in the case that both key and value // are present. If key is missing, evaluates to false. If key is present, but no // explicit value is provided, the flag evaluates to true. class FieldTrialFlag : public FieldTrialParameterInterface { public: explicit FieldTrialFlag(std::string key); FieldTrialFlag(std::string key, bool default_value); bool Get() const; operator bool() const; protected: bool Parse(absl::optional str_value) override; private: bool value_; }; template absl::optional> ParseOptionalParameter(std::string str) { if (str.empty()) return absl::optional(); auto parsed = ParseTypedParameter(str); if (parsed.has_value()) return parsed; return absl::nullopt; } template <> absl::optional ParseTypedParameter(std::string str); template <> absl::optional ParseTypedParameter(std::string str); template <> absl::optional ParseTypedParameter(std::string str); template <> absl::optional ParseTypedParameter(std::string str); template <> absl::optional ParseTypedParameter(std::string str); template <> absl::optional> ParseTypedParameter>( std::string str); template <> absl::optional> ParseTypedParameter>( std::string str); template <> absl::optional> ParseTypedParameter>(std::string str); template <> absl::optional> ParseTypedParameter>(std::string str); // Accepts true, false, else parsed with sscanf %i, true if != 0. extern template class FieldTrialParameter; // Interpreted using sscanf %lf. extern template class FieldTrialParameter; // Interpreted using sscanf %i. extern template class FieldTrialParameter; // Interpreted using sscanf %u. extern template class FieldTrialParameter; // Using the given value as is. extern template class FieldTrialParameter; extern template class FieldTrialConstrained; extern template class FieldTrialConstrained; extern template class FieldTrialConstrained; extern template class FieldTrialOptional; extern template class FieldTrialOptional; extern template class FieldTrialOptional; extern template class FieldTrialOptional; extern template class FieldTrialOptional; } // namespace webrtc #endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_