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