blob: 877e29a699fa488a4b6eed8778a9872a7fb8d8bf [file] [log] [blame]
/*
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
#include <initializer_list>
#include <memory>
#include <string>
#include <vector>
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/string_encode.h"
// List support for field trial strings. FieldTrialList and FieldTrialStructList
// are used similarly to the other FieldTrialParameters, but take a variable
// number of parameters. A FieldTrialList<T> parses a |-delimeted string into a
// list of T, using ParseTypedParameter to parse the individual tokens.
// Example string: "my_list:1|2|3,empty_list,other_list:aardvark".
// A FieldTrialStructList combines multiple lists into a list-of-structs. It
// ensures that all its sublists parse correctly and have the same length, then
// uses user-supplied accessor functions to write those elements into structs of
// a user-supplied type.
// See the unit test for usage and behavior.
namespace webrtc {
class FieldTrialListBase : public FieldTrialParameterInterface {
protected:
friend class FieldTrialListWrapper;
explicit FieldTrialListBase(std::string key);
bool Failed() const;
bool Used() const;
virtual int Size() = 0;
bool failed_;
bool parse_got_called_;
};
// This class represents a vector of type T. The elements are separated by a |
// and parsed using ParseTypedParameter.
template <typename T>
class FieldTrialList : public FieldTrialListBase {
public:
explicit FieldTrialList(std::string key) : FieldTrialList(key, {}) {}
FieldTrialList(std::string key, std::initializer_list<T> default_values)
: FieldTrialListBase(key), values_(default_values) {}
std::vector<T> Get() const { return values_; }
operator std::vector<T>() const { return Get(); }
const T& operator[](size_t index) const { return values_[index]; }
const std::vector<T>* operator->() const { return &values_; }
protected:
bool Parse(absl::optional<std::string> str_value) override {
parse_got_called_ = true;
if (!str_value) {
values_.clear();
return true;
}
std::vector<std::string> tokens;
std::vector<T> new_values_;
rtc::split(str_value.value(), '|', &tokens);
for (std::string token : tokens) {
absl::optional<T> value = ParseTypedParameter<T>(token);
if (value) {
new_values_.push_back(*value);
} else {
failed_ = true;
return false;
}
}
values_.swap(new_values_);
return true;
}
int Size() override { return values_.size(); }
private:
std::vector<T> values_;
};
class FieldTrialListWrapper {
public:
virtual ~FieldTrialListWrapper() = default;
// Takes the element at the given index in the wrapped list and writes it to
// the given struct.
virtual void WriteElement(void* struct_to_write, int index) = 0;
virtual FieldTrialListBase* GetList() = 0;
int Length();
// Returns true iff the wrapped list has failed to parse at least one token.
bool Failed();
bool Used();
protected:
FieldTrialListWrapper() = default;
};
namespace field_trial_list_impl {
// The LambdaTypeTraits struct provides type information about lambdas in the
// template expressions below.
template <typename T>
struct LambdaTypeTraits : public LambdaTypeTraits<decltype(&T::operator())> {};
template <typename ClassType, typename RetType, typename SourceType>
struct LambdaTypeTraits<RetType* (ClassType::*)(SourceType*)const> {
using ret = RetType;
using src = SourceType;
};
template <typename T>
struct TypedFieldTrialListWrapper : FieldTrialListWrapper {
public:
TypedFieldTrialListWrapper(std::string key,
std::function<void(void*, T)> sink)
: list_(key), sink_(sink) {}
void WriteElement(void* struct_to_write, int index) override {
sink_(struct_to_write, list_[index]);
}
FieldTrialListBase* GetList() override { return &list_; }
private:
FieldTrialList<T> list_;
std::function<void(void*, T)> sink_;
};
} // namespace field_trial_list_impl
template <typename F,
typename Traits = typename field_trial_list_impl::LambdaTypeTraits<F>>
FieldTrialListWrapper* FieldTrialStructMember(std::string key, F accessor) {
return new field_trial_list_impl::TypedFieldTrialListWrapper<
typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) {
*accessor(static_cast<typename Traits::src*>(s)) = t;
});
}
// This base class is here to reduce the amount of code we have to generate for
// each type of FieldTrialStructList.
class FieldTrialStructListBase : public FieldTrialParameterInterface {
protected:
FieldTrialStructListBase(
std::initializer_list<FieldTrialListWrapper*> sub_lists)
: FieldTrialParameterInterface(""), sub_lists_() {
// Take ownership of the list wrappers generated by FieldTrialStructMember
// on the call site.
for (FieldTrialListWrapper* const* it = sub_lists.begin();
it != sub_lists.end(); it++) {
sub_parameters_.push_back((*it)->GetList());
sub_lists_.push_back(std::unique_ptr<FieldTrialListWrapper>(*it));
}
}
// Check that all of our sublists that were in the field trial string had the
// same number of elements. If they do, we return that length. If they had
// different lengths, any sublist had parse failures or no sublists had
// user-supplied values, we return -1.
int ValidateAndGetLength();
bool Parse(absl::optional<std::string> str_value) override;
std::vector<std::unique_ptr<FieldTrialListWrapper>> sub_lists_;
};
template <typename S>
class FieldTrialStructList : public FieldTrialStructListBase {
public:
FieldTrialStructList(std::initializer_list<FieldTrialListWrapper*> l,
std::initializer_list<S> default_list)
: FieldTrialStructListBase(l), values_(default_list) {}
std::vector<S> Get() const { return values_; }
operator std::vector<S>() const { return Get(); }
const S& operator[](size_t index) const { return values_[index]; }
const std::vector<S>* operator->() const { return &values_; }
protected:
void ParseDone() override {
int length = ValidateAndGetLength();
if (length == -1)
return;
std::vector<S> new_values(length, S());
for (std::unique_ptr<FieldTrialListWrapper>& li : sub_lists_) {
if (li->Used()) {
for (int i = 0; i < length; i++) {
li->WriteElement(&new_values[i], i);
}
}
}
values_.swap(new_values);
}
private:
std::vector<S> values_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_