@@ -127,6 +127,43 @@ struct get_pair {
127127 }
128128};
129129
130+ // Advance from a DataRefIndices to the next one in O(1), reading only the
131+ // current header. Intended for use in iterators so that ++ is O(1) rather
132+ // than the O(n) while-loop that get_pair requires.
133+ //
134+ // New-style block (splitPayloadIndex == splitPayloadParts > 1):
135+ // layout: [header, payload_0, payload_1, ..., payload_{N-1}]
136+ // advance within block while payloads remain, then jump to the next block.
137+ //
138+ // Old-style block (splitPayloadIndex != splitPayloadParts, splitPayloadParts > 1)
139+ // or single pair (splitPayloadParts == 0):
140+ // layout: [header, payload] – always advance by two messages.
141+ struct get_next_pair {
142+ DataRefIndices current;
143+ template <typename R>
144+ requires std::ranges::random_access_range<R> && std::ranges::sized_range<R>
145+ friend DataRefIndices operator |(R&& r, get_next_pair self)
146+ {
147+ size_t hIdx = self.current .headerIdx ;
148+ auto * header = o2::header::get<o2::header::DataHeader*>(r[hIdx]->GetData ());
149+ if (!header) {
150+ throw std::runtime_error (" Not a DataHeader" );
151+ }
152+ if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts ) {
153+ // New-style block: one header followed by splitPayloadParts contiguous payloads.
154+ if (self.current .payloadIdx < hIdx + header->splitPayloadParts ) {
155+ // More sub-payloads remain in this block.
156+ return {hIdx, self.current .payloadIdx + 1 };
157+ }
158+ // Last sub-payload consumed; move to the first pair of the next block.
159+ size_t nextHIdx = hIdx + header->splitPayloadParts + 1 ;
160+ return {nextHIdx, nextHIdx + 1 };
161+ }
162+ // Old-style [header, payload] pairs or a single pair: advance by two messages.
163+ return {hIdx + 2 , hIdx + 3 };
164+ }
165+ };
166+
130167struct get_dataref_indices {
131168 size_t part;
132169 size_t subPart;
0 commit comments