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