xref: /aosp_15_r20/external/webrtc/rtc_base/experiments/field_trial_parser.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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