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}