ADTF File Library
Loading...
Searching...
No Matches
multi_file_reader.h
Go to the documentation of this file.
1
16
17#pragma once
18#include <adtf_file/reader.h>
19#include <unordered_map>
20#include <vector>
21#include <exception>
22
23namespace adtfdat_processing
24{
25
31{
32public:
37
38 std::string getReaderIdentifier() const override;
39 void open(const std::string& file_name,
40 std::shared_ptr<adtf_file::SampleFactory> sample_factory,
41 std::shared_ptr<adtf_file::StreamTypeFactory> stream_type_factory) override;
42
43 std::vector<adtf_file::Stream> getStreams() const override;
45 std::optional<uint64_t> getItemCount() const override;
46 std::optional<double> getProgress() const override;
47
48 uint64_t getItemIndexForTimeStamp(std::chrono::nanoseconds time_stamp) override;
49 uint64_t getItemIndexForStreamItemIndex(uint16_t stream_id, uint64_t stream_item_index) override;
50 std::shared_ptr<const adtf_file::StreamType> getStreamTypeBefore(uint64_t item_index, uint16_t stream_id) override;
51 void seekTo(uint64_t item_index) override;
52
53protected:
59
68 std::shared_ptr<adtf_file::Reader> addFile(const std::string& file_name,
69 std::shared_ptr<adtf_file::SampleFactory> sample_factory,
70 std::shared_ptr<adtf_file::StreamTypeFactory> stream_type_factory);
74 void build();
75
76private:
77 class ReaderSequence
78 {
79 public:
80 ReaderSequence() = default;
81 ReaderSequence(const ReaderSequence&) = delete;
82 ReaderSequence(ReaderSequence&&) = default;
83
84 void addReader(const std::shared_ptr<Reader>& reader, std::optional<uint64_t> first_item_index)
85 {
86 if (!_elements.empty())
87 {
88 const auto get_streams = [](adtf_file::Reader& reader)
89 {
90 std::unordered_map<uint16_t, std::string> streams;
91 for (const auto& stream : reader.getStreams())
92 {
93 streams[stream.stream_id] = stream.name;
94 }
95 return streams;
96 };
97
98 if (get_streams(*reader) != get_streams(*_elements.front().reader))
99 {
100 throw std::runtime_error(
101 "Sequence files (split files) do not share the same streams with the same stream ids.");
102 }
103 }
104
105 try
106 {
107 Element element{reader, reader->getNextItem(), first_item_index};
108 _elements.insert(std::lower_bound(_elements.begin(), _elements.end(), element), element);
109 }
111 {
112 if (_elements.empty())
113 {
114 _empty_streams_cache = reader->getStreams();
115 }
116 return;
117 }
118
119 _current = _elements.begin();
120 _empty_streams_cache.clear();
121 }
122
123 adtf_file::FileItem getNext()
124 {
125 while (_current != _elements.end())
126 {
127 if (!_current->item_cache.empty())
128 {
129 auto item = std::move(_current->item_cache.front());
130 _current->item_cache.pop_front();
131 return item;
132 }
133
134 try
135 {
136 auto item = _current->reader->getNextItem();
137 adjustTimeStamp(item);
138 return item;
139 }
141 {
142 ++_current;
143 if (_current != _elements.end())
144 {
145 if (const auto next = std::next(_current); next != _elements.end())
146 {
147 _current_timestamp_upper_bound = next->first_item_timestamp;
148 }
149 else
150 {
151 _current_timestamp_upper_bound.reset();
152 }
153
154 // Need additional synthetic items because this reader switch isn't visible to caller.
155 _current->item_cache.insert(_current->item_cache.begin(),
156 _current->initial_stream_types.cbegin(),
157 _current->initial_stream_types.cend());
158 }
159 }
160 }
161
163 }
164
165 uint64_t getItemIndexForTimeStamp(std::chrono::nanoseconds time_stamp)
166 {
167 std::exception_ptr last_exception;
168 for (auto element = _elements.begin(); element != _elements.end(); ++element)
169 {
170 if (auto next = std::next(element); next != _elements.end())
171 {
172 if (next->first_item_timestamp < time_stamp)
173 {
174 // Don't even query, this reader is ordered before another one that is also ordered before
175 // time_stamp.
176 continue;
177 }
178 else if (element->first_item_timestamp >= time_stamp)
179 {
180 // No need to query, we already know it's the first item of this reader.
181 _seek_cache.emplace_back(element, *element->first_item_index);
182 return _seek_cache.size() - 1;
183 }
184 }
185 const auto seekable_reader = std::dynamic_pointer_cast<adtf_file::SeekableReader>(element->reader);
186 try
187 {
188 const auto item_index = seekable_reader->getItemIndexForTimeStamp(time_stamp);
189 _seek_cache.emplace_back(element, item_index);
190 return _seek_cache.size() - 1;
191 }
192 catch (...)
193 {
194 last_exception = std::current_exception();
195 }
196 }
197 if (last_exception)
198 {
199 std::rethrow_exception(last_exception);
200 }
201 // Reachable when _elements is empty.
203 }
204
205 std::shared_ptr<const adtf_file::StreamType> getStreamTypeBefore(uint64_t item_index, uint16_t stream_id)
206 {
207 if (item_index >= _seek_cache.size())
208 {
209 throw std::logic_error("Invalid seek cache");
210 }
211
212 const auto cache = _seek_cache[item_index];
213 const auto seekable_reader = std::dynamic_pointer_cast<adtf_file::SeekableReader>(cache.first->reader);
214 return seekable_reader->getStreamTypeBefore(cache.second, stream_id);
215 }
216
217 void seekTo(uint64_t item_index)
218 {
219 if (item_index >= _seek_cache.size())
220 {
221 throw std::logic_error("Invalid seek cache");
222 }
223
224 const auto cache = _seek_cache[item_index];
225
226 {
227 const auto seekable_reader = std::dynamic_pointer_cast<adtf_file::SeekableReader>(cache.first->reader);
228 seekable_reader->seekTo(cache.second);
229 cache.first->item_cache.clear();
230 }
231
232 _current = cache.first;
233
234 if (const auto next = std::next(_current); next != _elements.end())
235 {
236 _current_timestamp_upper_bound = next->first_item_timestamp;
237 }
238 else
239 {
240 _current_timestamp_upper_bound.reset();
241 }
242
243 for (auto file = std::next(cache.first); file != _elements.end(); ++file)
244 {
245 const auto seekable_reader = std::dynamic_pointer_cast<adtf_file::SeekableReader>(file->reader);
246 seekable_reader->seekTo(*file->first_item_index);
247 file->item_cache.clear();
248 }
249 }
250
251 std::vector<adtf_file::Stream> getStreams() const
252 {
253 if (_elements.empty())
254 {
255 return _empty_streams_cache;
256 }
257
258 auto streams = _elements.front().reader->getStreams();
259
260 for (auto element = ++_elements.begin(); element != _elements.end(); ++element)
261 {
262 const auto next_parts = element->reader->getStreams();
263 size_t stream_index = 0;
264 for (const auto& stream : next_parts)
265 {
266 // All items from original reader plus one synthentic stream type item per additional reader and
267 // stream.
268 streams[stream_index].item_count += stream.item_count + streams.size();
269 streams[stream_index].timestamp_of_last_item = stream.timestamp_of_last_item;
270 ++stream_index;
271 }
272 }
273
274 return streams;
275 }
276
277 std::optional<uint64_t> getItemCount() const
278 {
279 uint64_t item_count = 0;
280 for (const auto& element : _elements)
281 {
282 if (const auto reader_item_count = element.reader->getItemCount())
283 {
284 item_count += *reader_item_count + element.initial_stream_types.size();
285 }
286 else
287 {
288 return {};
289 }
290 }
291 if (!_elements.empty())
292 {
293 // Those items are never visible.
294 item_count -= _elements.front().initial_stream_types.size();
295 }
296 return item_count;
297 }
298
299 std::optional<double> getProgress() const
300 {
301 if (_current != _elements.end())
302 {
303 double progress =
304 100.0 * ::std::distance<Sequence::const_iterator>(_elements.begin(), _current) / _elements.size();
305
306 if (const auto file_progress = _current->reader->getProgress())
307 {
308 progress += std::min(std::max(*file_progress, 0.0), 100.0) / _elements.size();
309 }
310 return progress;
311 }
312 else
313 {
314 return 100.0;
315 }
316 }
317
318 private:
319 void adjustTimeStamp(adtf_file::FileItem& item)
320 {
321 if (_current_timestamp_upper_bound && item.time_stamp > *_current_timestamp_upper_bound)
322 {
323 item.time_stamp = *_current_timestamp_upper_bound;
324 }
325 }
326
327 struct Element
328 {
329 Element(std::shared_ptr<Reader> reader,
330 adtf_file::FileItem first_item,
331 std::optional<uint64_t> first_item_index):
332 reader(std::move(reader)),
333 first_item_timestamp(first_item.time_stamp),
334 first_item_index(first_item_index),
335 item_cache({std::move(first_item)})
336 {
337 for (const auto& stream : this->reader->getStreams())
338 {
339 initial_stream_types.push_back(
340 adtf_file::FileItem{stream.stream_id, first_item_timestamp, stream.initial_type});
341 }
342 }
343
344 std::shared_ptr<Reader> reader;
345 // Item of first timestamp for timestamp correction in file sequence
346 std::chrono::nanoseconds first_item_timestamp;
347 // First item index if the reader is seekable
348 std::optional<uint64_t> first_item_index;
349
350 // Already read and/or synthetic items
351 std::deque<adtf_file::FileItem> item_cache;
352
353 // Synthetic items for initial stream types on roll over
354 std::vector<adtf_file::FileItem> initial_stream_types;
355
356 operator const std::chrono::nanoseconds&() const
357 {
358 return first_item_timestamp;
359 }
360
361 bool operator<(const Element& rhs) const
362 {
363 return first_item_timestamp < rhs.first_item_timestamp;
364 }
365 };
366
367 using Sequence = std::vector<Element>;
368 Sequence _elements;
369 Sequence::iterator _current = _elements.end();
370 std::vector<adtf_file::Stream> _empty_streams_cache;
371 std::optional<std::chrono::nanoseconds> _current_timestamp_upper_bound;
372
373 std::vector<std::pair<Sequence::iterator, uint64_t>> _seek_cache;
374 };
375
376 struct StreamGroup
377 {
378 ReaderSequence sequence;
379 std::unordered_map<uint16_t, uint16_t> stream_ids;
380
381 adtf_file::FileItem getNext()
382 {
383 auto item = sequence.getNext();
384 item.stream_id = stream_ids[item.stream_id];
385 return item;
386 }
387 };
388
389 std::vector<std::pair<std::shared_ptr<Reader>, std::optional<uint64_t>>> _readers;
390 std::vector<StreamGroup> _stream_groups;
391 std::vector<adtf_file::Stream> _streams;
392 struct GroupStream
393 {
394 size_t stream_group_index;
395 uint16_t stream_id;
396 };
397 std::vector<GroupStream> _group_streams;
398
399 struct QueueItem
400 {
401 StreamGroup* file;
402 adtf_file::FileItem file_item;
403 };
404 std::multimap<std::chrono::nanoseconds, QueueItem> _queue;
405
406 using stream_group_indices = std::vector<std::optional<uint64_t>>;
407 using stream_group_cache_item =
408 std::pair<std::chrono::nanoseconds, stream_group_indices>;
409 std::vector<stream_group_cache_item> _seek_cache;
410
411 bool _all_readers_seekable = true;
412};
413
418
419} // namespace adtfdat_processing
Definition reader.h:345
class to create or read a file item. This file item is either a sample, streamtype or trigger.
Definition reader.h:172
std::chrono::nanoseconds time_stamp
time stamp of the file item
Definition reader.h:177
uint16_t stream_id
stream id the file items belongs to
Definition reader.h:175
Definition reader.h:383
Default Reader factory implementation for readers using a standard default CTOR.
Definition reader.h:367
Definition reader.h:189
Interface for seekable Reader, where the file position can be adapted.
Definition reader.h:288
std::vector< adtf_file::Stream > getStreams() const override
Get the Streams.
adtf_file::FileItem getNextItem() override
uint64_t getItemIndexForStreamItemIndex(uint16_t stream_id, uint64_t stream_item_index) override
std::optional< uint64_t > getItemCount() const override
Get the Item Count. This gets the overall count of all items (samples, stream types and triggers) of ...
void open(const std::string &file_name, std::shared_ptr< adtf_file::SampleFactory > sample_factory, std::shared_ptr< adtf_file::StreamTypeFactory > stream_type_factory) override
opens a file by the given filename. The given factories must be used to create samples and streamtype...
uint64_t getItemIndexForTimeStamp(std::chrono::nanoseconds time_stamp) override
std::shared_ptr< adtf_file::Reader > addFile(const std::string &file_name, std::shared_ptr< adtf_file::SampleFactory > sample_factory, std::shared_ptr< adtf_file::StreamTypeFactory > stream_type_factory)
Adds a file to the multi file reader and opens the file if supported.
std::string getReaderIdentifier() const override
Get the Reader Identifier of the Reader.
std::shared_ptr< const adtf_file::StreamType > getStreamTypeBefore(uint64_t item_index, uint16_t stream_id) override
std::optional< double > getProgress() const override
Get the Progress, a relative file position between 0.0 and 1.0.
void build()
Merges the streams.
virtual adtf_file::ReaderFactories getFactories() const
Get all supported reader factories of this multireader.
void seekTo(uint64_t item_index) override
namespace for ADTF DAT Processing library.
Definition ddl_helpers.h:38
adtf_file::ReaderFactoryImplementation< MultiFileReader > MultiFileReaderFactory
Default reader factory implementation for the MultiFileReader.
Definition multi_file_reader.h:417
utils5ext::exceptions::EndOfFile EndOfFile
Exception to indicate the end of file was reached.
Definition indexedfile_types.h:114