1 /*
2  *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
11 #define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
12 
13 #include <stdint.h>
14 
15 #include <initializer_list>
16 #include <map>
17 #include <set>
18 #include <string>
19 #include <vector>
20 
21 #include "absl/types/optional.h"
22 
23 // Field trial parser functionality. Provides funcitonality to parse field trial
24 // argument strings in key:value format. Each parameter is described using
25 // key:value, parameters are separated with a ,. Values can't include the comma
26 // character, since there's no quote facility. For most types, white space is
27 // ignored. Parameters are declared with a given type for which an
28 // implementation of ParseTypedParameter should be provided. The
29 // ParseTypedParameter implementation is given whatever is between the : and the
30 // ,. If the key is provided without : a FieldTrialOptional will use nullopt.
31 
32 // Example string: "my_optional,my_int:3,my_string:hello"
33 
34 // For further description of usage and behavior, see the examples in the unit
35 // tests.
36 
37 namespace webrtc {
38 class FieldTrialParameterInterface {
39  public:
40   virtual ~FieldTrialParameterInterface();
key()41   std::string key() const { return key_; }
42 
43  protected:
44   // Protected to allow implementations to provide assignment and copy.
45   FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default;
46   FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) =
47       default;
48   explicit FieldTrialParameterInterface(std::string key);
49   friend void ParseFieldTrial(
50       std::initializer_list<FieldTrialParameterInterface*> fields,
51       std::string raw_string);
MarkAsUsed()52   void MarkAsUsed() { used_ = true; }
53   virtual bool Parse(absl::optional<std::string> str_value) = 0;
54 
ParseDone()55   virtual void ParseDone() {}
56 
57   std::vector<FieldTrialParameterInterface*> sub_parameters_;
58 
59  private:
60   std::string key_;
61   bool used_ = false;
62 };
63 
64 // ParseFieldTrial function parses the given string and fills the given fields
65 // with extracted values if available.
66 void ParseFieldTrial(
67     std::initializer_list<FieldTrialParameterInterface*> fields,
68     std::string raw_string);
69 
70 // Specialize this in code file for custom types. Should return absl::nullopt if
71 // the given string cannot be properly parsed.
72 template <typename T>
73 absl::optional<T> ParseTypedParameter(std::string);
74 
75 // This class uses the ParseTypedParameter function to implement a parameter
76 // implementation with an enforced default value.
77 template <typename T>
78 class FieldTrialParameter : public FieldTrialParameterInterface {
79  public:
FieldTrialParameter(std::string key,T default_value)80   FieldTrialParameter(std::string key, T default_value)
81       : FieldTrialParameterInterface(key), value_(default_value) {}
Get()82   T Get() const { return value_; }
T()83   operator T() const { return Get(); }
84   const T* operator->() const { return &value_; }
85 
SetForTest(T value)86   void SetForTest(T value) { value_ = value; }
87 
88  protected:
Parse(absl::optional<std::string> str_value)89   bool Parse(absl::optional<std::string> str_value) override {
90     if (str_value) {
91       absl::optional<T> value = ParseTypedParameter<T>(*str_value);
92       if (value.has_value()) {
93         value_ = value.value();
94         return true;
95       }
96     }
97     return false;
98   }
99 
100  private:
101   T value_;
102 };
103 
104 // This class uses the ParseTypedParameter function to implement a parameter
105 // implementation with an enforced default value and a range constraint. Values
106 // outside the configured range will be ignored.
107 template <typename T>
108 class FieldTrialConstrained : public FieldTrialParameterInterface {
109  public:
FieldTrialConstrained(std::string key,T default_value,absl::optional<T> lower_limit,absl::optional<T> upper_limit)110   FieldTrialConstrained(std::string key,
111                         T default_value,
112                         absl::optional<T> lower_limit,
113                         absl::optional<T> upper_limit)
114       : FieldTrialParameterInterface(key),
115         value_(default_value),
116         lower_limit_(lower_limit),
117         upper_limit_(upper_limit) {}
Get()118   T Get() const { return value_; }
T()119   operator T() const { return Get(); }
120   const T* operator->() const { return &value_; }
121 
122  protected:
Parse(absl::optional<std::string> str_value)123   bool Parse(absl::optional<std::string> str_value) override {
124     if (str_value) {
125       absl::optional<T> value = ParseTypedParameter<T>(*str_value);
126       if (value && (!lower_limit_ || *value >= *lower_limit_) &&
127           (!upper_limit_ || *value <= *upper_limit_)) {
128         value_ = *value;
129         return true;
130       }
131     }
132     return false;
133   }
134 
135  private:
136   T value_;
137   absl::optional<T> lower_limit_;
138   absl::optional<T> upper_limit_;
139 };
140 
141 class AbstractFieldTrialEnum : public FieldTrialParameterInterface {
142  public:
143   AbstractFieldTrialEnum(std::string key,
144                          int default_value,
145                          std::map<std::string, int> mapping);
146   ~AbstractFieldTrialEnum() override;
147   AbstractFieldTrialEnum(const AbstractFieldTrialEnum&);
148 
149  protected:
150   bool Parse(absl::optional<std::string> str_value) override;
151 
152  protected:
153   int value_;
154   std::map<std::string, int> enum_mapping_;
155   std::set<int> valid_values_;
156 };
157 
158 // The FieldTrialEnum class can be used to quickly define a parser for a
159 // specific enum. It handles values provided as integers and as strings if a
160 // mapping is provided.
161 template <typename T>
162 class FieldTrialEnum : public AbstractFieldTrialEnum {
163  public:
FieldTrialEnum(std::string key,T default_value,std::map<std::string,T> mapping)164   FieldTrialEnum(std::string key,
165                  T default_value,
166                  std::map<std::string, T> mapping)
167       : AbstractFieldTrialEnum(key,
168                                static_cast<int>(default_value),
169                                ToIntMap(mapping)) {}
Get()170   T Get() const { return static_cast<T>(value_); }
T()171   operator T() const { return Get(); }
172 
173  private:
ToIntMap(std::map<std::string,T> mapping)174   static std::map<std::string, int> ToIntMap(std::map<std::string, T> mapping) {
175     std::map<std::string, int> res;
176     for (const auto& it : mapping)
177       res[it.first] = static_cast<int>(it.second);
178     return res;
179   }
180 };
181 
182 // This class uses the ParseTypedParameter function to implement an optional
183 // parameter implementation that can default to absl::nullopt.
184 template <typename T>
185 class FieldTrialOptional : public FieldTrialParameterInterface {
186  public:
FieldTrialOptional(std::string key)187   explicit FieldTrialOptional(std::string key)
188       : FieldTrialParameterInterface(key) {}
FieldTrialOptional(std::string key,absl::optional<T> default_value)189   FieldTrialOptional(std::string key, absl::optional<T> default_value)
190       : FieldTrialParameterInterface(key), value_(default_value) {}
GetOptional()191   absl::optional<T> GetOptional() const { return value_; }
Value()192   const T& Value() const { return value_.value(); }
193   const T& operator*() const { return value_.value(); }
194   const T* operator->() const { return &value_.value(); }
195   explicit operator bool() const { return value_.has_value(); }
196 
197  protected:
Parse(absl::optional<std::string> str_value)198   bool Parse(absl::optional<std::string> str_value) override {
199     if (str_value) {
200       absl::optional<T> value = ParseTypedParameter<T>(*str_value);
201       if (!value.has_value())
202         return false;
203       value_ = value.value();
204     } else {
205       value_ = absl::nullopt;
206     }
207     return true;
208   }
209 
210  private:
211   absl::optional<T> value_;
212 };
213 
214 // Equivalent to a FieldTrialParameter<bool> in the case that both key and value
215 // are present. If key is missing, evaluates to false. If key is present, but no
216 // explicit value is provided, the flag evaluates to true.
217 class FieldTrialFlag : public FieldTrialParameterInterface {
218  public:
219   explicit FieldTrialFlag(std::string key);
220   FieldTrialFlag(std::string key, bool default_value);
221   bool Get() const;
222   operator bool() const;
223 
224  protected:
225   bool Parse(absl::optional<std::string> str_value) override;
226 
227  private:
228   bool value_;
229 };
230 
231 template <typename T>
ParseOptionalParameter(std::string str)232 absl::optional<absl::optional<T>> ParseOptionalParameter(std::string str) {
233   if (str.empty())
234     return absl::optional<T>();
235   auto parsed = ParseTypedParameter<T>(str);
236   if (parsed.has_value())
237     return parsed;
238   return absl::nullopt;
239 }
240 
241 template <>
242 absl::optional<bool> ParseTypedParameter<bool>(std::string str);
243 template <>
244 absl::optional<double> ParseTypedParameter<double>(std::string str);
245 template <>
246 absl::optional<int> ParseTypedParameter<int>(std::string str);
247 template <>
248 absl::optional<unsigned> ParseTypedParameter<unsigned>(std::string str);
249 template <>
250 absl::optional<std::string> ParseTypedParameter<std::string>(std::string str);
251 
252 template <>
253 absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
254     std::string str);
255 template <>
256 absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
257     std::string str);
258 template <>
259 absl::optional<absl::optional<unsigned>>
260 ParseTypedParameter<absl::optional<unsigned>>(std::string str);
261 template <>
262 absl::optional<absl::optional<double>>
263 ParseTypedParameter<absl::optional<double>>(std::string str);
264 
265 // Accepts true, false, else parsed with sscanf %i, true if != 0.
266 extern template class FieldTrialParameter<bool>;
267 // Interpreted using sscanf %lf.
268 extern template class FieldTrialParameter<double>;
269 // Interpreted using sscanf %i.
270 extern template class FieldTrialParameter<int>;
271 // Interpreted using sscanf %u.
272 extern template class FieldTrialParameter<unsigned>;
273 // Using the given value as is.
274 extern template class FieldTrialParameter<std::string>;
275 
276 extern template class FieldTrialConstrained<double>;
277 extern template class FieldTrialConstrained<int>;
278 extern template class FieldTrialConstrained<unsigned>;
279 
280 extern template class FieldTrialOptional<double>;
281 extern template class FieldTrialOptional<int>;
282 extern template class FieldTrialOptional<unsigned>;
283 extern template class FieldTrialOptional<bool>;
284 extern template class FieldTrialOptional<std::string>;
285 
286 }  // namespace webrtc
287 
288 #endif  // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
289