1 /*
2  *  Copyright 2019 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 #include "rtc_base/experiments/field_trial_parser.h"
11 
12 #include <inttypes.h>
13 
14 #include <algorithm>
15 #include <map>
16 #include <type_traits>
17 #include <utility>
18 
19 #include "rtc_base/checks.h"
20 #include "rtc_base/logging.h"
21 #include "rtc_base/numerics/safe_conversions.h"
22 
23 namespace webrtc {
24 namespace {
25 
FindOrEnd(std::string str,size_t start,char delimiter)26 int FindOrEnd(std::string str, size_t start, char delimiter) {
27   size_t pos = str.find(delimiter, start);
28   pos = (pos == std::string::npos) ? str.length() : pos;
29   return static_cast<int>(pos);
30 }
31 }  // namespace
32 
FieldTrialParameterInterface(std::string key)33 FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key)
34     : key_(key) {}
~FieldTrialParameterInterface()35 FieldTrialParameterInterface::~FieldTrialParameterInterface() {
36   RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_
37                     << "' never used.";
38 }
39 
ParseFieldTrial(std::initializer_list<FieldTrialParameterInterface * > fields,std::string trial_string)40 void ParseFieldTrial(
41     std::initializer_list<FieldTrialParameterInterface*> fields,
42     std::string trial_string) {
43   std::map<std::string, FieldTrialParameterInterface*> field_map;
44   FieldTrialParameterInterface* keyless_field = nullptr;
45   for (FieldTrialParameterInterface* field : fields) {
46     field->MarkAsUsed();
47     if (!field->sub_parameters_.empty()) {
48       for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) {
49         RTC_DCHECK(!sub_field->key_.empty());
50         sub_field->MarkAsUsed();
51         field_map[sub_field->key_] = sub_field;
52       }
53       continue;
54     }
55 
56     if (field->key_.empty()) {
57       RTC_DCHECK(!keyless_field);
58       keyless_field = field;
59     } else {
60       field_map[field->key_] = field;
61     }
62   }
63 
64   size_t i = 0;
65   while (i < trial_string.length()) {
66     int val_end = FindOrEnd(trial_string, i, ',');
67     int colon_pos = FindOrEnd(trial_string, i, ':');
68     int key_end = std::min(val_end, colon_pos);
69     int val_begin = key_end + 1;
70     std::string key = trial_string.substr(i, key_end - i);
71     absl::optional<std::string> opt_value;
72     if (val_end >= val_begin)
73       opt_value = trial_string.substr(val_begin, val_end - val_begin);
74     i = val_end + 1;
75     auto field = field_map.find(key);
76     if (field != field_map.end()) {
77       if (!field->second->Parse(std::move(opt_value))) {
78         RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key
79                             << "' in trial: \"" << trial_string << "\"";
80       }
81     } else if (!opt_value && keyless_field && !key.empty()) {
82       if (!keyless_field->Parse(key)) {
83         RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '"
84                             << key << "' in trial: \"" << trial_string << "\"";
85       }
86     } else {
87       RTC_LOG(LS_INFO) << "No field with key: '" << key
88                        << "' (found in trial: \"" << trial_string << "\")";
89       std::string valid_keys;
90       for (const auto& f : field_map) {
91         valid_keys += f.first;
92         valid_keys += ", ";
93       }
94       RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys;
95     }
96   }
97 
98   for (FieldTrialParameterInterface* field : fields) {
99     field->ParseDone();
100   }
101 }
102 
103 template <>
ParseTypedParameter(std::string str)104 absl::optional<bool> ParseTypedParameter<bool>(std::string str) {
105   if (str == "true" || str == "1") {
106     return true;
107   } else if (str == "false" || str == "0") {
108     return false;
109   }
110   return absl::nullopt;
111 }
112 
113 template <>
ParseTypedParameter(std::string str)114 absl::optional<double> ParseTypedParameter<double>(std::string str) {
115   double value;
116   char unit[2]{0, 0};
117   if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) {
118     if (unit[0] == '%')
119       return value / 100;
120     return value;
121   } else {
122     return absl::nullopt;
123   }
124 }
125 
126 template <>
ParseTypedParameter(std::string str)127 absl::optional<int> ParseTypedParameter<int>(std::string str) {
128   int64_t value;
129   if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) {
130     if (rtc::IsValueInRangeForNumericType<int, int64_t>(value)) {
131       return static_cast<int>(value);
132     }
133   }
134   return absl::nullopt;
135 }
136 
137 template <>
ParseTypedParameter(std::string str)138 absl::optional<unsigned> ParseTypedParameter<unsigned>(std::string str) {
139   int64_t value;
140   if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) {
141     if (rtc::IsValueInRangeForNumericType<unsigned, int64_t>(value)) {
142       return static_cast<unsigned>(value);
143     }
144   }
145   return absl::nullopt;
146 }
147 
148 template <>
ParseTypedParameter(std::string str)149 absl::optional<std::string> ParseTypedParameter<std::string>(std::string str) {
150   return std::move(str);
151 }
152 
153 template <>
ParseTypedParameter(std::string str)154 absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
155     std::string str) {
156   return ParseOptionalParameter<bool>(str);
157 }
158 template <>
ParseTypedParameter(std::string str)159 absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
160     std::string str) {
161   return ParseOptionalParameter<int>(str);
162 }
163 template <>
164 absl::optional<absl::optional<unsigned>>
ParseTypedParameter(std::string str)165 ParseTypedParameter<absl::optional<unsigned>>(std::string str) {
166   return ParseOptionalParameter<unsigned>(str);
167 }
168 template <>
169 absl::optional<absl::optional<double>>
ParseTypedParameter(std::string str)170 ParseTypedParameter<absl::optional<double>>(std::string str) {
171   return ParseOptionalParameter<double>(str);
172 }
173 
FieldTrialFlag(std::string key)174 FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {}
175 
FieldTrialFlag(std::string key,bool default_value)176 FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value)
177     : FieldTrialParameterInterface(key), value_(default_value) {}
178 
Get() const179 bool FieldTrialFlag::Get() const {
180   return value_;
181 }
182 
operator bool() const183 webrtc::FieldTrialFlag::operator bool() const {
184   return value_;
185 }
186 
Parse(absl::optional<std::string> str_value)187 bool FieldTrialFlag::Parse(absl::optional<std::string> str_value) {
188   // Only set the flag if there is no argument provided.
189   if (str_value) {
190     absl::optional<bool> opt_value = ParseTypedParameter<bool>(*str_value);
191     if (!opt_value)
192       return false;
193     value_ = *opt_value;
194   } else {
195     value_ = true;
196   }
197   return true;
198 }
199 
AbstractFieldTrialEnum(std::string key,int default_value,std::map<std::string,int> mapping)200 AbstractFieldTrialEnum::AbstractFieldTrialEnum(
201     std::string key,
202     int default_value,
203     std::map<std::string, int> mapping)
204     : FieldTrialParameterInterface(key),
205       value_(default_value),
206       enum_mapping_(mapping) {
207   for (auto& key_val : enum_mapping_)
208     valid_values_.insert(key_val.second);
209 }
210 AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) =
211     default;
212 AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default;
213 
Parse(absl::optional<std::string> str_value)214 bool AbstractFieldTrialEnum::Parse(absl::optional<std::string> str_value) {
215   if (str_value) {
216     auto it = enum_mapping_.find(*str_value);
217     if (it != enum_mapping_.end()) {
218       value_ = it->second;
219       return true;
220     }
221     absl::optional<int> value = ParseTypedParameter<int>(*str_value);
222     if (value.has_value() &&
223         (valid_values_.find(*value) != valid_values_.end())) {
224       value_ = *value;
225       return true;
226     }
227   }
228   return false;
229 }
230 
231 template class FieldTrialParameter<bool>;
232 template class FieldTrialParameter<double>;
233 template class FieldTrialParameter<int>;
234 template class FieldTrialParameter<unsigned>;
235 template class FieldTrialParameter<std::string>;
236 
237 template class FieldTrialConstrained<double>;
238 template class FieldTrialConstrained<int>;
239 template class FieldTrialConstrained<unsigned>;
240 
241 template class FieldTrialOptional<double>;
242 template class FieldTrialOptional<int>;
243 template class FieldTrialOptional<unsigned>;
244 template class FieldTrialOptional<bool>;
245 template class FieldTrialOptional<std::string>;
246 
247 }  // namespace webrtc
248