| // 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 |