From 2ffcd6b9d43fc32d795f1f6c7fff6a586a119cec Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Fri, 27 Feb 2026 13:33:15 -0600 Subject: [PATCH 01/15] Split all FORM components into reading and writing components. form_interface is now split between form_reader_interface and form_writer_interface for example. --- form/CMakeLists.txt | 6 + form/form/CMakeLists.txt | 2 +- form/form/form_reader.cpp | 37 +++++ form/form/form_reader.hpp | 34 ++++ form/form/{form.cpp => form_writer.cpp} | 32 ++-- form/form/{form.hpp => form_writer.hpp} | 28 ++-- form/form/product_with_name.hpp | 16 ++ form/form_module.cpp | 6 +- form/persistence/CMakeLists.txt | 2 +- form/persistence/ipersistence_reader.hpp | 40 +++++ form/persistence/ipersistence_writer.hpp | 42 +++++ form/persistence/persistence_reader.cpp | 66 ++++++++ form/persistence/persistence_reader.hpp | 52 +++++++ form/persistence/persistence_utils.cpp | 28 ++++ form/persistence/persistence_utils.hpp | 25 +++ form/persistence/persistence_writer.cpp | 79 ++++++++++ form/persistence/persistence_writer.hpp | 52 +++++++ form/root_storage/CMakeLists.txt | 4 +- form/root_storage/demangle_name.cpp | 18 +++ form/root_storage/demangle_name.hpp | 7 + .../root_rfield_read_container.cpp | 112 ++++++++++++++ .../root_rfield_read_container.hpp | 43 ++++++ .../root_rfield_write_container.cpp | 115 ++++++++++++++ .../root_rfield_write_container.hpp | 37 +++++ .../root_rntuple_read_container.cpp | 29 ++++ .../root_rntuple_read_container.hpp | 23 +++ .../root_rntuple_write_container.cpp | 43 ++++++ .../root_rntuple_write_container.hpp | 43 ++++++ .../root_tbranch_read_container.cpp | 111 ++++++++++++++ .../root_tbranch_read_container.hpp | 37 +++++ .../root_tbranch_write_container.cpp | 119 ++++++++++++++ .../root_tbranch_write_container.hpp | 39 +++++ .../root_ttree_read_container.cpp | 40 +++++ .../root_ttree_read_container.hpp | 36 +++++ .../root_ttree_write_container.cpp | 63 ++++++++ .../root_ttree_write_container.hpp | 38 +++++ form/storage/CMakeLists.txt | 12 +- form/storage/istorage.hpp | 51 ++++-- .../storage_associative_read_container.cpp | 29 ++++ .../storage_associative_read_container.hpp | 31 ++++ .../storage_associative_write_container.cpp | 29 ++++ .../storage_associative_write_container.hpp | 31 ++++ form/storage/storage_read_association.cpp | 23 +++ form/storage/storage_read_association.hpp | 22 +++ form/storage/storage_read_container.cpp | 24 +++ form/storage/storage_read_container.hpp | 32 ++++ form/storage/storage_reader.cpp | 78 ++++++++++ form/storage/storage_reader.hpp | 43 ++++++ form/storage/storage_util.hpp | 20 +++ form/storage/storage_write_association.cpp | 23 +++ form/storage/storage_write_association.hpp | 22 +++ form/storage/storage_write_container.cpp | 25 +++ form/storage/storage_write_container.hpp | 34 ++++ form/storage/storage_writer.cpp | 85 ++++++++++ form/storage/storage_writer.hpp | 39 +++++ form/util/factories.hpp | 91 +++++++++-- test/form/form_basics_test.cpp | 145 +++++++++++++----- test/form/reader.cpp | 4 +- test/form/test_helpers.hpp | 3 +- test/form/writer.cpp | 4 +- 60 files changed, 2278 insertions(+), 126 deletions(-) create mode 100644 form/form/form_reader.cpp create mode 100644 form/form/form_reader.hpp rename form/form/{form.cpp => form_writer.cpp} (62%) rename form/form/{form.hpp => form_writer.hpp} (52%) create mode 100644 form/form/product_with_name.hpp create mode 100644 form/persistence/ipersistence_reader.hpp create mode 100644 form/persistence/ipersistence_writer.hpp create mode 100644 form/persistence/persistence_reader.cpp create mode 100644 form/persistence/persistence_reader.hpp create mode 100644 form/persistence/persistence_utils.cpp create mode 100644 form/persistence/persistence_utils.hpp create mode 100644 form/persistence/persistence_writer.cpp create mode 100644 form/persistence/persistence_writer.hpp create mode 100644 form/root_storage/demangle_name.cpp create mode 100644 form/root_storage/demangle_name.hpp create mode 100644 form/root_storage/root_rfield_read_container.cpp create mode 100644 form/root_storage/root_rfield_read_container.hpp create mode 100644 form/root_storage/root_rfield_write_container.cpp create mode 100644 form/root_storage/root_rfield_write_container.hpp create mode 100644 form/root_storage/root_rntuple_read_container.cpp create mode 100644 form/root_storage/root_rntuple_read_container.hpp create mode 100644 form/root_storage/root_rntuple_write_container.cpp create mode 100644 form/root_storage/root_rntuple_write_container.hpp create mode 100644 form/root_storage/root_tbranch_read_container.cpp create mode 100644 form/root_storage/root_tbranch_read_container.hpp create mode 100644 form/root_storage/root_tbranch_write_container.cpp create mode 100644 form/root_storage/root_tbranch_write_container.hpp create mode 100644 form/root_storage/root_ttree_read_container.cpp create mode 100644 form/root_storage/root_ttree_read_container.hpp create mode 100644 form/root_storage/root_ttree_write_container.cpp create mode 100644 form/root_storage/root_ttree_write_container.hpp create mode 100644 form/storage/storage_associative_read_container.cpp create mode 100644 form/storage/storage_associative_read_container.hpp create mode 100644 form/storage/storage_associative_write_container.cpp create mode 100644 form/storage/storage_associative_write_container.hpp create mode 100644 form/storage/storage_read_association.cpp create mode 100644 form/storage/storage_read_association.hpp create mode 100644 form/storage/storage_read_container.cpp create mode 100644 form/storage/storage_read_container.hpp create mode 100644 form/storage/storage_reader.cpp create mode 100644 form/storage/storage_reader.hpp create mode 100644 form/storage/storage_util.hpp create mode 100644 form/storage/storage_write_association.cpp create mode 100644 form/storage/storage_write_association.hpp create mode 100644 form/storage/storage_write_container.cpp create mode 100644 form/storage/storage_write_container.hpp create mode 100644 form/storage/storage_writer.cpp create mode 100644 form/storage/storage_writer.hpp diff --git a/form/CMakeLists.txt b/form/CMakeLists.txt index ddbe7652..a6ea5602 100644 --- a/form/CMakeLists.txt +++ b/form/CMakeLists.txt @@ -18,6 +18,12 @@ if(FORM_USE_ROOT_STORAGE) add_definitions(-DUSE_ROOT_STORAGE) endif() +# RNTuple Storage toggle +option(FORM_USE_RNTUPLE_STORAGE "Enable RNTuple for ROOT Storage" ON) +if(FORM_USE_RNTUPLE_STORAGE) + add_definitions(-DUSE_RNTUPLE_STORAGE) +endif() + # Add sub directories add_subdirectory(form) add_subdirectory(core) 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_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..679775b7 --- /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.hpp b/form/form/form_writer.hpp similarity index 52% rename from form/form/form.hpp rename to form/form/form_writer.hpp index d01485bc..a19c6028 100644 --- a/form/form/form.hpp +++ b/form/form/form_writer.hpp @@ -1,30 +1,24 @@ // Copyright (C) 2025 ... -#ifndef FORM_FORM_FORM_HPP -#define FORM_FORM_FORM_HPP +#ifndef FORM_FORM_FORM_WRITER_HPP +#define FORM_FORM_FORM_WRITER_HPP #include "form/config.hpp" -#include "persistence/ipersistence.hpp" +#include "form/product_with_name.hpp" +#include "persistence/ipersistence_writer.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 { + class form_writer_interface { public: - form_interface(config::output_item_config const& output_config, + form_writer_interface(config::output_item_config const& output_config, config::tech_setting_config const& tech_config); - ~form_interface() = default; + ~form_writer_interface() = default; void write(std::string const& creator, std::string const& segment_id, @@ -34,14 +28,10 @@ namespace form::experimental { 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::unique_ptr m_pers; std::map m_product_to_config; }; } -#endif // FORM_FORM_FORM_HPP +#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..2d8343bf --- /dev/null +++ b/form/form/product_with_name.hpp @@ -0,0 +1,16 @@ +#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 7c884169..14238fd9 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,7 @@ 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 +102,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_writer.hpp b/form/persistence/ipersistence_writer.hpp new file mode 100644 index 00000000..1836cc1a --- /dev/null +++ b/form/persistence/ipersistence_writer.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_PERSISTENCE_IPERSISTENCE_WRITER_HPP +#define FORM_PERSISTENCE_IPERSISTENCE_WRITER_HPP + +#include +#include +#include +#include + +namespace form::experimental::config { + class output_item_config; + struct tech_setting_config; +} + +namespace form::detail::experimental { + + class IPersistenceWriter { + public: + IPersistenceWriter() {}; + virtual ~IPersistenceWriter() = 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 createContainers(std::string const& creator, + std::map const& products) = 0; + virtual void registerWrite(std::string const& creator, + std::string const& label, + void const* data, + std::type_info const& type) = 0; + virtual void commitOutput(std::string const& creator, std::string const& id) = 0; + }; + + std::unique_ptr createPersistenceWriter(); + +} // namespace form::detail::experimental + +#endif // FORM_PERSISTENCE_IPERSISTENCE_WRITER_HPP diff --git a/form/persistence/persistence_reader.cpp b/form/persistence/persistence_reader.cpp new file mode 100644 index 00000000..5d00c33e --- /dev/null +++ b/form/persistence/persistence_reader.cpp @@ -0,0 +1,66 @@ +// 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..6371f759 --- /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..b915947d --- /dev/null +++ b/form/persistence/persistence_utils.hpp @@ -0,0 +1,25 @@ +#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..cb3b2ca8 --- /dev/null +++ b/form/persistence/persistence_writer.cpp @@ -0,0 +1,79 @@ +// 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_writer.hpp b/form/persistence/persistence_writer.hpp new file mode 100644 index 00000000..7caee8aa --- /dev/null +++ b/form/persistence/persistence_writer.hpp @@ -0,0 +1,52 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_PERSISTENCE_PERSISTENCE_WRITER_HPP +#define FORM_PERSISTENCE_PERSISTENCE_WRITER_HPP + +#include "ipersistence_writer.hpp" + +#include "core/placement.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 PersistenceWriter : public IPersistenceWriter { + public: + PersistenceWriter(); + ~PersistenceWriter() = 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 createContainers(std::string const& creator, + std::map const& products) override; + void registerWrite(std::string const& creator, + std::string const& label, + void const* data, + std::type_info const& type) override; + void commitOutput(std::string const& creator, std::string const& id) override; + + private: + std::unique_ptr getPlacement(std::string const& creator, std::string const& label); + + 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_WRITER_HPP diff --git a/form/root_storage/CMakeLists.txt b/form/root_storage/CMakeLists.txt index f29b6cd8..3c645221 100644 --- a/form/root_storage/CMakeLists.txt +++ b/form/root_storage/CMakeLists.txt @@ -4,7 +4,7 @@ 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 root_rfield_read_container.cpp root_rfield_write_container.cpp root_rntuple_read_container.cpp root_rntuple_write_container.cpp demangle_name.cpp) # Link the ROOT libraries -target_link_libraries(root_storage PUBLIC ROOT::Core ROOT::RIO ROOT::Tree storage) +target_link_libraries(root_storage PUBLIC ROOT::Core ROOT::RIO ROOT::Tree ROOT::ROOTNTuple storage) 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_rfield_read_container.cpp b/form/root_storage/root_rfield_read_container.cpp new file mode 100644 index 00000000..65dd4463 --- /dev/null +++ b/form/root_storage/root_rfield_read_container.cpp @@ -0,0 +1,112 @@ +//A ROOT_RField_Read_Container reads data products of a single type from vectors stored in an RNTuple field on disk. + +#include "root_rfield_read_container.hpp" +#include "root_rntuple_read_container.hpp" +#include "root_tfile.hpp" +#include "demangle_name.hpp" + +#include "ROOT/RNTupleReader.hxx" +#include "ROOT/RNTupleView.hxx" +#include "TFile.h" + +#include + +namespace form::detail::experimental { + ROOT_RField_Read_ContainerImp::ROOT_RField_Read_ContainerImp(std::string const& name) : + Storage_Associative_Read_Container(name), m_force_streamer_field(false) + { + } + + ROOT_RField_Read_ContainerImp::~ROOT_RField_Read_ContainerImp() {} + + void ROOT_RField_Read_ContainerImp::setAttribute(std::string const& key, std::string const& /*value*/) + { + if (key == "force_streamer_field") { + m_force_streamer_field = true; + } else { + throw std::runtime_error("ROOT_RField_Read_ContainerImp supports some attributes, but not " + key); + } + } + + void ROOT_RField_Read_ContainerImp::setFile(std::shared_ptr file) + { + Storage_Read_Container::setFile(file); + + auto form_root_file = dynamic_cast(file.get()); + if (form_root_file) { + m_tfile = form_root_file->getTFile(); + } else { + throw std::runtime_error( + "ROOT_RField_Read_ContainerImp::setFile failed to convert an IStorage_File to a ROOT_TFileImp. " + "ROOT_RField_Read_ContainerImp only works with TFiles."); + } + + if (!m_tfile) { + throw std::runtime_error( + "ROOT_RField_Read_ContainerImp::setFile failed to get a TFile from a ROOT_TFileImp"); + } + + return; + } + + void ROOT_RField_Read_ContainerImp::setParent(std::shared_ptr parent) + { + this->Storage_Associative_Read_Container::setParent(parent); + } + + bool ROOT_RField_Read_ContainerImp::read(int id, void const** data, std::type_info const& type) + { + //Connect to file at the last possible moment at the cost of a little run-time branching + if (!m_view) { + if (!m_reader) { //First time this RNTuple is read this job + if (!m_tfile) { + throw std::runtime_error( + "ROOT_RField_Read_ContainerImp::read No file loaded to read from on first read() call!"); + } + + m_reader = ROOT::RNTupleReader::Open(top_name(), m_tfile->GetName()); + } + + try { + m_view = + std::make_unique>(m_reader->GetView(col_name(), nullptr, type)); + } catch (const ROOT::RException& e) { + //RNTupleView will fail to create a field for fields written in streamer mode or for which type does not match the field's type on disk. Passing an empty string for type forces it to create the same type of field as the object on disk. Do this to handle streamer fields, then perform our own type check. + m_view = + std::make_unique>(m_reader->GetView(col_name(), nullptr, "")); + //TClass takes the "std::" off of "std::vector<>" when RNTuple's on-disk format doesn't. Convert RNTuple's type name to match TClass for manual type check because our dictionary of choice will likely be the same as TClass. + if (strcmp(TClass::GetClass(m_view->GetField().GetTypeName().c_str())->GetName(), + TClass::GetClass(type)->GetName())) { + throw std::runtime_error( + "ROOT_RField_containerImp::read type " + DemangleName(type) + " requested for a field named " + + col_name() + + " does not match the type in the file: " + m_view->GetField().GetTypeName()); + } + } + } + + if (id >= (int)m_reader->GetNEntries()) + return false; + + //Using RNTupleView<> to read instead of reusing REntry gives us full schema evolution support: the ROOT feature that lets us read files with an old class version into a new class version's memory. + auto buffer = m_view->GetField().CreateObject(); //PHLEX gets ownership of this memory + if (!buffer) { + throw std::runtime_error("ROOT_RField_Read_Container::read failed to create an object of type " + + m_view->GetField().GetTypeName() + + ". Maybe the type name for this read() (" + DemangleName(type) + + ") doesn't match the type from the first read() (" + + m_view->GetField().GetTypeName() + ")?"); + } + + m_view->BindRawPtr(buffer.get()); + try { + (*m_view)(id); + } catch (const ROOT::RException& e) { + throw std::runtime_error("ROOT_RField_Read_ContainerImp::read got a ROOT exception: " + + std::string(e.what())); + } + *data = buffer.release(); + + return true; + } +} diff --git a/form/root_storage/root_rfield_read_container.hpp b/form/root_storage/root_rfield_read_container.hpp new file mode 100644 index 00000000..7cb5ece2 --- /dev/null +++ b/form/root_storage/root_rfield_read_container.hpp @@ -0,0 +1,43 @@ +//A ROOT_RField_Read_Container is a Storage_Read_Container that uses a shared RNTuple to read and write data products to and from disk. A single Storage_Read_Container encapsulates the location where a collection of data products of a single type is stored. + +#ifndef FORM_ROOT_STORAGE_ROOT_RFIELD_READ_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_RFIELD_READ_CONTAINER_HPP + +#include "storage/storage_associative_read_container.hpp" + +#include +#include + +class TFile; + +namespace ROOT { + class RNTupleReader; + template + class RNTupleView; + class RNTupleView; +} + +namespace form::detail::experimental { + class ROOT_RNTuple_Read_ContainerImp; + + class ROOT_RField_Read_ContainerImp : public Storage_Associative_Read_Container { + public: + ROOT_RField_Read_ContainerImp(std::string const& name); + ~ROOT_RField_Read_ContainerImp(); + + void setAttribute(std::string const& key, std::string const& value) override; + + void setFile(std::shared_ptr file) override; + void setParent(std::shared_ptr const parent) override; + bool read(int id, void const** data, std::type_info const& type) override; + + private: + std::shared_ptr m_tfile; + std::unique_ptr m_reader; + std::unique_ptr> m_view; + + bool m_force_streamer_field; + }; +} + +#endif // FORM_ROOT_STORAGE_ROOT_RFIELD_READ_CONTAINER_HPP diff --git a/form/root_storage/root_rfield_write_container.cpp b/form/root_storage/root_rfield_write_container.cpp new file mode 100644 index 00000000..1581ad39 --- /dev/null +++ b/form/root_storage/root_rfield_write_container.cpp @@ -0,0 +1,115 @@ +//A ROOT_RField_Write_Container writes data products of a single type from vectors stored in an RNTuple field on disk. + +#include "root_rfield_write_container.hpp" +#include "root_rntuple_write_container.hpp" +#include "root_tfile.hpp" +#include "demangle_name.hpp" + +#include "ROOT/RNTupleWriter.hxx" +#include "TFile.h" + +#include + +namespace form::detail::experimental { + ROOT_RField_Write_ContainerImp::ROOT_RField_Write_ContainerImp(std::string const& name) : + Storage_Associative_Write_Container(name), m_force_streamer_field(false) + { + } + + ROOT_RField_Write_ContainerImp::~ROOT_RField_Write_ContainerImp() {} + + void ROOT_RField_Write_ContainerImp::setAttribute(std::string const& key, std::string const& /*value*/) + { + if (key == "force_streamer_field") { + m_force_streamer_field = true; + } else { + throw std::runtime_error("ROOT_RField_Write_ContainerImp supports some attributes, but not " + key); + } + } + + void ROOT_RField_Write_ContainerImp::setFile(std::shared_ptr file) + { + Storage_Write_Container::setFile(file); + + auto form_root_file = dynamic_cast(file.get()); + if (form_root_file) { + m_tfile = form_root_file->getTFile(); + } else { + throw std::runtime_error( + "ROOT_RField_Write_ContainerImp::setFile failed to convert an IStorage_File to a ROOT_TFileImp. " + "ROOT_RField_Write_ContainerImp only works with TFiles."); + } + + if (!m_tfile) { + throw std::runtime_error( + "ROOT_RField_Write_ContainerImp::setFile failed to get a TFile from a ROOT_TFileImp"); + } + + return; + } + + void ROOT_RField_Write_ContainerImp::setParent(std::shared_ptr parent) + { + this->Storage_Associative_Write_Container::setParent(parent); + auto parentDerived = dynamic_pointer_cast(parent); + if (!parentDerived) { + throw std::runtime_error( + "ROOT_RField_Write_ContainerImp::setParent parent is not a ROOT_RNTuple_Write_ContainerImp! Something " + "may be wrong with how Storage works."); + } + m_rntuple_parent = parentDerived; + } + + void ROOT_RField_Write_ContainerImp::fill(void const* data) + { + if (!m_rntuple_parent->m_writer) { + if (!m_tfile) { + throw std::runtime_error( + "ROOT_RField_Write_ContainerImp::fill No file loaded to write to on first fill() call"); + } + + m_rntuple_parent->m_writer = + ROOT::RNTupleWriter::Append(std::move(m_rntuple_parent->m_model), top_name(), *m_tfile); + m_rntuple_parent->m_entry = m_rntuple_parent->m_writer->CreateRawPtrWriteEntry(); + } + m_rntuple_parent->m_entry->BindRawPtr(col_name(), data); + } + + void ROOT_RField_Write_ContainerImp::commit() + { + if (!m_rntuple_parent->m_entry) { + throw std::runtime_error("ROOT_RField_Write_ContainerImp::commit No RRawPtrWriteEntry set up. " + "You may have called commit() without calling setupWrite() first."); + } + if (!m_rntuple_parent->m_writer) { + throw std::runtime_error("ROOT_RField_Write_ContainerImp::commit No RNTupleWriter set up. " + "You may have called commit() without calling setupWrite() first."); + } + m_rntuple_parent->m_writer->Fill(*m_rntuple_parent->m_entry); + } + + void ROOT_RField_Write_ContainerImp::setupWrite(std::type_info const& type) + { + auto const& type_name = DemangleName(type); + std::unique_ptr field; + + if (m_force_streamer_field) { + field = std::make_unique(col_name(), type_name); + } else { + auto result = ROOT::RFieldBase::Create(col_name(), type_name); + if (result) { + field = result.Unwrap(); + } else { + std::cerr + << "ROOT_RField_Write_ContainerImp::setupWrite could not create column-wise storage for " + << type_name + << ". This class is probably using something obsolete like TLorentzVector. Storing it " + "in streamer mode to keep the application going." + << std::endl; + field = std::make_unique(col_name(), type_name); + } + } + + m_rntuple_parent->m_model->AddField(std::move(field)); + } +} diff --git a/form/root_storage/root_rfield_write_container.hpp b/form/root_storage/root_rfield_write_container.hpp new file mode 100644 index 00000000..53f75264 --- /dev/null +++ b/form/root_storage/root_rfield_write_container.hpp @@ -0,0 +1,37 @@ +//A ROOT_RField_Write_Container is a Storage_Write_Container that uses a shared RNTuple to write data products to disk. A single Storage_Write_Container encapsulates the location where a collection of data products of a single type is stored. + +#ifndef FORM_ROOT_STORAGE_ROOT_RFIELD_WRITE_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_RFIELD_WRITE_CONTAINER_HPP + +#include "storage/storage_associative_write_container.hpp" + +#include +#include + +class TFile; + +namespace form::detail::experimental { + class ROOT_RNTuple_Write_ContainerImp; + + class ROOT_RField_Write_ContainerImp : public Storage_Associative_Write_Container { + public: + ROOT_RField_Write_ContainerImp(std::string const& name); + ~ROOT_RField_Write_ContainerImp(); + + void setAttribute(std::string const& key, std::string const& value) override; + + void setFile(std::shared_ptr file) override; + void setupWrite(std::type_info const& type) override; + void setParent(std::shared_ptr const parent) override; + void fill(void const* data) override; + void commit() override; + + private: + std::shared_ptr m_tfile; + std::shared_ptr m_rntuple_parent; + + bool m_force_streamer_field; + }; +} + +#endif // FORM_ROOT_STORAGE_ROOT_RFIELD_WRITE_CONTAINER_HPP diff --git a/form/root_storage/root_rntuple_read_container.cpp b/form/root_storage/root_rntuple_read_container.cpp new file mode 100644 index 00000000..2de81b9f --- /dev/null +++ b/form/root_storage/root_rntuple_read_container.cpp @@ -0,0 +1,29 @@ +//A ROOT_RNTuple_Read_Container reads data products of a single type from vectors stored in an RNTuple field on disk. + +#include "root_rntuple_read_container.hpp" +#include "root_tfile.hpp" + +#include "ROOT/RNTupleReader.hxx" +#include "ROOT/RNTupleView.hxx" +#include "ROOT/RNTupleWriter.hxx" +#include "TFile.h" + +#include + +namespace form::detail::experimental { + ROOT_RNTuple_Read_ContainerImp::ROOT_RNTuple_Read_ContainerImp(std::string const& name) : + Storage_Read_Association(name) + { + } + + void ROOT_RNTuple_Read_ContainerImp::setFile(std::shared_ptr file) + { + Storage_Read_Container::setFile(file); + return; + } + + bool ROOT_RNTuple_Read_ContainerImp::read(int /*id*/, void const** /*data*/, std::type_info const& /*type*/) + { + throw std::runtime_error("ROOT_RNTuple_CotnainerImp::read not implemented"); + } +} diff --git a/form/root_storage/root_rntuple_read_container.hpp b/form/root_storage/root_rntuple_read_container.hpp new file mode 100644 index 00000000..7be15106 --- /dev/null +++ b/form/root_storage/root_rntuple_read_container.hpp @@ -0,0 +1,23 @@ +//A ROOT_RNTuple_ContainerImp is a Storage_Association (and therefore a Storage_Container) that coordinates the file accesses shared by several ROOT_RField_ContainerImps. It only coordinates RNTuple-specific file-based resources and doesn't actually implement write() or read() for example. This matches the early design of the TTree associative container. + +#ifndef FORM_ROOT_STORAGE_ROOT_RNTUPLE_READ_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_RNTUPLE_READ_CONTAINER_HPP + +#include "storage/storage_read_association.hpp" + +#include +#include + +namespace form::detail::experimental { + + class ROOT_RNTuple_Read_ContainerImp : public Storage_Read_Association { + public: + ROOT_RNTuple_Read_ContainerImp(std::string const& name); + ~ROOT_RNTuple_Read_ContainerImp() = default; + + void setFile(std::shared_ptr file) override; + bool read(int id, void const** data, std::type_info const& type) override; + }; +} + +#endif // FORM_ROOT_STORAGE_ROOT_RNTUPLE_READ_CONTAINER_HPP diff --git a/form/root_storage/root_rntuple_write_container.cpp b/form/root_storage/root_rntuple_write_container.cpp new file mode 100644 index 00000000..09d861da --- /dev/null +++ b/form/root_storage/root_rntuple_write_container.cpp @@ -0,0 +1,43 @@ +//A ROOT_RNTuple_Write_Container reads data products of a single type from vectors stored in an RNTuple field on disk. + +#include "root_rntuple_write_container.hpp" +#include "root_tfile.hpp" + +#include "ROOT/RNTupleReader.hxx" +#include "ROOT/RNTupleView.hxx" +#include "ROOT/RNTupleWriter.hxx" +#include "TFile.h" + +#include + +namespace form::detail::experimental { + ROOT_RNTuple_Write_ContainerImp::ROOT_RNTuple_Write_ContainerImp(std::string const& name) : + Storage_Write_Association(name), m_model(ROOT::RNTupleModel::Create()) + { + } + + ROOT_RNTuple_Write_ContainerImp::~ROOT_RNTuple_Write_ContainerImp() + { + if (m_writer) { + m_writer->CommitDataset(); + } + } + + void ROOT_RNTuple_Write_ContainerImp::setFile(std::shared_ptr file) + { + Storage_Write_Container::setFile(file); + return; + } + + void ROOT_RNTuple_Write_ContainerImp::fill(void const* /*data*/) + { + throw std::runtime_error("ROOT_RNTuple_Write_ContainerImp::fill not implemented"); + } + + void ROOT_RNTuple_Write_ContainerImp::commit() + { + throw std::runtime_error("ROOT_RNTuple_Write_ContainerImp::commit not implemented"); + } + + void ROOT_RNTuple_Write_ContainerImp::setupWrite(std::type_info const& /*type*/) { return; } +} diff --git a/form/root_storage/root_rntuple_write_container.hpp b/form/root_storage/root_rntuple_write_container.hpp new file mode 100644 index 00000000..4ba58dcc --- /dev/null +++ b/form/root_storage/root_rntuple_write_container.hpp @@ -0,0 +1,43 @@ +//A ROOT_RNTuple_ContainerImp is a Storage_Write_Association (and therefore a Storage_Container) that coordinates the file accesses shared by several ROOT_RField_ContainerImps. It only coordinates RNTuple-specific file-based resources and doesn't actually implement write() or read() for example. This matches the early design of the TTree associative container. + +#ifndef FORM_ROOT_STORAGE_ROOT_RNTUPLE_WRITE_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_RNTUPLE_WRITE_CONTAINER_HPP + +#include "storage/storage_write_association.hpp" + +#include +#include + +class TFile; + +namespace ROOT { + class RNTupleWriter; + class RNTupleModel; + + namespace Experimental { + namespace Detail { + class RRawPtrWriteEntry; + } + } +} + +namespace form::detail::experimental { + + class ROOT_RNTuple_Write_ContainerImp : public Storage_Write_Association { + public: + ROOT_RNTuple_Write_ContainerImp(std::string const& name); + ~ROOT_RNTuple_Write_ContainerImp(); + + void setFile(std::shared_ptr file) override; + void setupWrite(std::type_info const& type) override; + void fill(void const* data) override; + void commit() override; + + //State shared by ROOT_RField_ContainerImps + std::unique_ptr m_writer; + std::unique_ptr m_model; + std::unique_ptr m_entry; + }; +} + +#endif // FORM_ROOT_STORAGE_ROOT_RNTUPLE_WRITE_CONTAINER_HPP 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..11b4e926 --- /dev/null +++ b/form/root_storage/root_tbranch_read_container.cpp @@ -0,0 +1,111 @@ +// Copyright (C) 2025 ... + +#include "root_tbranch_read_container.hpp" +#include "root_tfile.hpp" +#include "root_ttree_read_container.hpp" +#include "demangle_name.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::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_Read_ContainerImp accepts some attributes, but not " + key); + } +} + +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..b9ccadff --- /dev/null +++ b/form/root_storage/root_tbranch_read_container.hpp @@ -0,0 +1,37 @@ +// 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 setAttribute(std::string const& key, std::string const& value) override; + + 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..0fcd27a9 --- /dev/null +++ b/form/root_storage/root_tbranch_write_container.cpp @@ -0,0 +1,119 @@ +// Copyright (C) 2025 ... + +#include "root_tbranch_write_container.hpp" +#include "root_tfile.hpp" +#include "root_ttree_write_container.hpp" +#include "demangle_name.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_write_container.hpp b/form/root_storage/root_tbranch_write_container.hpp new file mode 100644 index 00000000..0e2280c1 --- /dev/null +++ b/form/root_storage/root_tbranch_write_container.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_ROOT_STORAGE_ROOT_TBRANCH_WRITE_CONTAINER_HPP +#define FORM_ROOT_STORAGE_ROOT_TBRANCH_WRITE_CONTAINER_HPP + +#include "storage/storage_associative_write_container.hpp" + +#include +#include + +class TFile; +class TTree; +class TBranch; + +namespace form::detail::experimental { + + class ROOT_TBranch_Write_ContainerImp : public Storage_Associative_Write_Container { + public: + 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 setupWrite(std::type_info const& type = typeid(void)) override; + void fill(void const* data) override; + void commit() override; + + private: + std::shared_ptr m_tfile; + TTree* m_tree; + TBranch* m_branch; + }; + +} // namespace form::detail::experimental + +#endif // FORM_ROOT_STORAGE_ROOT_TBRANCH_WRITE_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..9924b0b0 --- /dev/null +++ b/form/root_storage/root_ttree_read_container.cpp @@ -0,0 +1,40 @@ +// 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() +{ + if (m_tree != nullptr) { + m_tree->Write(); + delete m_tree; + } +} + +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..d0f6e853 --- /dev/null +++ b/form/root_storage/root_ttree_write_container.cpp @@ -0,0 +1,63 @@ +// 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..4bdd4645 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,26 @@ 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_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..45dced75 --- /dev/null +++ b/form/storage/storage_associative_write_container.cpp @@ -0,0 +1,29 @@ +// 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_read_association.cpp b/form/storage/storage_read_association.cpp new file mode 100644 index 00000000..1a07ffdd --- /dev/null +++ b/form/storage/storage_read_association.cpp @@ -0,0 +1,23 @@ +// 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..6a7b4d97 --- /dev/null +++ b/form/storage/storage_read_container.cpp @@ -0,0 +1,24 @@ +// 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..dd99bf42 --- /dev/null +++ b/form/storage/storage_reader.cpp @@ -0,0 +1,78 @@ +// Copyright (C) 2025 ... + +#include "storage_reader.hpp" +#include "storage_read_association.hpp" +#include "storage_associative_read_container.hpp" +#include "storage_file.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_reader.hpp b/form/storage/storage_reader.hpp new file mode 100644 index 00000000..f1f57776 --- /dev/null +++ b/form/storage/storage_reader.hpp @@ -0,0 +1,43 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_READER_HPP +#define FORM_STORAGE_STORAGE_READER_HPP + +#include "istorage.hpp" +#include "storage_util.hpp" + +#include +#include +#include +#include +#include // for std::pair + +namespace form::detail::experimental { + + class StorageReader : public IStorageReader { + public: + StorageReader() = default; + ~StorageReader() = default; + + using table_t = form::experimental::config::tech_setting_config::table_t; + + int getIndex(Token const& token, + std::string const& id, + form::experimental::config::tech_setting_config const& settings) override; + void readContainer(Token const& token, + void const** data, + std::type_info const& type, + form::experimental::config::tech_setting_config const& settings) override; + + private: + std::map> m_files; + std::unordered_map, + std::shared_ptr, + pair_hash> + m_read_containers; + std::map> m_indexMaps; + }; + +} // namespace form::detail::experimental + +#endif // FORM_STORAGE_STORAGE_READER_HPP diff --git a/form/storage/storage_util.hpp b/form/storage/storage_util.hpp new file mode 100644 index 00000000..e8b2ca41 --- /dev/null +++ b/form/storage/storage_util.hpp @@ -0,0 +1,20 @@ +#include + +#ifndef FORM_STORAGE_STORAGE_UTIL_HPP +#define FORM_STORAGE_STORAGE_UTIL_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_UTIL_HPP diff --git a/form/storage/storage_write_association.cpp b/form/storage/storage_write_association.cpp new file mode 100644 index 00000000..44a83c04 --- /dev/null +++ b/form/storage/storage_write_association.cpp @@ -0,0 +1,23 @@ +// 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..70624b08 --- /dev/null +++ b/form/storage/storage_write_container.cpp @@ -0,0 +1,25 @@ +// 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_write_container.hpp b/form/storage/storage_write_container.hpp new file mode 100644 index 00000000..a13e0200 --- /dev/null +++ b/form/storage/storage_write_container.hpp @@ -0,0 +1,34 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_WRITE_CONTAINER_HPP +#define FORM_STORAGE_STORAGE_WRITE_CONTAINER_HPP + +#include "istorage.hpp" + +#include +#include + +namespace form::detail::experimental { + + class Storage_Write_Container : public IStorage_Write_Container { + public: + Storage_Write_Container(std::string const& name); + ~Storage_Write_Container() = default; + + std::string const& name() override; + + 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; + + 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_WRITE_CONTAINER_HPP diff --git a/form/storage/storage_writer.cpp b/form/storage/storage_writer.cpp new file mode 100644 index 00000000..1ae9a2c9 --- /dev/null +++ b/form/storage/storage_writer.cpp @@ -0,0 +1,85 @@ +// Copyright (C) 2025 ... + +#include "storage_writer.hpp" +#include "storage_write_association.hpp" +#include "storage_associative_write_container.hpp" +#include "storage_file.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..08208896 --- /dev/null +++ b/form/storage/storage_writer.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2025 ... + +#ifndef FORM_STORAGE_STORAGE_WRITER_HPP +#define FORM_STORAGE_STORAGE_WRITER_HPP + +#include "istorage.hpp" +#include "storage_util.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..7fdd5c9c 100644 --- a/form/util/factories.hpp +++ b/form/util/factories.hpp @@ -6,14 +6,25 @@ #include "form/technology.hpp" #include "storage/istorage.hpp" -#include "storage/storage_association.hpp" -#include "storage/storage_container.hpp" +#include "storage/storage_read_association.hpp" +#include "storage/storage_write_association.hpp" +#include "storage/storage_read_container.hpp" +#include "storage/storage_write_container.hpp" #include "storage/storage_file.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 + +#ifdef USE_RNTUPLE_STORAGE +#include "root_storage/root_rntuple_read_container.hpp" +#include "root_storage/root_rntuple_write_container.hpp" +#include "root_storage/root_rfield_read_container.hpp" +#include "root_storage/root_rfield_write_container.hpp" #endif #include @@ -29,47 +40,103 @@ 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); +#endif // USE_ROOT_STORAGE + } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { +#ifdef USE_RNTUPLE_STORAGE + return std::make_shared(name); +#endif // USE_RNTUPLE_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 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); + return std::make_shared(name); +#endif // USE_ROOT_STORAGE + } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { +#ifdef USE_RNTUPLE_STORAGE + return std::make_shared(name); +#endif // USE_RNTUPLE_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::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { +#ifdef USE_RNTUPLE_STORAGE + return std::make_shared(name); +#endif // USE_RNTUPLE_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 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::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { +#ifdef USE_RNTUPLE_STORAGE + return std::make_shared(name); +#endif // USE_RNTUPLE_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..69ae050d 100644 --- a/test/form/form_basics_test.cpp +++ b/test/form/form_basics_test.cpp @@ -1,10 +1,14 @@ #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_read_association.hpp" +#include "storage/storage_write_association.hpp" +#include "storage/storage_associative_read_container.hpp" +#include "storage/storage_associative_write_container.hpp" +#include "storage/storage_read_container.hpp" +#include "storage/storage_write_container.hpp" #include "storage/storage_file.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/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 7a831766..c3638767 100644 --- a/test/form/test_helpers.hpp +++ b/test/form/test_helpers.hpp @@ -2,6 +2,7 @@ #define TEST_HELPERS_HPP #include "data_products/track_start.hpp" -#include "form/form.hpp" +#include "form/form_reader.hpp" +#include "form/form_writer.hpp" #endif diff --git a/test/form/writer.cpp b/test/form/writer.cpp index 45462223..70ff2b50 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); From 0be8f744bb955d1c5cce29c078fe51a9d0f7f014 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Fri, 27 Feb 2026 13:43:45 -0600 Subject: [PATCH 02/15] Removing features from old prototype branch. --- form/CMakeLists.txt | 6 - form/root_storage/CMakeLists.txt | 4 +- .../root_rfield_read_container.cpp | 112 ----------------- .../root_rfield_read_container.hpp | 43 ------- .../root_rfield_write_container.cpp | 115 ------------------ .../root_rfield_write_container.hpp | 37 ------ .../root_rntuple_read_container.cpp | 29 ----- .../root_rntuple_read_container.hpp | 23 ---- .../root_rntuple_write_container.cpp | 43 ------- .../root_rntuple_write_container.hpp | 43 ------- form/util/factories.hpp | 11 +- 11 files changed, 4 insertions(+), 462 deletions(-) delete mode 100644 form/root_storage/root_rfield_read_container.cpp delete mode 100644 form/root_storage/root_rfield_read_container.hpp delete mode 100644 form/root_storage/root_rfield_write_container.cpp delete mode 100644 form/root_storage/root_rfield_write_container.hpp delete mode 100644 form/root_storage/root_rntuple_read_container.cpp delete mode 100644 form/root_storage/root_rntuple_read_container.hpp delete mode 100644 form/root_storage/root_rntuple_write_container.cpp delete mode 100644 form/root_storage/root_rntuple_write_container.hpp diff --git a/form/CMakeLists.txt b/form/CMakeLists.txt index a6ea5602..ddbe7652 100644 --- a/form/CMakeLists.txt +++ b/form/CMakeLists.txt @@ -18,12 +18,6 @@ if(FORM_USE_ROOT_STORAGE) add_definitions(-DUSE_ROOT_STORAGE) endif() -# RNTuple Storage toggle -option(FORM_USE_RNTUPLE_STORAGE "Enable RNTuple for ROOT Storage" ON) -if(FORM_USE_RNTUPLE_STORAGE) - add_definitions(-DUSE_RNTUPLE_STORAGE) -endif() - # Add sub directories add_subdirectory(form) add_subdirectory(core) diff --git a/form/root_storage/CMakeLists.txt b/form/root_storage/CMakeLists.txt index 3c645221..bd50f7e1 100644 --- a/form/root_storage/CMakeLists.txt +++ b/form/root_storage/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(ROOT REQUIRED COMPONENTS Core RIO Tree) # Component(s) in the package: -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 root_rfield_read_container.cpp root_rfield_write_container.cpp root_rntuple_read_container.cpp root_rntuple_write_container.cpp demangle_name.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) # Link the ROOT libraries -target_link_libraries(root_storage PUBLIC ROOT::Core ROOT::RIO ROOT::Tree ROOT::ROOTNTuple storage) +target_link_libraries(root_storage PUBLIC ROOT::Core ROOT::RIO ROOT::Tree storage) diff --git a/form/root_storage/root_rfield_read_container.cpp b/form/root_storage/root_rfield_read_container.cpp deleted file mode 100644 index 65dd4463..00000000 --- a/form/root_storage/root_rfield_read_container.cpp +++ /dev/null @@ -1,112 +0,0 @@ -//A ROOT_RField_Read_Container reads data products of a single type from vectors stored in an RNTuple field on disk. - -#include "root_rfield_read_container.hpp" -#include "root_rntuple_read_container.hpp" -#include "root_tfile.hpp" -#include "demangle_name.hpp" - -#include "ROOT/RNTupleReader.hxx" -#include "ROOT/RNTupleView.hxx" -#include "TFile.h" - -#include - -namespace form::detail::experimental { - ROOT_RField_Read_ContainerImp::ROOT_RField_Read_ContainerImp(std::string const& name) : - Storage_Associative_Read_Container(name), m_force_streamer_field(false) - { - } - - ROOT_RField_Read_ContainerImp::~ROOT_RField_Read_ContainerImp() {} - - void ROOT_RField_Read_ContainerImp::setAttribute(std::string const& key, std::string const& /*value*/) - { - if (key == "force_streamer_field") { - m_force_streamer_field = true; - } else { - throw std::runtime_error("ROOT_RField_Read_ContainerImp supports some attributes, but not " + key); - } - } - - void ROOT_RField_Read_ContainerImp::setFile(std::shared_ptr file) - { - Storage_Read_Container::setFile(file); - - auto form_root_file = dynamic_cast(file.get()); - if (form_root_file) { - m_tfile = form_root_file->getTFile(); - } else { - throw std::runtime_error( - "ROOT_RField_Read_ContainerImp::setFile failed to convert an IStorage_File to a ROOT_TFileImp. " - "ROOT_RField_Read_ContainerImp only works with TFiles."); - } - - if (!m_tfile) { - throw std::runtime_error( - "ROOT_RField_Read_ContainerImp::setFile failed to get a TFile from a ROOT_TFileImp"); - } - - return; - } - - void ROOT_RField_Read_ContainerImp::setParent(std::shared_ptr parent) - { - this->Storage_Associative_Read_Container::setParent(parent); - } - - bool ROOT_RField_Read_ContainerImp::read(int id, void const** data, std::type_info const& type) - { - //Connect to file at the last possible moment at the cost of a little run-time branching - if (!m_view) { - if (!m_reader) { //First time this RNTuple is read this job - if (!m_tfile) { - throw std::runtime_error( - "ROOT_RField_Read_ContainerImp::read No file loaded to read from on first read() call!"); - } - - m_reader = ROOT::RNTupleReader::Open(top_name(), m_tfile->GetName()); - } - - try { - m_view = - std::make_unique>(m_reader->GetView(col_name(), nullptr, type)); - } catch (const ROOT::RException& e) { - //RNTupleView will fail to create a field for fields written in streamer mode or for which type does not match the field's type on disk. Passing an empty string for type forces it to create the same type of field as the object on disk. Do this to handle streamer fields, then perform our own type check. - m_view = - std::make_unique>(m_reader->GetView(col_name(), nullptr, "")); - //TClass takes the "std::" off of "std::vector<>" when RNTuple's on-disk format doesn't. Convert RNTuple's type name to match TClass for manual type check because our dictionary of choice will likely be the same as TClass. - if (strcmp(TClass::GetClass(m_view->GetField().GetTypeName().c_str())->GetName(), - TClass::GetClass(type)->GetName())) { - throw std::runtime_error( - "ROOT_RField_containerImp::read type " + DemangleName(type) + " requested for a field named " + - col_name() + - " does not match the type in the file: " + m_view->GetField().GetTypeName()); - } - } - } - - if (id >= (int)m_reader->GetNEntries()) - return false; - - //Using RNTupleView<> to read instead of reusing REntry gives us full schema evolution support: the ROOT feature that lets us read files with an old class version into a new class version's memory. - auto buffer = m_view->GetField().CreateObject(); //PHLEX gets ownership of this memory - if (!buffer) { - throw std::runtime_error("ROOT_RField_Read_Container::read failed to create an object of type " + - m_view->GetField().GetTypeName() + - ". Maybe the type name for this read() (" + DemangleName(type) + - ") doesn't match the type from the first read() (" + - m_view->GetField().GetTypeName() + ")?"); - } - - m_view->BindRawPtr(buffer.get()); - try { - (*m_view)(id); - } catch (const ROOT::RException& e) { - throw std::runtime_error("ROOT_RField_Read_ContainerImp::read got a ROOT exception: " + - std::string(e.what())); - } - *data = buffer.release(); - - return true; - } -} diff --git a/form/root_storage/root_rfield_read_container.hpp b/form/root_storage/root_rfield_read_container.hpp deleted file mode 100644 index 7cb5ece2..00000000 --- a/form/root_storage/root_rfield_read_container.hpp +++ /dev/null @@ -1,43 +0,0 @@ -//A ROOT_RField_Read_Container is a Storage_Read_Container that uses a shared RNTuple to read and write data products to and from disk. A single Storage_Read_Container encapsulates the location where a collection of data products of a single type is stored. - -#ifndef FORM_ROOT_STORAGE_ROOT_RFIELD_READ_CONTAINER_HPP -#define FORM_ROOT_STORAGE_ROOT_RFIELD_READ_CONTAINER_HPP - -#include "storage/storage_associative_read_container.hpp" - -#include -#include - -class TFile; - -namespace ROOT { - class RNTupleReader; - template - class RNTupleView; - class RNTupleView; -} - -namespace form::detail::experimental { - class ROOT_RNTuple_Read_ContainerImp; - - class ROOT_RField_Read_ContainerImp : public Storage_Associative_Read_Container { - public: - ROOT_RField_Read_ContainerImp(std::string const& name); - ~ROOT_RField_Read_ContainerImp(); - - void setAttribute(std::string const& key, std::string const& value) override; - - void setFile(std::shared_ptr file) override; - void setParent(std::shared_ptr const parent) override; - bool read(int id, void const** data, std::type_info const& type) override; - - private: - std::shared_ptr m_tfile; - std::unique_ptr m_reader; - std::unique_ptr> m_view; - - bool m_force_streamer_field; - }; -} - -#endif // FORM_ROOT_STORAGE_ROOT_RFIELD_READ_CONTAINER_HPP diff --git a/form/root_storage/root_rfield_write_container.cpp b/form/root_storage/root_rfield_write_container.cpp deleted file mode 100644 index 1581ad39..00000000 --- a/form/root_storage/root_rfield_write_container.cpp +++ /dev/null @@ -1,115 +0,0 @@ -//A ROOT_RField_Write_Container writes data products of a single type from vectors stored in an RNTuple field on disk. - -#include "root_rfield_write_container.hpp" -#include "root_rntuple_write_container.hpp" -#include "root_tfile.hpp" -#include "demangle_name.hpp" - -#include "ROOT/RNTupleWriter.hxx" -#include "TFile.h" - -#include - -namespace form::detail::experimental { - ROOT_RField_Write_ContainerImp::ROOT_RField_Write_ContainerImp(std::string const& name) : - Storage_Associative_Write_Container(name), m_force_streamer_field(false) - { - } - - ROOT_RField_Write_ContainerImp::~ROOT_RField_Write_ContainerImp() {} - - void ROOT_RField_Write_ContainerImp::setAttribute(std::string const& key, std::string const& /*value*/) - { - if (key == "force_streamer_field") { - m_force_streamer_field = true; - } else { - throw std::runtime_error("ROOT_RField_Write_ContainerImp supports some attributes, but not " + key); - } - } - - void ROOT_RField_Write_ContainerImp::setFile(std::shared_ptr file) - { - Storage_Write_Container::setFile(file); - - auto form_root_file = dynamic_cast(file.get()); - if (form_root_file) { - m_tfile = form_root_file->getTFile(); - } else { - throw std::runtime_error( - "ROOT_RField_Write_ContainerImp::setFile failed to convert an IStorage_File to a ROOT_TFileImp. " - "ROOT_RField_Write_ContainerImp only works with TFiles."); - } - - if (!m_tfile) { - throw std::runtime_error( - "ROOT_RField_Write_ContainerImp::setFile failed to get a TFile from a ROOT_TFileImp"); - } - - return; - } - - void ROOT_RField_Write_ContainerImp::setParent(std::shared_ptr parent) - { - this->Storage_Associative_Write_Container::setParent(parent); - auto parentDerived = dynamic_pointer_cast(parent); - if (!parentDerived) { - throw std::runtime_error( - "ROOT_RField_Write_ContainerImp::setParent parent is not a ROOT_RNTuple_Write_ContainerImp! Something " - "may be wrong with how Storage works."); - } - m_rntuple_parent = parentDerived; - } - - void ROOT_RField_Write_ContainerImp::fill(void const* data) - { - if (!m_rntuple_parent->m_writer) { - if (!m_tfile) { - throw std::runtime_error( - "ROOT_RField_Write_ContainerImp::fill No file loaded to write to on first fill() call"); - } - - m_rntuple_parent->m_writer = - ROOT::RNTupleWriter::Append(std::move(m_rntuple_parent->m_model), top_name(), *m_tfile); - m_rntuple_parent->m_entry = m_rntuple_parent->m_writer->CreateRawPtrWriteEntry(); - } - m_rntuple_parent->m_entry->BindRawPtr(col_name(), data); - } - - void ROOT_RField_Write_ContainerImp::commit() - { - if (!m_rntuple_parent->m_entry) { - throw std::runtime_error("ROOT_RField_Write_ContainerImp::commit No RRawPtrWriteEntry set up. " - "You may have called commit() without calling setupWrite() first."); - } - if (!m_rntuple_parent->m_writer) { - throw std::runtime_error("ROOT_RField_Write_ContainerImp::commit No RNTupleWriter set up. " - "You may have called commit() without calling setupWrite() first."); - } - m_rntuple_parent->m_writer->Fill(*m_rntuple_parent->m_entry); - } - - void ROOT_RField_Write_ContainerImp::setupWrite(std::type_info const& type) - { - auto const& type_name = DemangleName(type); - std::unique_ptr field; - - if (m_force_streamer_field) { - field = std::make_unique(col_name(), type_name); - } else { - auto result = ROOT::RFieldBase::Create(col_name(), type_name); - if (result) { - field = result.Unwrap(); - } else { - std::cerr - << "ROOT_RField_Write_ContainerImp::setupWrite could not create column-wise storage for " - << type_name - << ". This class is probably using something obsolete like TLorentzVector. Storing it " - "in streamer mode to keep the application going." - << std::endl; - field = std::make_unique(col_name(), type_name); - } - } - - m_rntuple_parent->m_model->AddField(std::move(field)); - } -} diff --git a/form/root_storage/root_rfield_write_container.hpp b/form/root_storage/root_rfield_write_container.hpp deleted file mode 100644 index 53f75264..00000000 --- a/form/root_storage/root_rfield_write_container.hpp +++ /dev/null @@ -1,37 +0,0 @@ -//A ROOT_RField_Write_Container is a Storage_Write_Container that uses a shared RNTuple to write data products to disk. A single Storage_Write_Container encapsulates the location where a collection of data products of a single type is stored. - -#ifndef FORM_ROOT_STORAGE_ROOT_RFIELD_WRITE_CONTAINER_HPP -#define FORM_ROOT_STORAGE_ROOT_RFIELD_WRITE_CONTAINER_HPP - -#include "storage/storage_associative_write_container.hpp" - -#include -#include - -class TFile; - -namespace form::detail::experimental { - class ROOT_RNTuple_Write_ContainerImp; - - class ROOT_RField_Write_ContainerImp : public Storage_Associative_Write_Container { - public: - ROOT_RField_Write_ContainerImp(std::string const& name); - ~ROOT_RField_Write_ContainerImp(); - - void setAttribute(std::string const& key, std::string const& value) override; - - void setFile(std::shared_ptr file) override; - void setupWrite(std::type_info const& type) override; - void setParent(std::shared_ptr const parent) override; - void fill(void const* data) override; - void commit() override; - - private: - std::shared_ptr m_tfile; - std::shared_ptr m_rntuple_parent; - - bool m_force_streamer_field; - }; -} - -#endif // FORM_ROOT_STORAGE_ROOT_RFIELD_WRITE_CONTAINER_HPP diff --git a/form/root_storage/root_rntuple_read_container.cpp b/form/root_storage/root_rntuple_read_container.cpp deleted file mode 100644 index 2de81b9f..00000000 --- a/form/root_storage/root_rntuple_read_container.cpp +++ /dev/null @@ -1,29 +0,0 @@ -//A ROOT_RNTuple_Read_Container reads data products of a single type from vectors stored in an RNTuple field on disk. - -#include "root_rntuple_read_container.hpp" -#include "root_tfile.hpp" - -#include "ROOT/RNTupleReader.hxx" -#include "ROOT/RNTupleView.hxx" -#include "ROOT/RNTupleWriter.hxx" -#include "TFile.h" - -#include - -namespace form::detail::experimental { - ROOT_RNTuple_Read_ContainerImp::ROOT_RNTuple_Read_ContainerImp(std::string const& name) : - Storage_Read_Association(name) - { - } - - void ROOT_RNTuple_Read_ContainerImp::setFile(std::shared_ptr file) - { - Storage_Read_Container::setFile(file); - return; - } - - bool ROOT_RNTuple_Read_ContainerImp::read(int /*id*/, void const** /*data*/, std::type_info const& /*type*/) - { - throw std::runtime_error("ROOT_RNTuple_CotnainerImp::read not implemented"); - } -} diff --git a/form/root_storage/root_rntuple_read_container.hpp b/form/root_storage/root_rntuple_read_container.hpp deleted file mode 100644 index 7be15106..00000000 --- a/form/root_storage/root_rntuple_read_container.hpp +++ /dev/null @@ -1,23 +0,0 @@ -//A ROOT_RNTuple_ContainerImp is a Storage_Association (and therefore a Storage_Container) that coordinates the file accesses shared by several ROOT_RField_ContainerImps. It only coordinates RNTuple-specific file-based resources and doesn't actually implement write() or read() for example. This matches the early design of the TTree associative container. - -#ifndef FORM_ROOT_STORAGE_ROOT_RNTUPLE_READ_CONTAINER_HPP -#define FORM_ROOT_STORAGE_ROOT_RNTUPLE_READ_CONTAINER_HPP - -#include "storage/storage_read_association.hpp" - -#include -#include - -namespace form::detail::experimental { - - class ROOT_RNTuple_Read_ContainerImp : public Storage_Read_Association { - public: - ROOT_RNTuple_Read_ContainerImp(std::string const& name); - ~ROOT_RNTuple_Read_ContainerImp() = default; - - void setFile(std::shared_ptr file) override; - bool read(int id, void const** data, std::type_info const& type) override; - }; -} - -#endif // FORM_ROOT_STORAGE_ROOT_RNTUPLE_READ_CONTAINER_HPP diff --git a/form/root_storage/root_rntuple_write_container.cpp b/form/root_storage/root_rntuple_write_container.cpp deleted file mode 100644 index 09d861da..00000000 --- a/form/root_storage/root_rntuple_write_container.cpp +++ /dev/null @@ -1,43 +0,0 @@ -//A ROOT_RNTuple_Write_Container reads data products of a single type from vectors stored in an RNTuple field on disk. - -#include "root_rntuple_write_container.hpp" -#include "root_tfile.hpp" - -#include "ROOT/RNTupleReader.hxx" -#include "ROOT/RNTupleView.hxx" -#include "ROOT/RNTupleWriter.hxx" -#include "TFile.h" - -#include - -namespace form::detail::experimental { - ROOT_RNTuple_Write_ContainerImp::ROOT_RNTuple_Write_ContainerImp(std::string const& name) : - Storage_Write_Association(name), m_model(ROOT::RNTupleModel::Create()) - { - } - - ROOT_RNTuple_Write_ContainerImp::~ROOT_RNTuple_Write_ContainerImp() - { - if (m_writer) { - m_writer->CommitDataset(); - } - } - - void ROOT_RNTuple_Write_ContainerImp::setFile(std::shared_ptr file) - { - Storage_Write_Container::setFile(file); - return; - } - - void ROOT_RNTuple_Write_ContainerImp::fill(void const* /*data*/) - { - throw std::runtime_error("ROOT_RNTuple_Write_ContainerImp::fill not implemented"); - } - - void ROOT_RNTuple_Write_ContainerImp::commit() - { - throw std::runtime_error("ROOT_RNTuple_Write_ContainerImp::commit not implemented"); - } - - void ROOT_RNTuple_Write_ContainerImp::setupWrite(std::type_info const& /*type*/) { return; } -} diff --git a/form/root_storage/root_rntuple_write_container.hpp b/form/root_storage/root_rntuple_write_container.hpp deleted file mode 100644 index 4ba58dcc..00000000 --- a/form/root_storage/root_rntuple_write_container.hpp +++ /dev/null @@ -1,43 +0,0 @@ -//A ROOT_RNTuple_ContainerImp is a Storage_Write_Association (and therefore a Storage_Container) that coordinates the file accesses shared by several ROOT_RField_ContainerImps. It only coordinates RNTuple-specific file-based resources and doesn't actually implement write() or read() for example. This matches the early design of the TTree associative container. - -#ifndef FORM_ROOT_STORAGE_ROOT_RNTUPLE_WRITE_CONTAINER_HPP -#define FORM_ROOT_STORAGE_ROOT_RNTUPLE_WRITE_CONTAINER_HPP - -#include "storage/storage_write_association.hpp" - -#include -#include - -class TFile; - -namespace ROOT { - class RNTupleWriter; - class RNTupleModel; - - namespace Experimental { - namespace Detail { - class RRawPtrWriteEntry; - } - } -} - -namespace form::detail::experimental { - - class ROOT_RNTuple_Write_ContainerImp : public Storage_Write_Association { - public: - ROOT_RNTuple_Write_ContainerImp(std::string const& name); - ~ROOT_RNTuple_Write_ContainerImp(); - - void setFile(std::shared_ptr file) override; - void setupWrite(std::type_info const& type) override; - void fill(void const* data) override; - void commit() override; - - //State shared by ROOT_RField_ContainerImps - std::unique_ptr m_writer; - std::unique_ptr m_model; - std::unique_ptr m_entry; - }; -} - -#endif // FORM_ROOT_STORAGE_ROOT_RNTUPLE_WRITE_CONTAINER_HPP diff --git a/form/util/factories.hpp b/form/util/factories.hpp index 7fdd5c9c..d1ee228e 100644 --- a/form/util/factories.hpp +++ b/form/util/factories.hpp @@ -20,13 +20,6 @@ #include "root_storage/root_ttree_write_container.hpp" #endif -#ifdef USE_RNTUPLE_STORAGE -#include "root_storage/root_rntuple_read_container.hpp" -#include "root_storage/root_rntuple_write_container.hpp" -#include "root_storage/root_rfield_read_container.hpp" -#include "root_storage/root_rfield_write_container.hpp" -#endif - #include #include @@ -54,7 +47,7 @@ namespace form::detail::experimental { #endif // USE_ROOT_STORAGE } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { #ifdef USE_RNTUPLE_STORAGE - return std::make_shared(name); + //return std::make_shared(name); #endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { @@ -77,7 +70,7 @@ namespace form::detail::experimental { #endif // USE_ROOT_STORAGE } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { #ifdef USE_RNTUPLE_STORAGE - return std::make_shared(name); + //return std::make_shared(name); #endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { From 75fdbc52cb5bc8d9a95ee31cdebb5589bfbe2874 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 2 Mar 2026 15:35:20 -0600 Subject: [PATCH 03/15] Removed old unified Persistence class that is no longer needed. It has been replaced by PersistenceReader and PersistenceWriter. --- form/persistence/persistence.cpp | 135 ------------------------------- form/persistence/persistence.hpp | 66 --------------- 2 files changed, 201 deletions(-) delete mode 100644 form/persistence/persistence.cpp delete mode 100644 form/persistence/persistence.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.hpp b/form/persistence/persistence.hpp deleted file mode 100644 index 5710dec0..00000000 --- a/form/persistence/persistence.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_PERSISTENCE_PERSISTENCE_HPP -#define FORM_PERSISTENCE_PERSISTENCE_HPP - -#include "ipersistence.hpp" - -#include "core/placement.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 Persistence : public IPersistence { - public: - Persistence(); - ~Persistence() = 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 createContainers(std::string const& creator, - std::map const& products) override; - void registerWrite(std::string const& creator, - std::string const& label, - void const* data, - 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; - 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 From 471b535c08fdb4b04fb9e76e2a19f21f973380c2 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 2 Mar 2026 15:37:10 -0600 Subject: [PATCH 04/15] Removed old unified Storage class that is no longer used. --- form/storage/storage.cpp | 159 --------------------------------------- form/storage/storage.hpp | 60 --------------- 2 files changed, 219 deletions(-) delete mode 100644 form/storage/storage.cpp delete mode 100644 form/storage/storage.hpp 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.hpp b/form/storage/storage.hpp deleted file mode 100644 index e4a529db..00000000 --- a/form/storage/storage.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_STORAGE_STORAGE_HPP -#define FORM_STORAGE_STORAGE_HPP - -#include "istorage.hpp" - -#include -#include -#include -#include -#include // for std::pair - -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 { - public: - Storage() = default; - ~Storage() = 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, - form::experimental::config::tech_setting_config const& settings) override; - void readContainer(Token const& token, - void const** data, - std::type_info const& type, - form::experimental::config::tech_setting_config const& settings) override; - - private: - std::map> m_files; - std::unordered_map, - std::shared_ptr, - pair_hash> - m_containers; - std::map> m_indexMaps; - }; - -} // namespace form::detail::experimental - -#endif // FORM_STORAGE_STORAGE_HPP From b7ddde106f9f9c92fcab495c9ced42f6bea735a7 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 2 Mar 2026 15:40:17 -0600 Subject: [PATCH 05/15] Renamed storage_util.hpp to storage_utils.hpp to match naming convention in persistence directory. --- form/storage/storage_reader.hpp | 2 +- form/storage/{storage_util.hpp => storage_utils.hpp} | 6 +++--- form/storage/storage_writer.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename form/storage/{storage_util.hpp => storage_utils.hpp} (73%) diff --git a/form/storage/storage_reader.hpp b/form/storage/storage_reader.hpp index f1f57776..8d552d08 100644 --- a/form/storage/storage_reader.hpp +++ b/form/storage/storage_reader.hpp @@ -4,7 +4,7 @@ #define FORM_STORAGE_STORAGE_READER_HPP #include "istorage.hpp" -#include "storage_util.hpp" +#include "storage_utils.hpp" #include #include diff --git a/form/storage/storage_util.hpp b/form/storage/storage_utils.hpp similarity index 73% rename from form/storage/storage_util.hpp rename to form/storage/storage_utils.hpp index e8b2ca41..4a785d78 100644 --- a/form/storage/storage_util.hpp +++ b/form/storage/storage_utils.hpp @@ -1,7 +1,7 @@ #include -#ifndef FORM_STORAGE_STORAGE_UTIL_HPP -#define FORM_STORAGE_STORAGE_UTIL_HPP +#ifndef FORM_STORAGE_STORAGE_UTILS_HPP +#define FORM_STORAGE_STORAGE_UTILS_HPP namespace form::detail::experimental { @@ -17,4 +17,4 @@ namespace form::detail::experimental }; } -#endif // FORM_STORAGE_STORAGE_UTIL_HPP +#endif // FORM_STORAGE_STORAGE_UTILS_HPP diff --git a/form/storage/storage_writer.hpp b/form/storage/storage_writer.hpp index 08208896..49e7069c 100644 --- a/form/storage/storage_writer.hpp +++ b/form/storage/storage_writer.hpp @@ -4,7 +4,7 @@ #define FORM_STORAGE_STORAGE_WRITER_HPP #include "istorage.hpp" -#include "storage_util.hpp" +#include "storage_utils.hpp" #include #include From e464bdabfb1009e3d878cd788856572e64f862d5 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 2 Mar 2026 15:55:52 -0600 Subject: [PATCH 06/15] Got latest FORM Storage tests working with the read/write split. Removed a few files that were restored in the merge with main accidentally. --- form/persistence/ipersistence.hpp | 48 ----- form/root_storage/root_tbranch_container.cpp | 193 ------------------ form/root_storage/root_tbranch_container.hpp | 41 ---- form/root_storage/root_ttree_container.cpp | 79 ------- form/root_storage/root_ttree_container.hpp | 40 ---- form/storage/storage_association.cpp | 23 --- form/storage/storage_association.hpp | 22 -- .../storage/storage_associative_container.cpp | 29 --- .../storage/storage_associative_container.hpp | 31 --- form/storage/storage_container.cpp | 32 --- form/storage/storage_container.hpp | 36 ---- test/form/form_storage_test.cpp | 17 +- test/form/test_utils.hpp | 27 +-- 13 files changed, 24 insertions(+), 594 deletions(-) delete mode 100644 form/persistence/ipersistence.hpp delete mode 100644 form/root_storage/root_tbranch_container.cpp delete mode 100644 form/root_storage/root_tbranch_container.hpp delete mode 100644 form/root_storage/root_ttree_container.cpp delete mode 100644 form/root_storage/root_ttree_container.hpp delete mode 100644 form/storage/storage_association.cpp delete mode 100644 form/storage/storage_association.hpp delete mode 100644 form/storage/storage_associative_container.cpp delete mode 100644 form/storage/storage_associative_container.hpp delete mode 100644 form/storage/storage_container.cpp delete mode 100644 form/storage/storage_container.hpp diff --git a/form/persistence/ipersistence.hpp b/form/persistence/ipersistence.hpp deleted file mode 100644 index 9405a70c..00000000 --- a/form/persistence/ipersistence.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_PERSISTENCE_IPERSISTENCE_HPP -#define FORM_PERSISTENCE_IPERSISTENCE_HPP - -#include -#include -#include -#include - -namespace form::experimental::config { - class output_item_config; - struct tech_setting_config; -} - -namespace form::detail::experimental { - - class IPersistence { - public: - IPersistence() {}; - virtual ~IPersistence() = 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 createContainers(std::string const& creator, - std::map const& products) = 0; - virtual void registerWrite(std::string const& creator, - std::string const& label, - 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(); - -} // namespace form::detail::experimental - -#endif // FORM_PERSISTENCE_IPERSISTENCE_HPP 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_container.hpp b/form/root_storage/root_tbranch_container.hpp deleted file mode 100644 index ae6cc42f..00000000 --- a/form/root_storage/root_tbranch_container.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_ROOT_STORAGE_ROOT_TBRANCH_CONTAINER_HPP -#define FORM_ROOT_STORAGE_ROOT_TBRANCH_CONTAINER_HPP - -#include "storage/storage_associative_container.hpp" - -#include -#include -#include - -class TFile; -class TTree; -class TBranch; - -namespace form::detail::experimental { - - class ROOT_TBranch_ContainerImp : public Storage_Associative_Container { - public: - ROOT_TBranch_ContainerImp(std::string const& name); - ~ROOT_TBranch_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 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; - TTree* m_tree; - TBranch* m_branch; - }; - -} // namespace form::detail::experimental - -#endif // FORM_ROOT_STORAGE_ROOT_TBRANCH_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/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_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_container.hpp b/form/storage/storage_container.hpp deleted file mode 100644 index 87a00391..00000000 --- a/form/storage/storage_container.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2025 ... - -#ifndef FORM_STORAGE_STORAGE_CONTAINER_HPP -#define FORM_STORAGE_STORAGE_CONTAINER_HPP - -#include "istorage.hpp" - -#include -#include -#include - -namespace form::detail::experimental { - - class Storage_Container : public IStorage_Container { - public: - Storage_Container(std::string const& name); - ~Storage_Container() = default; - - std::string const& name() override; - - 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; - - 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_CONTAINER_HPP diff --git a/test/form/form_storage_test.cpp b/test/form/form_storage_test.cpp index f4d2c505..4bcfa474 100644 --- a/test/form/form_storage_test.cpp +++ b/test/form/form_storage_test.cpp @@ -17,7 +17,7 @@ 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 +78,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 +86,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/test_utils.hpp b/test/form/test_utils.hpp index f1c10d36..e9bd1555 100644 --- a/test/form/test_utils.hpp +++ b/test/form/test_utils.hpp @@ -4,7 +4,8 @@ #define 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)...); From 23e120bf811627f1924c7e44d5eac2ebe07ffa7c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:43:47 +0000 Subject: [PATCH 07/15] Apply YAML formatter fixes --- .github/workflows/cmake-build.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake-build.yaml b/.github/workflows/cmake-build.yaml index d68ea35e..5741ad3a 100644 --- a/.github/workflows/cmake-build.yaml +++ b/.github/workflows/cmake-build.yaml @@ -218,9 +218,8 @@ jobs: cmake-build-skipped: needs: [setup] if: > - needs.setup.result == 'success' && github.event_name != 'workflow_dispatch' && - !inputs.skip-relevance-check && needs.setup.outputs.is_act != 'true' && - needs.setup.outputs.has_changes != 'true' + needs.setup.result == 'success' && github.event_name != 'workflow_dispatch' && !inputs.skip-relevance-check && + needs.setup.outputs.is_act != 'true' && needs.setup.outputs.has_changes != 'true' runs-on: ubuntu-latest permissions: contents: read From 0e84f5ff9181fd6034ec614413215ddf28874fdc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:43:47 +0000 Subject: [PATCH 08/15] Apply cmake-format fixes --- form/root_storage/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/form/root_storage/CMakeLists.txt b/form/root_storage/CMakeLists.txt index 6ce745ce..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_read_container.cpp root_ttree_write_container.cpp root_tbranch_read_container.cpp root_tbranch_write_container.cpp demangle_name.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 From d586ade4d273448903f357e85d422a7977946e8d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:43:49 +0000 Subject: [PATCH 09/15] Apply clang-format fixes --- form/form/form_reader.hpp | 2 +- form/form/form_writer.hpp | 2 +- form/form/product_with_name.hpp | 3 +-- form/form_module.cpp | 3 ++- form/persistence/persistence_reader.cpp | 24 +++++++++++-------- form/persistence/persistence_utils.cpp | 12 +++++----- form/persistence/persistence_utils.hpp | 15 +++++------- form/persistence/persistence_writer.cpp | 18 ++++++++------ .../root_tbranch_read_container.cpp | 13 ++++++---- .../root_tbranch_write_container.cpp | 14 +++++++---- .../root_ttree_read_container.cpp | 4 +++- .../root_ttree_write_container.cpp | 3 ++- form/storage/istorage.hpp | 1 - .../storage_associative_write_container.cpp | 3 ++- form/storage/storage_read_association.cpp | 5 +++- form/storage/storage_read_container.cpp | 9 +++++-- form/storage/storage_reader.cpp | 20 +++++++++------- form/storage/storage_utils.hpp | 3 +-- form/storage/storage_write_association.cpp | 5 +++- form/storage/storage_write_container.cpp | 8 +++++-- form/storage/storage_writer.cpp | 19 ++++++++++----- form/storage/storage_writer.hpp | 9 ++++--- form/util/factories.hpp | 16 ++++++++----- test/form/form_basics_test.cpp | 6 ++--- test/form/form_storage_test.cpp | 3 ++- 25 files changed, 134 insertions(+), 86 deletions(-) diff --git a/form/form/form_reader.hpp b/form/form/form_reader.hpp index 679775b7..17ca0b8b 100644 --- a/form/form/form_reader.hpp +++ b/form/form/form_reader.hpp @@ -18,7 +18,7 @@ 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); + config::tech_setting_config const& tech_config); ~form_reader_interface() = default; void read(std::string const& creator, diff --git a/form/form/form_writer.hpp b/form/form/form_writer.hpp index a19c6028..0011f041 100644 --- a/form/form/form_writer.hpp +++ b/form/form/form_writer.hpp @@ -17,7 +17,7 @@ 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); + config::tech_setting_config const& tech_config); ~form_writer_interface() = default; void write(std::string const& creator, diff --git a/form/form/product_with_name.hpp b/form/form/product_with_name.hpp index 2d8343bf..f0789f8d 100644 --- a/form/form/product_with_name.hpp +++ b/form/form/product_with_name.hpp @@ -4,8 +4,7 @@ #include #include -namespace form::experimental -{ +namespace form::experimental { struct product_with_name { std::string label; void const* data; diff --git a/form/form_module.cpp b/form/form_module.cpp index b18a805c..fa67134f 100644 --- a/form/form_module.cpp +++ b/form/form_module.cpp @@ -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&) diff --git a/form/persistence/persistence_reader.cpp b/form/persistence/persistence_reader.cpp index 5d00c33e..b510592c 100644 --- a/form/persistence/persistence_reader.cpp +++ b/form/persistence/persistence_reader.cpp @@ -12,13 +12,17 @@ using namespace form::detail::experimental; -namespace form::detail::experimental -{ - std::unique_ptr createPersistenceReader() { return std::make_unique(); } +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 + m_store(createStorageReader()), + m_output_items(), + m_tech_settings() // constructor takes form config { } @@ -35,10 +39,10 @@ void PersistenceReader::configureOutputItems( } void PersistenceReader::read(std::string const& creator, - std::string const& label, - std::string const& id, - void const** data, - std::type_info const& type) + 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); @@ -46,8 +50,8 @@ void PersistenceReader::read(std::string const& creator, } std::unique_ptr PersistenceReader::getToken(std::string const& creator, - std::string const& label, - std::string const& id) + std::string const& label, + std::string const& id) { auto const config_item = findConfigItem(m_output_items, label); diff --git a/form/persistence/persistence_utils.cpp b/form/persistence/persistence_utils.cpp index 6371f759..9ce0c115 100644 --- a/form/persistence/persistence_utils.cpp +++ b/form/persistence/persistence_utils.cpp @@ -1,17 +1,17 @@ #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) +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. + : 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); } diff --git a/form/persistence/persistence_utils.hpp b/form/persistence/persistence_utils.hpp index b915947d..c7c6bb32 100644 --- a/form/persistence/persistence_utils.hpp +++ b/form/persistence/persistence_utils.hpp @@ -5,20 +5,17 @@ #include -namespace form -{ - namespace experimental::config - { +namespace form { + namespace experimental::config { class output_config; } - namespace detail::experimental - { + 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::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); + std::string buildFullLabel(std::string_view creator, std::string_view label); } } diff --git a/form/persistence/persistence_writer.cpp b/form/persistence/persistence_writer.cpp index cb3b2ca8..4b859744 100644 --- a/form/persistence/persistence_writer.cpp +++ b/form/persistence/persistence_writer.cpp @@ -12,13 +12,17 @@ using namespace form::detail::experimental; -namespace form::detail::experimental -{ - std::unique_ptr createPersistenceWriter() { return std::make_unique(); } +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 + m_store(createStorageWriter()), + m_output_items(), + m_tech_settings() // constructor takes form config { } @@ -34,8 +38,8 @@ void PersistenceWriter::configureOutputItems( m_output_items = output_items; } -void PersistenceWriter::createContainers(std::string const& creator, - std::map const& products) +void PersistenceWriter::createContainers( + std::string const& creator, std::map const& products) { std::map, std::type_info const*> containers; for (auto const& [label, type] : products) { @@ -65,7 +69,7 @@ void PersistenceWriter::commitOutput(std::string const& creator, std::string con } std::unique_ptr PersistenceWriter::getPlacement(std::string const& creator, - std::string const& label) + std::string const& label) { auto const config_item = findConfigItem(m_output_items, label); diff --git a/form/root_storage/root_tbranch_read_container.cpp b/form/root_storage/root_tbranch_read_container.cpp index 11b4e926..63b25dab 100644 --- a/form/root_storage/root_tbranch_read_container.cpp +++ b/form/root_storage/root_tbranch_read_container.cpp @@ -1,9 +1,9 @@ // Copyright (C) 2025 ... #include "root_tbranch_read_container.hpp" +#include "demangle_name.hpp" #include "root_tfile.hpp" #include "root_ttree_read_container.hpp" -#include "demangle_name.hpp" #include "TBranch.h" #include "TFile.h" @@ -24,7 +24,8 @@ void ROOT_TBranch_Read_ContainerImp::setAttribute(std::string const& key, std::s if (key == "auto_flush") { m_tree->SetAutoFlush(std::stol(value)); } else { - throw std::runtime_error("ROOT_TTree_Read_ContainerImp accepts some attributes, but not " + key); + throw std::runtime_error("ROOT_TTree_Read_ContainerImp accepts some attributes, but not " + + key); } } @@ -33,7 +34,8 @@ 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"); + throw std::runtime_error( + "ROOT_TBranch_Read_ContainerImp::setFile can't attach to non-ROOT file"); } m_tfile = root_tfile_imp->getTFile(); return; @@ -42,7 +44,8 @@ void ROOT_TBranch_Read_ContainerImp::setFile(std::shared_ptr file 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()); + 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"); } @@ -79,7 +82,7 @@ bool ROOT_TBranch_Read_ContainerImp::read(int id, void const** data, std::type_i DemangleName(type)); } - if(dictInfo->Property() & EProperty::kIsFundamental) { + 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()]; diff --git a/form/root_storage/root_tbranch_write_container.cpp b/form/root_storage/root_tbranch_write_container.cpp index 0fcd27a9..4a0132b0 100644 --- a/form/root_storage/root_tbranch_write_container.cpp +++ b/form/root_storage/root_tbranch_write_container.cpp @@ -1,9 +1,9 @@ // Copyright (C) 2025 ... #include "root_tbranch_write_container.hpp" +#include "demangle_name.hpp" #include "root_tfile.hpp" #include "root_ttree_write_container.hpp" -#include "demangle_name.hpp" #include "TBranch.h" #include "TFile.h" @@ -38,7 +38,8 @@ void ROOT_TBranch_Write_ContainerImp::setAttribute(std::string const& key, std:: 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); + throw std::runtime_error("ROOT_TTree_Write_ContainerImp accepts some attributes, but not " + + key); } } @@ -47,7 +48,8 @@ void ROOT_TBranch_Write_ContainerImp::setFile(std::shared_ptr fil 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"); + throw std::runtime_error( + "ROOT_TBranch_Write_ContainerImp::setFile can't attach to non-ROOT file"); } m_tfile = root_tfile_imp->getTFile(); return; @@ -56,7 +58,8 @@ void ROOT_TBranch_Write_ContainerImp::setFile(std::shared_ptr fil 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()); + 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"); } @@ -73,7 +76,8 @@ void ROOT_TBranch_Write_ContainerImp::setupWrite(std::type_info const& type) auto dictInfo = TDictionary::GetDictionary(type); if (m_branch == nullptr) { if (!dictInfo) { - throw std::runtime_error("ROOT_TBranch_Write_ContainerImp::setupWrite unsupported type: " + DemangleName(type)); + 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(), diff --git a/form/root_storage/root_ttree_read_container.cpp b/form/root_storage/root_ttree_read_container.cpp index 9924b0b0..37f4ef86 100644 --- a/form/root_storage/root_ttree_read_container.cpp +++ b/form/root_storage/root_ttree_read_container.cpp @@ -32,7 +32,9 @@ void ROOT_TTree_Read_ContainerImp::setFile(std::shared_ptr file) return; } -bool ROOT_TTree_Read_ContainerImp::read(int /* id*/, void const** /* data*/, std::type_info const& /* type*/) +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"); } diff --git a/form/root_storage/root_ttree_write_container.cpp b/form/root_storage/root_ttree_write_container.cpp index d0f6e853..533aa90c 100644 --- a/form/root_storage/root_ttree_write_container.cpp +++ b/form/root_storage/root_ttree_write_container.cpp @@ -26,7 +26,8 @@ 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"); + throw std::runtime_error( + "ROOT_TTree_Write_ContainerImp::setFile can't attach to non-ROOT file"); } m_tfile = dynamic_cast(file.get())->getTFile(); return; diff --git a/form/storage/istorage.hpp b/form/storage/istorage.hpp index 4bdd4645..555d58b2 100644 --- a/form/storage/istorage.hpp +++ b/form/storage/istorage.hpp @@ -81,7 +81,6 @@ namespace form::detail::experimental { virtual void setAttribute(std::string const& name, std::string const& value) = 0; }; - std::unique_ptr createStorageReader(); std::unique_ptr createStorageWriter(); diff --git a/form/storage/storage_associative_write_container.cpp b/form/storage/storage_associative_write_container.cpp index 45dced75..d045e0c5 100644 --- a/form/storage/storage_associative_write_container.cpp +++ b/form/storage/storage_associative_write_container.cpp @@ -23,7 +23,8 @@ std::string const& Storage_Associative_Write_Container::top_name() { return m_tN std::string const& Storage_Associative_Write_Container::col_name() { return m_cName; } -void Storage_Associative_Write_Container::setParent(std::shared_ptr parent) +void Storage_Associative_Write_Container::setParent( + std::shared_ptr parent) { m_parent = parent; } diff --git a/form/storage/storage_read_association.cpp b/form/storage/storage_read_association.cpp index 1a07ffdd..7f06b94e 100644 --- a/form/storage/storage_read_association.cpp +++ b/form/storage/storage_read_association.cpp @@ -20,4 +20,7 @@ Storage_Read_Association::Storage_Read_Association(std::string const& name) : { } -void Storage_Read_Association::setAttribute(std::string const& /*key*/, std::string const& /*value*/) {} +void Storage_Read_Association::setAttribute(std::string const& /*key*/, + std::string const& /*value*/) +{ +} diff --git a/form/storage/storage_read_container.cpp b/form/storage/storage_read_container.cpp index 6a7b4d97..8e5d7f25 100644 --- a/form/storage/storage_read_container.cpp +++ b/form/storage/storage_read_container.cpp @@ -5,13 +5,18 @@ using namespace form::detail::experimental; -Storage_Read_Container::Storage_Read_Container(std::string const& name) : m_name(name), m_file(nullptr) {} +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*/) +bool Storage_Read_Container::read(int /* id*/, + void const** /*data*/, + std::type_info const& /* type*/) { return false; } diff --git a/form/storage/storage_reader.cpp b/form/storage/storage_reader.cpp index dd99bf42..e1cfeae2 100644 --- a/form/storage/storage_reader.cpp +++ b/form/storage/storage_reader.cpp @@ -1,9 +1,9 @@ // Copyright (C) 2025 ... #include "storage_reader.hpp" -#include "storage_read_association.hpp" #include "storage_associative_read_container.hpp" #include "storage_file.hpp" +#include "storage_read_association.hpp" #include "util/factories.hpp" @@ -11,12 +11,15 @@ using namespace form::detail::experimental; // Factory function implementation namespace form::detail::experimental { - std::unique_ptr createStorageReader() { return std::unique_ptr(new StorageReader()); } + 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) + 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()); @@ -29,7 +32,8 @@ int StorageReader::getIndex(Token const& token, 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())}); + 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())) @@ -52,9 +56,9 @@ int StorageReader::getIndex(Token const& token, } void StorageReader::readContainer(Token const& token, - void const** data, - std::type_info const& type, - form::experimental::config::tech_setting_config const& settings) + 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); diff --git a/form/storage/storage_utils.hpp b/form/storage/storage_utils.hpp index 4a785d78..2abbd455 100644 --- a/form/storage/storage_utils.hpp +++ b/form/storage/storage_utils.hpp @@ -3,8 +3,7 @@ #ifndef FORM_STORAGE_STORAGE_UTILS_HPP #define FORM_STORAGE_STORAGE_UTILS_HPP -namespace form::detail::experimental -{ +namespace form::detail::experimental { // Hash function for std::pair struct pair_hash { template diff --git a/form/storage/storage_write_association.cpp b/form/storage/storage_write_association.cpp index 44a83c04..f19aae1c 100644 --- a/form/storage/storage_write_association.cpp +++ b/form/storage/storage_write_association.cpp @@ -20,4 +20,7 @@ Storage_Write_Association::Storage_Write_Association(std::string const& name) : { } -void Storage_Write_Association::setAttribute(std::string const& /*key*/, std::string const& /*value*/) {} +void Storage_Write_Association::setAttribute(std::string const& /*key*/, + std::string const& /*value*/) +{ +} diff --git a/form/storage/storage_write_container.cpp b/form/storage/storage_write_container.cpp index 70624b08..37742aca 100644 --- a/form/storage/storage_write_container.cpp +++ b/form/storage/storage_write_container.cpp @@ -5,7 +5,10 @@ using namespace form::detail::experimental; -Storage_Write_Container::Storage_Write_Container(std::string const& name) : m_name(name), m_file(nullptr) {} +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; } @@ -17,7 +20,8 @@ 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*/) +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 " + diff --git a/form/storage/storage_writer.cpp b/form/storage/storage_writer.cpp index 1ae9a2c9..4a03e2a7 100644 --- a/form/storage/storage_writer.cpp +++ b/form/storage/storage_writer.cpp @@ -1,9 +1,9 @@ // Copyright (C) 2025 ... #include "storage_writer.hpp" -#include "storage_write_association.hpp" #include "storage_associative_write_container.hpp" #include "storage_file.hpp" +#include "storage_write_association.hpp" #include "util/factories.hpp" @@ -11,11 +11,15 @@ using namespace form::detail::experimental; // Factory function implementation namespace form::detail::experimental { - std::unique_ptr createStorageWriter() { return std::unique_ptr(new StorageWriter()); } + 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) +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 @@ -36,7 +40,8 @@ void StorageWriter::createContainers(std::map, std::t 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); + 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); @@ -62,7 +67,9 @@ void StorageWriter::createContainers(std::map, std::t return; } -void StorageWriter::fillContainer(Placement const& plcmnt, void const* data, std::type_info const& /* type*/) +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()); diff --git a/form/storage/storage_writer.hpp b/form/storage/storage_writer.hpp index 49e7069c..55cc77f8 100644 --- a/form/storage/storage_writer.hpp +++ b/form/storage/storage_writer.hpp @@ -20,9 +20,12 @@ namespace form::detail::experimental { ~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 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: diff --git a/form/util/factories.hpp b/form/util/factories.hpp index d1ee228e..b0e24cfe 100644 --- a/form/util/factories.hpp +++ b/form/util/factories.hpp @@ -6,11 +6,11 @@ #include "form/technology.hpp" #include "storage/istorage.hpp" +#include "storage/storage_file.hpp" #include "storage/storage_read_association.hpp" -#include "storage/storage_write_association.hpp" #include "storage/storage_read_container.hpp" +#include "storage/storage_write_association.hpp" #include "storage/storage_write_container.hpp" -#include "storage/storage_file.hpp" #ifdef USE_ROOT_STORAGE #include "root_storage/root_tbranch_read_container.hpp" @@ -38,7 +38,8 @@ namespace form::detail::experimental { return std::make_shared(name, mode); } - inline std::shared_ptr createReadAssociation(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) { @@ -61,7 +62,8 @@ namespace form::detail::experimental { return std::make_shared(name); } - inline std::shared_ptr createWriteAssociation(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) { @@ -84,7 +86,8 @@ namespace form::detail::experimental { return std::make_shared(name); } - inline std::shared_ptr createReadContainer(int tech, std::string const& 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) { @@ -108,7 +111,8 @@ namespace form::detail::experimental { return std::make_shared(name); } - inline std::shared_ptr createWriteContainer(int tech, std::string const& 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) { diff --git a/test/form/form_basics_test.cpp b/test/form/form_basics_test.cpp index 69ae050d..38cb47e4 100644 --- a/test/form/form_basics_test.cpp +++ b/test/form/form_basics_test.cpp @@ -3,13 +3,13 @@ #include "persistence/persistence_reader.hpp" #include "persistence/persistence_writer.hpp" #include "storage/istorage.hpp" -#include "storage/storage_read_association.hpp" -#include "storage/storage_write_association.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 "storage/storage_file.hpp" #include "util/factories.hpp" #include diff --git a/test/form/form_storage_test.cpp b/test/form/form_storage_test.cpp index 4bcfa474..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 = createReadContainer(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); From ee7bae1a34f7be7ab977b30368b77466c9f9b8f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:43:49 +0000 Subject: [PATCH 10/15] Apply ruff fixes --- test/python/suffix.py | 146 +++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 51 deletions(-) diff --git a/test/python/suffix.py b/test/python/suffix.py index 4eefc264..0cae6a31 100644 --- a/test/python/suffix.py +++ b/test/python/suffix.py @@ -5,38 +5,44 @@ """ - def constant_one(i: int) -> int: """Constant 1.""" return 1 + def constant_two(i: int) -> int: """Constant 2.""" return 2 + def constant_three(i: int) -> int: """Constant 3.""" return 3 + def constant_four(i: int) -> int: """Constant 4.""" return 4 + def observe_one(i: int) -> None: """Observe i; expect 1.""" assert i == 1 + def observe_two(i: int, j: int) -> None: """Observe (i, j); expect (1, 2).""" assert i == 1 assert j == 2 + def observe_three(i: int, j: int, k: int) -> None: """Observe (i, j, k); expect (1, 2, 3).""" assert i == 1 assert j == 2 assert k == 3 + def observe_four(i: int, j: int, k: int, ll: int) -> None: """Observe (i, j, k, l); expect (1, 2, 3, 4).""" assert i == 1 @@ -59,10 +65,12 @@ def PHLEX_REGISTER_ALGORITHMS(m, config): None """ # tests for error handling of input specifications - for input_query in ({"creator": 42, "layer": "event", "suffix": "i"}, - {"creator": "input", "layer": 42, "suffix": "i"}, - {"layer": "event", "suffix": "i"}, - {"creator": "input", "suffix": "i"}): + for input_query in ( + {"creator": 42, "layer": "event", "suffix": "i"}, + {"creator": "input", "layer": 42, "suffix": "i"}, + {"layer": "event", "suffix": "i"}, + {"creator": "input", "suffix": "i"}, + ): try: m.transform(constant_one, input_family=[input_query], output_products=["output_one"]) assert not "supposed to be here" @@ -71,67 +79,103 @@ def PHLEX_REGISTER_ALGORITHMS(m, config): assert "or not a string" in str(e) # transforms with suffix to be used without suffix - m.transform(constant_one, input_family=[ - {"creator": "input", "layer": "event", "suffix": "i"}, - ], - output_products=["output_one"]) - m.transform(constant_two, input_family=[ - {"creator": "input", "layer": "event", "suffix": "i"}, - ], - output_products=["output_two"]) - m.transform(constant_three, input_family=[ - {"creator": "input", "layer": "event", "suffix": "i"}, - ], - output_products=["output_three"]) + m.transform( + constant_one, + input_family=[ + {"creator": "input", "layer": "event", "suffix": "i"}, + ], + output_products=["output_one"], + ) + m.transform( + constant_two, + input_family=[ + {"creator": "input", "layer": "event", "suffix": "i"}, + ], + output_products=["output_two"], + ) + m.transform( + constant_three, + input_family=[ + {"creator": "input", "layer": "event", "suffix": "i"}, + ], + output_products=["output_three"], + ) # observers without suffix # test for failing suffix (incorrect type: not a string) try: - m.observe(observe_one, input_family=[ - {"creator": "constant_one", "layer": "event", "suffix": 42}, - ]) + m.observe( + observe_one, + input_family=[ + {"creator": "constant_one", "layer": "event", "suffix": 42}, + ], + ) assert not "supposed to be here" except TypeError as e: assert "is not a string" in str(e) - m.observe(observe_one, input_family=[ - {"creator": "constant_one", "layer": "event"}, - ]) + m.observe( + observe_one, + input_family=[ + {"creator": "constant_one", "layer": "event"}, + ], + ) # regular - m.observe(observe_two, input_family=[ - {"creator": "constant_one", "layer": "event"}, - {"creator": "constant_two", "layer": "event"}, - ]) - m.observe(observe_three, input_family=[ - {"creator": "constant_one", "layer": "event"}, - {"creator": "constant_two", "layer": "event"}, - {"creator": "constant_three", "layer": "event"}, - ]) + m.observe( + observe_two, + input_family=[ + {"creator": "constant_one", "layer": "event"}, + {"creator": "constant_two", "layer": "event"}, + ], + ) + m.observe( + observe_three, + input_family=[ + {"creator": "constant_one", "layer": "event"}, + {"creator": "constant_two", "layer": "event"}, + {"creator": "constant_three", "layer": "event"}, + ], + ) # test for unsupported number of arguments (remove test once support # to arbitrary number of arguments becomes available try: - m.observe(observe_three, input_family=[ - {"creator": "constant_one", "layer": "event"}, - {"creator": "constant_two", "layer": "event"}, - {"creator": "constant_three", "layer": "event"}, - {"creator": "constant_four", "layer": "event"}, - ]) + m.observe( + observe_three, + input_family=[ + {"creator": "constant_one", "layer": "event"}, + {"creator": "constant_two", "layer": "event"}, + {"creator": "constant_three", "layer": "event"}, + {"creator": "constant_four", "layer": "event"}, + ], + ) assert not "supposed to be here" except TypeError: - pass # noqa + pass # noqa # observers with suffix - m.observe(observe_one, name="observe_one_ws", input_family=[ - {"creator": "constant_one", "layer": "event", "suffix": "output_one"}, - ]) - m.observe(observe_two, name="observe_two_ws", input_family=[ - {"creator": "constant_one", "layer": "event", "suffix": "output_one"}, - {"creator": "constant_two", "layer": "event", "suffix": "output_two"}, - ]) - m.observe(observe_three, name="observe_three_ws", input_family=[ - {"creator": "constant_one", "layer": "event", "suffix": "output_one"}, - {"creator": "constant_two", "layer": "event", "suffix": "output_two"}, - {"creator": "constant_three", "layer": "event", "suffix": "output_three"}, - ]) + m.observe( + observe_one, + name="observe_one_ws", + input_family=[ + {"creator": "constant_one", "layer": "event", "suffix": "output_one"}, + ], + ) + m.observe( + observe_two, + name="observe_two_ws", + input_family=[ + {"creator": "constant_one", "layer": "event", "suffix": "output_one"}, + {"creator": "constant_two", "layer": "event", "suffix": "output_two"}, + ], + ) + m.observe( + observe_three, + name="observe_three_ws", + input_family=[ + {"creator": "constant_one", "layer": "event", "suffix": "output_one"}, + {"creator": "constant_two", "layer": "event", "suffix": "output_two"}, + {"creator": "constant_three", "layer": "event", "suffix": "output_three"}, + ], + ) From a28c500728d147d1b0b327ef47b0751778841cf9 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 23 Mar 2026 11:00:29 -0500 Subject: [PATCH 11/15] Removed accidental RNTuple inclusion from factories. The classes it uses aren't available yet. --- form/util/factories.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/form/util/factories.hpp b/form/util/factories.hpp index d1ee228e..9a0bbe7f 100644 --- a/form/util/factories.hpp +++ b/form/util/factories.hpp @@ -94,7 +94,7 @@ namespace form::detail::experimental { #endif // USE_ROOT_STORAGE } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { #ifdef USE_RNTUPLE_STORAGE - return std::make_shared(name); + //return std::make_shared(name); #endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { @@ -118,7 +118,7 @@ namespace form::detail::experimental { #endif // USE_ROOT_STORAGE } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { #ifdef USE_RNTUPLE_STORAGE - return std::make_shared(name); + //return std::make_shared(name); #endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { From 7c9742a4436b3d7356f647c620e606768b55b824 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 23 Mar 2026 15:24:00 -0500 Subject: [PATCH 12/15] Removed accidental mention of RNTuple components that was setting off code coverage test. --- form/util/factories.hpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/form/util/factories.hpp b/form/util/factories.hpp index bd5bb96f..ba6eef09 100644 --- a/form/util/factories.hpp +++ b/form/util/factories.hpp @@ -46,10 +46,6 @@ namespace form::detail::experimental { #ifdef USE_ROOT_STORAGE return std::make_shared(name); #endif // USE_ROOT_STORAGE - } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { -#ifdef USE_RNTUPLE_STORAGE - //return std::make_shared(name); -#endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { #ifdef USE_HDF5_STORAGE @@ -70,10 +66,6 @@ namespace form::detail::experimental { #ifdef USE_ROOT_STORAGE return std::make_shared(name); #endif // USE_ROOT_STORAGE - } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { -#ifdef USE_RNTUPLE_STORAGE - //return std::make_shared(name); -#endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { #ifdef USE_HDF5_STORAGE @@ -95,10 +87,6 @@ namespace form::detail::experimental { #ifdef USE_ROOT_STORAGE return std::make_shared(name); #endif // USE_ROOT_STORAGE - } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { -#ifdef USE_RNTUPLE_STORAGE - //return std::make_shared(name); -#endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { #ifdef USE_HDF5_STORAGE @@ -120,10 +108,6 @@ namespace form::detail::experimental { #ifdef USE_ROOT_STORAGE return std::make_shared(name); #endif // USE_ROOT_STORAGE - } else if (form::technology::GetMinor(tech) == form::technology::ROOT_RNTUPLE_MINOR) { -#ifdef USE_RNTUPLE_STORAGE - //return std::make_shared(name); -#endif // USE_RNTUPLE_STORAGE } } else if (form::technology::GetMajor(tech) == form::technology::HDF5_MAJOR) { #ifdef USE_HDF5_STORAGE From 82aa92f4dbbb97d0ff927372f0ef9724f1683092 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 23 Mar 2026 17:00:11 -0500 Subject: [PATCH 13/15] Removed attribute for TBranch read container that is not relevant to reading. --- form/root_storage/root_tbranch_read_container.cpp | 10 ---------- form/root_storage/root_tbranch_read_container.hpp | 2 -- 2 files changed, 12 deletions(-) diff --git a/form/root_storage/root_tbranch_read_container.cpp b/form/root_storage/root_tbranch_read_container.cpp index 63b25dab..d9680d78 100644 --- a/form/root_storage/root_tbranch_read_container.cpp +++ b/form/root_storage/root_tbranch_read_container.cpp @@ -19,16 +19,6 @@ ROOT_TBranch_Read_ContainerImp::ROOT_TBranch_Read_ContainerImp(std::string const { } -void ROOT_TBranch_Read_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_Read_ContainerImp accepts some attributes, but not " + - key); - } -} - void ROOT_TBranch_Read_ContainerImp::setFile(std::shared_ptr file) { this->Storage_Associative_Read_Container::setFile(file); diff --git a/form/root_storage/root_tbranch_read_container.hpp b/form/root_storage/root_tbranch_read_container.hpp index b9ccadff..c2abed05 100644 --- a/form/root_storage/root_tbranch_read_container.hpp +++ b/form/root_storage/root_tbranch_read_container.hpp @@ -19,8 +19,6 @@ namespace form::detail::experimental { ROOT_TBranch_Read_ContainerImp(std::string const& name); ~ROOT_TBranch_Read_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; From 0153e498f775d0b19a963f2944bc7cac50d42054 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Mon, 23 Mar 2026 17:02:29 -0500 Subject: [PATCH 14/15] Removed Write command in TTree read container that's no longer needed. --- form/root_storage/root_ttree_read_container.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/form/root_storage/root_ttree_read_container.cpp b/form/root_storage/root_ttree_read_container.cpp index 37f4ef86..62f05e89 100644 --- a/form/root_storage/root_ttree_read_container.cpp +++ b/form/root_storage/root_ttree_read_container.cpp @@ -15,10 +15,6 @@ ROOT_TTree_Read_ContainerImp::ROOT_TTree_Read_ContainerImp(std::string const& na ROOT_TTree_Read_ContainerImp::~ROOT_TTree_Read_ContainerImp() { - if (m_tree != nullptr) { - m_tree->Write(); - delete m_tree; - } } void ROOT_TTree_Read_ContainerImp::setFile(std::shared_ptr file) From c47a8c61fbc6d834956d0575cd7855c684c8db25 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:08:34 +0000 Subject: [PATCH 15/15] Apply clang-format fixes --- form/root_storage/root_ttree_read_container.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/form/root_storage/root_ttree_read_container.cpp b/form/root_storage/root_ttree_read_container.cpp index 62f05e89..5be81c03 100644 --- a/form/root_storage/root_ttree_read_container.cpp +++ b/form/root_storage/root_ttree_read_container.cpp @@ -13,9 +13,7 @@ ROOT_TTree_Read_ContainerImp::ROOT_TTree_Read_ContainerImp(std::string const& na { } -ROOT_TTree_Read_ContainerImp::~ROOT_TTree_Read_ContainerImp() -{ -} +ROOT_TTree_Read_ContainerImp::~ROOT_TTree_Read_ContainerImp() {} void ROOT_TTree_Read_ContainerImp::setFile(std::shared_ptr file) {