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}