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}