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