exact_reader/
reader.rs

1use std::{
2    io::{Read, Seek},
3    ops::RangeInclusive,
4};
5
6use crate::{
7    multifile::{File, MultiFile},
8    utils::calculate_seek,
9    vec_deq::VecDeque,
10};
11
12/// The `ExactReader` struct simplifies reading data from a file(s).
13pub struct ExactReader<R> {
14    /// The inner reader for the file.
15    file: R,
16
17    /// The active range of file offsets within the buffer.
18    file_offset_view: RangeInclusive<usize>,
19
20    /// The size of the file(s).
21    size: usize,
22
23    /// The buffer used for caching data read from the file(s).
24    buffer: VecDeque<u8>,
25    /// The offset within the buffer
26    buffer_offset: usize,
27
28    /// Seek position to be used on `reserve`
29    seeked: Option<usize>,
30}
31
32impl<R: Read + Seek> ExactReader<MultiFile<R>> {
33    /// Creates a new `ExactReader` instance for reading data from multiple files.
34    pub fn new_multi(file: MultiFile<R>) -> Self {
35        let size = file.size();
36
37        Self {
38            file,
39            size,
40            buffer: VecDeque::new(),
41            file_offset_view: 0..=0,
42            buffer_offset: 0,
43            seeked: None,
44        }
45    }
46}
47
48impl<R: Read + Seek> ExactReader<File<R>> {
49    /// Creates a new `ExactReader` instance for reading data from a single file.
50    pub fn new_single(file: File<R>) -> Self {
51        let size = file.size;
52
53        Self {
54            file,
55            size,
56            buffer: VecDeque::new(),
57            file_offset_view: 0..=0,
58            buffer_offset: 0,
59            seeked: None,
60        }
61    }
62}
63
64impl<R: Read + Seek> ExactReader<R> {
65    /// The total size of the file(s) in bytes.
66    pub fn size(&self) -> usize {
67        self.size
68    }
69
70    /// Calculates the physical index within the file(s) from the current buffer offset.
71    #[inline]
72    fn physical_idx(&self) -> usize {
73        self.file_offset_view.start() + self.buffer_offset
74    }
75
76    /// Reads the given range from the inner file(s).
77    fn _read(&mut self, buf: &mut Vec<u8>, read_size: usize, head: usize, tail: usize) {
78        let _ = self.file.by_ref().take(read_size as u64).read_to_end(buf);
79        self.file_offset_view = head..=tail;
80    }
81
82    /// Reserves and caches space in the buffer for future reads
83    pub fn reserve(&mut self, reserve_size: usize) {
84        let real_head = self.file_offset_view.start();
85
86        if let Some(seek_head) = self.seeked.take() {
87            let seek_tail = seek_head + reserve_size;
88
89            if self.file_offset_view.contains(&seek_head) {
90                self.buffer_offset = seek_head - real_head;
91            } else if self.file_offset_view.contains(&seek_tail) {
92                let read_size = self.file_offset_view.start() - seek_head;
93                let mut buf: Vec<u8> = Vec::with_capacity(read_size); // TODO: make it zero copy
94
95                self._read(&mut buf, read_size, seek_head, seek_tail);
96                self.buffer_offset = 0;
97
98                self.buffer.extend_front(buf.as_slice());
99                return;
100            }
101            let mut buf: Vec<u8> = Vec::with_capacity(reserve_size); // TODO: make it zero copy
102            self._read(&mut buf, reserve_size, seek_head, seek_tail);
103
104            self.buffer_offset = 0;
105
106            self.buffer.clear();
107            self.buffer.extend_back(buf.as_slice());
108
109            return;
110        }
111
112        if self.buffer.len() >= self.buffer_offset + reserve_size {
113            return;
114        }
115
116        let mut buf: Vec<u8> = Vec::with_capacity(reserve_size); // TODO: make it zero copy
117        let tail = self.file_offset_view.start() + self.buffer.len() + buf.len();
118        self._read(&mut buf, reserve_size, *self.file_offset_view.start(), tail);
119
120        self.buffer.extend_back(buf.as_mut_slice());
121    }
122}
123
124impl<R: Read + Seek> Read for ExactReader<R> {
125    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
126        // TODO: read when size > file.size
127        let size = buf.len();
128        self.reserve(size);
129
130        let (head, tail) = self.buffer.as_slices();
131        let head_len = head.len();
132        let adjusted_head_len = head_len.saturating_sub(self.buffer_offset);
133        let tail_offset = self.buffer_offset.saturating_sub(head_len);
134
135        if adjusted_head_len == 0 {
136            // The buffer_offset is in the tail slice
137            buf.copy_from_slice(&tail[tail_offset..tail_offset + size]);
138        } else if adjusted_head_len >= size {
139            // The data is entirely in the head slice
140            buf.copy_from_slice(&head[self.buffer_offset..self.buffer_offset + size]);
141        } else {
142            // Data spans both head and tail slices
143            buf[..adjusted_head_len].copy_from_slice(&head[self.buffer_offset..]);
144            buf[adjusted_head_len..]
145                .copy_from_slice(&tail[tail_offset..tail_offset + size - adjusted_head_len]);
146        }
147        self.buffer_offset += size;
148
149        Ok(size)
150    }
151}
152
153impl<R: Read + Seek> Seek for ExactReader<R> {
154    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
155        let calculated_seek = calculate_seek(self.size, self.physical_idx(), pos)? as usize;
156        if self.file_offset_view.contains(&calculated_seek) {
157            self.buffer_offset = calculated_seek - self.file_offset_view.start();
158            return Ok(calculated_seek as u64);
159        }
160
161        let result = self.file.seek(pos)?;
162        self.seeked = Some(result as usize);
163
164        Ok(result)
165    }
166
167    fn stream_position(&mut self) -> std::io::Result<u64> {
168        Ok(self.physical_idx() as u64)
169    }
170}