blob: 3dfb56bdb383a10e48ac2cfbc8d32b3fc076d514 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/attribution_reporting/aggregatable_named_budget_defs.h"
#include <stddef.h>
#include <algorithm>
#include <optional>
#include <string>
#include <utility>
#include "base/check.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "components/attribution_reporting/aggregatable_utils.h"
#include "components/attribution_reporting/constants.h"
#include "components/attribution_reporting/parsing_utils.h"
#include "components/attribution_reporting/source_registration_error.mojom.h"
namespace attribution_reporting {
namespace {
using ::attribution_reporting::mojom::SourceRegistrationError;
bool AggregatableNamedBudgetKeyHasValidLength(const std::string& key) {
return key.size() <= kMaxLengthPerAggregatableNamedBudgetName;
}
bool IsValid(const AggregatableNamedBudgetDefs::BudgetMap& budgets) {
return budgets.size() <= kMaxAggregatableNamedBudgetsPerSource &&
std::ranges::all_of(budgets, [](const auto& budget) {
return AggregatableNamedBudgetKeyHasValidLength(budget.first) &&
IsAggregatableBudgetInRange(budget.second);
});
}
} // namespace
// static
base::expected<AggregatableNamedBudgetDefs, SourceRegistrationError>
AggregatableNamedBudgetDefs::FromJSON(const base::Value* v) {
if (!v) {
return AggregatableNamedBudgetDefs();
}
const base::Value::Dict* dict = v->GetIfDict();
if (!dict) {
return base::unexpected(
SourceRegistrationError::kAggregatableNamedBudgetsDictInvalid);
}
const size_t num_keys = dict->size();
if (num_keys > kMaxAggregatableNamedBudgetsPerSource) {
return base::unexpected(
SourceRegistrationError::kAggregatableNamedBudgetsDictInvalid);
}
BudgetMap::container_type budgets;
budgets.reserve(num_keys);
for (auto [key, value] : *dict) {
if (!AggregatableNamedBudgetKeyHasValidLength(key)) {
return base::unexpected(
SourceRegistrationError::kAggregatableNamedBudgetsKeyTooLong);
}
ASSIGN_OR_RETURN(int budget, ParseInt(value), [](ParseError) {
return SourceRegistrationError::kAggregatableNamedBudgetsValueInvalid;
});
if (!IsAggregatableBudgetInRange(budget)) {
return base::unexpected(
SourceRegistrationError::kAggregatableNamedBudgetsValueInvalid);
}
budgets.emplace_back(key, budget);
}
return AggregatableNamedBudgetDefs(
BudgetMap(base::sorted_unique, std::move(budgets)));
}
// static
std::optional<AggregatableNamedBudgetDefs>
AggregatableNamedBudgetDefs::FromBudgetMap(BudgetMap budgets) {
if (!IsValid(budgets)) {
return std::nullopt;
}
return AggregatableNamedBudgetDefs(std::move(budgets));
}
AggregatableNamedBudgetDefs::AggregatableNamedBudgetDefs(
AggregatableNamedBudgetDefs::BudgetMap budgets)
: budgets_(std::move(budgets)) {
CHECK(IsValid(budgets_));
}
AggregatableNamedBudgetDefs::AggregatableNamedBudgetDefs() = default;
AggregatableNamedBudgetDefs::~AggregatableNamedBudgetDefs() = default;
AggregatableNamedBudgetDefs::AggregatableNamedBudgetDefs(
const AggregatableNamedBudgetDefs&) = default;
AggregatableNamedBudgetDefs::AggregatableNamedBudgetDefs(
AggregatableNamedBudgetDefs&&) = default;
AggregatableNamedBudgetDefs& AggregatableNamedBudgetDefs::operator=(
const AggregatableNamedBudgetDefs&) = default;
AggregatableNamedBudgetDefs& AggregatableNamedBudgetDefs::operator=(
AggregatableNamedBudgetDefs&&) = default;
void AggregatableNamedBudgetDefs::Serialize(base::Value::Dict& dict) const {
if (budgets_.empty()) {
return;
}
base::Value::Dict budget_dict;
for (const auto& [key, value] : budgets_) {
budget_dict.Set(key, value);
}
dict.Set(kAggregatableNamedBudgets, std::move(budget_dict));
}
} // namespace attribution_reporting