294 lines
11 KiB
Rust
294 lines
11 KiB
Rust
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<usize, Error<Self::Error>>;
|
|
|
|
/// 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<Self::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::<Self::Error>::new_unexpected_eof_error())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error<Self::Error>> {
|
|
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<usize, Error<Self::Error>> {
|
|
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<usize, Error<Self::Error>>;
|
|
|
|
/// 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<Self::Error>> {
|
|
while !buf.is_empty() {
|
|
match self.write(buf) {
|
|
Ok(0) => {
|
|
return Err(Error::<Self::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<Self::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<u64, Error<Self::Error>>;
|
|
}
|
|
|
|
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
impl From<SeekFrom> 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<std::io::SeekFrom> 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<T: std::io::Read + IoBase<Error = std::io::Error>> Read for T {
|
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::Error>> {
|
|
self.read(buf).map_err(Error::Io)
|
|
}
|
|
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error<Self::Error>> {
|
|
self.read_exact(buf).map_err(Error::Io)
|
|
}
|
|
}
|
|
|
|
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
impl<T: std::io::Write + IoBase<Error = std::io::Error>> Write for T {
|
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Error<Self::Error>> {
|
|
self.write(buf).map_err(Error::Io)
|
|
}
|
|
|
|
fn write_all(&mut self, buf: &[u8]) -> Result<(), Error<Self::Error>> {
|
|
self.write_all(buf).map_err(Error::Io)
|
|
}
|
|
|
|
fn flush(&mut self) -> Result<(), Error<Self::Error>> {
|
|
self.flush().map_err(Error::Io)
|
|
}
|
|
}
|
|
|
|
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
impl<T: std::io::Seek + IoBase<Error = std::io::Error>> Seek for T {
|
|
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error<Self::Error>> {
|
|
self.seek(pos.into()).map_err(Error::Io)
|
|
}
|
|
}
|
|
|
|
pub(crate) trait ReadLeExt {
|
|
type Error;
|
|
fn read_u8(&mut self) -> Result<u8, Error<Self::Error>>;
|
|
fn read_u16_le(&mut self) -> Result<u16, Error<Self::Error>>;
|
|
fn read_u32_le(&mut self) -> Result<u32, Error<Self::Error>>;
|
|
}
|
|
|
|
impl<T: Read> ReadLeExt for T {
|
|
type Error = <Self as IoBase>::Error;
|
|
|
|
fn read_u8(&mut self) -> Result<u8, Error<Self::Error>> {
|
|
let mut buf = [0_u8; 1];
|
|
self.read_exact(&mut buf)?;
|
|
Ok(buf[0])
|
|
}
|
|
|
|
fn read_u16_le(&mut self) -> Result<u16, Error<Self::Error>> {
|
|
let mut buf = [0_u8; 2];
|
|
self.read_exact(&mut buf)?;
|
|
Ok(u16::from_le_bytes(buf))
|
|
}
|
|
|
|
fn read_u32_le(&mut self) -> Result<u32, Error<Self::Error>> {
|
|
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<Self::Error>>;
|
|
fn write_u16_le(&mut self, n: u16) -> Result<(), Error<Self::Error>>;
|
|
fn write_u32_le(&mut self, n: u32) -> Result<(), Error<Self::Error>>;
|
|
}
|
|
|
|
impl<T: Write> WriteLeExt for T {
|
|
type Error = <Self as IoBase>::Error;
|
|
|
|
fn write_u8(&mut self, n: u8) -> Result<(), Error<Self::Error>> {
|
|
self.write_all(&[n])
|
|
}
|
|
|
|
fn write_u16_le(&mut self, n: u16) -> Result<(), Error<Self::Error>> {
|
|
self.write_all(&n.to_le_bytes())
|
|
}
|
|
|
|
fn write_u32_le(&mut self, n: u32) -> Result<(), Error<Self::Error>> {
|
|
self.write_all(&n.to_le_bytes())
|
|
}
|
|
}
|