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