blob: 4f68e5c592b11486cb8712272137c4df0b1d4419 [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
12#include <algorithm>
13#include <map>
14#include <type_traits>
15#include <utility>
16
Sebastian Janssonfea46372018-09-03 10:15:13 +020017#include "rtc_base/checks.h"
Sebastian Jansson9eb38862018-06-14 16:47:42 +020018#include "rtc_base/logging.h"
19
20namespace webrtc {
21namespace {
22
23int FindOrEnd(std::string str, size_t start, char delimiter) {
24 size_t pos = str.find(delimiter, start);
25 pos = (pos == std::string::npos) ? str.length() : pos;
26 return static_cast<int>(pos);
27}
28} // namespace
29
30FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key)
31 : key_(key) {}
Sebastian Janssonfea46372018-09-03 10:15:13 +020032FieldTrialParameterInterface::~FieldTrialParameterInterface() {
33 RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_
34 << "' never used.";
35}
Sebastian Jansson9eb38862018-06-14 16:47:42 +020036
37void ParseFieldTrial(
38 std::initializer_list<FieldTrialParameterInterface*> fields,
39 std::string trial_string) {
40 std::map<std::string, FieldTrialParameterInterface*> field_map;
Sebastian Janssond69998a2018-12-20 12:25:19 +010041 FieldTrialParameterInterface* keyless_field = nullptr;
Sebastian Jansson9eb38862018-06-14 16:47:42 +020042 for (FieldTrialParameterInterface* field : fields) {
Sebastian Janssonfea46372018-09-03 10:15:13 +020043 field->MarkAsUsed();
Jonas Olsson97d84ef2019-04-11 11:53:26 +020044 if (!field->sub_parameters_.empty()) {
45 for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) {
46 RTC_DCHECK(!sub_field->key_.empty());
47 sub_field->MarkAsUsed();
48 field_map[sub_field->key_] = sub_field;
49 }
50 continue;
51 }
52
53 if (field->key_.empty()) {
Sebastian Janssond69998a2018-12-20 12:25:19 +010054 RTC_DCHECK(!keyless_field);
55 keyless_field = field;
56 } else {
Jonas Olsson97d84ef2019-04-11 11:53:26 +020057 field_map[field->key_] = field;
Sebastian Janssond69998a2018-12-20 12:25:19 +010058 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +020059 }
Jonas Olsson97d84ef2019-04-11 11:53:26 +020060
Sebastian Jansson9eb38862018-06-14 16:47:42 +020061 size_t i = 0;
62 while (i < trial_string.length()) {
63 int val_end = FindOrEnd(trial_string, i, ',');
64 int colon_pos = FindOrEnd(trial_string, i, ':');
65 int key_end = std::min(val_end, colon_pos);
66 int val_begin = key_end + 1;
67 std::string key = trial_string.substr(i, key_end - i);
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020068 absl::optional<std::string> opt_value;
Sebastian Jansson9eb38862018-06-14 16:47:42 +020069 if (val_end >= val_begin)
70 opt_value = trial_string.substr(val_begin, val_end - val_begin);
71 i = val_end + 1;
72 auto field = field_map.find(key);
73 if (field != field_map.end()) {
74 if (!field->second->Parse(std::move(opt_value))) {
75 RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key
76 << "' in trial: \"" << trial_string << "\"";
77 }
Sebastian Janssond69998a2018-12-20 12:25:19 +010078 } else if (!opt_value && keyless_field && !key.empty()) {
79 if (!keyless_field->Parse(key)) {
80 RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '"
81 << key << "' in trial: \"" << trial_string << "\"";
82 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +020083 } else {
84 RTC_LOG(LS_INFO) << "No field with key: '" << key
85 << "' (found in trial: \"" << trial_string << "\")";
86 }
87 }
Jonas Olsson97d84ef2019-04-11 11:53:26 +020088
89 for (FieldTrialParameterInterface* field : fields) {
90 field->ParseDone();
91 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +020092}
93
94template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020095absl::optional<bool> ParseTypedParameter<bool>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +020096 if (str == "true" || str == "1") {
97 return true;
98 } else if (str == "false" || str == "0") {
99 return false;
100 }
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200101 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200102}
103
104template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200105absl::optional<double> ParseTypedParameter<double>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200106 double value;
Sebastian Janssonec764662018-09-03 11:31:26 +0200107 char unit[2]{0, 0};
108 if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) {
109 if (unit[0] == '%')
110 return value / 100;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200111 return value;
112 } else {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200113 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200114 }
115}
116
117template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200118absl::optional<int> ParseTypedParameter<int>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200119 int value;
120 if (sscanf(str.c_str(), "%i", &value) == 1) {
121 return value;
122 } else {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200123 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200124 }
125}
126
127template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200128absl::optional<std::string> ParseTypedParameter<std::string>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200129 return std::move(str);
130}
131
Sebastian Jansson55251c32019-08-08 11:14:51 +0200132template <>
133absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
134 std::string str) {
135 return ParseOptionalParameter<bool>(str);
136}
137template <>
138absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
139 std::string str) {
140 return ParseOptionalParameter<int>(str);
141}
142template <>
143absl::optional<absl::optional<double>>
144ParseTypedParameter<absl::optional<double>>(std::string str) {
145 return ParseOptionalParameter<double>(str);
146}
147
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200148FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {}
149
150FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value)
151 : FieldTrialParameterInterface(key), value_(default_value) {}
152
153bool FieldTrialFlag::Get() const {
154 return value_;
155}
156
Sebastian Janssonfea46372018-09-03 10:15:13 +0200157webrtc::FieldTrialFlag::operator bool() const {
158 return value_;
159}
160
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200161bool FieldTrialFlag::Parse(absl::optional<std::string> str_value) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200162 // Only set the flag if there is no argument provided.
163 if (str_value) {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200164 absl::optional<bool> opt_value = ParseTypedParameter<bool>(*str_value);
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200165 if (!opt_value)
166 return false;
167 value_ = *opt_value;
168 } else {
169 value_ = true;
170 }
171 return true;
172}
173
Sebastian Jansson2c74d852018-06-26 10:30:16 +0200174AbstractFieldTrialEnum::AbstractFieldTrialEnum(
175 std::string key,
176 int default_value,
177 std::map<std::string, int> mapping)
178 : FieldTrialParameterInterface(key),
179 value_(default_value),
180 enum_mapping_(mapping) {
181 for (auto& key_val : enum_mapping_)
182 valid_values_.insert(key_val.second);
183}
184AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) =
185 default;
186AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default;
187
188bool AbstractFieldTrialEnum::Parse(absl::optional<std::string> str_value) {
189 if (str_value) {
190 auto it = enum_mapping_.find(*str_value);
191 if (it != enum_mapping_.end()) {
192 value_ = it->second;
193 return true;
194 }
195 absl::optional<int> value = ParseTypedParameter<int>(*str_value);
196 if (value.has_value() &&
197 (valid_values_.find(*value) != valid_values_.end())) {
198 value_ = *value;
199 return true;
200 }
201 }
202 return false;
203}
204
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200205template class FieldTrialParameter<bool>;
206template class FieldTrialParameter<double>;
207template class FieldTrialParameter<int>;
208template class FieldTrialParameter<std::string>;
209
Sebastian Janssonb22f0772018-11-19 17:44:33 +0100210template class FieldTrialConstrained<double>;
211template class FieldTrialConstrained<int>;
212
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200213template class FieldTrialOptional<double>;
214template class FieldTrialOptional<int>;
215template class FieldTrialOptional<bool>;
216template class FieldTrialOptional<std::string>;
217
218} // namespace webrtc