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