diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/libwebrtc/rtc_base/experiments/field_trial_parser.cc | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_base/experiments/field_trial_parser.cc b/third_party/libwebrtc/rtc_base/experiments/field_trial_parser.cc new file mode 100644 index 0000000000..78d5489f5e --- /dev/null +++ b/third_party/libwebrtc/rtc_base/experiments/field_trial_parser.cc @@ -0,0 +1,260 @@ +/* + * 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 <inttypes.h> + +#include <algorithm> +#include <map> +#include <type_traits> +#include <utility> + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +FieldTrialParameterInterface::FieldTrialParameterInterface( + absl::string_view key) + : key_(key) {} +FieldTrialParameterInterface::~FieldTrialParameterInterface() { + RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_ + << "' never used."; +} + +void ParseFieldTrial( + std::initializer_list<FieldTrialParameterInterface*> fields, + absl::string_view trial_string) { + std::map<absl::string_view, FieldTrialParameterInterface*> 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; + } + } + bool logged_unknown_key = false; + + absl::string_view tail = trial_string; + while (!tail.empty()) { + size_t key_end = tail.find_first_of(",:"); + absl::string_view key = tail.substr(0, key_end); + absl::optional<std::string> opt_value; + if (key_end == absl::string_view::npos) { + tail = ""; + } else if (tail[key_end] == ':') { + tail = tail.substr(key_end + 1); + size_t value_end = tail.find(','); + opt_value.emplace(tail.substr(0, value_end)); + if (value_end == absl::string_view::npos) { + tail = ""; + } else { + tail = tail.substr(value_end + 1); + } + } else { + RTC_DCHECK_EQ(tail[key_end], ','); + tail = tail.substr(key_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(std::string(key))) { + RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '" + << key << "' in trial: \"" << trial_string << "\""; + } + } else if (key.empty() || key[0] != '_') { + // "_" is be used to prefix keys that are part of the string for + // debugging purposes but not neccessarily used. + // e.g. WebRTC-Experiment/param: value, _DebuggingString + if (!logged_unknown_key) { + 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.append(f.first.data(), f.first.size()); + valid_keys += ", "; + } + RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys; + logged_unknown_key = true; + } + } + } + + for (FieldTrialParameterInterface* field : fields) { + field->ParseDone(); + } +} + +template <> +absl::optional<bool> ParseTypedParameter<bool>(absl::string_view str) { + if (str == "true" || str == "1") { + return true; + } else if (str == "false" || str == "0") { + return false; + } + return absl::nullopt; +} + +template <> +absl::optional<double> ParseTypedParameter<double>(absl::string_view str) { + double value; + char unit[2]{0, 0}; + if (sscanf(std::string(str).c_str(), "%lf%1s", &value, unit) >= 1) { + if (unit[0] == '%') + return value / 100; + return value; + } else { + return absl::nullopt; + } +} + +template <> +absl::optional<int> ParseTypedParameter<int>(absl::string_view str) { + int64_t value; + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { + if (rtc::IsValueInRangeForNumericType<int, int64_t>(value)) { + return static_cast<int>(value); + } + } + return absl::nullopt; +} + +template <> +absl::optional<unsigned> ParseTypedParameter<unsigned>(absl::string_view str) { + int64_t value; + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { + if (rtc::IsValueInRangeForNumericType<unsigned, int64_t>(value)) { + return static_cast<unsigned>(value); + } + } + return absl::nullopt; +} + +template <> +absl::optional<std::string> ParseTypedParameter<std::string>( + absl::string_view str) { + return std::string(str); +} + +template <> +absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>( + absl::string_view str) { + return ParseOptionalParameter<bool>(str); +} +template <> +absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>( + absl::string_view str) { + return ParseOptionalParameter<int>(str); +} +template <> +absl::optional<absl::optional<unsigned>> +ParseTypedParameter<absl::optional<unsigned>>(absl::string_view str) { + return ParseOptionalParameter<unsigned>(str); +} +template <> +absl::optional<absl::optional<double>> +ParseTypedParameter<absl::optional<double>>(absl::string_view str) { + return ParseOptionalParameter<double>(str); +} + +FieldTrialFlag::FieldTrialFlag(absl::string_view key) + : FieldTrialFlag(key, false) {} + +FieldTrialFlag::FieldTrialFlag(absl::string_view 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<std::string> str_value) { + // Only set the flag if there is no argument provided. + if (str_value) { + absl::optional<bool> opt_value = ParseTypedParameter<bool>(*str_value); + if (!opt_value) + return false; + value_ = *opt_value; + } else { + value_ = true; + } + return true; +} + +AbstractFieldTrialEnum::AbstractFieldTrialEnum( + absl::string_view key, + int default_value, + std::map<std::string, int> 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<std::string> str_value) { + if (str_value) { + auto it = enum_mapping_.find(*str_value); + if (it != enum_mapping_.end()) { + value_ = it->second; + return true; + } + absl::optional<int> value = ParseTypedParameter<int>(*str_value); + if (value.has_value() && + (valid_values_.find(*value) != valid_values_.end())) { + value_ = *value; + return true; + } + } + return false; +} + +template class FieldTrialParameter<bool>; +template class FieldTrialParameter<double>; +template class FieldTrialParameter<int>; +template class FieldTrialParameter<unsigned>; +template class FieldTrialParameter<std::string>; + +template class FieldTrialConstrained<double>; +template class FieldTrialConstrained<int>; +template class FieldTrialConstrained<unsigned>; + +template class FieldTrialOptional<double>; +template class FieldTrialOptional<int>; +template class FieldTrialOptional<unsigned>; +template class FieldTrialOptional<bool>; +template class FieldTrialOptional<std::string>; + +} // namespace webrtc |