//! This crate provides a zero-copy [`Read`] trait and a simplified [`Write`]
//! trait useful for possibly `no_std` environments.
//!
//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html
// zc_io types in rustdoc of other crates get linked to here:
#![doc(html_root_url = "https://docs.rs/zc_io/0.1.1")]
// Enable https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html:
#![cfg_attr(doc_cfg, feature(doc_cfg))]
// Support using zc_io without the standard library:
#![cfg_attr(not(feature = "std"), no_std)]
// Enable lints:
#![deny(clippy::pedantic, missing_docs)]
extern crate alloc;
#[macro_use]
mod error;
#[cfg(feature = "std")]
pub use error::ErrorKind;
pub use error::{Error, Result};
use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use core::{cmp, mem};
#[cfg(feature = "std")]
use std::{
fmt,
io::{self, IoSlice, IoSliceMut, SeekFrom},
slice,
};
/// The `Read<'data>` trait allows for reading bytes with a lifetime of `'data`
/// from some source.
///
/// Implementors of the `Read<'data>` trait are called "zero-copy readers".
///
/// Note that not every source will enable zero-copy reads. This is evident by
/// [`read_slice()`] returning a [`Cow`] (such that it might return a [`Vec`] if
/// the bytes requested cannot be borrowed).
///
/// [`read_slice()`]: Read::read_slice
pub trait Read<'data> {
/// Reads the next byte from the source.
///
/// # Errors
///
/// If this function encounters an error of the kind
/// [`ErrorKind::Interrupted`] then the error is ignored and the operation
/// will continue.
///
/// An [`ErrorKind::UnexpectedEof`] error is returned if this reader has
/// reached end-of-file before the call to this method.
///
/// If any other read error is encountered then this function immediately
/// returns.
///
/// If this function returns an error, it is unspecified how many bytes got
/// read.
fn read_next(&mut self) -> Result<u8>;
/// Reads `n` bytes from this reader, borrowing bytes if possible.
///
/// As this trait is safe to implement, callers cannot rely on the number of
/// returned bytes being equal to `n` for safety. Extra care needs to be
/// taken when `unsafe` functions are used to access the read bytes. Callers
/// have to ensure that no unchecked out-of-bounds accesses are possible
/// even if the number of returned bytes are greater or less than `n`.
///
/// # Errors
///
/// If this function encounters an error of the kind
/// [`ErrorKind::Interrupted`] then the error is ignored and the operation
/// will continue.
///
/// An [`ErrorKind::UnexpectedEof`] error is returned if this reader has
/// reached end-of-file before the call to this method.
///
/// If any other read error is encountered then this function immediately
/// returns.
///
/// If this function returns an error, it is unspecified how many bytes got
/// read.
fn read_slice(&mut self, n: usize) -> Result<Cow<'data, [u8]>>;
/// Reads exactly `N` bytes from this reader.
///
/// Note that because this method returns an array it will always copy the
/// bytes from the source regardless if zero-copy reads are possible.
///
/// # Errors
///
/// If this function encounters an error of the kind
/// [`ErrorKind::Interrupted`] then the error is ignored and the operation
/// will continue.
///
/// An [`ErrorKind::UnexpectedEof`] error is returned if this reader has
/// reached end-of-file before the call to this method.
///
/// If any other read error is encountered then this function immediately
/// returns.
///
/// If this function returns an error, it is unspecified how many bytes got
/// read.
fn read_array<const N: usize>(&mut self) -> Result<[u8; N]>;
}
impl<'data, R> Read<'data> for &mut R
where
R: Read<'data>,
{
#[inline]
fn read_next(&mut self) -> Result<u8> {
(**self).read_next()
}
#[inline]
fn read_slice(&mut self, len: usize) -> Result<Cow<'data, [u8]>> {
(**self).read_slice(len)
}
#[inline]
fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
(**self).read_array()
}
}
impl<'data, R> Read<'data> for Box<R>
where
R: Read<'data>,
{
#[inline]
fn read_next(&mut self) -> Result<u8> {
(**self).read_next()
}
#[inline]
fn read_slice(&mut self, len: usize) -> Result<Cow<'data, [u8]>> {
(**self).read_slice(len)
}
#[inline]
fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
(**self).read_array()
}
}
impl<'data> Read<'data> for &'data [u8] {
#[inline]
fn read_next(&mut self) -> Result<u8> {
if let Some((&byte, rest)) = self.split_first() {
*self = rest;
return Ok(byte);
}
Err(error!(UnexpectedEof, "failed to read byte"))
}
#[inline]
fn read_slice(&mut self, len: usize) -> Result<Cow<'data, [u8]>> {
if self.len() < len {
return Err(error!(UnexpectedEof, "failed to read slice"));
}
let (slice, rest) = self.split_at(len);
*self = rest;
Ok(Cow::Borrowed(slice))
}
#[inline]
fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
if self.len() < N {
return Err(error!(UnexpectedEof, "failed to read array"));
}
let (array, rest) = self.split_at(N);
*self = rest;
// SAFETY: a slice of bytes whose length is `N` is identical to
// `[u8; N]`.
Ok(unsafe { *array.as_ptr().cast::<[u8; N]>() })
}
}
/// The `IoReader<R>` struct implements [`Read<'data>`] to any reader.
///
/// Due to the interface of [`io::Read`], an `IoReader<R>` will never support
/// zero-copy operations, meaning that [`read_slice`] will always return an
/// [`Owned`] value.
///
/// [`Read<'data>`]: Read
/// [`read_slice`]: Read::read_slice
/// [`Owned`]: Cow::Owned
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub struct IoReader<R> {
inner: R,
}
#[cfg(feature = "std")]
impl<R> IoReader<R>
where
R: io::Read,
{
/// Creates a new `IoReader<R>` from some reader.
#[must_use]
#[inline]
pub fn new(reader: R) -> Self {
IoReader { inner: reader }
}
/// Gets a reference to the underlying reader.
#[must_use]
#[inline]
pub fn get_ref(&self) -> &R {
&self.inner
}
/// Gets a mutable reference to the underlying reader.
#[must_use]
#[inline]
pub fn get_mut(&mut self) -> &mut R {
&mut self.inner
}
/// Unwraps the `IoReader<R>`, returning the underlying reader.
#[must_use]
#[inline]
pub fn into_inner(self) -> R {
self.inner
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl<'data, R> Read<'data> for IoReader<R>
where
R: io::Read,
{
#[inline]
fn read_next(&mut self) -> Result<u8> {
let mut byte = 0;
self.inner.read_exact(slice::from_mut(&mut byte))?;
Ok(byte)
}
#[inline]
fn read_slice(&mut self, len: usize) -> Result<Cow<'data, [u8]>> {
let mut buf = vec![0; len];
self.inner.read_exact(&mut buf)?;
Ok(Cow::Owned(buf))
}
#[inline]
fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
let mut array = [0; N];
self.inner.read_exact(&mut array)?;
Ok(array)
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl<R> io::Read for IoReader<R>
where
R: io::Read,
{
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
#[inline]
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.inner.read_to_end(buf)
}
#[inline]
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
self.inner.read_to_string(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.inner.read_exact(buf)
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl<R> io::BufRead for IoReader<R>
where
R: io::BufRead,
{
#[inline]
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.inner.fill_buf()
}
#[inline]
fn consume(&mut self, amt: usize) {
self.inner.consume(amt);
}
#[inline]
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
self.inner.read_until(byte, buf)
}
#[inline]
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
self.inner.read_line(buf)
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl<R> io::Seek for IoReader<R>
where
R: io::Seek,
{
#[inline]
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.inner.seek(pos)
}
#[inline]
fn rewind(&mut self) -> io::Result<()> {
self.inner.rewind()
}
#[inline]
fn stream_position(&mut self) -> io::Result<u64> {
self.inner.stream_position()
}
}
/// A simplified facade of [`io::Write`] for easier use in possibly [`no_std`]
/// environments.
///
/// [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html
pub trait Write {
/// Write a buffer into this writer, returning how many bytes were written.
///
/// This function will attempt to write the entire contents of `buf`, but
/// the entire write might not succeed, or the write may also generate an
/// error. A call to `write` represents *at most one* attempt to write to
/// any wrapped object.
///
/// Calls to `write` are not guaranteed to block waiting for data to be
/// written, and a write which would otherwise block can be indicated through
/// an [`Err`] variant.
///
/// If the return value is [`Ok(n)`] then it must be guaranteed that
/// `n <= buf.len()`. A return value of `0` typically means that the
/// underlying object is no longer able to accept bytes and will likely not
/// be able to in the future as well, or that the buffer provided is empty.
///
/// # 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.
///
/// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the
/// write operation should be retried if there is nothing else to do.
fn write(&mut self, buf: &[u8]) -> Result<usize>;
/// 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 getting reached.
fn flush(&mut self) -> 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 of non-[`ErrorKind::Interrupted`] kind is
/// returned. This method will not return until the entire buffer has been
/// successfully written or such an error occurs. The first error that is
/// not of [`ErrorKind::Interrupted`] kind generated from this method will
/// be returned.
///
/// If the buffer contains no data, this will never call [`write`].
///
/// # Errors
///
/// This function will return the first error of
/// non-[`ErrorKind::Interrupted`] kind that [`write`] returns.
///
/// [`write`]: Write::write
fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
while !buf.is_empty() {
match self.write(buf) {
Ok(0) => return Err(error!(WriteZero, "failed to write whole buffer")),
Ok(n) => buf = &buf[n..],
#[cfg(feature = "std")]
Err(ref error) if error.kind() == ErrorKind::Interrupted => continue,
Err(error) => return Err(error),
}
}
Ok(())
}
}
impl<W> Write for &mut W
where
W: Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
(**self).write(buf)
}
#[inline]
fn flush(&mut self) -> Result<()> {
(**self).flush()
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
(**self).write_all(buf)
}
}
impl<W> Write for Box<W>
where
W: Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
(**self).write(buf)
}
#[inline]
fn flush(&mut self) -> Result<()> {
(**self).flush()
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
(**self).write_all(buf)
}
}
/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting
/// its data.
///
/// Note that writing updates the slice to point to the yet unwritten part.
/// The slice will be empty when it has been completely overwritten.
///
/// If the number of bytes to be written exceeds the size of the slice, write
/// operations will return short writes: ultimately, `Ok(0)`; in this situation,
/// `write_all` returns an error of kind `ErrorKind::WriteZero`.
impl Write for &mut [u8] {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let amount = cmp::min(buf.len(), self.len());
let (data, tail) = mem::take(self).split_at_mut(amount);
data.copy_from_slice(&buf[..amount]);
*self = tail;
Ok(amount)
}
#[inline]
fn flush(&mut self) -> Result<()> {
Ok(())
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
if self.write(buf)? == buf.len() {
Ok(())
} else {
Err(error!(WriteZero, "failed to write whole buffer"))
}
}
}
/// Write is implemented for `Vec<u8>` by appending to the vector. The vector
/// will grow as needed.
impl Write for Vec<u8> {
#[inline]
fn write(&mut self, data: &[u8]) -> Result<usize> {
self.extend_from_slice(data);
Ok(data.len())
}
#[inline]
fn flush(&mut self) -> Result<()> {
Ok(())
}
#[inline]
fn write_all(&mut self, data: &[u8]) -> Result<()> {
self.extend_from_slice(data);
Ok(())
}
}
/// The `IoWriter<W>` struct implements [`Write`] to any I/O writer.
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub struct IoWriter<W> {
inner: W,
}
#[cfg(feature = "std")]
impl<W> IoWriter<W>
where
W: io::Write,
{
/// Creates a new `IoWriter<W>` from some writer.
#[must_use]
#[inline]
pub fn new(writer: W) -> Self {
IoWriter { inner: writer }
}
/// Gets a reference to the underlying writer.
#[must_use]
#[inline]
pub fn get_ref(&self) -> &W {
&self.inner
}
/// Gets a mutable reference to the underlying writer.
#[must_use]
#[inline]
pub fn get_mut(&mut self) -> &mut W {
&mut self.inner
}
/// Unwraps the `IoWriter<W>`, returning the underlying writer.
#[must_use]
#[inline]
pub fn into_inner(self) -> W {
self.inner
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl<W> Write for IoWriter<W>
where
W: io::Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let amount = self.inner.write(buf)?;
Ok(amount)
}
#[inline]
fn flush(&mut self) -> Result<()> {
self.inner.flush()?;
Ok(())
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
self.inner.write_all(buf)?;
Ok(())
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl<W> io::Write for IoWriter<W>
where
W: io::Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.write_vectored(bufs)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.inner.write_all(buf)
}
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
self.inner.write_fmt(fmt)
}
}