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
121/// The image reader wrapper
122///
123/// This wraps anything that implements [ZByteReaderTrait] and
124/// extends the ability of the core trait methods by providing
125/// utilities like endian aware byte functions.
126///
127/// This prevents each implementation from providing its own
128pub struct ZReader<T> {
129    inner:       T,
130    temp_buffer: Vec<u8>
131}
132
133impl<T: ZByteReaderTrait> ZReader<T> {
134    /// Create a new reader from a source
135    /// that implements the [ZByteReaderTrait]
136    pub fn new(source: T) -> ZReader<T> {
137        ZReader {
138            inner:       source,
139            temp_buffer: vec![]
140        }
141    }
142    /// Destroy this reader returning
143    /// the underlying source of the bytes
144    /// from which we were decoding
145    #[inline(always)]
146    pub fn consume(self) -> T {
147        self.inner
148    }
149    /// Skip ahead ignoring `num` bytes
150    ///
151    /// For more advanced seek methods see [Self::seek] that allows
152    /// moving around via more advanced ways
153    ///
154    /// # Arguments
155    ///  - num: The number of bytes to skip.
156    ///
157    /// # Returns
158    ///  - `Ok(u64)`: The new position from the start of the stream.
159    ///  - `Error` If something went wrong
160    #[inline(always)]
161    pub fn skip(&mut self, num: usize) -> Result<u64, ZByteIoError> {
162        self.inner.z_seek(ZSeekFrom::Current(num as i64))
163    }
164    /// Move back from current position to a previous
165    /// position
166    ///
167    /// For more advanced seek methods see [Self::seek] that allows
168    /// moving around via more advanced ways
169    ///
170    /// # Arguments
171    /// - `num`: Positions to move before the current cursor
172    ///
173    /// # Returns
174    ///  - `Ok(u64)`: The new position from the start of the stream.
175    ///  - `Error` If something went wrong
176    #[inline(always)]
177    pub fn rewind(&mut self, num: usize) -> Result<u64, ZByteIoError> {
178        self.inner.z_seek(ZSeekFrom::Current(-(num as i64)))
179    }
180    /// Move around a stream of bytes
181    ///
182    /// This is analogous to the [std::io::Seek] trait with the same ergonomics
183    /// only implemented to allow use in a `no_std` environment
184    ///
185    /// # Arguments
186    /// - `from`: The seek operation type.
187    ///
188    /// # Returns
189    ///  - `Ok(u64)`: The new position from the start of the stream.
190    ///  -  Error if something went wrong.
191    #[inline(always)]
192    pub fn seek(&mut self, from: ZSeekFrom) -> Result<u64, ZByteIoError> {
193        self.inner.z_seek(from)
194    }
195
196    /// Read a single byte from the underlying stream
197    ///
198    /// If an error occurs, it will return `0` as default output
199    /// hence it may be difficult to distinguish a `0` from the underlying source
200    /// and a `0` from an error.
201    /// For that there is [Self::read_u8_err]
202    ///
203    /// # Returns.
204    /// - The next byte on the stream.
205    ///  
206    #[inline(always)]
207    pub fn read_u8(&mut self) -> u8 {
208        self.inner.read_byte_no_error()
209    }
210
211    /// Read a single byte returning an error if the read cannot be satisfied
212    ///
213    /// # Returns
214    /// - `Ok(u8)`: The next byte
215    /// - Error if the byte read could not be satisfied   
216    #[inline(always)]
217    pub fn read_u8_err(&mut self) -> Result<u8, ZByteIoError> {
218        let mut buf = [0];
219        self.inner.read_const_bytes(&mut buf)?;
220        Ok(buf[0])
221    }
222
223    /// Look ahead position bytes and return a reference
224    /// to num_bytes from that position, or an error if the
225    /// peek would be out of bounds.
226    ///
227    /// This doesn't increment the position, bytes would have to be discarded
228    /// at a later point.
229    #[inline]
230    pub fn peek_at(&mut self, position: usize, num_bytes: usize) -> Result<&[u8], ZByteIoError> {
231        // short circuit for zero
232        // important since implementations like File will
233        // cause a syscall on skip
234        if position != 0 {
235            // skip position bytes from start
236            self.skip(position)?;
237        }
238        if num_bytes > 20 * 1024 * 1024 {
239            // resize of 20 MBs, skipping too much, so panic
240            return Err(ZByteIoError::Generic("Too many bytes skipped"));
241        }
242        // resize buffer
243        self.temp_buffer.resize(num_bytes, 0);
244        // read bytes
245        match self.inner.peek_exact_bytes(&mut self.temp_buffer[..]) {
246            Ok(_) => {
247                // rewind back to where we were
248                if position != 0 {
249                    self.rewind(position)?;
250                }
251                Ok(&self.temp_buffer)
252            }
253            Err(e) => Err(e)
254        }
255    }
256    /// Read a fixed number of known bytes to a buffer and return the bytes or an error
257    /// if it occurred.
258    ///
259    /// The size of the `N` value must be small enough to fit the stack space otherwise
260    /// this will cause a stack overflow :)
261    ///
262    /// If you can ignore errors, you can use [Self::read_fixed_bytes_or_zero]
263    ///
264    /// # Returns
265    ///  - `Ok([u8;N])`: The bytes read from the source
266    ///  - An error if it occurred.
267    #[inline(always)]
268    pub fn read_fixed_bytes_or_error<const N: usize>(&mut self) -> Result<[u8; N], ZByteIoError> {
269        let mut byte_store: [u8; N] = [0; N];
270        match self.inner.read_const_bytes(&mut byte_store) {
271            Ok(_) => Ok(byte_store),
272            Err(e) => Err(e)
273        }
274    }
275    /// Read a fixed bytes to an array and if that is impossible, return an array containing
276    /// zeros
277    ///
278    /// If you want to handle errors, use [Self::read_fixed_bytes_or_error]
279    #[inline(always)]
280    pub fn read_fixed_bytes_or_zero<const N: usize>(&mut self) -> [u8; N] {
281        let mut byte_store: [u8; N] = [0; N];
282        self.inner.read_const_bytes_no_error(&mut byte_store);
283        byte_store
284    }
285
286    /// Move the cursor to a fixed position in the stream
287    ///
288    /// This will move the cursor to exacltly `position` bytes from the start of the buffer
289    ///
290    /// # Arguments
291    /// - `position`: The current position to move the cursor.
292    #[inline]
293    pub fn set_position(&mut self, position: usize) -> Result<(), ZByteIoError> {
294        self.seek(ZSeekFrom::Start(position as u64))?;
295
296        Ok(())
297    }
298
299    /// Return true if the underlying buffer can no longer produce bytes
300    ///
301    /// This call may be expensive depending on the underlying buffer type, e.g if
302    /// it's a file, we have to ask the os whether we have more contents, or in other words make a syscall.
303    ///
304    /// Use that wisely
305    ///
306    /// # Returns
307    ///  - `Ok(bool)`: True if we are in `EOF`, false if we can produce more bytes
308    ///  - Error if something went wrong
309    #[inline(always)]
310    pub fn eof(&mut self) -> Result<bool, ZByteIoError> {
311        self.inner.is_eof()
312    }
313
314    /// Return the current position of the inner reader or an error
315    /// if that occurred when reading.
316    ///
317    /// Like [eof](Self::eof), the perf characteristics may vary depending on underlying reader
318    ///
319    /// # Returns
320    /// - `Ok(u64)`: The current position of the inner reader
321    #[inline(always)]
322    pub fn position(&mut self) -> Result<u64, ZByteIoError> {
323        self.inner.z_position()
324    }
325
326    /// Read a fixed number of bytes from the underlying reader returning
327    /// an error if that can't be satisfied
328    ///
329    /// Similar to [std::io::Read::read_exact]
330    ///
331    /// # Returns
332    ///  - `Ok(())`: If the read was successful
333    ///  - An error if the read was unsuccessful including failure to fill the whole bytes
334    pub fn read_exact_bytes(&mut self, buf: &mut [u8]) -> Result<(), ZByteIoError> {
335        self.inner.read_exact_bytes(buf)
336    }
337
338    /// Read some bytes from the inner reader, and return number of bytes read
339    ///
340    /// The implementation may not read bytes enough to fill the buffer
341    ///
342    /// Similar to [std::io::Read::read]
343    ///
344    /// # Returns
345    /// - `Ok(usize)`: Number of bytes actually read to the buffer
346    /// - An error if something went wrong
347    pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<usize, ZByteIoError> {
348        self.inner.read_bytes(buf)
349    }
350}
351
352enum Mode {
353    // Big endian
354    BE,
355    // Little Endian
356    LE
357}
358macro_rules! get_single_type {
359    ($name:tt,$name2:tt,$name3:tt,$name4:tt,$name5:tt,$name6:tt,$int_type:tt) => {
360        impl<T:ZByteReaderTrait> ZReader<T>
361        {
362            #[inline(always)]
363            fn $name(&mut self, mode: Mode) -> $int_type
364            {
365                const SIZE_OF_VAL: usize = core::mem::size_of::<$int_type>();
366
367                let mut space = [0; SIZE_OF_VAL];
368
369                self.inner.read_const_bytes_no_error(&mut space);
370
371                match mode {
372                    Mode::BE => $int_type::from_be_bytes(space),
373                    Mode::LE => $int_type::from_le_bytes(space)
374                }
375            }
376
377            #[inline(always)]
378            fn $name2(&mut self, mode: Mode) -> Result<$int_type, ZByteIoError>
379            {
380                const SIZE_OF_VAL: usize = core::mem::size_of::<$int_type>();
381
382                let mut space = [0; SIZE_OF_VAL];
383
384                match self.inner.read_const_bytes(&mut space)
385                {
386                    Ok(_) => match mode {
387                        Mode::BE => Ok($int_type::from_be_bytes(space)),
388                        Mode::LE => Ok($int_type::from_le_bytes(space))
389                    },
390                     Err(e) =>  Err(e)
391                }
392            }
393            #[doc=concat!("Read ",stringify!($int_type)," as a big endian integer")]
394            #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," read.")]
395            #[inline]
396            pub fn $name3(&mut self) -> Result<$int_type, ZByteIoError>
397            {
398                self.$name2(Mode::BE)
399            }
400
401            #[doc=concat!("Read ",stringify!($int_type)," as a little endian integer")]
402            #[doc=concat!("Returning an error if the underlying buffer cannot support a ",stringify!($int_type)," read.")]
403            #[inline]
404            pub fn $name4(&mut self) -> Result<$int_type, ZByteIoError>
405            {
406                self.$name2(Mode::LE)
407            }
408            #[doc=concat!("Read ",stringify!($int_type)," as a big endian integer")]
409            #[doc=concat!("Returning 0 if the underlying  buffer does not have enough bytes for a ",stringify!($int_type)," read.")]
410            #[inline(always)]
411            pub fn $name5(&mut self) -> $int_type
412            {
413                self.$name(Mode::BE)
414            }
415            #[doc=concat!("Read ",stringify!($int_type)," as a little endian integer")]
416            #[doc=concat!("Returning 0 if the underlying buffer does not have enough bytes for a ",stringify!($int_type)," read.")]
417            #[inline(always)]
418            pub fn $name6(&mut self) -> $int_type
419            {
420                self.$name(Mode::LE)
421            }
422        }
423    };
424}
425
426get_single_type!(
427    get_u16_inner_or_default,
428    get_u16_inner_or_die,
429    get_u16_be_err,
430    get_u16_le_err,
431    get_u16_be,
432    get_u16_le,
433    u16
434);
435get_single_type!(
436    get_u32_inner_or_default,
437    get_u32_inner_or_die,
438    get_u32_be_err,
439    get_u32_le_err,
440    get_u32_be,
441    get_u32_le,
442    u32
443);
444get_single_type!(
445    get_u64_inner_or_default,
446    get_u64_inner_or_die,
447    get_u64_be_err,
448    get_u64_le_err,
449    get_u64_be,
450    get_u64_le,
451    u64
452);
453
454#[cfg(feature = "std")]
455impl<T> std::io::Read for ZReader<T>
456where
457    T: ZByteReaderTrait
458{
459    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
460        use std::io::ErrorKind;
461        self.read_bytes(buf)
462            .map_err(|e| std::io::Error::new(ErrorKind::Other, format!("{:?}", e)))
463    }
464}