blob: f823b48e53a3d69bcd4cf37390669aa2a67c560c [file] [log] [blame]
Sebastian Jansson9eb38862018-06-14 16:47:42 +02001/*
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#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 +020036std::string FieldTrialParameterInterface::Key() const {
37 return key_;
38}
39
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();
Sebastian Janssond69998a2018-12-20 12:25:19 +010047 if (field->Key().empty()) {
48 RTC_DCHECK(!keyless_field);
49 keyless_field = field;
50 } else {
51 field_map[field->Key()] = field;
52 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +020053 }
54 size_t i = 0;
55 while (i < trial_string.length()) {
56 int val_end = FindOrEnd(trial_string, i, ',');
57 int colon_pos = FindOrEnd(trial_string, i, ':');
58 int key_end = std::min(val_end, colon_pos);
59 int val_begin = key_end + 1;
60 std::string key = trial_string.substr(i, key_end - i);
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020061 absl::optional<std::string> opt_value;
Sebastian Jansson9eb38862018-06-14 16:47:42 +020062 if (val_end >= val_begin)
63 opt_value = trial_string.substr(val_begin, val_end - val_begin);
64 i = val_end + 1;
65 auto field = field_map.find(key);
66 if (field != field_map.end()) {
67 if (!field->second->Parse(std::move(opt_value))) {
68 RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key
69 << "' in trial: \"" << trial_string << "\"";
70 }
Sebastian Janssond69998a2018-12-20 12:25:19 +010071 } else if (!opt_value && keyless_field && !key.empty()) {
72 if (!keyless_field->Parse(key)) {
73 RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '"
74 << key << "' in trial: \"" << trial_string << "\"";
75 }
Sebastian Jansson9eb38862018-06-14 16:47:42 +020076 } else {
77 RTC_LOG(LS_INFO) << "No field with key: '" << key
78 << "' (found in trial: \"" << trial_string << "\")";
79 }
80 }
81}
82
83template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020084absl::optional<bool> ParseTypedParameter<bool>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +020085 if (str == "true" || str == "1") {
86 return true;
87 } else if (str == "false" || str == "0") {
88 return false;
89 }
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020090 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +020091}
92
93template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020094absl::optional<double> ParseTypedParameter<double>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +020095 double value;
Sebastian Janssonec764662018-09-03 11:31:26 +020096 char unit[2]{0, 0};
97 if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) {
98 if (unit[0] == '%')
99 return value / 100;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200100 return value;
101 } else {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200102 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200103 }
104}
105
106template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200107absl::optional<int> ParseTypedParameter<int>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200108 int value;
109 if (sscanf(str.c_str(), "%i", &value) == 1) {
110 return value;
111 } else {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200112 return absl::nullopt;
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200113 }
114}
115
116template <>
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200117absl::optional<std::string> ParseTypedParameter<std::string>(std::string str) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200118 return std::move(str);
119}
120
121FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {}
122
123FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value)
124 : FieldTrialParameterInterface(key), value_(default_value) {}
125
126bool FieldTrialFlag::Get() const {
127 return value_;
128}
129
Sebastian Janssonfea46372018-09-03 10:15:13 +0200130webrtc::FieldTrialFlag::operator bool() const {
131 return value_;
132}
133
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200134bool FieldTrialFlag::Parse(absl::optional<std::string> str_value) {
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200135 // Only set the flag if there is no argument provided.
136 if (str_value) {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200137 absl::optional<bool> opt_value = ParseTypedParameter<bool>(*str_value);
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200138 if (!opt_value)
139 return false;
140 value_ = *opt_value;
141 } else {
142 value_ = true;
143 }
144 return true;
145}
146
Sebastian Jansson2c74d852018-06-26 10:30:16 +0200147AbstractFieldTrialEnum::AbstractFieldTrialEnum(
148 std::string key,
149 int default_value,
150 std::map<std::string, int> mapping)
151 : FieldTrialParameterInterface(key),
152 value_(default_value),
153 enum_mapping_(mapping) {
154 for (auto& key_val : enum_mapping_)
155 valid_values_.insert(key_val.second);
156}
157AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) =
158 default;
159AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default;
160
161bool AbstractFieldTrialEnum::Parse(absl::optional<std::string> str_value) {
162 if (str_value) {
163 auto it = enum_mapping_.find(*str_value);
164 if (it != enum_mapping_.end()) {
165 value_ = it->second;
166 return true;
167 }
168 absl::optional<int> value = ParseTypedParameter<int>(*str_value);
169 if (value.has_value() &&
170 (valid_values_.find(*value) != valid_values_.end())) {
171 value_ = *value;
172 return true;
173 }
174 }
175 return false;
176}
177
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200178template class FieldTrialParameter<bool>;
179template class FieldTrialParameter<double>;
180template class FieldTrialParameter<int>;
181template class FieldTrialParameter<std::string>;
182
Sebastian Janssonb22f0772018-11-19 17:44:33 +0100183template class FieldTrialConstrained<double>;
184template class FieldTrialConstrained<int>;
185
Sebastian Jansson9eb38862018-06-14 16:47:42 +0200186template class FieldTrialOptional<double>;
187template class FieldTrialOptional<int>;
188template class FieldTrialOptional<bool>;
189template class FieldTrialOptional<std::string>;
190
191} // namespace webrtc