uninit-read 0.1.1

A marker trait and utilities for safe, high-performance reads into uninitialized buffers.
Documentation
use crate::UninitRead;
use std::fmt;
use std::fmt::{Arguments, Debug, Display};
use std::io::{BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};

/// A wrapper that asserts a reader upholds the `UninitRead` contract.
///
/// Use this to wrap a reader from a third-party crate that is known to be
/// well-behaved (i.e., it doesn't read from the buffer before writing to it)
/// but does not implement `UninitRead` itself.
#[repr(transparent)]
pub struct AssumeUninitRead<R>(R);

impl<R> AssumeUninitRead<R> {
    /// Wraps a reader, asserting that it adheres to the `UninitRead` contract.
    ///
    /// # Safety
    ///
    /// By calling this function, the caller guarantees that the `Read` and/or
    /// `AsyncRead` implementation of `R` will not read from any part of the
    /// provided buffer that has not been written to by the implementation
    /// itself within the same call.
    #[inline]
    pub const unsafe fn assume_uninit_read(reader: R) -> Self {
        Self(reader)
    }

    /// Unwraps this `AssumeUninitRead`, returning the underlying reader.
    #[inline]
    pub fn into_inner(self) -> R {
        self.0
    }
}

impl<R> AsRef<R> for AssumeUninitRead<R> {
    #[inline]
    fn as_ref(&self) -> &R {
        &self.0
    }
}

impl<R> AsMut<R> for AssumeUninitRead<R> {
    #[inline]
    fn as_mut(&mut self) -> &mut R {
        &mut self.0
    }
}

impl<R: Read> Read for AssumeUninitRead<R> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.0.read(buf)
    }

    #[inline]
    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
        self.0.read_vectored(bufs)
    }

    #[inline]
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
        self.0.read_to_end(buf)
    }

    #[inline]
    fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
        self.0.read_to_string(buf)
    }

    #[inline]
    fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
        self.0.read_exact(buf)
    }
}

impl<R: BufRead> BufRead for AssumeUninitRead<R> {
    #[inline]
    fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
        self.0.fill_buf()
    }

    #[inline]
    fn consume(&mut self, amount: usize) {
        self.0.consume(amount)
    }

    #[inline]
    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> std::io::Result<usize> {
        self.0.read_until(byte, buf)
    }

    #[inline]
    fn skip_until(&mut self, byte: u8) -> std::io::Result<usize> {
        self.0.skip_until(byte)
    }

    #[inline]
    fn read_line(&mut self, buf: &mut String) -> std::io::Result<usize> {
        self.0.read_line(buf)
    }
}

impl<R: Seek> Seek for AssumeUninitRead<R> {
    #[inline]
    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
        self.0.seek(pos)
    }

    #[inline]
    fn rewind(&mut self) -> std::io::Result<()> {
        self.0.rewind()
    }

    #[inline]
    fn stream_position(&mut self) -> std::io::Result<u64> {
        self.0.stream_position()
    }

    #[inline]
    fn seek_relative(&mut self, offset: i64) -> std::io::Result<()> {
        self.0.seek_relative(offset)
    }
}

impl<R: Write> Write for AssumeUninitRead<R> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.0.write(buf)
    }

    #[inline]
    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> std::io::Result<usize> {
        self.0.write_vectored(bufs)
    }

    #[inline]
    fn flush(&mut self) -> std::io::Result<()> {
        self.0.flush()
    }

    #[inline]
    fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
        self.0.write_all(buf)
    }

    #[inline]
    fn write_fmt(&mut self, args: Arguments<'_>) -> std::io::Result<()> {
        self.0.write_fmt(args)
    }
}

#[cfg(feature = "async")]
mod async_wrapper {
    use crate::AssumeUninitRead;
    use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite};
    use std::io::{IoSlice, IoSliceMut, SeekFrom};
    use std::pin::Pin;
    use std::task::{Context, Poll};

    impl<R> AssumeUninitRead<R> {
        /// Projects a pinned mutable reference to the inner reader.
        ///
        /// # Safety
        ///
        /// `AssumeUninitRead` is a transparent wrapper with no drop logic
        /// or pin projections. Pinning is structural: if the wrapper is pinned,
        /// the inner `R` is also pinned and never moved.
        #[inline]
        fn project_pin(self: Pin<&mut Self>) -> Pin<&mut R> {
            // SAFETY: See function documentation.
            unsafe { self.map_unchecked_mut(|s| &mut s.0) }
        }
    }

    impl<R: AsyncRead> AsyncRead for AssumeUninitRead<R> {
        #[inline]
        fn poll_read(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            buf: &mut [u8],
        ) -> Poll<std::io::Result<usize>> {
            self.project_pin().poll_read(cx, buf)
        }

        #[inline]
        fn poll_read_vectored(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            bufs: &mut [IoSliceMut<'_>],
        ) -> Poll<std::io::Result<usize>> {
            self.project_pin().poll_read_vectored(cx, bufs)
        }
    }

    impl<R: AsyncBufRead> AsyncBufRead for AssumeUninitRead<R> {
        #[inline]
        fn poll_fill_buf(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
        ) -> Poll<std::io::Result<&[u8]>> {
            self.project_pin().poll_fill_buf(cx)
        }

        #[inline]
        fn consume(self: Pin<&mut Self>, amt: usize) {
            self.project_pin().consume(amt)
        }
    }

    impl<R: AsyncSeek> AsyncSeek for AssumeUninitRead<R> {
        #[inline]
        fn poll_seek(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            pos: SeekFrom,
        ) -> Poll<std::io::Result<u64>> {
            self.project_pin().poll_seek(cx, pos)
        }
    }

    impl<R: AsyncWrite> AsyncWrite for AssumeUninitRead<R> {
        #[inline]
        fn poll_write(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            buf: &[u8],
        ) -> Poll<std::io::Result<usize>> {
            self.project_pin().poll_write(cx, buf)
        }

        #[inline]
        fn poll_write_vectored(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            bufs: &[IoSlice<'_>],
        ) -> Poll<std::io::Result<usize>> {
            self.project_pin().poll_write_vectored(cx, bufs)
        }

        #[inline]
        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
            self.project_pin().poll_flush(cx)
        }

        #[inline]
        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
            self.project_pin().poll_close(cx)
        }
    }
}

impl<R: Debug> Debug for AssumeUninitRead<R> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("AssumeUninitRead").field(&self.0).finish()
    }
}

impl<R: Display> Display for AssumeUninitRead<R> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Display::fmt(&self.0, f)
    }
}

impl<R: Clone> Clone for AssumeUninitRead<R> {
    #[inline]
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<R: Copy> Copy for AssumeUninitRead<R> {}

impl<R: PartialEq> PartialEq for AssumeUninitRead<R> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl<R: Eq> Eq for AssumeUninitRead<R> {}

impl<R: PartialOrd> PartialOrd for AssumeUninitRead<R> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl<R: Ord> Ord for AssumeUninitRead<R> {
    #[inline]
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<R: std::hash::Hash> std::hash::Hash for AssumeUninitRead<R> {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

// SAFETY: The caller of `AssumeUninitRead::assume_uninit_read` guarantees that the wrapped
// reader `R` upholds the `UninitRead` contract.
unsafe impl<R> UninitRead for AssumeUninitRead<R> {}