diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0594e8c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! Simple, Low-level I/O traits +//! +//! This crate provides two simple traits: `Read` and `Write`. These traits +//! mimic their counterparts in `std::io`, but are trimmed for simplicity +//! and can be used in `no_std` and `no_alloc` environments. Since this +//! crate contains only traits, inline functions and unit structs, it should +//! be a zero-cost abstraction. +//! +//! If the `std` feature is enabled, we provide blanket implementations for +//! all `std::io` types. If the `alloc` feature is enabled, we provide +//! implementations for `Vec<u8>`. In all cases, you get implementations +//! for byte slices. You can, of course, implement the traits for your own +//! types. + +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] +#![deny(clippy::all)] +#![deny(clippy::cargo)] + +extern crate std; + +#[cfg(feature = "alloc")] +extern crate alloc; + +/// A trait indicating a type that can read bytes +/// +/// Note that this is similar to `std::io::Read`, but simplified for use in a +/// `no_std` context. +pub trait Read { + /// The error type + type Error; + + /// Reads exactly `data.len()` bytes or fails + fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error>; +} + +/// A trait indicating a type that can write bytes +/// +/// Note that this is similar to `std::io::Write`, but simplified for use in a +/// `no_std` context. +pub trait Write { + /// The error type + type Error; + + /// Writes all bytes from `data` or fails + fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error>; + + /// Flushes all output + fn flush(&mut self) -> Result<(), Self::Error>; +} + +#[cfg(feature = "std")] +impl<T: std::io::Read> Read for T { + type Error = std::io::Error; + + #[inline] + fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.read_exact(data) + } +} + +#[cfg(feature = "std")] +impl<T: std::io::Write> Write for T { + type Error = std::io::Error; + + #[inline] + fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.write_all(data) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + self.flush() + } +} + +#[cfg(not(feature = "std"))] +impl<R: Read + ?Sized> Read for &mut R { + type Error = R::Error; + + #[inline] + fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + (**self).read_exact(data) + } +} + +#[cfg(not(feature = "std"))] +impl<W: Write + ?Sized> Write for &mut W { + type Error = W::Error; + + #[inline] + fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> { + (**self).write_all(data) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + (**self).flush() + } +} + +/// An error indicating there are no more bytes to read +#[cfg(not(feature = "std"))] +#[derive(Debug)] +pub struct EndOfFile(()); + +#[cfg(not(feature = "std"))] +impl Read for &[u8] { + type Error = EndOfFile; + + #[inline] + fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + if data.len() > self.len() { + return Err(EndOfFile(())); + } + + let (prefix, suffix) = self.split_at(data.len()); + data.copy_from_slice(prefix); + *self = suffix; + Ok(()) + } +} + +/// An error indicating that the output cannot accept more bytes +#[cfg(not(feature = "std"))] +#[derive(Debug)] +pub struct OutOfSpace(()); + +#[cfg(not(feature = "std"))] +impl Write for &mut [u8] { + type Error = OutOfSpace; + + #[inline] + fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> { + if data.len() > self.len() { + return Err(OutOfSpace(())); + } + + let (prefix, suffix) = core::mem::replace(self, &mut []).split_at_mut(data.len()); + prefix.copy_from_slice(data); + *self = suffix; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +#[cfg(all(not(feature = "std"), feature = "alloc"))] +impl Write for alloc::vec::Vec<u8> { + type Error = core::convert::Infallible; + + #[inline] + fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.extend_from_slice(data); + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn read_eof() { + let mut reader = &[1u8; 0][..]; + let mut buffer = [0u8; 1]; + + reader.read_exact(&mut buffer[..]).unwrap_err(); + } + + #[test] + fn read_one() { + let mut reader = &[1u8; 1][..]; + let mut buffer = [0u8; 1]; + + reader.read_exact(&mut buffer[..]).unwrap(); + assert_eq!(buffer[0], 1); + + reader.read_exact(&mut buffer[..]).unwrap_err(); + } + + #[test] + fn read_two() { + let mut reader = &[1u8; 2][..]; + let mut buffer = [0u8; 1]; + + reader.read_exact(&mut buffer[..]).unwrap(); + assert_eq!(buffer[0], 1); + + reader.read_exact(&mut buffer[..]).unwrap(); + assert_eq!(buffer[0], 1); + + reader.read_exact(&mut buffer[..]).unwrap_err(); + } + + #[test] + #[cfg(feature = "std")] + fn read_std() { + let mut reader = std::io::repeat(1); + let mut buffer = [0u8; 2]; + + reader.read_exact(&mut buffer[..]).unwrap(); + assert_eq!(buffer[0], 1); + assert_eq!(buffer[1], 1); + } + + #[test] + fn write_oos() { + let mut writer = &mut [0u8; 0][..]; + + writer.write_all(&[1u8; 1][..]).unwrap_err(); + } + + #[test] + fn write_one() { + let mut buffer = [0u8; 1]; + let mut writer = &mut buffer[..]; + + writer.write_all(&[1u8; 1][..]).unwrap(); + writer.write_all(&[1u8; 1][..]).unwrap_err(); + assert_eq!(buffer[0], 1); + } + + #[test] + fn write_two() { + let mut buffer = [0u8; 2]; + let mut writer = &mut buffer[..]; + + writer.write_all(&[1u8; 1][..]).unwrap(); + writer.write_all(&[1u8; 1][..]).unwrap(); + writer.write_all(&[1u8; 1][..]).unwrap_err(); + assert_eq!(buffer[0], 1); + assert_eq!(buffer[1], 1); + } + + #[test] + #[cfg(feature = "alloc")] + fn write_vec() { + let mut buffer = alloc::vec::Vec::new(); + + buffer.write_all(&[1u8; 1][..]).unwrap(); + buffer.write_all(&[1u8; 1][..]).unwrap(); + + assert_eq!(buffer.len(), 2); + assert_eq!(buffer[0], 1); + assert_eq!(buffer[1], 1); + } + + #[test] + #[cfg(feature = "std")] + fn write_std() { + let mut writer = std::io::sink(); + + writer.write_all(&[1u8; 1][..]).unwrap(); + writer.write_all(&[1u8; 1][..]).unwrap(); + } +} |