blob: dc596462894433b49393908dcf47ad0670ca7f80 [file]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mpact/sim/generic/component.h"
#include <string>
#include <utility>
namespace mpact {
namespace sim {
namespace generic {
using ::mpact::sim::proto::ComponentData;
using ::mpact::sim::proto::ComponentValueEntry;
// The Get[ChildComponent, Counter, Config] methods are all identical except
// for the element data type. This template static function factors out the
// commonalities.
template <typename M, typename T>
static T *GetMapEntry(const M &map, absl::string_view name) {
auto ptr = map.find(name);
return (ptr == map.end()) ? nullptr : ptr->second;
}
// The Add[ChildComponent,Counter,Config] methods are all identical except
// for the element data type. This template static function factors out the
// commonalities.
template <typename M, typename T>
static absl::Status AddMapEntry(absl::string_view name, T *entry, M *map) {
if (entry == nullptr) return absl::InvalidArgumentError("entry is nullptr");
auto ptr = map->find(name);
if (ptr != map->end()) {
return absl::InternalError(
absl::StrCat("entry with name '", name, "' already inserted"));
}
map->emplace(name, entry);
return absl::OkStatus();
}
// Constructors.
Component::Component(std::string name) : component_name_(std::move(name)) {}
Component::Component(std::string name, Component *parent)
: component_name_(std::move(name)) {
if (parent != nullptr) {
parent->AddChildComponent(*this).IgnoreError();
}
}
// Call the generic static function to Add the element.
absl::Status Component::AddChildComponent(Component &child) {
auto status = AddMapEntry(child.component_name(), &child, &child_map_);
if (!status.ok()) return status;
child.SetParent(this);
return absl::OkStatus();
}
absl::Status Component::AddCounter(CounterBaseInterface *counter) {
if (!counter->IsInitialized()) {
return absl::InvalidArgumentError("Counter has not been initialized");
}
return AddMapEntry(counter->GetName(), counter, &counter_map_);
}
absl::Status Component::AddConfig(ConfigBase *config) {
return AddMapEntry(config->name(), config, &config_map_);
}
// Call the generic static function to Get the element.
Component *Component::GetChildComponent(absl::string_view name) const {
return GetMapEntry<ComponentMap, Component>(child_map_, name);
}
CounterBaseInterface *Component::GetCounter(absl::string_view name) const {
return GetMapEntry<CounterMap, CounterBaseInterface>(counter_map_, name);
}
ConfigBase *Component::GetConfig(absl::string_view name) const {
return GetMapEntry<ConfigMap, ConfigBase>(config_map_, name);
}
// Import information from the component data proto.
absl::Status Component::Import(const ComponentData &component_data) {
// Checking that the proto name matches. Recursive calls will not generate
// this error, but need to check at the top level.
if (!component_data.has_name() ||
(component_name() != component_data.name())) {
return absl::InternalError(absl::StrCat(
"Name mismatch on import '", component_name(), "' != '",
(component_data.has_name() ? component_data.name() : ""), "'"));
}
// First import self - as this may cause new child components to be created
// based on the values of the configuration entries.
auto status = ImportSelf(component_data);
if (!status.ok()) return status;
// Then import for the child components.
status = ImportChildren(component_data);
if (!status.ok()) return status;
return absl::OkStatus();
}
absl::Status Component::ImportSelf(const ComponentData &component_data) {
for (auto const &entry : component_data.configuration()) {
if (!entry.has_name()) {
// The proto is malformed.
return absl::InternalError("Missing name in component value");
}
ConfigBase *config = GetConfig(entry.name());
// It's not an error if there are proto values for config entries that
// aren't registered. Just skip and continue.
if (config == nullptr) continue;
auto status = config->Import(&entry);
if (!status.ok()) return status;
}
return absl::OkStatus();
}
absl::Status Component::ImportChildren(const ComponentData &component_data) {
for (auto const &child_data : component_data.component_data()) {
if (!child_data.has_name()) {
return absl::InternalError("Unnamed child component");
}
Component *child = GetChildComponent(child_data.name());
if (child == nullptr) continue;
auto status = child->Import(child_data);
if (!status.ok()) return status;
}
return absl::OkStatus();
}
void Component::ImportDone() const {
// Propagate down the component hierarchy.
for (auto const &[unused, child] : child_map_) {
child->ImportDone();
}
// Notify through callbacks.
for (auto const &callback : callback_vec_) {
callback();
}
}
// Export the information reachable from this component to the mutable
// component data proto.
absl::Status Component::Export(ComponentData *component_data) {
if (component_data == nullptr) {
return absl::InvalidArgumentError("Component data is null");
}
component_data->set_name(component_name());
// Export the configuration values.
for (auto const &[unused, config_pair] : config_map_) {
ComponentValueEntry *entry = component_data->add_configuration();
auto status = config_pair->Export(entry);
if (!status.ok()) return status;
}
// Export the counter values.
for (auto const &[unused, counter_pair] : counter_map_) {
ComponentValueEntry *entry = component_data->add_statistics();
auto status = counter_pair->Export(entry);
if (!status.ok()) return status;
}
// Recursively export child component data.
for (auto const &[unused, child_pair] : child_map_) {
ComponentData *child_data = component_data->add_component_data();
auto status = child_pair->Export(child_data);
if (!status.ok()) return status;
}
return absl::OkStatus();
}
} // namespace generic
} // namespace sim
} // namespace mpact