blob: 16ce00c2d6c6e12a3fbf3b2324ece3ad57f9d24a [file] [log] [blame] [edit]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lib/client/cpp/buckets_config.h"
#include <cmath>
#include "src/logging.h"
namespace cobalt::config {
uint32_t IntegerBucketConfig::BucketIndex(int64_t val) const {
// 0 is the underflow bucket.
if (val < floors_[0]) {
return 0;
}
// TODO(https://fxbug.dev/278918086): Maybe switch to binary search?
for (uint32_t i = 1; i < floors_.size(); i++) {
if (val >= floors_[i - 1] && val < floors_[i]) {
return i;
}
}
// floors_.size() is the overflow bucket.
return static_cast<uint32_t>(floors_.size());
}
std::unique_ptr<IntegerBucketConfig> IntegerBucketConfig::CreateFromProto(
const IntegerBuckets& int_buckets) {
switch (int_buckets.buckets_case()) {
case IntegerBuckets::kExponential:
return CreateExponential(
int_buckets.exponential().floor(), int_buckets.exponential().num_buckets(),
int_buckets.exponential().initial_step(), int_buckets.exponential().step_multiplier_float());
case IntegerBuckets::kLinear:
return CreateLinear(int_buckets.linear().floor(), int_buckets.linear().num_buckets(),
int_buckets.linear().step_size());
case IntegerBuckets::BUCKETS_NOT_SET:
LOG(ERROR) << "IntegerBuckets with buckets field not set.";
return std::unique_ptr<IntegerBucketConfig>();
}
}
int64_t IntegerBucketConfig::BucketFloor(uint32_t index) const {
int64_t floor = INT64_MIN;
if (index > 0 && index <= floors_.size()) {
floor = floors_[index - 1];
}
return floor;
}
bool IntegerBucketConfig::IsImpossibleBucket(const uint32_t index) const {
if (index > floors_.size()) {
// Invalid bucket index
return true;
}
if (index == 0 || index == floors_.size()) {
// The over and underflow buckets are always possible
return false;
}
// Bucket 0 is [min_int64, floors_[0])
// Bucket floors_.size() is [floors_[floors_.size()-1], max_int64]
// Otherwise, bucket i is defined as [floors_[i-1], floors_[i])
// Impossible buckets don't contain an integer value, e.g. [1.1, 1.21). This, combined with the
// fact floors are computed with a ceiling functions, means impossible buckets will always have
// the same floor as the bucket after them.
return floors_[index - 1] == floors_[index];
}
// TODO(https://fxbug.dev/278930401): NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
std::unique_ptr<IntegerBucketConfig> IntegerBucketConfig::CreateLinear(int64_t floor,
uint32_t num_buckets,
uint32_t step_size) {
if (num_buckets == 0) {
LOG(ERROR) << "LinearIntegerBucket with 0 buckets.";
return std::unique_ptr<IntegerBucketConfig>();
}
if (step_size == 0) {
LOG(ERROR) << "LinearIntegerBucket with 0 step size.";
return std::unique_ptr<IntegerBucketConfig>();
}
std::vector<int64_t> floors(num_buckets + 1);
for (int64_t i = 0; i < num_buckets + 1; i++) {
floors[i] = floor + i * step_size;
}
return std::unique_ptr<IntegerBucketConfig>(new IntegerBucketConfig(floors));
}
std::unique_ptr<IntegerBucketConfig> IntegerBucketConfig::CreateExponential(
// TODO(https://fxbug.dev/278930401): NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
int64_t floor, uint32_t num_buckets, uint32_t initial_step, float step_multiplier_float) {
if (num_buckets == 0) {
LOG(ERROR) << "ExponentialIntegerBucket with 0 buckets.";
return std::unique_ptr<IntegerBucketConfig>();
}
if (initial_step == 0) {
LOG(ERROR) << "ExponentialIntegerBucket with 0 initial_step.";
return std::unique_ptr<IntegerBucketConfig>();
}
if (step_multiplier_float <= 1) {
LOG(ERROR) << "ExponentialIntegerBucket with less or equal to 1 step_multiplier_float.";
return std::unique_ptr<IntegerBucketConfig>();
}
std::vector<int64_t> floors(num_buckets + 1);
floors[0] = floor;
long double offset = static_cast<long double>(initial_step);
for (uint32_t i = 1; i < num_buckets + 1; i++) {
floors[i] = static_cast<int64_t>(std::ceil(floor + offset));
offset *= step_multiplier_float;
}
return std::unique_ptr<IntegerBucketConfig>(new IntegerBucketConfig(floors));
}
} // namespace cobalt::config