Skip to main content

wedeo_format/
io.rs

1use std::io::{Read, Seek, SeekFrom, Write};
2
3use wedeo_core::error::{Error, Result};
4
5/// Low-level I/O context trait. Implementations provide raw byte-level access
6/// to a data source (file, network, pipe, etc.).
7pub trait IoContext: Send + Sync {
8    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
9    fn write(&mut self, buf: &[u8]) -> Result<usize>;
10    fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
11    fn tell(&mut self) -> Result<u64>;
12    fn size(&mut self) -> Result<u64>;
13    fn is_seekable(&self) -> bool;
14}
15
16/// File-based I/O context.
17pub struct FileIo {
18    file: std::fs::File,
19}
20
21impl FileIo {
22    pub fn open(path: &str) -> Result<Self> {
23        let file = std::fs::File::open(path)?;
24        Ok(Self { file })
25    }
26
27    pub fn create(path: &str) -> Result<Self> {
28        let file = std::fs::OpenOptions::new()
29            .read(true)
30            .write(true)
31            .create(true)
32            .truncate(true)
33            .open(path)?;
34        Ok(Self { file })
35    }
36}
37
38impl IoContext for FileIo {
39    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
40        Ok(self.file.read(buf)?)
41    }
42
43    fn write(&mut self, buf: &[u8]) -> Result<usize> {
44        Ok(self.file.write(buf)?)
45    }
46
47    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
48        Ok(self.file.seek(pos)?)
49    }
50
51    fn tell(&mut self) -> Result<u64> {
52        Ok(self.file.stream_position()?)
53    }
54
55    fn size(&mut self) -> Result<u64> {
56        let metadata = self.file.metadata()?;
57        Ok(metadata.len())
58    }
59
60    fn is_seekable(&self) -> bool {
61        true
62    }
63}
64
65/// Dead I/O stub — all operations return errors.
66/// Used after `take_inner()` transfers ownership of the real I/O context.
67struct DeadIo;
68
69impl IoContext for DeadIo {
70    fn read(&mut self, _buf: &mut [u8]) -> Result<usize> {
71        Err(Error::Other("I/O context has been taken".into()))
72    }
73    fn write(&mut self, _buf: &[u8]) -> Result<usize> {
74        Err(Error::Other("I/O context has been taken".into()))
75    }
76    fn seek(&mut self, _pos: SeekFrom) -> Result<u64> {
77        Err(Error::Other("I/O context has been taken".into()))
78    }
79    fn tell(&mut self) -> Result<u64> {
80        Err(Error::Other("I/O context has been taken".into()))
81    }
82    fn size(&mut self) -> Result<u64> {
83        Err(Error::Other("I/O context has been taken".into()))
84    }
85    fn is_seekable(&self) -> bool {
86        false
87    }
88}
89
90/// Buffered I/O wrapper providing typed read/write operations.
91/// Wraps an `IoContext` and adds convenience methods for reading
92/// integers in specific byte orders.
93pub struct BufferedIo {
94    inner: Box<dyn IoContext>,
95    read_buf: Vec<u8>,
96    read_pos: usize,
97    read_len: usize,
98    write_buf: Vec<u8>,
99    write_pos: usize,
100}
101
102impl BufferedIo {
103    const DEFAULT_BUF_SIZE: usize = 32768;
104
105    pub fn new(inner: Box<dyn IoContext>) -> Self {
106        Self {
107            inner,
108            read_buf: vec![0u8; Self::DEFAULT_BUF_SIZE],
109            read_pos: 0,
110            read_len: 0,
111            write_buf: vec![0u8; Self::DEFAULT_BUF_SIZE],
112            write_pos: 0,
113        }
114    }
115
116    /// Transfer ownership of the inner IoContext out of this BufferedIo.
117    /// After this call, the BufferedIo is backed by a dead stub — all
118    /// subsequent I/O operations will return errors.
119    pub fn take_inner(&mut self) -> Box<dyn IoContext> {
120        self.read_pos = 0;
121        self.read_len = 0;
122        self.write_pos = 0;
123        std::mem::replace(&mut self.inner, Box::new(DeadIo))
124    }
125
126    /// Read exactly `buf.len()` bytes into `buf`.
127    pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
128        let mut filled = 0;
129        while filled < buf.len() {
130            if self.read_pos >= self.read_len {
131                self.fill_buffer()?;
132                if self.read_len == 0 {
133                    return Err(Error::Eof);
134                }
135            }
136            let available = self.read_len - self.read_pos;
137            let to_copy = available.min(buf.len() - filled);
138            buf[filled..filled + to_copy]
139                .copy_from_slice(&self.read_buf[self.read_pos..self.read_pos + to_copy]);
140            self.read_pos += to_copy;
141            filled += to_copy;
142        }
143        Ok(())
144    }
145
146    /// Read a single byte.
147    pub fn read_u8(&mut self) -> Result<u8> {
148        let mut buf = [0u8; 1];
149        self.read_exact(&mut buf)?;
150        Ok(buf[0])
151    }
152
153    /// Read a little-endian u16.
154    pub fn read_u16le(&mut self) -> Result<u16> {
155        let mut buf = [0u8; 2];
156        self.read_exact(&mut buf)?;
157        Ok(u16::from_le_bytes(buf))
158    }
159
160    /// Read a big-endian u16.
161    pub fn read_u16be(&mut self) -> Result<u16> {
162        let mut buf = [0u8; 2];
163        self.read_exact(&mut buf)?;
164        Ok(u16::from_be_bytes(buf))
165    }
166
167    /// Read a little-endian u32.
168    pub fn read_u32le(&mut self) -> Result<u32> {
169        let mut buf = [0u8; 4];
170        self.read_exact(&mut buf)?;
171        Ok(u32::from_le_bytes(buf))
172    }
173
174    /// Read a big-endian u32.
175    pub fn read_u32be(&mut self) -> Result<u32> {
176        let mut buf = [0u8; 4];
177        self.read_exact(&mut buf)?;
178        Ok(u32::from_be_bytes(buf))
179    }
180
181    /// Read a little-endian i32.
182    pub fn read_i32le(&mut self) -> Result<i32> {
183        let mut buf = [0u8; 4];
184        self.read_exact(&mut buf)?;
185        Ok(i32::from_le_bytes(buf))
186    }
187
188    /// Read a little-endian u64.
189    pub fn read_u64le(&mut self) -> Result<u64> {
190        let mut buf = [0u8; 8];
191        self.read_exact(&mut buf)?;
192        Ok(u64::from_le_bytes(buf))
193    }
194
195    /// Read a big-endian u64.
196    pub fn read_u64be(&mut self) -> Result<u64> {
197        let mut buf = [0u8; 8];
198        self.read_exact(&mut buf)?;
199        Ok(u64::from_be_bytes(buf))
200    }
201
202    /// Read raw bytes of specified length.
203    pub fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
204        let mut buf = vec![0u8; len];
205        self.read_exact(&mut buf)?;
206        Ok(buf)
207    }
208
209    /// Read up to `len` bytes, returning whatever is available.
210    /// Returns empty Vec only at true EOF. Matches FFmpeg's `av_get_packet()`
211    /// behavior of returning partial data when less than `len` bytes remain.
212    pub fn read_up_to(&mut self, len: usize) -> Result<Vec<u8>> {
213        let mut buf = Vec::with_capacity(len);
214        while buf.len() < len {
215            if self.read_pos >= self.read_len {
216                self.fill_buffer()?;
217                if self.read_len == 0 {
218                    break; // EOF
219                }
220            }
221            let available = self.read_len - self.read_pos;
222            let to_copy = available.min(len - buf.len());
223            buf.extend_from_slice(&self.read_buf[self.read_pos..self.read_pos + to_copy]);
224            self.read_pos += to_copy;
225        }
226        Ok(buf)
227    }
228
229    /// Skip `n` bytes forward.
230    pub fn skip(&mut self, n: u64) -> Result<()> {
231        if self.inner.is_seekable() {
232            // Use seek if possible
233            let buffered_remaining = (self.read_len - self.read_pos) as u64;
234            if n <= buffered_remaining {
235                self.read_pos += n as usize;
236                return Ok(());
237            }
238            let skip_from_io = n - buffered_remaining;
239            self.read_pos = 0;
240            self.read_len = 0;
241            self.inner.seek(SeekFrom::Current(skip_from_io as i64))?;
242        } else {
243            // Read and discard
244            let mut remaining = n;
245            let buf_len = self.read_buf.len();
246            let mut discard = vec![0u8; buf_len];
247            while remaining > 0 {
248                let to_skip = remaining.min(buf_len as u64) as usize;
249                self.read_exact(&mut discard[..to_skip])?;
250                remaining -= to_skip as u64;
251            }
252        }
253        Ok(())
254    }
255
256    /// Seek to an absolute position.
257    pub fn seek(&mut self, pos: u64) -> Result<u64> {
258        self.flush_write()?;
259        self.read_pos = 0;
260        self.read_len = 0;
261        self.inner.seek(SeekFrom::Start(pos))
262    }
263
264    /// Get current position.
265    pub fn tell(&mut self) -> Result<u64> {
266        let io_pos = self.inner.tell()?;
267        let read_offset = (self.read_len - self.read_pos) as u64;
268        let write_offset = self.write_pos as u64;
269        Ok(io_pos.saturating_sub(read_offset) + write_offset)
270    }
271
272    /// Get the total size of the underlying source.
273    pub fn size(&mut self) -> Result<u64> {
274        self.inner.size()
275    }
276
277    /// Whether the underlying I/O is seekable.
278    pub fn is_seekable(&self) -> bool {
279        self.inner.is_seekable()
280    }
281
282    // --- Write methods ---
283
284    /// Write a single byte.
285    pub fn write_u8(&mut self, v: u8) -> Result<()> {
286        self.write_all(&[v])
287    }
288
289    /// Write a little-endian u16.
290    pub fn write_u16le(&mut self, v: u16) -> Result<()> {
291        self.write_all(&v.to_le_bytes())
292    }
293
294    /// Write a big-endian u16.
295    pub fn write_u16be(&mut self, v: u16) -> Result<()> {
296        self.write_all(&v.to_be_bytes())
297    }
298
299    /// Write a little-endian u32.
300    pub fn write_u32le(&mut self, v: u32) -> Result<()> {
301        self.write_all(&v.to_le_bytes())
302    }
303
304    /// Write a big-endian u32.
305    pub fn write_u32be(&mut self, v: u32) -> Result<()> {
306        self.write_all(&v.to_be_bytes())
307    }
308
309    /// Write a little-endian u64.
310    pub fn write_u64le(&mut self, v: u64) -> Result<()> {
311        self.write_all(&v.to_le_bytes())
312    }
313
314    /// Write raw bytes.
315    pub fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
316        self.write_all(data)
317    }
318
319    /// Write all bytes from the buffer.
320    pub fn write_all(&mut self, data: &[u8]) -> Result<()> {
321        let mut offset = 0;
322        while offset < data.len() {
323            let space = self.write_buf.len() - self.write_pos;
324            let to_copy = space.min(data.len() - offset);
325            self.write_buf[self.write_pos..self.write_pos + to_copy]
326                .copy_from_slice(&data[offset..offset + to_copy]);
327            self.write_pos += to_copy;
328            offset += to_copy;
329            if self.write_pos >= self.write_buf.len() {
330                self.flush_write()?;
331            }
332        }
333        Ok(())
334    }
335
336    /// Flush buffered write data to the underlying IoContext.
337    pub fn flush(&mut self) -> Result<()> {
338        self.flush_write()
339    }
340
341    fn flush_write(&mut self) -> Result<()> {
342        if self.write_pos > 0 {
343            let mut written = 0;
344            while written < self.write_pos {
345                let n = self.inner.write(&self.write_buf[written..self.write_pos])?;
346                if n == 0 {
347                    return Err(Error::Io(std::io::ErrorKind::WriteZero));
348                }
349                written += n;
350            }
351            self.write_pos = 0;
352        }
353        Ok(())
354    }
355
356    fn fill_buffer(&mut self) -> Result<()> {
357        let n = self.inner.read(&mut self.read_buf)?;
358        self.read_pos = 0;
359        self.read_len = n;
360        Ok(())
361    }
362}
363
364/// Read a 4-byte ASCII tag (e.g., "RIFF", "WAVE").
365pub fn read_tag(io: &mut BufferedIo) -> Result<[u8; 4]> {
366    let mut tag = [0u8; 4];
367    io.read_exact(&mut tag)?;
368    Ok(tag)
369}