@@ -83,3 +83,145 @@ std::unique_ptr<ROOT::REntry> ROOT::Experimental::RNTupleAttrSetReader::CreateEn
8383 return fUserModel ->CreateEntry ();
8484}
8585
86+ // Entry ranges should be sorted with respect to GetStart by construction.
87+ bool ROOT::Experimental::RNTupleAttrSetReader::EntryRangesAreSorted (const decltype (fEntryRanges ) &ranges)
88+ {
89+ ROOT::NTupleSize_t prevStart = 0 ;
90+ for (const auto &[range, _] : ranges) {
91+ if (range.GetStart () < prevStart)
92+ return false ;
93+ prevStart = range.GetStart ();
94+ }
95+ return true ;
96+ };
97+
98+ std::vector<ROOT::NTupleSize_t>
99+ ROOT::Experimental::RNTupleAttrSetReader::GetAttributesRangeInternal (NTupleSize_t startEntry, NTupleSize_t endEntry,
100+ bool rangeIsContained)
101+ {
102+ std::vector<ROOT::NTupleSize_t> result;
103+
104+ if (endEntry < startEntry) {
105+ R__LOG_WARNING (ROOT::Internal::NTupleLog ())
106+ << " end < start when getting attributes from Attribute Set '" << GetDescriptor ().GetName ()
107+ << " ' (range given: [" << startEntry << " , " << endEntry << " ]." ;
108+ return result;
109+ }
110+
111+ assert (EntryRangesAreSorted (fEntryRanges ));
112+
113+ const auto FullyContained = [rangeIsContained](auto startInner, auto endInner, auto startOuter, auto endOuter) {
114+ if (rangeIsContained) {
115+ std::swap (startOuter, startInner);
116+ std::swap (endOuter, endInner);
117+ }
118+ return startOuter <= startInner && endInner <= endOuter;
119+ };
120+
121+ // TODO: consider using binary search, since fEntryRanges is sorted
122+ // (maybe it should be done only if the size of the list is bigger than a threshold).
123+ for (const auto &[range, index] : fEntryRanges ) {
124+ const auto &firstLast = range.GetFirstLast ();
125+ if (!firstLast)
126+ continue ;
127+
128+ const auto &[first, last] = *firstLast;
129+ if (first >= endEntry)
130+ break ; // We can break here because fEntryRanges is sorted.
131+
132+ if (FullyContained (startEntry, endEntry, first, last + 1 )) {
133+ result.push_back (index);
134+ }
135+ }
136+
137+ return result;
138+ }
139+
140+ ROOT::Experimental::RNTupleAttrEntryIterable
141+ ROOT::Experimental::RNTupleAttrSetReader::GetAttributesContainingRange (NTupleSize_t startEntry, NTupleSize_t endEntry)
142+ {
143+ RNTupleAttrRange range;
144+ if (endEntry <= startEntry) {
145+ R__LOG_WARNING (ROOT::Internal::NTupleLog ())
146+ << " empty range given when getting attributes from Attribute Set '" << GetDescriptor ().GetName ()
147+ << " ' (range given: [" << startEntry << " , " << endEntry << " ))." ;
148+ // Make sure we find 0 entries
149+ range = RNTupleAttrRange::FromStartLength (startEntry, 0 );
150+ } else {
151+ range = RNTupleAttrRange::FromStartEnd (startEntry, endEntry);
152+ }
153+ RNTupleAttrEntryIterable::RFilter filter{range, false };
154+ return RNTupleAttrEntryIterable{*this , filter};
155+ }
156+
157+ ROOT::Experimental::RNTupleAttrEntryIterable
158+ ROOT::Experimental::RNTupleAttrSetReader::GetAttributesInRange (NTupleSize_t startEntry, NTupleSize_t endEntry)
159+ {
160+ RNTupleAttrRange range;
161+ if (endEntry <= startEntry) {
162+ R__LOG_WARNING (ROOT::Internal::NTupleLog ())
163+ << " empty range given when getting attributes from Attribute Set '" << GetDescriptor ().GetName ()
164+ << " ' (range given: [" << startEntry << " , " << endEntry << " ))." ;
165+ // Make sure we find 0 entries
166+ range = RNTupleAttrRange::FromStartLength (startEntry, 0 );
167+ } else {
168+ range = RNTupleAttrRange::FromStartEnd (startEntry, endEntry);
169+ }
170+ RNTupleAttrEntryIterable::RFilter filter{range, true };
171+ return RNTupleAttrEntryIterable{*this , filter};
172+ }
173+
174+ ROOT::Experimental::RNTupleAttrEntryIterable
175+ ROOT::Experimental::RNTupleAttrSetReader::GetAttributes (NTupleSize_t entryIndex)
176+ {
177+ RNTupleAttrEntryIterable::RFilter filter{RNTupleAttrRange::FromStartEnd (entryIndex, entryIndex + 1 ), false };
178+ return RNTupleAttrEntryIterable{*this , filter};
179+ }
180+
181+ ROOT::Experimental::RNTupleAttrEntryIterable ROOT::Experimental::RNTupleAttrSetReader::GetAttributes ()
182+ {
183+ return RNTupleAttrEntryIterable{*this };
184+ }
185+
186+ //
187+ // RNTupleAttrEntryIterable
188+ //
189+ bool ROOT::Experimental::RNTupleAttrEntryIterable::RIterator::FullyContained (RNTupleAttrRange range) const
190+ {
191+ assert (fFilter );
192+ if (fFilter ->fIsContained ) {
193+ return fFilter ->fRange .GetStart () <= range.GetStart () && range.GetEnd () <= fFilter ->fRange .GetEnd ();
194+ } else {
195+ return range.GetStart () <= fFilter ->fRange .GetStart () && fFilter ->fRange .GetEnd () <= range.GetEnd ();
196+ }
197+ }
198+
199+ ROOT::Experimental::RNTupleAttrEntryIterable::RIterator::Iter_t
200+ ROOT::Experimental::RNTupleAttrEntryIterable::RIterator::Next () const
201+ {
202+ // TODO: consider using binary search, since fEntryRanges is sorted
203+ // (maybe it should be done only if the size of the list is bigger than a threshold).
204+ for (auto it = fCur ; it != fEnd ; ++it) {
205+ const auto &[range, index] = *it;
206+ // If we have no filter, every entry is valid.
207+ if (!fFilter )
208+ return it;
209+
210+ const auto &firstLast = range.GetFirstLast ();
211+ // If this is nullopt it means this is a zero-length entry: we always skip those except
212+ // for the "catch-all" GetAttributes() (which is when fFilter is also nullopt).
213+ if (!firstLast)
214+ continue ;
215+
216+ const auto &[first, last] = *firstLast;
217+ if (first >= fFilter ->fRange .GetEnd ()) {
218+ // Since fEntryRanges is sorted we know we are at the end of the iteration
219+ // TODO: tweak fEnd to directly pass the last entry?
220+ return fEnd ;
221+ }
222+
223+ if (FullyContained (RNTupleAttrRange::FromStartEnd (first, last + 1 )))
224+ return it;
225+ }
226+ return fEnd ;
227+ }
0 commit comments