/* * Copyright 2018 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. */ #ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ #define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ #include #include #include #include #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/string_encode.h" // List support for field trial strings. FieldTrialList and FieldTrialStructList // are used similarly to the other FieldTrialParameters, but take a variable // number of parameters. A FieldTrialList parses a |-delimeted string into a // list of T, using ParseTypedParameter to parse the individual tokens. // Example string: "my_list:1|2|3,empty_list,other_list:aardvark". // A FieldTrialStructList combines multiple lists into a list-of-structs. It // ensures that all its sublists parse correctly and have the same length, then // uses user-supplied accessor functions to write those elements into structs of // a user-supplied type. // See the unit test for usage and behavior. namespace webrtc { class FieldTrialListBase : public FieldTrialParameterInterface { protected: friend class FieldTrialListWrapper; explicit FieldTrialListBase(std::string key); bool Failed() const; bool Used() const; virtual int Size() = 0; bool failed_; bool parse_got_called_; }; // This class represents a vector of type T. The elements are separated by a | // and parsed using ParseTypedParameter. template class FieldTrialList : public FieldTrialListBase { public: explicit FieldTrialList(std::string key) : FieldTrialList(key, {}) {} FieldTrialList(std::string key, std::initializer_list default_values) : FieldTrialListBase(key), values_(default_values) {} std::vector Get() const { return values_; } operator std::vector() const { return Get(); } const T& operator[](size_t index) const { return values_[index]; } const std::vector* operator->() const { return &values_; } protected: bool Parse(absl::optional str_value) override { parse_got_called_ = true; if (!str_value) { values_.clear(); return true; } std::vector tokens; std::vector new_values_; rtc::split(str_value.value(), '|', &tokens); for (std::string token : tokens) { absl::optional value = ParseTypedParameter(token); if (value) { new_values_.push_back(*value); } else { failed_ = true; return false; } } values_.swap(new_values_); return true; } int Size() override { return values_.size(); } private: std::vector values_; }; class FieldTrialListWrapper { public: virtual ~FieldTrialListWrapper() = default; // Takes the element at the given index in the wrapped list and writes it to // the given struct. virtual void WriteElement(void* struct_to_write, int index) = 0; virtual FieldTrialListBase* GetList() = 0; int Length(); // Returns true iff the wrapped list has failed to parse at least one token. bool Failed(); bool Used(); protected: FieldTrialListWrapper() = default; }; namespace field_trial_list_impl { // The LambdaTypeTraits struct provides type information about lambdas in the // template expressions below. template struct LambdaTypeTraits : public LambdaTypeTraits {}; template struct LambdaTypeTraits { using ret = RetType; using src = SourceType; }; template struct TypedFieldTrialListWrapper : FieldTrialListWrapper { public: TypedFieldTrialListWrapper(std::string key, std::function sink) : list_(key), sink_(sink) {} void WriteElement(void* struct_to_write, int index) override { sink_(struct_to_write, list_[index]); } FieldTrialListBase* GetList() override { return &list_; } private: FieldTrialList list_; std::function sink_; }; } // namespace field_trial_list_impl template > FieldTrialListWrapper* FieldTrialStructMember(std::string key, F accessor) { return new field_trial_list_impl::TypedFieldTrialListWrapper< typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) { *accessor(static_cast(s)) = t; }); } // This base class is here to reduce the amount of code we have to generate for // each type of FieldTrialStructList. class FieldTrialStructListBase : public FieldTrialParameterInterface { protected: FieldTrialStructListBase( std::initializer_list sub_lists) : FieldTrialParameterInterface(""), sub_lists_() { // Take ownership of the list wrappers generated by FieldTrialStructMember // on the call site. for (FieldTrialListWrapper* const* it = sub_lists.begin(); it != sub_lists.end(); it++) { sub_parameters_.push_back((*it)->GetList()); sub_lists_.push_back(std::unique_ptr(*it)); } } // Check that all of our sublists that were in the field trial string had the // same number of elements. If they do, we return that length. If they had // different lengths, any sublist had parse failures or no sublists had // user-supplied values, we return -1. int ValidateAndGetLength(); bool Parse(absl::optional str_value) override; std::vector> sub_lists_; }; template class FieldTrialStructList : public FieldTrialStructListBase { public: FieldTrialStructList(std::initializer_list l, std::initializer_list default_list) : FieldTrialStructListBase(l), values_(default_list) {} std::vector Get() const { return values_; } operator std::vector() const { return Get(); } const S& operator[](size_t index) const { return values_[index]; } const std::vector* operator->() const { return &values_; } protected: void ParseDone() override { int length = ValidateAndGetLength(); if (length == -1) return; std::vector new_values(length, S()); for (std::unique_ptr& li : sub_lists_) { if (li->Used()) { for (int i = 0; i < length; i++) { li->WriteElement(&new_values[i], i); } } } values_.swap(new_values); } private: std::vector values_; }; } // namespace webrtc #endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_