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