Skip to main content

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