blob: 8fc89cec8fe8793bfc084ec162e3435e8f7f5f86 [file] [log] [blame]
Sebastian Jansson9eb38862018-06-14 16:47:42 +02001/*
Jonas Olsson97d84ef2019-04-11 11:53:26 +02002 * Copyright 2019 The WebRTC project authors. All Rights Reserved.
Sebastian Jansson9eb38862018-06-14 16:47:42 +02003 *
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
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +020012#include <inttypes.h>
13
Sebastian Jansson9eb38862018-06-14 16:47:42 +020014#include <algorithm>
15#include <map>
16#include <type_traits>
17#include <utility>
18
Sebastian Janssonfea46372018-09-03 10:15:13 +020019#include "rtc_base/checks.h"
Sebastian Jansson9eb38862018-06-14 16:47:42 +020020#include "rtc_base/logging.h"
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +020021#include "rtc_base/numerics/safe_conversions.h"
Sebastian Jansson9eb38862018-06-14 16:47:42 +020022
23namespace webrtc {
24namespace {
25
26int 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
33FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key)
34 : key_(key) {}
Sebastian Janssonfea46372018-09-03 10:15:13 +020035FieldTrialParameterInterface::~FieldTrialParameterInterface() {
36 RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_
37 << "' never used.";
38}
Sebastian Jansson9eb38862018-06-14 16:47:42 +020039
40void ParseFieldTrial(
41 std::initializer_list<FieldTrialParameterInterface*> fields,
42 std::string trial_string) {
43 std::map<std::string, FieldTrialParameterInterface*> field_map;
Sebastian Janssond69998a2018-12-20 12:25:19 +010044 FieldTrialParameterInterface* keyless_field = nullptr;
Sebastian Jansson9eb38862018-06-14 16:47:42 +020045 for (FieldTrialParameterInterface* field : fields) {
Sebastian Janssonfea46372018-09-03 10:15:13 +020046 field->MarkAsUsed();
Jonas Olsson97d84ef2019-04-11 11:53:26 +020047 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()) {
Sebastian Janssond69998a2018-12-20 12:25:19 +010057 RTC_DCHECK(!keyless_field);
58 keyless_field = field;
59 } else {
Jonas Olsson97d84ef2019-04-11 11:53:26 +020060 field_map[field->key_] = field;
Sebastian Janssond69998a2018-12-20 12:25:19 +010061 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +020062 }
Jonas Olsson97d84ef2019-04-11 11:53:26 +020063
Sebastian Jansson9eb38862018-06-14 16:47:42 +020064 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);
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020071 absl::optional<std::string> opt_value;
Sebastian Jansson9eb38862018-06-14 16:47:42 +020072 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 }
Sebastian Janssond69998a2018-12-20 12:25:19 +010081 } 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 }
Ying Wang22e37d82021-02-02 15:36:16 +010086 } else if (key.empty() || key[0] != '_') {
87 // "_" is be used to prefix keys that are part of the string for
88 // debugging purposes but not neccessarily used.
89 // e.g. WebRTC-Experiment/param: value, _DebuggingString
Sebastian Jansson9eb38862018-06-14 16:47:42 +020090 RTC_LOG(LS_INFO) << "No field with key: '" << key
91 << "' (found in trial: \"" << trial_string << "\")";
Björn Terelius86873f02019-09-18 13:37:30 +020092 std::string valid_keys;
93 for (const auto& f : field_map) {
94 valid_keys += f.first;
95 valid_keys += ", ";
96 }
97 RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys;
Sebastian Jansson9eb38862018-06-14 16:47:42 +020098 }
99 }
Jonas Olsson97d84ef2019-04-11 11:53:26 +0200100
101 for (FieldTrialParameterInterface* field : fields) {
102 field->ParseDone();
103 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200104}
105
106template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200107absl::optional<bool> ParseTypedParameter<bool>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200108 if (str == "true" || str == "1") {
109 return true;
110 } else if (str == "false" || str == "0") {
111 return false;
112 }
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200113 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200114}
115
116template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200117absl::optional<double> ParseTypedParameter<double>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200118 double value;
Sebastian Janssonec764662018-09-03 11:31:26 +0200119 char unit[2]{0, 0};
120 if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) {
121 if (unit[0] == '%')
122 return value / 100;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200123 return value;
124 } else {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200125 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200126 }
127}
128
129template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200130absl::optional<int> ParseTypedParameter<int>(std::string str) {
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +0200131 int64_t value;
132 if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) {
133 if (rtc::IsValueInRangeForNumericType<int, int64_t>(value)) {
134 return static_cast<int>(value);
135 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200136 }
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +0200137 return absl::nullopt;
138}
139
140template <>
141absl::optional<unsigned> ParseTypedParameter<unsigned>(std::string str) {
142 int64_t value;
143 if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) {
144 if (rtc::IsValueInRangeForNumericType<unsigned, int64_t>(value)) {
145 return static_cast<unsigned>(value);
146 }
147 }
148 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200149}
150
151template <>
Philip Eliasson29ab4872019-09-10 14:05:43 +0000152absl::optional<std::string> ParseTypedParameter<std::string>(std::string str) {
153 return std::move(str);
154}
155
156template <>
Sebastian Jansson55251c32019-08-08 11:14:51 +0200157absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
158 std::string str) {
159 return ParseOptionalParameter<bool>(str);
160}
161template <>
162absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
163 std::string str) {
164 return ParseOptionalParameter<int>(str);
165}
166template <>
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +0200167absl::optional<absl::optional<unsigned>>
168ParseTypedParameter<absl::optional<unsigned>>(std::string str) {
169 return ParseOptionalParameter<unsigned>(str);
170}
171template <>
Sebastian Jansson55251c32019-08-08 11:14:51 +0200172absl::optional<absl::optional<double>>
173ParseTypedParameter<absl::optional<double>>(std::string str) {
174 return ParseOptionalParameter<double>(str);
175}
176
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200177FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {}
178
179FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value)
180 : FieldTrialParameterInterface(key), value_(default_value) {}
181
182bool FieldTrialFlag::Get() const {
183 return value_;
184}
185
Sebastian Janssonfea46372018-09-03 10:15:13 +0200186webrtc::FieldTrialFlag::operator bool() const {
187 return value_;
188}
189
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200190bool FieldTrialFlag::Parse(absl::optional<std::string> str_value) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200191 // Only set the flag if there is no argument provided.
192 if (str_value) {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200193 absl::optional<bool> opt_value = ParseTypedParameter<bool>(*str_value);
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200194 if (!opt_value)
195 return false;
196 value_ = *opt_value;
197 } else {
198 value_ = true;
199 }
200 return true;
201}
202
Sebastian Jansson2c74d852018-06-26 10:30:16 +0200203AbstractFieldTrialEnum::AbstractFieldTrialEnum(
204 std::string key,
205 int default_value,
206 std::map<std::string, int> mapping)
207 : FieldTrialParameterInterface(key),
208 value_(default_value),
209 enum_mapping_(mapping) {
210 for (auto& key_val : enum_mapping_)
211 valid_values_.insert(key_val.second);
212}
213AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) =
214 default;
215AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default;
216
217bool AbstractFieldTrialEnum::Parse(absl::optional<std::string> str_value) {
218 if (str_value) {
219 auto it = enum_mapping_.find(*str_value);
220 if (it != enum_mapping_.end()) {
221 value_ = it->second;
222 return true;
223 }
224 absl::optional<int> value = ParseTypedParameter<int>(*str_value);
225 if (value.has_value() &&
226 (valid_values_.find(*value) != valid_values_.end())) {
227 value_ = *value;
228 return true;
229 }
230 }
231 return false;
232}
233
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200234template class FieldTrialParameter<bool>;
235template class FieldTrialParameter<double>;
236template class FieldTrialParameter<int>;
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +0200237template class FieldTrialParameter<unsigned>;
Philip Eliasson29ab4872019-09-10 14:05:43 +0000238template class FieldTrialParameter<std::string>;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200239
Sebastian Janssonb22f0772018-11-19 17:44:33 +0100240template class FieldTrialConstrained<double>;
241template class FieldTrialConstrained<int>;
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +0200242template class FieldTrialConstrained<unsigned>;
Sebastian Janssonb22f0772018-11-19 17:44:33 +0100243
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200244template class FieldTrialOptional<double>;
245template class FieldTrialOptional<int>;
Bjorn Terelius9f00f0e2019-08-30 09:39:31 +0200246template class FieldTrialOptional<unsigned>;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200247template class FieldTrialOptional<bool>;
Philip Eliasson29ab4872019-09-10 14:05:43 +0000248template class FieldTrialOptional<std::string>;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200249
250} // namespace webrtc