• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #ifndef BASE_METRICS_FIELD_TRIAL_PARAMS_H_
11 #define BASE_METRICS_FIELD_TRIAL_PARAMS_H_
12 
13 #include <array>
14 #include <map>
15 #include <string>
16 
17 #include "base/base_export.h"
18 #include "base/feature_list.h"
19 #include "base/memory/raw_ptr_exclusion.h"
20 #include "base/no_destructor.h"
21 #include "base/notreached.h"
22 #include "base/time/time.h"
23 
24 namespace base {
25 
26 namespace internal {
27 
28 BASE_EXPORT bool IsFeatureParamWithCacheEnabled();
29 
30 // A traits struct to manage the type for the default value in the following
31 // FeatureParam<> template. `std::string` needs to use a string literal instead
32 // of `std::string` to realize compile time construction.
33 template <typename T>
34 struct FeatureParamTraits {
35   using DefaultValueType = T;
36   using CacheStorageType = T;
ToCacheStorageTypeFeatureParamTraits37   static CacheStorageType ToCacheStorageType(const T& value) {
38     return value;
39   }
FromCacheStorageTypeFeatureParamTraits40   static constexpr T FromCacheStorageType(const CacheStorageType& storage) {
41     return storage;
42   }
43 };
44 
45 template <>
46 struct FeatureParamTraits<std::string> {
47   using DefaultValueType = const char*;
48   using CacheStorageType = NoDestructor<std::string>;
49   static CacheStorageType ToCacheStorageType(
50       const std::string& value) {
51     return CacheStorageType(value);
52   }
53   static constexpr std::string FromCacheStorageType(
54       const CacheStorageType& storage) {
55     return *storage;
56   }
57 };
58 
59 }  // namespace internal
60 
61 // Key-value mapping type for field trial parameters.
62 typedef std::map<std::string, std::string> FieldTrialParams;
63 
64 // Param string decoding function for AssociateFieldTrialParamsFromString().
65 typedef std::string (*FieldTrialParamsDecodeStringFunc)(const std::string& str);
66 
67 // Unescapes special characters from the given string. Used in
68 // AssociateFieldTrialParamsFromString() as one of the feature params decoding
69 // functions.
70 BASE_EXPORT std::string UnescapeValue(const std::string& value);
71 
72 // Associates the specified set of key-value |params| with the field trial
73 // specified by |trial_name| and |group_name|. Fails and returns false if the
74 // specified field trial already has params associated with it or the trial
75 // is already active (group() has been called on it). Thread safe.
76 BASE_EXPORT bool AssociateFieldTrialParams(const std::string& trial_name,
77                                            const std::string& group_name,
78                                            const FieldTrialParams& params);
79 
80 // Provides a mechanism to associate multiple set of params to multiple groups
81 // with a formatted string as returned by FieldTrialList::AllParamsToString().
82 // |decode_data_func| allows specifying a custom decoding function.
83 BASE_EXPORT bool AssociateFieldTrialParamsFromString(
84     const std::string& params_string,
85     FieldTrialParamsDecodeStringFunc decode_data_func);
86 
87 // Retrieves the set of key-value |params| for the specified field trial, based
88 // on its selected group. If the field trial does not exist or its selected
89 // group does not have any parameters associated with it, returns false and
90 // does not modify |params|. Calling this function will result in the field
91 // trial being marked as active if found (i.e. group() will be called on it),
92 // if it wasn't already. Thread safe.
93 BASE_EXPORT bool GetFieldTrialParams(const std::string& trial_name,
94                                      FieldTrialParams* params);
95 
96 // Retrieves the set of key-value |params| for the field trial associated with
97 // the specified |feature|. A feature is associated with at most one field
98 // trial and selected group. See  base/feature_list.h for more information on
99 // features. If the feature is not enabled, or if there's no associated params,
100 // returns false and does not modify |params|. Calling this function will
101 // result in the associated field trial being marked as active if found (i.e.
102 // group() will be called on it), if it wasn't already. Thread safe.
103 BASE_EXPORT bool GetFieldTrialParamsByFeature(const base::Feature& feature,
104                                               FieldTrialParams* params);
105 
106 // Retrieves a specific parameter value corresponding to |param_name| for the
107 // specified field trial, based on its selected group. If the field trial does
108 // not exist or the specified parameter does not exist, returns an empty
109 // string. Calling this function will result in the field trial being marked as
110 // active if found (i.e. group() will be called on it), if it wasn't already.
111 // Thread safe.
112 BASE_EXPORT std::string GetFieldTrialParamValue(const std::string& trial_name,
113                                                 const std::string& param_name);
114 
115 // Retrieves a specific parameter value corresponding to |param_name| for the
116 // field trial associated with the specified |feature|. A feature is associated
117 // with at most one field trial and selected group. See base/feature_list.h for
118 // more information on features. If the feature is not enabled, or the
119 // specified parameter does not exist, returns an empty string. Calling this
120 // function will result in the associated field trial being marked as active if
121 // found (i.e. group() will be called on it), if it wasn't already. Thread safe.
122 BASE_EXPORT std::string GetFieldTrialParamValueByFeature(
123     const base::Feature& feature,
124     const std::string& param_name);
125 
126 // Same as GetFieldTrialParamValueByFeature(). But internally relies on
127 // GetFieldTrialParamsByFeature to handle empty values in the map, and returns
128 // |default_value| only if |param_name| is not found in the map.
129 BASE_EXPORT std::string GetFieldTrialParamByFeatureAsString(
130     const base::Feature& feature,
131     const std::string& param_name,
132     const std::string& default_value);
133 
134 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
135 // string value into an int using base::StringToInt() and returns it, if
136 // successful. Otherwise, it returns |default_value|. If the string value is not
137 // empty and the conversion does not succeed, it produces a warning to LOG.
138 BASE_EXPORT int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature,
139                                                  const std::string& param_name,
140                                                  int default_value);
141 
142 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
143 // string value into a double using base::StringToDouble() and returns it, if
144 // successful. Otherwise, it returns |default_value|. If the string value is not
145 // empty and the conversion does not succeed, it produces a warning to LOG.
146 BASE_EXPORT double GetFieldTrialParamByFeatureAsDouble(
147     const base::Feature& feature,
148     const std::string& param_name,
149     double default_value);
150 
151 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
152 // string value into a boolean and returns it, if successful. Otherwise, it
153 // returns |default_value|. The only string representations accepted here are
154 // "true" and "false". If the string value is not empty and the conversion does
155 // not succeed, it produces a warning to LOG.
156 BASE_EXPORT bool GetFieldTrialParamByFeatureAsBool(
157     const base::Feature& feature,
158     const std::string& param_name,
159     bool default_value);
160 
161 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
162 // string value into a base::TimeDelta and returns it, if successful. Otherwise,
163 // it returns `default_value`. If the string value is not empty and the
164 // conversion does not succeed, it produces a warning to LOG.
165 BASE_EXPORT base::TimeDelta GetFieldTrialParamByFeatureAsTimeDelta(
166     const Feature& feature,
167     const std::string& param_name,
168     base::TimeDelta default_value);
169 
170 // Shared declaration for various FeatureParam<T> types.
171 //
172 // This template is defined for the following types T:
173 //   bool
174 //   int
175 //   size_t
176 //   double
177 //   std::string
178 //   enum types
179 //   base::TimeDelta
180 //
181 // Attempting to use it with any other type is a compile error.
182 //
183 // Getting a param value from a FeatureParam<T> will have the same semantics as
184 // GetFieldTrialParamValueByFeature(), see that function's comments for details.
185 // `cache_getter` is used to provide a dedicated getter tbat is used to give a
186 // local cache to the FeatureParam. Usually, this is automatically generated and
187 // provided via BASE_FEATURE_PARAM() or BASE_FEATURE_ENUM_PARAM() macro.
188 //
189 // Example to declares a double-valued parameter.
190 //
191 //     constexpr FeatureParam<double> kAssistantTriggerThreshold = {
192 //         &kAssistantFeature, "trigger_threshold", 0.10};
193 //
194 // If the feature is not enabled, the parameter is not set, or set to an invalid
195 // value, then Get() will return the default value.
196 template <typename T, bool IsEnum = std::is_enum_v<T>>
197 struct FeatureParam {
198   using DefaultValueType =
199       typename internal::FeatureParamTraits<T>::DefaultValueType;
200 
201   // Prevent use of FeatureParam<> with unsupported types (e.g. void*). Uses T
202   // in its definition so that evaluation is deferred until the template is
203   // instantiated.
204   static_assert(std::is_same_v<bool, T> || std::is_same_v<int, T> ||
205                     std::is_same_v<size_t, T> || std::is_same_v<double, T> ||
206                     std::is_same_v<std::string, T> ||
207                     std::is_same_v<base::TimeDelta, T>,
208                 "Unsupported FeatureParam<> type");
209 
210   constexpr FeatureParam(const Feature* feature,
211                          const char* name,
212                          DefaultValueType default_value,
213                          T (*cache_getter)(const FeatureParam<T>*) = nullptr)
214       : feature(feature),
215         name(name),
216         default_value(default_value),
217         cache_getter(cache_getter) {}
218 
219   // Calling Get() or GetWithoutCache() will activate the field trial associated
220   // with |feature|. See GetFieldTrialParamValueByFeature() for more details.
221   BASE_EXPORT T Get() const {
222     if (internal::IsFeatureParamWithCacheEnabled() && cache_getter) {
223       return cache_getter(this);
224     }
225     return GetWithoutCache();
226   }
227   BASE_EXPORT T GetWithoutCache() const;
228 
229   // RAW_PTR_EXCLUSION: #global-scope
230   RAW_PTR_EXCLUSION const Feature* const feature;
231   const char* const name;
232   const DefaultValueType default_value;
233   T (*const cache_getter)(const FeatureParam<T>*);
234 };
235 
236 // Declarations for GetWithoutCache() specializations and explicit
237 // instantiations for the FeatureParam<> to ensure instantiating them
238 // in the base components to export them.
239 template <>
240 bool FeatureParam<bool>::GetWithoutCache() const;
241 template struct FeatureParam<bool>;
242 template struct internal::FeatureParamTraits<bool>;
243 
244 template <>
245 int FeatureParam<int>::GetWithoutCache() const;
246 template struct FeatureParam<int>;
247 template struct internal::FeatureParamTraits<int>;
248 
249 template <>
250 size_t FeatureParam<size_t>::GetWithoutCache() const;
251 template struct FeatureParam<size_t>;
252 template struct internal::FeatureParamTraits<size_t>;
253 
254 template <>
255 double FeatureParam<double>::GetWithoutCache() const;
256 template struct FeatureParam<double>;
257 template struct internal::FeatureParamTraits<double>;
258 
259 template <>
260 std::string FeatureParam<std::string>::GetWithoutCache() const;
261 template struct FeatureParam<std::string>;
262 
263 template <>
264 TimeDelta FeatureParam<TimeDelta>::GetWithoutCache() const;
265 template struct FeatureParam<TimeDelta>;
266 template struct internal::FeatureParamTraits<TimeDelta>;
267 
268 BASE_EXPORT void LogInvalidEnumValue(const Feature& feature,
269                                      const std::string& param_name,
270                                      const std::string& value_as_string,
271                                      int default_value_as_int);
272 
273 // Feature param declaration for an enum, with associated options. Example:
274 //
275 //     constexpr FeatureParam<ShapeEnum>::Option kShapeParamOptions[] = {
276 //         {SHAPE_CIRCLE, "circle"},
277 //         {SHAPE_CYLINDER, "cylinder"},
278 //         {SHAPE_PAPERCLIP, "paperclip"}};
279 //     constexpr FeatureParam<ShapeEnum> kAssistantShapeParam = {
280 //         &kAssistantFeature, "shape", SHAPE_CIRCLE, &kShapeParamOptions};
281 //
282 // With this declaration, the parameter may be set to "circle", "cylinder", or
283 // "paperclip", and that will be translated to one of the three enum values. By
284 // default, or if the param is set to an unknown value, the parameter will be
285 // assumed to be SHAPE_CIRCLE.
286 template <typename Enum>
287 struct FeatureParam<Enum, true> {
288   struct Option {
289     constexpr Option(Enum value, const char* name) : value(value), name(name) {}
290 
291     const Enum value;
292     const char* const name;
293   };
294 
295   template <size_t option_count>
296   constexpr FeatureParam(
297       const Feature* feature,
298       const char* name,
299       const Enum default_value,
300       const std::array<Option, option_count>& options,
301       Enum (*cache_getter)(const FeatureParam<Enum>*) = nullptr)
302       : feature(feature),
303         name(name),
304         default_value(default_value),
305         options(options.data()),
306         option_count(option_count),
307         cache_getter(cache_getter) {
308     static_assert(option_count >= 1, "FeatureParam<enum> has no options");
309   }
310 
311   template <size_t option_count>
312   constexpr FeatureParam(
313       const Feature* feature,
314       const char* name,
315       const Enum default_value,
316       const Option (*options)[option_count],
317       Enum (*cache_getter)(const FeatureParam<Enum>*) = nullptr)
318       : feature(feature),
319         name(name),
320         default_value(default_value),
321         options(*options),
322         option_count(option_count),
323         cache_getter(cache_getter) {
324     static_assert(option_count >= 1, "FeatureParam<enum> has no options");
325   }
326 
327   // Calling Get() or GetWithoutCache() will activate the field trial associated
328   // with |feature|. See GetFieldTrialParamValueByFeature() for more details.
329   Enum Get() const {
330     if (internal::IsFeatureParamWithCacheEnabled() && cache_getter) {
331       return cache_getter(this);
332     }
333     return GetWithoutCache();
334   }
335   Enum GetWithoutCache() const {
336     std::string value = GetFieldTrialParamValueByFeature(*feature, name);
337     if (value.empty()) {
338       return default_value;
339     }
340     for (size_t i = 0; i < option_count; ++i) {
341       if (value == options[i].name) {
342         return options[i].value;
343       }
344     }
345     LogInvalidEnumValue(*feature, name, value, static_cast<int>(default_value));
346     return default_value;
347   }
348 
349   // Returns the param-string for the given enum value.
350   std::string GetName(Enum value) const {
351     for (size_t i = 0; i < option_count; ++i) {
352       if (value == options[i].value) {
353         return options[i].name;
354       }
355     }
356     NOTREACHED();
357   }
358 
359   // RAW_PTR_EXCLUSION: #global-scope
360   RAW_PTR_EXCLUSION const base::Feature* const feature;
361   const char* const name;
362   const Enum default_value;
363   // RAW_PTR_EXCLUSION: #global-scope
364   RAW_PTR_EXCLUSION const Option* const options;
365   const size_t option_count;
366   Enum (*const cache_getter)(const FeatureParam<Enum>*);
367 };
368 
369 }  // namespace base
370 
371 #endif  // BASE_METRICS_FIELD_TRIAL_PARAMS_H_
372