Skip to main content

pure_magic/
readers.rs

1//! Data readers for magic number detection.
2//!
3//! Provides efficient readers for different data sources with caching and buffering
4//! strategies optimized for file format identification.
5//!
6//! # Types
7//!
8//! - [`DataReader`] - A generic reader enum supporting slices, vectors, and files.
9//! - [`BufReader`] - A buffered reader for in-memory byte slices.
10//! - [`LazyCache`] - A lazy-loading cache reader for files with multi-tiered caching.
11//!
12//! # Traits
13//!
14//! - [`DataRead`] - Extended read operations for magic number detection.
15
16use std::{
17    fs::File,
18    io::{self, SeekFrom},
19    ops::Range,
20};
21
22mod cache;
23pub use cache::LazyCache;
24
25mod slice;
26pub use slice::BufReader;
27
28/// A trait for reading data with position tracking and range-based access.
29///
30/// Implementors provide efficient random access to byte data for file magic
31/// detection, supporting both in-memory and file-backed storage.
32pub trait DataRead {
33    /// Returns the current position in the data stream.
34    fn stream_position(&self) -> u64;
35
36    /// Computes the absolute byte offset from a [`SeekFrom`] position.
37    #[inline]
38    fn offset_from_start(&self, pos: SeekFrom) -> u64 {
39        match pos {
40            SeekFrom::Start(s) => s,
41            SeekFrom::Current(p) => {
42                (self.stream_position() as i128 + p as i128).clamp(0, u64::MAX as i128) as u64
43            }
44            SeekFrom::End(e) => {
45                (self.data_size() as i128 + e as i128).clamp(0, u64::MAX as i128) as u64
46            }
47        }
48    }
49
50    /// Reads a range of bytes from the data.
51    ///
52    /// Returns an empty slice if the range is beyond the end of data.
53    fn read_range(&mut self, range: Range<u64>) -> Result<&[u8], io::Error>;
54
55    /// Reads up to `count` bytes from the current position.
56    ///
57    /// Returns fewer bytes if the end of data is reached.
58    #[inline]
59    fn read_count(&mut self, count: u64) -> Result<&[u8], io::Error> {
60        let pos = self.stream_position();
61        let range = pos..(pos.saturating_add(count));
62        self.read_range(range)
63    }
64
65    /// Reads exactly the specified byte range.
66    ///
67    /// # Errors
68    ///
69    /// Returns an error if the range extends beyond the available data.
70    fn read_exact_range(&mut self, range: Range<u64>) -> Result<&[u8], io::Error> {
71        let range_len = range.end - range.start;
72        let b = self.read_range(range)?;
73        if b.len() as u64 != range_len {
74            Err(io::Error::from(io::ErrorKind::UnexpectedEof))
75        } else {
76            Ok(b)
77        }
78    }
79
80    /// Reads exactly `count` bytes from the current position.
81    ///
82    /// # Errors
83    ///
84    /// Returns an error if fewer than `count` bytes are available.
85    fn read_exact_count(&mut self, count: u64) -> Result<&[u8], io::Error> {
86        let b = self.read_count(count)?;
87        debug_assert!(b.len() <= count as usize);
88        if b.len() as u64 != count {
89            Err(io::ErrorKind::UnexpectedEof.into())
90        } else {
91            Ok(b)
92        }
93    }
94
95    /// Reads exactly enough bytes to fill `buf`.
96    ///
97    /// # Errors
98    ///
99    /// Returns an error if fewer than `buf.len()` bytes are available.
100    fn read_exact_into(&mut self, buf: &mut [u8]) -> Result<(), io::Error> {
101        let read = self.read_exact_count(buf.len() as u64)?;
102        // this function call should not panic as read_exact
103        // guarantees we read exactly the length of buf
104        buf.copy_from_slice(read);
105        Ok(())
106    }
107
108    /// Reads bytes until any of the delimiters or `limit` bytes is reached.
109    ///
110    /// The delimiter byte is included in the returned slice.
111    fn read_until_any_delim_or_limit(
112        &mut self,
113        delims: &[u8],
114        limit: u64,
115    ) -> Result<&[u8], io::Error>;
116
117    /// Reads bytes until `byte` or `limit` bytes is reached.
118    ///
119    /// The delimiter byte is included in the returned slice.
120    fn read_until_or_limit(&mut self, byte: u8, limit: u64) -> Result<&[u8], io::Error>;
121
122    /// Reads bytes while `f` returns `true` or until `limit` bytes is reached.
123    ///
124    /// The byte that caused `f` to return `false` is not included.
125    fn read_while_or_limit<F>(&mut self, f: F, limit: u64) -> Result<&[u8], io::Error>
126    where
127        F: Fn(u8) -> bool;
128
129    /// Reads bytes until a UTF-16 character or `limit` bytes is reached.
130    ///
131    /// The UTF-16 character is included in the returned slice.
132    fn read_until_utf16_or_limit(
133        &mut self,
134        utf16_char: &[u8; 2],
135        limit: u64,
136    ) -> Result<&[u8], io::Error>;
137
138    /// Returns the total size of the data in bytes.
139    fn data_size(&self) -> u64;
140
141    /// Sets the position for future reads.
142    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64>;
143}
144
145/// A generic reader for data backed by different sources.
146///
147/// Provides a uniform interface for reading from in-memory buffers or files.
148pub enum DataReader<'b> {
149    /// A reader backed by a borrowed byte slice.
150    ///
151    /// Useful for zero-copy reads from existing in-memory data.
152    Slice(BufReader<&'b [u8]>),
153    /// A reader backed by an owned byte vector.
154    ///
155    /// Useful when the data needs to be owned.
156    Vec(BufReader<Vec<u8>>),
157    /// A reader backed by a file with lazy caching.
158    ///
159    /// Uses [`LazyCache`] for efficient disk I/O.
160    File(LazyCache<File>),
161}
162
163impl DataReader<'_> {
164    /// Creates a new `DataReader` backed by a file with lazy caching.
165    ///
166    /// The file is wrapped in a [`LazyCache`] with:
167    /// - A hot cache of 8 KiB (4 KiB head, 4 KiB tail), always loaded up
168    ///   front so most magic rules (which read near the start or end of
169    ///   a file) never need a further read
170    /// - A warm cache of 100 MiB, lazily populated per 4 KiB block for
171    ///   reads that fall outside the hot cache
172    ///
173    /// # Errors
174    ///
175    /// Returns an error if the file cannot be read or if cache initialization fails.
176    pub fn from_file(r: File) -> Result<Self, io::Error> {
177        let x = LazyCache::<File>::from_read_seek(r)
178            .and_then(|lc| lc.with_hot_cache(4096 * 2))
179            .map(|lc| lc.with_warm_cache(100 << 20))?;
180        Ok(Self::File(x))
181    }
182}
183
184impl<'b> DataReader<'b> {
185    /// Creates a new `DataReader` backed by a borrowed byte slice.
186    ///
187    /// This is a zero-copy constructor that wraps the slice in a [`BufReader`].
188    /// The lifetime of the returned reader is tied to the input slice.
189    ///
190    /// # Examples
191    ///
192    /// ```
193    /// use pure_magic::readers::{DataReader, DataRead};
194    ///
195    /// let data = b"hello world";
196    /// let reader = DataReader::from_slice(data);
197    /// assert_eq!(reader.data_size(), data.len() as u64);
198    /// ```
199    pub fn from_slice(s: &'b [u8]) -> Self {
200        Self::Slice(BufReader::from_slice(s))
201    }
202}
203
204impl DataReader<'_> {
205    /// Creates a new `DataReader` backed by an owned byte vector.
206    ///
207    /// The vector is wrapped in a [`BufReader`], allowing the data to be owned
208    /// independently of any borrow.
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// use pure_magic::readers::{DataReader, DataRead};
214    ///
215    /// let data = vec![1u8, 2, 3, 4, 5];
216    /// let reader = DataReader::from_vec(data);
217    /// assert_eq!(reader.data_size(), 5);
218    /// ```
219    pub fn from_vec(v: Vec<u8>) -> Self {
220        Self::Vec(BufReader::from_slice(v))
221    }
222}
223
224impl DataRead for DataReader<'_> {
225    fn stream_position(&self) -> u64 {
226        match self {
227            DataReader::Slice(b) => b.stream_position(),
228            DataReader::Vec(v) => v.stream_position(),
229            DataReader::File(f) => f.stream_position(),
230        }
231    }
232
233    fn offset_from_start(&self, pos: SeekFrom) -> u64 {
234        match self {
235            DataReader::Slice(b) => b.offset_from_start(pos),
236            DataReader::Vec(v) => v.offset_from_start(pos),
237            DataReader::File(f) => f.offset_from_start(pos),
238        }
239    }
240
241    fn read_range(&mut self, range: Range<u64>) -> Result<&[u8], io::Error> {
242        match self {
243            DataReader::Slice(b) => b.read_range(range),
244            DataReader::Vec(v) => v.read_range(range),
245            DataReader::File(f) => f.read_range(range),
246        }
247    }
248
249    fn read_count(&mut self, count: u64) -> Result<&[u8], io::Error> {
250        match self {
251            DataReader::Slice(b) => b.read_count(count),
252            DataReader::Vec(v) => v.read_count(count),
253            DataReader::File(f) => f.read_count(count),
254        }
255    }
256
257    fn read_exact_range(&mut self, range: Range<u64>) -> Result<&[u8], io::Error> {
258        match self {
259            DataReader::Slice(b) => b.read_exact_range(range),
260            DataReader::Vec(v) => v.read_exact_range(range),
261            DataReader::File(f) => f.read_exact_range(range),
262        }
263    }
264
265    fn read_exact_count(&mut self, count: u64) -> Result<&[u8], io::Error> {
266        match self {
267            DataReader::Slice(b) => b.read_exact_count(count),
268            DataReader::Vec(v) => v.read_exact_count(count),
269            DataReader::File(f) => f.read_exact_count(count),
270        }
271    }
272
273    fn read_exact_into(&mut self, buf: &mut [u8]) -> Result<(), io::Error> {
274        match self {
275            DataReader::Slice(b) => b.read_exact_into(buf),
276            DataReader::Vec(v) => v.read_exact_into(buf),
277            DataReader::File(f) => f.read_exact_into(buf),
278        }
279    }
280
281    fn read_until_any_delim_or_limit(
282        &mut self,
283        delims: &[u8],
284        limit: u64,
285    ) -> Result<&[u8], io::Error> {
286        match self {
287            DataReader::Slice(b) => b.read_until_any_delim_or_limit(delims, limit),
288            DataReader::Vec(v) => v.read_until_any_delim_or_limit(delims, limit),
289            DataReader::File(f) => f.read_until_any_delim_or_limit(delims, limit),
290        }
291    }
292
293    fn read_until_or_limit(&mut self, byte: u8, limit: u64) -> Result<&[u8], io::Error> {
294        match self {
295            DataReader::Slice(b) => b.read_until_or_limit(byte, limit),
296            DataReader::Vec(v) => v.read_until_or_limit(byte, limit),
297            DataReader::File(f) => f.read_until_or_limit(byte, limit),
298        }
299    }
300
301    fn read_while_or_limit<F>(&mut self, f: F, limit: u64) -> Result<&[u8], io::Error>
302    where
303        F: Fn(u8) -> bool,
304    {
305        match self {
306            DataReader::Slice(b) => b.read_while_or_limit(f, limit),
307            DataReader::Vec(v) => v.read_while_or_limit(f, limit),
308            DataReader::File(l) => l.read_while_or_limit(f, limit),
309        }
310    }
311
312    fn read_until_utf16_or_limit(
313        &mut self,
314        utf16_char: &[u8; 2],
315        limit: u64,
316    ) -> Result<&[u8], io::Error> {
317        match self {
318            DataReader::Slice(b) => b.read_until_utf16_or_limit(utf16_char, limit),
319            DataReader::Vec(v) => v.read_until_utf16_or_limit(utf16_char, limit),
320            DataReader::File(f) => f.read_until_utf16_or_limit(utf16_char, limit),
321        }
322    }
323
324    fn data_size(&self) -> u64 {
325        match self {
326            DataReader::Slice(b) => b.data_size(),
327            DataReader::Vec(v) => v.data_size(),
328            DataReader::File(f) => f.data_size(),
329        }
330    }
331
332    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
333        match self {
334            DataReader::Slice(b) => b.seek(pos),
335            DataReader::Vec(v) => v.seek(pos),
336            DataReader::File(f) => f.seek(pos),
337        }
338    }
339}