wfdb/record/segment_reader.rs
1use crate::record::segment::SegmentManager;
2use crate::{Error, MultiSignalReader, Result, Sample, SegmentInfo};
3use std::path::PathBuf;
4
5/// Reader for multi-segment records with seeking support.
6///
7/// Handles automatic segment switching and provides unified access to signals
8/// across all segments in a multi-segment record.
9///
10/// # Examples
11///
12/// ```no_run
13/// use wfdb::Record;
14///
15/// # fn main() -> wfdb::Result<()> {
16/// let record = Record::open("data/multi_segment_record")?;
17/// let mut reader = record.segment_reader()?;
18///
19/// // Read frames across all segments
20/// while let Some(frame) = reader.read_frame()? {
21/// // Process frame...
22/// }
23/// # Ok(())
24/// # }
25/// ```
26pub struct SegmentReader {
27 /// Segment manager for handling segment metadata and switching.
28 segment_manager: SegmentManager,
29 /// Current multi-signal reader (for current segment).
30 current_reader: Option<MultiSignalReader>,
31 /// Total samples read across all segments.
32 samples_read: u64,
33}
34
35impl SegmentReader {
36 /// Create a new segment reader.
37 pub(crate) fn new(base_path: PathBuf, segments: Vec<SegmentInfo>) -> Self {
38 let segment_manager = SegmentManager::new(base_path, segments);
39
40 Self {
41 segment_manager,
42 current_reader: None,
43 samples_read: 0,
44 }
45 }
46
47 /// Read one frame (one sample from each signal).
48 ///
49 /// Returns `None` when all segments have been read.
50 ///
51 /// # Errors
52 ///
53 /// Returns an error if:
54 /// - A segment cannot be loaded
55 /// - A frame cannot be read
56 pub fn read_frame(&mut self) -> Result<Option<Vec<Sample>>> {
57 loop {
58 // Try to read from current reader
59 if let Some(reader) = &mut self.current_reader {
60 let frame = reader.read_frame()?;
61 if !frame.is_empty() {
62 self.samples_read += 1;
63 return Ok(Some(frame));
64 }
65 // Current segment exhausted, move to next
66 }
67
68 // Move to next segment
69 if !self.advance_segment()? {
70 return Ok(None); // No more segments
71 }
72 }
73 }
74
75 /// Read multiple frames.
76 ///
77 /// # Errors
78 ///
79 /// Returns an error if:
80 /// - A segment cannot be loaded
81 /// - Frames cannot be read
82 pub fn read_frames(&mut self, count: usize) -> Result<Vec<Vec<Sample>>> {
83 let mut frames = Vec::with_capacity(count);
84 for _ in 0..count {
85 match self.read_frame()? {
86 Some(frame) => frames.push(frame),
87 None => break,
88 }
89 }
90 Ok(frames)
91 }
92
93 /// Seek to a specific sample number across all segments.
94 ///
95 /// Automatically switches to the appropriate segment and positions
96 /// the reader at the target sample.
97 ///
98 /// Returns the actual sample position after seeking.
99 ///
100 /// # Errors
101 ///
102 /// Returns an error if:
103 /// - The segment containing the target sample cannot be loaded
104 /// - Seeking within the segment fails
105 pub fn seek_to_sample(&mut self, sample: u64) -> Result<u64> {
106 // Find which segment contains this sample
107 let segment_index = self.find_segment_for_sample(sample)?;
108
109 // Calculate offset within segment
110 let segment_start = if segment_index == 0 {
111 0
112 } else {
113 self.segment_manager
114 .segment_info(segment_index - 1)
115 .map_or(0, |s| s.num_samples)
116 };
117 let offset_in_segment = sample - segment_start;
118
119 // Switch to target segment
120 self.switch_to_segment(segment_index)?;
121
122 // Seek within segment
123 if let Some(reader) = &mut self.current_reader {
124 reader.seek_to_frame(offset_in_segment)?;
125 }
126
127 self.samples_read = sample;
128 Ok(sample)
129 }
130
131 /// Get current sample position across all segments.
132 #[must_use]
133 pub const fn position(&self) -> u64 {
134 self.samples_read
135 }
136
137 /// Get total number of samples across all segments.
138 #[must_use]
139 pub fn total_samples(&self) -> u64 {
140 self.segment_manager
141 .segment_info(self.segment_manager.num_segments() - 1)
142 .map_or(0, |s| s.num_samples)
143 }
144
145 /// Get current segment index.
146 #[must_use]
147 pub const fn current_segment(&self) -> usize {
148 self.segment_manager.current_index()
149 }
150
151 /// Get total number of segments.
152 #[must_use]
153 pub const fn num_segments(&self) -> usize {
154 self.segment_manager.num_segments()
155 }
156
157 // [Private helper methods]
158
159 /// Advance to the next segment.
160 ///
161 /// Returns `true` if successfully advanced, `false` if no more segments.
162 fn advance_segment(&mut self) -> Result<bool> {
163 let next_index = self.segment_manager.current_index() + 1;
164 if next_index >= self.segment_manager.num_segments() {
165 return Ok(false);
166 }
167
168 self.switch_to_segment(next_index)?;
169 Ok(true)
170 }
171
172 /// Switch to a specific segment.
173 fn switch_to_segment(&mut self, index: usize) -> Result<()> {
174 // Load segment data
175 self.segment_manager.load_segment(index)?;
176
177 // Get signals and base path for this segment
178 let signals = self.segment_manager.current_signals()?.to_vec();
179 let base_path = self.segment_manager.current_base_path()?.to_path_buf();
180
181 // Create new multi-signal reader for this segment
182 let reader = MultiSignalReader::new(&base_path, &signals)?;
183
184 self.current_reader = Some(reader);
185 Ok(())
186 }
187
188 /// Find which segment contains a given sample number.
189 fn find_segment_for_sample(&self, sample: u64) -> Result<usize> {
190 let mut cumulative = 0u64;
191 for i in 0..self.segment_manager.num_segments() {
192 let segment_info = self
193 .segment_manager
194 .segment_info(i)
195 .ok_or_else(|| Error::InvalidHeader("Segment not found".to_string()))?;
196
197 if sample < cumulative + segment_info.num_samples {
198 return Ok(i);
199 }
200 cumulative += segment_info.num_samples;
201 }
202
203 Err(Error::InvalidHeader(format!(
204 "Sample {sample} is beyond the end of the record"
205 )))
206 }
207}