-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[ntuple] Add support for attributes reading #21778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
silverweed
wants to merge
8
commits into
root-project:master
Choose a base branch
from
silverweed:ntuple_attr_read
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7ebe8ee
[ntuple] Reorganize meta field indexes/names a bit
silverweed e5485d8
[ntuple] Start introducing RNTupleAttrSetReader
silverweed 4def8a1
[ntuple] Change OpenWithDifferentAnchor to use RNTupleLink
silverweed 2076e4e
[ntuple] Move OpenWithDifferentAnchor to RPageSource
silverweed d008ce9
[ntuple] allow creating an AttrSetReader from RNTupleReader
silverweed b7e6b24
[ntuple] Add basic reading tests
silverweed f7ef86c
[ntuple] add GetAttributes methods to RNTupleAttrSetReader
silverweed 9bbb92d
[ntuple] Add more attributes tests
silverweed File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,246 @@ | ||
| /// \file ROOT/RNTupleAttrReading.hxx | ||
| /// \ingroup NTuple | ||
| /// \author Giacomo Parolini <giacomo.parolini@cern.ch> | ||
| /// \date 2026-04-01 | ||
| /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback | ||
| /// is welcome! | ||
|
|
||
| #ifndef ROOT7_RNTuple_Attr_Reading | ||
| #define ROOT7_RNTuple_Attr_Reading | ||
|
|
||
| #include <memory> | ||
| #include <optional> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #include <ROOT/RNTupleFillContext.hxx> | ||
| #include <ROOT/RNTupleAttrUtils.hxx> | ||
| #include <ROOT/RNTupleUtils.hxx> | ||
|
|
||
| namespace ROOT { | ||
|
|
||
| class REntry; | ||
| class RNTupleDescriptor; | ||
| class RNTupleModel; | ||
|
|
||
| namespace Experimental { | ||
|
|
||
| class RNTupleAttrEntryIterable; | ||
|
|
||
| // clang-format off | ||
| /** | ||
| \class ROOT::Experimental::RNTupleAttrRange | ||
| \ingroup NTuple | ||
| \brief A range of main entries referred to by an attribute entry | ||
|
|
||
| Each attribute entry contains a set of values referring to 0 or more contiguous entries in the main RNTuple. | ||
| This class represents that contiguous range of entries. | ||
| */ | ||
| // clang-format on | ||
| class RNTupleAttrRange final { | ||
| ROOT::NTupleSize_t fStart = 0; | ||
| ROOT::NTupleSize_t fLength = 0; | ||
|
|
||
| RNTupleAttrRange(ROOT::NTupleSize_t start, ROOT::NTupleSize_t length) : fStart(start), fLength(length) {} | ||
|
|
||
| public: | ||
| static RNTupleAttrRange FromStartLength(ROOT::NTupleSize_t start, ROOT::NTupleSize_t length) | ||
| { | ||
| return RNTupleAttrRange{start, length}; | ||
| } | ||
|
|
||
| /// Creates an AttributeRange from [start, end), where `end` is one past the last valid entry of the range | ||
| /// (`FromStartEnd(0, 10)` will create a range whose last valid index is 9). | ||
| static RNTupleAttrRange FromStartEnd(ROOT::NTupleSize_t start, ROOT::NTupleSize_t end) | ||
| { | ||
| R__ASSERT(end >= start); | ||
| return RNTupleAttrRange{start, end - start}; | ||
| } | ||
|
|
||
| RNTupleAttrRange() = default; | ||
|
|
||
| /// Returns the first valid entry index in the range. Returns nullopt if the range has zero length. | ||
| std::optional<ROOT::NTupleSize_t> GetFirst() const { return fLength ? std::make_optional(fStart) : std::nullopt; } | ||
| /// Returns the beginning of the range. Note that this is *not* a valid index in the range if the range has zero | ||
| /// length. | ||
| ROOT::NTupleSize_t GetStart() const { return fStart; } | ||
| /// Returns the last valid entry index in the range. Returns nullopt if the range has zero length. | ||
| std::optional<ROOT::NTupleSize_t> GetLast() const | ||
| { | ||
| return fLength ? std::make_optional(fStart + fLength - 1) : std::nullopt; | ||
| } | ||
| /// Returns one past the last valid index of the range, equal to `GetStart() + GetLength()`. | ||
| ROOT::NTupleSize_t GetEnd() const { return fStart + fLength; } | ||
| ROOT::NTupleSize_t GetLength() const { return fLength; } | ||
|
|
||
| /// Returns the pair { firstEntryIdx, lastEntryIdx } (inclusive). Returns nullopt if the range has zero length. | ||
| std::optional<std::pair<ROOT::NTupleSize_t, ROOT::NTupleSize_t>> GetFirstLast() const | ||
| { | ||
| return fLength ? std::make_optional(std::make_pair(fStart, fStart + fLength - 1)) : std::nullopt; | ||
| } | ||
| /// Returns the pair { start, length }. | ||
| std::pair<ROOT::NTupleSize_t, ROOT::NTupleSize_t> GetStartLength() const { return {fStart, fLength}; } | ||
| }; | ||
|
|
||
| // clang-format off | ||
| /** | ||
| \class ROOT::Experimental::RNTupleAttrSetReader | ||
| \ingroup NTuple | ||
| \brief Class used to read a RNTupleAttrSet in the context of a RNTupleReader | ||
|
|
||
| An RNTupleAttrSetReader is created via RNTupleReader::OpenAttributeSet. Once created, it may outlive its parent Reader. | ||
| Reading Attributes works similarly to reading regular RNTuple entries: you can either create entries or just use the | ||
| AttrSetReader Model's default entry and load data into it via LoadEntry. | ||
|
|
||
| ~~ {.cpp} | ||
| // Reading Attributes via RNTupleAttrSetReader | ||
| // ------------------------------------------- | ||
|
|
||
| // Assuming `reader` is a RNTupleReader: | ||
| auto attrSet = reader->OpenAttributeSet("MyAttrSet"); | ||
|
|
||
| // Just like how you would read a regular RNTuple, first get the pointer to the fields you want to read: | ||
| auto &attrEntry = attrSet->GetModel().GetDefaultEntry(); | ||
| auto pAttr = attrEntry->GetPtr<std::string>("myAttr"); | ||
|
|
||
| // Then select which attributes you want to read. E.g. read all attributes linked to the entry at index 10: | ||
| for (auto idx : attrSet->GetAttributes(10)) { | ||
| attrSet->LoadEntry(idx); | ||
| cout << "entry " << idx << " has attribute " << *pAttr << "\n"; | ||
| } | ||
| ~~ | ||
| */ | ||
| // clang-format on | ||
| class RNTupleAttrSetReader final { | ||
| friend class ROOT::RNTupleReader; | ||
| friend class RNTupleAttrEntryIterable; | ||
|
|
||
| /// List containing pairs { entryRange, entryIndex }, used to quickly find out which entries in the Attribute | ||
| /// RNTuple contain entries that overlap a given range. The list is sorted by range start, i.e. | ||
| /// entryRange.first.Start(). | ||
| std::vector<std::pair<RNTupleAttrRange, NTupleSize_t>> fEntryRanges; | ||
| /// The internal Reader used to read the AttributeSet RNTuple | ||
| std::unique_ptr<RNTupleReader> fReader; | ||
| /// The reconstructed user model | ||
| std::unique_ptr<ROOT::RNTupleModel> fUserModel; | ||
|
|
||
| static bool EntryRangesAreSorted(const decltype(fEntryRanges) &ranges); | ||
|
|
||
| explicit RNTupleAttrSetReader(std::unique_ptr<RNTupleReader> reader); | ||
|
|
||
| public: | ||
| RNTupleAttrSetReader(const RNTupleAttrSetReader &) = delete; | ||
| RNTupleAttrSetReader &operator=(const RNTupleAttrSetReader &) = delete; | ||
| RNTupleAttrSetReader(RNTupleAttrSetReader &&) = default; | ||
| RNTupleAttrSetReader &operator=(RNTupleAttrSetReader &&) = default; | ||
| ~RNTupleAttrSetReader() = default; | ||
|
|
||
| /// Returns the read-only descriptor of this attribute set | ||
| const ROOT::RNTupleDescriptor &GetDescriptor() const; | ||
| /// Returns the read-only model of this attribute set | ||
| const ROOT::RNTupleModel &GetModel() const { return *fUserModel; } | ||
|
|
||
| /// Creates an entry suitable for use with LoadEntry. | ||
| /// This is a convenience method equivalent to GetModel().CreateEntry(). | ||
| std::unique_ptr<REntry> CreateEntry(); | ||
|
|
||
| /// Loads the attribute entry at position `index` into the default entry. | ||
| /// Returns the range of main RNTuple entries that the loaded set of attributes refers to. | ||
| RNTupleAttrRange LoadEntry(NTupleSize_t index); | ||
| /// Loads the attribute entry at position `index` into the given entry. | ||
| /// Returns the range of main RNTuple entries that the loaded set of attributes refers to. | ||
| RNTupleAttrRange LoadEntry(NTupleSize_t index, REntry &entry); | ||
|
|
||
| /// Returns the number of all attribute entries in this attribute set. | ||
| std::size_t GetNEntries() const { return fEntryRanges.size(); } | ||
|
|
||
| /// Returns all the attributes in this Set. The returned attributes are sorted by entry range start. | ||
| RNTupleAttrEntryIterable GetAttributes(); | ||
| /// Returns all the attributes whose range contains index `entryIndex`. | ||
| RNTupleAttrEntryIterable GetAttributes(NTupleSize_t entryIndex); | ||
| /// Returns all the attributes whose range fully contains `[startEntry, endEntry)` | ||
| RNTupleAttrEntryIterable GetAttributesContainingRange(NTupleSize_t startEntry, NTupleSize_t endEntry); | ||
| /// Returns all the attributes whose range is fully contained in `[startEntry, endEntry)` | ||
| RNTupleAttrEntryIterable GetAttributesInRange(NTupleSize_t startEntry, NTupleSize_t endEntry); | ||
| }; | ||
|
|
||
| // clang-format off | ||
| /** | ||
| \class ROOT::Experimental::RNTupleAttrEntryIterable | ||
| \ingroup NTuple | ||
| \brief Iterable class used to loop over attribute entries. | ||
|
|
||
| This class allows to perform range-for iteration on some set of attributes, typically returned by the | ||
| RNTupleAttrSetReader::GetAttributes family of methods. | ||
|
|
||
| See the documentation of RNTupleAttrSetReader for example usage. | ||
| */ | ||
| // clang-format on | ||
| class RNTupleAttrEntryIterable final { | ||
| public: | ||
| struct RFilter { | ||
| RNTupleAttrRange fRange; | ||
| bool fIsContained; | ||
| }; | ||
|
|
||
| private: | ||
| RNTupleAttrSetReader *fReader = nullptr; | ||
| std::optional<RFilter> fFilter; | ||
|
|
||
| public: | ||
| class RIterator final { | ||
| private: | ||
| using Iter_t = decltype(std::declval<RNTupleAttrSetReader>().fEntryRanges.begin()); | ||
| Iter_t fCur, fEnd; | ||
| std::optional<RFilter> fFilter; | ||
|
|
||
| Iter_t SkipFiltered() const; | ||
| bool FullyContained(RNTupleAttrRange range) const; | ||
|
|
||
| public: | ||
| using iterator_category = std::forward_iterator_tag; | ||
| using iterator = RIterator; | ||
| using value_type = NTupleSize_t; | ||
| using difference_type = std::ptrdiff_t; | ||
| using pointer = const value_type *; | ||
| using reference = const value_type &; | ||
|
|
||
| RIterator(Iter_t iter, Iter_t end, std::optional<RFilter> filter) : fCur(iter), fEnd(end), fFilter(filter) | ||
| { | ||
| if (fFilter) { | ||
| if (fFilter->fRange.GetLength() == 0) | ||
| fCur = end; | ||
| else | ||
| fCur = SkipFiltered(); | ||
| } | ||
| } | ||
| iterator operator++() | ||
| { | ||
| ++fCur; | ||
| fCur = SkipFiltered(); | ||
| return *this; | ||
| } | ||
| iterator operator++(int) | ||
| { | ||
| iterator it = *this; | ||
| operator++(); | ||
| return it; | ||
| } | ||
| reference operator*() { return fCur->second; } | ||
| bool operator!=(const iterator &rh) const { return !operator==(rh); } | ||
| bool operator==(const iterator &rh) const { return fCur == rh.fCur; } | ||
| }; | ||
|
|
||
| explicit RNTupleAttrEntryIterable(RNTupleAttrSetReader &reader, std::optional<RFilter> filter = {}) | ||
| : fReader(&reader), fFilter(filter) | ||
| { | ||
| } | ||
|
|
||
| RIterator begin() { return RIterator{fReader->fEntryRanges.begin(), fReader->fEntryRanges.end(), fFilter}; } | ||
| RIterator end() { return RIterator{fReader->fEntryRanges.end(), fReader->fEntryRanges.end(), fFilter}; } | ||
| }; | ||
|
|
||
| } // namespace Experimental | ||
| } // namespace ROOT | ||
|
|
||
| #endif | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.