blob: 0ab2853e3730a0a6042cbda4e22ebcfe446920ce [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2017 The Chromium Authors
asvitkine79ab08c2017-01-30 23:27:052// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
danakj51d26a42024-04-25 14:23:565#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
asvitkine79ab08c2017-01-30 23:27:0510#ifndef BASE_METRICS_FIELD_TRIAL_PARAMS_H_
11#define BASE_METRICS_FIELD_TRIAL_PARAMS_H_
12
danakje75c6c812024-07-26 20:37:4713#include <array>
asvitkine79ab08c2017-01-30 23:27:0514#include <map>
15#include <string>
16
17#include "base/base_export.h"
Anthony Vallee-Dubois9dbbbda32022-08-26 01:25:3118#include "base/feature_list.h"
Keishi Hattori8a7e15d2023-01-19 07:16:2919#include "base/memory/raw_ptr_exclusion.h"
Takashi Toyoshima8a54cdf2024-11-21 02:38:4020#include "base/no_destructor.h"
Hans Wennborgafeb3902020-06-17 14:42:2921#include "base/notreached.h"
Tal Pressman3e91d6fd2020-07-02 04:38:0122#include "base/time/time.h"
asvitkine79ab08c2017-01-30 23:27:0523
24namespace base {
25
Takashi Toyoshima03c3a4d2024-09-06 06:32:0226namespace internal {
27
Takashi Toyoshima5e7b25152024-08-23 04:48:0328BASE_EXPORT bool IsFeatureParamWithCacheEnabled();
Takashi Toyoshima03c3a4d2024-09-06 06:32:0229
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.
33template <typename T>
34struct FeatureParamTraits {
35 using DefaultValueType = T;
Takashi Toyoshima8a54cdf2024-11-21 02:38:4036 using CacheStorageType = T;
Peter Kasting134ef9af2024-12-28 02:30:0937 static CacheStorageType ToCacheStorageType(const T& value) { return value; }
Takashi Toyoshima8a54cdf2024-11-21 02:38:4038 static constexpr T FromCacheStorageType(const CacheStorageType& storage) {
39 return storage;
40 }
Takashi Toyoshima03c3a4d2024-09-06 06:32:0241};
42
43template <>
44struct FeatureParamTraits<std::string> {
45 using DefaultValueType = const char*;
Takashi Toyoshima8a54cdf2024-11-21 02:38:4046 using CacheStorageType = NoDestructor<std::string>;
Peter Kasting134ef9af2024-12-28 02:30:0947 static CacheStorageType ToCacheStorageType(const std::string& value) {
Takashi Toyoshima8a54cdf2024-11-21 02:38:4048 return CacheStorageType(value);
49 }
50 static constexpr std::string FromCacheStorageType(
51 const CacheStorageType& storage) {
52 return *storage;
53 }
Takashi Toyoshima03c3a4d2024-09-06 06:32:0254};
55
56} // namespace internal
Takashi Toyoshima5e7b25152024-08-23 04:48:0357
Miyoung Shinb5ad87f2019-05-13 20:12:4558// Key-value mapping type for field trial parameters.
59typedef std::map<std::string, std::string> FieldTrialParams;
60
Alexei Svitkine8724ea502019-06-14 21:51:4661// Param string decoding function for AssociateFieldTrialParamsFromString().
62typedef std::string (*FieldTrialParamsDecodeStringFunc)(const std::string& str);
63
Peter Kasting18e51f82025-01-14 16:02:3864// Shared declaration for various FeatureParam<T> types.
65//
66// This template is defined for the following types T:
67// bool
68// int
69// size_t
70// double
71// std::string
72// enum types
73// base::TimeDelta
74//
75// Attempting to use it with any other type is a compile error.
76//
77// Getting a param value from a FeatureParam<T> will have the same semantics as
78// GetFieldTrialParamValueByFeature(), see that function's comments for details.
Trevor Perrier3666dee2025-01-16 15:09:0579// `cache_getter` is used to provide a dedicated getter that is used to give a
Peter Kasting18e51f82025-01-14 16:02:3880// local cache to the FeatureParam. Usually, this is automatically generated and
81// provided via BASE_FEATURE_PARAM() or BASE_FEATURE_ENUM_PARAM() macro.
82//
83// Example to declares a double-valued parameter.
84//
85// constexpr FeatureParam<double> kAssistantTriggerThreshold = {
86// &kAssistantFeature, "trigger_threshold", 0.10};
87//
88// If the feature is not enabled, the parameter is not set, or set to an invalid
89// value, then Get() will return the default value.
90template <typename T, bool IsEnum = std::is_enum_v<T>>
91struct FeatureParam {
92 using DefaultValueType =
93 typename internal::FeatureParamTraits<T>::DefaultValueType;
94
95 // Prevent use of FeatureParam<> with unsupported types (e.g. void*). Uses T
96 // in its definition so that evaluation is deferred until the template is
97 // instantiated.
98 static_assert(std::is_same_v<bool, T> || std::is_same_v<int, T> ||
99 std::is_same_v<size_t, T> || std::is_same_v<double, T> ||
100 std::is_same_v<std::string, T> ||
101 std::is_same_v<base::TimeDelta, T>,
102 "Unsupported FeatureParam<> type");
103
104 constexpr FeatureParam(const Feature* feature,
105 const char* name,
106 DefaultValueType default_value,
107 T (*cache_getter)(const FeatureParam<T>*) = nullptr)
108 : feature(feature),
109 name(name),
110 default_value(default_value),
111 cache_getter(cache_getter) {}
112
113 // Calling Get() or GetWithoutCache() will activate the field trial associated
114 // with |feature|. See GetFieldTrialParamValueByFeature() for more details.
115 BASE_EXPORT T Get() const {
116 if (internal::IsFeatureParamWithCacheEnabled() && cache_getter) {
117 return cache_getter(this);
118 }
119 return GetWithoutCache();
120 }
121 BASE_EXPORT T GetWithoutCache() const;
122
Jayson Adams6bd00112025-01-15 21:01:41123 // RAW_PTR_EXCLUSION: Using raw_ptr here would make FeatureParam lose its
124 // trivial destructor, causing it to be classified as a non-trivial class
125 // member in contexts where it's included. This can complicate code and
126 // impact performance. base::Features are expected to be globals with
127 // static lifetime, so this is pretty much always safe.
Peter Kasting18e51f82025-01-14 16:02:38128 RAW_PTR_EXCLUSION const Feature* const feature;
129 const char* const name;
130 const DefaultValueType default_value;
131 T (*const cache_getter)(const FeatureParam<T>*);
132};
133
134// Declarations for GetWithoutCache() specializations and explicit
135// instantiations for the FeatureParam<> to ensure instantiating them
136// in the base components to export them.
137template <>
138bool FeatureParam<bool>::GetWithoutCache() const;
139template struct FeatureParam<bool>;
140template struct internal::FeatureParamTraits<bool>;
141
142template <>
143int FeatureParam<int>::GetWithoutCache() const;
144template struct FeatureParam<int>;
145template struct internal::FeatureParamTraits<int>;
146
147template <>
148size_t FeatureParam<size_t>::GetWithoutCache() const;
149template struct FeatureParam<size_t>;
150template struct internal::FeatureParamTraits<size_t>;
151
152template <>
153double FeatureParam<double>::GetWithoutCache() const;
154template struct FeatureParam<double>;
155template struct internal::FeatureParamTraits<double>;
156
157template <>
158std::string FeatureParam<std::string>::GetWithoutCache() const;
159template struct FeatureParam<std::string>;
160
161template <>
162TimeDelta FeatureParam<TimeDelta>::GetWithoutCache() const;
163template struct FeatureParam<TimeDelta>;
164template struct internal::FeatureParamTraits<TimeDelta>;
165
166// Feature param declaration for an enum, with associated options. Example:
167//
168// constexpr FeatureParam<ShapeEnum>::Option kShapeParamOptions[] = {
169// {SHAPE_CIRCLE, "circle"},
170// {SHAPE_CYLINDER, "cylinder"},
171// {SHAPE_PAPERCLIP, "paperclip"}};
172// constexpr FeatureParam<ShapeEnum> kAssistantShapeParam = {
173// &kAssistantFeature, "shape", SHAPE_CIRCLE, &kShapeParamOptions};
174//
175// With this declaration, the parameter may be set to "circle", "cylinder", or
176// "paperclip", and that will be translated to one of the three enum values. By
177// default, or if the param is set to an unknown value, the parameter will be
178// assumed to be SHAPE_CIRCLE.
179template <typename Enum>
180struct FeatureParam<Enum, true> {
181 struct Option {
182 constexpr Option(Enum value, const char* name) : value(value), name(name) {}
183
184 const Enum value;
185 const char* const name;
186 };
187
188 template <size_t option_count>
189 constexpr FeatureParam(
190 const Feature* feature,
191 const char* name,
192 const Enum default_value,
193 const std::array<Option, option_count>& options,
194 Enum (*cache_getter)(const FeatureParam<Enum>*) = nullptr)
195 : feature(feature),
196 name(name),
197 default_value(default_value),
198 options(options.data()),
199 option_count(option_count),
200 cache_getter(cache_getter) {
201 static_assert(option_count >= 1, "FeatureParam<enum> has no options");
202 }
203
204 template <size_t option_count>
205 constexpr FeatureParam(
206 const Feature* feature,
207 const char* name,
208 const Enum default_value,
209 const Option (*options)[option_count],
210 Enum (*cache_getter)(const FeatureParam<Enum>*) = nullptr)
211 : feature(feature),
212 name(name),
213 default_value(default_value),
214 options(*options),
215 option_count(option_count),
216 cache_getter(cache_getter) {
217 static_assert(option_count >= 1, "FeatureParam<enum> has no options");
218 }
219
220 // Calling Get() or GetWithoutCache() will activate the field trial associated
221 // with |feature|. See GetFieldTrialParamValueByFeature() for more details.
222 Enum Get() const {
223 if (internal::IsFeatureParamWithCacheEnabled() && cache_getter) {
224 return cache_getter(this);
225 }
226 return GetWithoutCache();
227 }
228 Enum GetWithoutCache() const {
229 return GetFieldTrialParamByFeatureAsEnum(
230 *feature, name, default_value, base::span(&*options, option_count));
231 }
232
233 // Returns the param-string for the given enum value.
234 std::string GetName(Enum value) const {
235 for (size_t i = 0; i < option_count; ++i) {
236 if (value == options[i].value) {
237 return options[i].name;
238 }
239 }
240 NOTREACHED();
241 }
242
243 const raw_ptr<const base::Feature> feature;
244 const char* const name;
245 const Enum default_value;
246 // TODO(crbug.com/40284755): Remove AllowPtrArithmetic if possible after
247 // unsafe buffers have been evaluated.
248 const raw_ptr<const Option, AllowPtrArithmetic> options;
249 const size_t option_count;
250 Enum (*const cache_getter)(const FeatureParam<Enum>*);
251};
252
Weilun Shi1cd8fb92020-07-17 23:31:00253// Unescapes special characters from the given string. Used in
254// AssociateFieldTrialParamsFromString() as one of the feature params decoding
255// functions.
256BASE_EXPORT std::string UnescapeValue(const std::string& value);
257
Peter Kasting18e51f82025-01-14 16:02:38258// Logs a warning that a feature param expected to map to an enum was an unknown
259// non-empty value.
260BASE_EXPORT void LogInvalidEnumValue(const Feature& feature,
261 const std::string& param_name,
262 const std::string& value_as_string,
263 int default_value_as_int);
264
asvitkine79ab08c2017-01-30 23:27:05265// Associates the specified set of key-value |params| with the field trial
266// specified by |trial_name| and |group_name|. Fails and returns false if the
267// specified field trial already has params associated with it or the trial
268// is already active (group() has been called on it). Thread safe.
Miyoung Shinb5ad87f2019-05-13 20:12:45269BASE_EXPORT bool AssociateFieldTrialParams(const std::string& trial_name,
270 const std::string& group_name,
271 const FieldTrialParams& params);
asvitkine79ab08c2017-01-30 23:27:05272
Alexei Svitkine8724ea502019-06-14 21:51:46273// Provides a mechanism to associate multiple set of params to multiple groups
274// with a formatted string as returned by FieldTrialList::AllParamsToString().
275// |decode_data_func| allows specifying a custom decoding function.
276BASE_EXPORT bool AssociateFieldTrialParamsFromString(
277 const std::string& params_string,
278 FieldTrialParamsDecodeStringFunc decode_data_func);
279
asvitkine79ab08c2017-01-30 23:27:05280// Retrieves the set of key-value |params| for the specified field trial, based
281// on its selected group. If the field trial does not exist or its selected
282// group does not have any parameters associated with it, returns false and
283// does not modify |params|. Calling this function will result in the field
284// trial being marked as active if found (i.e. group() will be called on it),
285// if it wasn't already. Thread safe.
Miyoung Shinb5ad87f2019-05-13 20:12:45286BASE_EXPORT bool GetFieldTrialParams(const std::string& trial_name,
287 FieldTrialParams* params);
asvitkine79ab08c2017-01-30 23:27:05288
289// Retrieves the set of key-value |params| for the field trial associated with
290// the specified |feature|. A feature is associated with at most one field
291// trial and selected group. See base/feature_list.h for more information on
292// features. If the feature is not enabled, or if there's no associated params,
293// returns false and does not modify |params|. Calling this function will
294// result in the associated field trial being marked as active if found (i.e.
295// group() will be called on it), if it wasn't already. Thread safe.
Miyoung Shinb5ad87f2019-05-13 20:12:45296BASE_EXPORT bool GetFieldTrialParamsByFeature(const base::Feature& feature,
297 FieldTrialParams* params);
asvitkine79ab08c2017-01-30 23:27:05298
299// Retrieves a specific parameter value corresponding to |param_name| for the
300// specified field trial, based on its selected group. If the field trial does
301// not exist or the specified parameter does not exist, returns an empty
302// string. Calling this function will result in the field trial being marked as
303// active if found (i.e. group() will be called on it), if it wasn't already.
304// Thread safe.
305BASE_EXPORT std::string GetFieldTrialParamValue(const std::string& trial_name,
306 const std::string& param_name);
307
308// Retrieves a specific parameter value corresponding to |param_name| for the
309// field trial associated with the specified |feature|. A feature is associated
310// with at most one field trial and selected group. See base/feature_list.h for
311// more information on features. If the feature is not enabled, or the
312// specified parameter does not exist, returns an empty string. Calling this
313// function will result in the associated field trial being marked as active if
314// found (i.e. group() will be called on it), if it wasn't already. Thread safe.
315BASE_EXPORT std::string GetFieldTrialParamValueByFeature(
316 const base::Feature& feature,
317 const std::string& param_name);
318
Takashi Toyoshima2671988952024-08-26 05:58:34319// Same as GetFieldTrialParamValueByFeature(). But internally relies on
320// GetFieldTrialParamsByFeature to handle empty values in the map, and returns
321// |default_value| only if |param_name| is not found in the map.
322BASE_EXPORT std::string GetFieldTrialParamByFeatureAsString(
323 const base::Feature& feature,
324 const std::string& param_name,
325 const std::string& default_value);
326
asvitkine79ab08c2017-01-30 23:27:05327// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
328// string value into an int using base::StringToInt() and returns it, if
329// successful. Otherwise, it returns |default_value|. If the string value is not
330// empty and the conversion does not succeed, it produces a warning to LOG.
331BASE_EXPORT int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature,
332 const std::string& param_name,
333 int default_value);
334
335// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
336// string value into a double using base::StringToDouble() and returns it, if
337// successful. Otherwise, it returns |default_value|. If the string value is not
338// empty and the conversion does not succeed, it produces a warning to LOG.
339BASE_EXPORT double GetFieldTrialParamByFeatureAsDouble(
340 const base::Feature& feature,
341 const std::string& param_name,
342 double default_value);
343
344// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
345// string value into a boolean and returns it, if successful. Otherwise, it
346// returns |default_value|. The only string representations accepted here are
347// "true" and "false". If the string value is not empty and the conversion does
348// not succeed, it produces a warning to LOG.
349BASE_EXPORT bool GetFieldTrialParamByFeatureAsBool(
350 const base::Feature& feature,
351 const std::string& param_name,
352 bool default_value);
353
Minoru Chikamunefedb8272023-09-27 02:08:24354// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
355// string value into a base::TimeDelta and returns it, if successful. Otherwise,
356// it returns `default_value`. If the string value is not empty and the
357// conversion does not succeed, it produces a warning to LOG.
358BASE_EXPORT base::TimeDelta GetFieldTrialParamByFeatureAsTimeDelta(
359 const Feature& feature,
360 const std::string& param_name,
361 base::TimeDelta default_value);
362
Peter Kasting18e51f82025-01-14 16:02:38363// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
364// string value into an Enum and returns it, if successful. Otherwise, it
365// returns `default_value`. If the string value is not empty and the conversion
366// does not succeed, it produces a warning to LOG.
sfiera01757be62017-09-20 16:36:13367template <typename Enum>
Peter Kasting18e51f82025-01-14 16:02:38368Enum GetFieldTrialParamByFeatureAsEnum(
369 const Feature& feature,
370 const std::string& param_name,
371 Enum default_value,
372 base::span<const typename FeatureParam<Enum, true>::Option> options) {
373 std::string value = GetFieldTrialParamValueByFeature(feature, param_name);
374 if (value.empty()) {
sfiera01757be62017-09-20 16:36:13375 return default_value;
376 }
Peter Kasting18e51f82025-01-14 16:02:38377 for (const auto& option : options) {
378 if (value == option.name) {
379 return option.value;
Fergal Dalybcac47a2020-03-27 01:54:21380 }
Fergal Dalybcac47a2020-03-27 01:54:21381 }
Peter Kasting18e51f82025-01-14 16:02:38382 LogInvalidEnumValue(feature, param_name, value,
383 static_cast<int>(default_value));
384 return default_value;
385}
sfiera01757be62017-09-20 16:36:13386
asvitkine79ab08c2017-01-30 23:27:05387} // namespace base
388
389#endif // BASE_METRICS_FIELD_TRIAL_PARAMS_H_