uninit-read 0.1.1

A marker trait and utilities for safe, high-performance reads into uninitialized buffers.
Documentation
use std::mem::MaybeUninit;

pub(crate) mod sync_read_ext {
    use super::as_mut_bytes;
    use crate::UninitRead;
    use std::io::Read;
    use std::mem::MaybeUninit;

    /// An extension trait that provides convenience methods for synchronous readers
    /// that implement [`UninitRead`].
    ///
    /// This trait is automatically implemented for any type that satisfies the bounds
    /// `Read + UninitRead`. It offers safe wrappers for reading directly into
    /// uninitialized buffers, abstracting away the underlying `unsafe` operations.
    pub trait UninitSyncReadExt: UninitRead + Read {
        /// Pulls some bytes from this source into the provided uninitialized buffer.
        ///
        /// This method is a wrapper around [`Read::read`] that accepts a slice of
        /// [`MaybeUninit<u8>`], avoiding the need for the caller to perform `unsafe`
        /// operations.
        ///
        /// On a successful read, this method returns a slice `&[u8]` that represents the
        /// portion of the buffer that was initialized by the read. The length of the
        /// returned slice is equivalent to the number of bytes read. An empty slice indicates
        /// EOF has been reached.
        ///
        /// # Errors
        ///
        /// This function will return an error if the underlying call to [`Read::read`]
        /// returns an error.
        fn read_uninit<'buf>(
            &mut self,
            buf: &'buf mut [MaybeUninit<u8>],
        ) -> Result<&'buf [u8], std::io::Error> {
            // Safety: UninitRead guarantees the reader won't read from the buffer
            // before writing to it, making this transmute safe.
            let buf = unsafe { as_mut_bytes(buf) };
            let n = self.read(buf)?;
            Ok(&buf[..n])
        }

        /// Reads the exact number of bytes required to fill `buf` from this source.
        ///
        /// This method is a wrapper around [`Read::read_exact`] that accepts a slice of
        /// [`MaybeUninit<u8>`], avoiding the need for the caller to perform `unsafe`
        /// operations.
        ///
        /// On a successful read, this method returns a slice `&[u8]` that represents the
        /// entire buffer, now guaranteed to be fully initialized.
        ///
        /// # Errors
        ///
        /// This function will return an error if the underlying call to [`Read::read_exact`]
        /// returns an error. If an EOF is found before the buffer is filled, an error
        /// of kind [`std::io::ErrorKind::UnexpectedEof`] is returned.
        fn read_uninit_exact<'buf>(
            &mut self,
            buf: &'buf mut [MaybeUninit<u8>],
        ) -> Result<&'buf [u8], std::io::Error> {
            // Safety: UninitRead guarantees the reader won't read from the buffer
            // before writing to it, making this transmute safe.
            let buf = unsafe { as_mut_bytes(buf) };
            self.read_exact(buf)?;
            Ok(&buf[..])
        }
    }

    impl<R: ?Sized> UninitSyncReadExt for R where R: UninitRead + Read {}
}

#[cfg(feature = "futures-lite")]
pub(crate) mod async_read_ext {
    use super::as_mut_bytes;
    use crate::UninitRead;
    use futures_io::AsyncRead;
    use futures_lite::AsyncReadExt;
    use std::mem::MaybeUninit;

    /// An extension trait that provides convenience methods for asynchronous readers
    /// that implement [`UninitRead`].
    ///
    /// This trait is automatically implemented for any type that satisfies the bounds
    /// `AsyncRead + UninitRead`. It offers safe wrappers for reading directly into
    /// uninitialized buffers, abstracting away the underlying `unsafe` operations.
    pub trait UninitAsyncReadExt: UninitRead + AsyncRead {
        /// Asynchronously pulls some bytes from this source into the provided
        /// uninitialized buffer.
        ///
        /// This method is a wrapper around [`AsyncReadExt::read`] that accepts a slice of
        /// [`MaybeUninit<u8>`], avoiding the need for the caller to perform `unsafe`
        /// operations.
        ///
        /// On a successful read, this method returns a slice `&[u8]` that represents the
        /// portion of the buffer that was initialized by the read. The length of the
        /// returned slice is equivalent to the number of bytes read. An empty slice indicates
        /// EOF has been reached.
        ///
        /// # Errors
        ///
        /// This function will return an error if the underlying call to [`AsyncReadExt::read`]
        /// returns an error.
        fn read_uninit<'buf>(
            &mut self,
            buf: &'buf mut [MaybeUninit<u8>],
        ) -> impl Future<Output = Result<&'buf [u8], std::io::Error>>
        where
            Self: Unpin,
        {
            async move {
                // Safety: UninitRead guarantees the reader won't read from the buffer
                // before writing to it, making this transmute safe.
                let buf = unsafe { as_mut_bytes(buf) };
                let n = self.read(buf).await?;
                Ok(&buf[..n])
            }
        }

        /// Asynchronously reads the exact number of bytes required to fill `buf`.
        ///
        /// This method is a wrapper around [`AsyncReadExt::read_exact`] that accepts a
        /// slice of [`MaybeUninit<u8>`], avoiding the need for the caller to perform
        /// `unsafe` operations.
        ///
        /// On a successful read, this method returns a slice `&[u8]` that represents the
        /// entire buffer, now guaranteed to be fully initialized.
        ///
        /// # Errors
        ///
        /// This function will return an error if the underlying call to
        /// [`AsyncReadExt::read_exact`] returns an error. If an EOF is found before
        /// the buffer is filled, an error of kind [`std::io::ErrorKind::UnexpectedEof`]
        /// is returned.
        fn read_uninit_exact<'buf>(
            &mut self,
            buf: &'buf mut [MaybeUninit<u8>],
        ) -> impl Future<Output = Result<&'buf [u8], std::io::Error>>
        where
            Self: Unpin,
        {
            async move {
                // Safety: UninitRead guarantees the reader won't read from the buffer
                // before writing to it, making this transmute safe.
                let buf = unsafe { as_mut_bytes(buf) };
                self.read_exact(buf).await?;
                Ok(&buf[..])
            }
        }
    }

    impl<R: ?Sized> UninitAsyncReadExt for R where R: UninitRead + AsyncRead {}
}

unsafe fn as_mut_bytes(buf: &mut [MaybeUninit<u8>]) -> &mut [u8] {
    unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) }
}