use crate::error::{Error, IoError}; #[cfg(feature = "alloc")] use alloc::string::String; #[cfg(feature = "alloc")] use alloc::vec::Vec; /// Provides IO error as an associated type. /// /// Must be implemented for all types that also implement at least one of the following traits: `Read`, `Write`, /// `Seek`. pub trait IoBase { /// Type of errors returned by input/output operations. type Error: IoError; } /// The `Read` trait allows for reading bytes from a source. /// /// It is based on the `std::io::Read` trait. pub trait Read: IoBase { /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. /// /// This function does not provide any guarantees about whether it blocks waiting for data, but if an object needs /// to block for a read and cannot, it will typically signal this via an Err return value. /// /// If the return value of this method is `Ok(n)`, then it must be guaranteed that `0 <= n <= buf.len()`. A nonzero /// `n` value indicates that the buffer buf has been filled in with n bytes of data from this source. If `n` is /// `0`, then it can indicate one of two scenarios: /// /// 1. This reader has reached its "end of file" and will likely no longer be able to produce bytes. Note that this /// does not mean that the reader will always no longer be able to produce bytes. /// 2. The buffer specified was 0 bytes in length. /// /// It is not an error if the returned value `n` is smaller than the buffer size, even when the reader is not at /// the end of the stream yet. This may happen for example because fewer bytes are actually available right now /// (e. g. being close to end-of-file) or because `read()` was interrupted by a signal. /// /// # Errors /// /// If this function encounters any form of I/O or other error, an error will be returned. If an error is returned /// then it must be guaranteed that no bytes were read. /// An error for which `IoError::is_interrupted` returns true is non-fatal and the read operation should be retried /// if there is nothing else to do. fn read(&mut self, buf: &mut [u8]) -> Result>; /// Read the exact number of bytes required to fill `buf`. /// /// This function reads as many bytes as necessary to completely fill the specified buffer `buf`. /// /// # Errors /// /// If this function encounters an error for which `IoError::is_interrupted` returns true then the error is ignored /// and the operation will continue. /// /// If this function encounters an end of file before completely filling the buffer, it returns an error /// instantiated by a call to `IoError::new_unexpected_eof_error`. The contents of `buf` are unspecified in this /// case. /// /// If this function returns an error, it is unspecified how many bytes it has read, but it will never read more /// than would be necessary to completely fill the buffer. fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Error> { while !buf.is_empty() { match self.read(buf) { Ok(0) => break, Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; } Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } if buf.is_empty() { Ok(()) } else { Err(Error::::new_unexpected_eof_error()) } } #[cfg(feature = "alloc")] fn read_to_end(&mut self, buf: &mut Vec) -> Result> { const CHUNK_SIZE: usize = 32; let start_len = buf.len(); loop { let mut chunk_buf = [0; CHUNK_SIZE]; let read = self.read(&mut chunk_buf)?; buf.extend_from_slice(&chunk_buf[..read]); if read == 0 { return Ok(buf.len() - start_len); } } } #[cfg(feature = "alloc")] fn read_to_string(&mut self, buf: &mut String) -> Result> { let read = self.read_to_end(unsafe { buf.as_mut_vec() })?; if str::from_utf8(buf.as_bytes()).is_err() { Err(Error::InvalidUTF8) } else { Ok(read) } } } /// The `Write` trait allows for writing bytes into the sink. /// /// It is based on the `std::io::Write` trait. pub trait Write: IoBase { /// Write a buffer into this writer, returning how many bytes were written. /// /// # Errors /// /// Each call to write may generate an I/O error indicating that the operation could not be completed. If an error /// is returned then no bytes in the buffer were written to this writer. /// It is not considered an error if the entire buffer could not be written to this writer. fn write(&mut self, buf: &[u8]) -> Result>; /// Attempts to write an entire buffer into this writer. /// /// This method will continuously call `write` until there is no more data to be written or an error is returned. /// Errors for which `IoError::is_interrupted` method returns true are being skipped. This method will not return /// until the entire buffer has been successfully written or such an error occurs. /// If `write` returns 0 before the entire buffer has been written this method will return an error instantiated by /// a call to `IoError::new_write_zero_error`. /// /// # Errors /// /// This function will return the first error for which `IoError::is_interrupted` method returns false that `write` /// returns. fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error> { while !buf.is_empty() { match self.write(buf) { Ok(0) => { return Err(Error::::new_write_zero_error()); } Ok(n) => buf = &buf[n..], Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } Ok(()) } /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. /// /// # Errors /// /// It is considered an error if not all bytes could be written due to I/O errors or EOF being reached. fn flush(&mut self) -> Result<(), Error>; } /// Enumeration of possible methods to seek within an I/O object. /// /// It is based on the `std::io::SeekFrom` enum. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. Start(u64), /// Sets the offset to the size of this object plus the specified number of bytes. End(i64), /// Sets the offset to the current position plus the specified number of bytes. Current(i64), } /// The `Seek` trait provides a cursor which can be moved within a stream of bytes. /// /// It is based on the `std::io::Seek` trait. pub trait Seek: IoBase { /// Seek to an offset, in bytes, in a stream. /// /// A seek beyond the end of a stream or to a negative position is not allowed. /// /// If the seek operation completed successfully, this method returns the new position from the start of the /// stream. That position can be used later with `SeekFrom::Start`. /// /// # Errors /// Seeking to a negative offset is considered an error. fn seek(&mut self, pos: SeekFrom) -> Result>; } #[cfg(all(feature = "std", not(target_arch = "riscv64")))] impl From for std::io::SeekFrom { fn from(from: SeekFrom) -> Self { match from { SeekFrom::Start(n) => std::io::SeekFrom::Start(n), SeekFrom::End(n) => std::io::SeekFrom::End(n), SeekFrom::Current(n) => std::io::SeekFrom::Current(n), } } } #[cfg(all(feature = "std", not(target_arch = "riscv64")))] impl From for SeekFrom { fn from(from: std::io::SeekFrom) -> Self { match from { std::io::SeekFrom::Start(n) => SeekFrom::Start(n), std::io::SeekFrom::End(n) => SeekFrom::End(n), std::io::SeekFrom::Current(n) => SeekFrom::Current(n), } } } #[cfg(all(feature = "std", not(target_arch = "riscv64")))] impl IoBase for std::fs::File { type Error = std::io::Error; } #[cfg(all(feature = "std", not(target_arch = "riscv64")))] impl> Read for T { fn read(&mut self, buf: &mut [u8]) -> Result> { self.read(buf).map_err(Error::Io) } fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { self.read_exact(buf).map_err(Error::Io) } } #[cfg(all(feature = "std", not(target_arch = "riscv64")))] impl> Write for T { fn write(&mut self, buf: &[u8]) -> Result> { self.write(buf).map_err(Error::Io) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { self.write_all(buf).map_err(Error::Io) } fn flush(&mut self) -> Result<(), Error> { self.flush().map_err(Error::Io) } } #[cfg(all(feature = "std", not(target_arch = "riscv64")))] impl> Seek for T { fn seek(&mut self, pos: SeekFrom) -> Result> { self.seek(pos.into()).map_err(Error::Io) } } pub(crate) trait ReadLeExt { type Error; fn read_u8(&mut self) -> Result>; fn read_u16_le(&mut self) -> Result>; fn read_u32_le(&mut self) -> Result>; } impl ReadLeExt for T { type Error = ::Error; fn read_u8(&mut self) -> Result> { let mut buf = [0_u8; 1]; self.read_exact(&mut buf)?; Ok(buf[0]) } fn read_u16_le(&mut self) -> Result> { let mut buf = [0_u8; 2]; self.read_exact(&mut buf)?; Ok(u16::from_le_bytes(buf)) } fn read_u32_le(&mut self) -> Result> { let mut buf = [0_u8; 4]; self.read_exact(&mut buf)?; Ok(u32::from_le_bytes(buf)) } } #[allow(unused)] pub(crate) trait WriteLeExt { type Error; fn write_u8(&mut self, n: u8) -> Result<(), Error>; fn write_u16_le(&mut self, n: u16) -> Result<(), Error>; fn write_u32_le(&mut self, n: u32) -> Result<(), Error>; } impl WriteLeExt for T { type Error = ::Error; fn write_u8(&mut self, n: u8) -> Result<(), Error> { self.write_all(&[n]) } fn write_u16_le(&mut self, n: u16) -> Result<(), Error> { self.write_all(&n.to_le_bytes()) } fn write_u32_le(&mut self, n: u32) -> Result<(), Error> { self.write_all(&n.to_le_bytes()) } }