xref: /aosp_15_r20/external/webrtc/rtc_base/experiments/field_trial_list.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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