/* * Copyright 2019 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "rtc_base/experiments/field_trial_parser.h" #include #include #include #include #include #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { namespace { int FindOrEnd(std::string str, size_t start, char delimiter) { size_t pos = str.find(delimiter, start); pos = (pos == std::string::npos) ? str.length() : pos; return static_cast(pos); } } // namespace FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key) : key_(key) {} FieldTrialParameterInterface::~FieldTrialParameterInterface() { RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_ << "' never used."; } void ParseFieldTrial( std::initializer_list fields, std::string trial_string) { std::map field_map; FieldTrialParameterInterface* keyless_field = nullptr; for (FieldTrialParameterInterface* field : fields) { field->MarkAsUsed(); if (!field->sub_parameters_.empty()) { for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) { RTC_DCHECK(!sub_field->key_.empty()); sub_field->MarkAsUsed(); field_map[sub_field->key_] = sub_field; } continue; } if (field->key_.empty()) { RTC_DCHECK(!keyless_field); keyless_field = field; } else { field_map[field->key_] = field; } } size_t i = 0; while (i < trial_string.length()) { int val_end = FindOrEnd(trial_string, i, ','); int colon_pos = FindOrEnd(trial_string, i, ':'); int key_end = std::min(val_end, colon_pos); int val_begin = key_end + 1; std::string key = trial_string.substr(i, key_end - i); absl::optional opt_value; if (val_end >= val_begin) opt_value = trial_string.substr(val_begin, val_end - val_begin); i = val_end + 1; auto field = field_map.find(key); if (field != field_map.end()) { if (!field->second->Parse(std::move(opt_value))) { RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key << "' in trial: \"" << trial_string << "\""; } } else if (!opt_value && keyless_field && !key.empty()) { if (!keyless_field->Parse(key)) { RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '" << key << "' in trial: \"" << trial_string << "\""; } } else { RTC_LOG(LS_INFO) << "No field with key: '" << key << "' (found in trial: \"" << trial_string << "\")"; std::string valid_keys; for (const auto& f : field_map) { valid_keys += f.first; valid_keys += ", "; } RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys; } } for (FieldTrialParameterInterface* field : fields) { field->ParseDone(); } } template <> absl::optional ParseTypedParameter(std::string str) { if (str == "true" || str == "1") { return true; } else if (str == "false" || str == "0") { return false; } return absl::nullopt; } template <> absl::optional ParseTypedParameter(std::string str) { double value; char unit[2]{0, 0}; if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) { if (unit[0] == '%') return value / 100; return value; } else { return absl::nullopt; } } template <> absl::optional ParseTypedParameter(std::string str) { int64_t value; if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { if (rtc::IsValueInRangeForNumericType(value)) { return static_cast(value); } } return absl::nullopt; } template <> absl::optional ParseTypedParameter(std::string str) { int64_t value; if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { if (rtc::IsValueInRangeForNumericType(value)) { return static_cast(value); } } return absl::nullopt; } template <> absl::optional ParseTypedParameter(std::string str) { return std::move(str); } template <> absl::optional> ParseTypedParameter>( std::string str) { return ParseOptionalParameter(str); } template <> absl::optional> ParseTypedParameter>( std::string str) { return ParseOptionalParameter(str); } template <> absl::optional> ParseTypedParameter>(std::string str) { return ParseOptionalParameter(str); } template <> absl::optional> ParseTypedParameter>(std::string str) { return ParseOptionalParameter(str); } FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {} FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value) : FieldTrialParameterInterface(key), value_(default_value) {} bool FieldTrialFlag::Get() const { return value_; } webrtc::FieldTrialFlag::operator bool() const { return value_; } bool FieldTrialFlag::Parse(absl::optional str_value) { // Only set the flag if there is no argument provided. if (str_value) { absl::optional opt_value = ParseTypedParameter(*str_value); if (!opt_value) return false; value_ = *opt_value; } else { value_ = true; } return true; } AbstractFieldTrialEnum::AbstractFieldTrialEnum( std::string key, int default_value, std::map mapping) : FieldTrialParameterInterface(key), value_(default_value), enum_mapping_(mapping) { for (auto& key_val : enum_mapping_) valid_values_.insert(key_val.second); } AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) = default; AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default; bool AbstractFieldTrialEnum::Parse(absl::optional str_value) { if (str_value) { auto it = enum_mapping_.find(*str_value); if (it != enum_mapping_.end()) { value_ = it->second; return true; } absl::optional value = ParseTypedParameter(*str_value); if (value.has_value() && (valid_values_.find(*value) != valid_values_.end())) { value_ = *value; return true; } } return false; } template class FieldTrialParameter; template class FieldTrialParameter; template class FieldTrialParameter; template class FieldTrialParameter; template class FieldTrialParameter; template class FieldTrialConstrained; template class FieldTrialConstrained; template class FieldTrialConstrained; template class FieldTrialOptional; template class FieldTrialOptional; template class FieldTrialOptional; template class FieldTrialOptional; template class FieldTrialOptional; } // namespace webrtc