tsfile/utils/
io.rs

1use core::{cmp, fmt};
2use std::cell::RefCell;
3use std::io::{Cursor, Read, Result, Seek, SeekFrom};
4
5use crate::file::reader::{Length, TryClone};
6
7const DEFAULT_BUF_SIZE: usize = 8 * 1024;
8
9pub trait TsFileReader: Read + Seek + Length + TryClone {}
10
11impl<T: Read + Seek + Length + TryClone> TsFileReader for T {}
12
13pub struct FileSource<R: TsFileReader> {
14    reader: RefCell<R>,
15    start: u64,
16    end: u64,
17    buf: Vec<u8>,
18    buf_pos: usize,
19    buf_cap: usize,
20}
21
22pub trait Position {
23    /// Returns position in the stream.
24    fn pos(&self) -> u64;
25}
26
27impl<R: TsFileReader> fmt::Debug for FileSource<R> {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        f.debug_struct("FileSource")
30            .field("reader", &"OPAQUE")
31            .field("start", &self.start)
32            .field("end", &self.end)
33            .field("buf.len", &self.buf.len())
34            .field("buf_pos", &self.buf_pos)
35            .field("buf_cap", &self.buf_cap)
36            .finish()
37    }
38}
39
40impl<R: TsFileReader> FileSource<R> {
41    pub fn new(fd: &R, start: u64, length: usize) -> Self {
42        let reader = RefCell::new(fd.try_clone().unwrap());
43        Self {
44            reader,
45            start,
46            end: start + length as u64,
47            buf: vec![0_u8; DEFAULT_BUF_SIZE],
48            buf_pos: 0,
49            buf_cap: 0,
50        }
51    }
52
53    fn fill_inner_buf(&mut self) -> Result<&[u8]> {
54        if self.buf_pos >= self.buf_cap {
55            // If we've reached the end of our internal buffer then we need to fetch
56            // some more data from the underlying reader.
57            // Branch using `>=` instead of the more correct `==`
58            // to tell the compiler that the pos..cap slice is always valid.
59            debug_assert!(self.buf_pos == self.buf_cap);
60            let mut reader = self.reader.borrow_mut();
61            reader.seek(SeekFrom::Start(self.start))?; // always seek to start before reading
62            self.buf_cap = reader.read(&mut self.buf)?;
63            self.buf_pos = 0;
64        }
65        Ok(&self.buf[self.buf_pos..self.buf_cap])
66    }
67
68    fn skip_inner_buf(&mut self, buf: &mut [u8]) -> Result<usize> {
69        // discard buffer
70        self.buf_pos = 0;
71        self.buf_cap = 0;
72        // read directly into param buffer
73        let mut reader = self.reader.borrow_mut();
74        reader.seek(SeekFrom::Start(self.start))?; // always seek to start before reading
75        let nread = reader.read(buf)?;
76        self.start += nread as u64;
77        Ok(nread)
78    }
79}
80
81impl<R: TsFileReader> Read for FileSource<R> {
82    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
83        let bytes_to_read = cmp::min(buf.len(), (self.end - self.start) as usize);
84        let buf = &mut buf[0..bytes_to_read];
85
86        // If we don't have any buffered data and we're doing a massive read
87        // (larger than our internal buffer), bypass our internal buffer
88        // entirely.
89        if self.buf_pos == self.buf_cap && buf.len() >= self.buf.len() {
90            return self.skip_inner_buf(buf);
91        }
92        let nread = {
93            let mut rem = self.fill_inner_buf()?;
94            // copy the data from the inner buffer to the param buffer
95            rem.read(buf)?
96        };
97        // consume from buffer
98        self.buf_pos = cmp::min(self.buf_pos + nread, self.buf_cap);
99
100        self.start += nread as u64;
101        Ok(nread)
102    }
103}
104
105impl<R: TsFileReader> Position for FileSource<R> {
106    fn pos(&self) -> u64 {
107        self.start
108    }
109}
110
111impl<R: TsFileReader> Length for FileSource<R> {
112    fn len(&self) -> u64 {
113        self.end - self.start
114    }
115}