1 /* 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 #ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ 11 #define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ 12 13 #include <initializer_list> 14 #include <memory> 15 #include <string> 16 #include <vector> 17 18 #include "absl/strings/string_view.h" 19 #include "rtc_base/experiments/field_trial_parser.h" 20 #include "rtc_base/string_encode.h" 21 22 // List support for field trial strings. FieldTrialList and FieldTrialStructList 23 // are used similarly to the other FieldTrialParameters, but take a variable 24 // number of parameters. A FieldTrialList<T> parses a |-delimeted string into a 25 // list of T, using ParseTypedParameter to parse the individual tokens. 26 // Example string: "my_list:1|2|3,empty_list,other_list:aardvark". 27 28 // A FieldTrialStructList combines multiple lists into a list-of-structs. It 29 // ensures that all its sublists parse correctly and have the same length, then 30 // uses user-supplied accessor functions to write those elements into structs of 31 // a user-supplied type. 32 33 // See the unit test for usage and behavior. 34 35 namespace webrtc { 36 37 class FieldTrialListBase : public FieldTrialParameterInterface { 38 protected: 39 friend class FieldTrialListWrapper; 40 explicit FieldTrialListBase(absl::string_view key); 41 42 bool Failed() const; 43 bool Used() const; 44 45 virtual int Size() = 0; 46 47 bool failed_; 48 bool parse_got_called_; 49 }; 50 51 // This class represents a vector of type T. The elements are separated by a | 52 // and parsed using ParseTypedParameter. 53 template <typename T> 54 class FieldTrialList : public FieldTrialListBase { 55 public: FieldTrialList(absl::string_view key)56 explicit FieldTrialList(absl::string_view key) : FieldTrialList(key, {}) {} FieldTrialList(absl::string_view key,std::initializer_list<T> default_values)57 FieldTrialList(absl::string_view key, std::initializer_list<T> default_values) 58 : FieldTrialListBase(key), values_(default_values) {} 59 Get()60 std::vector<T> Get() const { return values_; } 61 operator std::vector<T>() const { return Get(); } 62 typename std::vector<T>::const_reference operator[](size_t index) const { 63 return values_[index]; 64 } 65 const std::vector<T>* operator->() const { return &values_; } 66 67 protected: Parse(absl::optional<std::string> str_value)68 bool Parse(absl::optional<std::string> str_value) override { 69 parse_got_called_ = true; 70 71 if (!str_value) { 72 values_.clear(); 73 return true; 74 } 75 76 std::vector<T> new_values_; 77 78 for (const absl::string_view token : rtc::split(str_value.value(), '|')) { 79 absl::optional<T> value = ParseTypedParameter<T>(token); 80 if (value) { 81 new_values_.push_back(*value); 82 } else { 83 failed_ = true; 84 return false; 85 } 86 } 87 88 values_.swap(new_values_); 89 return true; 90 } 91 Size()92 int Size() override { return values_.size(); } 93 94 private: 95 std::vector<T> values_; 96 }; 97 98 class FieldTrialListWrapper { 99 public: 100 virtual ~FieldTrialListWrapper() = default; 101 102 // Takes the element at the given index in the wrapped list and writes it to 103 // the given struct. 104 virtual void WriteElement(void* struct_to_write, int index) = 0; 105 106 virtual FieldTrialListBase* GetList() = 0; 107 108 int Length(); 109 110 // Returns true iff the wrapped list has failed to parse at least one token. 111 bool Failed(); 112 113 bool Used(); 114 115 protected: 116 FieldTrialListWrapper() = default; 117 }; 118 119 namespace field_trial_list_impl { 120 // The LambdaTypeTraits struct provides type information about lambdas in the 121 // template expressions below. 122 template <typename T> 123 struct LambdaTypeTraits : public LambdaTypeTraits<decltype(&T::operator())> {}; 124 125 template <typename ClassType, typename RetType, typename SourceType> 126 struct LambdaTypeTraits<RetType* (ClassType::*)(SourceType*)const> { 127 using ret = RetType; 128 using src = SourceType; 129 }; 130 131 template <typename T> 132 struct TypedFieldTrialListWrapper : FieldTrialListWrapper { 133 public: 134 TypedFieldTrialListWrapper(absl::string_view key, 135 std::function<void(void*, T)> sink) 136 : list_(key), sink_(sink) {} 137 138 void WriteElement(void* struct_to_write, int index) override { 139 sink_(struct_to_write, list_[index]); 140 } 141 142 FieldTrialListBase* GetList() override { return &list_; } 143 144 private: 145 FieldTrialList<T> list_; 146 std::function<void(void*, T)> sink_; 147 }; 148 149 } // namespace field_trial_list_impl 150 151 template <typename F, 152 typename Traits = typename field_trial_list_impl::LambdaTypeTraits<F>> 153 FieldTrialListWrapper* FieldTrialStructMember(absl::string_view key, 154 F accessor) { 155 return new field_trial_list_impl::TypedFieldTrialListWrapper< 156 typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) { 157 *accessor(static_cast<typename Traits::src*>(s)) = t; 158 }); 159 } 160 161 // This base class is here to reduce the amount of code we have to generate for 162 // each type of FieldTrialStructList. 163 class FieldTrialStructListBase : public FieldTrialParameterInterface { 164 protected: 165 FieldTrialStructListBase( 166 std::initializer_list<FieldTrialListWrapper*> sub_lists) 167 : FieldTrialParameterInterface(""), sub_lists_() { 168 // Take ownership of the list wrappers generated by FieldTrialStructMember 169 // on the call site. 170 for (FieldTrialListWrapper* const* it = sub_lists.begin(); 171 it != sub_lists.end(); it++) { 172 sub_parameters_.push_back((*it)->GetList()); 173 sub_lists_.push_back(std::unique_ptr<FieldTrialListWrapper>(*it)); 174 } 175 } 176 177 // Check that all of our sublists that were in the field trial string had the 178 // same number of elements. If they do, we return that length. If they had 179 // different lengths, any sublist had parse failures or no sublists had 180 // user-supplied values, we return -1. 181 int ValidateAndGetLength(); 182 183 bool Parse(absl::optional<std::string> str_value) override; 184 185 std::vector<std::unique_ptr<FieldTrialListWrapper>> sub_lists_; 186 }; 187 188 template <typename S> 189 class FieldTrialStructList : public FieldTrialStructListBase { 190 public: 191 FieldTrialStructList(std::initializer_list<FieldTrialListWrapper*> l, 192 std::initializer_list<S> default_list) 193 : FieldTrialStructListBase(l), values_(default_list) {} 194 195 std::vector<S> Get() const { return values_; } 196 operator std::vector<S>() const { return Get(); } 197 const S& operator[](size_t index) const { return values_[index]; } 198 const std::vector<S>* operator->() const { return &values_; } 199 200 protected: 201 void ParseDone() override { 202 int length = ValidateAndGetLength(); 203 204 if (length == -1) 205 return; 206 207 std::vector<S> new_values(length, S()); 208 209 for (std::unique_ptr<FieldTrialListWrapper>& li : sub_lists_) { 210 if (li->Used()) { 211 for (int i = 0; i < length; i++) { 212 li->WriteElement(&new_values[i], i); 213 } 214 } 215 } 216 217 values_.swap(new_values); 218 } 219 220 private: 221 std::vector<S> values_; 222 }; 223 224 } // namespace webrtc 225 226 #endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ 227