blob: c18c01962c24d7a938c86096826a952296f0a96f [file] [log] [blame]
#ifndef AOS_UTIL_ERROR_COUNTER_H_
#define AOS_UTIL_ERROR_COUNTER_H_
#include <stddef.h>
#include <array>
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "flatbuffers/buffer.h"
#include "flatbuffers/flatbuffer_builder.h"
#include "flatbuffers/vector.h"
namespace aos::util {
// Class to manage simple error counters for flatbuffer status message.
// This presumes that you have a flatbuffer enum type Error which has
// enum values that are continuous and start at zero. These are then
// counted by a Count flatbuffer table that is of the format:
// table Count {
// error:Error (id: 0);
// count:uint (id: 1);
// }
// And which is stored as a vector in the resulting status message,
// where the index within the vector corresponds with the underlying
// value of the enum.
template <typename Error, typename Count>
class ErrorCounter {
public:
static constexpr size_t kNumErrors =
static_cast<int>(Error::MAX) - static_cast<int>(Error::MIN) + 1;
static_assert(0 == static_cast<int>(Error::MIN),
"Expected Error enum values to start at zero.");
// TODO(james): Is there any good way to check that the values are contiguous?
// There's no Error::COUNT, and the method I previously used (checking the
// size of the return type of EnumValues*()) requires the user to pass that
// method as a template argument.
ErrorCounter() = default;
static flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
Initialize(flatbuffers::FlatBufferBuilder *fbb) {
std::array<flatbuffers::Offset<Count>, kNumErrors> count_offsets;
for (size_t ii = 0; ii < kNumErrors; ++ii) {
typename Count::Builder builder(*fbb);
builder.add_error(static_cast<Error>(ii));
builder.add_count(0);
count_offsets[ii] = builder.Finish();
}
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
offset = fbb->CreateVector(count_offsets.data(), count_offsets.size());
return offset;
}
template <typename Static>
static void InitializeStaticFbs(Static *builder) {
CHECK(builder->reserve(kNumErrors));
for (size_t ii = 0; ii < kNumErrors; ++ii) {
auto element = builder->emplace_back();
CHECK(element != nullptr);
element->set_error(static_cast<Error>(ii));
element->set_count(0);
}
}
void set_mutable_vector(
flatbuffers::Vector<flatbuffers::Offset<Count>> *vector) {
vector_ = vector;
}
void InvalidateBuffer() { vector_ = nullptr; }
void IncrementError(Error error) {
CHECK(vector_ != nullptr);
DCHECK_LT(static_cast<size_t>(error), vector_->size());
Count *counter = vector_->GetMutableObject(static_cast<size_t>(error));
counter->mutate_count(counter->count() + 1);
}
// Sets all the error counts to zero.
void ResetCounts() {
CHECK(vector_ != nullptr);
DCHECK_EQ(vector_->size(), kNumErrors) << this << " vector " << vector_;
for (size_t ii = 0; ii < kNumErrors; ++ii) {
vector_->GetMutableObject(ii)->mutate_count(0);
}
}
private:
flatbuffers::Vector<flatbuffers::Offset<Count>> *vector_ = nullptr;
};
// The ArrayErrorCounter serves the same purpose as the ErrorCounter class,
// except that:
// (a) It owns its own memory, rather than modifying a flatbuffer in-place.
// (b) Because of this, the user has greater flexibility in choosing when to
// reset the error counters.
template <typename Error, typename Count>
class ArrayErrorCounter {
public:
static constexpr size_t kNumErrors = ErrorCounter<Error, Count>::kNumErrors;
ArrayErrorCounter() { ResetCounts(); }
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
PopulateCounts(flatbuffers::FlatBufferBuilder *fbb) const {
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
offset = ErrorCounter<Error, Count>::Initialize(fbb);
flatbuffers::Vector<flatbuffers::Offset<Count>> *vector =
flatbuffers::GetMutableTemporaryPointer(*fbb, offset);
for (size_t ii = 0; ii < kNumErrors; ++ii) {
vector->GetMutableObject(ii)->mutate_count(error_counts_.at(ii));
}
return offset;
}
template <typename Static>
void PopulateCountsStaticFbs(Static *builder) const {
ErrorCounter<Error, Count>::InitializeStaticFbs(builder);
for (size_t ii = 0; ii < kNumErrors; ++ii) {
builder->at(ii).set_count(error_counts_.at(ii));
}
}
void IncrementError(Error error) {
DCHECK_LT(static_cast<size_t>(error), error_counts_.size());
error_counts_.at(static_cast<size_t>(error))++;
}
// Sets all the error counts to zero.
void ResetCounts() { error_counts_.fill(0); }
private:
std::array<size_t, kNumErrors> error_counts_;
};
} // namespace aos::util
#endif // AOS_UTIL_ERROR_COUNTER_H_