diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4209e7..d4e8d30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,6 @@ jobs: matrix: rust-toolchain: - nightly - - nightly-2025-05-20 - nightly-2025-12-12 - stable env: diff --git a/Cargo.toml b/Cargo.toml index d835d00..51c3ab3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "axio" version = "0.2.1" -edition = "2021" +edition = "2024" authors = ["Yuekai Jia "] -description = "`std::io`-like I/O traits for `no_std` environment" +description = "`std::io` for `no_std` environment" license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" homepage = "https://github.com/arceos-org/arceos" repository = "https://github.com/arceos-org/axio" @@ -16,7 +16,9 @@ alloc = [] default = [] [dependencies] -axerrno = "0.1" +axerrno = "0.2" +heapless = "0.9" +memchr = { version = "2", default-features = false } [build-dependencies] autocfg = "1" diff --git a/README.md b/README.md index 0ee809b..060d97d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,40 @@ [![Docs.rs](https://docs.rs/axio/badge.svg)](https://docs.rs/axio) [![CI](https://github.com/arceos-org/axio/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/arceos-org/axio/actions/workflows/ci.yml) -[`std::io`][1]-like I/O traits for `no_std` environment. +[`std::io`][1] for `no_std` environment. [1]: https://doc.rust-lang.org/std/io/index.html + +### Features + +- **alloc**: + - Enables extra methods on `Read`: `read_to_end`, `read_to_string`. + - Enables extra methods on `BufRead`: `read_until`, `read_line`, `split`, `lines`. + - Enables implementations of axio traits for `alloc` types like `Vec`, `Box`, etc. + - Enables `BufWriter::with_capacity`. (If `alloc` is disabled, only `BufWriter::new` is available.) + - Removes the capacity limit on `BufReader`. (If `alloc` is disabled, `BufReader::with_capacity` will panic if the capacity is larger than a fixed limit.) + +### Differences to `std::io` + +- Error types from `axerrno` instead of `std::io::Error`. +- No `IoSlice` and `*_vectored` APIs. + +### Limitations + +- Requires nightly Rust. + +## License + +Licensed under either of + +- GNU General Public License v3.0 or later, () +- Apache License, Version 2.0, () +- Mulan Permissive Software License, Version 2, () + +at your option. + +--- + +Almost all of the code in this repository is a copy of the [Rust language codebase](https://github.com/rust-lang/rust) with minor modifications. + +For attributions, see . diff --git a/build.rs b/build.rs index 7be3362..1f5277d 100644 --- a/build.rs +++ b/build.rs @@ -1,8 +1,9 @@ fn main() { - autocfg::emit_possibility("borrowedbuf_init"); autocfg::rerun_path("build.rs"); let ac = autocfg::new(); + + autocfg::emit_possibility("borrowedbuf_init"); let code = r#" #![no_std] #![feature(core_io_borrowed_buf)] @@ -13,4 +14,15 @@ fn main() { if ac.probe_raw(code).is_ok() { autocfg::emit("borrowedbuf_init"); } + + autocfg::emit_possibility("maybe_uninit_slice"); + let code = r#" + #![no_std] + pub fn probe() { + let _ = <[core::mem::MaybeUninit<()>]>::assume_init_mut; + } + "#; + if ac.probe_raw(code).is_ok() { + autocfg::emit("maybe_uninit_slice"); + } } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..9c4bd7d --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,18 @@ +unstable_features = true + +style_edition = "2024" + +group_imports = "StdExternalCrate" +imports_granularity = "Crate" + +normalize_comments = true +wrap_comments = true +comment_width = 100 + +condense_wildcard_suffixes = true +enum_discrim_align_threshold = 20 +use_field_init_shorthand = true + +format_strings = true +format_code_in_doc_comments = true +format_macro_matchers = true diff --git a/src/buffered/bufreader.rs b/src/buffered/bufreader.rs deleted file mode 100644 index b466c1e..0000000 --- a/src/buffered/bufreader.rs +++ /dev/null @@ -1,158 +0,0 @@ -use crate::{BufRead, Read, Result}; - -#[cfg(feature = "alloc")] -use alloc::{string::String, vec::Vec}; - -const DEFAULT_BUF_SIZE: usize = 1024; - -/// The `BufReader` struct adds buffering to any reader. -pub struct BufReader { - inner: R, - pos: usize, - filled: usize, - buf: [u8; DEFAULT_BUF_SIZE], -} - -impl BufReader { - /// Creates a new `BufReader` with a default buffer capacity (1 KB). - pub const fn new(inner: R) -> BufReader { - Self { - inner, - pos: 0, - filled: 0, - buf: [0; DEFAULT_BUF_SIZE], - } - } -} - -impl BufReader { - /// Gets a reference to the underlying reader. - pub const fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Returns a reference to the internally buffered data. - /// - /// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty. - /// - /// [`fill_buf`]: BufRead::fill_buf - pub fn buffer(&self) -> &[u8] { - &self.buf[self.pos..self.filled] - } - - /// Returns the number of bytes the internal buffer can hold at once. - pub const fn capacity(&self) -> usize { - DEFAULT_BUF_SIZE - } - - /// Unwraps this `BufReader`, returning the underlying reader. - pub fn into_inner(self) -> R { - self.inner - } - - fn discard_buffer(&mut self) { - self.pos = 0; - self.filled = 0; - } - - const fn is_empty(&self) -> bool { - self.pos >= self.filled - } -} - -impl Read for BufReader { - fn read(&mut self, buf: &mut [u8]) -> Result { - // If we don't have any buffered data and we're doing a massive read - // (larger than our internal buffer), bypass our internal buffer - // entirely. - if self.is_empty() && buf.len() >= self.capacity() { - self.discard_buffer(); - return self.inner.read(buf); - } - let nread = { - let mut rem = self.fill_buf()?; - rem.read(buf)? - }; - self.consume(nread); - Ok(nread) - } - - // Small read_exacts from a BufReader are extremely common when used with a deserializer. - // The default implementation calls read in a loop, which results in surprisingly poor code - // generation for the common path where the buffer has enough bytes to fill the passed-in - // buffer. - fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { - let amt = buf.len(); - if let Some(claimed) = self.buffer().get(..amt) { - buf.copy_from_slice(claimed); - self.pos += amt; - return Ok(()); - } - self.inner.read_exact(buf) - } - - // The inner reader might have an optimized `read_to_end`. Drain our buffer and then - // delegate to the inner implementation. - #[cfg(feature = "alloc")] - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - let inner_buf = self.buffer(); - buf.extend_from_slice(inner_buf); - let nread = inner_buf.len(); - self.discard_buffer(); - Ok(nread + self.inner.read_to_end(buf)?) - } - - // The inner reader might have an optimized `read_to_end`. Drain our buffer and then - // delegate to the inner implementation. - #[cfg(feature = "alloc")] - fn read_to_string(&mut self, buf: &mut String) -> Result { - // In the general `else` case below we must read bytes into a side buffer, check - // that they are valid UTF-8, and then append them to `buf`. This requires a - // potentially large memcpy. - // - // If `buf` is empty--the most common case--we can leverage `append_to_string` - // to read directly into `buf`'s internal byte buffer, saving an allocation and - // a memcpy. - if buf.is_empty() { - // `append_to_string`'s safety relies on the buffer only being appended to since - // it only checks the UTF-8 validity of new data. If there were existing content in - // `buf` then an untrustworthy reader (i.e. `self.inner`) could not only append - // bytes but also modify existing bytes and render them invalid. On the other hand, - // if `buf` is empty then by definition any writes must be appends and - // `append_to_string` will validate all of the new bytes. - unsafe { crate::append_to_string(buf, |b| self.read_to_end(b)) } - } else { - // We cannot append our byte buffer directly onto the `buf` String as there could - // be an incomplete UTF-8 sequence that has only been partially read. We must read - // everything into a side buffer first and then call `from_utf8` on the complete - // buffer. - let mut bytes = Vec::new(); - self.read_to_end(&mut bytes)?; - let string = core::str::from_utf8(&bytes).map_err(|_| { - axerrno::ax_err_type!(InvalidData, "stream did not contain valid UTF-8") - })?; - *buf += string; - Ok(string.len()) - } - } -} - -impl BufRead for BufReader { - fn fill_buf(&mut self) -> Result<&[u8]> { - if self.is_empty() { - let read_len = self.inner.read(&mut self.buf)?; - self.pos = 0; - self.filled = read_len; - } - Ok(self.buffer()) - } - - fn consume(&mut self, amt: usize) { - self.pos = core::cmp::min(self.pos + amt, self.filled); - } -} diff --git a/src/buffered/bufreader/buffer.rs b/src/buffered/bufreader/buffer.rs new file mode 100644 index 0000000..80b94c7 --- /dev/null +++ b/src/buffered/bufreader/buffer.rs @@ -0,0 +1,177 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +use core::{cmp, io::BorrowedBuf, mem::MaybeUninit}; + +#[cfg(not(feature = "alloc"))] +use heapless::Vec; + +#[cfg(not(feature = "alloc"))] +use crate::DEFAULT_BUF_SIZE; +use crate::{Read, Result}; + +pub struct Buffer { + // The buffer. + #[cfg(feature = "alloc")] + buf: Box<[MaybeUninit]>, + // The buffer. + #[cfg(not(feature = "alloc"))] + buf: Vec, DEFAULT_BUF_SIZE, u16>, + + // The current seek offset into `buf`, must always be <= `filled`. + pos: usize, + // Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are + // initialized with bytes from a read. + filled: usize, + #[cfg(borrowedbuf_init)] + // This is the max number of bytes returned across all `fill_buf` calls. We track this so that + // we can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much + // of its defensive initialization as possible. Note that while this often the same as + // `filled`, it doesn't need to be. Calls to `fill_buf` are not required to actually fill the + // buffer, and omitting this is a huge perf regression for `Read` impls that do not. + initialized: usize, +} + +impl Buffer { + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + #[cfg(feature = "alloc")] + let buf = Box::new_uninit_slice(capacity); + #[cfg(not(feature = "alloc"))] + let buf = { + let mut buf = Vec::new(); + assert!(capacity <= buf.capacity()); + unsafe { buf.set_len(capacity) }; + buf + }; + Self { + buf, + pos: 0, + filled: 0, + #[cfg(borrowedbuf_init)] + initialized: 0, + } + } + + #[inline] + pub fn buffer(&self) -> &[u8] { + // SAFETY: self.pos and self.filled are valid, and self.filled => self.pos, and + // that region is initialized because those are all invariants of this type. + unsafe { + self.buf + .get_unchecked(self.pos..self.filled) + .assume_init_ref() + } + } + + #[inline] + pub fn capacity(&self) -> usize { + self.buf.len() + } + + #[inline] + pub fn filled(&self) -> usize { + self.filled + } + + #[inline] + pub fn pos(&self) -> usize { + self.pos + } + + #[cfg(borrowedbuf_init)] + #[inline] + pub fn initialized(&self) -> usize { + self.initialized + } + + #[inline] + pub fn discard_buffer(&mut self) { + self.pos = 0; + self.filled = 0; + } + + #[inline] + pub fn consume(&mut self, amt: usize) { + self.pos = cmp::min(self.pos + amt, self.filled); + } + + /// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to + /// `visitor` and return true. If there are not enough bytes available, return false. + #[inline] + pub fn consume_with(&mut self, amt: usize, mut visitor: V) -> bool + where + V: FnMut(&[u8]), + { + if let Some(claimed) = self.buffer().get(..amt) { + visitor(claimed); + // If the indexing into self.buffer() succeeds, amt must be a valid increment. + self.pos += amt; + true + } else { + false + } + } + + #[inline] + pub fn unconsume(&mut self, amt: usize) { + self.pos = self.pos.saturating_sub(amt); + } + + /// Read more bytes into the buffer without discarding any of its contents + pub fn read_more(&mut self, mut reader: impl Read) -> Result { + let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]); + #[cfg(borrowedbuf_init)] + let old_init = self.initialized - self.filled; + #[cfg(borrowedbuf_init)] + unsafe { + buf.set_init(old_init); + } + reader.read_buf(buf.unfilled())?; + self.filled += buf.len(); + #[cfg(borrowedbuf_init)] + { + self.initialized += buf.init_len() - old_init; + } + Ok(buf.len()) + } + + /// Remove bytes that have already been read from the buffer. + pub fn backshift(&mut self) { + self.buf.copy_within(self.pos.., 0); + self.filled -= self.pos; + self.pos = 0; + } + + #[inline] + pub fn fill_buf(&mut self, mut reader: impl Read) -> Result<&[u8]> { + // If we've reached the end of our internal buffer then we need to fetch + // some more data from the reader. + // Branch using `>=` instead of the more correct `==` + // to tell the compiler that the pos..cap slice is always valid. + if self.pos >= self.filled { + debug_assert!(self.pos == self.filled); + + #[cfg(feature = "alloc")] + let mut buf = BorrowedBuf::from(&mut *self.buf); + #[cfg(not(feature = "alloc"))] + let mut buf = BorrowedBuf::from(self.buf.as_mut_slice()); + #[cfg(borrowedbuf_init)] + // SAFETY: `self.filled` bytes will always have been initialized. + unsafe { + buf.set_init(self.initialized); + } + + let result = reader.read_buf(buf.unfilled()); + + self.pos = 0; + self.filled = buf.len(); + #[cfg(borrowedbuf_init)] + { + self.initialized = buf.init_len(); + } + + result?; + } + Ok(self.buffer()) + } +} diff --git a/src/buffered/bufreader/mod.rs b/src/buffered/bufreader/mod.rs new file mode 100644 index 0000000..2a26d61 --- /dev/null +++ b/src/buffered/bufreader/mod.rs @@ -0,0 +1,340 @@ +mod buffer; + +#[cfg(feature = "alloc")] +use alloc::{string::String, vec::Vec}; +use core::{fmt, io::BorrowedCursor}; + +use self::buffer::Buffer; +#[cfg(feature = "alloc")] +use crate::Error; +use crate::{BufRead, DEFAULT_BUF_SIZE, Read, Result, Seek, SeekFrom}; + +/// The `BufReader` struct adds buffering to any reader. +/// +/// See [`std::io::BufReader`] for more details. +pub struct BufReader { + buf: Buffer, + inner: R, +} + +impl BufReader { + /// Creates a new `BufReader` with the specified buffer capacity. + pub fn with_capacity(capacity: usize, inner: R) -> BufReader { + BufReader { + buf: Buffer::with_capacity(capacity), + inner, + } + } + + /// Creates a new `BufReader` with a default buffer capacity. + pub fn new(inner: R) -> BufReader { + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) + } +} + +impl BufReader { + /// Gets a reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + pub fn get_ref(&self) -> &R { + &self.inner + } + + /// Gets a mutable reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + pub fn get_mut(&mut self) -> &mut R { + &mut self.inner + } + + /// Returns a reference to the internally buffered data. + /// + /// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty. + /// + /// [`fill_buf`]: BufRead::fill_buf + pub fn buffer(&self) -> &[u8] { + self.buf.buffer() + } + + /// Returns the number of bytes the internal buffer can hold at once. + pub fn capacity(&self) -> usize { + self.buf.capacity() + } + + #[cfg(borrowedbuf_init)] + #[doc(hidden)] + pub fn initialized(&self) -> usize { + self.buf.initialized() + } + + /// Unwraps this `BufReader`, returning the underlying reader. + /// + /// Note that any leftover data in the internal buffer is lost. Therefore, + /// a following read from the underlying reader may lead to data loss. + pub fn into_inner(self) -> R + where + R: Sized, + { + self.inner + } + + /// Invalidates all data in the internal buffer. + #[inline] + pub(crate) fn discard_buffer(&mut self) { + self.buf.discard_buffer() + } +} + +impl BufReader { + /// Attempt to look ahead `n` bytes. + /// + /// `n` must be less than or equal to `capacity`. + /// + /// The returned slice may be less than `n` bytes long if + /// end of file is reached. + /// + /// After calling this method, you may call [`consume`](BufRead::consume) + /// with a value less than or equal to `n` to advance over some or all of + /// the returned bytes. + pub fn peek(&mut self, n: usize) -> Result<&[u8]> { + assert!(n <= self.capacity()); + while n > self.buf.buffer().len() { + if self.buf.pos() > 0 { + self.buf.backshift(); + } + let new = self.buf.read_more(&mut self.inner)?; + if new == 0 { + // end of file, no more bytes to read + return Ok(self.buf.buffer()); + } + debug_assert_eq!(self.buf.pos(), 0); + } + Ok(&self.buf.buffer()[..n]) + } +} + +impl fmt::Debug for BufReader +where + R: ?Sized + fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BufReader") + .field("reader", &&self.inner) + .field( + "buffer", + &format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()), + ) + .finish() + } +} + +impl Read for BufReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + // If we don't have any buffered data and we're doing a massive read + // (larger than our internal buffer), bypass our internal buffer + // entirely. + if self.buf.pos() == self.buf.filled() && buf.len() >= self.capacity() { + self.discard_buffer(); + return self.inner.read(buf); + } + let mut rem = self.fill_buf()?; + let nread = rem.read(buf)?; + self.consume(nread); + Ok(nread) + } + + fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { + // If we don't have any buffered data and we're doing a massive read + // (larger than our internal buffer), bypass our internal buffer + // entirely. + if self.buf.pos() == self.buf.filled() && cursor.capacity() >= self.capacity() { + self.discard_buffer(); + return self.inner.read_buf(cursor); + } + + let prev = cursor.written(); + + let mut rem = self.fill_buf()?; + rem.read_buf(cursor.reborrow())?; // actually never fails + + self.consume(cursor.written() - prev); // slice impl of read_buf known to never unfill buf + + Ok(()) + } + + // Small read_exacts from a BufReader are extremely common when used with a deserializer. + // The default implementation calls read in a loop, which results in surprisingly poor code + // generation for the common path where the buffer has enough bytes to fill the passed-in + // buffer. + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + if self + .buf + .consume_with(buf.len(), |claimed| buf.copy_from_slice(claimed)) + { + return Ok(()); + } + + crate::default_read_exact(self, buf) + } + + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { + if self + .buf + .consume_with(cursor.capacity(), |claimed| cursor.append(claimed)) + { + return Ok(()); + } + + crate::default_read_buf_exact(self, cursor) + } + + // The inner reader might have an optimized `read_to_end`. Drain our buffer and then + // delegate to the inner implementation. + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let inner_buf = self.buffer(); + buf.try_reserve(inner_buf.len()) + .map_err(|_| Error::NoMemory)?; + buf.extend_from_slice(inner_buf); + let nread = inner_buf.len(); + self.discard_buffer(); + Ok(nread + self.inner.read_to_end(buf)?) + } + + // The inner reader might have an optimized `read_to_end`. Drain our buffer and then + // delegate to the inner implementation. + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + // In the general `else` case below we must read bytes into a side buffer, check + // that they are valid UTF-8, and then append them to `buf`. This requires a + // potentially large memcpy. + // + // If `buf` is empty--the most common case--we can leverage `append_to_string` + // to read directly into `buf`'s internal byte buffer, saving an allocation and + // a memcpy. + + if buf.is_empty() { + // `append_to_string`'s safety relies on the buffer only being appended to since + // it only checks the UTF-8 validity of new data. If there were existing content in + // `buf` then an untrustworthy reader (i.e. `self.inner`) could not only append + // bytes but also modify existing bytes and render them invalid. On the other hand, + // if `buf` is empty then by definition any writes must be appends and + // `append_to_string` will validate all of the new bytes. + unsafe { crate::append_to_string(buf, |b| self.read_to_end(b)) } + } else { + // We cannot append our byte buffer directly onto the `buf` String as there could + // be an incomplete UTF-8 sequence that has only been partially read. We must read + // everything into a side buffer first and then call `from_utf8` on the complete + // buffer. + let mut bytes = Vec::new(); + self.read_to_end(&mut bytes)?; + let string = str::from_utf8(&bytes).map_err(|_| Error::IllegalBytes)?; + *buf += string; + Ok(string.len()) + } + } +} + +impl BufRead for BufReader { + fn fill_buf(&mut self) -> Result<&[u8]> { + self.buf.fill_buf(&mut self.inner) + } + + fn consume(&mut self, amt: usize) { + self.buf.consume(amt) + } +} + +impl Seek for BufReader { + /// Seek to an offset, in bytes, in the underlying reader. + /// + /// The position used for seeking with [SeekFrom::Current]\(_) is the + /// position the underlying reader would be at if the `BufReader` had no + /// internal buffer. + /// + /// Seeking always discards the internal buffer, even if the seek position + /// would otherwise fall within it. This guarantees that calling + /// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader + /// at the same position. + /// + /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`]. + /// + /// See [`Seek`] for more details. + /// + /// Note: In the edge case where you're seeking with [SeekFrom::Current]\(n) + /// where `n` minus the internal buffer length overflows an `i64`, two + /// seeks will be performed instead of one. If the second seek returns + /// [`Err`], the underlying reader will be left at the same position it would + /// have if you called `seek` with [SeekFrom::Current]\(0). + fn seek(&mut self, pos: SeekFrom) -> Result { + let result: u64; + if let SeekFrom::Current(n) = pos { + let remainder = (self.buf.filled() - self.buf.pos()) as i64; + // it should be safe to assume that remainder fits within an i64 as the alternative + // means we managed to allocate 8 exbibytes and that's absurd. + // But it's not out of the realm of possibility for some weird underlying reader to + // support seeking by i64::MIN so we need to handle underflow when subtracting + // remainder. + if let Some(offset) = n.checked_sub(remainder) { + result = self.inner.seek(SeekFrom::Current(offset))?; + } else { + // seek backwards by our remainder, and then by the offset + self.inner.seek(SeekFrom::Current(-remainder))?; + self.discard_buffer(); + result = self.inner.seek(SeekFrom::Current(n))?; + } + } else { + // Seeking with Start/End doesn't care about our buffer length. + result = self.inner.seek(pos)?; + } + self.discard_buffer(); + Ok(result) + } + + /// Returns the current seek position from the start of the stream. + /// + /// The value returned is equivalent to `self.seek(SeekFrom::Current(0))` + /// but does not flush the internal buffer. Due to this optimization the + /// function does not guarantee that calling `.into_inner()` immediately + /// afterwards will yield the underlying reader at the same position. Use + /// [`BufReader::seek`] instead if you require that guarantee. + /// + /// # Panics + /// + /// This function will panic if the position of the inner reader is smaller + /// than the amount of buffered data. That can happen if the inner reader + /// has an incorrect implementation of [`Seek::stream_position`], or if the + /// position has gone out of sync due to calling [`Seek::seek`] directly on + /// the underlying reader. + fn stream_position(&mut self) -> Result { + let remainder = (self.buf.filled() - self.buf.pos()) as u64; + self.inner.stream_position().map(|pos| { + pos.checked_sub(remainder).expect( + "overflow when subtracting remaining buffer size from inner stream position", + ) + }) + } + + /// Seeks relative to the current position. + /// + /// If the new position lies within the buffer, the buffer will not be + /// flushed, allowing for more efficient seeks. This method does not return + /// the location of the underlying reader, so the caller must track this + /// information themselves if it is required. + fn seek_relative(&mut self, offset: i64) -> Result<()> { + let pos = self.buf.pos() as u64; + if offset < 0 { + if pos.checked_sub((-offset) as u64).is_some() { + self.buf.unconsume((-offset) as usize); + return Ok(()); + } + } else if let Some(new_pos) = pos.checked_add(offset as u64) + && new_pos <= self.buf.filled() as u64 + { + self.buf.consume(offset as usize); + return Ok(()); + } + + self.seek(SeekFrom::Current(offset)).map(drop) + } +} diff --git a/src/buffered/bufwriter/mod.rs b/src/buffered/bufwriter/mod.rs new file mode 100644 index 0000000..bae2475 --- /dev/null +++ b/src/buffered/bufwriter/mod.rs @@ -0,0 +1,390 @@ +use core::{fmt, mem::ManuallyDrop, ptr}; + +use crate::{DEFAULT_BUF_SIZE, Error, IntoInnerError, Result, Seek, SeekFrom, Write}; + +#[cfg(feature = "alloc")] +type Buffer = alloc::vec::Vec; +#[cfg(not(feature = "alloc"))] +type Buffer = heapless::Vec; + +/// Wraps a writer and buffers its output. +/// +/// See [std::io::BufWriter] for more details. +pub struct BufWriter { + // The buffer. + buf: Buffer, + // #30888: If the inner writer panics in a call to write, we don't want to + // write the buffered data a second time in BufWriter's destructor. This + // flag tells the Drop impl if it should skip the flush. + panicked: bool, + inner: W, +} + +impl BufWriter { + /// Creates a new `BufWriter` with a default buffer capacity. + pub fn new(inner: W) -> BufWriter { + #[cfg(feature = "alloc")] + let buf = Buffer::with_capacity(DEFAULT_BUF_SIZE); + #[cfg(not(feature = "alloc"))] + let buf = Buffer::new(); + BufWriter { + buf, + panicked: false, + inner, + } + } + + /// Creates a new `BufWriter` with at least the specified buffer capacity. + #[cfg(feature = "alloc")] + pub fn with_capacity(capacity: usize, inner: W) -> BufWriter { + BufWriter { + buf: Buffer::with_capacity(capacity), + panicked: false, + inner, + } + } + + /// Unwraps this `BufWriter`, returning the underlying writer. + /// + /// The buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + #[cfg_attr(not(feature = "alloc"), allow(clippy::result_large_err))] + pub fn into_inner(mut self) -> core::result::Result>> { + match self.flush_buf() { + Err(e) => Err(IntoInnerError::new(self, e)), + Ok(()) => Ok(self.into_parts().0), + } + } + + /// Disassembles this `BufWriter`, returning the underlying writer, and any buffered but + /// unwritten data. + /// + /// If the underlying writer panicked, it is not known what portion of the data was written. + /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer + /// contents can still be recovered). + /// + /// `into_parts` makes no attempt to flush data and cannot fail. + pub fn into_parts(self) -> (W, core::result::Result) { + let mut this = ManuallyDrop::new(self); + let buf = core::mem::take(&mut this.buf); + let buf = if !this.panicked { + Ok(buf) + } else { + Err(WriterPanicked { buf }) + }; + + // SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never + // dropped + let inner = unsafe { ptr::read(&this.inner) }; + + (inner, buf) + } +} + +/// Error returned for the buffered data from `BufWriter::into_parts`, when the underlying +/// writer has previously panicked. Contains the (possibly partly written) buffered data. +pub struct WriterPanicked { + buf: Buffer, +} + +impl WriterPanicked { + /// Returns the perhaps-unwritten data. Some of this data may have been written by the + /// panicking call(s) to the underlying writer, so simply writing it again is not a good idea. + #[must_use = "`self` will be dropped if the result is not used"] + pub fn into_inner(self) -> Buffer { + self.buf + } +} + +impl fmt::Display for WriterPanicked { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "BufWriter inner writer panicked, what data remains unwritten is not known".fmt(f) + } +} + +impl fmt::Debug for WriterPanicked { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WriterPanicked") + .field( + "buffer", + &format_args!("{}/{}", self.buf.len(), self.buf.capacity()), + ) + .finish() + } +} + +impl BufWriter { + /// Gets a reference to the underlying writer. + pub fn get_ref(&self) -> &W { + &self.inner + } + + /// Gets a mutable reference to the underlying writer. + /// + /// It is inadvisable to directly write to the underlying writer. + pub fn get_mut(&mut self) -> &mut W { + &mut self.inner + } + + /// Returns a reference to the internally buffered data. + pub fn buffer(&self) -> &[u8] { + self.buf.as_slice() + } + + /// Returns the number of bytes the internal buffer can hold without flushing. + pub fn capacity(&self) -> usize { + self.buf.capacity() + } + + pub(crate) fn buffer_mut(&mut self) -> &mut Buffer { + &mut self.buf + } + + /// Send data in our local buffer into the inner writer, looping as + /// necessary until either it's all been sent or an error occurs. + /// + /// Because all the data in the buffer has been reported to our owner as + /// "successfully written" (by returning nonzero success values from + /// `write`), any 0-length writes from `inner` must be reported as i/o + /// errors from this method. + pub(crate) fn flush_buf(&mut self) -> Result<()> { + /// Helper struct to ensure the buffer is updated after all the writes + /// are complete. It tracks the number of written bytes and drains them + /// all from the front of the buffer when dropped. + struct BufGuard<'a> { + buffer: &'a mut Buffer, + written: usize, + } + + impl<'a> BufGuard<'a> { + fn new(buffer: &'a mut Buffer) -> Self { + Self { buffer, written: 0 } + } + + /// The unwritten part of the buffer + fn remaining(&self) -> &[u8] { + &self.buffer.as_slice()[self.written..] + } + + /// Flag some bytes as removed from the front of the buffer + fn consume(&mut self, amt: usize) { + self.written += amt; + } + + /// true if all of the bytes have been written + fn done(&self) -> bool { + self.written >= self.buffer.len() + } + } + + impl Drop for BufGuard<'_> { + fn drop(&mut self) { + if self.written > 0 { + self.buffer.drain(..self.written); + } + } + } + + let mut guard = BufGuard::new(&mut self.buf); + while !guard.done() { + self.panicked = true; + let r = self.inner.write(guard.remaining()); + self.panicked = false; + + match r { + Ok(0) => { + return Err(Error::WriteZero); + } + Ok(n) => guard.consume(n), + Err(ref e) if e.canonicalize() == Error::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn spare_capacity(&self) -> usize { + self.buf.capacity() - self.buf.len() + } + + // SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`, + // i.e., that input buffer length is less than or equal to spare capacity. + #[inline] + unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) { + debug_assert!(buf.len() <= self.spare_capacity()); + let old_len = self.buf.len(); + let buf_len = buf.len(); + let src = buf.as_ptr(); + unsafe { + let dst = self.buf.as_mut_ptr().add(old_len); + core::ptr::copy_nonoverlapping(src, dst, buf_len); + self.buf.set_len(old_len + buf_len); + } + } + + /// Buffer some data without flushing it, regardless of the size of the + /// data. Writes as much as possible without exceeding capacity. Returns + /// the number of bytes written. + pub(crate) fn write_to_buf(&mut self, buf: &[u8]) -> usize { + let available = self.spare_capacity(); + let amt_to_buffer = available.min(buf.len()); + + // SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction. + unsafe { + self.write_to_buffer_unchecked(&buf[..amt_to_buffer]); + } + + amt_to_buffer + } + + // Ensure this function does not get inlined into `write`, so that it + // remains inlineable and its common path remains as short as possible. + // If this function ends up being called frequently relative to `write`, + // it's likely a sign that the client is using an improperly sized buffer + // or their write patterns are somewhat pathological. + #[cold] + #[inline(never)] + fn write_cold(&mut self, buf: &[u8]) -> Result { + if buf.len() > self.spare_capacity() { + self.flush_buf()?; + } + + // Why not len > capacity? To avoid a needless trip through the buffer when the input + // exactly fills it. We'd just need to flush it to the underlying writer anyway. + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write(buf); + self.panicked = false; + r + } else { + // Write to the buffer. In this case, we write to the buffer even if it fills it + // exactly. Doing otherwise would mean flushing the buffer, then writing this + // input to the inner writer, which in many cases would be a worse strategy. + + // SAFETY: There was either enough spare capacity already, or there wasn't and we + // flushed the buffer to ensure that there is. In the latter case, we know that there + // is because flushing ensured that our entire buffer is spare capacity, and we entered + // this block because the input buffer length is less than that capacity. In either + // case, it's safe to write the input buffer to our buffer. + unsafe { + self.write_to_buffer_unchecked(buf); + } + + Ok(buf.len()) + } + } + + // Ensure this function does not get inlined into `write_all`, so that it + // remains inlineable and its common path remains as short as possible. + // If this function ends up being called frequently relative to `write_all`, + // it's likely a sign that the client is using an improperly sized buffer + // or their write patterns are somewhat pathological. + #[cold] + #[inline(never)] + fn write_all_cold(&mut self, buf: &[u8]) -> Result<()> { + // Normally, `write_all` just calls `write` in a loop. We can do better + // by calling `self.get_mut().write_all()` directly, which avoids + // round trips through the buffer in the event of a series of partial + // writes in some circumstances. + + if buf.len() > self.spare_capacity() { + self.flush_buf()?; + } + + // Why not len > capacity? To avoid a needless trip through the buffer when the input + // exactly fills it. We'd just need to flush it to the underlying writer anyway. + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write_all(buf); + self.panicked = false; + r + } else { + // Write to the buffer. In this case, we write to the buffer even if it fills it + // exactly. Doing otherwise would mean flushing the buffer, then writing this + // input to the inner writer, which in many cases would be a worse strategy. + + // SAFETY: There was either enough spare capacity already, or there wasn't and we + // flushed the buffer to ensure that there is. In the latter case, we know that there + // is because flushing ensured that our entire buffer is spare capacity, and we entered + // this block because the input buffer length is less than that capacity. In either + // case, it's safe to write the input buffer to our buffer. + unsafe { + self.write_to_buffer_unchecked(buf); + } + + Ok(()) + } + } +} + +impl fmt::Debug for BufWriter { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BufWriter") + .field("writer", &&self.inner) + .field( + "buffer", + &format_args!("{}/{}", self.buf.len(), self.buf.capacity()), + ) + .finish() + } +} + +impl Write for BufWriter { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + // Use < instead of <= to avoid a needless trip through the buffer in some cases. + // See `write_cold` for details. + if buf.len() < self.spare_capacity() { + // SAFETY: safe by above conditional. + unsafe { + self.write_to_buffer_unchecked(buf); + } + + Ok(buf.len()) + } else { + self.write_cold(buf) + } + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + // Use < instead of <= to avoid a needless trip through the buffer in some cases. + // See `write_all_cold` for details. + if buf.len() < self.spare_capacity() { + // SAFETY: safe by above conditional. + unsafe { + self.write_to_buffer_unchecked(buf); + } + + Ok(()) + } else { + self.write_all_cold(buf) + } + } + + fn flush(&mut self) -> Result<()> { + self.flush_buf().and_then(|()| self.get_mut().flush()) + } +} + +impl Seek for BufWriter { + /// Seek to the offset, in bytes, in the underlying writer. + /// + /// Seeking always writes out the internal buffer before seeking. + fn seek(&mut self, pos: SeekFrom) -> Result { + self.flush_buf()?; + self.get_mut().seek(pos) + } +} + +impl Drop for BufWriter { + fn drop(&mut self) { + if !self.panicked { + // dtors should not panic, so we ignore a failed flush + let _r = self.flush_buf(); + } + } +} diff --git a/src/buffered/linewriter/mod.rs b/src/buffered/linewriter/mod.rs new file mode 100644 index 0000000..032ae0c --- /dev/null +++ b/src/buffered/linewriter/mod.rs @@ -0,0 +1,103 @@ +mod shim; + +use core::fmt; + +use self::shim::LineWriterShim; +use crate::{BufWriter, IntoInnerError, Result, Write}; + +/// Wraps a writer and buffers output to it, flushing whenever a newline +/// (`0x0a`, `'\n'`) is detected. +/// +/// The [`BufWriter`] struct wraps a writer and buffers its output. +/// But it only does this batched write when it goes out of scope, or when the +/// internal buffer is full. Sometimes, you'd prefer to write each line as it's +/// completed, rather than the entire buffer at once. Enter `LineWriter`. It +/// does exactly that. +/// +/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the +/// `LineWriter` goes out of scope or when its internal buffer is full. +/// +/// If there's still a partial line in the buffer when the `LineWriter` is +/// dropped, it will flush those contents. +/// +/// See [`std::io::LineWriter`] for more details. +pub struct LineWriter { + inner: BufWriter, +} + +impl LineWriter { + /// Creates a new `LineWriter`. + pub fn new(inner: W) -> LineWriter { + LineWriter { + inner: BufWriter::new(inner), + } + } + + /// Creates a new `LineWriter` with at least the specified capacity for the + /// internal buffer. + #[cfg(feature = "alloc")] + pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { + LineWriter { + inner: BufWriter::with_capacity(capacity, inner), + } + } + + /// Unwraps this `LineWriter`, returning the underlying writer. + /// + /// The internal buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + #[cfg_attr(not(feature = "alloc"), allow(clippy::result_large_err))] + pub fn into_inner(self) -> core::result::Result>> { + self.inner + .into_inner() + .map_err(|err| err.new_wrapped(|inner| LineWriter { inner })) + } +} + +impl LineWriter { + /// Gets a reference to the underlying writer. + pub fn get_ref(&self) -> &W { + self.inner.get_ref() + } + + /// Gets a mutable reference to the underlying writer. + /// + /// Caution must be taken when calling methods on the mutable reference + /// returned as extra writes could corrupt the output stream. + pub fn get_mut(&mut self) -> &mut W { + self.inner.get_mut() + } +} + +impl Write for LineWriter { + fn write(&mut self, buf: &[u8]) -> Result { + LineWriterShim::new(&mut self.inner).write(buf) + } + + fn flush(&mut self) -> Result<()> { + self.inner.flush() + } + + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + LineWriterShim::new(&mut self.inner).write_all(buf) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { + LineWriterShim::new(&mut self.inner).write_fmt(fmt) + } +} + +impl fmt::Debug for LineWriter { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("LineWriter") + .field("writer", &self.get_ref()) + .field( + "buffer", + &format_args!("{}/{}", self.inner.buffer().len(), self.inner.capacity()), + ) + .finish_non_exhaustive() + } +} diff --git a/src/buffered/linewriter/shim.rs b/src/buffered/linewriter/shim.rs new file mode 100644 index 0000000..dcb3b8a --- /dev/null +++ b/src/buffered/linewriter/shim.rs @@ -0,0 +1,178 @@ +use crate::{BufWriter, Result, Write}; + +/// Private helper struct for implementing the line-buffered writing logic. +/// +/// This shim temporarily wraps a BufWriter, and uses its internals to +/// implement a line-buffered writer (specifically by using the internal +/// methods like write_to_buf and flush_buf). In this way, a more +/// efficient abstraction can be created than one that only had access to +/// `write` and `flush`, without needlessly duplicating a lot of the +/// implementation details of BufWriter. This also allows existing +/// `BufWriters` to be temporarily given line-buffering logic; this is what +/// enables Stdout to be alternately in line-buffered or block-buffered mode. +#[derive(Debug)] +pub struct LineWriterShim<'a, W: ?Sized + Write> { + buffer: &'a mut BufWriter, +} + +impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { + pub fn new(buffer: &'a mut BufWriter) -> Self { + Self { buffer } + } + + /// Gets a mutable reference to the inner writer (that is, the writer + /// wrapped by the BufWriter). Be careful with this writer, as writes to + /// it will bypass the buffer. + fn inner_mut(&mut self) -> &mut W { + self.buffer.get_mut() + } + + /// Gets the content currently buffered in self.buffer + fn buffered(&self) -> &[u8] { + self.buffer.buffer() + } + + /// Flushes the buffer iff the last byte is a newline (indicating that an + /// earlier write only succeeded partially, and we want to retry flushing + /// the buffered line before continuing with a subsequent write). + fn flush_if_completed_line(&mut self) -> Result<()> { + match self.buffered().last().copied() { + Some(b'\n') => self.buffer.flush_buf(), + _ => Ok(()), + } + } +} + +impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { + /// Writes some data into this BufWriter with line buffering. + /// + /// This means that, if any newlines are present in the data, the data up to + /// the last newline is sent directly to the underlying writer, and data + /// after it is buffered. Returns the number of bytes written. + /// + /// This function operates on a "best effort basis"; in keeping with the + /// convention of `Write::write`, it makes at most one attempt to write + /// new data to the underlying writer. If that write only reports a partial + /// success, the remaining data will be buffered. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it ends with a + /// newline, even if the incoming data does not contain any newlines. + fn write(&mut self, buf: &[u8]) -> Result { + let newline_idx = match memchr::memrchr(b'\n', buf) { + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write (which may flush if + // we exceed the inner buffer's size) + None => { + self.flush_if_completed_line()?; + return self.buffer.write(buf); + } + // Otherwise, arrange for the lines to be written directly to the + // inner writer. + Some(newline_idx) => newline_idx + 1, + }; + + // Flush existing content to prepare for our write. We have to do this + // before attempting to write `buf` in order to maintain consistency; + // if we add `buf` to the buffer then try to flush it all at once, + // we're obligated to return Ok(), which would mean suppressing any + // errors that occur during flush. + self.buffer.flush_buf()?; + + // This is what we're going to try to write directly to the inner + // writer. The rest will be buffered, if nothing goes wrong. + let lines = &buf[..newline_idx]; + + // Write `lines` directly to the inner writer. In keeping with the + // `write` convention, make at most one attempt to add new (unbuffered) + // data. Because this write doesn't touch the BufWriter state directly, + // and the buffer is known to be empty, we don't need to worry about + // self.buffer.panicked here. + let flushed = self.inner_mut().write(lines)?; + + // If buffer returns Ok(0), propagate that to the caller without + // doing additional buffering; otherwise we're just guaranteeing + // an "ErrorKind::WriteZero" later. + if flushed == 0 { + return Ok(0); + } + + // Now that the write has succeeded, buffer the rest (or as much of + // the rest as possible). If there were any unwritten newlines, we + // only buffer out to the last unwritten newline that fits in the + // buffer; this helps prevent flushing partial lines on subsequent + // calls to LineWriterShim::write. + + // Handle the cases in order of most-common to least-common, under + // the presumption that most writes succeed in totality, and that most + // writes are smaller than the buffer. + // - Is this a partial line (ie, no newlines left in the unwritten tail) + // - If not, does the data out to the last unwritten newline fit in the buffer? + // - If not, scan for the last newline that *does* fit in the buffer + let tail = if flushed >= newline_idx { + let tail = &buf[flushed..]; + // Avoid unnecessary short writes by not splitting the remaining + // bytes if they're larger than the buffer. + // They can be written in full by the next call to write. + if tail.len() >= self.buffer.capacity() { + return Ok(flushed); + } + tail + } else if newline_idx - flushed <= self.buffer.capacity() { + &buf[flushed..newline_idx] + } else { + let scan_area = &buf[flushed..]; + let scan_area = &scan_area[..self.buffer.capacity()]; + match memchr::memrchr(b'\n', scan_area) { + Some(newline_idx) => &scan_area[..newline_idx + 1], + None => scan_area, + } + }; + + let buffered = self.buffer.write_to_buf(tail); + Ok(flushed + buffered) + } + + fn flush(&mut self) -> Result<()> { + self.buffer.flush() + } + + /// Writes some data into this BufWriter with line buffering. + /// + /// This means that, if any newlines are present in the data, the data up to + /// the last newline is sent directly to the underlying writer, and data + /// after it is buffered. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it contains any + /// newlines, even if the incoming data does not contain any newlines. + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + match memchr::memrchr(b'\n', buf) { + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write (which may flush if + // we exceed the inner buffer's size) + None => { + self.flush_if_completed_line()?; + self.buffer.write_all(buf) + } + Some(newline_idx) => { + let (lines, tail) = buf.split_at(newline_idx + 1); + + if self.buffered().is_empty() { + self.inner_mut().write_all(lines)?; + } else { + // If there is any buffered data, we add the incoming lines + // to that buffer before flushing, which saves us at least + // one write call. We can't really do this with `write`, + // since we can't do this *and* not suppress errors *and* + // report a consistent state to the caller in a return + // value, but here in write_all it's fine. + self.buffer.write_all(lines)?; + self.buffer.flush_buf()?; + } + + self.buffer.write_all(tail) + } + } + } +} diff --git a/src/buffered/mod.rs b/src/buffered/mod.rs index c7dfe1e..d953053 100644 --- a/src/buffered/mod.rs +++ b/src/buffered/mod.rs @@ -1,3 +1,76 @@ mod bufreader; +mod bufwriter; +mod linewriter; -pub use self::bufreader::BufReader; +use core::fmt; + +pub use self::{ + bufreader::BufReader, + bufwriter::{BufWriter, WriterPanicked}, + linewriter::LineWriter, +}; +use crate::Error; + +/// An error returned by [`BufWriter::into_inner`] which combines an error that +/// happened while writing out the buffer, and the buffered writer object +/// which may be used to recover from the condition. +#[derive(Debug)] +pub struct IntoInnerError(W, Error); + +impl IntoInnerError { + /// Constructs a new IntoInnerError + fn new(writer: W, error: Error) -> Self { + Self(writer, error) + } + + /// Helper to construct a new IntoInnerError; intended to help with + /// adapters that wrap other adapters + fn new_wrapped(self, f: impl FnOnce(W) -> W2) -> IntoInnerError { + let Self(writer, error) = self; + IntoInnerError::new(f(writer), error) + } + + /// Returns the error which caused the call to [`BufWriter::into_inner()`] + /// to fail. + /// + /// This error was returned when attempting to write the internal buffer. + pub fn error(&self) -> &Error { + &self.1 + } + + /// Returns the buffered writer instance which generated the error. + /// + /// The returned object can be used for error recovery, such as + /// re-inspecting the buffer. + pub fn into_inner(self) -> W { + self.0 + } + + /// Consumes the [`IntoInnerError`] and returns the error which caused the call to + /// [`BufWriter::into_inner()`] to fail. Unlike `error`, this can be used to + /// obtain ownership of the underlying error. + pub fn into_error(self) -> Error { + self.1 + } + + /// Consumes the [`IntoInnerError`] and returns the error which caused the call to + /// [`BufWriter::into_inner()`] to fail, and the underlying writer. + /// + /// This can be used to simply obtain ownership of the underlying error; it can also be used for + /// advanced error recovery. + pub fn into_parts(self) -> (Error, W) { + (self.1, self.0) + } +} + +impl From> for Error { + fn from(iie: IntoInnerError) -> Error { + iie.1 + } +} + +impl fmt::Display for IntoInnerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.error().fmt(f) + } +} diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index ee2d716..0000000 --- a/src/error.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub use axerrno::AxError as Error; -pub use axerrno::AxResult as Result; diff --git a/src/impls.rs b/src/impls.rs deleted file mode 100644 index 96f83db..0000000 --- a/src/impls.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{prelude::*, Result}; -use core::cmp; - -impl Read for &[u8] { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - let amt = cmp::min(buf.len(), self.len()); - let a = &self[..amt]; - let b = &self[amt..]; - - // First check if the amount of bytes we want to read is small: - // `copy_from_slice` will generally expand to a call to `memcpy`, and - // for a single byte the overhead is significant. - if amt == 1 { - buf[0] = a[0]; - } else { - buf[..amt].copy_from_slice(a); - } - - *self = b; - Ok(amt) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { - if buf.len() > self.len() { - return axerrno::ax_err!(UnexpectedEof, "failed to fill whole buffer"); - } - let amt = buf.len(); - let a = &self[..amt]; - let b = &self[amt..]; - - // First check if the amount of bytes we want to read is small: - // `copy_from_slice` will generally expand to a call to `memcpy`, and - // for a single byte the overhead is significant. - if amt == 1 { - buf[0] = a[0]; - } else { - buf[..amt].copy_from_slice(a); - } - - *self = b; - Ok(()) - } - - #[inline] - #[cfg(feature = "alloc")] - fn read_to_end(&mut self, buf: &mut alloc::vec::Vec) -> Result { - buf.extend_from_slice(self); - let len = self.len(); - *self = &self[len..]; - Ok(len) - } -} diff --git a/src/lib.rs b/src/lib.rs index 34c1fb8..66b8297 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,397 +1,29 @@ -//! [`std::io`]-like I/O traits for `no_std` environment. - +#![doc = include_str!("../README.md")] #![cfg_attr(not(doc), no_std)] #![feature(doc_cfg)] #![feature(core_io_borrowed_buf)] -#![cfg_attr(not(borrowedbuf_init), feature(maybe_uninit_fill))] +#![feature(min_specialization)] +#![feature(maybe_uninit_fill)] +#![cfg_attr(not(maybe_uninit_slice), feature(maybe_uninit_slice))] +#![warn(missing_docs)] #[cfg(feature = "alloc")] extern crate alloc; -use core::fmt; +#[doc(no_inline)] +pub use axerrno::{AxError as Error, AxErrorKind as ErrorKind, AxResult as Result}; -mod buffered; -mod error; -mod impls; +/// Default buffer size for I/O operations. +pub const DEFAULT_BUF_SIZE: usize = 1024 * 2; +mod buffered; pub mod prelude; +mod read; +mod seek; +mod utils; +mod write; -pub use self::buffered::BufReader; -pub use self::error::{Error, Result}; - -#[cfg(feature = "alloc")] -use alloc::{string::String, vec::Vec}; - -use axerrno::ax_err; - -/// Default [`Read::read_to_end`] implementation with optional size hint. -/// -/// Adapted from [`std::io::default_read_to_end`]. -/// -/// [`std::io::default_read_to_end`]: https://github.com/rust-lang/rust/blob/30f168ef811aec63124eac677e14699baa9395bd/library/std/src/io/mod.rs#L409 -#[cfg(feature = "alloc")] -pub fn default_read_to_end( - r: &mut R, - buf: &mut Vec, - size_hint: Option, -) -> Result { - use core::io::BorrowedBuf; - - const DEFAULT_BUF_SIZE: usize = 1024; - - let start_len = buf.len(); - let start_cap = buf.capacity(); - // Optionally limit the maximum bytes read on each iteration. - // This adds an arbitrary fiddle factor to allow for more data than we expect. - let mut max_read_size = size_hint - .and_then(|s| { - s.checked_add(1024)? - .checked_next_multiple_of(DEFAULT_BUF_SIZE) - }) - .unwrap_or(DEFAULT_BUF_SIZE); - - const PROBE_SIZE: usize = 32; - - fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { - let mut probe = [0u8; PROBE_SIZE]; - - let n = r.read(&mut probe)?; - buf.extend_from_slice(&probe[..n]); - Ok(n) - } - - if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE { - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(0); - } - } - - #[cfg(borrowedbuf_init)] - let mut initialized = 0; // Extra initialized bytes from previous loop iteration - #[cfg(borrowedbuf_init)] - let mut consecutive_short_reads = 0; - - loop { - if buf.len() == buf.capacity() && buf.capacity() == start_cap { - // The buffer might be an exact fit. Let's read into a probe buffer - // and see if it returns `Ok(0)`. If so, we've avoided an - // unnecessary doubling of the capacity. But if not, append the - // probe buffer to the primary buffer and let its capacity grow. - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(buf.len() - start_len); - } - } - - if buf.len() == buf.capacity() { - // buf is full, need more space - if let Err(e) = buf.try_reserve(PROBE_SIZE) { - return ax_err!(NoMemory, e); - } - } - - let mut spare = buf.spare_capacity_mut(); - let buf_len = spare.len().min(max_read_size); - spare = &mut spare[..buf_len]; - let mut read_buf: BorrowedBuf<'_> = spare.into(); - - #[cfg(borrowedbuf_init)] - // SAFETY: These bytes were initialized but not filled in the previous loop - unsafe { - read_buf.set_init(initialized); - } - - let mut cursor = read_buf.unfilled(); - // Difference from `std`: We don't have a `read_buf` method that returns both data and an error, so we return early on error. - #[cfg(borrowedbuf_init)] - { - let n = r.read(cursor.ensure_init().init_mut())?; - cursor.advance(n); - } - #[cfg(not(borrowedbuf_init))] - { - // SAFETY: We do not uninitialize any part of the buffer. - let n = r.read(unsafe { cursor.as_mut().write_filled(0) })?; - assert!(n <= cursor.capacity()); - // SAFETY: We've initialized the entire buffer, and `read` can't make it uninitialized. - unsafe { - cursor.advance(n); - } - } - - #[cfg(borrowedbuf_init)] - let unfilled_but_initialized = cursor.init_mut().len(); - let bytes_read = cursor.written(); - #[cfg(borrowedbuf_init)] - let was_fully_initialized = read_buf.init_len() == buf_len; - - // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. - unsafe { - let new_len = bytes_read + buf.len(); - buf.set_len(new_len); - } - - if bytes_read == 0 { - return Ok(buf.len() - start_len); - } - - #[cfg(borrowedbuf_init)] - if bytes_read < buf_len { - consecutive_short_reads += 1; - } else { - consecutive_short_reads = 0; - } - - #[cfg(borrowedbuf_init)] - { - // store how much was initialized but not filled - initialized = unfilled_but_initialized; - } - - // Use heuristics to determine the max read size if no initial size hint was provided - if size_hint.is_none() { - #[cfg(borrowedbuf_init)] - // The reader is returning short reads but it doesn't call ensure_init(). - // In that case we no longer need to restrict read sizes to avoid - // initialization costs. - // When reading from disk we usually don't get any short reads except at EOF. - // So we wait for at least 2 short reads before uncapping the read buffer; - // this helps with the Windows issue. - if !was_fully_initialized && consecutive_short_reads > 1 { - max_read_size = usize::MAX; - } - - // we have passed a larger buffer than previously and the - // reader still hasn't returned a short read - if buf_len >= max_read_size && bytes_read == buf_len { - max_read_size = max_read_size.saturating_mul(2); - } - } - } -} - -/// The `Read` trait allows for reading bytes from a source. -pub trait Read { - /// Pull some bytes from this source into the specified buffer, returning - /// how many bytes were read. - fn read(&mut self, buf: &mut [u8]) -> Result; - - /// Read all bytes until EOF in this source, placing them into `buf`. - #[cfg(feature = "alloc")] - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - default_read_to_end(self, buf, None) - } - - /// Read all bytes until EOF in this source, appending them to `buf`. - #[cfg(feature = "alloc")] - fn read_to_string(&mut self, buf: &mut String) -> Result { - unsafe { append_to_string(buf, |b| self.read_to_end(b)) } - } - - /// Read the exact number of bytes required to fill `buf`. - fn read_exact(&mut self, mut buf: &mut [u8]) -> Result { - while !buf.is_empty() { - match self.read(buf) { - Ok(0) => break, - Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; - } - Err(e) => return Err(e), - } - } - if !buf.is_empty() { - ax_err!(UnexpectedEof, "failed to fill whole buffer") - } else { - Ok(()) - } - } -} - -/// A trait for objects which are byte-oriented sinks. -pub trait Write { - /// Write a buffer into this writer, returning how many bytes were written. - fn write(&mut self, buf: &[u8]) -> Result; - - /// Flush this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - fn flush(&mut self) -> Result; - - /// Attempts to write an entire buffer into this writer. - fn write_all(&mut self, mut buf: &[u8]) -> Result { - while !buf.is_empty() { - match self.write(buf) { - Ok(0) => return ax_err!(WriteZero, "failed to write whole buffer"), - Ok(n) => buf = &buf[n..], - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Writes a formatted string into this writer, returning any error - /// encountered. - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { - // Create a shim which translates a Write to a fmt::Write and saves - // off I/O errors. instead of discarding them - struct Adapter<'a, T: ?Sized + 'a> { - inner: &'a mut T, - error: Result<()>, - } - - impl fmt::Write for Adapter<'_, T> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_all(s.as_bytes()) { - Ok(()) => Ok(()), - Err(e) => { - self.error = Err(e); - Err(fmt::Error) - } - } - } - } - - let mut output = Adapter { - inner: self, - error: Ok(()), - }; - match fmt::write(&mut output, fmt) { - Ok(()) => Ok(()), - Err(..) => { - // check if the error came from the underlying `Write` or not - if output.error.is_err() { - output.error - } else { - ax_err!(InvalidData, "formatter error") - } - } - } - } -} - -/// The `Seek` trait provides a cursor which can be moved within a stream of -/// bytes. -pub trait Seek { - /// Seek to an offset, in bytes, in a stream. - /// - /// A seek beyond the end of a stream is allowed, but behavior is defined - /// by the implementation. - /// - /// 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`]. - fn seek(&mut self, pos: SeekFrom) -> Result; - - /// Rewind to the beginning of a stream. - /// - /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. - fn rewind(&mut self) -> Result<()> { - self.seek(SeekFrom::Start(0))?; - Ok(()) - } - - /// Returns the current seek position from the start of the stream. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(0))`. - fn stream_position(&mut self) -> Result { - self.seek(SeekFrom::Current(0)) - } -} - -/// Enumeration of possible methods to seek within an I/O object. -/// -/// It is used by the [`Seek`] trait. -#[derive(Copy, PartialEq, Eq, Clone, Debug)] -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. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - End(i64), - - /// Sets the offset to the current position plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - Current(i64), -} - -/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it -/// to perform extra ways of reading. -pub trait BufRead: Read { - /// Returns the contents of the internal buffer, filling it with more data - /// from the inner reader if it is empty. - fn fill_buf(&mut self) -> Result<&[u8]>; - - /// Tells this buffer that `amt` bytes have been consumed from the buffer, - /// so they should no longer be returned in calls to `read`. - fn consume(&mut self, amt: usize); - - /// Check if the underlying `Read` has any data left to be read. - fn has_data_left(&mut self) -> Result { - self.fill_buf().map(|b| !b.is_empty()) - } - - /// Read all bytes into `buf` until the delimiter `byte` or EOF is reached. - #[cfg(feature = "alloc")] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match self.fill_buf() { - Ok(n) => n, - Err(Error::WouldBlock) => continue, - Err(e) => return Err(e), - }; - match available.iter().position(|&b| b == byte) { - Some(i) => { - buf.extend_from_slice(&available[..=i]); - (true, i + 1) - } - None => { - buf.extend_from_slice(available); - (false, available.len()) - } - } - }; - self.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } - } - - /// Read all bytes until a newline (the `0xA` byte) is reached, and append - /// them to the provided `String` buffer. - #[cfg(feature = "alloc")] - fn read_line(&mut self, buf: &mut String) -> Result { - unsafe { append_to_string(buf, |b| self.read_until(b'\n', b)) } - } -} - -#[cfg(feature = "alloc")] -unsafe fn append_to_string(buf: &mut String, f: F) -> Result -where - F: FnOnce(&mut Vec) -> Result, -{ - let old_len = buf.len(); - let buf = unsafe { buf.as_mut_vec() }; - let ret = f(buf)?; - if core::str::from_utf8(&buf[old_len..]).is_err() { - ax_err!(InvalidData, "stream did not contain valid UTF-8") - } else { - Ok(ret) - } -} +pub use self::{buffered::*, read::*, seek::*, utils::*, write::*}; /// I/O poll results. #[derive(Debug, Default, Clone, Copy)] diff --git a/src/prelude.rs b/src/prelude.rs index 9f8020c..6116972 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -5,7 +5,7 @@ //! //! ``` //! # #![allow(unused_imports)] -//! use std::io::prelude::*; +//! use axio::prelude::*; //! ``` -pub use super::{BufRead, Read, Seek, Write}; +pub use crate::{BufRead, Read, Seek, Write}; diff --git a/src/read/impls.rs b/src/read/impls.rs new file mode 100644 index 0000000..3a00dca --- /dev/null +++ b/src/read/impls.rs @@ -0,0 +1,357 @@ +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, collections::VecDeque, string::String, vec::Vec}; +use core::{cmp, io::BorrowedCursor}; + +use crate::{BufRead, Error, Read, Result}; + +// ============================================================================= +// Forwarding implementations + +impl Read for &mut R { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + (**self).read(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + (**self).read_exact(buf) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + (**self).read_buf(cursor) + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + (**self).read_buf_exact(cursor) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + (**self).read_to_end(buf) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + (**self).read_to_string(buf) + } +} + +impl BufRead for &mut B { + #[inline] + fn fill_buf(&mut self) -> Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn has_data_left(&mut self) -> Result { + (**self).has_data_left() + } + + #[inline] + fn skip_until(&mut self, byte: u8) -> Result { + (**self).skip_until(byte) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + (**self).read_until(byte, buf) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_line(&mut self, buf: &mut String) -> Result { + (**self).read_line(buf) + } +} + +#[cfg(feature = "alloc")] +impl Read for Box { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + (**self).read(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + (**self).read_exact(buf) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + (**self).read_buf(cursor) + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + (**self).read_buf_exact(cursor) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + (**self).read_to_end(buf) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + (**self).read_to_string(buf) + } +} + +#[cfg(feature = "alloc")] +impl BufRead for Box { + #[inline] + fn fill_buf(&mut self) -> Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn has_data_left(&mut self) -> Result { + (**self).has_data_left() + } + + #[inline] + fn skip_until(&mut self, byte: u8) -> Result { + (**self).skip_until(byte) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + (**self).read_until(byte, buf) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_line(&mut self, buf: &mut String) -> Result { + (**self).read_line(buf) + } +} + +// ============================================================================= +// In-memory buffer implementations + +impl Read for &[u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + let amt = cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + if buf.len() > self.len() { + // `read_exact` makes no promise about the content of `buf` if it + // fails so don't bother about that. + *self = &self[self.len()..]; + return Err(Error::UnexpectedEof); + } + let (a, b) = self.split_at(buf.len()); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if buf.len() == 1 { + buf[0] = a[0]; + } else { + buf.copy_from_slice(a); + } + + *self = b; + Ok(()) + } + + #[inline] + fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { + let amt = cmp::min(cursor.capacity(), self.len()); + let (a, b) = self.split_at(amt); + + cursor.append(a); + + *self = b; + Ok(()) + } + + #[inline] + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { + if cursor.capacity() > self.len() { + // Append everything we can to the cursor. + cursor.append(self); + *self = &self[self.len()..]; + return Err(Error::UnexpectedEof); + } + let (a, b) = self.split_at(cursor.capacity()); + + cursor.append(a); + + *self = b; + Ok(()) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let len = self.len(); + buf.try_reserve(len).map_err(|_| Error::NoMemory)?; + buf.extend_from_slice(self); + *self = &self[len..]; + Ok(len) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + let content = str::from_utf8(self).map_err(|_| Error::IllegalBytes)?; + let len = self.len(); + buf.try_reserve(len).map_err(|_| Error::NoMemory)?; + buf.push_str(content); + *self = &self[len..]; + Ok(len) + } +} + +impl BufRead for &[u8] { + #[inline] + fn fill_buf(&mut self) -> Result<&[u8]> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} + +#[cfg(feature = "alloc")] +impl Read for VecDeque { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + let (ref mut front, _) = self.as_slices(); + let n = Read::read(front, buf)?; + self.drain(..n); + Ok(n) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + let (front, back) = self.as_slices(); + + // Use only the front buffer if it is big enough to fill `buf`, else use + // the back buffer too. + match buf.split_at_mut_checked(front.len()) { + None => buf.copy_from_slice(&front[..buf.len()]), + Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) { + Some((back, _)) => { + buf_front.copy_from_slice(front); + buf_back.copy_from_slice(back); + } + None => { + self.clear(); + return Err(Error::UnexpectedEof); + } + }, + } + + self.drain(..buf.len()); + Ok(()) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + let (ref mut front, _) = self.as_slices(); + let n = cmp::min(cursor.capacity(), front.len()); + Read::read_buf(front, cursor)?; + self.drain(..n); + Ok(()) + } + + #[inline] + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { + let len = cursor.capacity(); + let (front, back) = self.as_slices(); + + match front.split_at_checked(cursor.capacity()) { + Some((front, _)) => cursor.append(front), + None => { + cursor.append(front); + match back.split_at_checked(cursor.capacity()) { + Some((back, _)) => cursor.append(back), + None => { + cursor.append(back); + self.clear(); + return Err(Error::UnexpectedEof); + } + } + } + } + + self.drain(..len); + Ok(()) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + // The total len is known upfront so we can reserve it in a single call. + let len = self.len(); + buf.try_reserve(len).map_err(|_| Error::NoMemory)?; + let (front, back) = self.as_slices(); + buf.extend_from_slice(front); + buf.extend_from_slice(back); + self.clear(); + Ok(len) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + // SAFETY: We only append to the buffer + unsafe { super::append_to_string(buf, |buf| self.read_to_end(buf)) } + } +} + +#[cfg(feature = "alloc")] +impl BufRead for VecDeque { + /// Returns the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. + #[inline] + fn fill_buf(&mut self) -> Result<&[u8]> { + let (front, _) = self.as_slices(); + Ok(front) + } + + #[inline] + fn consume(&mut self, amt: usize) { + self.drain(..amt); + } +} diff --git a/src/read/mod.rs b/src/read/mod.rs new file mode 100644 index 0000000..d98bd87 --- /dev/null +++ b/src/read/mod.rs @@ -0,0 +1,521 @@ +#[cfg(feature = "alloc")] +use alloc::{string::String, vec::Vec}; +use core::io::BorrowedCursor; + +use crate::{Chain, Error, Result, Take}; + +mod impls; + +/// Default [`Read::read_exact`] implementation. +pub fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { + while !buf.is_empty() { + match this.read(buf) { + Ok(0) => break, + Ok(n) => { + buf = &mut buf[n..]; + } + Err(e) if e.canonicalize() == Error::Interrupted => continue, + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(Error::UnexpectedEof) + } else { + Ok(()) + } +} + +/// Default [`Read::read_buf`] implementation. +pub fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> +where + F: FnOnce(&mut [u8]) -> Result, +{ + #[cfg(borrowedbuf_init)] + { + let n = read(cursor.ensure_init().init_mut())?; + cursor.advance(n); + } + #[cfg(not(borrowedbuf_init))] + { + // SAFETY: We do not uninitialize any part of the buffer. + let n = read(unsafe { cursor.as_mut().write_filled(0) })?; + assert!(n <= cursor.capacity()); + // SAFETY: We've initialized the entire buffer, and `read` can't make it uninitialized. + unsafe { + cursor.advance(n); + } + } + Ok(()) +} + +/// Default [`Read::read_buf_exact`] implementation. +pub fn default_read_buf_exact( + this: &mut R, + mut cursor: BorrowedCursor<'_>, +) -> Result<()> { + while cursor.capacity() > 0 { + let prev_written = cursor.written(); + match this.read_buf(cursor.reborrow()) { + Ok(()) => {} + Err(e) if e.canonicalize() == Error::Interrupted => continue, + Err(e) => return Err(e), + } + + if cursor.written() == prev_written { + return Err(Error::UnexpectedEof); + } + } + + Ok(()) +} + +/// Default [`Read::read_to_end`] implementation with optional size hint. +#[cfg(feature = "alloc")] +pub fn default_read_to_end( + r: &mut R, + buf: &mut Vec, + size_hint: Option, +) -> Result { + use core::io::BorrowedBuf; + + use crate::DEFAULT_BUF_SIZE; + + let start_len = buf.len(); + let start_cap = buf.capacity(); + // Optionally limit the maximum bytes read on each iteration. + // This adds an arbitrary fiddle factor to allow for more data than we expect. + let mut max_read_size = size_hint + .and_then(|s| { + s.checked_add(1024)? + .checked_next_multiple_of(DEFAULT_BUF_SIZE) + }) + .unwrap_or(DEFAULT_BUF_SIZE); + + const PROBE_SIZE: usize = 32; + + fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { + let mut probe = [0u8; PROBE_SIZE]; + + loop { + match r.read(&mut probe) { + Ok(n) => { + // there is no way to recover from allocation failure here + // because the data has already been read. + buf.extend_from_slice(&probe[..n]); + return Ok(n); + } + Err(e) if e.canonicalize() == Error::Interrupted => continue, + Err(e) => return Err(e), + } + } + } + + if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE { + let read = small_probe_read(r, buf)?; + + if read == 0 { + return Ok(0); + } + } + + #[cfg(borrowedbuf_init)] + let mut initialized = 0; // Extra initialized bytes from previous loop iteration + #[cfg(borrowedbuf_init)] + let mut consecutive_short_reads = 0; + + loop { + if buf.len() == buf.capacity() && buf.capacity() == start_cap { + // The buffer might be an exact fit. Let's read into a probe buffer + // and see if it returns `Ok(0)`. If so, we've avoided an + // unnecessary doubling of the capacity. But if not, append the + // probe buffer to the primary buffer and let its capacity grow. + let read = small_probe_read(r, buf)?; + + if read == 0 { + return Ok(buf.len() - start_len); + } + } + + if buf.len() == buf.capacity() { + // buf is full, need more space + buf.try_reserve(PROBE_SIZE).map_err(|_| Error::NoMemory)?; + } + + let mut spare = buf.spare_capacity_mut(); + let buf_len = spare.len().min(max_read_size); + spare = &mut spare[..buf_len]; + let mut read_buf: BorrowedBuf<'_> = spare.into(); + + #[cfg(borrowedbuf_init)] + // SAFETY: These bytes were initialized but not filled in the previous loop + unsafe { + read_buf.set_init(initialized); + } + + let mut cursor = read_buf.unfilled(); + let result = loop { + match r.read_buf(cursor.reborrow()) { + Err(e) if e.canonicalize() == Error::Interrupted => continue, + // Do not stop now in case of error: we might have received both data + // and an error + res => break res, + } + }; + + #[cfg(borrowedbuf_init)] + let unfilled_but_initialized = cursor.init_mut().len(); + let bytes_read = cursor.written(); + #[cfg(borrowedbuf_init)] + let was_fully_initialized = read_buf.init_len() == buf_len; + + // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. + unsafe { + let new_len = bytes_read + buf.len(); + buf.set_len(new_len); + } + + // Now that all data is pushed to the vector, we can fail without data loss + result?; + + if bytes_read == 0 { + return Ok(buf.len() - start_len); + } + + #[cfg(borrowedbuf_init)] + if bytes_read < buf_len { + consecutive_short_reads += 1; + } else { + consecutive_short_reads = 0; + } + + #[cfg(borrowedbuf_init)] + { + // store how much was initialized but not filled + initialized = unfilled_but_initialized; + } + + // Use heuristics to determine the max read size if no initial size hint was provided + if size_hint.is_none() { + #[cfg(borrowedbuf_init)] + // The reader is returning short reads but it doesn't call ensure_init(). + // In that case we no longer need to restrict read sizes to avoid + // initialization costs. + // When reading from disk we usually don't get any short reads except at EOF. + // So we wait for at least 2 short reads before uncapping the read buffer; + // this helps with the Windows issue. + if !was_fully_initialized && consecutive_short_reads > 1 { + max_read_size = usize::MAX; + } + + // we have passed a larger buffer than previously and the + // reader still hasn't returned a short read + if buf_len >= max_read_size && bytes_read == buf_len { + max_read_size = max_read_size.saturating_mul(2); + } + } + } +} + +#[cfg(feature = "alloc")] +pub(crate) unsafe fn append_to_string(buf: &mut String, f: F) -> Result +where + F: FnOnce(&mut Vec) -> Result, +{ + struct Guard<'a> { + buf: &'a mut Vec, + len: usize, + } + + impl Drop for Guard<'_> { + fn drop(&mut self) { + unsafe { + self.buf.set_len(self.len); + } + } + } + + let mut g = Guard { + len: buf.len(), + buf: unsafe { buf.as_mut_vec() }, + }; + let ret = f(g.buf); + + // SAFETY: the caller promises to only append data to `buf` + let appended = unsafe { g.buf.get_unchecked(g.len..) }; + if str::from_utf8(appended).is_err() { + ret.and(Err(Error::IllegalBytes)) + } else { + g.len = g.buf.len(); + ret + } +} + +/// Default [`Read::read_to_string`] implementation with optional size hint. +#[cfg(feature = "alloc")] +pub fn default_read_to_string( + r: &mut R, + buf: &mut String, + size_hint: Option, +) -> Result { + // Note that we do *not* call `r.read_to_end()` here. We are passing + // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` + // method to fill it up. An arbitrary implementation could overwrite the + // entire contents of the vector, not just append to it (which is what + // we are expecting). + // + // To prevent extraneously checking the UTF-8-ness of the entire buffer + // we pass it to our hardcoded `default_read_to_end` implementation which + // we know is guaranteed to only read data into the end of the buffer. + unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } +} + +/// The `Read` trait allows for reading bytes from a source. +/// +/// See [`std::io::Read`] for more details. +pub trait Read { + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read the exact number of bytes required to fill `buf`. + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + default_read_exact(self, buf) + } + + /// Pull some bytes from this source into the specified buffer. + /// + /// This method makes it possible to return both data and an error but it is advised against. + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + default_read_buf(|b| self.read(b), buf) + } + + /// Reads the exact number of bytes required to fill `cursor`. + /// + /// If this function returns an error, all bytes read will be appended to `cursor`. + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + default_read_buf_exact(self, cursor) + } + + /// Read all bytes until EOF in this source, placing them into `buf`. + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + default_read_to_end(self, buf, None) + } + + /// Read all bytes until EOF in this source, appending them to `buf`. + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + default_read_to_string(self, buf, None) + } + + /// Creates a "by reference" adapter for this instance of `Read`. + /// + /// The returned `adapter` also implements Read and will simply borrow this + /// current reader. + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } + + /// Creates an adapter which will chain this stream with another. + /// + /// The returned `Read` instance will first read all bytes from this object + /// until EOF is encountered. Afterwards the output is equivalent to the + /// output of `next`. + fn chain(self, next: R) -> Chain + where + Self: Sized, + { + Chain::new(self, next) + } + + /// Creates an adapter which will read at most `limit` bytes from it. + /// + /// This function returns a new instance of `Read` which will read at most + /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any + /// read errors will not count towards the number of bytes read and future + /// calls to [`read()`] may succeed. + /// + /// [`Ok(0)`]: Ok + /// [`read()`]: Read::read + fn take(self, limit: u64) -> Take + where + Self: Sized, + { + Take::new(self, limit) + } +} + +/// Reads all bytes from a [reader][Read] into a new [`String`]. +/// +/// This is a convenience function for [`Read::read_to_string`]. +/// +/// See [`std::io::read_to_string`] for more details. +#[cfg(feature = "alloc")] +pub fn read_to_string(mut reader: R) -> Result { + let mut buf = String::new(); + reader.read_to_string(&mut buf)?; + Ok(buf) +} + +/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it +/// to perform extra ways of reading. +/// +/// See [`std::io::BufRead`] for more details. +pub trait BufRead: Read { + /// Returns the contents of the internal buffer, filling it with more data, via `Read` methods, + /// if empty. + fn fill_buf(&mut self) -> Result<&[u8]>; + + /// Marks the given `amount` of additional bytes from the internal buffer as having been read. + /// Subsequent calls to `read` only return bytes that have not been marked as read. + fn consume(&mut self, amount: usize); + + /// Checks if there is any data left to be `read`. + fn has_data_left(&mut self) -> Result { + self.fill_buf().map(|b| !b.is_empty()) + } + + /// Skips all bytes until the delimiter `byte` or EOF is reached. + fn skip_until(&mut self, byte: u8) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = self.fill_buf()?; + match memchr::memchr(byte, available) { + Some(i) => (true, i + 1), + None => (false, available.len()), + } + }; + self.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } + } + + /// Read all bytes into `buf` until the delimiter `byte` or EOF is reached. + #[cfg(feature = "alloc")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = self.fill_buf()?; + match memchr::memchr(byte, available) { + Some(i) => { + buf.extend_from_slice(&available[..=i]); + (true, i + 1) + } + None => { + buf.extend_from_slice(available); + (false, available.len()) + } + } + }; + self.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } + } + + /// Read all bytes until a newline (the `0xA` byte) is reached, and append + /// them to the provided `String` buffer. + #[cfg(feature = "alloc")] + fn read_line(&mut self, buf: &mut String) -> Result { + unsafe { super::append_to_string(buf, |b| self.read_until(b'\n', b)) } + } + + /// Returns an iterator over the contents of this reader split on the byte + /// `byte`. + #[cfg(feature = "alloc")] + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + Split { + buf: self, + delim: byte, + } + } + + /// Returns an iterator over the lines of this reader. + #[cfg(feature = "alloc")] + fn lines(self) -> Lines + where + Self: Sized, + { + Lines { buf: self } + } +} + +/// An iterator over the contents of an instance of `BufRead` split on a +/// particular byte. +/// +/// This struct is generally created by calling [`split`] on a `BufRead`. +/// Please see the documentation of [`split`] for more details. +/// +/// [`split`]: BufRead::split +#[cfg(feature = "alloc")] +#[derive(Debug)] +pub struct Split { + buf: B, + delim: u8, +} + +#[cfg(feature = "alloc")] +impl Iterator for Split { + type Item = Result>; + + fn next(&mut self) -> Option>> { + let mut buf = Vec::new(); + match self.buf.read_until(self.delim, &mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf[buf.len() - 1] == self.delim { + buf.pop(); + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +/// An iterator over the lines of an instance of `BufRead`. +/// +/// This struct is generally created by calling [`lines`] on a `BufRead`. +/// Please see the documentation of [`lines`] for more details. +/// +/// [`lines`]: BufRead::lines +#[cfg(feature = "alloc")] +#[derive(Debug)] +pub struct Lines { + buf: B, +} + +#[cfg(feature = "alloc")] +impl Iterator for Lines { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut buf = String::new(); + match self.buf.read_line(&mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf.ends_with('\n') { + buf.pop(); + if buf.ends_with('\r') { + buf.pop(); + } + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} diff --git a/src/seek/impls.rs b/src/seek/impls.rs new file mode 100644 index 0000000..3bfb3e2 --- /dev/null +++ b/src/seek/impls.rs @@ -0,0 +1,62 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crate::{Result, Seek, SeekFrom}; + +// ============================================================================= +// Forwarding implementations + +impl Seek for &mut S { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> Result<()> { + (**self).seek_relative(offset) + } +} + +#[cfg(feature = "alloc")] +impl Seek for Box { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> Result<()> { + (**self).seek_relative(offset) + } +} diff --git a/src/seek/mod.rs b/src/seek/mod.rs new file mode 100644 index 0000000..f76b126 --- /dev/null +++ b/src/seek/mod.rs @@ -0,0 +1,82 @@ +use crate::Result; + +mod impls; + +/// Enumeration of possible methods to seek within an I/O object. +/// +/// It is used by the [`Seek`] trait. +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +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. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + End(i64), + + /// Sets the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + Current(i64), +} + +/// Default [`Seek::stream_len`] implementation. +pub fn default_stream_len(this: &mut T) -> Result { + let old_pos = this.stream_position()?; + let len = this.seek(SeekFrom::End(0))?; + + // Avoid seeking a third time when we were already at the end of the + // stream. The branch is usually way cheaper than a seek operation. + if old_pos != len { + this.seek(SeekFrom::Start(old_pos))?; + } + + Ok(len) +} + +/// The `Seek` trait provides a cursor which can be moved within a stream of +/// bytes. +/// +/// See [`std::io::Seek`] for more details. +pub trait Seek { + /// Seek to an offset, in bytes, in a stream. + /// + /// A seek beyond the end of a stream is allowed, but behavior is defined + /// by the implementation. + /// + /// 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`]. + fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + /// + /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. + fn rewind(&mut self) -> Result<()> { + self.seek(SeekFrom::Start(0))?; + Ok(()) + } + + /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)) + } + + /// Returns the length of this stream (in bytes). + fn stream_len(&mut self) -> Result { + default_stream_len(self) + } + + /// Seeks relative to the current position. + fn seek_relative(&mut self, offset: i64) -> Result<()> { + self.seek(SeekFrom::Current(offset))?; + Ok(()) + } +} diff --git a/src/utils/chain.rs b/src/utils/chain.rs new file mode 100644 index 0000000..574539d --- /dev/null +++ b/src/utils/chain.rs @@ -0,0 +1,136 @@ +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use core::io::BorrowedCursor; + +use crate::{BufRead, Read, Result}; + +/// Adapter to chain together two readers. +/// +/// This struct is generally created by calling [`chain`] on a reader. +/// Please see the documentation of [`chain`] for more details. +/// +/// See [`std::io::Chain`] for more details. +/// +/// [`chain`]: Read::chain +#[derive(Debug)] +pub struct Chain { + first: T, + second: U, + done_first: bool, +} + +impl Chain { + pub(crate) fn new(first: T, second: U) -> Self { + Chain { + first, + second, + done_first: false, + } + } + + /// Consumes the `Chain`, returning the wrapped readers. + pub fn into_inner(self) -> (T, U) { + (self.first, self.second) + } + + /// Gets references to the underlying readers in this `Chain`. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying readers as doing so may corrupt the internal state of this + /// `Chain`. + pub fn get_ref(&self) -> (&T, &U) { + (&self.first, &self.second) + } + + /// Gets mutable references to the underlying readers in this `Chain`. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying readers as doing so may corrupt the internal state of this + /// `Chain`. + pub fn get_mut(&mut self) -> (&mut T, &mut U) { + (&mut self.first, &mut self.second) + } +} + +impl Read for Chain { + fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.done_first { + match self.first.read(buf)? { + 0 if !buf.is_empty() => self.done_first = true, + n => return Ok(n), + } + } + self.second.read(buf) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + if buf.capacity() == 0 { + return Ok(()); + } + + if !self.done_first { + let old_len = buf.written(); + self.first.read_buf(buf.reborrow())?; + + if buf.written() != old_len { + return Ok(()); + } else { + self.done_first = true; + } + } + self.second.read_buf(buf) + } + + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let mut read = 0; + if !self.done_first { + read += self.first.read_to_end(buf)?; + self.done_first = true; + } + read += self.second.read_to_end(buf)?; + Ok(read) + } + + // We don't override `read_to_string` here because an UTF-8 sequence could + // be split between the two parts of the chain +} + +impl BufRead for Chain { + fn fill_buf(&mut self) -> Result<&[u8]> { + if !self.done_first { + match self.first.fill_buf()? { + [] => self.done_first = true, + buf => return Ok(buf), + } + } + self.second.fill_buf() + } + + fn consume(&mut self, amt: usize) { + if !self.done_first { + self.first.consume(amt) + } else { + self.second.consume(amt) + } + } + + #[cfg(feature = "alloc")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + let mut read = 0; + if !self.done_first { + let n = self.first.read_until(byte, buf)?; + read += n; + + match buf.last() { + Some(b) if *b == byte && n != 0 => return Ok(read), + _ => self.done_first = true, + } + } + read += self.second.read_until(byte, buf)?; + Ok(read) + } + + // We don't override `read_line` here because an UTF-8 sequence could be + // split between the two parts of the chain +} diff --git a/src/utils/copy.rs b/src/utils/copy.rs new file mode 100644 index 0000000..2b5c14c --- /dev/null +++ b/src/utils/copy.rs @@ -0,0 +1,252 @@ +#[cfg(feature = "alloc")] +use alloc::{collections::vec_deque::VecDeque, vec::Vec}; +use core::{io::BorrowedBuf, mem::MaybeUninit}; + +use crate::{BufReader, BufWriter, DEFAULT_BUF_SIZE, Error, Read, Result, Write}; + +/// Copies the entire contents of a reader into a writer. +/// +/// This function will continuously read data from `reader` and then +/// write it into `writer` in a streaming fashion until `reader` +/// returns EOF. +/// +/// On success, the total number of bytes that were copied from +/// `reader` to `writer` is returned. +/// +/// See [`std::io::copy`] for more details. +pub fn copy(reader: &mut R, writer: &mut W) -> Result +where + R: Read + ?Sized, + W: Write + ?Sized, +{ + let read_buf = BufferedReaderSpec::buffer_size(reader); + let write_buf = BufferedWriterSpec::buffer_size(writer); + + if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf { + return BufferedReaderSpec::copy_to(reader, writer); + } + + BufferedWriterSpec::copy_from(writer, reader) +} + +/// Fallback [`copy`] implementation using a stack-allocated buffer. +pub fn stack_buffer_copy(reader: &mut R, writer: &mut W) -> Result +where + R: Read + ?Sized, + W: Write + ?Sized, +{ + let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE]; + let mut buf: BorrowedBuf<'_> = buf.into(); + + let mut len = 0; + + loop { + match reader.read_buf(buf.unfilled()) { + Ok(()) => {} + Err(e) if e.canonicalize() == Error::Interrupted => continue, + Err(e) => return Err(e), + }; + + if buf.filled().is_empty() { + break; + } + + len += buf.filled().len() as u64; + writer.write_all(buf.filled())?; + buf.clear(); + } + + Ok(len) +} + +/// Specialization of the read-write loop that reuses the internal +/// buffer of a BufReader. If there's no buffer then the writer side +/// should be used instead. +trait BufferedReaderSpec { + fn buffer_size(&self) -> usize; + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result; +} + +impl BufferedReaderSpec for T +where + Self: Read, + T: ?Sized, +{ + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result { + unreachable!("only called from specializations") + } +} + +impl BufferedReaderSpec for &[u8] { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let len = self.len(); + to.write_all(self)?; + *self = &self[len..]; + Ok(len as u64) + } +} + +#[cfg(feature = "alloc")] +impl BufferedReaderSpec for VecDeque { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let len = self.len(); + let (front, back) = self.as_slices(); + to.write_all(front)?; + to.write_all(back)?; + self.clear(); + Ok(len as u64) + } +} + +impl BufferedReaderSpec for BufReader +where + Self: Read, + I: ?Sized, +{ + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let mut len = 0; + + loop { + // Hack: this relies on `impl Read for BufReader` always calling fill_buf + // if the buffer is empty, even for empty slices. + // It can't be called directly here since specialization prevents us + // from adding I: Read + match self.read(&mut []) { + Ok(_) => {} + Err(e) if e.canonicalize() == Error::Interrupted => continue, + Err(e) => return Err(e), + } + let buf = self.buffer(); + if self.buffer().is_empty() { + return Ok(len); + } + + // In case the writer side is a BufWriter then its write_all + // implements an optimization that passes through large + // buffers to the underlying writer. That code path is #[cold] + // but we're still avoiding redundant memcopies when doing + // a copy between buffered inputs and outputs. + to.write_all(buf)?; + len += buf.len() as u64; + self.discard_buffer(); + } + } +} + +/// Specialization of the read-write loop that either uses a stack buffer +/// or reuses the internal buffer of a BufWriter +trait BufferedWriterSpec: Write { + fn buffer_size(&self) -> usize; + + fn copy_from(&mut self, reader: &mut R) -> Result; +} + +impl BufferedWriterSpec for W { + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_from(&mut self, reader: &mut R) -> Result { + stack_buffer_copy(reader, self) + } +} + +#[cfg(feature = "alloc")] +impl BufferedWriterSpec for Vec { + fn buffer_size(&self) -> usize { + core::cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) + } + + fn copy_from(&mut self, reader: &mut R) -> Result { + reader + .read_to_end(self) + .map(|bytes| u64::try_from(bytes).expect("usize overflowed u64")) + } +} + +impl BufferedWriterSpec for BufWriter { + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_from(&mut self, reader: &mut R) -> Result { + if self.capacity() < DEFAULT_BUF_SIZE { + return stack_buffer_copy(reader, self); + } + + let mut len = 0; + #[cfg(borrowedbuf_init)] + let mut init = 0; + + loop { + let buf = self.buffer_mut(); + let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into(); + + #[cfg(borrowedbuf_init)] + unsafe { + // SAFETY: init is either 0 or the init_len from the previous iteration. + read_buf.set_init(init); + } + + if read_buf.capacity() >= DEFAULT_BUF_SIZE { + let mut cursor = read_buf.unfilled(); + match reader.read_buf(cursor.reborrow()) { + Ok(()) => { + let bytes_read = cursor.written(); + + if bytes_read == 0 { + return Ok(len); + } + + #[cfg(borrowedbuf_init)] + { + init = read_buf.init_len() - bytes_read; + } + len += bytes_read as u64; + + // SAFETY: BorrowedBuf guarantees all of its filled bytes are init + unsafe { buf.set_len(buf.len() + bytes_read) }; + + // Read again if the buffer still has enough capacity, as BufWriter itself + // would do This will occur if the reader returns + // short reads + } + Err(ref e) if e.canonicalize() == Error::Interrupted => {} + Err(e) => return Err(e), + } + } else { + #[cfg(borrowedbuf_init)] + { + // All the bytes that were already in the buffer are initialized, + // treat them as such when the buffer is flushed. + init += buf.len(); + } + + self.flush_buf()?; + } + } + } +} diff --git a/src/utils/cursor.rs b/src/utils/cursor.rs new file mode 100644 index 0000000..468d11f --- /dev/null +++ b/src/utils/cursor.rs @@ -0,0 +1,389 @@ +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, string::String, vec::Vec}; +use core::{cmp, io::BorrowedCursor}; + +use crate::{BufRead, Error, Read, Result, Seek, SeekFrom, Write}; + +/// A `Cursor` wraps an in-memory buffer and provides it with a +/// [`Seek`] implementation. +/// +/// `Cursor`s are used with in-memory buffers, anything implementing +/// [AsRef]<\[u8]>, to allow them to implement [`Read`] and/or [`Write`], +/// allowing these buffers to be used anywhere you might use a reader or writer +/// that does actual I/O. +#[derive(Debug, Default, Eq, PartialEq)] +pub struct Cursor { + inner: T, + pos: u64, +} + +impl Cursor { + /// Creates a new cursor wrapping the provided underlying in-memory buffer. + /// + /// Cursor initial position is `0` even if underlying buffer (e.g., [`Vec`]) + /// is not empty. So writing to cursor starts with overwriting [`Vec`] + /// content, not with appending to it. + pub const fn new(inner: T) -> Cursor { + Cursor { pos: 0, inner } + } + + /// Consumes this cursor, returning the underlying value. + pub fn into_inner(self) -> T { + self.inner + } + + /// Gets a reference to the underlying value in this cursor. + pub const fn get_ref(&self) -> &T { + &self.inner + } + + /// Gets a mutable reference to the underlying value in this cursor. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying value as it may corrupt this cursor's position. + pub const fn get_mut(&mut self) -> &mut T { + &mut self.inner + } + + /// Returns the current position of this cursor. + pub const fn position(&self) -> u64 { + self.pos + } + + /// Sets the position of this cursor. + pub const fn set_position(&mut self, pos: u64) { + self.pos = pos; + } +} + +impl Cursor +where + T: AsRef<[u8]>, +{ + /// Splits the underlying slice at the cursor position and returns them. + pub fn split(&self) -> (&[u8], &[u8]) { + let slice = self.inner.as_ref(); + let pos = self.pos.min(slice.len() as u64); + slice.split_at(pos as usize) + } +} + +impl Cursor +where + T: AsMut<[u8]>, +{ + /// Splits the underlying slice at the cursor position and returns them + /// mutably. + pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) { + let slice = self.inner.as_mut(); + let pos = self.pos.min(slice.len() as u64); + slice.split_at_mut(pos as usize) + } +} + +impl Clone for Cursor +where + T: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Cursor { + inner: self.inner.clone(), + pos: self.pos, + } + } + + #[inline] + fn clone_from(&mut self, other: &Self) { + self.inner.clone_from(&other.inner); + self.pos = other.pos; + } +} + +impl Seek for Cursor +where + T: AsRef<[u8]>, +{ + fn seek(&mut self, style: SeekFrom) -> Result { + let (base_pos, offset) = match style { + SeekFrom::Start(n) => { + self.pos = n; + return Ok(n); + } + SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n), + SeekFrom::Current(n) => (self.pos, n), + }; + match base_pos.checked_add_signed(offset) { + Some(n) => { + self.pos = n; + Ok(self.pos) + } + None => Err(Error::InvalidInput), + } + } + + fn stream_len(&mut self) -> Result { + Ok(self.inner.as_ref().len() as u64) + } + + fn stream_position(&mut self) -> Result { + Ok(self.pos) + } +} + +impl Read for Cursor +where + T: AsRef<[u8]>, +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + let n = Read::read(&mut Cursor::split(self).1, buf)?; + self.pos += n as u64; + Ok(n) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + let result = Read::read_exact(&mut Cursor::split(self).1, buf); + + match result { + Ok(_) => self.pos += buf.len() as u64, + // The only possible error condition is EOF, so place the cursor at "EOF" + Err(_) => self.pos = self.inner.as_ref().len() as u64, + } + + result + } + + fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { + let prev_written = cursor.written(); + + Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?; + + self.pos += (cursor.written() - prev_written) as u64; + + Ok(()) + } + + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { + let prev_written = cursor.written(); + + let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow()); + self.pos += (cursor.written() - prev_written) as u64; + + result + } + + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let content = Cursor::split(self).1; + let len = content.len(); + buf.try_reserve(len).map_err(|_| Error::NoMemory)?; + buf.extend_from_slice(content); + self.pos += len as u64; + + Ok(len) + } + + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + let content = str::from_utf8(Cursor::split(self).1).map_err(|_| Error::IllegalBytes)?; + let len = content.len(); + buf.try_reserve(len).map_err(|_| Error::NoMemory)?; + buf.push_str(content); + self.pos += len as u64; + + Ok(len) + } +} + +impl BufRead for Cursor +where + T: AsRef<[u8]>, +{ + fn fill_buf(&mut self) -> Result<&[u8]> { + Ok(Cursor::split(self).1) + } + + fn consume(&mut self, amt: usize) { + self.pos += amt as u64; + } +} + +fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> Result { + let pos = cmp::min(*pos_mut, slice.len() as u64); + let amt = (&mut slice[(pos as usize)..]).write(buf)?; + *pos_mut += amt as u64; + Ok(amt) +} + +#[inline] +fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> Result<()> { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { + Err(Error::WriteZero) + } else { + Ok(()) + } +} + +/// Reserves the required space, and pads the vec with 0s if necessary. +#[cfg(feature = "alloc")] +fn reserve_and_pad(pos_mut: &mut u64, vec: &mut Vec, buf_len: usize) -> Result { + let pos: usize = (*pos_mut).try_into().map_err(|_| Error::InvalidInput)?; + + // For safety reasons, we don't want these numbers to overflow + // otherwise our allocation won't be enough + let desired_cap = pos.saturating_add(buf_len); + if desired_cap > vec.capacity() { + // We want our vec's total capacity + // to have room for (pos+buf_len) bytes. Reserve allocates + // based on additional elements from the length, so we need to + // reserve the difference + vec.reserve(desired_cap - vec.len()); + } + // Pad if pos is above the current len. + if pos > vec.len() { + let diff = pos - vec.len(); + // Unfortunately, `resize()` would suffice but the optimiser does not + // realise the `reserve` it does can be eliminated. So we do it manually + // to eliminate that extra branch + let spare = vec.spare_capacity_mut(); + debug_assert!(spare.len() >= diff); + // Safety: we have allocated enough capacity for this. + // And we are only writing, not reading + unsafe { + spare + .get_unchecked_mut(..diff) + .fill(core::mem::MaybeUninit::new(0)); + vec.set_len(pos); + } + } + + Ok(pos) +} + +/// Writes the slice to the vec without allocating. +/// +/// # Safety +/// +/// `vec` must have `buf.len()` spare capacity. +#[cfg(feature = "alloc")] +unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize { + debug_assert!(vec.capacity() >= pos + buf.len()); + unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; + pos + buf.len() +} + +/// Resizing `write_all` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the [`Write`] +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that [`Write`] will pad the vec with 0 initially, +/// before writing anything from that point +#[cfg(feature = "alloc")] +fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> Result { + let buf_len = buf.len(); + let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; + + // Write the buf then progress the vec forward if necessary + // Safety: we have ensured that the capacity is available + // and that all bytes get written up to pos + unsafe { + pos = vec_write_all_unchecked(pos, vec, buf); + if pos > vec.len() { + vec.set_len(pos); + } + }; + + // Bump us forward + *pos_mut += buf_len as u64; + Ok(buf_len) +} + +impl Write for Cursor<&mut [u8]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + slice_write(&mut self.pos, self.inner, buf) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + slice_write_all(&mut self.pos, self.inner, buf) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl Write for Cursor<&mut Vec> { + fn write(&mut self, buf: &[u8]) -> Result { + vec_write_all(&mut self.pos, self.inner, buf) + } + + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + vec_write_all(&mut self.pos, self.inner, buf)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl Write for Cursor> { + fn write(&mut self, buf: &[u8]) -> Result { + vec_write_all(&mut self.pos, &mut self.inner, buf) + } + + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + vec_write_all(&mut self.pos, &mut self.inner, buf)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl Write for Cursor> { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + slice_write(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + slice_write_all(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +impl Write for Cursor<[u8; N]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + slice_write(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + slice_write_all(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/src/utils/empty.rs b/src/utils/empty.rs new file mode 100644 index 0000000..6181454 --- /dev/null +++ b/src/utils/empty.rs @@ -0,0 +1,157 @@ +#[cfg(feature = "alloc")] +use alloc::{string::String, vec::Vec}; +use core::{fmt, io::BorrowedCursor}; + +use crate::{BufRead, Error, Read, Result, Seek, SeekFrom, Write}; + +/// `Empty` ignores any data written via [`Write`], and will always be empty +/// (returning zero bytes) when read via [`Read`]. +/// +/// This struct is generally created by calling [`empty()`]. Please +/// see the documentation of [`empty()`] for more details. +#[non_exhaustive] +#[derive(Copy, Clone, Debug, Default)] +pub struct Empty; + +/// Creates a value that is always at EOF for reads, and ignores all data written. +/// +/// See [`std::io::empty()`] for more details. +#[must_use] +pub const fn empty() -> Empty { + Empty +} + +impl Read for Empty { + #[inline] + fn read(&mut self, _buf: &mut [u8]) -> Result { + Ok(0) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + if !buf.is_empty() { + Err(Error::UnexpectedEof) + } else { + Ok(()) + } + } + + #[inline] + fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> Result<()> { + Ok(()) + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + if cursor.capacity() != 0 { + Err(Error::UnexpectedEof) + } else { + Ok(()) + } + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, _buf: &mut Vec) -> Result { + Ok(0) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, _buf: &mut String) -> Result { + Ok(0) + } +} + +impl BufRead for Empty { + #[inline] + fn fill_buf(&mut self) -> Result<&[u8]> { + Ok(&[]) + } + + #[inline] + fn consume(&mut self, _n: usize) {} + + #[inline] + fn has_data_left(&mut self) -> Result { + Ok(false) + } + + #[inline] + fn skip_until(&mut self, _byte: u8) -> Result { + Ok(0) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_until(&mut self, _byte: u8, _buf: &mut Vec) -> Result { + Ok(0) + } + + #[inline] + #[cfg(feature = "alloc")] + fn read_line(&mut self, _buf: &mut String) -> Result { + Ok(0) + } +} + +impl Seek for Empty { + #[inline] + fn seek(&mut self, _pos: SeekFrom) -> Result { + Ok(0) + } + + #[inline] + fn stream_len(&mut self) -> Result { + Ok(0) + } + + #[inline] + fn stream_position(&mut self) -> Result { + Ok(0) + } +} + +impl Write for Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + Ok(buf.len()) + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +impl Write for &Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + Ok(buf.len()) + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..ed83006 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,9 @@ +mod chain; +mod copy; +mod cursor; +mod empty; +mod repeat; +mod sink; +mod take; + +pub use self::{chain::*, copy::*, cursor::*, empty::*, repeat::*, sink::*, take::*}; diff --git a/src/utils/repeat.rs b/src/utils/repeat.rs new file mode 100644 index 0000000..c59abaa --- /dev/null +++ b/src/utils/repeat.rs @@ -0,0 +1,75 @@ +#[cfg(feature = "alloc")] +use alloc::{string::String, vec::Vec}; +use core::{fmt, io::BorrowedCursor}; + +use crate::{Read, Result}; + +/// A reader which yields one byte over and over and over and over and over and... +/// +/// This struct is generally created by calling [`repeat()`]. Please +/// see the documentation of [`repeat()`] for more details. +pub struct Repeat { + byte: u8, +} + +/// Creates an instance of a reader that infinitely repeats one byte. +/// +/// All reads from this reader will succeed by filling the specified buffer with +/// the given byte. +/// +/// See [`std::io::repeat()`] for more details. +#[must_use] +pub const fn repeat(byte: u8) -> Repeat { + Repeat { byte } +} + +impl Read for Repeat { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + buf.fill(self.byte); + Ok(buf.len()) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + buf.fill(self.byte); + Ok(()) + } + + #[inline] + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + // SAFETY: No uninit bytes are being written. + unsafe { buf.as_mut() }.write_filled(self.byte); + // SAFETY: the entire unfilled portion of buf has been initialized. + unsafe { + #[cfg(borrowedbuf_init)] + buf.advance_unchecked(buf.capacity()); + #[cfg(not(borrowedbuf_init))] + buf.advance(buf.capacity()); + }; + Ok(()) + } + + #[inline] + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + self.read_buf(buf) + } + + /// This function is not supported by `Repeat`, because there's no end of its data + #[cfg(feature = "alloc")] + fn read_to_end(&mut self, _: &mut Vec) -> Result { + Err(crate::Error::NoMemory) + } + + /// This function is not supported by `Repeat`, because there's no end of its data + #[cfg(feature = "alloc")] + fn read_to_string(&mut self, _: &mut String) -> Result { + Err(crate::Error::NoMemory) + } +} + +impl fmt::Debug for Repeat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Repeat").finish_non_exhaustive() + } +} diff --git a/src/utils/sink.rs b/src/utils/sink.rs new file mode 100644 index 0000000..a3817a0 --- /dev/null +++ b/src/utils/sink.rs @@ -0,0 +1,61 @@ +use core::fmt; + +use crate::{Result, Write}; + +/// A writer which will move data into the void. +/// +/// This struct is generally created by calling [`sink()`]. Please +/// see the documentation of [`sink()`] for more details. +#[non_exhaustive] +#[derive(Copy, Clone, Debug, Default)] +pub struct Sink; + +/// Creates an instance of a writer which will successfully consume all data. +#[must_use] +pub const fn sink() -> Sink { + Sink +} + +impl Write for Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + Ok(buf.len()) + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +impl Write for &Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + Ok(buf.len()) + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/src/utils/take.rs b/src/utils/take.rs new file mode 100644 index 0000000..2b64d7d --- /dev/null +++ b/src/utils/take.rs @@ -0,0 +1,216 @@ +use core::{ + cmp, + io::{BorrowedBuf, BorrowedCursor}, +}; + +use crate::{BufRead, Error, Read, Result, Seek, SeekFrom}; + +/// Reader adapter which limits the bytes read from an underlying reader. +/// +/// This struct is generally created by calling [`take`] on a reader. +/// Please see the documentation of [`take`] for more details. +/// +/// See [`std::io::Take`] for more details. +/// +/// [`take`]: Read::take +#[derive(Debug)] +pub struct Take { + inner: T, + len: u64, + limit: u64, +} + +impl Take { + pub(crate) fn new(inner: T, limit: u64) -> Self { + Take { + inner, + len: limit, + limit, + } + } + + /// Returns the number of bytes that can be read before this instance will + /// return EOF. + pub fn limit(&self) -> u64 { + self.limit + } + + /// Returns the number of bytes read so far. + pub fn position(&self) -> u64 { + self.len - self.limit + } + + /// Sets the number of bytes that can be read before this instance will + /// return EOF. This is the same as constructing a new `Take` instance, so + /// the amount of bytes read and the previous limit value don't matter when + /// calling this method. + pub fn set_limit(&mut self, limit: u64) { + self.len = limit; + self.limit = limit; + } + + /// Consumes the `Take`, returning the wrapped reader. + pub fn into_inner(self) -> T { + self.inner + } + + /// Gets a reference to the underlying reader. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying reader as doing so may corrupt the internal limit of this + /// `Take`. + pub fn get_ref(&self) -> &T { + &self.inner + } + + /// Gets a mutable reference to the underlying reader. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying reader as doing so may corrupt the internal limit of this + /// `Take`. + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl Read for Take { + fn read(&mut self, buf: &mut [u8]) -> Result { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(0); + } + + let max = cmp::min(buf.len() as u64, self.limit) as usize; + let n = self.inner.read(&mut buf[..max])?; + assert!(n as u64 <= self.limit, "number of read bytes exceeds limit"); + self.limit -= n as u64; + Ok(n) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(()); + } + + if self.limit < buf.capacity() as u64 { + // The condition above guarantees that `self.limit` fits in `usize`. + let limit = self.limit as usize; + + #[cfg(borrowedbuf_init)] + let extra_init = cmp::min(limit, buf.init_mut().len()); + + // SAFETY: no uninit data is written to ibuf + let ibuf = unsafe { &mut buf.as_mut()[..limit] }; + + let mut sliced_buf: BorrowedBuf<'_> = ibuf.into(); + + #[cfg(borrowedbuf_init)] + // SAFETY: extra_init bytes of ibuf are known to be initialized + unsafe { + sliced_buf.set_init(extra_init); + } + + let mut cursor = sliced_buf.unfilled(); + let result = self.inner.read_buf(cursor.reborrow()); + + #[cfg(borrowedbuf_init)] + let new_init = cursor.init_mut().len(); + let filled = sliced_buf.len(); + + // cursor / sliced_buf / ibuf must drop here + + #[cfg(borrowedbuf_init)] + unsafe { + // SAFETY: filled bytes have been filled and therefore initialized + buf.advance_unchecked(filled); + // SAFETY: new_init bytes of buf's unfilled buffer have been initialized + buf.set_init(new_init); + } + #[cfg(not(borrowedbuf_init))] + // SAFETY: filled bytes have been filled and therefore initialized + unsafe { + buf.advance(filled); + } + + self.limit -= filled as u64; + + result + } else { + let written = buf.written(); + let result = self.inner.read_buf(buf.reborrow()); + self.limit -= (buf.written() - written) as u64; + result + } + } +} + +impl BufRead for Take { + fn fill_buf(&mut self) -> Result<&[u8]> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(&[]); + } + + let buf = self.inner.fill_buf()?; + let cap = cmp::min(buf.len() as u64, self.limit) as usize; + Ok(&buf[..cap]) + } + + fn consume(&mut self, amt: usize) { + // Don't let callers reset the limit by passing an overlarge value + let amt = cmp::min(amt as u64, self.limit) as usize; + self.limit -= amt as u64; + self.inner.consume(amt); + } +} + +impl Seek for Take { + fn seek(&mut self, pos: SeekFrom) -> Result { + let new_position = match pos { + SeekFrom::Start(v) => Some(v), + SeekFrom::Current(v) => self.position().checked_add_signed(v), + SeekFrom::End(v) => self.len.checked_add_signed(v), + }; + let new_position = match new_position { + Some(v) if v <= self.len => v, + _ => return Err(Error::InvalidInput), + }; + while new_position != self.position() { + if let Some(offset) = new_position.checked_signed_diff(self.position()) { + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + break; + } + let offset = if new_position > self.position() { + i64::MAX + } else { + i64::MIN + }; + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + } + Ok(new_position) + } + + fn stream_len(&mut self) -> Result { + Ok(self.len) + } + + fn stream_position(&mut self) -> Result { + Ok(self.position()) + } + + fn seek_relative(&mut self, offset: i64) -> Result<()> { + if self + .position() + .checked_add_signed(offset) + .is_none_or(|p| p > self.len) + { + return Err(Error::InvalidInput); + } + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + Ok(()) + } +} diff --git a/src/write/impls.rs b/src/write/impls.rs new file mode 100644 index 0000000..5da75f5 --- /dev/null +++ b/src/write/impls.rs @@ -0,0 +1,144 @@ +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, collections::VecDeque, vec::Vec}; +use core::{cmp, fmt, io::BorrowedCursor, mem}; + +use crate::{Error, Result, Write}; + +// ============================================================================= +// Forwarding implementations + +impl Write for &mut W { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + (**self).write(buf) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { + (**self).write_fmt(fmt) + } +} + +#[cfg(feature = "alloc")] +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + (**self).write(buf) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { + (**self).write_fmt(fmt) + } +} + +// ============================================================================= +// In-memory buffer implementations + +impl Write for &mut [u8] { + #[inline] + fn write(&mut self, data: &[u8]) -> Result { + let amt = cmp::min(data.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&data[..amt]); + *self = b; + Ok(amt) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_all(&mut self, data: &[u8]) -> Result<()> { + if self.write(data)? < data.len() { + Err(Error::WriteZero) + } else { + Ok(()) + } + } +} + +#[cfg(feature = "alloc")] +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + self.extend_from_slice(buf); + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl Write for VecDeque { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.extend(buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + self.extend(buf); + Ok(()) + } +} + +impl Write for BorrowedCursor<'_> { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + let amt = cmp::min(buf.len(), self.capacity()); + self.append(&buf[..amt]); + Ok(amt) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + if self.write(buf)? < buf.len() { + Err(Error::WriteZero) + } else { + Ok(()) + } + } +} diff --git a/src/write/mod.rs b/src/write/mod.rs new file mode 100644 index 0000000..1310208 --- /dev/null +++ b/src/write/mod.rs @@ -0,0 +1,94 @@ +use core::fmt; + +use crate::{Error, Result}; + +mod impls; + +pub(crate) fn default_write_fmt( + this: &mut W, + args: fmt::Arguments<'_>, +) -> Result<()> { + // Create a shim which translates a `Write` to a `fmt::Write` and saves off + // I/O errors, instead of discarding them. + struct Adapter<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { + inner: this, + error: Ok(()), + }; + match fmt::write(&mut output, args) { + Ok(()) => Ok(()), + Err(..) => { + // Check whether the error came from the underlying `Write`. + if output.error.is_err() { + output.error + } else { + // This shouldn't happen: the underlying stream did not error, + // but somehow the formatter still errored? + panic!( + "a formatting trait implementation returned an error when the underlying \ + stream did not" + ); + } + } + } +} + +/// A trait for objects which are byte-oriented sinks. +/// +/// See [`std::io::Write`] for more details. +pub trait Write { + /// Write a buffer into this writer, returning how many bytes were written. + fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this writer. + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => return Err(Error::WriteZero), + Ok(n) => buf = &buf[n..], + Err(e) if e.canonicalize() == Error::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { + // NOTE: std's implementation uses an internal method + // `as_statically_known_str` to optimize. + default_write_fmt(self, args) + } + + /// Creates a "by reference" adapter for this instance of `Write`. + /// + /// The returned adapter also implements `Write` and will simply borrow this + /// current writer. + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} diff --git a/tests/buffered.rs b/tests/buffered.rs new file mode 100644 index 0000000..5098ed4 --- /dev/null +++ b/tests/buffered.rs @@ -0,0 +1,943 @@ +#![feature(test)] +#![feature(core_io_borrowed_buf)] +#![allow(unused)] + +extern crate test; + +use std::{ + io::BorrowedBuf, + mem::MaybeUninit, + panic, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use axio::{ + BufReader, BufWriter, Cursor, Error, LineWriter, Result, SeekFrom, empty, prelude::*, sink, +}; + +/// A dummy reader intended at testing short-reads propagation. +pub struct ShortReader { + lengths: Vec, +} + +impl Read for ShortReader { + fn read(&mut self, _: &mut [u8]) -> Result { + if self.lengths.is_empty() { + Ok(0) + } else { + Ok(self.lengths.remove(0)) + } + } +} + +#[test] +fn test_buffered_reader() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 3); + assert_eq!(buf, [5, 6, 7]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 2); + assert_eq!(buf, [0, 1]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + assert_eq!(buf, [2]); + assert_eq!(reader.buffer(), [3]); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + assert_eq!(buf, [3, 0, 0]); + assert_eq!(reader.buffer(), []); + + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + assert_eq!(buf, [4, 0, 0]); + assert_eq!(reader.buffer(), []); + + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_buffered_reader_read_buf() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 3]; + let mut buf: BorrowedBuf<'_> = buf.into(); + + reader.read_buf(buf.unfilled()).unwrap(); + + assert_eq!(buf.filled(), [5, 6, 7]); + assert_eq!(reader.buffer(), []); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 2]; + let mut buf: BorrowedBuf<'_> = buf.into(); + + reader.read_buf(buf.unfilled()).unwrap(); + + assert_eq!(buf.filled(), [0, 1]); + assert_eq!(reader.buffer(), []); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1]; + let mut buf: BorrowedBuf<'_> = buf.into(); + + reader.read_buf(buf.unfilled()).unwrap(); + + assert_eq!(buf.filled(), [2]); + assert_eq!(reader.buffer(), [3]); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 3]; + let mut buf: BorrowedBuf<'_> = buf.into(); + + reader.read_buf(buf.unfilled()).unwrap(); + + assert_eq!(buf.filled(), [3]); + assert_eq!(reader.buffer(), []); + + reader.read_buf(buf.unfilled()).unwrap(); + + assert_eq!(buf.filled(), [3, 4]); + assert_eq!(reader.buffer(), []); + + buf.clear(); + + reader.read_buf(buf.unfilled()).unwrap(); + + assert!(buf.filled().is_empty()); +} + +#[test] +fn test_buffered_reader_seek() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, Cursor::new(inner)); + + assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert_eq!(reader.stream_position().ok(), Some(3)); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4)); + assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..])); + reader.consume(1); + assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3)); +} + +#[test] +fn test_buffered_reader_seek_relative() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, Cursor::new(inner)); + + assert!(reader.seek_relative(3).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(0).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[1][..])); + assert!(reader.seek_relative(-1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(2).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..])); +} + +#[test] +fn test_buffered_reader_stream_position() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, Cursor::new(inner)); + + assert_eq!(reader.stream_position().ok(), Some(0)); + assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); + assert_eq!(reader.stream_position().ok(), Some(3)); + // relative seeking within the buffer and reading position should keep the buffer + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(0).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(3)); + assert_eq!(reader.buffer(), &[0, 1][..]); + assert!(reader.seek_relative(1).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(4)); + assert_eq!(reader.buffer(), &[1][..]); + assert!(reader.seek_relative(-1).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(3)); + assert_eq!(reader.buffer(), &[0, 1][..]); + // relative seeking outside the buffer will discard it + assert!(reader.seek_relative(2).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(5)); + assert_eq!(reader.buffer(), &[][..]); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_buffered_reader_stream_position_panic() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(4, Cursor::new(inner)); + + // cause internal buffer to be filled but read only partially + let mut buffer = [0, 0]; + assert!(reader.read_exact(&mut buffer).is_ok()); + // rewinding the internal reader will cause buffer to loose sync + let inner = reader.get_mut(); + assert!(inner.seek(SeekFrom::Start(0)).is_ok()); + // overflow when subtracting the remaining buffer size from current position + let result = panic::catch_unwind(panic::AssertUnwindSafe(|| reader.stream_position().ok())); + assert!(result.is_err()); +} + +#[test] +fn test_buffered_reader_invalidated_after_read() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(3, Cursor::new(inner)); + + assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); + reader.consume(3); + + let mut buffer = [0, 0, 0, 0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(5)); + assert_eq!(buffer, [0, 1, 2, 3, 4]); + + assert!(reader.seek_relative(-2).is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(2)); + assert_eq!(buffer, [3, 4]); +} + +#[test] +fn test_buffered_reader_invalidated_after_seek() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(3, Cursor::new(inner)); + + assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); + reader.consume(3); + + assert!(reader.seek(SeekFrom::Current(5)).is_ok()); + + assert!(reader.seek_relative(-2).is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(2)); + assert_eq!(buffer, [3, 4]); +} + +#[test] +fn test_buffered_reader_seek_underflow() { + // gimmick reader that yields its position modulo 256 for each byte + struct PositionReader { + pos: u64, + } + impl Read for PositionReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + let len = buf.len(); + for x in buf { + *x = self.pos as u8; + self.pos = self.pos.wrapping_add(1); + } + Ok(len) + } + } + // note: this implementation of `Seek` is "broken" due to position + // wrapping, so calling `reader.seek(Current(0))` is semantically different + // than `reader.stream_position()` + impl Seek for PositionReader { + fn seek(&mut self, pos: SeekFrom) -> Result { + match pos { + SeekFrom::Start(n) => { + self.pos = n; + } + SeekFrom::Current(n) => { + self.pos = self.pos.wrapping_add(n as u64); + } + SeekFrom::End(n) => { + self.pos = u64::MAX.wrapping_add(n as u64); + } + } + Ok(self.pos) + } + } + + let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 }); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..])); + assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::MAX - 5)); + assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); + // the following seek will require two underlying seeks + let expected = 9223372036854775802; + assert_eq!( + reader.seek(SeekFrom::Current(i64::MIN)).ok(), + Some(expected) + ); + assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); + // seeking to 0 should empty the buffer. + assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected)); + assert_eq!(reader.get_ref().pos, expected); +} + +#[test] +fn test_buffered_reader_seek_underflow_discard_buffer_between_seeks() { + // gimmick reader that returns Err after first seek + struct ErrAfterFirstSeekReader { + first_seek: bool, + } + impl Read for ErrAfterFirstSeekReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + for x in &mut *buf { + *x = 0; + } + Ok(buf.len()) + } + } + impl Seek for ErrAfterFirstSeekReader { + fn seek(&mut self, _: SeekFrom) -> Result { + if self.first_seek { + self.first_seek = false; + Ok(0) + } else { + Err(Error::Io) + } + } + } + + let mut reader = BufReader::with_capacity(5, ErrAfterFirstSeekReader { first_seek: true }); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 0, 0, 0, 0][..])); + + // The following seek will require two underlying seeks. The first will + // succeed but the second will fail. This should still invalidate the + // buffer. + assert!(reader.seek(SeekFrom::Current(i64::MIN)).is_err()); + assert_eq!(reader.buffer().len(), 0); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_buffered_reader_read_to_end_consumes_buffer() { + let data: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = BufReader::with_capacity(3, data); + let mut buf = Vec::new(); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2][..])); + assert_eq!(reader.read_to_end(&mut buf).ok(), Some(8)); + assert_eq!(&buf, &[0, 1, 2, 3, 4, 5, 6, 7]); + assert!(reader.buffer().is_empty()); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_buffered_reader_read_to_string_consumes_buffer() { + let data: &[u8] = "deadbeef".as_bytes(); + let mut reader = BufReader::with_capacity(3, data); + let mut buf = String::new(); + assert_eq!(reader.fill_buf().ok(), Some("dea".as_bytes())); + assert_eq!(reader.read_to_string(&mut buf).ok(), Some(8)); + assert_eq!(&buf, "deadbeef"); + assert!(reader.buffer().is_empty()); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_buffered_writer() { + let inner = Vec::new(); + let mut writer = BufWriter::with_capacity(2, inner); + + writer.write(&[0, 1]).unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[2]).unwrap(); + assert_eq!(writer.buffer(), [2]); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[3]).unwrap(); + assert_eq!(writer.buffer(), [2, 3]); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.flush().unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[4]).unwrap(); + writer.write(&[5]).unwrap(); + assert_eq!(writer.buffer(), [4, 5]); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[6]).unwrap(); + assert_eq!(writer.buffer(), [6]); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); + + writer.write(&[7, 8]).unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); + + writer.write(&[9, 10, 11]).unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + + writer.flush().unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_buffered_writer_inner_flushes() { + let mut w = BufWriter::with_capacity(3, Vec::new()); + w.write(&[0, 1]).unwrap(); + assert_eq!(*w.get_ref(), []); + let w = w.into_inner().unwrap(); + assert_eq!(w, [0, 1]); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_buffered_writer_seek() { + let mut w = BufWriter::with_capacity(3, Cursor::new(Vec::new())); + w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap(); + w.write_all(&[6, 7]).unwrap(); + assert_eq!(w.stream_position().ok(), Some(8)); + assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); + assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2)); + w.write_all(&[8, 9]).unwrap(); + assert_eq!( + &w.into_inner().unwrap().into_inner()[..], + &[0, 1, 8, 9, 4, 5, 6, 7] + ); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_read_until() { + let inner: &[u8] = &[0, 1, 2, 1, 0]; + let mut reader = BufReader::with_capacity(2, inner); + let mut v = Vec::new(); + reader.read_until(0, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(2, &mut v).unwrap(); + assert_eq!(v, [1, 2]); + v.truncate(0); + reader.read_until(1, &mut v).unwrap(); + assert_eq!(v, [1]); + v.truncate(0); + reader.read_until(8, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(9, &mut v).unwrap(); + assert_eq!(v, []); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_line_buffer() { + let mut writer = LineWriter::new(Vec::new()); + writer.write(&[0]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.write(&[1]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); + writer.write(&[3, b'\n']).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_read_line() { + let in_buf: &[u8] = b"a\nb\nc"; + let mut reader = BufReader::with_capacity(2, in_buf); + let mut s = String::new(); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "a\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "b\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "c"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, ""); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_lines() { + let in_buf: &[u8] = b"a\nb\nc"; + let reader = BufReader::with_capacity(2, in_buf); + let mut it = reader.lines(); + assert_eq!(it.next().unwrap().unwrap(), "a".to_string()); + assert_eq!(it.next().unwrap().unwrap(), "b".to_string()); + assert_eq!(it.next().unwrap().unwrap(), "c".to_string()); + assert!(it.next().is_none()); +} + +#[test] +fn test_short_reads() { + let inner = ShortReader { + lengths: vec![0, 1, 2, 0, 1, 0], + }; + let mut reader = BufReader::new(inner); + let mut buf = [0, 0]; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.read(&mut buf).unwrap(), 2); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +#[should_panic] +fn dont_panic_in_drop_on_panicked_flush() { + struct FailFlushWriter; + + impl Write for FailFlushWriter { + fn write(&mut self, buf: &[u8]) -> Result { + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Err(Error::Io) + } + } + + let writer = FailFlushWriter; + let _writer = BufWriter::new(writer); + + // If writer panics *again* due to the flush error then the process will + // abort. + panic!(); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn panic_in_write_doesnt_flush_in_drop() { + static WRITES: AtomicUsize = AtomicUsize::new(0); + + struct PanicWriter; + + impl Write for PanicWriter { + fn write(&mut self, _: &[u8]) -> Result { + WRITES.fetch_add(1, Ordering::SeqCst); + panic!(); + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } + } + + std::thread::spawn(|| { + let mut writer = BufWriter::new(PanicWriter); + let _ = writer.write(b"hello world"); + let _ = writer.flush(); + }) + .join() + .unwrap_err(); + + assert_eq!(WRITES.load(Ordering::SeqCst), 1); +} + +#[bench] +fn bench_buffered_reader(b: &mut test::Bencher) { + b.iter(|| BufReader::new(empty())); +} + +#[bench] +fn bench_buffered_reader_small_reads(b: &mut test::Bencher) { + let data = (0..u8::MAX).cycle().take(1024 * 4).collect::>(); + b.iter(|| { + let mut reader = BufReader::new(&data[..]); + let mut buf = [0u8; 4]; + for _ in 0..1024 { + reader.read_exact(&mut buf).unwrap(); + core::hint::black_box(&buf); + } + }); +} + +#[bench] +fn bench_buffered_writer(b: &mut test::Bencher) { + b.iter(|| BufWriter::new(sink())); +} + +/// A simple `Write` target, designed to be wrapped by `LineWriter` / +/// `BufWriter` / etc, that can have its `write` & `flush` behavior +/// configured +#[derive(Default, Clone)] +struct ProgrammableSink { + // Writes append to this slice + pub buffer: Vec, + + // If true, writes will always be an error + pub always_write_error: bool, + + // If true, flushes will always be an error + pub always_flush_error: bool, + + // If set, only up to this number of bytes will be written in a single + // call to `write` + pub accept_prefix: Option, + + // If set, counts down with each write, and writes return an error + // when it hits 0 + pub max_writes: Option, + + // If set, attempting to write when max_writes == Some(0) will be an + // error; otherwise, it will return Ok(0). + pub error_after_max_writes: bool, +} + +impl Write for ProgrammableSink { + fn write(&mut self, data: &[u8]) -> Result { + if self.always_write_error { + return Err(Error::Io); + } + + match self.max_writes { + Some(0) if self.error_after_max_writes => { + return Err(Error::Io); + } + Some(0) => return Ok(0), + Some(ref mut count) => *count -= 1, + None => {} + } + + let len = match self.accept_prefix { + None => data.len(), + Some(prefix) => data.len().min(prefix), + }; + + let data = &data[..len]; + self.buffer.extend_from_slice(data); + + Ok(len) + } + + fn flush(&mut self) -> Result<()> { + if self.always_flush_error { + Err(Error::Io) + } else { + Ok(()) + } + } +} + +/// Previously the `LineWriter` could successfully write some bytes but +/// then fail to report that it has done so. Additionally, an erroneous +/// flush after a successful write was permanently ignored. +/// +/// Test that a line writer correctly reports the number of written bytes, +/// and that it attempts to flush buffered lines from previous writes +/// before processing new data +/// +/// Regression test for #37807 +#[test] +fn erroneous_flush_retried() { + let writer = ProgrammableSink { + // Only write up to 4 bytes at a time + accept_prefix: Some(4), + + // Accept the first two writes, then error the others + max_writes: Some(2), + error_after_max_writes: true, + + ..Default::default() + }; + + // This should write the first 4 bytes. The rest will be buffered, out + // to the last newline. + let mut writer = LineWriter::new(writer); + assert_eq!(writer.write(b"a\nb\nc\nd\ne").unwrap(), 8); + + // This write should attempt to flush "c\nd\n", then buffer "e". No + // errors should happen here because no further writes should be + // attempted against `writer`. + assert_eq!(writer.write(b"e").unwrap(), 1); + assert_eq!(&writer.get_ref().buffer, b"a\nb\nc\nd\n"); +} + +/// Test that, given this input: +/// +/// Line 1\n +/// Line 2\n +/// Line 3\n +/// Line 4 +/// +/// And given a result that only writes to midway through Line 2 +/// +/// That only up to the end of Line 3 is buffered +/// +/// This behavior is desirable because it prevents flushing partial lines +#[test] +fn partial_write_buffers_line() { + let writer = ProgrammableSink { + accept_prefix: Some(13), + ..Default::default() + }; + let mut writer = LineWriter::new(writer); + + assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3\nLine4").unwrap(), 21); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2"); + + assert_eq!(writer.write(b"Line 4").unwrap(), 6); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n"); +} + +/// Test that, given this input: +/// +/// Line 1\n +/// Line 2\n +/// Line 3 +/// +/// And given that the full write of lines 1 and 2 was successful +/// That data up to Line 3 is buffered +#[test] +fn partial_line_buffered_after_line_write() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::new(writer); + + assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3").unwrap(), 20); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\n"); + + assert!(writer.flush().is_ok()); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3"); +} + +/// Test that for calls to LineBuffer::write where the passed bytes do not contain +/// a newline and on their own are greater in length than the internal buffer, the +/// passed bytes are immediately written to the inner writer. +#[cfg(feature = "alloc")] +#[test] +fn long_line_flushed() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::with_capacity(5, writer); + + assert_eq!(writer.write(b"0123456789").unwrap(), 10); + assert_eq!(&writer.get_ref().buffer, b"0123456789"); +} + +/// Test that, given a very long partial line *after* successfully +/// flushing a complete line, no additional writes take place. This assures +/// the property that `write` should make at-most-one attempt to write +/// new data. +#[cfg(feature = "alloc")] +#[test] +fn line_long_tail_not_flushed() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::with_capacity(5, writer); + + // Assert that Line 1\n is flushed and the long tail isn't. + let bytes = b"Line 1\n0123456789"; + writer.write(bytes).unwrap(); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); +} + +// Test that appending to a full buffer emits a single write, flushing the buffer. +#[cfg(feature = "alloc")] +#[test] +fn line_full_buffer_flushed() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::with_capacity(5, writer); + assert_eq!(writer.write(b"01234").unwrap(), 5); + + // Because the buffer is full, this subsequent write will flush it + assert_eq!(writer.write(b"5").unwrap(), 1); + assert_eq!(&writer.get_ref().buffer, b"01234"); +} + +/// Test that, if an attempt to pre-flush buffered data returns Ok(0), +/// this is propagated as an error. +#[test] +fn line_buffer_write0_error() { + let writer = ProgrammableSink { + // Accept one write, then return Ok(0) on subsequent ones + max_writes: Some(1), + + ..Default::default() + }; + let mut writer = LineWriter::new(writer); + + // This should write "Line 1\n" and buffer "Partial" + assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); + + // This will attempt to flush "partial", which will return Ok(0), which + // needs to be an error, because we've already informed the client + // that we accepted the write. + let err = writer.write(b" Line End\n").unwrap_err(); + assert_eq!(err, Error::WriteZero); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); +} + +/// Test that, if a write returns Ok(0) after a successful pre-flush, this +/// is propagated as Ok(0) +#[test] +fn line_buffer_write0_normal() { + let writer = ProgrammableSink { + // Accept two writes, then return Ok(0) on subsequent ones + max_writes: Some(2), + + ..Default::default() + }; + let mut writer = LineWriter::new(writer); + + // This should write "Line 1\n" and buffer "Partial" + assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); + + // This will flush partial, which will succeed, but then return Ok(0) + // when flushing " Line End\n" + assert_eq!(writer.write(b" Line End\n").unwrap(), 0); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nPartial"); +} + +/// LineWriter has a custom `write_all`; make sure it works correctly +#[test] +fn line_write_all() { + let writer = ProgrammableSink { + // Only write 5 bytes at a time + accept_prefix: Some(5), + ..Default::default() + }; + let mut writer = LineWriter::new(writer); + + writer + .write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial") + .unwrap(); + assert_eq!( + &writer.get_ref().buffer, + b"Line 1\nLine 2\nLine 3\nLine 4\n" + ); + writer.write_all(b" Line 5\n").unwrap(); + assert_eq!( + writer.get_ref().buffer.as_slice(), + b"Line 1\nLine 2\nLine 3\nLine 4\nPartial Line 5\n".as_ref(), + ); +} + +#[test] +fn line_write_all_error() { + let writer = ProgrammableSink { + // Only accept up to 3 writes of up to 5 bytes each + accept_prefix: Some(5), + max_writes: Some(3), + ..Default::default() + }; + + let mut writer = LineWriter::new(writer); + let res = writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial"); + assert!(res.is_err()); + // An error from write_all leaves everything in an indeterminate state, + // so there's nothing else to test here +} + +/// Under certain circumstances, the old implementation of LineWriter +/// would try to buffer "to the last newline" but be forced to buffer +/// less than that, leading to inappropriate partial line writes. +/// Regression test for that issue. +#[cfg(feature = "alloc")] +#[test] +fn partial_multiline_buffering() { + let writer = ProgrammableSink { + // Write only up to 5 bytes at a time + accept_prefix: Some(5), + ..Default::default() + }; + + let mut writer = LineWriter::with_capacity(10, writer); + + let content = b"AAAAABBBBB\nCCCCDDDDDD\nEEE"; + + // When content is written, LineWriter will try to write blocks A, B, + // C, and D. Only block A will succeed. Under the old behavior, LineWriter + // would then try to buffer B, C and D, but because its capacity is 10, + // it will only be able to buffer B and C. We don't want to buffer + // partial lines concurrent with whole lines, so the correct behavior + // is to buffer only block B (out to the newline) + assert_eq!(writer.write(content).unwrap(), 11); + assert_eq!(writer.get_ref().buffer, *b"AAAAA"); + + writer.flush().unwrap(); + assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB\n"); +} + +/// Same as test_partial_multiline_buffering, but in the event NO full lines +/// fit in the buffer, just buffer as much as possible +#[cfg(feature = "alloc")] +#[test] +fn partial_multiline_buffering_without_full_line() { + let writer = ProgrammableSink { + // Write only up to 5 bytes at a time + accept_prefix: Some(5), + ..Default::default() + }; + + let mut writer = LineWriter::with_capacity(5, writer); + + let content = b"AAAAABBBBBBBBBB\nCCCCC\nDDDDD"; + + // When content is written, LineWriter will try to write blocks A, B, + // and C. Only block A will succeed. Under the old behavior, LineWriter + // would then try to buffer B and C, but because its capacity is 5, + // it will only be able to buffer part of B. Because it's not possible + // for it to buffer any complete lines, it should buffer as much of B as + // possible + assert_eq!(writer.write(content).unwrap(), 10); + assert_eq!(writer.get_ref().buffer, *b"AAAAA"); + + writer.flush().unwrap(); + assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB"); +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum RecordedEvent { + Write(String), + Flush, +} + +#[derive(Debug, Clone, Default)] +struct WriteRecorder { + pub events: Vec, +} + +impl Write for WriteRecorder { + fn write(&mut self, buf: &[u8]) -> Result { + self.events.push(RecordedEvent::Write( + str::from_utf8(buf).unwrap().to_string(), + )); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + self.events.push(RecordedEvent::Flush); + Ok(()) + } +} + +/// Test that a normal, formatted writeln only results in a single write +/// call to the underlying writer. A naive implementation of +/// LineWriter::write_all results in two writes: one of the buffered data, +/// and another of the final substring in the formatted set +#[test] +fn single_formatted_write() { + let writer = WriteRecorder::default(); + let mut writer = LineWriter::new(writer); + + // Under a naive implementation of LineWriter, this will result in two + // writes: "hello, world" and "!\n", because write() has to flush the + // buffer before attempting to write the last "!\n". write_all shouldn't + // have this limitation. + #[allow(clippy::write_literal)] + writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap(); + assert_eq!( + writer.get_ref().events, + [RecordedEvent::Write("hello, world!\n".to_string())] + ); +} diff --git a/tests/copy.rs b/tests/copy.rs new file mode 100644 index 0000000..50dcf4b --- /dev/null +++ b/tests/copy.rs @@ -0,0 +1,145 @@ +#![allow(unused)] + +use std::{ + cmp::{max, min}, + collections::VecDeque, +}; + +use axio::{ + BufReader, BufWriter, Cursor, DEFAULT_BUF_SIZE, Result, copy, prelude::*, repeat, sink, +}; + +#[test] +fn copy_copies() { + let mut r = repeat(0).take(4); + let mut w = sink(); + assert_eq!(copy(&mut r, &mut w).unwrap(), 4); + + let mut r = repeat(0).take(1 << 17); + assert_eq!( + copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), + 1 << 17 + ); +} + +struct ShortReader { + cap: usize, + read_size: usize, + observed_buffer: usize, +} + +impl Read for ShortReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + let bytes = min(self.cap, self.read_size).min(buf.len()); + self.cap -= bytes; + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(bytes) + } +} + +struct WriteObserver { + observed_buffer: usize, +} + +impl Write for WriteObserver { + fn write(&mut self, buf: &[u8]) -> Result { + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +#[cfg(feature = "alloc")] +#[test] +fn copy_specializes_bufwriter() { + let cap = 117 * 1024; + let buf_sz = 16 * 1024; + let mut r = ShortReader { + cap, + observed_buffer: 0, + read_size: 1337, + }; + let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); + assert_eq!( + copy(&mut r, &mut w).unwrap(), + cap as u64, + "expected the whole capacity to be copied" + ); + assert_eq!( + r.observed_buffer, buf_sz, + "expected a large buffer to be provided to the reader" + ); + assert!( + w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, + "expected coalesced writes" + ); +} + +#[cfg(feature = "alloc")] +#[test] +fn copy_specializes_bufreader() { + let mut source = vec![0; 768 * 1024]; + source[1] = 42; + let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source)); + + let mut sink = Vec::new(); + assert_eq!(copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!(source.as_slice(), sink.as_slice()); + + let buf_sz = 71 * 1024; + assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition"); + + let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source)); + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!( + sink.observed_buffer, buf_sz, + "expected a large buffer to be provided to the writer" + ); +} + +#[cfg(feature = "alloc")] +#[test] +fn copy_specializes_to_vec() { + let cap = DEFAULT_BUF_SIZE * 10; + let mut source = ShortReader { + cap, + observed_buffer: 0, + read_size: DEFAULT_BUF_SIZE, + }; + let mut sink = Vec::new(); + let copied = copy(&mut source, &mut sink).unwrap(); + assert_eq!(cap as u64, copied); + assert_eq!(sink.len() as u64, copied); + assert!( + source.observed_buffer > DEFAULT_BUF_SIZE, + "expected a large buffer to be provided to the reader, got {}", + source.observed_buffer + ); +} + +#[cfg(feature = "alloc")] +#[test] +fn copy_specializes_from_vecdeque() { + let mut source = VecDeque::with_capacity(100 * 1024); + for _ in 0..20 * 1024 { + source.push_front(0); + } + for _ in 0..20 * 1024 { + source.push_back(0); + } + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(40 * 1024u64, copy(&mut source, &mut sink).unwrap()); + assert_eq!(20 * 1024, sink.observed_buffer); +} + +#[test] +fn copy_specializes_from_slice() { + let mut source = [1; 60 * 1024].as_slice(); + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(60 * 1024u64, copy(&mut source, &mut sink).unwrap()); + assert_eq!(60 * 1024, sink.observed_buffer); +} diff --git a/tests/cursor.rs b/tests/cursor.rs new file mode 100644 index 0000000..140e38d --- /dev/null +++ b/tests/cursor.rs @@ -0,0 +1,425 @@ +#![feature(test)] + +extern crate test; + +use axio::{Cursor, SeekFrom, prelude::*}; + +#[cfg(feature = "alloc")] +#[test] +fn test_vec_writer() { + let mut writer = Vec::new(); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer, b); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_mem_writer() { + let mut writer = Cursor::new(Vec::new()); + writer.set_position(10); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..10], &[0; 10]); + assert_eq!(&writer.get_ref()[10..], b); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_mem_writer_preallocated() { + let mut writer = Cursor::new(vec![0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10]); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(&writer.get_ref()[..], b); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_mem_mut_writer() { + let mut vec = Vec::new(); + let mut writer = Cursor::new(&mut vec); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); +} + +fn test_slice_writer(writer: &mut Cursor) +where + T: AsRef<[u8]>, + Cursor: Write, +{ + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write(&[8, 9]).unwrap(), 1); + assert_eq!(writer.write(&[10]).unwrap(), 0); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(writer.get_ref().as_ref(), b); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_box_slice_writer() { + let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); + test_slice_writer(&mut writer); +} + +#[test] +fn test_array_writer() { + let mut writer = Cursor::new([0u8; 9]); + test_slice_writer(&mut writer); +} + +#[test] +fn test_buf_writer() { + let mut buf = [0u8; 9]; + let mut writer = Cursor::new(&mut buf[..]); + test_slice_writer(&mut writer); +} + +#[test] +fn test_buf_writer_seek() { + let mut buf = [0u8; 8]; + { + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[1]).unwrap(), 1); + assert_eq!(writer.position(), 1); + + assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2); + assert_eq!(writer.position(), 2); + assert_eq!(writer.write(&[2]).unwrap(), 1); + assert_eq!(writer.position(), 3); + + assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[3]).unwrap(), 1); + assert_eq!(writer.position(), 2); + + assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); + assert_eq!(writer.position(), 7); + assert_eq!(writer.write(&[4]).unwrap(), 1); + assert_eq!(writer.position(), 8); + } + let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; + assert_eq!(buf, b); +} + +#[test] +fn test_buf_writer_error() { + let mut buf = [0u8; 2]; + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[0, 0]).unwrap(), 1); + assert_eq!(writer.write(&[0, 0]).unwrap(), 0); +} + +#[test] +fn test_mem_reader() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_boxed_slice_reader() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[cfg(feature = "alloc")] +#[test] +fn read_to_end() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); + let mut v = Vec::new(); + reader.read_to_end(&mut v).unwrap(); + assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); +} + +#[test] +fn test_slice_reader() { + let in_buf = [0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(&buf[..], b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.len(), 3); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(&buf[..], b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_read_exact() { + let in_buf = [0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert!(reader.read_exact(&mut buf).is_ok()); + let mut buf = [8]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf[0], 0); + assert_eq!(reader.len(), 7); + let mut buf = [0, 0, 0, 0, 0, 0, 0]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]); + assert_eq!(reader.len(), 0); + let mut buf = [0]; + assert!(reader.read_exact(&mut buf).is_err()); +} + +#[test] +fn test_buf_reader() { + let in_buf = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = Cursor::new(&in_buf[..]); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn seek_past_end() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.read(&mut [0]).unwrap(), 0); + + let mut r = Cursor::new(vec![10]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.read(&mut [0]).unwrap(), 0); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 0); + + #[cfg(feature = "alloc")] + { + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 0); + } +} + +#[test] +fn seek_past_i64() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut r = Cursor::new(vec![10]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); +} + +#[test] +fn seek_before_0() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut r = Cursor::new(vec![10]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert!(r.seek(SeekFrom::End(-2)).is_err()); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_seekable_mem_writer() { + let mut writer = Cursor::new(Vec::::new()); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[3, 4]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3); + assert_eq!(writer.write(&[0, 1]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); + assert_eq!(writer.write(&[1, 2]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10); + assert_eq!(writer.write(&[1]).unwrap(), 1); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; + assert_eq!(&writer.get_ref()[..], b); +} + +#[cfg(feature = "alloc")] +#[test] +fn vec_seek_past_end() { + let mut r = Cursor::new(Vec::new()); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 1); +} + +#[test] +fn vec_seek_before_0() { + let mut r = Cursor::new(Vec::new()); + assert!(r.seek(SeekFrom::End(-2)).is_err()); +} + +#[test] +#[cfg(target_pointer_width = "32")] +fn vec_seek_and_write_past_usize_max() { + let mut c = Cursor::new(Vec::new()); + c.set_position(usize::MAX as u64 + 1); + assert!(c.write_all(&[1, 2, 3]).is_err()); +} + +#[test] +fn test_partial_eq() { + assert_eq!(Cursor::new(Vec::::new()), Cursor::new(Vec::::new())); +} + +#[test] +fn test_eq() { + struct AssertEq(pub T); + + let _: AssertEq>> = AssertEq(Cursor::new(Vec::new())); +} + +#[allow(dead_code)] +fn const_cursor() { + const CURSOR: Cursor<&[u8]> = Cursor::new(&[0]); + const _: &&[u8] = CURSOR.get_ref(); + const _: u64 = CURSOR.position(); +} + +#[cfg(feature = "alloc")] +#[bench] +fn bench_write_vec(b: &mut test::Bencher) { + let slice = &[1; 128]; + + b.iter(|| { + let mut buf = b"some random data to overwrite".to_vec(); + let mut cursor = Cursor::new(&mut buf); + + let _ = cursor.write_all(slice); + test::black_box(&cursor); + }) +} diff --git a/tests/impls.rs b/tests/impls.rs new file mode 100644 index 0000000..df0b346 --- /dev/null +++ b/tests/impls.rs @@ -0,0 +1,61 @@ +#![feature(test)] + +extern crate test; + +use axio::prelude::*; + +#[bench] +fn bench_read_slice(b: &mut test::Bencher) { + let buf = [5; 1024]; + let mut dst = [0; 128]; + + b.iter(|| { + let mut rd = &buf[..]; + for _ in 0..8 { + let _ = rd.read(&mut dst); + test::black_box(&dst); + } + }) +} + +#[bench] +fn bench_write_slice(b: &mut test::Bencher) { + let mut buf = [0; 1024]; + let src = [5; 128]; + + b.iter(|| { + let mut wr = &mut buf[..]; + for _ in 0..8 { + let _ = wr.write_all(&src); + test::black_box(&wr); + } + }) +} + +#[bench] +fn bench_read_vec(b: &mut test::Bencher) { + let buf = vec![5; 1024]; + let mut dst = [0; 128]; + + b.iter(|| { + let mut rd = &buf[..]; + for _ in 0..8 { + let _ = rd.read(&mut dst); + test::black_box(&dst); + } + }) +} + +#[bench] +fn bench_write_vec(b: &mut test::Bencher) { + let mut buf = Vec::with_capacity(1024); + let src = [5; 128]; + + b.iter(|| { + let mut wr = &mut buf[..]; + for _ in 0..8 { + let _ = wr.write_all(&src); + test::black_box(&wr); + } + }) +} diff --git a/tests/io.rs b/tests/io.rs new file mode 100644 index 0000000..03ee94f --- /dev/null +++ b/tests/io.rs @@ -0,0 +1,696 @@ +#![feature(core_io_borrowed_buf)] +#![feature(test)] + +extern crate test; + +use std::{ + io::{BorrowedBuf, BorrowedCursor}, + mem::MaybeUninit, +}; + +use axio::{BufRead, BufReader, Cursor, DEFAULT_BUF_SIZE, Error, Read, Result, Seek, SeekFrom}; + +#[cfg(feature = "alloc")] +#[test] +fn read_until() { + let mut buf = Cursor::new(&b"12"[..]); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2); + assert_eq!(v, b"12"); + + let mut buf = Cursor::new(&b"1233"[..]); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3); + assert_eq!(v, b"123"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1); + assert_eq!(v, b"3"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0); + assert_eq!(v, []); +} + +#[cfg(feature = "alloc")] +#[test] +fn skip_until() { + let bytes: &[u8] = b"read\0ignore\0read\0ignore\0read\0ignore\0"; + let mut reader = BufReader::new(bytes); + + // read from the bytes, alternating between + // consuming `read\0`s and skipping `ignore\0`s + loop { + // consume `read\0` + let mut out = Vec::new(); + let read = reader.read_until(0, &mut out).unwrap(); + if read == 0 { + // eof + break; + } else { + assert_eq!(out, b"read\0"); + assert_eq!(read, b"read\0".len()); + } + + // skip past `ignore\0` + let skipped = reader.skip_until(0).unwrap(); + assert_eq!(skipped, b"ignore\0".len()); + } + + // ensure we are at the end of the byte slice and that we can skip no further + // also ensure skip_until matches the behavior of read_until at EOF + let skipped = reader.skip_until(0).unwrap(); + assert_eq!(skipped, 0); +} + +#[cfg(feature = "alloc")] +#[test] +fn split() { + let buf = Cursor::new(&b"12"[..]); + let mut s = buf.split(b'3'); + assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); + assert!(s.next().is_none()); + + let buf = Cursor::new(&b"1233"[..]); + let mut s = buf.split(b'3'); + assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); + assert_eq!(s.next().unwrap().unwrap(), vec![]); + assert!(s.next().is_none()); +} + +#[cfg(feature = "alloc")] +#[test] +fn read_line() { + let mut buf = Cursor::new(&b"12"[..]); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v).unwrap(), 2); + assert_eq!(v, "12"); + + let mut buf = Cursor::new(&b"12\n\n"[..]); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v).unwrap(), 3); + assert_eq!(v, "12\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v).unwrap(), 1); + assert_eq!(v, "\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v).unwrap(), 0); + assert_eq!(v, ""); +} + +#[cfg(feature = "alloc")] +#[test] +fn lines() { + let buf = Cursor::new(&b"12\r"[..]); + let mut s = buf.lines(); + assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string()); + assert!(s.next().is_none()); + + let buf = Cursor::new(&b"12\r\n\n"[..]); + let mut s = buf.lines(); + assert_eq!(s.next().unwrap().unwrap(), "12".to_string()); + assert_eq!(s.next().unwrap().unwrap(), "".to_string()); + assert!(s.next().is_none()); +} + +#[test] +fn buf_read_has_data_left() { + let mut buf = Cursor::new(&b"abcd"[..]); + assert!(buf.has_data_left().unwrap()); + buf.read_exact(&mut [0; 2]).unwrap(); + assert!(buf.has_data_left().unwrap()); + buf.read_exact(&mut [0; 2]).unwrap(); + assert!(!buf.has_data_left().unwrap()); +} + +#[cfg(feature = "alloc")] +#[test] +fn read_to_end() { + let mut c = Cursor::new(&b""[..]); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v).unwrap(), 0); + assert_eq!(v, []); + + let mut c = Cursor::new(&b"1"[..]); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v).unwrap(), 1); + assert_eq!(v, b"1"); + + let cap = if cfg!(miri) { 1024 } else { 1024 * 1024 }; + let data = (0..cap).map(|i| (i / 3) as u8).collect::>(); + let mut v = Vec::new(); + let (a, b) = data.split_at(data.len() / 2); + assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len()); + assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len()); + assert_eq!(v, data); +} + +#[cfg(feature = "alloc")] +#[test] +fn read_to_string() { + let mut c = Cursor::new(&b""[..]); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v).unwrap(), 0); + assert_eq!(v, ""); + + let mut c = Cursor::new(&b"1"[..]); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v).unwrap(), 1); + assert_eq!(v, "1"); + + let mut c = Cursor::new(&b"\xff"[..]); + let mut v = String::new(); + assert!(c.read_to_string(&mut v).is_err()); +} + +#[test] +fn read_exact() { + let mut buf = [0; 4]; + + let mut c = Cursor::new(&b""[..]); + assert_eq!(c.read_exact(&mut buf).unwrap_err(), Error::UnexpectedEof); + + let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..])); + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"1234"); + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"5678"); + assert_eq!(c.read_exact(&mut buf).unwrap_err(), Error::UnexpectedEof); +} + +#[test] +fn read_exact_slice() { + let mut buf = [0; 4]; + + let mut c = &b""[..]; + assert_eq!(c.read_exact(&mut buf).unwrap_err(), Error::UnexpectedEof); + + let mut c = &b"123"[..]; + assert_eq!(c.read_exact(&mut buf).unwrap_err(), Error::UnexpectedEof); + // make sure the optimized (early returning) method is being used + assert_eq!(&buf, &[0; 4]); + + let mut c = &b"1234"[..]; + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"1234"); + + let mut c = &b"56789"[..]; + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"5678"); + assert_eq!(c, b"9"); +} + +#[test] +fn read_buf_exact() { + let buf: &mut [_] = &mut [0; 4]; + let mut buf: BorrowedBuf<'_> = buf.into(); + + let mut c = Cursor::new(&b""[..]); + assert_eq!( + c.read_buf_exact(buf.unfilled()).unwrap_err(), + Error::UnexpectedEof + ); + + let mut c = Cursor::new(&b"123456789"[..]); + c.read_buf_exact(buf.unfilled()).unwrap(); + assert_eq!(buf.filled(), b"1234"); + + buf.clear(); + + c.read_buf_exact(buf.unfilled()).unwrap(); + assert_eq!(buf.filled(), b"5678"); + + buf.clear(); + + assert_eq!( + c.read_buf_exact(buf.unfilled()).unwrap_err(), + Error::UnexpectedEof + ); +} + +#[test] +fn take_eof() { + struct R; + + impl Read for R { + fn read(&mut self, _: &mut [u8]) -> Result { + Err(Error::Io) + } + } + impl BufRead for R { + fn fill_buf(&mut self) -> Result<&[u8]> { + Err(Error::Io) + } + + fn consume(&mut self, _amt: usize) {} + } + + let mut buf = [0; 1]; + assert_eq!(0, R.take(0).read(&mut buf).unwrap()); + assert_eq!(b"", R.take(0).fill_buf().unwrap()); +} + +fn cmp_bufread(mut br1: Br1, mut br2: Br2, exp: &[u8]) { + let mut cat = Vec::new(); + loop { + let consume = { + let buf1 = br1.fill_buf().unwrap(); + let buf2 = br2.fill_buf().unwrap(); + let minlen = if buf1.len() < buf2.len() { + buf1.len() + } else { + buf2.len() + }; + assert_eq!(buf1[..minlen], buf2[..minlen]); + cat.extend_from_slice(&buf1[..minlen]); + minlen + }; + if consume == 0 { + break; + } + br1.consume(consume); + br2.consume(consume); + } + assert_eq!(br1.fill_buf().unwrap().len(), 0); + assert_eq!(br2.fill_buf().unwrap().len(), 0); + assert_eq!(&cat[..], exp) +} + +#[test] +fn chain_bufread() { + let testdata = b"ABCDEFGHIJKL"; + let chain1 = (&testdata[..3]) + .chain(&testdata[3..6]) + .chain(&testdata[6..9]) + .chain(&testdata[9..]); + let chain2 = (&testdata[..4]) + .chain(&testdata[4..8]) + .chain(&testdata[8..]); + cmp_bufread(chain1, chain2, &testdata[..]); +} + +#[cfg(feature = "alloc")] +#[test] +fn chain_splitted_char() { + let chain = b"\xc3".chain(b"\xa9".as_slice()); + assert_eq!(axio::read_to_string(chain).unwrap(), "é"); + + let mut chain = b"\xc3".chain(b"\xa9\n".as_slice()); + let mut buf = String::new(); + assert_eq!(chain.read_line(&mut buf).unwrap(), 3); + assert_eq!(buf, "é\n"); +} + +#[cfg(feature = "alloc")] +#[test] +fn chain_zero_length_read_is_not_eof() { + let a = b"A"; + let b = b"B"; + let mut s = String::new(); + let mut chain = (&a[..]).chain(&b[..]); + chain.read(&mut []).unwrap(); + chain.read_to_string(&mut s).unwrap(); + assert_eq!("AB", s); +} + +#[cfg(feature = "alloc")] +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_read_to_end(b: &mut test::Bencher) { + b.iter(|| { + let mut lr = axio::repeat(1).take(10000000); + let mut vec = Vec::with_capacity(1024); + axio::default_read_to_end(&mut lr, &mut vec, None) + }); +} + +#[test] +fn seek_len() -> Result<()> { + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_len()?, 15); + + c.seek(SeekFrom::End(0))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + Ok(()) +} + +#[test] +fn seek_position() -> Result<()> { + // All `asserts` are duplicated here to make sure the method does not + // change anything about the seek state. + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_position()?, 0); + assert_eq!(c.stream_position()?, 0); + + c.seek(SeekFrom::End(0))?; + assert_eq!(c.stream_position()?, 15); + assert_eq!(c.stream_position()?, 15); + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + assert_eq!(c.stream_position()?, 9); + assert_eq!(c.stream_position()?, 9); + + c.seek(SeekFrom::End(-3))?; + c.seek(SeekFrom::Current(1))?; + c.seek(SeekFrom::Current(-5))?; + assert_eq!(c.stream_position()?, 8); + assert_eq!(c.stream_position()?, 8); + + c.rewind()?; + assert_eq!(c.stream_position()?, 0); + assert_eq!(c.stream_position()?, 0); + + Ok(()) +} + +#[test] +fn take_seek() -> Result<()> { + let mut buf = Cursor::new(b"0123456789"); + buf.set_position(2); + let mut take = buf.by_ref().take(4); + let mut buf1 = [0u8; 1]; + let mut buf2 = [0u8; 2]; + assert_eq!(take.position(), 0); + + assert_eq!(take.seek(SeekFrom::Start(0))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + assert_eq!(take.seek(SeekFrom::Start(1))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + assert_eq!(take.seek(SeekFrom::Start(2))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + assert_eq!(take.seek(SeekFrom::Start(3))?, 3); + take.read_exact(&mut buf1)?; + assert_eq!(buf1, [b'5']); + assert_eq!(take.seek(SeekFrom::Start(4))?, 4); + assert_eq!(take.read(&mut buf1)?, 0); + + assert_eq!(take.seek(SeekFrom::End(0))?, 4); + assert_eq!(take.seek(SeekFrom::End(-1))?, 3); + take.read_exact(&mut buf1)?; + assert_eq!(buf1, [b'5']); + assert_eq!(take.seek(SeekFrom::End(-2))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + assert_eq!(take.seek(SeekFrom::End(-3))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + assert_eq!(take.seek(SeekFrom::End(-4))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + + assert_eq!(take.seek(SeekFrom::Current(0))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + + assert_eq!(take.seek(SeekFrom::Current(-3))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + + assert_eq!(take.seek(SeekFrom::Current(-1))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + + assert_eq!(take.seek(SeekFrom::Current(-4))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + + assert_eq!(take.seek(SeekFrom::Current(2))?, 4); + assert_eq!(take.read(&mut buf1)?, 0); + + Ok(()) +} + +#[test] +fn take_seek_error() { + let buf = Cursor::new(b"0123456789"); + let mut take = buf.take(2); + assert!(take.seek(SeekFrom::Start(3)).is_err()); + assert!(take.seek(SeekFrom::End(1)).is_err()); + assert!(take.seek(SeekFrom::End(-3)).is_err()); + assert!(take.seek(SeekFrom::Current(-1)).is_err()); + assert!(take.seek(SeekFrom::Current(3)).is_err()); +} + +struct ExampleHugeRangeOfZeroes { + position: u64, +} + +impl Read for ExampleHugeRangeOfZeroes { + fn read(&mut self, buf: &mut [u8]) -> Result { + let max = buf.len().min(usize::MAX); + for (i, e) in buf.iter_mut().enumerate().take(max) { + if self.position == u64::MAX { + return Ok(i); + } + self.position += 1; + *e = 0; + } + Ok(max) + } +} + +impl Seek for ExampleHugeRangeOfZeroes { + fn seek(&mut self, pos: SeekFrom) -> Result { + match pos { + SeekFrom::Start(i) => self.position = i, + SeekFrom::End(i) if i >= 0 => self.position = u64::MAX, + SeekFrom::End(i) => self.position -= i.unsigned_abs(), + SeekFrom::Current(i) => { + self.position = if i >= 0 { + self.position.saturating_add(i.unsigned_abs()) + } else { + self.position.saturating_sub(i.unsigned_abs()) + }; + } + } + Ok(self.position) + } +} + +#[test] +fn take_seek_big_offsets() -> Result<()> { + let inner = ExampleHugeRangeOfZeroes { position: 1 }; + let mut take = inner.take(u64::MAX - 2); + assert_eq!(take.seek(SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2); + assert_eq!(take.get_ref().position, u64::MAX - 1); + assert_eq!(take.seek(SeekFrom::Start(0))?, 0); + assert_eq!(take.get_ref().position, 1); + assert_eq!(take.seek(SeekFrom::End(-1))?, u64::MAX - 3); + assert_eq!(take.get_ref().position, u64::MAX - 2); + Ok(()) +} + +// A simple example reader which uses the default implementation of +// read_to_end. +#[cfg(feature = "alloc")] +struct ExampleSliceReader<'a> { + slice: &'a [u8], +} + +#[cfg(feature = "alloc")] +impl<'a> Read for ExampleSliceReader<'a> { + fn read(&mut self, buf: &mut [u8]) -> Result { + let len = core::cmp::min(self.slice.len(), buf.len()); + buf[..len].copy_from_slice(&self.slice[..len]); + self.slice = &self.slice[len..]; + Ok(len) + } +} + +#[cfg(feature = "alloc")] +#[test] +fn test_read_to_end_capacity() -> Result<()> { + let input = &b"foo"[..]; + + // read_to_end() takes care not to over-allocate when a buffer is the + // exact size needed. + let mut vec1 = Vec::with_capacity(input.len()); + ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?; + assert_eq!(vec1.len(), input.len()); + assert_eq!(vec1.capacity(), input.len(), "did not allocate more"); + + Ok(()) +} + +// Issue 94981 +#[test] +#[should_panic = "number of read bytes exceeds limit"] +fn test_take_wrong_length() { + struct LieAboutSize(bool); + + impl Read for LieAboutSize { + fn read(&mut self, buf: &mut [u8]) -> Result { + // Lie about the read size at first time of read. + if core::mem::take(&mut self.0) { + Ok(buf.len() + 1) + } else { + Ok(buf.len()) + } + } + } + + let mut buffer = [0; 4]; + let mut reader = LieAboutSize(true).take(4); + // Primed the `Limit` by lying about the read size. + let _ = reader.read(&mut buffer[..]); +} + +#[test] +fn slice_read_exact_eof() { + let slice = &b"123456"[..]; + + let mut r = slice; + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + +#[test] +fn cursor_read_exact_eof() { + let slice = Cursor::new(b"123456"); + + let mut r = slice.clone(); + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(Cursor::split(&r).1.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(Cursor::split(&r).1.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + +#[bench] +fn bench_take_read(b: &mut test::Bencher) { + b.iter(|| { + let mut buf = [0; 64]; + + [255; 128].take(64).read(&mut buf).unwrap(); + }); +} + +#[bench] +fn bench_take_read_buf(b: &mut test::Bencher) { + b.iter(|| { + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 64]; + + let mut buf: BorrowedBuf<'_> = buf.into(); + + [255; 128].take(64).read_buf(buf.unfilled()).unwrap(); + }); +} + +// Issue #120603 +#[test] +#[should_panic] +fn read_buf_broken_read() { + struct MalformedRead; + + impl Read for MalformedRead { + fn read(&mut self, buf: &mut [u8]) -> Result { + // broken length calculation + Ok(buf.len() + 1) + } + } + + let _ = BufReader::new(MalformedRead).fill_buf(); +} + +#[test] +fn read_buf_full_read() { + struct FullRead; + + impl Read for FullRead { + fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(buf.len()) + } + } + + assert_eq!( + BufReader::new(FullRead).fill_buf().unwrap().len(), + DEFAULT_BUF_SIZE + ); +} + +struct DataAndErrorReader(&'static [u8]); + +impl Read for DataAndErrorReader { + fn read(&mut self, _buf: &mut [u8]) -> Result { + panic!("We want tests to use `read_buf`") + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { + self.0.read_buf(buf).unwrap(); + Err(Error::Io) + } +} + +#[test] +fn read_buf_data_and_error_take() { + let mut buf = [0; 64]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + + let mut r = DataAndErrorReader(&[4, 5, 6]).take(1); + assert!(r.read_buf(buf.unfilled()).is_err()); + assert_eq!(buf.filled(), &[4]); + + assert!(r.read_buf(buf.unfilled()).is_ok()); + assert_eq!(buf.filled(), &[4]); + assert_eq!(r.get_ref().0, &[5, 6]); +} + +#[test] +fn read_buf_data_and_error_buf() { + let mut r = BufReader::new(DataAndErrorReader(&[4, 5, 6])); + + assert!(r.fill_buf().is_err()); + assert_eq!(r.fill_buf().unwrap(), &[4, 5, 6]); +} + +#[cfg(feature = "alloc")] +#[test] +fn read_buf_data_and_error_read_to_end() { + let mut r = DataAndErrorReader(&[4, 5, 6]); + + let mut v = Vec::with_capacity(200); + assert!(r.read_to_end(&mut v).is_err()); + + assert_eq!(v, &[4, 5, 6]); +} + +#[cfg(feature = "alloc")] +#[test] +fn read_to_end_error() { + struct ErrorReader; + + impl Read for ErrorReader { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(Error::Io) + } + } + + let mut r = [4, 5, 6].chain(ErrorReader); + + let mut v = Vec::with_capacity(200); + assert!(r.read_to_end(&mut v).is_err()); + + assert_eq!(v, &[4, 5, 6]); +} diff --git a/tests/utils.rs b/tests/utils.rs new file mode 100644 index 0000000..df37b26 --- /dev/null +++ b/tests/utils.rs @@ -0,0 +1,165 @@ +#![feature(core_io_borrowed_buf)] + +use std::{fmt, io::BorrowedBuf, mem::MaybeUninit}; + +use axio::{Empty, Error, Repeat, SeekFrom, Sink, empty, prelude::*, repeat, sink}; + +struct ErrorDisplay; + +impl fmt::Display for ErrorDisplay { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Err(fmt::Error) + } +} + +struct PanicDisplay; + +impl fmt::Display for PanicDisplay { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + panic!() + } +} + +#[track_caller] +fn test_sinking(mut w: W) { + assert_eq!(w.write(&[]).unwrap(), 0); + assert_eq!(w.write(&[0]).unwrap(), 1); + assert_eq!(w.write(&[0; 1024]).unwrap(), 1024); + w.write_all(&[]).unwrap(); + w.write_all(&[0]).unwrap(); + w.write_all(&[0; 1024]).unwrap(); + assert!(w.flush().is_ok()); + assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024); + // Ignores fmt arguments + w.write_fmt(format_args!("{ErrorDisplay}")).unwrap(); + w.write_fmt(format_args!("{PanicDisplay}")).unwrap(); +} + +#[test] +fn sink_sinks() { + test_sinking(sink()); +} + +#[test] +fn empty_reads() { + let mut e = empty(); + assert_eq!(e.read(&mut []).unwrap(), 0); + assert_eq!(e.read(&mut [0]).unwrap(), 0); + assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); + assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0); + + e.read_exact(&mut []).unwrap(); + assert_eq!(e.read_exact(&mut [0]).unwrap_err(), Error::UnexpectedEof); + assert_eq!( + e.read_exact(&mut [0; 1024]).unwrap_err(), + Error::UnexpectedEof + ); + + let buf: &mut [MaybeUninit<_>] = &mut []; + let mut buf: BorrowedBuf<'_> = buf.into(); + e.read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit()]; + let mut buf: BorrowedBuf<'_> = buf.into(); + e.read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; + let mut buf: BorrowedBuf<'_> = buf.into(); + e.read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; + let mut buf: BorrowedBuf<'_> = buf.into(); + Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.len(), 0); + + let buf: &mut [MaybeUninit<_>] = &mut []; + let mut buf: BorrowedBuf<'_> = buf.into(); + e.read_buf_exact(buf.unfilled()).unwrap(); + assert_eq!(buf.len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit()]; + let mut buf: BorrowedBuf<'_> = buf.into(); + assert_eq!( + e.read_buf_exact(buf.unfilled()).unwrap_err(), + Error::UnexpectedEof + ); + assert_eq!(buf.len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; + let mut buf: BorrowedBuf<'_> = buf.into(); + assert_eq!( + e.read_buf_exact(buf.unfilled()).unwrap_err(), + Error::UnexpectedEof + ); + assert_eq!(buf.len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; + let mut buf: BorrowedBuf<'_> = buf.into(); + assert_eq!( + Read::by_ref(&mut e) + .read_buf_exact(buf.unfilled()) + .unwrap_err(), + Error::UnexpectedEof, + ); + assert_eq!(buf.len(), 0); + + #[cfg(feature = "alloc")] + { + let mut buf = Vec::new(); + assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf, vec![]); + let mut buf = vec![1, 2, 3]; + assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf, vec![1, 2, 3]); + + let mut buf = String::new(); + assert_eq!(e.read_to_string(&mut buf).unwrap(), 0); + assert_eq!(buf, ""); + let mut buf = "hello".to_owned(); + assert_eq!(e.read_to_string(&mut buf).unwrap(), 0); + assert_eq!(buf, "hello"); + } +} + +#[test] +fn empty_seeks() { + let mut e = empty(); + assert!(matches!(e.seek(SeekFrom::Start(0)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Start(1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Start(u64::MAX)), Ok(0))); + + assert!(matches!(e.seek(SeekFrom::End(i64::MIN)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(-1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(0)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(i64::MAX)), Ok(0))); + + assert!(matches!(e.seek(SeekFrom::Current(i64::MIN)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(-1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(0)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(i64::MAX)), Ok(0))); +} + +#[test] +fn empty_sinks() { + test_sinking(empty()); +} + +#[test] +fn repeat_repeats() { + let mut r = repeat(4); + let mut b = [0; 1024]; + assert_eq!(r.read(&mut b).unwrap(), 1024); + assert!(b.iter().all(|b| *b == 4)); +} + +#[allow(dead_code)] +fn const_utils() { + const _: Empty = empty(); + const _: Repeat = repeat(b'c'); + const _: Sink = sink(); +}