diff --git a/form/form/CMakeLists.txt b/form/form/CMakeLists.txt index 5057b795..bb46bb43 100644 --- a/form/form/CMakeLists.txt +++ b/form/form/CMakeLists.txt @@ -3,5 +3,5 @@ # External dependencies: find_package( PHLEX ) # Component(s) in the package: -add_library(form form.cpp config.cpp) +add_library(form form_reader.cpp form_writer.cpp config.cpp) target_link_libraries(form persistence) diff --git a/form/form/form.hpp b/form/form/form.hpp deleted file mode 100644 index d01485bc..00000000 --- a/form/form/form.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_FORM_FORM_HPP -#define FORM_FORM_FORM_HPP - -#include "form/config.hpp" -#include "persistence/ipersistence.hpp" - -#include -#include -#include -#include -#include - -namespace form::experimental { - - struct product_with_name { - std::string label; - void const* data; - std::type_info const* type; - }; - - class form_interface { - public: - form_interface(config::output_item_config const& output_config, - config::tech_setting_config const& tech_config); - ~form_interface() = default; - - void write(std::string const& creator, - std::string const& segment_id, - product_with_name const& product); - - void write(std::string const& creator, - std::string const& segment_id, - std::vector const& products); - - void read(std::string const& creator, - std::string const& segment_id, - product_with_name& product); - - private: - std::unique_ptr m_pers; - std::map m_product_to_config; - }; -} - -#endif // FORM_FORM_FORM_HPP diff --git a/form/form/form_reader.cpp b/form/form/form_reader.cpp new file mode 100644 index 00000000..0bc009a0 --- /dev/null +++ b/form/form/form_reader.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2025 ... + +#include "form_reader.hpp" + +#include +#include + +namespace form::experimental { + + form_reader_interface::form_reader_interface(config::output_item_config const& output_config, + config::tech_setting_config const& tech_config) : + m_pers(nullptr) + { + for (auto const& item : output_config.getItems()) { + m_product_to_config.emplace(item.product_name, + form::experimental::config::PersistenceItem( + item.product_name, item.file_name, item.technology)); + } + + m_pers = form::detail::experimental::createPersistenceReader(); + m_pers->configureOutputItems(output_config); + m_pers->configureTechSettings(tech_config); + } + + void form_reader_interface::read(std::string const& creator, + std::string const& segment_id, + product_with_name& pb) + { + + auto it = m_product_to_config.find(pb.label); + if (it == m_product_to_config.end()) { + throw std::runtime_error("No configuration found for product: " + pb.label); + } + + m_pers->read(creator, pb.label, segment_id, &pb.data, *pb.type); + } +} diff --git a/form/form/form_reader.hpp b/form/form/form_reader.hpp new file mode 100644 index 00000000..17ca0b8b --- /dev/null +++ b/form/form/form_reader.hpp @@ -0,0 +1,34 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_FORM_FORM_READER_HPP +#define FORM_FORM_FORM_READER_HPP + +#include "form/config.hpp" +#include "form/product_with_name.hpp" +#include "persistence/ipersistence_reader.hpp" + +#include +#include +#include +#include +#include + +namespace form::experimental { + + class form_reader_interface { + public: + form_reader_interface(config::output_item_config const& output_config, + config::tech_setting_config const& tech_config); + ~form_reader_interface() = default; + + void read(std::string const& creator, + std::string const& segment_id, + product_with_name& product); + + private: + std::unique_ptr m_pers; + std::map m_product_to_config; + }; +} + +#endif // FORM_FORM_FORM_READER_HPP diff --git a/form/form/form.cpp b/form/form/form_writer.cpp similarity index 62% rename from form/form/form.cpp rename to form/form/form_writer.cpp index 70fa7853..afc3f393 100644 --- a/form/form/form.cpp +++ b/form/form/form_writer.cpp @@ -1,14 +1,14 @@ // Copyright (C) 2025 ... -#include "form.hpp" +#include "form_writer.hpp" #include #include namespace form::experimental { - form_interface::form_interface(config::output_item_config const& output_config, - config::tech_setting_config const& tech_config) : + form_writer_interface::form_writer_interface(config::output_item_config const& output_config, + config::tech_setting_config const& tech_config) : m_pers(nullptr) { for (auto const& item : output_config.getItems()) { @@ -17,14 +17,14 @@ namespace form::experimental { item.product_name, item.file_name, item.technology)); } - m_pers = form::detail::experimental::createPersistence(); + m_pers = form::detail::experimental::createPersistenceWriter(); m_pers->configureOutputItems(output_config); m_pers->configureTechSettings(tech_config); } - void form_interface::write(std::string const& creator, - std::string const& segment_id, - product_with_name const& pb) + void form_writer_interface::write(std::string const& creator, + std::string const& segment_id, + product_with_name const& pb) { auto it = m_product_to_config.find(pb.label); @@ -40,9 +40,9 @@ namespace form::experimental { m_pers->commitOutput(creator, segment_id); } - void form_interface::write(std::string const& creator, - std::string const& segment_id, - std::vector const& products) + void form_writer_interface::write(std::string const& creator, + std::string const& segment_id, + std::vector const& products) { if (products.empty()) @@ -69,16 +69,4 @@ namespace form::experimental { m_pers->commitOutput(creator, segment_id); } - void form_interface::read(std::string const& creator, - std::string const& segment_id, - product_with_name& pb) - { - - auto it = m_product_to_config.find(pb.label); - if (it == m_product_to_config.end()) { - throw std::runtime_error("No configuration found for product: " + pb.label); - } - - m_pers->read(creator, pb.label, segment_id, &pb.data, *pb.type); - } } diff --git a/form/form/form_writer.hpp b/form/form/form_writer.hpp new file mode 100644 index 00000000..0011f041 --- /dev/null +++ b/form/form/form_writer.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_FORM_FORM_WRITER_HPP +#define FORM_FORM_FORM_WRITER_HPP + +#include "form/config.hpp" +#include "form/product_with_name.hpp" +#include "persistence/ipersistence_writer.hpp" + +#include +#include +#include +#include + +namespace form::experimental { + + class form_writer_interface { + public: + form_writer_interface(config::output_item_config const& output_config, + config::tech_setting_config const& tech_config); + ~form_writer_interface() = default; + + void write(std::string const& creator, + std::string const& segment_id, + product_with_name const& product); + + void write(std::string const& creator, + std::string const& segment_id, + std::vector const& products); + + private: + std::unique_ptr m_pers; + std::map m_product_to_config; + }; +} + +#endif // FORM_FORM_FORM_WRITER_HPP diff --git a/form/form/product_with_name.hpp b/form/form/product_with_name.hpp new file mode 100644 index 00000000..f0789f8d --- /dev/null +++ b/form/form/product_with_name.hpp @@ -0,0 +1,15 @@ +#ifndef FORM_FORM_PRODUCT_WITH_NAME_HPP +#define FORM_FORM_PRODUCT_WITH_NAME_HPP + +#include +#include + +namespace form::experimental { + struct product_with_name { + std::string label; + void const* data; + std::type_info const* type; + }; +} + +#endif // FORM_FORM_PRODUCT_WITH_NAME_HPP diff --git a/form/form_module.cpp b/form/form_module.cpp index f18a5c07..fa67134f 100644 --- a/form/form_module.cpp +++ b/form/form_module.cpp @@ -5,7 +5,7 @@ // FORM headers - these need to be available via CMake configuration // need to set up the build system to find these headers #include "form/config.hpp" -#include "form/form.hpp" +#include "form/form_writer.hpp" #include "form/technology.hpp" #include @@ -43,7 +43,8 @@ namespace { } // Initialize FORM interface - m_form_interface = std::make_unique(output_cfg, tech_cfg); + m_form_interface = + std::make_unique(output_cfg, tech_cfg); } // This method is called by Phlex - signature must be: void(product_store const&) @@ -102,7 +103,7 @@ namespace { private: std::string const m_output_file; int const m_technology; - std::unique_ptr m_form_interface; + std::unique_ptr m_form_interface; }; } diff --git a/form/persistence/CMakeLists.txt b/form/persistence/CMakeLists.txt index 7df3484e..887b9f46 100644 --- a/form/persistence/CMakeLists.txt +++ b/form/persistence/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2025 ... # Component(s) in the package: -add_library(persistence persistence.cpp) +add_library(persistence persistence_reader.cpp persistence_writer.cpp persistence_utils.cpp) target_link_libraries(persistence core storage) diff --git a/form/persistence/ipersistence_reader.hpp b/form/persistence/ipersistence_reader.hpp new file mode 100644 index 00000000..b5183ac2 --- /dev/null +++ b/form/persistence/ipersistence_reader.hpp @@ -0,0 +1,40 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_PERSISTENCE_IPERSISTENCE_READER_HPP +#define FORM_PERSISTENCE_IPERSISTENCE_READER_HPP + +#include +#include +#include +#include + +namespace form::experimental::config { + class output_item_config; + struct tech_setting_config; +} + +namespace form::detail::experimental { + + class IPersistenceReader { + public: + IPersistenceReader() {}; + virtual ~IPersistenceReader() = default; + + virtual void configureTechSettings( + form::experimental::config::tech_setting_config const& tech_config_settings) = 0; + + virtual void configureOutputItems( + form::experimental::config::output_item_config const& outputItems) = 0; + + virtual void read(std::string const& creator, + std::string const& label, + std::string const& id, + void const** data, + std::type_info const& type) = 0; + }; + + std::unique_ptr createPersistenceReader(); + +} // namespace form::detail::experimental + +#endif // FORM_PERSISTENCE_IPERSISTENCE_READER_HPP diff --git a/form/persistence/ipersistence.hpp b/form/persistence/ipersistence_writer.hpp similarity index 67% rename from form/persistence/ipersistence.hpp rename to form/persistence/ipersistence_writer.hpp index 9405a70c..1836cc1a 100644 --- a/form/persistence/ipersistence.hpp +++ b/form/persistence/ipersistence_writer.hpp @@ -1,7 +1,7 @@ // Copyright (C) 2025 ... -#ifndef FORM_PERSISTENCE_IPERSISTENCE_HPP -#define FORM_PERSISTENCE_IPERSISTENCE_HPP +#ifndef FORM_PERSISTENCE_IPERSISTENCE_WRITER_HPP +#define FORM_PERSISTENCE_IPERSISTENCE_WRITER_HPP #include #include @@ -15,10 +15,10 @@ namespace form::experimental::config { namespace form::detail::experimental { - class IPersistence { + class IPersistenceWriter { public: - IPersistence() {}; - virtual ~IPersistence() = default; + IPersistenceWriter() {}; + virtual ~IPersistenceWriter() = default; virtual void configureTechSettings( form::experimental::config::tech_setting_config const& tech_config_settings) = 0; @@ -33,16 +33,10 @@ namespace form::detail::experimental { void const* data, std::type_info const& type) = 0; virtual void commitOutput(std::string const& creator, std::string const& id) = 0; - - virtual void read(std::string const& creator, - std::string const& label, - std::string const& id, - void const** data, - std::type_info const& type) = 0; }; - std::unique_ptr createPersistence(); + std::unique_ptr createPersistenceWriter(); } // namespace form::detail::experimental -#endif // FORM_PERSISTENCE_IPERSISTENCE_HPP +#endif // FORM_PERSISTENCE_IPERSISTENCE_WRITER_HPP diff --git a/form/persistence/persistence.cpp b/form/persistence/persistence.cpp deleted file mode 100644 index 9c52798a..00000000 --- a/form/persistence/persistence.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (C) 2025 ... - -#include "persistence.hpp" - -#include -#include -#include -#include -#include -#include - -using namespace form::detail::experimental; - -// Factory function implementation -namespace form::detail::experimental { - std::unique_ptr createPersistence() { return std::make_unique(); } -} // namespace form::detail::experimental - -Persistence::Persistence() : - m_store(createStorage()), m_output_items(), m_tech_settings() // constructor takes form config -{ -} - -void Persistence::configureTechSettings( - form::experimental::config::tech_setting_config const& tech_config_settings) -{ - m_tech_settings = tech_config_settings; -} - -void Persistence::configureOutputItems( - form::experimental::config::output_item_config const& output_items) -{ - m_output_items = output_items; -} - -void Persistence::createContainers(std::string const& creator, - std::map const& products) -{ - std::map, std::type_info const*> containers; - for (auto const& [label, type] : products) { - containers.insert(std::make_pair(getPlacement(creator, label), type)); - } - containers.insert(std::make_pair(getPlacement(creator, "index"), &typeid(std::string))); - m_store->createContainers(containers, m_tech_settings); - return; -} - -void Persistence::registerWrite(std::string const& creator, - std::string const& label, - void const* data, - std::type_info const& type) -{ - std::unique_ptr plcmnt = getPlacement(creator, label); - m_store->fillContainer(*plcmnt, data, type); - return; -} - -void Persistence::commitOutput(std::string const& creator, std::string const& id) -{ - std::unique_ptr plcmnt = getPlacement(creator, "index"); - m_store->fillContainer(*plcmnt, &id, typeid(std::string)); - m_store->commitContainers(*plcmnt); - return; -} - -void Persistence::read(std::string const& creator, - std::string const& label, - std::string const& id, - void const** data, - std::type_info const& type) -{ - std::unique_ptr token = getToken(creator, label, id); - m_store->readContainer(*token, data, type, m_tech_settings); - return; -} - -form::experimental::config::PersistenceItem const* Persistence::findConfigItem( - std::string const& label) const -{ - auto const& items = m_output_items.getItems(); - if (label == "index") - return (items.empty()) - ? nullptr - : &(*items - .begin()); //emulate how FORM did this before Phlex PR #22. Will be fixed in a future FORM update. - - auto it = std::find_if( - items.begin(), items.end(), [&label](auto const& item) { return item.product_name == label; }); - - return (it != items.end()) ? &(*it) : nullptr; -} - -std::string Persistence::buildFullLabel(std::string_view creator, std::string_view label) const -{ - std::string result; - result.reserve(creator.size() + 1 + label.size()); - result += creator; - result += '/'; - result += label; - return result; -} - -std::unique_ptr Persistence::getPlacement(std::string const& creator, - std::string const& label) -{ - auto const* config_item = findConfigItem(label); - - if (!config_item) { - throw std::runtime_error("No configuration found for product: " + label + - " from creator: " + creator); - } - - std::string const full_label = buildFullLabel(creator, label); - return std::make_unique(config_item->file_name, full_label, config_item->technology); -} - -std::unique_ptr Persistence::getToken(std::string const& creator, - std::string const& label, - std::string const& id) -{ - auto const* config_item = findConfigItem(label); - - if (!config_item) { - throw std::runtime_error("No configuration found for product: " + label + - " from creator: " + creator); - } - - std::string const full_label = buildFullLabel(creator, label); - std::string const index_label = buildFullLabel(creator, "index"); - - int const rowId = m_store->getIndex( - Token{config_item->file_name, index_label, config_item->technology}, id, m_tech_settings); - return std::make_unique( - config_item->file_name, full_label, config_item->technology, rowId); -} diff --git a/form/persistence/persistence_reader.cpp b/form/persistence/persistence_reader.cpp new file mode 100644 index 00000000..b510592c --- /dev/null +++ b/form/persistence/persistence_reader.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 2025 ... + +#include "persistence_reader.hpp" +#include "persistence_utils.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace form::detail::experimental; + +namespace form::detail::experimental { + std::unique_ptr createPersistenceReader() + { + return std::make_unique(); + } +} + +PersistenceReader::PersistenceReader() : + m_store(createStorageReader()), + m_output_items(), + m_tech_settings() // constructor takes form config +{ +} + +void PersistenceReader::configureTechSettings( + form::experimental::config::tech_setting_config const& tech_config_settings) +{ + m_tech_settings = tech_config_settings; +} + +void PersistenceReader::configureOutputItems( + form::experimental::config::output_item_config const& output_items) +{ + m_output_items = output_items; +} + +void PersistenceReader::read(std::string const& creator, + std::string const& label, + std::string const& id, + void const** data, + std::type_info const& type) +{ + std::unique_ptr token = getToken(creator, label, id); + m_store->readContainer(*token, data, type, m_tech_settings); + return; +} + +std::unique_ptr PersistenceReader::getToken(std::string const& creator, + std::string const& label, + std::string const& id) +{ + auto const config_item = findConfigItem(m_output_items, label); + + if (!config_item) { + throw std::runtime_error("No configuration found for product: " + label + + " from creator: " + creator); + } + + std::string const full_label = buildFullLabel(creator, label); + std::string const index_label = buildFullLabel(creator, "index"); + + int const rowId = m_store->getIndex( + Token{config_item->file_name, index_label, config_item->technology}, id, m_tech_settings); + return std::make_unique( + config_item->file_name, full_label, config_item->technology, rowId); +} diff --git a/form/persistence/persistence_reader.hpp b/form/persistence/persistence_reader.hpp new file mode 100644 index 00000000..c018b364 --- /dev/null +++ b/form/persistence/persistence_reader.hpp @@ -0,0 +1,52 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_PERSISTENCE_PERSISTENCE_READER_HPP +#define FORM_PERSISTENCE_PERSISTENCE_READER_HPP + +#include "ipersistence_reader.hpp" + +#include "core/token.hpp" +#include "storage/istorage.hpp" + +#include +#include +#include + +// forward declaration for form config +namespace form::experimental::config { + class output_item_config; + struct tech_setting_config; +} + +namespace form::detail::experimental { + + class PersistenceReader : public IPersistenceReader { + public: + PersistenceReader(); + ~PersistenceReader() = default; + void configureTechSettings( + form::experimental::config::tech_setting_config const& tech_config_settings) override; + + void configureOutputItems( + form::experimental::config::output_item_config const& output_items) override; + + void read(std::string const& creator, + std::string const& label, + std::string const& id, + void const** data, + std::type_info const& type) override; + + private: + std::unique_ptr getToken(std::string const& creator, + std::string const& label, + std::string const& id); + + private: + std::unique_ptr m_store; + form::experimental::config::output_item_config m_output_items; + form::experimental::config::tech_setting_config m_tech_settings; + }; + +} // namespace form::detail::experimental + +#endif // FORM_PERSISTENCE_PERSISTENCE_READER_HPP diff --git a/form/persistence/persistence_utils.cpp b/form/persistence/persistence_utils.cpp new file mode 100644 index 00000000..9ce0c115 --- /dev/null +++ b/form/persistence/persistence_utils.cpp @@ -0,0 +1,28 @@ +#include "persistence_utils.hpp" +#include "form/config.hpp" + +namespace form::detail::experimental { + std::optional findConfigItem( + form::experimental::config::output_item_config const& config, std::string const& label) + { + auto const& items = config.getItems(); + if (label == "index") + return (items.empty()) + ? std::nullopt + : std::make_optional( + *items + .begin()); //emulate how FORM did this before Phlex PR #22. Will be fixed in a future FORM update. + + return config.findItem(label); + } + + std::string buildFullLabel(std::string_view creator, std::string_view label) + { + std::string result; + result.reserve(creator.size() + 1 + label.size()); + result += creator; + result += '/'; + result += label; + return result; + } +} diff --git a/form/persistence/persistence_utils.hpp b/form/persistence/persistence_utils.hpp new file mode 100644 index 00000000..c7c6bb32 --- /dev/null +++ b/form/persistence/persistence_utils.hpp @@ -0,0 +1,22 @@ +#ifndef FORM_PERSISTENCE_PERSISTENCE_UTILS_HPP +#define FORM_PERSISTENCE_PERSISTENCE_UTILS_HPP + +#include "form/config.hpp" + +#include + +namespace form { + namespace experimental::config { + class output_config; + } + + namespace detail::experimental { + //findConfigItem() is here and not a member of output_config because of the way it handles the label "index". Different hypothetical Persistence implementations may want to handle "index" different ways. + std::optional findConfigItem( + form::experimental::config::output_item_config const& config, std::string const& label); + + std::string buildFullLabel(std::string_view creator, std::string_view label); + } +} + +#endif // FORM_PERSISTENCE_PERSISTENCE_UTILS_HPP diff --git a/form/persistence/persistence_writer.cpp b/form/persistence/persistence_writer.cpp new file mode 100644 index 00000000..4b859744 --- /dev/null +++ b/form/persistence/persistence_writer.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2025 ... + +#include "persistence_writer.hpp" +#include "persistence_utils.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace form::detail::experimental; + +namespace form::detail::experimental { + std::unique_ptr createPersistenceWriter() + { + return std::make_unique(); + } +} + +PersistenceWriter::PersistenceWriter() : + m_store(createStorageWriter()), + m_output_items(), + m_tech_settings() // constructor takes form config +{ +} + +void PersistenceWriter::configureTechSettings( + form::experimental::config::tech_setting_config const& tech_config_settings) +{ + m_tech_settings = tech_config_settings; +} + +void PersistenceWriter::configureOutputItems( + form::experimental::config::output_item_config const& output_items) +{ + m_output_items = output_items; +} + +void PersistenceWriter::createContainers( + std::string const& creator, std::map const& products) +{ + std::map, std::type_info const*> containers; + for (auto const& [label, type] : products) { + containers.insert(std::make_pair(getPlacement(creator, label), type)); + } + containers.insert(std::make_pair(getPlacement(creator, "index"), &typeid(std::string))); + m_store->createContainers(containers, m_tech_settings); + return; +} + +void PersistenceWriter::registerWrite(std::string const& creator, + std::string const& label, + void const* data, + std::type_info const& type) +{ + std::unique_ptr plcmnt = getPlacement(creator, label); + m_store->fillContainer(*plcmnt, data, type); + return; +} + +void PersistenceWriter::commitOutput(std::string const& creator, std::string const& id) +{ + std::unique_ptr plcmnt = getPlacement(creator, "index"); + m_store->fillContainer(*plcmnt, &id, typeid(std::string)); + m_store->commitContainers(*plcmnt); + return; +} + +std::unique_ptr PersistenceWriter::getPlacement(std::string const& creator, + std::string const& label) +{ + auto const config_item = findConfigItem(m_output_items, label); + + if (!config_item) { + throw std::runtime_error("No configuration found for product: " + label + + " from creator: " + creator); + } + + std::string const full_label = buildFullLabel(creator, label); + return std::make_unique(config_item->file_name, full_label, config_item->technology); +} diff --git a/form/persistence/persistence.hpp b/form/persistence/persistence_writer.hpp similarity index 59% rename from form/persistence/persistence.hpp rename to form/persistence/persistence_writer.hpp index 5710dec0..7caee8aa 100644 --- a/form/persistence/persistence.hpp +++ b/form/persistence/persistence_writer.hpp @@ -1,12 +1,11 @@ // Copyright (C) 2025 ... -#ifndef FORM_PERSISTENCE_PERSISTENCE_HPP -#define FORM_PERSISTENCE_PERSISTENCE_HPP +#ifndef FORM_PERSISTENCE_PERSISTENCE_WRITER_HPP +#define FORM_PERSISTENCE_PERSISTENCE_WRITER_HPP -#include "ipersistence.hpp" +#include "ipersistence_writer.hpp" #include "core/placement.hpp" -#include "core/token.hpp" #include "storage/istorage.hpp" #include @@ -21,10 +20,10 @@ namespace form::experimental::config { namespace form::detail::experimental { - class Persistence : public IPersistence { + class PersistenceWriter : public IPersistenceWriter { public: - Persistence(); - ~Persistence() = default; + PersistenceWriter(); + ~PersistenceWriter() = default; void configureTechSettings( form::experimental::config::tech_setting_config const& tech_config_settings) override; @@ -39,28 +38,15 @@ namespace form::detail::experimental { std::type_info const& type) override; void commitOutput(std::string const& creator, std::string const& id) override; - void read(std::string const& creator, - std::string const& label, - std::string const& id, - void const** data, - std::type_info const& type) override; - private: std::unique_ptr getPlacement(std::string const& creator, std::string const& label); - std::unique_ptr getToken(std::string const& creator, - std::string const& label, - std::string const& id); - - form::experimental::config::PersistenceItem const* findConfigItem( - std::string const& label) const; - std::string buildFullLabel(std::string_view creator, std::string_view label) const; private: - std::unique_ptr m_store; + std::unique_ptr m_store; form::experimental::config::output_item_config m_output_items; form::experimental::config::tech_setting_config m_tech_settings; }; } // namespace form::detail::experimental -#endif // FORM_PERSISTENCE_PERSISTENCE_HPP +#endif // FORM_PERSISTENCE_PERSISTENCE_WRITER_HPP diff --git a/form/root_storage/CMakeLists.txt b/form/root_storage/CMakeLists.txt index c8e895f0..0b4e0e9c 100644 --- a/form/root_storage/CMakeLists.txt +++ b/form/root_storage/CMakeLists.txt @@ -4,7 +4,15 @@ find_package(ROOT REQUIRED COMPONENTS Core RIO Tree) # Component(s) in the package: -add_library(root_storage root_tfile.cpp root_ttree_container.cpp root_tbranch_container.cpp) +add_library( + root_storage + root_tfile.cpp + root_ttree_read_container.cpp + root_ttree_write_container.cpp + root_tbranch_read_container.cpp + root_tbranch_write_container.cpp + demangle_name.cpp +) target_compile_definitions(root_storage PUBLIC USE_ROOT_STORAGE) # Link the ROOT libraries diff --git a/form/root_storage/demangle_name.cpp b/form/root_storage/demangle_name.cpp new file mode 100644 index 00000000..cad19382 --- /dev/null +++ b/form/root_storage/demangle_name.cpp @@ -0,0 +1,18 @@ +#include "TClassEdit.h" + +namespace form::detail::experimental { + // Return the demangled type name + std::string DemangleName(std::type_info const& type) + { + int errorCode; + // The TClassEdit version works on both linux and Windows. + char* demangledName = TClassEdit::DemangleTypeIdName(type, errorCode); + if (errorCode != 0) { + // NOTE: Instead of throwing, we could return the mangled name as a fallback. + throw std::runtime_error("Failed to demangle type name"); + } + std::string result(demangledName); + std::free(demangledName); + return result; + } +} diff --git a/form/root_storage/demangle_name.hpp b/form/root_storage/demangle_name.hpp new file mode 100644 index 00000000..038f7a79 --- /dev/null +++ b/form/root_storage/demangle_name.hpp @@ -0,0 +1,7 @@ +#include +#include + +namespace form::detail::experimental { + // Return the demangled type name + std::string DemangleName(std::type_info const& type); +} diff --git a/form/root_storage/root_tbranch_container.cpp b/form/root_storage/root_tbranch_container.cpp deleted file mode 100644 index 9c0c6f69..00000000 --- a/form/root_storage/root_tbranch_container.cpp +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (C) 2025 ... - -#include "root_tbranch_container.hpp" -#include "root_tfile.hpp" -#include "root_ttree_container.hpp" - -#include "TBranch.h" -#include "TClassEdit.h" -#include "TFile.h" -#include "TLeaf.h" -#include "TTree.h" - -#include - -namespace { - // Return the demangled type name - std::string DemangleName(std::type_info const& type) - { - int errorCode; - // The TClassEdit version works on both linux and Windows. - char* demangledName = TClassEdit::DemangleTypeIdName(type, errorCode); - if (errorCode != 0) { - // NOTE: Instead of throwing, we could return the mangled name as a fallback. - throw std::runtime_error("Failed to demangle type name"); - } - std::string result(demangledName); - std::free(demangledName); - return result; - } - //Type name conversion based on https://root.cern.ch/doc/master/classTTree.html#ac1fa9466ce018d4aa739b357f981c615 - //An empty leaf list defaults to Float_t - std::unordered_map typeNameToLeafList = {{"int", "/I"}, - {"unsigned int", "/i"}, - {"float", "/F"}, - {"double", "/D"}, - {"short int", "/S"}, - {"unsigned short", "/s"}, - {"long int", "/L"}, - {"unsigned long int", "/l"}, - {"bool", "/O"}}; -} - -using namespace form::detail::experimental; - -ROOT_TBranch_ContainerImp::ROOT_TBranch_ContainerImp(std::string const& name) : - Storage_Associative_Container(name), m_tfile(nullptr), m_tree(nullptr), m_branch(nullptr) -{ -} - -void ROOT_TBranch_ContainerImp::setAttribute(std::string const& key, std::string const& value) -{ - if (key == "auto_flush") { - m_tree->SetAutoFlush(std::stol(value)); - } else { - throw std::runtime_error("ROOT_TTree_ContainerImp accepts some attributes, but not " + key); - } -} - -void ROOT_TBranch_ContainerImp::setFile(std::shared_ptr file) -{ - this->Storage_Associative_Container::setFile(file); - ROOT_TFileImp* root_tfile_imp = dynamic_cast(file.get()); - if (root_tfile_imp == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::setFile can't attach to non-ROOT file"); - } - m_tfile = root_tfile_imp->getTFile(); - return; -} - -void ROOT_TBranch_ContainerImp::setParent(std::shared_ptr parent) -{ - this->Storage_Associative_Container::setParent(parent); - ROOT_TTree_ContainerImp* root_ttree_imp = dynamic_cast(parent.get()); - if (root_ttree_imp == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::setParent"); - } - m_tree = root_ttree_imp->getTTree(); - return; -} - -void ROOT_TBranch_ContainerImp::setupWrite(std::type_info const& type) -{ - if (m_tree == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::setupWrite no tree found"); - } - - auto dictInfo = TDictionary::GetDictionary(type); - if (m_branch == nullptr) { - if (!dictInfo) { - throw std::runtime_error( - std::string{"ROOT_TBranch_ContainerImp::setupWrite unsupported type: "} + - DemangleName(type)); - } - if (dictInfo->Property() & EProperty::kIsFundamental) { - m_branch = m_tree->Branch(col_name().c_str(), - static_cast(nullptr), - (col_name() + typeNameToLeafList[dictInfo->GetName()]).c_str(), - 4096); - } else { - m_branch = - m_tree->Branch(col_name().c_str(), dictInfo->GetName(), static_cast(nullptr)); - } - } - if (m_branch == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::setupWrite no branch created"); - } - return; -} - -void ROOT_TBranch_ContainerImp::fill(void const* data) -{ - if (m_branch == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::fill no branch found"); - } - TLeaf* leaf = m_branch->GetLeaf(col_name().c_str()); - if (leaf != nullptr && - TDictionary::GetDictionary(leaf->GetTypeName())->Property() & EProperty::kIsFundamental) { - m_branch->SetAddress(const_cast(data)); //FIXME: const_cast? - } else { - m_branch->SetAddress(&data); - } - m_branch->Fill(); - m_branch->ResetAddress(); - return; -} - -void ROOT_TBranch_ContainerImp::commit() -{ - // Forward the tree - if (!m_tree) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::commit no tree attached"); - } - m_tree->SetEntries(m_branch->GetEntries()); - return; -} - -bool ROOT_TBranch_ContainerImp::read(int id, void const** data, std::type_info const& type) -{ - if (m_tfile == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::read no file attached"); - } - if (m_tree == nullptr) { - m_tree = m_tfile->Get(top_name().c_str()); - } - if (m_tree == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::read no tree found"); - } - if (m_branch == nullptr) { - m_branch = m_tree->GetBranch(col_name().c_str()); - } - if (m_branch == nullptr) { - throw std::runtime_error("ROOT_TBranch_ContainerImp::read no branch found"); - } - if (id > m_tree->GetEntries()) - return false; - - void* branchBuffer = nullptr; - auto dictInfo = TDictionary::GetDictionary(type); - int branchStatus = 0; - if (!dictInfo) { - throw std::runtime_error(std::string{"ROOT_TBranch_ContainerImp::read unsupported type: "} + - DemangleName(type)); - } - - if (dictInfo->Property() & EProperty::kIsFundamental) { - auto fundInfo = static_cast(dictInfo); - branchBuffer = new char[fundInfo->Size()]; - branchStatus = m_tree->SetBranchAddress( - col_name().c_str(), &branchBuffer, nullptr, EDataType(fundInfo->GetType()), true); - } else { - auto klass = TClass::GetClass(type); - if (!klass) { - throw std::runtime_error(std::string{"ROOT_TBranch_ContainerImp::read missing TClass"} + - " (col_name='" + col_name() + "', type='" + DemangleName(type) + - "')"); - } - branchBuffer = klass->New(); - branchStatus = - m_tree->SetBranchAddress(col_name().c_str(), &branchBuffer, klass, EDataType::kOther_t, true); - } - - if (branchStatus < 0) { - throw std::runtime_error( - std::string{"ROOT_TBranch_ContainerImp::read SetBranchAddress() failed"} + " (col_name='" + - col_name() + "', type='" + DemangleName(type) + "')" + " with error code " + - std::to_string(branchStatus)); - } - - Long64_t tentry = m_tree->LoadTree(id); - m_branch->GetEntry(tentry); - *data = branchBuffer; - return true; -} diff --git a/form/root_storage/root_tbranch_read_container.cpp b/form/root_storage/root_tbranch_read_container.cpp new file mode 100644 index 00000000..d9680d78 --- /dev/null +++ b/form/root_storage/root_tbranch_read_container.cpp @@ -0,0 +1,104 @@ +// Copyright (C) 2025 ... + +#include "root_tbranch_read_container.hpp" +#include "demangle_name.hpp" +#include "root_tfile.hpp" +#include "root_ttree_read_container.hpp" + +#include "TBranch.h" +#include "TFile.h" +#include "TLeaf.h" +#include "TTree.h" + +#include + +using namespace form::detail::experimental; + +ROOT_TBranch_Read_ContainerImp::ROOT_TBranch_Read_ContainerImp(std::string const& name) : + Storage_Associative_Read_Container(name), m_tfile(nullptr), m_tree(nullptr), m_branch(nullptr) +{ +} + +void ROOT_TBranch_Read_ContainerImp::setFile(std::shared_ptr file) +{ + this->Storage_Associative_Read_Container::setFile(file); + ROOT_TFileImp* root_tfile_imp = dynamic_cast(file.get()); + if (root_tfile_imp == nullptr) { + throw std::runtime_error( + "ROOT_TBranch_Read_ContainerImp::setFile can't attach to non-ROOT file"); + } + m_tfile = root_tfile_imp->getTFile(); + return; +} + +void ROOT_TBranch_Read_ContainerImp::setParent(std::shared_ptr parent) +{ + this->Storage_Associative_Read_Container::setParent(parent); + ROOT_TTree_Read_ContainerImp* root_ttree_imp = + dynamic_cast(parent.get()); + if (root_ttree_imp == nullptr) { + throw std::runtime_error("ROOT_TBranch_Read_ContainerImp::setParent"); + } + m_tree = root_ttree_imp->getTTree(); + return; +} + +bool ROOT_TBranch_Read_ContainerImp::read(int id, void const** data, std::type_info const& type) +{ + if (m_tfile == nullptr) { + throw std::runtime_error("ROOT_TBranch_Read_ContainerImp::read no file attached"); + } + if (m_tree == nullptr) { + m_tree = m_tfile->Get(top_name().c_str()); + } + if (m_tree == nullptr) { + throw std::runtime_error("ROOT_TBranch_Read_ContainerImp::read no tree found"); + } + if (m_branch == nullptr) { + m_branch = m_tree->GetBranch(col_name().c_str()); + } + if (m_branch == nullptr) { + throw std::runtime_error("ROOT_TBranch_Read_ContainerImp::read no branch found"); + } + if (id > m_tree->GetEntries()) + return false; + + void* branchBuffer = nullptr; + auto dictInfo = TDictionary::GetDictionary(type); + int branchStatus = 0; + + if (!dictInfo) { + throw std::runtime_error(std::string{"ROOT_TBranch_ContainerImp::read unsupported type: "} + + DemangleName(type)); + } + + if (dictInfo->Property() & EProperty::kIsFundamental) { + //Assume this is a fundamental type like int or double + auto fundInfo = static_cast(TDictionary::GetDictionary(type)); + branchBuffer = new char[fundInfo->Size()]; + branchStatus = m_tree->SetBranchAddress( + col_name().c_str(), &branchBuffer, nullptr, EDataType(fundInfo->GetType()), true); + } else { + auto klass = TClass::GetClass(type); + if (!klass) { + throw std::runtime_error(std::string{"ROOT_TBranch_ContainerImp::read missing TClass"} + + " (col_name='" + col_name() + "', type='" + DemangleName(type) + + "')"); + } + branchBuffer = klass->New(); + branchStatus = + m_tree->SetBranchAddress(col_name().c_str(), &branchBuffer, klass, EDataType::kOther_t, true); + } + + if (branchStatus < 0) { + throw std::runtime_error( + std::string{"ROOT_TBranch_ContainerImp::read SetBranchAddress() failed"} + " (col_name='" + + col_name() + "', type='" + DemangleName(type) + "')" + " with error code " + + std::to_string(branchStatus)); + } + + Long64_t tentry = m_tree->LoadTree(id); + m_branch->GetEntry(tentry); + *data = branchBuffer; + return true; +} diff --git a/form/root_storage/root_tbranch_read_container.hpp b/form/root_storage/root_tbranch_read_container.hpp new file mode 100644 index 00000000..c2abed05 --- /dev/null +++ b/form/root_storage/root_tbranch_read_container.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_ROOT_STORAGE_ROOT_TBRANCH_READ_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_TBRANCH_READ_CONTAINER_HPP + +#include "storage/storage_associative_read_container.hpp" + +#include +#include + +class TFile; +class TTree; +class TBranch; + +namespace form::detail::experimental { + + class ROOT_TBranch_Read_ContainerImp : public Storage_Associative_Read_Container { + public: + ROOT_TBranch_Read_ContainerImp(std::string const& name); + ~ROOT_TBranch_Read_ContainerImp() = default; + + void setFile(std::shared_ptr file) override; + void setParent(std::shared_ptr parent) override; + + bool read(int id, void const** data, std::type_info const& type) override; + + private: + std::shared_ptr m_tfile; + TTree* m_tree; + TBranch* m_branch; + }; + +} // namespace form::detail::experimental + +#endif // FORM_ROOT_STORAGE_ROOT_TBRANCH_READ_CONTAINER_HPP diff --git a/form/root_storage/root_tbranch_write_container.cpp b/form/root_storage/root_tbranch_write_container.cpp new file mode 100644 index 00000000..4a0132b0 --- /dev/null +++ b/form/root_storage/root_tbranch_write_container.cpp @@ -0,0 +1,123 @@ +// Copyright (C) 2025 ... + +#include "root_tbranch_write_container.hpp" +#include "demangle_name.hpp" +#include "root_tfile.hpp" +#include "root_ttree_write_container.hpp" + +#include "TBranch.h" +#include "TFile.h" +#include "TLeaf.h" +#include "TTree.h" + +#include + +namespace { + //Type name conversion based on https://root.cern.ch/doc/master/classTTree.html#ac1fa9466ce018d4aa739b357f981c615 + //An empty leaf list defaults to Float_t + std::unordered_map typeNameToLeafList = {{"int", "/I"}, + {"unsigned int", "/i"}, + {"float", "/F"}, + {"double", "/D"}, + {"short int", "/S"}, + {"unsigned short", "/s"}, + {"long int", "/L"}, + {"unsigned long int", "/l"}, + {"bool", "/O"}}; +} + +using namespace form::detail::experimental; + +ROOT_TBranch_Write_ContainerImp::ROOT_TBranch_Write_ContainerImp(std::string const& name) : + Storage_Associative_Write_Container(name), m_tfile(nullptr), m_tree(nullptr), m_branch(nullptr) +{ +} + +void ROOT_TBranch_Write_ContainerImp::setAttribute(std::string const& key, std::string const& value) +{ + if (key == "auto_flush") { + m_tree->SetAutoFlush(std::stol(value)); + } else { + throw std::runtime_error("ROOT_TTree_Write_ContainerImp accepts some attributes, but not " + + key); + } +} + +void ROOT_TBranch_Write_ContainerImp::setFile(std::shared_ptr file) +{ + this->Storage_Associative_Write_Container::setFile(file); + ROOT_TFileImp* root_tfile_imp = dynamic_cast(file.get()); + if (root_tfile_imp == nullptr) { + throw std::runtime_error( + "ROOT_TBranch_Write_ContainerImp::setFile can't attach to non-ROOT file"); + } + m_tfile = root_tfile_imp->getTFile(); + return; +} + +void ROOT_TBranch_Write_ContainerImp::setParent(std::shared_ptr parent) +{ + this->Storage_Associative_Write_Container::setParent(parent); + ROOT_TTree_Write_ContainerImp* root_ttree_imp = + dynamic_cast(parent.get()); + if (root_ttree_imp == nullptr) { + throw std::runtime_error("ROOT_TBranch_Write_ContainerImp::setParent"); + } + m_tree = root_ttree_imp->getTTree(); + return; +} + +void ROOT_TBranch_Write_ContainerImp::setupWrite(std::type_info const& type) +{ + if (m_tree == nullptr) { + throw std::runtime_error("ROOT_TBranch_Write_ContainerImp::setupWrite no tree found"); + } + + auto dictInfo = TDictionary::GetDictionary(type); + if (m_branch == nullptr) { + if (!dictInfo) { + throw std::runtime_error("ROOT_TBranch_Write_ContainerImp::setupWrite unsupported type: " + + DemangleName(type)); + } + if (dictInfo->Property() & EProperty::kIsFundamental) { + m_branch = m_tree->Branch(col_name().c_str(), + static_cast(nullptr), + (col_name() + typeNameToLeafList[dictInfo->GetName()]).c_str(), + 4096); + } else { + m_branch = + m_tree->Branch(col_name().c_str(), dictInfo->GetName(), static_cast(nullptr)); + } + } + if (m_branch == nullptr) { + throw std::runtime_error("ROOT_TBranch_Write_ContainerImp::setupWrite no branch created"); + } + return; +} + +void ROOT_TBranch_Write_ContainerImp::fill(void const* data) +{ + if (m_branch == nullptr) { + throw std::runtime_error("ROOT_TBranch_Write_ContainerImp::fill no branch found"); + } + TLeaf* leaf = m_branch->GetLeaf(col_name().c_str()); + if (leaf != nullptr && + TDictionary::GetDictionary(leaf->GetTypeName())->Property() & EProperty::kIsFundamental) { + m_branch->SetAddress(const_cast(data)); //FIXME: const_cast? + } else { + m_branch->SetAddress(&data); + } + m_branch->Fill(); + m_branch->ResetAddress(); + return; +} + +void ROOT_TBranch_Write_ContainerImp::commit() +{ + // Forward the tree + if (!m_tree) { + throw std::runtime_error("ROOT_TBranch_Write_ContainerImp::commit no tree attached"); + } + m_tree->SetEntries(m_branch->GetEntries()); + return; +} diff --git a/form/root_storage/root_tbranch_container.hpp b/form/root_storage/root_tbranch_write_container.hpp similarity index 51% rename from form/root_storage/root_tbranch_container.hpp rename to form/root_storage/root_tbranch_write_container.hpp index ae6cc42f..0e2280c1 100644 --- a/form/root_storage/root_tbranch_container.hpp +++ b/form/root_storage/root_tbranch_write_container.hpp @@ -1,13 +1,12 @@ // Copyright (C) 2025 ... -#ifndef FORM_ROOT_STORAGE_ROOT_TBRANCH_CONTAINER_HPP -#define FORM_ROOT_STORAGE_ROOT_TBRANCH_CONTAINER_HPP +#ifndef FORM_ROOT_STORAGE_ROOT_TBRANCH_WRITE_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_TBRANCH_WRITE_CONTAINER_HPP -#include "storage/storage_associative_container.hpp" +#include "storage/storage_associative_write_container.hpp" #include #include -#include class TFile; class TTree; @@ -15,20 +14,19 @@ class TBranch; namespace form::detail::experimental { - class ROOT_TBranch_ContainerImp : public Storage_Associative_Container { + class ROOT_TBranch_Write_ContainerImp : public Storage_Associative_Write_Container { public: - ROOT_TBranch_ContainerImp(std::string const& name); - ~ROOT_TBranch_ContainerImp() = default; + ROOT_TBranch_Write_ContainerImp(std::string const& name); + ~ROOT_TBranch_Write_ContainerImp() = default; void setAttribute(std::string const& key, std::string const& value) override; void setFile(std::shared_ptr file) override; - void setParent(std::shared_ptr parent) override; + void setParent(std::shared_ptr parent) override; void setupWrite(std::type_info const& type = typeid(void)) override; void fill(void const* data) override; void commit() override; - bool read(int id, void const** data, std::type_info const& type) override; private: std::shared_ptr m_tfile; @@ -38,4 +36,4 @@ namespace form::detail::experimental { } // namespace form::detail::experimental -#endif // FORM_ROOT_STORAGE_ROOT_TBRANCH_CONTAINER_HPP +#endif // FORM_ROOT_STORAGE_ROOT_TBRANCH_WRITE_CONTAINER_HPP diff --git a/form/root_storage/root_ttree_container.cpp b/form/root_storage/root_ttree_container.cpp deleted file mode 100644 index adc529ba..00000000 --- a/form/root_storage/root_ttree_container.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2025 ... - -#include "root_ttree_container.hpp" -#include "root_tfile.hpp" - -#include "TFile.h" -#include "TTree.h" - -using namespace form::detail::experimental; - -ROOT_TTree_ContainerImp::ROOT_TTree_ContainerImp(std::string const& name) : - Storage_Association(name), m_tfile(nullptr), m_tree(nullptr) -{ -} - -ROOT_TTree_ContainerImp::~ROOT_TTree_ContainerImp() -{ - if (m_tree != nullptr) { - // Calling: - // m_tree->Write(); - // requires the TTree's directory to be the current directory, so we could do - // TDirectory::TContext ctxt(m_tree->GetDirectory()); - // m_tree->Write(); - // or let's just do: - m_tree->AutoSave("flushbaskets"); - delete m_tree; - } -} - -void ROOT_TTree_ContainerImp::setFile(std::shared_ptr file) -{ - this->Storage_Association::setFile(file); - ROOT_TFileImp* root_tfile_imp = dynamic_cast(file.get()); - if (root_tfile_imp == nullptr) { - throw std::runtime_error("ROOT_TTree_ContainerImp::setFile can't attach to non-ROOT file"); - } - m_tfile = dynamic_cast(file.get())->getTFile(); - return; -} - -void ROOT_TTree_ContainerImp::setupWrite(std::type_info const& /*type*/) -{ - if (m_tfile == nullptr) { - throw std::runtime_error("ROOT_TTree_ContainerImp::setupWrite no file attached"); - } - if (m_tree == nullptr) { - m_tree = m_tfile->Get(name().c_str()); - } - if (m_tree == nullptr) { - m_tree = new TTree(name().c_str(), name().c_str()); - m_tree->SetDirectory(m_tfile.get()); - } - if (m_tree == nullptr) { - throw std::runtime_error("ROOT_TTree_ContainerImp::setupWrite no tree created"); - } - if (m_tree->GetDirectory() == nullptr) { - throw std::runtime_error("ROOT_TTree_ContainerImp::setupWrite not attached to any file"); - } - return; -} - -void ROOT_TTree_ContainerImp::fill(void const* /* data*/) -{ - throw std::runtime_error("ROOT_TTree_ContainerImp::fill not implemented"); -} - -void ROOT_TTree_ContainerImp::commit() -{ - throw std::runtime_error("ROOT_TTree_ContainerImp::commit not implemented"); -} - -bool ROOT_TTree_ContainerImp::read(int /* id*/, - void const** /* data*/, - std::type_info const& /* type*/) -{ - throw std::runtime_error("ROOT_TTree_ContainerImp::read not implemented"); -} - -TTree* ROOT_TTree_ContainerImp::getTTree() { return m_tree; } diff --git a/form/root_storage/root_ttree_container.hpp b/form/root_storage/root_ttree_container.hpp deleted file mode 100644 index 0711ce54..00000000 --- a/form/root_storage/root_ttree_container.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_ROOT_STORAGE_ROOT_TTREE_CONTAINER_HPP -#define FORM_ROOT_STORAGE_ROOT_TTREE_CONTAINER_HPP - -#include "storage/storage_association.hpp" - -#include -#include -#include - -class TFile; -class TTree; - -namespace form::detail::experimental { - - class ROOT_TTree_ContainerImp : public Storage_Association { - public: - ROOT_TTree_ContainerImp(std::string const& name); - ~ROOT_TTree_ContainerImp(); - - ROOT_TTree_ContainerImp(ROOT_TTree_ContainerImp const& other) = delete; - ROOT_TTree_ContainerImp& operator=(ROOT_TTree_ContainerImp& other) = delete; - - void setFile(std::shared_ptr file) override; - void setupWrite(std::type_info const& type = typeid(void)) override; - void fill(void const* data) override; - void commit() override; - bool read(int id, void const** data, std::type_info const& type) override; - - TTree* getTTree(); - - private: - std::shared_ptr m_tfile; - TTree* m_tree; - }; - -} //namespace form::detail::experimental - -#endif // FORM_ROOT_STORAGE_ROOT_TTREE_CONTAINER_HPP diff --git a/form/root_storage/root_ttree_read_container.cpp b/form/root_storage/root_ttree_read_container.cpp new file mode 100644 index 00000000..5be81c03 --- /dev/null +++ b/form/root_storage/root_ttree_read_container.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2025 ... + +#include "root_ttree_read_container.hpp" +#include "root_tfile.hpp" + +#include "TFile.h" +#include "TTree.h" + +using namespace form::detail::experimental; + +ROOT_TTree_Read_ContainerImp::ROOT_TTree_Read_ContainerImp(std::string const& name) : + Storage_Read_Association(name), m_tfile(nullptr), m_tree(nullptr) +{ +} + +ROOT_TTree_Read_ContainerImp::~ROOT_TTree_Read_ContainerImp() {} + +void ROOT_TTree_Read_ContainerImp::setFile(std::shared_ptr file) +{ + this->Storage_Read_Association::setFile(file); + ROOT_TFileImp* root_tfile_imp = dynamic_cast(file.get()); + if (root_tfile_imp == nullptr) { + throw std::runtime_error("ROOT_TTree_Read_ContainerImp::setFile can't attach to non-ROOT file"); + } + m_tfile = dynamic_cast(file.get())->getTFile(); + return; +} + +bool ROOT_TTree_Read_ContainerImp::read(int /* id*/, + void const** /* data*/, + std::type_info const& /* type*/) +{ + throw std::runtime_error("ROOT_TTree_Read_ContainerImp::read not implemented"); +} + +TTree* ROOT_TTree_Read_ContainerImp::getTTree() { return m_tree; } diff --git a/form/root_storage/root_ttree_read_container.hpp b/form/root_storage/root_ttree_read_container.hpp new file mode 100644 index 00000000..6a47c7b5 --- /dev/null +++ b/form/root_storage/root_ttree_read_container.hpp @@ -0,0 +1,36 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_ROOT_STORAGE_ROOT_TTREE_READ_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_TTREE_READ_CONTAINER_HPP + +#include "storage/storage_read_association.hpp" + +#include +#include + +class TFile; +class TTree; + +namespace form::detail::experimental { + + class ROOT_TTree_Read_ContainerImp : public Storage_Read_Association { + public: + ROOT_TTree_Read_ContainerImp(std::string const& name); + ~ROOT_TTree_Read_ContainerImp(); + + ROOT_TTree_Read_ContainerImp(ROOT_TTree_Read_ContainerImp const& other) = delete; + ROOT_TTree_Read_ContainerImp& operator=(ROOT_TTree_Read_ContainerImp& other) = delete; + + void setFile(std::shared_ptr file) override; + bool read(int id, void const** data, std::type_info const& type) override; + + TTree* getTTree(); + + private: + std::shared_ptr m_tfile; + TTree* m_tree; + }; + +} //namespace form::detail::experimental + +#endif // FORM_ROOT_STORAGE_ROOT_TTREE_READ_CONTAINER_HPP diff --git a/form/root_storage/root_ttree_write_container.cpp b/form/root_storage/root_ttree_write_container.cpp new file mode 100644 index 00000000..533aa90c --- /dev/null +++ b/form/root_storage/root_ttree_write_container.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2025 ... + +#include "root_ttree_write_container.hpp" +#include "root_tfile.hpp" + +#include "TFile.h" +#include "TTree.h" + +using namespace form::detail::experimental; + +ROOT_TTree_Write_ContainerImp::ROOT_TTree_Write_ContainerImp(std::string const& name) : + Storage_Write_Association(name), m_tfile(nullptr), m_tree(nullptr) +{ +} + +ROOT_TTree_Write_ContainerImp::~ROOT_TTree_Write_ContainerImp() +{ + if (m_tree != nullptr) { + m_tree->Write(); + delete m_tree; + } +} + +void ROOT_TTree_Write_ContainerImp::setFile(std::shared_ptr file) +{ + this->Storage_Write_Association::setFile(file); + ROOT_TFileImp* root_tfile_imp = dynamic_cast(file.get()); + if (root_tfile_imp == nullptr) { + throw std::runtime_error( + "ROOT_TTree_Write_ContainerImp::setFile can't attach to non-ROOT file"); + } + m_tfile = dynamic_cast(file.get())->getTFile(); + return; +} + +void ROOT_TTree_Write_ContainerImp::setupWrite(std::type_info const& /* type*/) +{ + if (m_tfile == nullptr) { + throw std::runtime_error("ROOT_TTree_Write_ContainerImp::setupWrite no file attached"); + } + if (m_tree == nullptr) { + m_tree = m_tfile->Get(name().c_str()); + } + if (m_tree == nullptr) { + m_tree = new TTree(name().c_str(), name().c_str()); + m_tree->SetDirectory(m_tfile.get()); + } + if (m_tree == nullptr) { + throw std::runtime_error("ROOT_TTree_Write_ContainerImp::setupWrite no tree created"); + } + return; +} + +void ROOT_TTree_Write_ContainerImp::fill(void const* /* data*/) +{ + throw std::runtime_error("ROOT_TTree_Write_ContainerImp::fill not implemented"); +} + +void ROOT_TTree_Write_ContainerImp::commit() +{ + throw std::runtime_error("ROOT_TTree_Write_ContainerImp::commit not implemented"); +} + +TTree* ROOT_TTree_Write_ContainerImp::getTTree() { return m_tree; } diff --git a/form/root_storage/root_ttree_write_container.hpp b/form/root_storage/root_ttree_write_container.hpp new file mode 100644 index 00000000..f9af0465 --- /dev/null +++ b/form/root_storage/root_ttree_write_container.hpp @@ -0,0 +1,38 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_ROOT_STORAGE_ROOT_TTREE_WRITE_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_TTREE_WRITE_CONTAINER_HPP + +#include "storage/storage_write_association.hpp" + +#include +#include + +class TFile; +class TTree; + +namespace form::detail::experimental { + + class ROOT_TTree_Write_ContainerImp : public Storage_Write_Association { + public: + ROOT_TTree_Write_ContainerImp(std::string const& name); + ~ROOT_TTree_Write_ContainerImp(); + + ROOT_TTree_Write_ContainerImp(ROOT_TTree_Write_ContainerImp const& other) = delete; + ROOT_TTree_Write_ContainerImp& operator=(ROOT_TTree_Write_ContainerImp& other) = delete; + + void setFile(std::shared_ptr file) override; + void setupWrite(std::type_info const& type) override; + void fill(void const* data) override; + void commit() override; + + TTree* getTTree(); + + private: + std::shared_ptr m_tfile; + TTree* m_tree; + }; + +} //namespace form::detail::experimental + +#endif // FORM_ROOT_STORAGE_ROOT_TTREE_WRITE_CONTAINER_HPP diff --git a/form/storage/CMakeLists.txt b/form/storage/CMakeLists.txt index 75d60f6f..46960e5a 100644 --- a/form/storage/CMakeLists.txt +++ b/form/storage/CMakeLists.txt @@ -3,11 +3,15 @@ # Component(s) in the package: add_library( storage - storage.cpp + storage_reader.cpp + storage_writer.cpp storage_file.cpp - storage_container.cpp - storage_association.cpp - storage_associative_container.cpp + storage_read_container.cpp + storage_write_container.cpp + storage_read_association.cpp + storage_write_association.cpp + storage_associative_read_container.cpp + storage_associative_write_container.cpp ) if(FORM_USE_ROOT_STORAGE) diff --git a/form/storage/istorage.hpp b/form/storage/istorage.hpp index 4c6da0e2..555d58b2 100644 --- a/form/storage/istorage.hpp +++ b/form/storage/istorage.hpp @@ -10,23 +10,14 @@ #include #include #include -#include #include namespace form::detail::experimental { - class IStorage { + class IStorageReader { public: - IStorage() = default; - virtual ~IStorage() = default; - - virtual void createContainers( - std::map, std::type_info const*> const& containers, - form::experimental::config::tech_setting_config const& settings) = 0; - virtual void fillContainer(Placement const& plcmnt, - void const* data, - std::type_info const& type) = 0; - virtual void commitContainers(Placement const& plcmnt) = 0; + IStorageReader() = default; + virtual ~IStorageReader() = default; virtual int getIndex(Token const& token, std::string const& id, @@ -37,6 +28,20 @@ namespace form::detail::experimental { form::experimental::config::tech_setting_config const& settings) = 0; }; + class IStorageWriter { + public: + IStorageWriter() = default; + virtual ~IStorageWriter() = default; + + virtual void createContainers( + std::map, std::type_info const*> const& containers, + form::experimental::config::tech_setting_config const& settings) = 0; + virtual void fillContainer(Placement const& plcmnt, + void const* data, + std::type_info const& type) = 0; + virtual void commitContainers(Placement const& plcmnt) = 0; + }; + class IStorage_File { public: IStorage_File() = default; @@ -48,10 +53,10 @@ namespace form::detail::experimental { virtual void setAttribute(std::string const& name, std::string const& value) = 0; }; - class IStorage_Container { + class IStorage_Write_Container { public: - IStorage_Container() = default; - virtual ~IStorage_Container() = default; + IStorage_Write_Container() = default; + virtual ~IStorage_Write_Container() = default; virtual std::string const& name() = 0; @@ -59,12 +64,25 @@ namespace form::detail::experimental { virtual void setupWrite(std::type_info const& type = typeid(void)) = 0; virtual void fill(void const* data) = 0; virtual void commit() = 0; + + virtual void setAttribute(std::string const& name, std::string const& value) = 0; + }; + + class IStorage_Read_Container { + public: + IStorage_Read_Container() = default; + virtual ~IStorage_Read_Container() = default; + + virtual std::string const& name() = 0; + + virtual void setFile(std::shared_ptr file) = 0; virtual bool read(int id, void const** data, std::type_info const& type) = 0; virtual void setAttribute(std::string const& name, std::string const& value) = 0; }; - std::unique_ptr createStorage(); + std::unique_ptr createStorageReader(); + std::unique_ptr createStorageWriter(); } // namespace form::detail::experimental diff --git a/form/storage/storage.cpp b/form/storage/storage.cpp deleted file mode 100644 index 5d67ad73..00000000 --- a/form/storage/storage.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (C) 2025 ... - -#include "storage.hpp" -#include "storage_association.hpp" -#include "storage_associative_container.hpp" -#include "storage_file.hpp" - -#include "util/factories.hpp" - -#include -#include -#include - -using namespace form::detail::experimental; - -// Factory function implementation -namespace form::detail::experimental { - std::unique_ptr createStorage() { return std::unique_ptr(new Storage()); } -} - -void Storage::createContainers( - std::map, std::type_info const*> const& containers, - form::experimental::config::tech_setting_config const& settings) -{ - for (auto const& [plcmnt, type] : containers) { - // Use file+container as composite key - auto key = std::make_pair(plcmnt->fileName(), plcmnt->containerName()); - auto cont = m_containers.find(key); - if (cont == m_containers.end()) { - // Ensure the file exists - auto file = m_files.find(plcmnt->fileName()); - if (file == m_files.end()) { - m_files.insert( - {plcmnt->fileName(), createFile(plcmnt->technology(), plcmnt->fileName(), 'o')}); - file = m_files.find(plcmnt->fileName()); - for (auto const& [key, value] : - settings.getFileTable(plcmnt->technology(), plcmnt->fileName())) - file->second->setAttribute(key, value); - } - // Create and bind container to file - auto container = createContainer(plcmnt->technology(), plcmnt->containerName()); - m_containers.insert({key, container}); - // For associative container, create association layer - auto associative_container = - std::dynamic_pointer_cast(container); - if (associative_container) { - auto parent_key = std::make_pair(plcmnt->fileName(), associative_container->top_name()); - auto parent = m_containers.find(parent_key); - if (parent == m_containers.end()) { - auto parent_cont = - createAssociation(plcmnt->technology(), associative_container->top_name()); - m_containers.insert({parent_key, parent_cont}); - parent_cont->setFile(file->second); - parent_cont->setupWrite(); - associative_container->setParent(parent_cont); - } else { - associative_container->setParent(parent->second); - } - } - - for (auto const& [key, value] : - settings.getContainerTable(plcmnt->technology(), plcmnt->containerName())) - container->setAttribute(key, value); - container->setFile(file->second); - if (!type) { - throw std::runtime_error("Storage::createContainers got nullptr type for container: " + - plcmnt->containerName()); - } - container->setupWrite(*type); - } - } - return; -} - -void Storage::fillContainer(Placement const& plcmnt, - void const* data, - std::type_info const& /* type*/) -{ - // Use file+container as composite key - auto key = std::make_pair(plcmnt.fileName(), plcmnt.containerName()); - auto cont = m_containers.find(key); - if (cont == m_containers.end()) { - // FIXME: For now throw an exception here, but in future, we may have storage technology do that. - throw std::runtime_error("Storage::fillContainer Container doesn't exist: " + - plcmnt.containerName()); - } - cont->second->fill(data); - return; -} - -void Storage::commitContainers(Placement const& plcmnt) -{ - auto key = std::make_pair(plcmnt.fileName(), plcmnt.containerName()); - auto cont = m_containers.find(key); - cont->second->commit(); - return; -} - -int Storage::getIndex(Token const& token, - std::string const& id, - form::experimental::config::tech_setting_config const& settings) -{ - if (m_indexMaps[token.containerName()].empty()) { - auto key = std::make_pair(token.fileName(), token.containerName()); - auto cont = m_containers.find(key); - if (cont == m_containers.end()) { - auto file = m_files.find(token.fileName()); - if (file == m_files.end()) { - m_files.insert({token.fileName(), createFile(token.technology(), token.fileName(), 'i')}); - file = m_files.find(token.fileName()); - for (auto const& [key, value] : settings.getFileTable(token.technology(), token.fileName())) - file->second->setAttribute(key, value); - } - m_containers.insert({key, createContainer(token.technology(), token.containerName())}); - cont = m_containers.find(key); - for (auto const& [key, value] : - settings.getContainerTable(token.technology(), token.containerName())) - cont->second->setAttribute(key, value); - cont->second->setFile(file->second); - } - void const* data; - int entry = 1; - while (cont->second->read(entry, &data, typeid(std::string))) { - m_indexMaps[token.containerName()].insert( - std::make_pair(*(static_cast(data)), entry)); - delete static_cast( - data); //FIXME: smart pointer? The overhead to delete an arbitrary type is not much prettier - entry++; - } - } - int entry = m_indexMaps[token.containerName()][id]; - return entry; -} - -void Storage::readContainer(Token const& token, - void const** data, - std::type_info const& type, - form::experimental::config::tech_setting_config const& settings) -{ - auto key = std::make_pair(token.fileName(), token.containerName()); - auto cont = m_containers.find(key); - if (cont == m_containers.end()) { - auto file = m_files.find(token.fileName()); - if (file == m_files.end()) { - m_files.insert({token.fileName(), createFile(token.technology(), token.fileName(), 'i')}); - file = m_files.find(token.fileName()); - for (auto const& [key, value] : settings.getFileTable(token.technology(), token.fileName())) - file->second->setAttribute(key, value); - } - m_containers.insert({key, createContainer(token.technology(), token.containerName())}); - cont = m_containers.find(key); - cont->second->setFile(file->second); - for (auto const& [key, value] : - settings.getContainerTable(token.technology(), token.containerName())) - cont->second->setAttribute(key, value); - } - cont->second->read(token.id(), data, type); - return; -} diff --git a/form/storage/storage_association.cpp b/form/storage/storage_association.cpp deleted file mode 100644 index 8c0fba21..00000000 --- a/form/storage/storage_association.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2025 ... - -#include "storage_association.hpp" - -using namespace form::detail::experimental; - -namespace { - std::string maybe_remove_suffix(std::string const& name) - { - auto del_pos = name.find("/"); - if (del_pos != std::string::npos) { - return name.substr(0, del_pos); - } - return name; - } -} - -Storage_Association::Storage_Association(std::string const& name) : - Storage_Container::Storage_Container(maybe_remove_suffix(name)) -{ -} - -void Storage_Association::setAttribute(std::string const& /*key*/, std::string const& /*value*/) {} diff --git a/form/storage/storage_association.hpp b/form/storage/storage_association.hpp deleted file mode 100644 index 64953a04..00000000 --- a/form/storage/storage_association.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_STORAGE_STORAGE_ASSOCIATION_HPP -#define FORM_STORAGE_STORAGE_ASSOCIATION_HPP - -#include "storage_container.hpp" - -#include - -namespace form::detail::experimental { - - class Storage_Association : public Storage_Container { - public: - Storage_Association(std::string const& name); - ~Storage_Association() = default; - - void setAttribute(std::string const& key, std::string const& value) override; - }; - -} // namespace form::detail::experimental - -#endif // FORM_STORAGE_STORAGE_ASSOCIATION_HPP diff --git a/form/storage/storage_associative_container.cpp b/form/storage/storage_associative_container.cpp deleted file mode 100644 index 66b86263..00000000 --- a/form/storage/storage_associative_container.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2025 ... - -#include "storage_associative_container.hpp" - -using namespace form::detail::experimental; - -Storage_Associative_Container::Storage_Associative_Container(std::string const& name) : - Storage_Container::Storage_Container(name), m_tName(), m_cName(), m_parent(nullptr) -{ - auto del_pos = name.find("/"); - if (del_pos != std::string::npos) { - m_tName = name.substr(0, del_pos); - m_cName = name.substr(del_pos + 1); - } else { - m_tName = name; - m_cName = "Main"; - } -} - -Storage_Associative_Container::~Storage_Associative_Container() = default; - -std::string const& Storage_Associative_Container::top_name() { return m_tName; } - -std::string const& Storage_Associative_Container::col_name() { return m_cName; } - -void Storage_Associative_Container::setParent(std::shared_ptr parent) -{ - m_parent = parent; -} diff --git a/form/storage/storage_associative_container.hpp b/form/storage/storage_associative_container.hpp deleted file mode 100644 index cb7a98a4..00000000 --- a/form/storage/storage_associative_container.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_STORAGE_STORAGE_ASSOCIATIVE_CONTAINER_HPP -#define FORM_STORAGE_STORAGE_ASSOCIATIVE_CONTAINER_HPP - -#include "storage_container.hpp" - -#include - -namespace form::detail::experimental { - - class Storage_Associative_Container : public Storage_Container { - public: - Storage_Associative_Container(std::string const& name); - ~Storage_Associative_Container(); - - std::string const& top_name(); - std::string const& col_name(); - - virtual void setParent(std::shared_ptr parent); - - private: - std::string m_tName; - std::string m_cName; - - std::shared_ptr m_parent; - }; - -} // namespace form::detail::experimental - -#endif // FORM_STORAGE_STORAGE_ASSOCIATIVE_CONTAINER_HPP diff --git a/form/storage/storage_associative_read_container.cpp b/form/storage/storage_associative_read_container.cpp new file mode 100644 index 00000000..e3538597 --- /dev/null +++ b/form/storage/storage_associative_read_container.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2025 ... + +#include "storage_associative_read_container.hpp" + +using namespace form::detail::experimental; + +Storage_Associative_Read_Container::Storage_Associative_Read_Container(std::string const& name) : + Storage_Read_Container::Storage_Read_Container(name), m_tName(), m_cName(), m_parent(nullptr) +{ + auto del_pos = name.find("/"); + if (del_pos != std::string::npos) { + m_tName = name.substr(0, del_pos); + m_cName = name.substr(del_pos + 1); + } else { + m_tName = name; + m_cName = "Main"; + } +} + +Storage_Associative_Read_Container::~Storage_Associative_Read_Container() = default; + +std::string const& Storage_Associative_Read_Container::top_name() { return m_tName; } + +std::string const& Storage_Associative_Read_Container::col_name() { return m_cName; } + +void Storage_Associative_Read_Container::setParent(std::shared_ptr parent) +{ + m_parent = parent; +} diff --git a/form/storage/storage_associative_read_container.hpp b/form/storage/storage_associative_read_container.hpp new file mode 100644 index 00000000..3639885b --- /dev/null +++ b/form/storage/storage_associative_read_container.hpp @@ -0,0 +1,31 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_ASSOCIATIVE_READ_CONTAINER_HPP +#define FORM_STORAGE_STORAGE_ASSOCIATIVE_READ_CONTAINER_HPP + +#include "storage_read_container.hpp" + +#include + +namespace form::detail::experimental { + + class Storage_Associative_Read_Container : public Storage_Read_Container { + public: + Storage_Associative_Read_Container(std::string const& name); + ~Storage_Associative_Read_Container(); + + std::string const& top_name(); + std::string const& col_name(); + + virtual void setParent(std::shared_ptr parent); + + private: + std::string m_tName; + std::string m_cName; + + std::shared_ptr m_parent; + }; + +} // namespace form::detail::experimental + +#endif // FORM_STORAGE_STORAGE_ASSOCIATIVE_READ_CONTAINER_HPP diff --git a/form/storage/storage_associative_write_container.cpp b/form/storage/storage_associative_write_container.cpp new file mode 100644 index 00000000..d045e0c5 --- /dev/null +++ b/form/storage/storage_associative_write_container.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2025 ... + +#include "storage_associative_write_container.hpp" + +using namespace form::detail::experimental; + +Storage_Associative_Write_Container::Storage_Associative_Write_Container(std::string const& name) : + Storage_Write_Container::Storage_Write_Container(name), m_tName(), m_cName(), m_parent(nullptr) +{ + auto del_pos = name.find("/"); + if (del_pos != std::string::npos) { + m_tName = name.substr(0, del_pos); + m_cName = name.substr(del_pos + 1); + } else { + m_tName = name; + m_cName = "Main"; + } +} + +Storage_Associative_Write_Container::~Storage_Associative_Write_Container() = default; + +std::string const& Storage_Associative_Write_Container::top_name() { return m_tName; } + +std::string const& Storage_Associative_Write_Container::col_name() { return m_cName; } + +void Storage_Associative_Write_Container::setParent( + std::shared_ptr parent) +{ + m_parent = parent; +} diff --git a/form/storage/storage_associative_write_container.hpp b/form/storage/storage_associative_write_container.hpp new file mode 100644 index 00000000..2b2524fd --- /dev/null +++ b/form/storage/storage_associative_write_container.hpp @@ -0,0 +1,31 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_ASSOCIATIVE_WRITE_CONTAINER_HPP +#define FORM_STORAGE_STORAGE_ASSOCIATIVE_WRITE_CONTAINER_HPP + +#include "storage_write_container.hpp" + +#include + +namespace form::detail::experimental { + + class Storage_Associative_Write_Container : public Storage_Write_Container { + public: + Storage_Associative_Write_Container(std::string const& name); + ~Storage_Associative_Write_Container(); + + std::string const& top_name(); + std::string const& col_name(); + + virtual void setParent(std::shared_ptr parent); + + private: + std::string m_tName; + std::string m_cName; + + std::shared_ptr m_parent; + }; + +} // namespace form::detail::experimental + +#endif // FORM_STORAGE_STORAGE_ASSOCIATIVE_WRITE_CONTAINER_HPP diff --git a/form/storage/storage_container.cpp b/form/storage/storage_container.cpp deleted file mode 100644 index e3c61416..00000000 --- a/form/storage/storage_container.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2025 ... - -#include "storage_container.hpp" -#include "storage_file.hpp" - -#include - -using namespace form::detail::experimental; - -Storage_Container::Storage_Container(std::string const& name) : m_name(name), m_file(nullptr) {} - -std::string const& Storage_Container::name() { return m_name; } - -void Storage_Container::setFile(std::shared_ptr file) { m_file = file; } - -void Storage_Container::setupWrite(std::type_info const& /*type*/) { return; } - -void Storage_Container::fill(void const* /* data*/) { return; } - -void Storage_Container::commit() { return; } - -bool Storage_Container::read(int /* id*/, void const** /*data*/, std::type_info const& /* type*/) -{ - return false; -} - -void Storage_Container::setAttribute(std::string const& /*name*/, std::string const& /*value*/) -{ - throw std::runtime_error( - "Storage_Container::setAttribute does not accept any attributes for a container named " + - m_name); -} diff --git a/form/storage/storage_read_association.cpp b/form/storage/storage_read_association.cpp new file mode 100644 index 00000000..7f06b94e --- /dev/null +++ b/form/storage/storage_read_association.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2025 ... + +#include "storage_read_association.hpp" + +using namespace form::detail::experimental; + +namespace { + std::string maybe_remove_suffix(std::string const& name) + { + auto del_pos = name.find("/"); + if (del_pos != std::string::npos) { + return name.substr(0, del_pos); + } + return name; + } +} + +Storage_Read_Association::Storage_Read_Association(std::string const& name) : + Storage_Read_Container::Storage_Read_Container(maybe_remove_suffix(name)) +{ +} + +void Storage_Read_Association::setAttribute(std::string const& /*key*/, + std::string const& /*value*/) +{ +} diff --git a/form/storage/storage_read_association.hpp b/form/storage/storage_read_association.hpp new file mode 100644 index 00000000..15815d0b --- /dev/null +++ b/form/storage/storage_read_association.hpp @@ -0,0 +1,22 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_READ_ASSOCIATION_HPP +#define FORM_STORAGE_STORAGE_READ_ASSOCIATION_HPP + +#include "storage_read_container.hpp" + +#include + +namespace form::detail::experimental { + + class Storage_Read_Association : public Storage_Read_Container { + public: + Storage_Read_Association(std::string const& name); + ~Storage_Read_Association() = default; + + void setAttribute(std::string const& key, std::string const& value) override; + }; + +} // namespace form::detail::experimental + +#endif // FORM_STORAGE_STORAGE_READ_ASSOCIATION_HPP diff --git a/form/storage/storage_read_container.cpp b/form/storage/storage_read_container.cpp new file mode 100644 index 00000000..8e5d7f25 --- /dev/null +++ b/form/storage/storage_read_container.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2025 ... + +#include "storage_read_container.hpp" +#include "storage_file.hpp" + +using namespace form::detail::experimental; + +Storage_Read_Container::Storage_Read_Container(std::string const& name) : + m_name(name), m_file(nullptr) +{ +} + +std::string const& Storage_Read_Container::name() { return m_name; } + +void Storage_Read_Container::setFile(std::shared_ptr file) { m_file = file; } + +bool Storage_Read_Container::read(int /* id*/, + void const** /*data*/, + std::type_info const& /* type*/) +{ + return false; +} + +void Storage_Read_Container::setAttribute(std::string const& /*name*/, std::string const& /*value*/) +{ + throw std::runtime_error( + "Storage_Read_Container::setAttribute does not accept any attributes for a container named " + + m_name); +} diff --git a/form/storage/storage_read_container.hpp b/form/storage/storage_read_container.hpp new file mode 100644 index 00000000..11dbef21 --- /dev/null +++ b/form/storage/storage_read_container.hpp @@ -0,0 +1,32 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_READ_CONTAINER_HPP +#define FORM_STORAGE_STORAGE_READ_CONTAINER_HPP + +#include "istorage.hpp" + +#include +#include + +namespace form::detail::experimental { + + class Storage_Read_Container : public IStorage_Read_Container { + public: + Storage_Read_Container(std::string const& name); + ~Storage_Read_Container() = default; + + std::string const& name() override; + + void setFile(std::shared_ptr file) override; + + bool read(int id, void const** data, std::type_info const& type) override; + + void setAttribute(std::string const& name, std::string const& value) override; + + private: + std::string m_name; + std::shared_ptr m_file; + }; +} // namespace form::detail::experimental + +#endif // FORM_STORAGE_STORAGE_READ_CONTAINER_HPP diff --git a/form/storage/storage_reader.cpp b/form/storage/storage_reader.cpp new file mode 100644 index 00000000..e1cfeae2 --- /dev/null +++ b/form/storage/storage_reader.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2025 ... + +#include "storage_reader.hpp" +#include "storage_associative_read_container.hpp" +#include "storage_file.hpp" +#include "storage_read_association.hpp" + +#include "util/factories.hpp" + +using namespace form::detail::experimental; + +// Factory function implementation +namespace form::detail::experimental { + std::unique_ptr createStorageReader() + { + return std::unique_ptr(new StorageReader()); + } +} + +int StorageReader::getIndex(Token const& token, + std::string const& id, + form::experimental::config::tech_setting_config const& settings) +{ + if (m_indexMaps[token.containerName()].empty()) { + auto key = std::make_pair(token.fileName(), token.containerName()); + auto cont = m_read_containers.find(key); + if (cont == m_read_containers.end()) { + auto file = m_files.find(token.fileName()); + if (file == m_files.end()) { + m_files.insert({token.fileName(), createFile(token.technology(), token.fileName(), 'i')}); + file = m_files.find(token.fileName()); + for (auto const& [key, value] : settings.getFileTable(token.technology(), token.fileName())) + file->second->setAttribute(key, value); + } + m_read_containers.insert( + {key, createReadContainer(token.technology(), token.containerName())}); + cont = m_read_containers.find(key); + for (auto const& [key, value] : + settings.getContainerTable(token.technology(), token.containerName())) + cont->second->setAttribute(key, value); + cont->second->setFile(file->second); + } + void const* data; + auto const& type = typeid(std::string); + int entry = 1; + while (cont->second->read(entry, &data, type)) { + m_indexMaps[token.containerName()].insert( + std::make_pair(*(static_cast(data)), entry)); + delete static_cast( + data); //FIXME: smart pointer? The overhead to delete an arbitrary type is not much prettier + entry++; + } + } + int entry = m_indexMaps[token.containerName()][id]; + return entry; +} + +void StorageReader::readContainer(Token const& token, + void const** data, + std::type_info const& type, + form::experimental::config::tech_setting_config const& settings) +{ + auto key = std::make_pair(token.fileName(), token.containerName()); + auto cont = m_read_containers.find(key); + if (cont == m_read_containers.end()) { + auto file = m_files.find(token.fileName()); + if (file == m_files.end()) { + m_files.insert({token.fileName(), createFile(token.technology(), token.fileName(), 'i')}); + file = m_files.find(token.fileName()); + for (auto const& [key, value] : settings.getFileTable(token.technology(), token.fileName())) + file->second->setAttribute(key, value); + } + m_read_containers.insert({key, createReadContainer(token.technology(), token.containerName())}); + cont = m_read_containers.find(key); + cont->second->setFile(file->second); + for (auto const& [key, value] : + settings.getContainerTable(token.technology(), token.containerName())) + cont->second->setAttribute(key, value); + } + cont->second->read(token.id(), data, type); + return; +} diff --git a/form/storage/storage.hpp b/form/storage/storage_reader.hpp similarity index 51% rename from form/storage/storage.hpp rename to form/storage/storage_reader.hpp index e4a529db..8d552d08 100644 --- a/form/storage/storage.hpp +++ b/form/storage/storage_reader.hpp @@ -1,9 +1,10 @@ // Copyright (C) 2025 ... -#ifndef FORM_STORAGE_STORAGE_HPP -#define FORM_STORAGE_STORAGE_HPP +#ifndef FORM_STORAGE_STORAGE_READER_HPP +#define FORM_STORAGE_STORAGE_READER_HPP #include "istorage.hpp" +#include "storage_utils.hpp" #include #include @@ -13,30 +14,12 @@ namespace form::detail::experimental { - // Hash function for std::pair - struct pair_hash { - template - std::size_t operator()(std::pair const& p) const - { - std::hash h1; - std::hash h2; - return h1(p.first) ^ (h2(p.second) << 1); - } - }; - - class Storage : public IStorage { + class StorageReader : public IStorageReader { public: - Storage() = default; - ~Storage() = default; + StorageReader() = default; + ~StorageReader() = default; using table_t = form::experimental::config::tech_setting_config::table_t; - void createContainers( - std::map, std::type_info const*> const& containers, - form::experimental::config::tech_setting_config const& settings) override; - void fillContainer(Placement const& plcmnt, - void const* data, - std::type_info const& type) override; - void commitContainers(Placement const& plcmnt) override; int getIndex(Token const& token, std::string const& id, @@ -49,12 +32,12 @@ namespace form::detail::experimental { private: std::map> m_files; std::unordered_map, - std::shared_ptr, + std::shared_ptr, pair_hash> - m_containers; + m_read_containers; std::map> m_indexMaps; }; } // namespace form::detail::experimental -#endif // FORM_STORAGE_STORAGE_HPP +#endif // FORM_STORAGE_STORAGE_READER_HPP diff --git a/form/storage/storage_utils.hpp b/form/storage/storage_utils.hpp new file mode 100644 index 00000000..2abbd455 --- /dev/null +++ b/form/storage/storage_utils.hpp @@ -0,0 +1,19 @@ +#include + +#ifndef FORM_STORAGE_STORAGE_UTILS_HPP +#define FORM_STORAGE_STORAGE_UTILS_HPP + +namespace form::detail::experimental { + // Hash function for std::pair + struct pair_hash { + template + std::size_t operator()(std::pair const& p) const + { + std::hash h1; + std::hash h2; + return h1(p.first) ^ (h2(p.second) << 1); + } + }; +} + +#endif // FORM_STORAGE_STORAGE_UTILS_HPP diff --git a/form/storage/storage_write_association.cpp b/form/storage/storage_write_association.cpp new file mode 100644 index 00000000..f19aae1c --- /dev/null +++ b/form/storage/storage_write_association.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2025 ... + +#include "storage_write_association.hpp" + +using namespace form::detail::experimental; + +namespace { + std::string maybe_remove_suffix(std::string const& name) + { + auto del_pos = name.find("/"); + if (del_pos != std::string::npos) { + return name.substr(0, del_pos); + } + return name; + } +} + +Storage_Write_Association::Storage_Write_Association(std::string const& name) : + Storage_Write_Container::Storage_Write_Container(maybe_remove_suffix(name)) +{ +} + +void Storage_Write_Association::setAttribute(std::string const& /*key*/, + std::string const& /*value*/) +{ +} diff --git a/form/storage/storage_write_association.hpp b/form/storage/storage_write_association.hpp new file mode 100644 index 00000000..d11bd83e --- /dev/null +++ b/form/storage/storage_write_association.hpp @@ -0,0 +1,22 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_WRITE_ASSOCIATION_HPP +#define FORM_STORAGE_STORAGE_WRITE_ASSOCIATION_HPP + +#include "storage_write_container.hpp" + +#include + +namespace form::detail::experimental { + + class Storage_Write_Association : public Storage_Write_Container { + public: + Storage_Write_Association(std::string const& name); + ~Storage_Write_Association() = default; + + void setAttribute(std::string const& key, std::string const& value) override; + }; + +} // namespace form::detail::experimental + +#endif // FORM_STORAGE_STORAGE_WRITE_ASSOCIATION_HPP diff --git a/form/storage/storage_write_container.cpp b/form/storage/storage_write_container.cpp new file mode 100644 index 00000000..37742aca --- /dev/null +++ b/form/storage/storage_write_container.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2025 ... + +#include "storage_write_container.hpp" +#include "storage_file.hpp" + +using namespace form::detail::experimental; + +Storage_Write_Container::Storage_Write_Container(std::string const& name) : + m_name(name), m_file(nullptr) +{ +} + +std::string const& Storage_Write_Container::name() { return m_name; } + +void Storage_Write_Container::setFile(std::shared_ptr file) { m_file = file; } + +void Storage_Write_Container::setupWrite(std::type_info const& /* type*/) { return; } + +void Storage_Write_Container::fill(void const* /* data*/) { return; } + +void Storage_Write_Container::commit() { return; } + +void Storage_Write_Container::setAttribute(std::string const& /*name*/, + std::string const& /*value*/) +{ + throw std::runtime_error( + "Storage_Write_Container::setAttribute does not accept any attributes for a container named " + + m_name); +} diff --git a/form/storage/storage_container.hpp b/form/storage/storage_write_container.hpp similarity index 61% rename from form/storage/storage_container.hpp rename to form/storage/storage_write_container.hpp index 87a00391..a13e0200 100644 --- a/form/storage/storage_container.hpp +++ b/form/storage/storage_write_container.hpp @@ -1,20 +1,19 @@ // Copyright (C) 2025 ... -#ifndef FORM_STORAGE_STORAGE_CONTAINER_HPP -#define FORM_STORAGE_STORAGE_CONTAINER_HPP +#ifndef FORM_STORAGE_STORAGE_WRITE_CONTAINER_HPP +#define FORM_STORAGE_STORAGE_WRITE_CONTAINER_HPP #include "istorage.hpp" #include #include -#include namespace form::detail::experimental { - class Storage_Container : public IStorage_Container { + class Storage_Write_Container : public IStorage_Write_Container { public: - Storage_Container(std::string const& name); - ~Storage_Container() = default; + Storage_Write_Container(std::string const& name); + ~Storage_Write_Container() = default; std::string const& name() override; @@ -23,7 +22,6 @@ namespace form::detail::experimental { void setupWrite(std::type_info const& type = typeid(void)) override; void fill(void const* data) override; void commit() override; - bool read(int id, void const** data, std::type_info const& type) override; void setAttribute(std::string const& name, std::string const& value) override; @@ -33,4 +31,4 @@ namespace form::detail::experimental { }; } // namespace form::detail::experimental -#endif // FORM_STORAGE_STORAGE_CONTAINER_HPP +#endif // FORM_STORAGE_STORAGE_WRITE_CONTAINER_HPP diff --git a/form/storage/storage_writer.cpp b/form/storage/storage_writer.cpp new file mode 100644 index 00000000..4a03e2a7 --- /dev/null +++ b/form/storage/storage_writer.cpp @@ -0,0 +1,92 @@ +// Copyright (C) 2025 ... + +#include "storage_writer.hpp" +#include "storage_associative_write_container.hpp" +#include "storage_file.hpp" +#include "storage_write_association.hpp" + +#include "util/factories.hpp" + +using namespace form::detail::experimental; + +// Factory function implementation +namespace form::detail::experimental { + std::unique_ptr createStorageWriter() + { + return std::unique_ptr(new StorageWriter()); + } +} + +void StorageWriter::createContainers( + std::map, std::type_info const*> const& containers, + form::experimental::config::tech_setting_config const& settings) +{ + for (auto const& [plcmnt, type] : containers) { + // Use file+container as composite key + auto key = std::make_pair(plcmnt->fileName(), plcmnt->containerName()); + auto cont = m_write_containers.find(key); + if (cont == m_write_containers.end()) { + // Ensure the file exists + auto file = m_files.find(plcmnt->fileName()); + if (file == m_files.end()) { + m_files.insert( + {plcmnt->fileName(), createFile(plcmnt->technology(), plcmnt->fileName(), 'o')}); + file = m_files.find(plcmnt->fileName()); + for (auto const& [key, value] : + settings.getFileTable(plcmnt->technology(), plcmnt->fileName())) + file->second->setAttribute(key, value); + } + // Create and bind container to file + auto container = createWriteContainer(plcmnt->technology(), plcmnt->containerName()); + m_write_containers.insert({key, container}); + // For associative container, create association layer + auto associative_container = + dynamic_pointer_cast(container); + if (associative_container) { + auto parent_key = std::make_pair(plcmnt->fileName(), associative_container->top_name()); + auto parent = m_write_containers.find(parent_key); + if (parent == m_write_containers.end()) { + auto parent_cont = + createWriteAssociation(plcmnt->technology(), associative_container->top_name()); + m_write_containers.insert({parent_key, parent_cont}); + parent_cont->setFile(file->second); + parent_cont->setupWrite(); + associative_container->setParent(parent_cont); + } else { + associative_container->setParent(parent->second); + } + } + + for (auto const& [key, value] : + settings.getContainerTable(plcmnt->technology(), plcmnt->containerName())) + container->setAttribute(key, value); + container->setFile(file->second); + container->setupWrite(*type); + } + } + return; +} + +void StorageWriter::fillContainer(Placement const& plcmnt, + void const* data, + std::type_info const& /* type*/) +{ + // Use file+container as composite key + auto key = std::make_pair(plcmnt.fileName(), plcmnt.containerName()); + auto cont = m_write_containers.find(key); + if (cont == m_write_containers.end()) { + // FIXME: For now throw an exception here, but in future, we may have storage technology do that. + throw std::runtime_error("StorageWriter::fillContainer Container doesn't exist: " + + plcmnt.containerName()); + } + cont->second->fill(data); + return; +} + +void StorageWriter::commitContainers(Placement const& plcmnt) +{ + auto key = std::make_pair(plcmnt.fileName(), plcmnt.containerName()); + auto cont = m_write_containers.find(key); + cont->second->commit(); + return; +} diff --git a/form/storage/storage_writer.hpp b/form/storage/storage_writer.hpp new file mode 100644 index 00000000..55cc77f8 --- /dev/null +++ b/form/storage/storage_writer.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_WRITER_HPP +#define FORM_STORAGE_STORAGE_WRITER_HPP + +#include "istorage.hpp" +#include "storage_utils.hpp" + +#include +#include +#include +#include +#include // for std::pair + +namespace form::detail::experimental { + + class StorageWriter : public IStorageWriter { + public: + StorageWriter() = default; + ~StorageWriter() = default; + + using table_t = form::experimental::config::tech_setting_config::table_t; + void createContainers( + std::map, std::type_info const*> const& containers, + form::experimental::config::tech_setting_config const& settings) override; + void fillContainer(Placement const& plcmnt, + void const* data, + std::type_info const& type) override; + void commitContainers(Placement const& plcmnt) override; + + private: + std::map> m_files; + std::unordered_map, + std::shared_ptr, + pair_hash> + m_write_containers; + std::map> m_indexMaps; + }; + +} // namespace form::detail::experimental + +#endif // FORM_STORAGE_STORAGE_WRITER_HPP diff --git a/form/util/factories.hpp b/form/util/factories.hpp index 5ad2f73e..ba6eef09 100644 --- a/form/util/factories.hpp +++ b/form/util/factories.hpp @@ -6,14 +6,18 @@ #include "form/technology.hpp" #include "storage/istorage.hpp" -#include "storage/storage_association.hpp" -#include "storage/storage_container.hpp" #include "storage/storage_file.hpp" +#include "storage/storage_read_association.hpp" +#include "storage/storage_read_container.hpp" +#include "storage/storage_write_association.hpp" +#include "storage/storage_write_container.hpp" #ifdef USE_ROOT_STORAGE -#include "root_storage/root_tbranch_container.hpp" +#include "root_storage/root_tbranch_read_container.hpp" +#include "root_storage/root_tbranch_write_container.hpp" #include "root_storage/root_tfile.hpp" -#include "root_storage/root_ttree_container.hpp" +#include "root_storage/root_ttree_read_container.hpp" +#include "root_storage/root_ttree_write_container.hpp" #endif #include @@ -29,47 +33,91 @@ namespace form::detail::experimental { #endif } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { // Handle HDF5 file creation when implemented + // return std::make_shared(name, mode); } return std::make_shared(name, mode); } - inline std::shared_ptr createAssociation(int tech, std::string const& name) + inline std::shared_ptr createReadAssociation(int tech, + std::string const& name) { if (form::technology::GetMajor(tech) == form::technology::ROOT_MAJOR) { if (form::technology::GetMinor(tech) == form::technology::ROOT_TTREE_MINOR) { #ifdef USE_ROOT_STORAGE - return std::make_shared(name); + return std::make_shared(name); #endif // USE_ROOT_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { #ifdef USE_HDF5_STORAGE // Add HDF5 implementation when available - // return std::make_shared(name); + // return std::make_shared(name); #endif // USE_HDF5_STORAGE } // Default fallback - return std::make_shared(name); + return std::make_shared(name); } - inline std::shared_ptr createContainer(int tech, std::string const& name) + inline std::shared_ptr createWriteAssociation(int tech, + std::string const& name) + { + if (form::technology::GetMajor(tech) == form::technology::ROOT_MAJOR) { + if (form::technology::GetMinor(tech) == form::technology::ROOT_TTREE_MINOR) { +#ifdef USE_ROOT_STORAGE + return std::make_shared(name); +#endif // USE_ROOT_STORAGE + } + } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { +#ifdef USE_HDF5_STORAGE + // Add HDF5 implementation when available + // return std::make_shared(name); +#endif // USE_HDF5_STORAGE + } + + // Default fallback + return std::make_shared(name); + } + + inline std::shared_ptr createReadContainer(int tech, + std::string const& name) + { + // Use the helper functions from Technology namespace for consistency + if (form::technology::GetMajor(tech) == form::technology::ROOT_MAJOR) { + if (form::technology::GetMinor(tech) == form::technology::ROOT_TTREE_MINOR) { +#ifdef USE_ROOT_STORAGE + return std::make_shared(name); +#endif // USE_ROOT_STORAGE + } + } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { +#ifdef USE_HDF5_STORAGE + // Add HDF5 implementation when available + // return std::make_shared(name); +#endif // USE_HDF5_STORAGE + } + + // Default fallback + return std::make_shared(name); + } + + inline std::shared_ptr createWriteContainer(int tech, + std::string const& name) { // Use the helper functions from Technology namespace for consistency if (form::technology::GetMajor(tech) == form::technology::ROOT_MAJOR) { if (form::technology::GetMinor(tech) == form::technology::ROOT_TTREE_MINOR) { #ifdef USE_ROOT_STORAGE - return std::make_shared(name); + return std::make_shared(name); #endif // USE_ROOT_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { #ifdef USE_HDF5_STORAGE // Add HDF5 implementation when available - // return std::make_shared(name); + // return std::make_shared(name); #endif // USE_HDF5_STORAGE } // Default fallback - return std::make_shared(name); + return std::make_shared(name); } } // namespace form::detail::experimental diff --git a/test/form/form_basics_test.cpp b/test/form/form_basics_test.cpp index 6596b732..38cb47e4 100644 --- a/test/form/form_basics_test.cpp +++ b/test/form/form_basics_test.cpp @@ -1,11 +1,15 @@ #include "core/token.hpp" #include "form/config.hpp" -#include "persistence/persistence.hpp" +#include "persistence/persistence_reader.hpp" +#include "persistence/persistence_writer.hpp" #include "storage/istorage.hpp" -#include "storage/storage_association.hpp" -#include "storage/storage_associative_container.hpp" -#include "storage/storage_container.hpp" +#include "storage/storage_associative_read_container.hpp" +#include "storage/storage_associative_write_container.hpp" #include "storage/storage_file.hpp" +#include "storage/storage_read_association.hpp" +#include "storage/storage_read_container.hpp" +#include "storage/storage_write_association.hpp" +#include "storage/storage_write_container.hpp" #include "util/factories.hpp" #include @@ -31,9 +35,23 @@ TEST_CASE("Storage_File basics", "[form]") CHECK_THROWS_AS(f.setAttribute("key", "value"), std::runtime_error); } -TEST_CASE("Storage_Container basics", "[form]") +TEST_CASE("Storage_Read_Container basics", "[form]") { - Storage_Container c("my_container"); + Storage_Read_Container c("my_container"); + CHECK(c.name() == "my_container"); + + auto f = std::make_shared("test.root", 'o'); + c.setFile(f); + + void const* data = nullptr; + CHECK_FALSE(c.read(1, &data, typeid(int))); + + CHECK_THROWS_AS(c.setAttribute("key", "value"), std::runtime_error); +} + +TEST_CASE("Storage_Write_Container basics", "[form]") +{ + Storage_Write_Container c("my_container"); CHECK(c.name() == "my_container"); auto f = std::make_shared("test.root", 'o'); @@ -44,37 +62,62 @@ TEST_CASE("Storage_Container basics", "[form]") c.fill(&value); c.commit(); - void const* data = nullptr; - CHECK_FALSE(c.read(1, &data, typeid(int))); - CHECK_THROWS_AS(c.setAttribute("key", "value"), std::runtime_error); } -TEST_CASE("Storage_Association basics", "[form]") +TEST_CASE("Storage_Read_Association basics", "[form]") { - Storage_Association a("my_assoc/extra"); + Storage_Read_Association a("my_assoc/extra"); CHECK(a.name() == "my_assoc"); // maybe_remove_suffix should remove /extra - a.setAttribute("key", "value"); // Storage_Association overrides setAttribute to do nothing + a.setAttribute("key", "value"); // Storage_Read_Association overrides setAttribute to do nothing } -TEST_CASE("Storage_Associative_Container basics", "[form]") +TEST_CASE("Storage_Write_Association basics", "[form]") +{ + Storage_Write_Association a("my_assoc/extra"); + CHECK(a.name() == "my_assoc"); // maybe_remove_suffix should remove /extra + + a.setAttribute("key", "value"); // Storage_Write_Association overrides setAttribute to do nothing +} + +TEST_CASE("Storage_Associative_Read_Container basics", "[form]") +{ + SECTION("With slash") + { + Storage_Associative_Read_Container c("parent/child"); + CHECK(c.top_name() == "parent"); + CHECK(c.col_name() == "child"); + } + SECTION("Without slash") + { + Storage_Associative_Read_Container c("no_slash"); + CHECK(c.top_name() == "no_slash"); + CHECK(c.col_name() == "Main"); + } + + Storage_Associative_Read_Container c("p/c"); + auto parent = std::make_shared("p"); + c.setParent(parent); +} + +TEST_CASE("Storage_Associative_Write_Container basics", "[form]") { SECTION("With slash") { - Storage_Associative_Container c("parent/child"); + Storage_Associative_Write_Container c("parent/child"); CHECK(c.top_name() == "parent"); CHECK(c.col_name() == "child"); } SECTION("Without slash") { - Storage_Associative_Container c("no_slash"); + Storage_Associative_Write_Container c("no_slash"); CHECK(c.top_name() == "no_slash"); CHECK(c.col_name() == "Main"); } - Storage_Associative_Container c("p/c"); - auto parent = std::make_shared("p"); + Storage_Associative_Write_Container c("p/c"); + auto parent = std::make_shared("p"); c.setParent(parent); } @@ -83,16 +126,37 @@ TEST_CASE("Factories fallback", "[form]") auto f = createFile(0, "test.root", 'o'); CHECK(dynamic_cast(f.get()) != nullptr); - auto a = createAssociation(0, "assoc"); - CHECK(dynamic_cast(a.get()) != nullptr); + auto ra = createReadAssociation(0, "assoc"); + CHECK(dynamic_cast(ra.get()) != nullptr); + + auto rc = createReadContainer(0, "cont"); + CHECK(dynamic_cast(rc.get()) != nullptr); + + auto wa = createWriteAssociation(0, "assoc"); + CHECK(dynamic_cast(wa.get()) != nullptr); - auto c = createContainer(0, "cont"); - CHECK(dynamic_cast(c.get()) != nullptr); + auto wc = createWriteContainer(0, "cont"); + CHECK(dynamic_cast(wc.get()) != nullptr); } -TEST_CASE("Storage basic operations", "[form]") +TEST_CASE("StorageReader basic operations", "[form]") { - auto storage = createStorage(); + auto storage = createStorageReader(); + REQUIRE(storage != nullptr); + + form::experimental::config::tech_setting_config settings; + + Token token("file.root", "cont", 0, 1); + void const* read_data = nullptr; + storage->readContainer(token, &read_data, typeid(int), settings); + + int index = storage->getIndex(token, "some_id", settings); + CHECK(index == 0); +} + +TEST_CASE("StorageWriter basic operations", "[form]") +{ + auto storage = createStorageWriter(); REQUIRE(storage != nullptr); form::experimental::config::tech_setting_config settings; @@ -107,18 +171,11 @@ TEST_CASE("Storage basic operations", "[form]") int data = 42; storage->fillContainer(p2, &data, typeid(int)); storage->commitContainers(p2); - - Token token("file.root", "cont", 0, 1); - void const* read_data = nullptr; - storage->readContainer(token, &read_data, typeid(int), settings); - - int index = storage->getIndex(token, "some_id", settings); - CHECK(index == 0); } -TEST_CASE("Persistence basic operations", "[form]") +TEST_CASE("PersistenceReader basic operations", "[form]") { - auto p = createPersistence(); + auto p = createPersistenceReader(); REQUIRE(p != nullptr); using namespace form::experimental::config; @@ -132,19 +189,25 @@ TEST_CASE("Persistence basic operations", "[form]") SECTION("Full Lifecycle") { - std::map products; - products["prod"] = &typeid(int); - products["parent/child"] = &typeid(double); - CHECK_NOTHROW(p->createContainers("my_creator", products)); - - int val = 42; - CHECK_NOTHROW(p->registerWrite("my_creator", "prod", &val, typeid(int))); - CHECK_NOTHROW(p->commitOutput("my_creator", "event_1")); - void const* data = nullptr; // This will call getToken -> getIndex (returns 0 for Storage_Container) -> readContainer CHECK_NOTHROW(p->read("my_creator", "prod", "event_1", &data, typeid(int))); } +} + +TEST_CASE("PersistenceWriter basic operations", "[form]") +{ + auto p = createPersistenceWriter(); + REQUIRE(p != nullptr); + + using namespace form::experimental::config; + output_item_config out_cfg; + out_cfg.addItem("prod", "file.root", 0); + out_cfg.addItem("parent/child", "file.root", 0); + p->configureOutputItems(out_cfg); + + tech_setting_config tech_cfg; + p->configureTechSettings(tech_cfg); SECTION("Errors") { diff --git a/test/form/form_storage_test.cpp b/test/form/form_storage_test.cpp index f4d2c505..46f84978 100644 --- a/test/form/form_storage_test.cpp +++ b/test/form/form_storage_test.cpp @@ -17,7 +17,8 @@ TEST_CASE("Storage_Container read wrong type", "[form]") form::test::write(technology, primes); auto file = createFile(technology, form::test::testFileName, 'i'); - auto container = createContainer(technology, form::test::makeTestBranchName>()); + auto container = + createReadContainer(technology, form::test::makeTestBranchName>()); container->setFile(file); void const* dataPtr; CHECK_THROWS_AS(container->read(0, &dataPtr, typeid(double)), std::runtime_error); @@ -78,7 +79,7 @@ TEST_CASE("FORM Container setup error handling") { int const technology = form::technology::ROOT_TTREE; auto file = createFile(technology, "testContainerErrorHandling.root", 'o'); - auto container = createContainer(technology, "test/testData"); + auto writeContainer = createWriteContainer(technology, "test/testData"); std::vector testData; void const* ptrTestData = &testData; @@ -86,24 +87,27 @@ TEST_CASE("FORM Container setup error handling") SECTION("fill() before setParent()") { - CHECK_THROWS_AS(container->setupWrite(typeInfo), std::runtime_error); - CHECK_THROWS_AS(container->fill(ptrTestData), std::runtime_error); + CHECK_THROWS_AS(writeContainer->setupWrite(typeInfo), std::runtime_error); + CHECK_THROWS_AS(writeContainer->fill(ptrTestData), std::runtime_error); } SECTION("commit() before setParent()") { - CHECK_THROWS_AS(container->commit(), std::runtime_error); + CHECK_THROWS_AS(writeContainer->commit(), std::runtime_error); } + auto readContainer = createReadContainer(technology, "test/testData"); + SECTION("read() before setParent()") { - CHECK_THROWS_AS(container->read(0, &ptrTestData, typeInfo), std::runtime_error); + CHECK_THROWS_AS(readContainer->read(0, &ptrTestData, typeInfo), std::runtime_error); } SECTION("mismatched file type") { std::shared_ptr wrongFile( new Storage_File("testContainerErrorHandling.root", 'o')); - CHECK_THROWS_AS(container->setFile(wrongFile), std::runtime_error); + CHECK_THROWS_AS(readContainer->setFile(wrongFile), std::runtime_error); + CHECK_THROWS_AS(writeContainer->setFile(wrongFile), std::runtime_error); } } diff --git a/test/form/reader.cpp b/test/form/reader.cpp index 8d424732..2c914eef 100644 --- a/test/form/reader.cpp +++ b/test/form/reader.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2025 ... #include "data_products/track_start.hpp" -#include "form/form.hpp" +#include "form/form_reader.hpp" #include "form/technology.hpp" #include "test_helpers.hpp" @@ -73,7 +73,7 @@ int main(int argc, char** argv) form::experimental::config::tech_setting_config tech_config; - form::experimental::form_interface form(output_config, tech_config); + form::experimental::form_reader_interface form(output_config, tech_config); bool all_passed = true; diff --git a/test/form/test_helpers.hpp b/test/form/test_helpers.hpp index 57292251..0d503542 100644 --- a/test/form/test_helpers.hpp +++ b/test/form/test_helpers.hpp @@ -2,6 +2,7 @@ #define TEST_FORM_TEST_HELPERS_HPP #include "data_products/track_start.hpp" -#include "form/form.hpp" +#include "form/form_reader.hpp" +#include "form/form_writer.hpp" #endif // TEST_FORM_TEST_HELPERS_HPP diff --git a/test/form/test_utils.hpp b/test/form/test_utils.hpp index 88ace4b8..fdf57ac0 100644 --- a/test/form/test_utils.hpp +++ b/test/form/test_utils.hpp @@ -4,7 +4,8 @@ #define TEST_FORM_TEST_UTILS_HPP #include "storage/istorage.hpp" -#include "storage/storage_associative_container.hpp" +#include "storage/storage_associative_read_container.hpp" +#include "storage/storage_associative_write_container.hpp" #include "util/factories.hpp" #include "TClass.h" @@ -31,25 +32,25 @@ namespace form::test { return testTreeName + "/" + getTypeName(); } - inline std::vector> doWrite( + inline std::vector> doWrite( std::shared_ptr& /*file*/, int const /*technology*/, - std::shared_ptr& /*parent*/) + std::shared_ptr& /*parent*/) { - return std::vector>(); + return std::vector>(); } template - inline std::vector> doWrite( + inline std::vector> doWrite( std::shared_ptr& file, int const technology, - std::shared_ptr& parent, + std::shared_ptr& parent, PROD& prod, PRODS&... prods) { auto const branchName = makeTestBranchName(); - auto container = createContainer(technology, branchName); - auto assoc = dynamic_pointer_cast(container); + auto container = createWriteContainer(technology, branchName); + auto assoc = dynamic_pointer_cast(container); if (assoc) { assoc->setParent(parent); } @@ -66,7 +67,7 @@ namespace form::test { inline void write(int const technology, PRODS&... prods) { auto file = createFile(technology, testFileName.c_str(), 'o'); - auto parent = createAssociation(technology, testTreeName); + auto parent = createWriteAssociation(technology, testTreeName); parent->setFile(file); parent->setupWrite(); @@ -78,10 +79,10 @@ namespace form::test { template inline std::unique_ptr doRead(std::shared_ptr& file, int const technology, - std::shared_ptr& parent) + std::shared_ptr& parent) { - auto container = createContainer(technology, makeTestBranchName()); - auto assoc = dynamic_pointer_cast(container); + auto container = createReadContainer(technology, makeTestBranchName()); + auto assoc = dynamic_pointer_cast(container); if (assoc) { assoc->setParent(parent); } @@ -99,7 +100,7 @@ namespace form::test { inline std::tuple...> read(int const technology) { auto file = createFile(technology, testFileName, 'i'); - auto parent = createAssociation(technology, testTreeName); + auto parent = createReadAssociation(technology, testTreeName); parent->setFile(file); return std::make_tuple(doRead(file, technology, parent)...); diff --git a/test/form/writer.cpp b/test/form/writer.cpp index a1accc4a..eafd7f8f 100644 --- a/test/form/writer.cpp +++ b/test/form/writer.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2025 ... #include "data_products/track_start.hpp" -#include "form/form.hpp" +#include "form/form_writer.hpp" #include "form/technology.hpp" #include "test_helpers.hpp" #include "toy_tracker.hpp" @@ -54,7 +54,7 @@ int main(int argc, char** argv) tech_config.container_settings[form::technology::ROOT_RNTUPLE]["Toy_Tracker/trackStartPoints"] .emplace_back("force_streamer_field", "true"); - form::experimental::form_interface form(output_config, tech_config); + form::experimental::form_writer_interface form(output_config, tech_config); ToyTracker tracker(4 * 1024);