zune_core/bytestream/
reader.rs

1use alloc::string::String;
2use alloc::vec;
3use alloc::vec::Vec;
4use core::fmt::Formatter;
5
6pub(crate) mod no_std_readers;
7pub(crate) mod std_readers;
8use crate::bytestream::ZByteReaderTrait;
9
10/// Enumeration of possible methods to seek within an I/O object.
11///
12/// It is analogous to the [SeekFrom](std::io::SeekFrom) in the std library but
13/// it's here to allow this to work in no-std crates
14#[derive(Copy, PartialEq, Eq, Clone, Debug)]
15pub enum ZSeekFrom {
16    /// Sets the offset to the provided number of bytes.
17    Start(u64),
18
19    /// Sets the offset to the size of this object plus the specified number of
20    /// bytes.
21    ///
22    /// It is possible to seek beyond the end of an object, but it's an error to
23    /// seek before byte 0.
24    End(i64),
25
26    /// Sets the offset to the current position plus the specified number of
27    /// bytes.
28    ///
29    /// It is possible to seek beyond the end of an object, but it's an error to
30    /// seek before byte 0.
31    Current(i64)
32}
33
34impl ZSeekFrom {
35    /// Convert to [SeekFrom](std::io::SeekFrom) from the `std::io` library
36    ///
37    /// This is only present when std feature is present
38    #[cfg(feature = "std")]
39    pub(crate) fn to_std_seek(self) -> std::io::SeekFrom {
40        match self {
41            ZSeekFrom::Start(pos) => std::io::SeekFrom::Start(pos),
42            ZSeekFrom::End(pos) => std::io::SeekFrom::End(pos),
43            ZSeekFrom::Current(pos) => std::io::SeekFrom::Current(pos)
44        }
45    }
46}
47
48pub enum ZByteIoError {
49    /// A standard library error
50    /// Only available with the `std` feature
51    #[cfg(feature = "std")]
52    StdIoError(std::io::Error),
53    /// An error converting from one type to another
54    TryFromIntError(core::num::TryFromIntError),
55    /// Not enough bytes to satisfy a read
56    // requested, read
57    NotEnoughBytes(usize, usize),
58    /// The output buffer is too small to write the bytes
59    NotEnoughBuffer(usize, usize),
60    /// An error that may occur randomly
61    Generic(&'static str),
62    /// An error that occurred during a seek operation
63    SeekError(&'static str),
64    /// An error that occurred during a seek operation
65    SeekErrorOwned(String)
66}
67
68impl core::fmt::Debug for ZByteIoError {
69    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
70        match self {
71            #[cfg(feature = "std")]
72            ZByteIoError::StdIoError(err) => {
73                writeln!(f, "Underlying I/O error {}", err)
74            }
75            ZByteIoError::TryFromIntError(err) => {
76                writeln!(f, "Cannot convert to int {}", err)
77            }
78            ZByteIoError::NotEnoughBytes(found, expected) => {
79                writeln!(f, "Not enough bytes, expected {expected} but found {found}")
80            }
81            ZByteIoError::NotEnoughBuffer(expected, found) => {
82                writeln!(
83                    f,
84                    "Not enough buffer to write {expected} bytes, buffer size is {found}"
85                )
86            }
87            ZByteIoError::Generic(err) => {
88                writeln!(f, "Generic I/O error: {err}")
89            }
90            ZByteIoError::SeekError(err) => {
91                writeln!(f, "Seek error: {err}")
92            }
93            ZByteIoError::SeekErrorOwned(err) => {
94                writeln!(f, "Seek error {err}")
95            }
96        }
97    }
98}
99
100#[cfg(feature = "std")]
101impl From<std::io::Error> for ZByteIoError {
102    fn from(value: std::io::Error) -> Self {
103        ZByteIoError::StdIoError(value)
104    }
105}
106
107impl From<core::num::TryFromIntError> for ZByteIoError {
108    fn from(value: core::num::TryFromIntError) -> Self {
109        ZByteIoError::TryFromIntError(value)
110    }
111}
112
113impl From<&'static str> for ZByteIoError {
114    fn from(value: &'static str) -> Self {
115        ZByteIoError::Generic(value)
116    }
117}
118
119/// The image reader wrapper
120///
121/// This wraps anything that implements [ZByteReaderTrait] and
122/// extends the ability of the core trait methods by providing
123/// utilities like endian aware byte functions.
124///
125/// This prevents each implementation from providing its own
126pub struct ZReader<T: ZByteReaderTrait> {
127    inner:       T,
128    temp_buffer: Vec<u8>
129}
130
131impl<T: ZByteReaderTrait> ZReader<T> {
132    /// Create a new reader from a source
133    /// that implements the [ZByteReaderTrait]
134    pub fn new(source: T) -> ZReader<T> {
135        ZReader {
136            inner:       source,
137            temp_buffer: vec![]
138        }
139    }
140    /// Destroy this reader returning
141    /// the underlying source of the bytes
142    /// from which we were decoding
143    #[inline(always)]
144    pub fn consume(self) -> T {
145        self.inner
146    }
147    /// Skip ahead ignoring `num` bytes
148    ///
149    /// For more advanced seek methods see [Self::seek] that allows
150    /// moving around via more advanced ways
151    ///
152    /// # Arguments
153    ///  - num: The number of bytes to skip.
154    ///
155    /// # Returns
156    ///  - `Ok(u64)`: The new position from the start of the stream.
157    ///  - `Error` If something went wrong
158    #[inline(always)]
159    pub fn skip(&mut self, num: usize) -> Result<u64, ZByteIoError> {
160        self.inner.z_seek(ZSeekFrom::Current(num as i64))
161    }
162    /// Move back from current position to a previous
163    /// position
164    ///
165    /// For more advanced seek methods see [Self::seek] that allows
166    /// moving around via more advanced ways
167    ///
168    /// # Arguments
169    /// - `num`: Positions to move before the current cursor
170    ///
171    /// # Returns
172    ///  - `Ok(u64)`: The new position from the start of the stream.
173    ///  - `Error` If something went wrong
174    #[inline(always)]
175    pub fn rewind(&mut self, num: usize) -> Result<u64, ZByteIoError> {
176        self.inner.z_seek(ZSeekFrom::Current(-(num as i64)))
177    }
178    /// Move around a stream of bytes
179    ///
180    /// This is analogous to the [std::io::Seek] trait with the same ergonomics
181    /// only implemented to allow use in a `no_std` environment
182    ///
183    /// # Arguments
184    /// - `from`: The seek operation type.
185    ///
186    /// # Returns
187    ///  - `Ok(u64)`: The new position from the start of the stream.
188    ///  -  Error if something went wrong.
189    #[inline(always)]
190    pub fn seek(&mut self, from: ZSeekFrom) -> Result<u64, ZByteIoError> {
191        self.inner.z_seek(from)
192    }
193
194    /// Read a single byte from the underlying stream
195    ///
196    /// If an error occurs, it will return `0` as default output
197    /// hence it may be difficult to distinguish a `0` from the underlying source
198    /// and a `0` from an error.
199    /// For that there is [Self::read_u8_err]
200    ///
201    /// # Returns.
202    /// - The next byte on the stream.
203    ///  
204    #[inline(always)]
205    pub fn read_u8(&mut self) -> u8 {
206        self.inner.read_byte_no_error()
207    }
208
209    /// Read a single byte returning an error if the read cannot be satisfied
210    ///
211    /// # Returns
212    /// - `Ok(u8)`: The next byte
213    /// - Error if the byte read could not be satisfied   
214    #[inline(always)]
215    pub fn read_u8_err(&mut self) -> Result<u8, ZByteIoError> {
216        let mut buf = [0];
217        self.inner.read_const_bytes(&mut buf)?;
218        Ok(buf[0])
219    }
220
221    /// Look ahead position bytes and return a reference
222    /// to num_bytes from that position, or an error if the
223    /// peek would be out of bounds.
224    ///
225    /// This doesn't increment the position, bytes would have to be discarded
226    /// at a later point.
227    #[inline]
228    pub fn peek_at(&mut self, position: usize, num_bytes: usize) -> Result<&[u8], ZByteIoError> {
229        // short circuit for zero
230        // important since implementations like File will
231        // cause a syscall on skip
232        if position != 0 {
233            // skip position bytes from start
234            self.skip(position)?;
235        }
236        if num_bytes > 20 * 1024 * 1024 {
237            // resize of 20 MBs, skipping too much, so panic
238            return Err(ZByteIoError::Generic("Too many bytes skipped"));
239        }
240        // resize buffer
241        self.temp_buffer.resize(num_bytes, 0);
242        // read bytes
243        match self.inner.peek_exact_bytes(&mut self.temp_buffer[..]) {
244            Ok(_) => {
245                // rewind back to where we were
246                if position != 0 {
247                    self.rewind(position)?;
248                }
249                Ok(&self.temp_buffer)
250            }
251            Err(e) => Err(e)
252        }
253    }
254    /// Read a fixed number of known bytes to a buffer and return the bytes or an error
255    /// if it occurred.
256    ///
257    /// The size of the `N` value must be small enough to fit the stack space otherwise
258    /// this will cause a stack overflow :)
259    ///
260    /// If you can ignore errors, you can use [Self::read_fixed_bytes_or_zero]
261    ///
262    /// # Returns
263    ///  - `Ok([u8;N])`: The bytes read from the source
264    ///  - An error if it occurred.
265    #[inline(always)]
266    pub fn read_fixed_bytes_or_error<const N: usize>(&mut self) -> Result<[u8; N], ZByteIoError> {
267        let mut byte_store: [u8; N] = [0; N];
268        match self.inner.read_const_bytes(&mut byte_store) {
269            Ok(_) => Ok(byte_store),
270            Err(e) => Err(e)
271        }
272    }
273    /// Read a fixed bytes to an array and if that is impossible, return an array containing
274    /// zeros
275    ///
276    /// If you want to handle errors, use [Self::read_fixed_bytes_or_error]
277    #[inline(always)]
278    pub fn read_fixed_bytes_or_zero<const N: usize>(&mut self) -> [u8; N] {
279        let mut byte_store: [u8; N] = [0; N];
280        self.inner.read_const_bytes_no_error(&mut byte_store);
281        byte_store
282    }
283
284    /// Move the cursor to a fixed position in the stream
285    ///
286    /// This will move the cursor to exacltly `position` bytes from the start of the buffer
287    ///
288    /// # Arguments
289    /// - `position`: The current position to move the cursor.
290    #[inline]
291    pub fn set_position(&mut self, position: usize) -> Result<(), ZByteIoError> {
292        self.seek(ZSeekFrom::Start(position as u64))?;
293
294        Ok(())
295    }
296
297    /// Return true if the underlying buffer can no longer produce bytes
298    ///
299    /// This call may be expensive depending on the underlying buffer type, e.g if
300    /// it's a file, we have to ask the os whether we have more contents, or in other words make a syscall.
301    ///
302    /// Use that wisely
303    ///
304    /// # Returns
305    ///  - `Ok(bool)`: True if we are in `EOF`, false if we can produce more bytes
306    ///  - Error if something went wrong
307    #[inline(always)]
308    pub fn eof(&mut self) -> Result<bool, ZByteIoError> {
309        self.inner.is_eof()
310    }
311
312    /// Return the current position of the inner reader or an error
313    /// if that occurred when reading.
314    ///
315    /// Like [eof](Self::eof), the perf characteristics may vary depending on underlying reader
316    ///
317    /// # Returns
318    /// - `Ok(u64)`: The current position of the inner reader
319    #[inline(always)]
320    pub fn position(&mut self) -> Result<u64, ZByteIoError> {
321        self.inner.z_position()
322    }
323
324    /// Read a fixed number of bytes from the underlying reader returning
325    /// an error if that can't be satisfied
326    ///
327    /// Similar to [std::io::Read::read_exact]
328    ///
329    /// # Returns
330    ///  - `Ok(())`: If the read was successful
331    ///  - An error if the read was unsuccessful including failure to fill the whole bytes
332    pub fn read_exact_bytes(&mut self, buf: &mut [u8]) -> Result<(), ZByteIoError> {
333        self.inner.read_exact_bytes(buf)
334    }
335
336    /// Read some bytes from the inner reader, and return number of bytes read
337    ///
338    /// The implementation may not read bytes enough to fill the buffer
339    ///
340    /// Similar to [std::io::Read::read]
341    ///
342    /// # Returns
343    /// - `Ok(usize)`: Number of bytes actually read to the buffer
344    /// - An error if something went wrong
345    pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<usize, ZByteIoError> {
346        self.inner.read_bytes(buf)
347    }
348}
349
350enum Mode {
351    // Big endian
352    BE,
353    // Little Endian
354    LE
355}
356macro_rules! get_single_type {
357    ($name:tt,$name2:tt,$name3:tt,$name4:tt,$name5:tt,$name6:tt,$int_type:tt) => {
358        impl<T:ZByteReaderTrait> ZReader<T>
359        {
360            #[inline(always)]
361            fn $name(&mut self, mode: Mode) -> $int_type
362            {
363                const SIZE_OF_VAL: usize = core::mem::size_of::<$int_type>();
364
365                let mut space = [0; SIZE_OF_VAL];
366
367                self.inner.read_const_bytes_no_error(&mut space);
368
369                match mode {
370                    Mode::BE => $int_type::from_be_bytes(space),
371                    Mode::LE => $int_type::from_le_bytes(space)
372                }
373            }
374
375            #[inline(always)]
376            fn $name2(&mut self, mode: Mode) -> Result<$int_type, ZByteIoError>
377            {
378                const SIZE_OF_VAL: usize = core::mem::size_of::<$int_type>();
379
380                let mut space = [0; SIZE_OF_VAL];
381
382                match self.inner.read_const_bytes(&mut space)
383                {
384                    Ok(_) => match mode {
385                        Mode::BE => Ok($int_type::from_be_bytes(space)),
386                        Mode::LE => Ok($int_type::from_le_bytes(space))
387                    },
388                     Err(e) =>  Err(e)
389                }
390            }
391            #[doc=concat!("Read ",stringify!($int_type)," as a big endian integer")]
392            #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," read.")]
393            #[inline]
394            pub fn $name3(&mut self) -> Result<$int_type, ZByteIoError>
395            {
396                self.$name2(Mode::BE)
397            }
398
399            #[doc=concat!("Read ",stringify!($int_type)," as a little endian integer")]
400            #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," read.")]
401            #[inline]
402            pub fn $name4(&mut self) -> Result<$int_type, ZByteIoError>
403            {
404                self.$name2(Mode::LE)
405            }
406            #[doc=concat!("Read ",stringify!($int_type)," as a big endian integer")]
407            #[doc=concat!("Returning 0 if the underlying  buffer does not have enough bytes for a ",stringify!($int_type)," read.")]
408            #[inline(always)]
409            pub fn $name5(&mut self) -> $int_type
410            {
411                self.$name(Mode::BE)
412            }
413            #[doc=concat!("Read ",stringify!($int_type)," as a little endian integer")]
414            #[doc=concat!("Returning 0 if the underlying buffer does not have enough bytes for a ",stringify!($int_type)," read.")]
415            #[inline(always)]
416            pub fn $name6(&mut self) -> $int_type
417            {
418                self.$name(Mode::LE)
419            }
420        }
421    };
422}
423
424get_single_type!(
425    get_u16_inner_or_default,
426    get_u16_inner_or_die,
427    get_u16_be_err,
428    get_u16_le_err,
429    get_u16_be,
430    get_u16_le,
431    u16
432);
433get_single_type!(
434    get_u32_inner_or_default,
435    get_u32_inner_or_die,
436    get_u32_be_err,
437    get_u32_le_err,
438    get_u32_be,
439    get_u32_le,
440    u32
441);
442get_single_type!(
443    get_u64_inner_or_default,
444    get_u64_inner_or_die,
445    get_u64_be_err,
446    get_u64_le_err,
447    get_u64_be,
448    get_u64_le,
449    u64
450);
451
452#[cfg(feature = "std")]
453impl<T> std::io::Read for ZReader<T>
454where
455    T: ZByteReaderTrait
456{
457    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
458        use std::io::ErrorKind;
459        self.read_bytes(buf)
460            .map_err(|e| std::io::Error::new(ErrorKind::Other, format!("{:?}", e)))
461    }
462}