blob: 6bf158054057e33a5619dffdfd3f327c87451e97 [file] [log] [blame]
// Copyright 2019 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.
#ifndef SRC_LIB_INSPECT_DEPRECATED_HIERARCHY_H_
#define SRC_LIB_INSPECT_DEPRECATED_HIERARCHY_H_
#include <fuchsia/inspect/deprecated/cpp/fidl.h>
#include <lib/fit/optional.h>
#include <lib/fit/variant.h>
#include <limits>
#include <string>
#include <vector>
namespace inspect_deprecated {
// Namespace hierarchy contains classes representing the parts of a parsed
// ObjectHierarchy.
namespace hierarchy {
// Describes how an array of values should be displayed.
enum class ArrayDisplayFormat {
// The array should be displayed as a flat list of numeric types.
FLAT,
// The array consists of parameters and buckets for a linear histogram.
LINEAR_HISTOGRAM,
// The array consists of parameters and buckets for an exponential
// histogram.
EXPONENTIAL_HISTOGRAM,
};
namespace internal {
template <typename T, size_t FormatIndex>
// Internal class wrapping a typed value.
class Value {
public:
// Index into the format enum for this type.
constexpr static size_t format_index = FormatIndex;
// Construct an empty value.
Value() {}
// Construct a Value wrapping the specific value.
explicit Value(T value) : value_(std::move(value)) {}
// Obtain the wrapped value.
const T& value() const { return value_; }
private:
T value_;
};
// An Array is a specialization of Value that contains multiple values as well
// as a display format.
template <typename T, size_t FormatIndex>
class Array : public Value<std::vector<T>, FormatIndex> {
public:
// Describes a single bucket in a histogram.
//
// This contains the count of values falling in interval [floor, upper_limit).
struct HistogramBucket {
// The floor of values falling in this bucket, inclusive.
T floor;
// The upper limit for values falling in this bucket, exclusive.
T upper_limit;
// The count of values falling in [floor, upper_limit).
T count;
HistogramBucket(T floor, T upper_limit, T count)
: floor(floor), upper_limit(upper_limit), count(count) {}
};
// Constructs an array consisting of values and a display format.
Array(std::vector<T> values, ArrayDisplayFormat display_format)
: Value<std::vector<T>, FormatIndex>(std::move(values)), display_format_(display_format) {}
// Gets the display format for this array.
ArrayDisplayFormat GetDisplayFormat() const { return display_format_; }
// Gets the buckets for this array interpreted as a histogram.
// If the array does not represent a valid histogram, the returned array will
// be empty.
std::vector<HistogramBucket> GetBuckets() const;
private:
// The display format for this array.
ArrayDisplayFormat display_format_;
};
template <typename T, size_t FormatIndex>
std::vector<typename Array<T, FormatIndex>::HistogramBucket> Array<T, FormatIndex>::GetBuckets()
const {
std::vector<HistogramBucket> ret;
const auto& value = this->value();
if (display_format_ == ArrayDisplayFormat::LINEAR_HISTOGRAM) {
if (value.size() < 5) {
// We need at least floor, step_size, underflow, bucket 0, overflow.
return ret;
}
T floor = value[0];
const T step_size = value[1];
if (std::numeric_limits<T>::has_infinity) {
ret.push_back(HistogramBucket(-std::numeric_limits<T>::infinity(), floor, value[2]));
} else {
ret.push_back(HistogramBucket(std::numeric_limits<T>::min(), floor, value[2]));
}
for (size_t i = 3; i < value.size() - 1; i++) {
ret.push_back(HistogramBucket(floor, floor + step_size, value[i]));
floor += step_size;
}
if (std::numeric_limits<T>::has_infinity) {
ret.push_back(
HistogramBucket(floor, std::numeric_limits<T>::infinity(), value[value.size() - 1]));
} else {
ret.push_back(HistogramBucket(floor, std::numeric_limits<T>::max(), value[value.size() - 1]));
}
} else if (display_format_ == ArrayDisplayFormat::EXPONENTIAL_HISTOGRAM) {
if (value.size() < 6) {
// We need at least floor, initial_step, step_multiplier, underflow,
// bucket 0, overflow.
return ret;
}
T floor = value[0];
T current_step = value[1];
const T step_multiplier = value[2];
if (std::numeric_limits<T>::has_infinity) {
ret.push_back(HistogramBucket(-std::numeric_limits<T>::infinity(), floor, value[3]));
} else {
ret.push_back(HistogramBucket(std::numeric_limits<T>::min(), floor, value[3]));
}
T current_floor = floor;
T offset = current_step;
for (size_t i = 4; i < value.size() - 1; i++) {
T upper = floor + offset;
ret.push_back(HistogramBucket(current_floor, upper, value[i]));
offset *= step_multiplier;
current_floor = upper;
}
if (std::numeric_limits<T>::has_infinity) {
ret.push_back(HistogramBucket(current_floor, std::numeric_limits<T>::infinity(),
value[value.size() - 1]));
} else {
ret.push_back(
HistogramBucket(current_floor, std::numeric_limits<T>::max(), value[value.size() - 1]));
}
}
return ret;
}
// Internal class associating a name with one of several types of value.
template <typename TypeVariant, typename FormatType>
class NamedValue {
public:
// Constructs a NamedValue associating the given name with the value.
template <typename T>
NamedValue(std::string name, T value) : name_(std::move(name)) {
format_ = static_cast<FormatType>(T::format_index);
value_.template emplace<T::format_index>(std::move(value));
}
// Checks if this NamedValue contains the templated type.
template <typename T>
bool Contains() const {
return value_.index() == T::format_index;
}
// Gets the value by type. If this NamedValue does not contain the given type,
// this method panics.
template <typename T>
const T& Get() const {
return value_.template get<T::format_index>();
}
// Gets the name of this NamedValue.
const std::string& name() const { return name_; }
// Gets the format of the wrapped value.
FormatType format() const { return format_; }
private:
FormatType format_;
std::string name_;
TypeVariant value_;
};
} // namespace internal
// Describes the format of a parsed metric.
enum class MetricFormat {
INVALID = 0,
INT = 1,
UINT = 2,
DOUBLE = 3,
INT_ARRAY = 4,
UINT_ARRAY = 5,
DOUBLE_ARRAY = 6,
};
using IntMetric = internal::Value<int64_t, static_cast<size_t>(MetricFormat::INT)>;
using UIntMetric = internal::Value<uint64_t, static_cast<size_t>(MetricFormat::UINT)>;
using DoubleMetric = internal::Value<double, static_cast<size_t>(MetricFormat::DOUBLE)>;
using IntArray = internal::Array<int64_t, static_cast<size_t>(MetricFormat::INT_ARRAY)>;
using UIntArray = internal::Array<uint64_t, static_cast<size_t>(MetricFormat::UINT_ARRAY)>;
using DoubleArray = internal::Array<double, static_cast<size_t>(MetricFormat::DOUBLE_ARRAY)>;
// Metric consist of a name and a value corresponding to one MetricFormat.
using Metric =
internal::NamedValue<fit::internal::variant<fit::internal::monostate, IntMetric, UIntMetric,
DoubleMetric, IntArray, UIntArray, DoubleArray>,
MetricFormat>;
enum class PropertyFormat {
INVALID = 0,
STRING = 1,
BYTES = 2,
};
using StringProperty = internal::Value<std::string, static_cast<size_t>(PropertyFormat::STRING)>;
using ByteVectorProperty =
internal::Value<std::vector<uint8_t>, static_cast<size_t>(PropertyFormat::BYTES)>;
// Property consists of a name and a value corresponding to one PropertyFormat.
using Property = internal::NamedValue<
fit::internal::variant<fit::internal::monostate, StringProperty, ByteVectorProperty>,
PropertyFormat>;
// A Node stored in an ObjectHierarchy.
class Node {
public:
// Construct an empty Node.
Node() = default;
// Construct a Node with a name and no properties or metrics.
explicit Node(std::string name);
// Construct a Node with a name, properties, and metrics.
Node(std::string name, std::vector<Property> properties, std::vector<Metric> metrics);
// Obtains reference to name.
const std::string& name() const { return name_; }
std::string& name() { return name_; }
// Obtains reference to properties.
const std::vector<Property>& properties() const { return properties_; }
std::vector<Property>& properties() { return properties_; }
// Obtains reference to metrics.
const std::vector<Metric>& metrics() const { return metrics_; }
std::vector<Metric>& metrics() { return metrics_; }
// Sorts the metrics and properties of this object by name.
void Sort();
private:
// The name of this Node.
std::string name_;
// The properties for this Node.
std::vector<Property> properties_;
// The metrics for this Node.
std::vector<Metric> metrics_;
};
} // namespace hierarchy
// Represents a hierarchy of node objects rooted under one particular node.
// This class includes constructors that handle reading the hierarchy from
// various sources.
//
// TODO(CF-703): Rename to InspectHierarchy.
class ObjectHierarchy {
public:
ObjectHierarchy() = default;
// Directly construct an object hierarchy consisting of a node and a list
// of children.
ObjectHierarchy(hierarchy::Node node, std::vector<ObjectHierarchy> children);
// Allow moving, disallow copying.
ObjectHierarchy(ObjectHierarchy&&) = default;
ObjectHierarchy(const ObjectHierarchy&) = delete;
ObjectHierarchy& operator=(ObjectHierarchy&&) = default;
ObjectHierarchy& operator=(const ObjectHierarchy&) = delete;
// Obtains the Node at this level of this hierarchy.
const hierarchy::Node& node() const { return node_; }
hierarchy::Node& node() { return node_; }
// Obtains the name of the Node at this level of the hierarchy.
const std::string& name() const { return node_.name(); }
// Gets the children of this object in the hierarchy.
const std::vector<ObjectHierarchy>& children() const { return children_; };
std::vector<ObjectHierarchy>& children() { return children_; };
// Gets a child in this ObjectHierarchy by path.
// Returns NULL if the requested child could not be found.
const ObjectHierarchy* GetByPath(std::vector<std::string> path) const;
// Sort metrics, properties, and children of this object by name.
void Sort();
private:
hierarchy::Node node_;
std::vector<ObjectHierarchy> children_;
};
} // namespace inspect_deprecated
#endif // SRC_LIB_INSPECT_DEPRECATED_HIERARCHY_H_