add patch for 29ef6cf
This commit is contained in:
commit
e9d3edf027
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "core_io"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = []
|
||||||
|
collections = ["alloc"]
|
438
src/buffered/bufreader.rs
Normal file
438
src/buffered/bufreader.rs
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
use core::cmp;
|
||||||
|
use core::fmt;
|
||||||
|
use crate::{
|
||||||
|
self as io, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
|
||||||
|
};
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
/// The `BufReader<R>` struct adds buffering to any reader.
|
||||||
|
///
|
||||||
|
/// It can be excessively inefficient to work directly with a [`Read`] instance.
|
||||||
|
/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`]
|
||||||
|
/// results in a system call. A `BufReader<R>` performs large, infrequent reads on
|
||||||
|
/// the underlying [`Read`] and maintains an in-memory buffer of the results.
|
||||||
|
///
|
||||||
|
/// `BufReader<R>` can improve the speed of programs that make *small* and
|
||||||
|
/// *repeated* read calls to the same file or network socket. It does not
|
||||||
|
/// help when reading very large amounts at once, or reading just one or a few
|
||||||
|
/// times. It also provides no advantage when reading from a source that is
|
||||||
|
/// already in memory, like a [`Vec`]`<u8>`.
|
||||||
|
///
|
||||||
|
/// When the `BufReader<R>` is dropped, the contents of its buffer will be
|
||||||
|
/// discarded. Creating multiple instances of a `BufReader<R>` on the same
|
||||||
|
/// stream can cause data loss. Reading from the underlying reader after
|
||||||
|
/// unwrapping the `BufReader<R>` with [`BufReader::into_inner`] can also cause
|
||||||
|
/// data loss.
|
||||||
|
///
|
||||||
|
// HACK(#78696): can't use `crate` for associated items
|
||||||
|
/// [`TcpStream::read`]: super::super::super::net::TcpStream::read
|
||||||
|
/// [`TcpStream`]: crate::net::TcpStream
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::BufReader;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f = File::open("log.txt")?;
|
||||||
|
/// let mut reader = BufReader::new(f);
|
||||||
|
///
|
||||||
|
/// let mut line = String::new();
|
||||||
|
/// let len = reader.read_line(&mut line)?;
|
||||||
|
/// println!("First line is {} bytes long", len);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct BufReader<R> {
|
||||||
|
inner: R,
|
||||||
|
buf: Box<[u8]>,
|
||||||
|
pos: usize,
|
||||||
|
cap: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> BufReader<R> {
|
||||||
|
/// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KB,
|
||||||
|
/// but may change in the future.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufReader;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f = File::open("log.txt")?;
|
||||||
|
/// let reader = BufReader::new(f);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn new(inner: R) -> BufReader<R> {
|
||||||
|
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `BufReader<R>` with the specified buffer capacity.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Creating a buffer with ten bytes of capacity:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufReader;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f = File::open("log.txt")?;
|
||||||
|
/// let reader = BufReader::with_capacity(10, f);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
|
||||||
|
unsafe {
|
||||||
|
let mut buf = Box::new_uninit_slice(capacity).assume_init();
|
||||||
|
inner.initializer().initialize(&mut buf);
|
||||||
|
BufReader { inner, buf, pos: 0, cap: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> BufReader<R> {
|
||||||
|
/// Gets a reference to the underlying reader.
|
||||||
|
///
|
||||||
|
/// It is inadvisable to directly read from the underlying reader.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufReader;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f1 = File::open("log.txt")?;
|
||||||
|
/// let reader = BufReader::new(f1);
|
||||||
|
///
|
||||||
|
/// let f2 = reader.get_ref();
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufReader;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f1 = File::open("log.txt")?;
|
||||||
|
/// let mut reader = BufReader::new(f1);
|
||||||
|
///
|
||||||
|
/// let f2 = reader.get_mut();
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
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
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::{BufReader, BufRead};
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f = File::open("log.txt")?;
|
||||||
|
/// let mut reader = BufReader::new(f);
|
||||||
|
/// assert!(reader.buffer().is_empty());
|
||||||
|
///
|
||||||
|
/// if reader.fill_buf()?.len() > 0 {
|
||||||
|
/// assert!(!reader.buffer().is_empty());
|
||||||
|
/// }
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn buffer(&self) -> &[u8] {
|
||||||
|
&self.buf[self.pos..self.cap]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of bytes the internal buffer can hold at once.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::{BufReader, BufRead};
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f = File::open("log.txt")?;
|
||||||
|
/// let mut reader = BufReader::new(f);
|
||||||
|
///
|
||||||
|
/// let capacity = reader.capacity();
|
||||||
|
/// let buffer = reader.fill_buf()?;
|
||||||
|
/// assert!(buffer.len() <= capacity);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn capacity(&self) -> usize {
|
||||||
|
self.buf.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwraps this `BufReader<R>`, 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufReader;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let f1 = File::open("log.txt")?;
|
||||||
|
/// let reader = BufReader::new(f1);
|
||||||
|
///
|
||||||
|
/// let f2 = reader.into_inner();
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> R {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invalidates all data in the internal buffer.
|
||||||
|
#[inline]
|
||||||
|
fn discard_buffer(&mut self) {
|
||||||
|
self.pos = 0;
|
||||||
|
self.cap = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Seek> BufReader<R> {
|
||||||
|
/// 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.
|
||||||
|
pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||||
|
let pos = self.pos as u64;
|
||||||
|
if offset < 0 {
|
||||||
|
if let Some(new_pos) = pos.checked_sub((-offset) as u64) {
|
||||||
|
self.pos = new_pos as usize;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(new_pos) = pos.checked_add(offset as u64) {
|
||||||
|
if new_pos <= self.cap as u64 {
|
||||||
|
self.pos = new_pos as usize;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.seek(SeekFrom::Current(offset)).map(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> Read for BufReader<R> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
// 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.pos == self.cap && buf.len() >= self.buf.len() {
|
||||||
|
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]) -> io::Result<()> {
|
||||||
|
if self.buffer().len() >= buf.len() {
|
||||||
|
buf.copy_from_slice(&self.buffer()[..buf.len()]);
|
||||||
|
self.consume(buf.len());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::default_read_exact(self, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
||||||
|
if self.pos == self.cap && total_len >= self.buf.len() {
|
||||||
|
self.discard_buffer();
|
||||||
|
return self.inner.read_vectored(bufs);
|
||||||
|
}
|
||||||
|
let nread = {
|
||||||
|
let mut rem = self.fill_buf()?;
|
||||||
|
rem.read_vectored(bufs)?
|
||||||
|
};
|
||||||
|
self.consume(nread);
|
||||||
|
Ok(nread)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
self.inner.is_read_vectored()
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can't skip unconditionally because of the large buffer case in read.
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
self.inner.initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> BufRead for BufReader<R> {
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
// If we've reached the end of our internal buffer then we need to fetch
|
||||||
|
// some more data from the underlying reader.
|
||||||
|
// Branch using `>=` instead of the more correct `==`
|
||||||
|
// to tell the compiler that the pos..cap slice is always valid.
|
||||||
|
if self.pos >= self.cap {
|
||||||
|
debug_assert!(self.pos == self.cap);
|
||||||
|
self.cap = self.inner.read(&mut self.buf)?;
|
||||||
|
self.pos = 0;
|
||||||
|
}
|
||||||
|
Ok(&self.buf[self.pos..self.cap])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
self.pos = cmp::min(self.pos + amt, self.cap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> fmt::Debug for BufReader<R>
|
||||||
|
where
|
||||||
|
R: fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt.debug_struct("BufReader")
|
||||||
|
.field("reader", &self.inner)
|
||||||
|
.field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len()))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Seek> Seek for BufReader<R> {
|
||||||
|
/// 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<R>` 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 [`std::io::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)`.
|
||||||
|
///
|
||||||
|
/// [`std::io::Seek`]: Seek
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
let result: u64;
|
||||||
|
if let SeekFrom::Current(n) = pos {
|
||||||
|
let remainder = (self.cap - self.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.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::{
|
||||||
|
/// io::{self, BufRead, BufReader, Seek},
|
||||||
|
/// fs::File,
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// fn main() -> io::Result<()> {
|
||||||
|
/// let mut f = BufReader::new(File::open("foo.txt")?);
|
||||||
|
///
|
||||||
|
/// let before = f.stream_position()?;
|
||||||
|
/// f.read_line(&mut String::new())?;
|
||||||
|
/// let after = f.stream_position()?;
|
||||||
|
///
|
||||||
|
/// println!("The first line was {} bytes long", after - before);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn stream_position(&mut self) -> io::Result<u64> {
|
||||||
|
let remainder = (self.cap - self.pos) as u64;
|
||||||
|
self.inner.stream_position().map(|pos| {
|
||||||
|
pos.checked_sub(remainder).expect(
|
||||||
|
"overflow when subtracting remaining buffer size from inner stream position",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SizeHint for BufReader<T> {
|
||||||
|
#[inline]
|
||||||
|
fn lower_bound(&self) -> usize {
|
||||||
|
SizeHint::lower_bound(self.get_ref()) + self.buffer().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn upper_bound(&self) -> Option<usize> {
|
||||||
|
SizeHint::upper_bound(self.get_ref()).and_then(|up| self.buffer().len().checked_add(up))
|
||||||
|
}
|
||||||
|
}
|
648
src/buffered/bufwriter.rs
Normal file
648
src/buffered/bufwriter.rs
Normal file
@ -0,0 +1,648 @@
|
|||||||
|
use core::fmt;
|
||||||
|
use crate::{
|
||||||
|
self as io, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE,
|
||||||
|
};
|
||||||
|
use core::ptr;
|
||||||
|
use core::mem;
|
||||||
|
use crate::Vec;
|
||||||
|
|
||||||
|
/// Wraps a writer and buffers its output.
|
||||||
|
///
|
||||||
|
/// It can be excessively inefficient to work directly with something that
|
||||||
|
/// implements [`Write`]. For example, every call to
|
||||||
|
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
||||||
|
/// `BufWriter<W>` keeps an in-memory buffer of data and writes it to an underlying
|
||||||
|
/// writer in large, infrequent batches.
|
||||||
|
///
|
||||||
|
/// `BufWriter<W>` can improve the speed of programs that make *small* and
|
||||||
|
/// *repeated* write calls to the same file or network socket. It does not
|
||||||
|
/// help when writing very large amounts at once, or writing just one or a few
|
||||||
|
/// times. It also provides no advantage when writing to a destination that is
|
||||||
|
/// in memory, like a [`Vec`]`<u8>`.
|
||||||
|
///
|
||||||
|
/// It is critical to call [`flush`] before `BufWriter<W>` is dropped. Though
|
||||||
|
/// dropping will attempt to flush the contents of the buffer, any errors
|
||||||
|
/// that happen in the process of dropping will be ignored. Calling [`flush`]
|
||||||
|
/// ensures that the buffer is empty and thus dropping will not even attempt
|
||||||
|
/// file operations.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Let's write the numbers one through ten to a [`TcpStream`]:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// stream.write(&[i+1]).unwrap();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Because we're not buffering, we write each one in turn, incurring the
|
||||||
|
/// overhead of a system call per byte written. We can fix this with a
|
||||||
|
/// `BufWriter<W>`:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// stream.write(&[i+1]).unwrap();
|
||||||
|
/// }
|
||||||
|
/// stream.flush().unwrap();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// By wrapping the stream with a `BufWriter<W>`, these ten writes are all grouped
|
||||||
|
/// together by the buffer and will all be written out in one system call when
|
||||||
|
/// the `stream` is flushed.
|
||||||
|
///
|
||||||
|
// HACK(#78696): can't use `crate` for associated items
|
||||||
|
/// [`TcpStream::write`]: super::super::super::net::TcpStream::write
|
||||||
|
/// [`TcpStream`]: crate::net::TcpStream
|
||||||
|
/// [`flush`]: BufWriter::flush
|
||||||
|
pub struct BufWriter<W: Write> {
|
||||||
|
inner: W,
|
||||||
|
// The buffer. Avoid using this like a normal `Vec` in common code paths.
|
||||||
|
// That is, don't use `buf.push`, `buf.extend_from_slice`, or any other
|
||||||
|
// methods that require bounds checking or the like. This makes an enormous
|
||||||
|
// difference to performance (we may want to stop using a `Vec` entirely).
|
||||||
|
buf: Vec<u8>,
|
||||||
|
// #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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> BufWriter<W> {
|
||||||
|
/// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KB,
|
||||||
|
/// but may change in the future.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
/// ```
|
||||||
|
pub fn new(inner: W) -> BufWriter<W> {
|
||||||
|
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `BufWriter<W>` with the specified buffer capacity.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Creating a buffer with a buffer of a hundred bytes.
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||||
|
/// let mut buffer = BufWriter::with_capacity(100, stream);
|
||||||
|
/// ```
|
||||||
|
pub fn with_capacity(capacity: usize, inner: W) -> BufWriter<W> {
|
||||||
|
BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) -> io::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 Vec<u8>,
|
||||||
|
written: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BufGuard<'a> {
|
||||||
|
fn new(buffer: &'a mut Vec<u8>) -> Self {
|
||||||
|
Self { buffer, written: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The unwritten part of the buffer
|
||||||
|
fn remaining(&self) -> &[u8] {
|
||||||
|
&self.buffer[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::new_const(
|
||||||
|
ErrorKind::WriteZero,
|
||||||
|
&"failed to write the buffered data",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(n) => guard.consume(n),
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(super) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a reference to the underlying writer.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // we can use reference just like buffer
|
||||||
|
/// let reference = buffer.get_ref();
|
||||||
|
/// ```
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // we can use reference just like buffer
|
||||||
|
/// let reference = buffer.get_mut();
|
||||||
|
/// ```
|
||||||
|
pub fn get_mut(&mut self) -> &mut W {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the internally buffered data.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // See how many bytes are currently buffered
|
||||||
|
/// let bytes_buffered = buf_writer.buffer().len();
|
||||||
|
/// ```
|
||||||
|
pub fn buffer(&self) -> &[u8] {
|
||||||
|
&self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the internal buffer.
|
||||||
|
///
|
||||||
|
/// This can be used to write data directly into the buffer without triggering writers
|
||||||
|
/// to the underlying writer.
|
||||||
|
///
|
||||||
|
/// That the buffer is a `Vec` is an implementation detail.
|
||||||
|
/// Callers should not modify the capacity as there currently is no public API to do so
|
||||||
|
/// and thus any capacity changes would be unexpected by the user.
|
||||||
|
pub(crate) fn buffer_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of bytes the internal buffer can hold without flushing.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // Check the capacity of the inner buffer
|
||||||
|
/// let capacity = buf_writer.capacity();
|
||||||
|
/// // Calculate how many bytes can be written without flushing
|
||||||
|
/// let without_flush = capacity - buf_writer.buffer().len();
|
||||||
|
/// ```
|
||||||
|
pub fn capacity(&self) -> usize {
|
||||||
|
self.buf.capacity()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwraps this `BufWriter<W>`, 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // unwrap the TcpStream and flush the buffer
|
||||||
|
/// let stream = buffer.into_inner().unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
|
||||||
|
match self.flush_buf() {
|
||||||
|
Err(e) => Err(IntoInnerError::new(self, e)),
|
||||||
|
Ok(()) => Ok(self.into_parts().0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disassembles this `BufWriter<W>`, 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{BufWriter, Write};
|
||||||
|
///
|
||||||
|
/// let mut buffer = [0u8; 10];
|
||||||
|
/// let mut stream = BufWriter::new(buffer.as_mut());
|
||||||
|
/// write!(stream, "too much data").unwrap();
|
||||||
|
/// stream.flush().expect_err("it doesn't fit");
|
||||||
|
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||||
|
/// assert_eq!(recovered_writer.len(), 0);
|
||||||
|
/// assert_eq!(&buffered_data.unwrap(), b"ata");
|
||||||
|
/// ```
|
||||||
|
pub fn into_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) {
|
||||||
|
let buf = mem::take(&mut self.buf);
|
||||||
|
let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
|
||||||
|
|
||||||
|
// SAFETY: forget(self) prevents double dropping inner
|
||||||
|
let inner = unsafe { ptr::read(&mut self.inner) };
|
||||||
|
mem::forget(self);
|
||||||
|
|
||||||
|
(inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]) -> io::Result<usize> {
|
||||||
|
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]) -> io::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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
let dst = self.buf.as_mut_ptr().add(old_len);
|
||||||
|
ptr::copy_nonoverlapping(src, dst, buf_len);
|
||||||
|
self.buf.set_len(old_len + buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn spare_capacity(&self) -> usize {
|
||||||
|
self.buf.capacity() - self.buf.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error returned for the buffered data from `BufWriter::into_parts`, when the underlying
|
||||||
|
/// writer has previously panicked. Contains the (possibly partly written) buffered data.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{self, BufWriter, Write};
|
||||||
|
/// use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
|
///
|
||||||
|
/// struct PanickingWriter;
|
||||||
|
/// impl Write for PanickingWriter {
|
||||||
|
/// fn write(&mut self, buf: &[u8]) -> io::Result<usize> { panic!() }
|
||||||
|
/// fn flush(&mut self) -> io::Result<()> { panic!() }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let mut stream = BufWriter::new(PanickingWriter);
|
||||||
|
/// write!(stream, "some data").unwrap();
|
||||||
|
/// let result = catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
/// stream.flush().unwrap()
|
||||||
|
/// }));
|
||||||
|
/// assert!(result.is_err());
|
||||||
|
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||||
|
/// assert!(matches!(recovered_writer, PanickingWriter));
|
||||||
|
/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data");
|
||||||
|
/// ```
|
||||||
|
pub struct WriterPanicked {
|
||||||
|
buf: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
pub fn into_inner(self) -> Vec<u8> {
|
||||||
|
self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
const DESCRIPTION: &'static str =
|
||||||
|
"BufWriter inner writer panicked, what data remains unwritten is not known";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WriterPanicked {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", Self::DESCRIPTION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<W: Write> Write for BufWriter<W> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
// 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]) -> io::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 write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
// FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied
|
||||||
|
// to `write` and `write_all`. The performance benefits can be significant. See #79930.
|
||||||
|
if self.get_ref().is_write_vectored() {
|
||||||
|
// We have to handle the possibility that the total length of the buffers overflows
|
||||||
|
// `usize` (even though this can only happen if multiple `IoSlice`s reference the
|
||||||
|
// same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the
|
||||||
|
// computation overflows, then surely the input cannot fit in our buffer, so we forward
|
||||||
|
// to the inner writer's `write_vectored` method to let it handle it appropriately.
|
||||||
|
let saturated_total_len =
|
||||||
|
bufs.iter().fold(0usize, |acc, b| acc.saturating_add(b.len()));
|
||||||
|
|
||||||
|
if saturated_total_len > self.spare_capacity() {
|
||||||
|
// Flush if the total length of the input exceeds our buffer's spare capacity.
|
||||||
|
// If we would have overflowed, this condition also holds, and we need to flush.
|
||||||
|
self.flush_buf()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if saturated_total_len >= self.buf.capacity() {
|
||||||
|
// Forward to our inner writer if the total length of the input is greater than or
|
||||||
|
// equal to our buffer capacity. If we would have overflowed, this condition also
|
||||||
|
// holds, and we punt to the inner writer.
|
||||||
|
self.panicked = true;
|
||||||
|
let r = self.get_mut().write_vectored(bufs);
|
||||||
|
self.panicked = false;
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
// `saturated_total_len < self.buf.capacity()` implies that we did not saturate.
|
||||||
|
|
||||||
|
// SAFETY: We checked whether or not the spare capacity was large enough above. If
|
||||||
|
// it was, then we're safe already. If it wasn't, we flushed, making sufficient
|
||||||
|
// room for any input <= the buffer size, which includes this input.
|
||||||
|
unsafe {
|
||||||
|
bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(saturated_total_len)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut iter = bufs.iter();
|
||||||
|
let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {
|
||||||
|
// This is the first non-empty slice to write, so if it does
|
||||||
|
// not fit in the buffer, we still get to flush and proceed.
|
||||||
|
if buf.len() > self.spare_capacity() {
|
||||||
|
self.flush_buf()?;
|
||||||
|
}
|
||||||
|
if buf.len() >= self.buf.capacity() {
|
||||||
|
// The slice is at least as large as the buffering capacity,
|
||||||
|
// so it's better to write it directly, bypassing the buffer.
|
||||||
|
self.panicked = true;
|
||||||
|
let r = self.get_mut().write(buf);
|
||||||
|
self.panicked = false;
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
|
// SAFETY: We checked whether or not the spare capacity was large enough above.
|
||||||
|
// If it was, then we're safe already. If it wasn't, we flushed, making
|
||||||
|
// sufficient room for any input <= the buffer size, which includes this input.
|
||||||
|
unsafe {
|
||||||
|
self.write_to_buffer_unchecked(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.len()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(0);
|
||||||
|
};
|
||||||
|
debug_assert!(total_written != 0);
|
||||||
|
for buf in iter {
|
||||||
|
if buf.len() <= self.spare_capacity() {
|
||||||
|
// SAFETY: safe by above conditional.
|
||||||
|
unsafe {
|
||||||
|
self.write_to_buffer_unchecked(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This cannot overflow `usize`. If we are here, we've written all of the bytes
|
||||||
|
// so far to our buffer, and we've ensured that we never exceed the buffer's
|
||||||
|
// capacity. Therefore, `total_written` <= `self.buf.capacity()` <= `usize::MAX`.
|
||||||
|
total_written += buf.len();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(total_written)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.flush_buf().and_then(|()| self.get_mut().flush())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> fmt::Debug for BufWriter<W>
|
||||||
|
where
|
||||||
|
W: fmt::Debug,
|
||||||
|
{
|
||||||
|
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<W: Write + Seek> Seek for BufWriter<W> {
|
||||||
|
/// 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) -> io::Result<u64> {
|
||||||
|
self.flush_buf()?;
|
||||||
|
self.get_mut().seek(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> Drop for BufWriter<W> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.panicked {
|
||||||
|
// dtors should not panic, so we ignore a failed flush
|
||||||
|
let _r = self.flush_buf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
224
src/buffered/linewriter.rs
Normal file
224
src/buffered/linewriter.rs
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
use core::fmt;
|
||||||
|
use crate::{self as io, buffered::LineWriterShim, BufWriter, IntoInnerError, IoSlice, 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// We can use `LineWriter` to write one line at a time, significantly
|
||||||
|
/// reducing the number of actual writes to the file.
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::fs::{self, File};
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::LineWriter;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let road_not_taken = b"I shall be telling this with a sigh
|
||||||
|
/// Somewhere ages and ages hence:
|
||||||
|
/// Two roads diverged in a wood, and I -
|
||||||
|
/// I took the one less traveled by,
|
||||||
|
/// And that has made all the difference.";
|
||||||
|
///
|
||||||
|
/// let file = File::create("poem.txt")?;
|
||||||
|
/// let mut file = LineWriter::new(file);
|
||||||
|
///
|
||||||
|
/// file.write_all(b"I shall be telling this with a sigh")?;
|
||||||
|
///
|
||||||
|
/// // No bytes are written until a newline is encountered (or
|
||||||
|
/// // the internal buffer is filled).
|
||||||
|
/// assert_eq!(fs::read_to_string("poem.txt")?, "");
|
||||||
|
/// file.write_all(b"\n")?;
|
||||||
|
/// assert_eq!(
|
||||||
|
/// fs::read_to_string("poem.txt")?,
|
||||||
|
/// "I shall be telling this with a sigh\n",
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // Write the rest of the poem.
|
||||||
|
/// file.write_all(b"Somewhere ages and ages hence:
|
||||||
|
/// Two roads diverged in a wood, and I -
|
||||||
|
/// I took the one less traveled by,
|
||||||
|
/// And that has made all the difference.")?;
|
||||||
|
///
|
||||||
|
/// // The last line of the poem doesn't end in a newline, so
|
||||||
|
/// // we have to flush or drop the `LineWriter` to finish
|
||||||
|
/// // writing.
|
||||||
|
/// file.flush()?;
|
||||||
|
///
|
||||||
|
/// // Confirm the whole poem was written.
|
||||||
|
/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct LineWriter<W: Write> {
|
||||||
|
inner: BufWriter<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> LineWriter<W> {
|
||||||
|
/// Creates a new `LineWriter`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::fs::File;
|
||||||
|
/// use std::io::LineWriter;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let file = File::create("poem.txt")?;
|
||||||
|
/// let file = LineWriter::new(file);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn new(inner: W) -> LineWriter<W> {
|
||||||
|
// Lines typically aren't that long, don't use a giant buffer
|
||||||
|
LineWriter::with_capacity(1024, inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `LineWriter` with a specified capacity for the internal
|
||||||
|
/// buffer.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::fs::File;
|
||||||
|
/// use std::io::LineWriter;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let file = File::create("poem.txt")?;
|
||||||
|
/// let file = LineWriter::with_capacity(100, file);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> {
|
||||||
|
LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a reference to the underlying writer.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::fs::File;
|
||||||
|
/// use std::io::LineWriter;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let file = File::create("poem.txt")?;
|
||||||
|
/// let file = LineWriter::new(file);
|
||||||
|
///
|
||||||
|
/// let reference = file.get_ref();
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::fs::File;
|
||||||
|
/// use std::io::LineWriter;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let file = File::create("poem.txt")?;
|
||||||
|
/// let mut file = LineWriter::new(file);
|
||||||
|
///
|
||||||
|
/// // we can use reference just like file
|
||||||
|
/// let reference = file.get_mut();
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn get_mut(&mut self) -> &mut W {
|
||||||
|
self.inner.get_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::fs::File;
|
||||||
|
/// use std::io::LineWriter;
|
||||||
|
///
|
||||||
|
/// fn main() -> std::io::Result<()> {
|
||||||
|
/// let file = File::create("poem.txt")?;
|
||||||
|
///
|
||||||
|
/// let writer: LineWriter<File> = LineWriter::new(file);
|
||||||
|
///
|
||||||
|
/// let file: File = writer.into_inner()?;
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
|
||||||
|
self.inner.into_inner().map_err(|err| err.new_wrapped(|inner| LineWriter { inner }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> Write for LineWriter<W> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
LineWriterShim::new(&mut self.inner).write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.inner.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
LineWriterShim::new(&mut self.inner).write_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
self.inner.is_write_vectored()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||||
|
LineWriterShim::new(&mut self.inner).write_all(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||||
|
LineWriterShim::new(&mut self.inner).write_all_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||||
|
LineWriterShim::new(&mut self.inner).write_fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> fmt::Debug for LineWriter<W>
|
||||||
|
where
|
||||||
|
W: fmt::Debug,
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
276
src/buffered/linewritershim.rs
Normal file
276
src/buffered/linewritershim.rs
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
use crate::{self as io, BufWriter, IoSlice, Write};
|
||||||
|
use core::slice::memchr;
|
||||||
|
|
||||||
|
/// 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: Write> {
|
||||||
|
buffer: &'a mut BufWriter<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, W: Write> LineWriterShim<'a, W> {
|
||||||
|
pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
|
||||||
|
Self { buffer }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the inner writer (that is, the writer
|
||||||
|
/// wrapped by the BufWriter).
|
||||||
|
fn inner(&self) -> &W {
|
||||||
|
self.buffer.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the content currently buffered in self.buffer
|
||||||
|
fn buffered(&self) -> &[u8] {
|
||||||
|
self.buffer.buffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flush 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) -> io::Result<()> {
|
||||||
|
match self.buffered().last().copied() {
|
||||||
|
Some(b'\n') => self.buffer.flush_buf(),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, W: Write> Write for LineWriterShim<'a, W> {
|
||||||
|
/// Write some data into this BufReader 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]) -> io::Result<usize> {
|
||||||
|
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 {
|
||||||
|
&buf[flushed..]
|
||||||
|
} 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) -> io::Result<()> {
|
||||||
|
self.buffer.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write some vectored data into this BufReader with line buffering. This
|
||||||
|
/// means that, if any newlines are present in the data, the data up to
|
||||||
|
/// and including the buffer containing the last newline is sent directly
|
||||||
|
/// to the inner writer, and the 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.
|
||||||
|
///
|
||||||
|
/// Because this function attempts to send completed lines to the underlying
|
||||||
|
/// writer, it will also flush the existing buffer if it contains any
|
||||||
|
/// newlines.
|
||||||
|
///
|
||||||
|
/// Because sorting through an array of `IoSlice` can be a bit convoluted,
|
||||||
|
/// This method differs from write in the following ways:
|
||||||
|
///
|
||||||
|
/// - It attempts to write the full content of all the buffers up to and
|
||||||
|
/// including the one containing the last newline. This means that it
|
||||||
|
/// may attempt to write a partial line, that buffer has data past the
|
||||||
|
/// newline.
|
||||||
|
/// - If the write only reports partial success, it does not attempt to
|
||||||
|
/// find the precise location of the written bytes and buffer the rest.
|
||||||
|
///
|
||||||
|
/// If the underlying vector doesn't support vectored writing, we instead
|
||||||
|
/// simply write the first non-empty buffer with `write`. This way, we
|
||||||
|
/// get the benefits of more granular partial-line handling without losing
|
||||||
|
/// anything in efficiency
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
// If there's no specialized behavior for write_vectored, just use
|
||||||
|
// write. This has the benefit of more granular partial-line handling.
|
||||||
|
if !self.is_write_vectored() {
|
||||||
|
return match bufs.iter().find(|buf| !buf.is_empty()) {
|
||||||
|
Some(buf) => self.write(buf),
|
||||||
|
None => Ok(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the buffer containing the last newline
|
||||||
|
let last_newline_buf_idx = bufs
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i));
|
||||||
|
|
||||||
|
// If there are no new newlines (that is, if this write is less than
|
||||||
|
// one line), just do a regular buffered write
|
||||||
|
let last_newline_buf_idx = match last_newline_buf_idx {
|
||||||
|
// No newlines; just do a normal buffered write
|
||||||
|
None => {
|
||||||
|
self.flush_if_completed_line()?;
|
||||||
|
return self.buffer.write_vectored(bufs);
|
||||||
|
}
|
||||||
|
Some(i) => i,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flush existing content to prepare for our write
|
||||||
|
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, tail) = bufs.split_at(last_newline_buf_idx + 1);
|
||||||
|
|
||||||
|
// 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.panicked here.
|
||||||
|
let flushed = self.inner_mut().write_vectored(lines)?;
|
||||||
|
|
||||||
|
// If inner 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't try to reconstruct the exact amount written; just bail
|
||||||
|
// in the event of a partial write
|
||||||
|
let lines_len = lines.iter().map(|buf| buf.len()).sum();
|
||||||
|
if flushed < lines_len {
|
||||||
|
return Ok(flushed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that the write has succeeded, buffer the rest (or as much of the
|
||||||
|
// rest as possible)
|
||||||
|
let buffered: usize = tail
|
||||||
|
.iter()
|
||||||
|
.filter(|buf| !buf.is_empty())
|
||||||
|
.map(|buf| self.buffer.write_to_buf(buf))
|
||||||
|
.take_while(|&n| n > 0)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
Ok(flushed + buffered)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
self.inner().is_write_vectored()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write some data into this BufReader 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]) -> io::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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
179
src/buffered/mod.rs
Normal file
179
src/buffered/mod.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
//! Buffering wrappers for I/O traits
|
||||||
|
|
||||||
|
mod bufreader;
|
||||||
|
mod bufwriter;
|
||||||
|
mod linewriter;
|
||||||
|
mod linewritershim;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
pub use self::bufreader::BufReader;
|
||||||
|
pub use self::bufwriter::BufWriter;
|
||||||
|
pub use self::bufwriter::WriterPanicked;
|
||||||
|
pub use self::linewriter::LineWriter;
|
||||||
|
use self::linewritershim::LineWriterShim;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // do stuff with the stream
|
||||||
|
///
|
||||||
|
/// // we want to get our `TcpStream` back, so let's try:
|
||||||
|
///
|
||||||
|
/// let stream = match stream.into_inner() {
|
||||||
|
/// Ok(s) => s,
|
||||||
|
/// Err(e) => {
|
||||||
|
/// // Here, e is an IntoInnerError
|
||||||
|
/// panic!("An error occurred");
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IntoInnerError<W>(W, Error);
|
||||||
|
|
||||||
|
impl<W> IntoInnerError<W> {
|
||||||
|
/// Construct 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<W2>(self, f: impl FnOnce(W) -> W2) -> IntoInnerError<W2> {
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // do stuff with the stream
|
||||||
|
///
|
||||||
|
/// // we want to get our `TcpStream` back, so let's try:
|
||||||
|
///
|
||||||
|
/// let stream = match stream.into_inner() {
|
||||||
|
/// Ok(s) => s,
|
||||||
|
/// Err(e) => {
|
||||||
|
/// // Here, e is an IntoInnerError, let's log the inner error.
|
||||||
|
/// //
|
||||||
|
/// // We'll just 'log' to stdout for this example.
|
||||||
|
/// println!("{}", e.error());
|
||||||
|
///
|
||||||
|
/// panic!("An unexpected error occurred.");
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::BufWriter;
|
||||||
|
/// use std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||||
|
///
|
||||||
|
/// // do stuff with the stream
|
||||||
|
///
|
||||||
|
/// // we want to get our `TcpStream` back, so let's try:
|
||||||
|
///
|
||||||
|
/// let stream = match stream.into_inner() {
|
||||||
|
/// Ok(s) => s,
|
||||||
|
/// Err(e) => {
|
||||||
|
/// // Here, e is an IntoInnerError, let's re-examine the buffer:
|
||||||
|
/// let buffer = e.into_inner();
|
||||||
|
///
|
||||||
|
/// // do stuff to try to recover
|
||||||
|
///
|
||||||
|
/// // afterwards, let's just return the stream
|
||||||
|
/// buffer.into_inner().unwrap()
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||||
|
///
|
||||||
|
/// let mut not_enough_space = [0u8; 10];
|
||||||
|
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||||
|
/// write!(stream, "this cannot be actually written").unwrap();
|
||||||
|
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||||
|
/// let err = into_inner_err.into_error();
|
||||||
|
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||||
|
/// ```
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||||
|
///
|
||||||
|
/// let mut not_enough_space = [0u8; 10];
|
||||||
|
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||||
|
/// write!(stream, "this cannot be actually written").unwrap();
|
||||||
|
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||||
|
/// let (err, recovered_writer) = into_inner_err.into_parts();
|
||||||
|
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||||
|
/// assert_eq!(recovered_writer.buffer(), b"t be actually written");
|
||||||
|
/// ```
|
||||||
|
pub fn into_parts(self) -> (Error, W) {
|
||||||
|
(self.1, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> From<IntoInnerError<W>> for Error {
|
||||||
|
fn from(iie: IntoInnerError<W>) -> Error {
|
||||||
|
iie.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> fmt::Display for IntoInnerError<W> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.error().fmt(f)
|
||||||
|
}
|
||||||
|
}
|
970
src/buffered/tests.rs
Normal file
970
src/buffered/tests.rs
Normal file
@ -0,0 +1,970 @@
|
|||||||
|
use crate::io::prelude::*;
|
||||||
|
use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom};
|
||||||
|
use crate::panic;
|
||||||
|
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use crate::thread;
|
||||||
|
|
||||||
|
/// A dummy reader intended at testing short-reads propagation.
|
||||||
|
pub struct ShortReader {
|
||||||
|
lengths: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: rustfmt and tidy disagree about the correct formatting of this
|
||||||
|
// function. This leads to issues for users with editors configured to
|
||||||
|
// rustfmt-on-save.
|
||||||
|
impl Read for ShortReader {
|
||||||
|
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||||
|
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_seek() {
|
||||||
|
let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
|
||||||
|
let mut reader = BufReader::with_capacity(2, io::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.seek(SeekFrom::Current(0)).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, io::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, io::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]
|
||||||
|
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, io::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, io::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, io::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]) -> io::Result<usize> {
|
||||||
|
let len = buf.len();
|
||||||
|
for x in buf {
|
||||||
|
*x = self.pos as u8;
|
||||||
|
self.pos = self.pos.wrapping_add(1);
|
||||||
|
}
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Seek for PositionReader {
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
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]) -> io::Result<usize> {
|
||||||
|
for x in &mut *buf {
|
||||||
|
*x = 0;
|
||||||
|
}
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Seek for ErrAfterFirstSeekReader {
|
||||||
|
fn seek(&mut self, _: SeekFrom) -> io::Result<u64> {
|
||||||
|
if self.first_seek {
|
||||||
|
self.first_seek = false;
|
||||||
|
Ok(0)
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "oh no!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buffered_writer_seek() {
|
||||||
|
let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new()));
|
||||||
|
w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap();
|
||||||
|
w.write_all(&[6, 7]).unwrap();
|
||||||
|
assert_eq!(w.seek(SeekFrom::Current(0)).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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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']);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]) -> io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(target_os = "emscripten", ignore)]
|
||||||
|
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]) -> io::Result<usize> {
|
||||||
|
WRITES.fetch_add(1, Ordering::SeqCst);
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(io::empty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_buffered_reader_small_reads(b: &mut test::Bencher) {
|
||||||
|
let data = (0..u8::MAX).cycle().take(1024 * 4).collect::<Vec<_>>();
|
||||||
|
b.iter(|| {
|
||||||
|
let mut reader = BufReader::new(&data[..]);
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
for _ in 0..1024 {
|
||||||
|
reader.read_exact(&mut buf).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_buffered_writer(b: &mut test::Bencher) {
|
||||||
|
b.iter(|| BufWriter::new(io::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<u8>,
|
||||||
|
|
||||||
|
// Flush sets this flag
|
||||||
|
pub flushed: bool,
|
||||||
|
|
||||||
|
// 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<usize>,
|
||||||
|
|
||||||
|
// If set, counts down with each write, and writes return an error
|
||||||
|
// when it hits 0
|
||||||
|
pub max_writes: Option<usize>,
|
||||||
|
|
||||||
|
// 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]) -> io::Result<usize> {
|
||||||
|
if self.always_write_error {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "test - always_write_error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.max_writes {
|
||||||
|
Some(0) if self.error_after_max_writes => {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "test - max_writes"));
|
||||||
|
}
|
||||||
|
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) -> io::Result<()> {
|
||||||
|
if self.always_flush_error {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "test - always_flush_error"))
|
||||||
|
} else {
|
||||||
|
self.flushed = true;
|
||||||
|
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]
|
||||||
|
fn line_vectored() {
|
||||||
|
let mut a = LineWriter::new(Vec::new());
|
||||||
|
assert_eq!(
|
||||||
|
a.write_vectored(&[
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(b"\n"),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(b"a"),
|
||||||
|
])
|
||||||
|
.unwrap(),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
assert_eq!(a.get_ref(), b"\n");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
a.write_vectored(&[
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(b"b"),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(b"a"),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(b"c"),
|
||||||
|
])
|
||||||
|
.unwrap(),
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
assert_eq!(a.get_ref(), b"\n");
|
||||||
|
a.flush().unwrap();
|
||||||
|
assert_eq!(a.get_ref(), b"\nabac");
|
||||||
|
assert_eq!(a.write_vectored(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
a.write_vectored(&[
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
])
|
||||||
|
.unwrap(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3);
|
||||||
|
assert_eq!(a.get_ref(), b"\nabaca\nb");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn line_vectored_partial_and_errors() {
|
||||||
|
use crate::collections::VecDeque;
|
||||||
|
|
||||||
|
enum Call {
|
||||||
|
Write { inputs: Vec<&'static [u8]>, output: io::Result<usize> },
|
||||||
|
Flush { output: io::Result<()> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Writer {
|
||||||
|
calls: VecDeque<Call>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Writer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.write_vectored(&[IoSlice::new(buf)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
match self.calls.pop_front().expect("unexpected call to write") {
|
||||||
|
Call::Write { inputs, output } => {
|
||||||
|
assert_eq!(inputs, buf.iter().map(|b| &**b).collect::<Vec<_>>());
|
||||||
|
output
|
||||||
|
}
|
||||||
|
Call::Flush { .. } => panic!("unexpected call to write; expected a flush"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
match self.calls.pop_front().expect("Unexpected call to flush") {
|
||||||
|
Call::Flush { output } => output,
|
||||||
|
Call::Write { .. } => panic!("unexpected call to flush; expected a write"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Writer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !thread::panicking() {
|
||||||
|
assert_eq!(self.calls.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// partial writes keep going
|
||||||
|
let mut a = LineWriter::new(Writer::default());
|
||||||
|
a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap();
|
||||||
|
|
||||||
|
a.get_mut().calls.push_back(Call::Write { inputs: vec![b"abc"], output: Ok(1) });
|
||||||
|
a.get_mut().calls.push_back(Call::Write { inputs: vec![b"bc"], output: Ok(2) });
|
||||||
|
a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\n"], output: Ok(2) });
|
||||||
|
|
||||||
|
a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap();
|
||||||
|
|
||||||
|
a.get_mut().calls.push_back(Call::Flush { output: Ok(()) });
|
||||||
|
a.flush().unwrap();
|
||||||
|
|
||||||
|
// erroneous writes stop and don't write more
|
||||||
|
a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\na"], output: Err(err()) });
|
||||||
|
a.get_mut().calls.push_back(Call::Flush { output: Ok(()) });
|
||||||
|
assert!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).is_err());
|
||||||
|
a.flush().unwrap();
|
||||||
|
|
||||||
|
fn err() -> io::Error {
|
||||||
|
io::Error::new(io::ErrorKind::Other, "x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that, in cases where vectored writing is not enabled, the
|
||||||
|
/// LineWriter uses the normal `write` call, which more-correctly handles
|
||||||
|
/// partial lines
|
||||||
|
#[test]
|
||||||
|
fn line_vectored_ignored() {
|
||||||
|
let writer = ProgrammableSink::default();
|
||||||
|
let mut writer = LineWriter::new(writer);
|
||||||
|
|
||||||
|
let content = [
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(b"Line 1\nLine"),
|
||||||
|
IoSlice::new(b" 2\nLine 3\nL"),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(&[]),
|
||||||
|
IoSlice::new(b"ine 4"),
|
||||||
|
IoSlice::new(b"\nLine 5\n"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let count = writer.write_vectored(&content).unwrap();
|
||||||
|
assert_eq!(count, 11);
|
||||||
|
assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
|
||||||
|
|
||||||
|
let count = writer.write_vectored(&content[2..]).unwrap();
|
||||||
|
assert_eq!(count, 11);
|
||||||
|
assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
|
||||||
|
|
||||||
|
let count = writer.write_vectored(&content[5..]).unwrap();
|
||||||
|
assert_eq!(count, 5);
|
||||||
|
assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
|
||||||
|
|
||||||
|
let count = writer.write_vectored(&content[6..]).unwrap();
|
||||||
|
assert_eq!(count, 8);
|
||||||
|
assert_eq!(
|
||||||
|
writer.get_ref().buffer.as_slice(),
|
||||||
|
b"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n".as_ref()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, given a partial line that exceeds the length of
|
||||||
|
/// LineBuffer's buffer (that is, without a trailing newline), that that
|
||||||
|
/// line is written to the inner writer
|
||||||
|
#[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, that that line is buffered unconditionally,
|
||||||
|
/// and no additional writes take place. This assures the property that
|
||||||
|
/// `write` should make at-most-one attempt to write new data.
|
||||||
|
#[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 01234 is buffered
|
||||||
|
assert_eq!(writer.write(b"Line 1\n0123456789").unwrap(), 12);
|
||||||
|
assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
|
||||||
|
|
||||||
|
// 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"Line 1\n01234");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.kind(), ErrorKind::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.
|
||||||
|
#[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
|
||||||
|
#[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<RecordedEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for WriteRecorder {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
use crate::str::from_utf8;
|
||||||
|
|
||||||
|
self.events.push(RecordedEvent::Write(from_utf8(buf).unwrap().to_string()));
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::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.
|
||||||
|
writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap();
|
||||||
|
assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!\n".to_string())]);
|
||||||
|
}
|
151
src/copy.rs
Normal file
151
src/copy.rs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
use super::{ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
|
||||||
|
#[cfg(feature = "collections")] use super::BufWriter;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// If you’re wanting to copy the contents of one file to another and you’re
|
||||||
|
/// working with filesystem paths, see the [`fs::copy`] function.
|
||||||
|
///
|
||||||
|
/// [`fs::copy`]: crate::fs::copy
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error immediately if any call to [`read`] or
|
||||||
|
/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
|
||||||
|
/// handled by this function and the underlying operation is retried.
|
||||||
|
///
|
||||||
|
/// [`read`]: Read::read
|
||||||
|
/// [`write`]: Write::write
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// fn main() -> io::Result<()> {
|
||||||
|
/// let mut reader: &[u8] = b"hello";
|
||||||
|
/// let mut writer: Vec<u8> = vec![];
|
||||||
|
///
|
||||||
|
/// io::copy(&mut reader, &mut writer)?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(&b"hello"[..], &writer[..]);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
|
||||||
|
where
|
||||||
|
R: Read,
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
generic_copy(reader, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The userspace read-write-loop implementation of `io::copy` that is used when
|
||||||
|
/// OS-specific specializations for copy offloading are not available or not applicable.
|
||||||
|
pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
|
||||||
|
where
|
||||||
|
R: Read,
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
BufferedCopySpec::copy_to(reader, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specialization of the read-write loop that either uses a stack buffer
|
||||||
|
/// or reuses the internal buffer of a BufWriter
|
||||||
|
trait BufferedCopySpec: Write {
|
||||||
|
fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write + ?Sized> BufferedCopySpec for W {
|
||||||
|
default fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> {
|
||||||
|
stack_buffer_copy(reader, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
impl<I: Write> BufferedCopySpec for BufWriter<I> {
|
||||||
|
fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> {
|
||||||
|
if writer.capacity() < DEFAULT_BUF_SIZE {
|
||||||
|
return stack_buffer_copy(reader, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: #42788
|
||||||
|
//
|
||||||
|
// - This creates a (mut) reference to a slice of
|
||||||
|
// _uninitialized_ integers, which is **undefined behavior**
|
||||||
|
//
|
||||||
|
// - Only the standard library gets to soundly "ignore" this,
|
||||||
|
// based on its privileged knowledge of unstable rustc
|
||||||
|
// internals;
|
||||||
|
unsafe {
|
||||||
|
let spare_cap = writer.buffer_mut().spare_capacity_mut();
|
||||||
|
reader.initializer().initialize(MaybeUninit::slice_assume_init_mut(spare_cap));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut len = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let buf = writer.buffer_mut();
|
||||||
|
let spare_cap = buf.spare_capacity_mut();
|
||||||
|
|
||||||
|
if spare_cap.len() >= DEFAULT_BUF_SIZE {
|
||||||
|
match reader.read(unsafe { MaybeUninit::slice_assume_init_mut(spare_cap) }) {
|
||||||
|
Ok(0) => return Ok(len), // EOF reached
|
||||||
|
Ok(bytes_read) => {
|
||||||
|
assert!(bytes_read <= spare_cap.len());
|
||||||
|
// SAFETY: The initializer contract guarantees that either it or `read`
|
||||||
|
// will have initialized these bytes. And we just checked that the number
|
||||||
|
// of bytes is within the buffer capacity.
|
||||||
|
unsafe { buf.set_len(buf.len() + bytes_read) };
|
||||||
|
len += bytes_read as u64;
|
||||||
|
// Read again if the buffer still has enough capacity, as BufWriter itself would do
|
||||||
|
// This will occur if the reader returns short reads
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.flush_buf()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
|
||||||
|
reader: &mut R,
|
||||||
|
writer: &mut W,
|
||||||
|
) -> Result<u64> {
|
||||||
|
let mut buf = MaybeUninit::<[u8; DEFAULT_BUF_SIZE]>::uninit();
|
||||||
|
// FIXME: #42788
|
||||||
|
//
|
||||||
|
// - This creates a (mut) reference to a slice of
|
||||||
|
// _uninitialized_ integers, which is **undefined behavior**
|
||||||
|
//
|
||||||
|
// - Only the standard library gets to soundly "ignore" this,
|
||||||
|
// based on its privileged knowledge of unstable rustc
|
||||||
|
// internals;
|
||||||
|
unsafe {
|
||||||
|
reader.initializer().initialize(buf.assume_init_mut());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut written = 0;
|
||||||
|
loop {
|
||||||
|
let len = match reader.read(unsafe { buf.assume_init_mut() }) {
|
||||||
|
Ok(0) => return Ok(written),
|
||||||
|
Ok(len) => len,
|
||||||
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
|
||||||
|
written += len as u64;
|
||||||
|
}
|
||||||
|
}
|
515
src/cursor.rs
Normal file
515
src/cursor.rs
Normal file
@ -0,0 +1,515 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use core::cmp;
|
||||||
|
use crate::{self as io, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom};
|
||||||
|
|
||||||
|
#[cfg(feature="collections")] use core::convert::TryInto;
|
||||||
|
#[cfg(feature="collections")] use crate::Vec;
|
||||||
|
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// The standard library implements some I/O traits on various types which
|
||||||
|
/// are commonly used as a buffer, like `Cursor<`[`Vec`]`<u8>>` and
|
||||||
|
/// `Cursor<`[`&[u8]`][bytes]`>`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// We may want to write bytes to a [`File`] in our production
|
||||||
|
/// code, but use an in-memory buffer in our tests. We can do this with
|
||||||
|
/// `Cursor`:
|
||||||
|
///
|
||||||
|
/// [bytes]: crate::slice
|
||||||
|
/// [`File`]: crate::fs::File
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::{self, SeekFrom};
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// // a library function we've written
|
||||||
|
/// fn write_ten_bytes_at_end<W: Write + Seek>(writer: &mut W) -> io::Result<()> {
|
||||||
|
/// writer.seek(SeekFrom::End(-10))?;
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// writer.write(&[i])?;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // all went well
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn foo() -> io::Result<()> {
|
||||||
|
/// // Here's some code that uses this library function.
|
||||||
|
/// //
|
||||||
|
/// // We might want to use a BufReader here for efficiency, but let's
|
||||||
|
/// // keep this example focused.
|
||||||
|
/// let mut file = File::create("foo.txt")?;
|
||||||
|
///
|
||||||
|
/// write_ten_bytes_at_end(&mut file)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// // now let's write a test
|
||||||
|
/// #[test]
|
||||||
|
/// fn test_writes_bytes() {
|
||||||
|
/// // setting up a real File is much slower than an in-memory buffer,
|
||||||
|
/// // let's use a cursor instead
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
/// let mut buff = Cursor::new(vec![0; 15]);
|
||||||
|
///
|
||||||
|
/// write_ten_bytes_at_end(&mut buff).unwrap();
|
||||||
|
///
|
||||||
|
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct Cursor<T> {
|
||||||
|
inner: T,
|
||||||
|
pos: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cursor<T> {
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
/// ```
|
||||||
|
pub const fn new(inner: T) -> Cursor<T> {
|
||||||
|
Cursor { pos: 0, inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes this cursor, returning the underlying value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let vec = buff.into_inner();
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a reference to the underlying value in this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let reference = buff.get_ref();
|
||||||
|
/// ```
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let reference = buff.get_mut();
|
||||||
|
/// ```
|
||||||
|
pub fn get_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current position of this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::SeekFrom;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(buff.position(), 0);
|
||||||
|
///
|
||||||
|
/// buff.seek(SeekFrom::Current(2)).unwrap();
|
||||||
|
/// assert_eq!(buff.position(), 2);
|
||||||
|
///
|
||||||
|
/// buff.seek(SeekFrom::Current(-1)).unwrap();
|
||||||
|
/// assert_eq!(buff.position(), 1);
|
||||||
|
/// ```
|
||||||
|
pub const fn position(&self) -> u64 {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the position of this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(buff.position(), 0);
|
||||||
|
///
|
||||||
|
/// buff.set_position(2);
|
||||||
|
/// assert_eq!(buff.position(), 2);
|
||||||
|
///
|
||||||
|
/// buff.set_position(4);
|
||||||
|
/// assert_eq!(buff.position(), 4);
|
||||||
|
/// ```
|
||||||
|
pub fn set_position(&mut self, pos: u64) {
|
||||||
|
self.pos = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cursor<T>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
/// Returns the remaining slice.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(cursor_remaining)]
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(buff.remaining_slice(), &[1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// buff.set_position(2);
|
||||||
|
/// assert_eq!(buff.remaining_slice(), &[3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// buff.set_position(4);
|
||||||
|
/// assert_eq!(buff.remaining_slice(), &[5]);
|
||||||
|
///
|
||||||
|
/// buff.set_position(6);
|
||||||
|
/// assert_eq!(buff.remaining_slice(), &[]);
|
||||||
|
/// ```
|
||||||
|
pub fn remaining_slice(&self) -> &[u8] {
|
||||||
|
let len = self.pos.min(self.inner.as_ref().len() as u64);
|
||||||
|
&self.inner.as_ref()[(len as usize)..]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the remaining slice is empty.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(cursor_remaining)]
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// buff.set_position(2);
|
||||||
|
/// assert!(!buff.is_empty());
|
||||||
|
///
|
||||||
|
/// buff.set_position(5);
|
||||||
|
/// assert!(buff.is_empty());
|
||||||
|
///
|
||||||
|
/// buff.set_position(10);
|
||||||
|
/// assert!(buff.is_empty());
|
||||||
|
/// ```
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.pos >= self.inner.as_ref().len() as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for Cursor<T>
|
||||||
|
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<T> io::Seek for Cursor<T>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
let new_pos = if offset >= 0 {
|
||||||
|
base_pos.checked_add(offset as u64)
|
||||||
|
} else {
|
||||||
|
base_pos.checked_sub((offset.wrapping_neg()) as u64)
|
||||||
|
};
|
||||||
|
match new_pos {
|
||||||
|
Some(n) => {
|
||||||
|
self.pos = n;
|
||||||
|
Ok(self.pos)
|
||||||
|
}
|
||||||
|
None => Err(Error::new_const(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
&"invalid seek to a negative or overflowing position",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_len(&mut self) -> io::Result<u64> {
|
||||||
|
Ok(self.inner.as_ref().len() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_position(&mut self) -> io::Result<u64> {
|
||||||
|
Ok(self.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Read for Cursor<T>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
let n = Read::read(&mut self.remaining_slice(), buf)?;
|
||||||
|
self.pos += n as u64;
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
let mut nread = 0;
|
||||||
|
for buf in bufs {
|
||||||
|
let n = self.read(buf)?;
|
||||||
|
nread += n;
|
||||||
|
if n < buf.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(nread)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
let n = buf.len();
|
||||||
|
Read::read_exact(&mut self.remaining_slice(), buf)?;
|
||||||
|
self.pos += n as u64;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<T> BufRead for Cursor<T>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
Ok(self.remaining_slice())
|
||||||
|
}
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
self.pos += amt as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-resizing write implementation
|
||||||
|
#[inline]
|
||||||
|
fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> {
|
||||||
|
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_vectored(
|
||||||
|
pos_mut: &mut u64,
|
||||||
|
slice: &mut [u8],
|
||||||
|
bufs: &[IoSlice<'_>],
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
let mut nwritten = 0;
|
||||||
|
for buf in bufs {
|
||||||
|
let n = slice_write(pos_mut, slice, buf)?;
|
||||||
|
nwritten += n;
|
||||||
|
if n < buf.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(nwritten)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resizing write implementation
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
let pos: usize = (*pos_mut).try_into().map_err(|_| {
|
||||||
|
Error::new_const(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
&"cursor position exceeds maximum possible vector length",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
// Make sure the internal buffer is as least as big as where we
|
||||||
|
// currently are
|
||||||
|
let len = vec.len();
|
||||||
|
if len < pos {
|
||||||
|
// use `resize` so that the zero filling is as efficient as possible
|
||||||
|
vec.resize(pos, 0);
|
||||||
|
}
|
||||||
|
// Figure out what bytes will be used to overwrite what's currently
|
||||||
|
// there (left), and what will be appended on the end (right)
|
||||||
|
{
|
||||||
|
let space = vec.len() - pos;
|
||||||
|
let (left, right) = buf.split_at(cmp::min(space, buf.len()));
|
||||||
|
vec[pos..pos + left.len()].copy_from_slice(left);
|
||||||
|
vec.extend_from_slice(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bump us forward
|
||||||
|
*pos_mut = (pos + buf.len()) as u64;
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
fn vec_write_vectored(
|
||||||
|
pos_mut: &mut u64,
|
||||||
|
vec: &mut Vec<u8>,
|
||||||
|
bufs: &[IoSlice<'_>],
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
let mut nwritten = 0;
|
||||||
|
for buf in bufs {
|
||||||
|
nwritten += vec_write(pos_mut, vec, buf)?;
|
||||||
|
}
|
||||||
|
Ok(nwritten)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Cursor<&mut [u8]> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
slice_write(&mut self.pos, self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
slice_write_vectored(&mut self.pos, self.inner, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl Write for Cursor<&mut Vec<u8>> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
vec_write(&mut self.pos, self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
vec_write_vectored(&mut self.pos, self.inner, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "collections")]
|
||||||
|
impl Write for Cursor<Vec<u8>> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
vec_write(&mut self.pos, &mut self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
vec_write_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl Write for Cursor<::alloc::boxed::Box<[u8]>> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
slice_write(&mut self.pos, &mut self.inner, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
slice_write_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
523
src/cursor/tests.rs
Normal file
523
src/cursor/tests.rs
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
use crate::io::prelude::*;
|
||||||
|
use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||||
|
.unwrap(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
assert_eq!(writer, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mem_writer() {
|
||||||
|
let mut writer = Cursor::new(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);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||||
|
.unwrap(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||||
|
.unwrap(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
assert_eq!(&writer.get_ref()[..], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_box_slice_writer() {
|
||||||
|
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||||
|
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(), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_box_slice_writer_vectored() {
|
||||||
|
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
writer.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),]).unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
assert_eq!(&**writer.get_ref(), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_writer() {
|
||||||
|
let mut buf = [0 as u8; 9];
|
||||||
|
{
|
||||||
|
let mut writer = Cursor::new(&mut buf[..]);
|
||||||
|
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!(buf, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_writer_vectored() {
|
||||||
|
let mut buf = [0 as u8; 9];
|
||||||
|
{
|
||||||
|
let mut writer = Cursor::new(&mut buf[..]);
|
||||||
|
assert_eq!(writer.position(), 0);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.position(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
writer
|
||||||
|
.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7])],)
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(writer.position(), 8);
|
||||||
|
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
|
||||||
|
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_buf_writer_seek() {
|
||||||
|
let mut buf = [0 as u8; 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 = [0 as u8; 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_mem_reader_vectored() {
|
||||||
|
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||||
|
assert_eq!(reader.position(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(
|
||||||
|
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
assert_eq!(reader.position(), 1);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf1 = [0; 4];
|
||||||
|
let mut buf2 = [0; 4];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
let b1: &[_] = &[1, 2, 3, 4];
|
||||||
|
let b2: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(buf1, b1);
|
||||||
|
assert_eq!(&buf2[..3], b2);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boxed_slice_reader_vectored() {
|
||||||
|
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||||
|
assert_eq!(reader.position(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(
|
||||||
|
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
assert_eq!(reader.position(), 1);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf1 = [0; 4];
|
||||||
|
let mut buf2 = [0; 4];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
let b1: &[_] = &[1, 2, 3, 4];
|
||||||
|
let b2: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(buf1, b1);
|
||||||
|
assert_eq!(&buf2[..3], b2);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 = vec![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_slice_reader_vectored() {
|
||||||
|
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
let reader = &mut &in_buf[..];
|
||||||
|
let mut buf = [];
|
||||||
|
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||||
|
let mut buf = [0];
|
||||||
|
assert_eq!(
|
||||||
|
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
assert_eq!(reader.len(), 7);
|
||||||
|
let b: &[_] = &[0];
|
||||||
|
assert_eq!(buf, b);
|
||||||
|
let mut buf1 = [0; 4];
|
||||||
|
let mut buf2 = [0; 4];
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||||
|
.unwrap(),
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
let b1: &[_] = &[1, 2, 3, 4];
|
||||||
|
let b2: &[_] = &[5, 6, 7];
|
||||||
|
assert_eq!(buf1, b1);
|
||||||
|
assert_eq!(&buf2[..3], b2);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_exact() {
|
||||||
|
let in_buf = vec![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 = vec![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);
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_seekable_mem_writer() {
|
||||||
|
let mut writer = Cursor::new(Vec::<u8>::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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::<u8>::new()), Cursor::new(Vec::<u8>::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eq() {
|
||||||
|
struct AssertEq<T: Eq>(pub T);
|
||||||
|
|
||||||
|
let _: AssertEq<Cursor<Vec<u8>>> = 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();
|
||||||
|
}
|
662
src/error.rs
Normal file
662
src/error.rs
Normal file
@ -0,0 +1,662 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use core::convert::From;
|
||||||
|
use core::fmt;
|
||||||
|
use core::result;
|
||||||
|
|
||||||
|
use core::convert::Into;
|
||||||
|
use core::marker::{Send, Sync};
|
||||||
|
use core::option::Option::{self, Some, None};
|
||||||
|
#[cfg(feature="alloc")] use alloc::boxed::Box;
|
||||||
|
#[cfg(not(feature="alloc"))] use crate::FakeBox as Box;
|
||||||
|
#[cfg(feature="collections")] use alloc::string::String;
|
||||||
|
#[cfg(not(feature="collections"))] use crate::ErrorString as String;
|
||||||
|
|
||||||
|
/// A specialized [`Result`] type for I/O operations.
|
||||||
|
///
|
||||||
|
/// This type is broadly used across [`std::io`] for any operation which may
|
||||||
|
/// produce an error.
|
||||||
|
///
|
||||||
|
/// This typedef is generally used to avoid writing out [`io::Error`] directly and
|
||||||
|
/// is otherwise a direct mapping to [`Result`].
|
||||||
|
///
|
||||||
|
/// While usual Rust style is to import types directly, aliases of [`Result`]
|
||||||
|
/// often are not, to make it easier to distinguish between them. [`Result`] is
|
||||||
|
/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias
|
||||||
|
/// will generally use `io::Result` instead of shadowing the [prelude]'s import
|
||||||
|
/// of [`std::result::Result`][`Result`].
|
||||||
|
///
|
||||||
|
/// [`std::io`]: crate::io
|
||||||
|
/// [`io::Error`]: Error
|
||||||
|
/// [`Result`]: crate::result::Result
|
||||||
|
/// [prelude]: crate::prelude
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A convenience function that bubbles an `io::Result` to its caller:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// fn get_string() -> io::Result<String> {
|
||||||
|
/// let mut buffer = String::new();
|
||||||
|
///
|
||||||
|
/// io::stdin().read_line(&mut buffer)?;
|
||||||
|
///
|
||||||
|
/// Ok(buffer)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and
|
||||||
|
/// associated traits.
|
||||||
|
///
|
||||||
|
/// Errors mostly originate from the underlying OS, but custom instances of
|
||||||
|
/// `Error` can be created with crafted error messages and a particular value of
|
||||||
|
/// [`ErrorKind`].
|
||||||
|
///
|
||||||
|
/// [`Read`]: crate::io::Read
|
||||||
|
/// [`Write`]: crate::io::Write
|
||||||
|
/// [`Seek`]: crate::io::Seek
|
||||||
|
pub struct Error {
|
||||||
|
repr: Repr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.repr, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Repr {
|
||||||
|
Os(i32),
|
||||||
|
Simple(ErrorKind),
|
||||||
|
// &str is a fat pointer, but &&str is a thin pointer.
|
||||||
|
SimpleMessage(ErrorKind, &'static &'static str),
|
||||||
|
#[cfg(feature="alloc")]
|
||||||
|
Custom(Box<Custom>),
|
||||||
|
#[cfg(not(feature="alloc"))]
|
||||||
|
Custom(Custom),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Custom {
|
||||||
|
kind: ErrorKind,
|
||||||
|
error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list specifying general categories of I/O error.
|
||||||
|
///
|
||||||
|
/// This list is intended to grow over time and it is not recommended to
|
||||||
|
/// exhaustively match against it.
|
||||||
|
///
|
||||||
|
/// It is used with the [`io::Error`] type.
|
||||||
|
///
|
||||||
|
/// [`io::Error`]: Error
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
#[allow(deprecated)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
/// An entity was not found, often a file.
|
||||||
|
NotFound,
|
||||||
|
/// The operation lacked the necessary privileges to complete.
|
||||||
|
PermissionDenied,
|
||||||
|
/// The connection was refused by the remote server.
|
||||||
|
ConnectionRefused,
|
||||||
|
/// The connection was reset by the remote server.
|
||||||
|
ConnectionReset,
|
||||||
|
/// The remote host is not reachable.
|
||||||
|
HostUnreachable,
|
||||||
|
/// The network containing the remote host is not reachable.
|
||||||
|
NetworkUnreachable,
|
||||||
|
/// The connection was aborted (terminated) by the remote server.
|
||||||
|
ConnectionAborted,
|
||||||
|
/// The network operation failed because it was not connected yet.
|
||||||
|
NotConnected,
|
||||||
|
/// A socket address could not be bound because the address is already in
|
||||||
|
/// use elsewhere.
|
||||||
|
AddrInUse,
|
||||||
|
/// A nonexistent interface was requested or the requested address was not
|
||||||
|
/// local.
|
||||||
|
AddrNotAvailable,
|
||||||
|
/// The system's networking is down.
|
||||||
|
NetworkDown,
|
||||||
|
/// The operation failed because a pipe was closed.
|
||||||
|
BrokenPipe,
|
||||||
|
/// An entity already exists, often a file.
|
||||||
|
AlreadyExists,
|
||||||
|
/// The operation needs to block to complete, but the blocking operation was
|
||||||
|
/// requested to not occur.
|
||||||
|
WouldBlock,
|
||||||
|
/// For example, a filesystem path was specified where one of the intermediate directory
|
||||||
|
/// components was, in fact, a plain file.
|
||||||
|
NotADirectory,
|
||||||
|
/// The filesystem object is, unexpectedly, a directory.
|
||||||
|
///
|
||||||
|
/// A directory was specified when a non-directory was expected.
|
||||||
|
IsADirectory,
|
||||||
|
/// A non-empty directory was specified where an empty directory was expected.
|
||||||
|
DirectoryNotEmpty,
|
||||||
|
/// The filesystem or storage medium is read-only, but a write operation was attempted.
|
||||||
|
ReadOnlyFilesystem,
|
||||||
|
/// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links.
|
||||||
|
///
|
||||||
|
/// There was a loop (or excessively long chain) resolving a filesystem object
|
||||||
|
/// or file IO object.
|
||||||
|
///
|
||||||
|
/// On Unix this is usually the result of a symbolic link loop; or, of exceeding the
|
||||||
|
/// system-specific limit on the depth of symlink traversal.
|
||||||
|
FilesystemLoop,
|
||||||
|
/// Stale network file handle.
|
||||||
|
///
|
||||||
|
/// With some network filesystems, notably NFS, an open file (or directory) can be invalidated
|
||||||
|
/// by problems with the network or server.
|
||||||
|
StaleNetworkFileHandle,
|
||||||
|
/// A parameter was incorrect.
|
||||||
|
InvalidInput,
|
||||||
|
/// Data not valid for the operation were encountered.
|
||||||
|
///
|
||||||
|
/// Unlike [`InvalidInput`], this typically means that the operation
|
||||||
|
/// parameters were valid, however the error was caused by malformed
|
||||||
|
/// input data.
|
||||||
|
///
|
||||||
|
/// For example, a function that reads a file into a string will error with
|
||||||
|
/// `InvalidData` if the file's contents are not valid UTF-8.
|
||||||
|
///
|
||||||
|
/// [`InvalidInput`]: ErrorKind::InvalidInput
|
||||||
|
InvalidData,
|
||||||
|
/// The I/O operation's timeout expired, causing it to be canceled.
|
||||||
|
TimedOut,
|
||||||
|
/// An error returned when an operation could not be completed because a
|
||||||
|
/// call to [`write`] returned [`Ok(0)`].
|
||||||
|
///
|
||||||
|
/// This typically means that an operation could only succeed if it wrote a
|
||||||
|
/// particular number of bytes but only a smaller number of bytes could be
|
||||||
|
/// written.
|
||||||
|
///
|
||||||
|
/// [`write`]: crate::io::Write::write
|
||||||
|
/// [`Ok(0)`]: Ok
|
||||||
|
WriteZero,
|
||||||
|
///
|
||||||
|
/// This does not include out of quota errors.
|
||||||
|
StorageFull,
|
||||||
|
/// Seek on unseekable file.
|
||||||
|
///
|
||||||
|
/// Seeking was attempted on an open file handle which is not suitable for seeking - for
|
||||||
|
/// example, on Unix, a named pipe opened with `File::open`.
|
||||||
|
NotSeekable,
|
||||||
|
/// Filesystem quota was exceeded.
|
||||||
|
FilesystemQuotaExceeded,
|
||||||
|
/// File larger than allowed or supported.
|
||||||
|
///
|
||||||
|
/// This might arise from a hard limit of the underlying filesystem or file access API, or from
|
||||||
|
/// an administratively imposed resource limitation. Simple disk full, and out of quota, have
|
||||||
|
/// their own errors.
|
||||||
|
FileTooLarge,
|
||||||
|
/// Resource is busy.
|
||||||
|
ResourceBusy,
|
||||||
|
/// Executable file is busy.
|
||||||
|
///
|
||||||
|
/// An attempt was made to write to a file which is also in use as a running program. (Not all
|
||||||
|
/// operating systems detect this situation.)
|
||||||
|
ExecutableFileBusy,
|
||||||
|
/// Deadlock (avoided).
|
||||||
|
///
|
||||||
|
/// A file locking operation would result in deadlock. This situation is typically detected, if
|
||||||
|
/// at all, on a best-effort basis.
|
||||||
|
Deadlock,
|
||||||
|
/// Cross-device or cross-filesystem (hard) link or rename.
|
||||||
|
CrossesDevices,
|
||||||
|
/// Too many (hard) links to the same filesystem object.
|
||||||
|
///
|
||||||
|
/// The filesystem does not support making so many hardlinks to the same file.
|
||||||
|
TooManyLinks,
|
||||||
|
/// Filename too long.
|
||||||
|
///
|
||||||
|
/// The limit might be from the underlying filesystem or API, or an administratively imposed
|
||||||
|
/// resource limit.
|
||||||
|
FilenameTooLong,
|
||||||
|
/// Program argument list too long.
|
||||||
|
///
|
||||||
|
/// When trying to run an external program, a system or process limit on the size of the
|
||||||
|
/// arguments would have been exceeded.
|
||||||
|
ArgumentListTooLong,
|
||||||
|
/// This operation was interrupted.
|
||||||
|
///
|
||||||
|
/// Interrupted operations can typically be retried.
|
||||||
|
Interrupted,
|
||||||
|
|
||||||
|
/// This operation is unsupported on this platform.
|
||||||
|
///
|
||||||
|
/// This means that the operation can never succeed.
|
||||||
|
Unsupported,
|
||||||
|
|
||||||
|
// ErrorKinds which are primarily categorisations for OS error
|
||||||
|
// codes should be added above.
|
||||||
|
//
|
||||||
|
/// An error returned when an operation could not be completed because an
|
||||||
|
/// "end of file" was reached prematurely.
|
||||||
|
///
|
||||||
|
/// This typically means that an operation could only succeed if it read a
|
||||||
|
/// particular number of bytes but only a smaller number of bytes could be
|
||||||
|
/// read.
|
||||||
|
UnexpectedEof,
|
||||||
|
|
||||||
|
/// An operation could not be completed, because it failed
|
||||||
|
/// to allocate enough memory.
|
||||||
|
OutOfMemory,
|
||||||
|
|
||||||
|
// "Unusual" error kinds which do not correspond simply to (sets
|
||||||
|
// of) OS error codes, should be added just above this comment.
|
||||||
|
// `Other` and `Uncategorised` should remain at the end:
|
||||||
|
//
|
||||||
|
/// A custom error that does not fall under any other I/O error kind.
|
||||||
|
///
|
||||||
|
/// This can be used to construct your own [`Error`]s that do not match any
|
||||||
|
/// [`ErrorKind`].
|
||||||
|
///
|
||||||
|
/// This [`ErrorKind`] is not used by the standard library.
|
||||||
|
Other,
|
||||||
|
|
||||||
|
/// Any I/O error from the standard library that's not part of this list.
|
||||||
|
///
|
||||||
|
/// Errors that are `Uncategorized` now may move to a different or a new
|
||||||
|
/// [`ErrorKind`] variant in the future. It is not recommended to match
|
||||||
|
/// an error against `Uncategorized`; use a wildcard match (`_`) instead.
|
||||||
|
#[doc(hidden)]
|
||||||
|
Uncategorized,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorKind {
|
||||||
|
pub(crate) fn as_str(&self) -> &'static str {
|
||||||
|
use ErrorKind::*;
|
||||||
|
// Strictly alphabetical, please. (Sadly rustfmt cannot do this yet.)
|
||||||
|
match *self {
|
||||||
|
AddrInUse => "address in use",
|
||||||
|
AddrNotAvailable => "address not available",
|
||||||
|
AlreadyExists => "entity already exists",
|
||||||
|
ArgumentListTooLong => "argument list too long",
|
||||||
|
BrokenPipe => "broken pipe",
|
||||||
|
ConnectionAborted => "connection aborted",
|
||||||
|
ConnectionRefused => "connection refused",
|
||||||
|
ConnectionReset => "connection reset",
|
||||||
|
CrossesDevices => "cross-device link or rename",
|
||||||
|
Deadlock => "deadlock",
|
||||||
|
DirectoryNotEmpty => "directory not empty",
|
||||||
|
ExecutableFileBusy => "executable file busy",
|
||||||
|
FileTooLarge => "file too large",
|
||||||
|
FilenameTooLong => "filename too long",
|
||||||
|
FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)",
|
||||||
|
FilesystemQuotaExceeded => "filesystem quota exceeded",
|
||||||
|
HostUnreachable => "host unreachable",
|
||||||
|
Interrupted => "operation interrupted",
|
||||||
|
InvalidData => "invalid data",
|
||||||
|
InvalidInput => "invalid input parameter",
|
||||||
|
IsADirectory => "is a directory",
|
||||||
|
NetworkDown => "network down",
|
||||||
|
NetworkUnreachable => "network unreachable",
|
||||||
|
NotADirectory => "not a directory",
|
||||||
|
NotConnected => "not connected",
|
||||||
|
NotFound => "entity not found",
|
||||||
|
NotSeekable => "seek on unseekable file",
|
||||||
|
Other => "other error",
|
||||||
|
OutOfMemory => "out of memory",
|
||||||
|
PermissionDenied => "permission denied",
|
||||||
|
ReadOnlyFilesystem => "read-only filesystem or storage medium",
|
||||||
|
ResourceBusy => "resource busy",
|
||||||
|
StaleNetworkFileHandle => "stale network file handle",
|
||||||
|
StorageFull => "no storage space",
|
||||||
|
TimedOut => "timed out",
|
||||||
|
TooManyLinks => "too many links",
|
||||||
|
Uncategorized => "uncategorized error",
|
||||||
|
UnexpectedEof => "unexpected end of file",
|
||||||
|
Unsupported => "unsupported",
|
||||||
|
WouldBlock => "operation would block",
|
||||||
|
WriteZero => "write zero",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intended for use for errors not exposed to the user, where allocating onto
|
||||||
|
/// the heap (for normal construction via Error::new) is too costly.
|
||||||
|
impl From<ErrorKind> for Error {
|
||||||
|
/// Converts an [`ErrorKind`] into an [`Error`].
|
||||||
|
///
|
||||||
|
/// This conversion allocates a new error with a simple representation of error kind.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// let not_found = ErrorKind::NotFound;
|
||||||
|
/// let error = Error::from(not_found);
|
||||||
|
/// assert_eq!("entity not found", format!("{}", error));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
fn from(kind: ErrorKind) -> Error {
|
||||||
|
Error { repr: Repr::Simple(kind) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
/// Creates a new I/O error from a known kind of error as well as an
|
||||||
|
/// arbitrary error payload.
|
||||||
|
///
|
||||||
|
/// This function is used to generically create I/O errors which do not
|
||||||
|
/// originate from the OS itself. The `error` argument is an arbitrary
|
||||||
|
/// payload which will be contained in this [`Error`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// // errors can be created from strings
|
||||||
|
/// let custom_error = Error::new(ErrorKind::Other, "oh no!");
|
||||||
|
///
|
||||||
|
/// // errors can also be created from other errors
|
||||||
|
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
|
||||||
|
/// ```
|
||||||
|
pub fn new<E>(kind: ErrorKind, error: E) -> Error
|
||||||
|
where
|
||||||
|
E: Into<String>,
|
||||||
|
{
|
||||||
|
Self::_new(kind, error.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _new(kind: ErrorKind, error: String) -> Error {
|
||||||
|
Error { repr: Repr::Custom(Box::new(Custom { kind, error })) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new I/O error from a known kind of error as well as a
|
||||||
|
/// constant message.
|
||||||
|
///
|
||||||
|
/// This function does not allocate.
|
||||||
|
///
|
||||||
|
/// This function should maybe change to
|
||||||
|
/// `new_const<const MSG: &'static str>(kind: ErrorKind)`
|
||||||
|
/// in the future, when const generics allow that.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) const fn new_const(kind: ErrorKind, message: &'static &'static str) -> Error {
|
||||||
|
Self { repr: Repr::SimpleMessage(kind, message) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new instance of an [`Error`] from a particular OS error code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// On Linux:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # if cfg!(target_os = "linux") {
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// let error = io::Error::from_raw_os_error(22);
|
||||||
|
/// assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// On Windows:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # if cfg!(windows) {
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// let error = io::Error::from_raw_os_error(10022);
|
||||||
|
/// assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_raw_os_error(code: i32) -> Error {
|
||||||
|
Error { repr: Repr::Os(code) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the OS error that this error represents (if any).
|
||||||
|
///
|
||||||
|
/// If this [`Error`] was constructed via [`last_os_error`] or
|
||||||
|
/// [`from_raw_os_error`], then this function will return [`Some`], otherwise
|
||||||
|
/// it will return [`None`].
|
||||||
|
///
|
||||||
|
/// [`last_os_error`]: Error::last_os_error
|
||||||
|
/// [`from_raw_os_error`]: Error::from_raw_os_error
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_os_error(err: &Error) {
|
||||||
|
/// if let Some(raw_os_err) = err.raw_os_error() {
|
||||||
|
/// println!("raw OS error: {:?}", raw_os_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("Not an OS error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "raw OS error: ...".
|
||||||
|
/// print_os_error(&Error::last_os_error());
|
||||||
|
/// // Will print "Not an OS error".
|
||||||
|
/// print_os_error(&Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn raw_os_error(&self) -> Option<i32> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(i) => Some(i),
|
||||||
|
Repr::Custom(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
Repr::SimpleMessage(..) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the inner error wrapped by this error (if any).
|
||||||
|
///
|
||||||
|
/// If this [`Error`] was constructed via [`new`] then this function will
|
||||||
|
/// return [`Some`], otherwise it will return [`None`].
|
||||||
|
///
|
||||||
|
/// [`new`]: Error::new
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: &Error) {
|
||||||
|
/// if let Some(inner_err) = err.get_ref() {
|
||||||
|
/// println!("Inner error: {:?}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(&Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(&Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn get_ref(&self) -> Option<&String> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
Repr::SimpleMessage(..) => None,
|
||||||
|
Repr::Custom(ref c) => Some(&c.error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the inner error wrapped by this error
|
||||||
|
/// (if any).
|
||||||
|
///
|
||||||
|
/// If this [`Error`] was constructed via [`new`] then this function will
|
||||||
|
/// return [`Some`], otherwise it will return [`None`].
|
||||||
|
///
|
||||||
|
/// [`new`]: Error::new
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
/// use std::{error, fmt};
|
||||||
|
/// use std::fmt::Display;
|
||||||
|
///
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// struct MyError {
|
||||||
|
/// v: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl MyError {
|
||||||
|
/// fn new() -> MyError {
|
||||||
|
/// MyError {
|
||||||
|
/// v: "oh no!".to_string()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn change_message(&mut self, new_message: &str) {
|
||||||
|
/// self.v = new_message.to_string();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl error::Error for MyError {}
|
||||||
|
///
|
||||||
|
/// impl Display for MyError {
|
||||||
|
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
/// write!(f, "MyError: {}", &self.v)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn change_error(mut err: Error) -> Error {
|
||||||
|
/// if let Some(inner_err) = err.get_mut() {
|
||||||
|
/// inner_err.downcast_mut::<MyError>().unwrap().change_message("I've been changed!");
|
||||||
|
/// }
|
||||||
|
/// err
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn print_error(err: &Error) {
|
||||||
|
/// if let Some(inner_err) = err.get_ref() {
|
||||||
|
/// println!("Inner error: {}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(&change_error(Error::last_os_error()));
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new())));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn get_mut(&mut self) -> Option<&mut String> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
Repr::SimpleMessage(..) => None,
|
||||||
|
Repr::Custom(ref mut c) => Some(&mut c.error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the `Error`, returning its inner error (if any).
|
||||||
|
///
|
||||||
|
/// If this [`Error`] was constructed via [`new`] then this function will
|
||||||
|
/// return [`Some`], otherwise it will return [`None`].
|
||||||
|
///
|
||||||
|
/// [`new`]: Error::new
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: Error) {
|
||||||
|
/// if let Some(inner_err) = err.into_inner() {
|
||||||
|
/// println!("Inner error: {}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn into_inner(self) -> Option<String> {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(..) => None,
|
||||||
|
Repr::Simple(..) => None,
|
||||||
|
Repr::SimpleMessage(..) => None,
|
||||||
|
Repr::Custom(c) => Some(c.error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the corresponding [`ErrorKind`] for this error.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: Error) {
|
||||||
|
/// println!("{:?}", err.kind());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "Uncategorized".
|
||||||
|
/// print_error(Error::last_os_error());
|
||||||
|
/// // Will print "AddrInUse".
|
||||||
|
/// print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn kind(&self) -> ErrorKind {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(_code) => ErrorKind::Other,
|
||||||
|
Repr::Custom(ref c) => c.kind,
|
||||||
|
Repr::Simple(kind) => kind,
|
||||||
|
Repr::SimpleMessage(kind, _) => kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Repr {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Repr::Os(code) => fmt
|
||||||
|
.debug_struct("Os")
|
||||||
|
.field("code", &code)
|
||||||
|
.finish(),
|
||||||
|
Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
|
||||||
|
Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
|
||||||
|
Repr::SimpleMessage(kind, &message) => {
|
||||||
|
fmt.debug_struct("Error").field("kind", &kind).field("message", &message).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.repr {
|
||||||
|
Repr::Os(code) => {
|
||||||
|
write!(fmt, "os error {}", code)
|
||||||
|
}
|
||||||
|
Repr::Custom(ref c) => c.error.fmt(fmt),
|
||||||
|
Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
|
||||||
|
Repr::SimpleMessage(_, &msg) => msg.fmt(fmt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _assert_error_is_sync_send() {
|
||||||
|
fn _is_sync_send<T: Sync + Send>() {}
|
||||||
|
_is_sync_send::<Error>();
|
||||||
|
}
|
69
src/error/tests.rs
Normal file
69
src/error/tests.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use super::{Custom, Error, ErrorKind, Repr};
|
||||||
|
use crate::error;
|
||||||
|
use crate::fmt;
|
||||||
|
use crate::mem::size_of;
|
||||||
|
use crate::sys::decode_error_kind;
|
||||||
|
use crate::sys::os::error_string;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_size() {
|
||||||
|
assert!(size_of::<Error>() <= size_of::<[usize; 2]>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_debug_error() {
|
||||||
|
let code = 6;
|
||||||
|
let msg = error_string(code);
|
||||||
|
let kind = decode_error_kind(code);
|
||||||
|
let err = Error {
|
||||||
|
repr: Repr::Custom(box Custom {
|
||||||
|
kind: ErrorKind::InvalidInput,
|
||||||
|
error: box Error { repr: super::Repr::Os(code) },
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let expected = format!(
|
||||||
|
"Custom {{ \
|
||||||
|
kind: InvalidInput, \
|
||||||
|
error: Os {{ \
|
||||||
|
code: {:?}, \
|
||||||
|
kind: {:?}, \
|
||||||
|
message: {:?} \
|
||||||
|
}} \
|
||||||
|
}}",
|
||||||
|
code, kind, msg
|
||||||
|
);
|
||||||
|
assert_eq!(format!("{:?}", err), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_downcasting() {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TestError;
|
||||||
|
|
||||||
|
impl fmt::Display for TestError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("asdf")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for TestError {}
|
||||||
|
|
||||||
|
// we have to call all of these UFCS style right now since method
|
||||||
|
// resolution won't implicitly drop the Send+Sync bounds
|
||||||
|
let mut err = Error::new(ErrorKind::Other, TestError);
|
||||||
|
assert!(err.get_ref().unwrap().is::<TestError>());
|
||||||
|
assert_eq!("asdf", err.get_ref().unwrap().to_string());
|
||||||
|
assert!(err.get_mut().unwrap().is::<TestError>());
|
||||||
|
let extracted = err.into_inner().unwrap();
|
||||||
|
extracted.downcast::<TestError>().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_const() {
|
||||||
|
const E: Error = Error::new_const(ErrorKind::NotFound, &"hello");
|
||||||
|
|
||||||
|
assert_eq!(E.kind(), ErrorKind::NotFound);
|
||||||
|
assert_eq!(E.to_string(), "hello");
|
||||||
|
assert!(format!("{:?}", E).contains("\"hello\""));
|
||||||
|
assert!(format!("{:?}", E).contains("NotFound"));
|
||||||
|
}
|
408
src/impls.rs
Normal file
408
src/impls.rs
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
#[cfg(feature="collections")] use core::alloc::Allocator;
|
||||||
|
use core::cmp;
|
||||||
|
use core::fmt;
|
||||||
|
use crate::{
|
||||||
|
self as io, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write,
|
||||||
|
};
|
||||||
|
#[cfg(feature="collections")] use crate::BufRead;
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
#[cfg(feature="alloc")] use alloc::boxed::Box;
|
||||||
|
#[cfg(feature="collections")] use alloc::string::String;
|
||||||
|
#[cfg(feature="collections")] use alloc::vec::Vec;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Forwarding implementations
|
||||||
|
|
||||||
|
impl<R: Read + ?Sized> Read for &mut R {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
(**self).read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
(**self).read_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
(**self).is_read_vectored()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
(**self).initializer()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_to_end(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_to_string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
(**self).read_exact(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<W: Write + ?Sized> Write for &mut W {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
(**self).write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
(**self).write_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
(**self).is_write_vectored()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
(**self).flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||||
|
(**self).write_all(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||||
|
(**self).write_fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<S: Seek + ?Sized> Seek for &mut S {
|
||||||
|
#[inline]
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
(**self).seek(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn stream_position(&mut self) -> io::Result<u64> {
|
||||||
|
(**self).stream_position()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<B: BufRead + ?Sized> BufRead for &mut B {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
(**self).fill_buf()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
(**self).consume(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_until(byte, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_line(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="alloc")]
|
||||||
|
impl<R: Read + ?Sized> Read for Box<R> {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
(**self).read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
(**self).read_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
(**self).is_read_vectored()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
(**self).initializer()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_to_end(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_to_string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
(**self).read_exact(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="alloc")]
|
||||||
|
impl<W: Write + ?Sized> Write for Box<W> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
(**self).write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
(**self).write_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
(**self).is_write_vectored()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
(**self).flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||||
|
(**self).write_all(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||||
|
(**self).write_fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="alloc")]
|
||||||
|
impl<S: Seek + ?Sized> Seek for Box<S> {
|
||||||
|
#[inline]
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
(**self).seek(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn stream_position(&mut self) -> io::Result<u64> {
|
||||||
|
(**self).stream_position()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<B: BufRead + ?Sized> BufRead for Box<B> {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
(**self).fill_buf()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
(**self).consume(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
(**self).read_until(byte, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||||
|
(**self).read_line(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// In-memory buffer implementations
|
||||||
|
|
||||||
|
/// Read is implemented for `&[u8]` by copying from the slice.
|
||||||
|
///
|
||||||
|
/// Note that reading updates the slice to point to the yet unread part.
|
||||||
|
/// The slice will be empty when EOF is reached.
|
||||||
|
impl Read for &[u8] {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
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_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
let mut nread = 0;
|
||||||
|
for buf in bufs {
|
||||||
|
nread += self.read(buf)?;
|
||||||
|
if self.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(nread)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
if buf.len() > self.len() {
|
||||||
|
return Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer"));
|
||||||
|
}
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
#[inline]
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
|
buf.extend_from_slice(*self);
|
||||||
|
let len = self.len();
|
||||||
|
*self = &self[len..];
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl BufRead for &[u8] {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
*self = &self[amt..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting
|
||||||
|
/// its data.
|
||||||
|
///
|
||||||
|
/// Note that writing updates the slice to point to the yet unwritten part.
|
||||||
|
/// The slice will be empty when it has been completely overwritten.
|
||||||
|
///
|
||||||
|
/// If the number of bytes to be written exceeds the size of the slice, write operations will
|
||||||
|
/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of
|
||||||
|
/// kind `ErrorKind::WriteZero`.
|
||||||
|
impl Write for &mut [u8] {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||||
|
let amt = cmp::min(data.len(), self.len());
|
||||||
|
let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
|
||||||
|
a.copy_from_slice(&data[..amt]);
|
||||||
|
*self = b;
|
||||||
|
Ok(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
let mut nwritten = 0;
|
||||||
|
for buf in bufs {
|
||||||
|
nwritten += self.write(buf)?;
|
||||||
|
if self.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(nwritten)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
|
||||||
|
if self.write(data)? == data.len() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::new_const(ErrorKind::WriteZero, &"failed to write whole buffer"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write is implemented for `Vec<u8>` by appending to the vector.
|
||||||
|
/// The vector will grow as needed.
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl<A: Allocator> Write for Vec<u8, A> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
let len = bufs.iter().map(|b| b.len()).sum();
|
||||||
|
self.reserve(len);
|
||||||
|
for buf in bufs {
|
||||||
|
self.extend_from_slice(buf);
|
||||||
|
}
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||||
|
self.extend_from_slice(buf);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
57
src/impls/tests.rs
Normal file
57
src/impls/tests.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use crate::io::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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
2548
src/lib.rs
Normal file
2548
src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
15
src/prelude.rs
Normal file
15
src/prelude.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//! The I/O Prelude.
|
||||||
|
//!
|
||||||
|
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||||
|
//! by adding a glob import to the top of I/O heavy modules:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # #![allow(unused_imports)]
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
pub use super::{Read, Seek, Write};
|
||||||
|
#[cfg(feature="collections")] pub use super::BufRead;
|
||||||
|
|
||||||
|
#[cfg(feature="collections")] pub use alloc::boxed::Box;
|
||||||
|
#[cfg(feature="collections")] pub use alloc::vec::Vec;
|
166
src/stdio/tests.rs
Normal file
166
src/stdio/tests.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
|
use crate::sync::mpsc::sync_channel;
|
||||||
|
use crate::thread;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stdout_unwind_safe() {
|
||||||
|
assert_unwind_safe::<Stdout>();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn stdoutlock_unwind_safe() {
|
||||||
|
assert_unwind_safe::<StdoutLock<'_>>();
|
||||||
|
assert_unwind_safe::<StdoutLock<'static>>();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn stderr_unwind_safe() {
|
||||||
|
assert_unwind_safe::<Stderr>();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn stderrlock_unwind_safe() {
|
||||||
|
assert_unwind_safe::<StderrLock<'_>>();
|
||||||
|
assert_unwind_safe::<StderrLock<'static>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
|
fn panic_doesnt_poison() {
|
||||||
|
thread::spawn(|| {
|
||||||
|
let _a = stdin();
|
||||||
|
let _a = _a.lock();
|
||||||
|
let _a = stdout();
|
||||||
|
let _a = _a.lock();
|
||||||
|
let _a = stderr();
|
||||||
|
let _a = _a.lock();
|
||||||
|
panic!();
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.unwrap_err();
|
||||||
|
|
||||||
|
let _a = stdin();
|
||||||
|
let _a = _a.lock();
|
||||||
|
let _a = stdout();
|
||||||
|
let _a = _a.lock();
|
||||||
|
let _a = stderr();
|
||||||
|
let _a = _a.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
|
fn test_lock_stderr() {
|
||||||
|
test_lock(stderr, stderr_locked);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
|
fn test_lock_stdin() {
|
||||||
|
test_lock(stdin, stdin_locked);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
|
fn test_lock_stdout() {
|
||||||
|
test_lock(stdout, stdout_locked);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper trait to make lock testing function generic.
|
||||||
|
trait Stdio<'a>: 'static
|
||||||
|
where
|
||||||
|
Self::Lock: 'a,
|
||||||
|
{
|
||||||
|
type Lock;
|
||||||
|
fn lock(&'a self) -> Self::Lock;
|
||||||
|
}
|
||||||
|
impl<'a> Stdio<'a> for Stderr {
|
||||||
|
type Lock = StderrLock<'a>;
|
||||||
|
fn lock(&'a self) -> StderrLock<'a> {
|
||||||
|
self.lock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Stdio<'a> for Stdin {
|
||||||
|
type Lock = StdinLock<'a>;
|
||||||
|
fn lock(&'a self) -> StdinLock<'a> {
|
||||||
|
self.lock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Stdio<'a> for Stdout {
|
||||||
|
type Lock = StdoutLock<'a>;
|
||||||
|
fn lock(&'a self) -> StdoutLock<'a> {
|
||||||
|
self.lock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper trait to make lock testing function generic.
|
||||||
|
trait StdioOwnedLock: 'static {}
|
||||||
|
impl StdioOwnedLock for StderrLock<'static> {}
|
||||||
|
impl StdioOwnedLock for StdinLock<'static> {}
|
||||||
|
impl StdioOwnedLock for StdoutLock<'static> {}
|
||||||
|
|
||||||
|
// Tests locking on stdio handles by starting two threads and checking that
|
||||||
|
// they block each other appropriately.
|
||||||
|
fn test_lock<T, U>(get_handle: fn() -> T, get_locked: fn() -> U)
|
||||||
|
where
|
||||||
|
T: for<'a> Stdio<'a>,
|
||||||
|
U: StdioOwnedLock,
|
||||||
|
{
|
||||||
|
// State enum to track different phases of the test, primarily when
|
||||||
|
// each lock is acquired and released.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum State {
|
||||||
|
Start1,
|
||||||
|
Acquire1,
|
||||||
|
Start2,
|
||||||
|
Release1,
|
||||||
|
Acquire2,
|
||||||
|
Release2,
|
||||||
|
}
|
||||||
|
use State::*;
|
||||||
|
// Logging vector to be checked to make sure lock acquisitions and
|
||||||
|
// releases happened in the correct order.
|
||||||
|
let log = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let ((tx1, rx1), (tx2, rx2)) = (sync_channel(0), sync_channel(0));
|
||||||
|
let th1 = {
|
||||||
|
let (log, tx) = (Arc::clone(&log), tx1);
|
||||||
|
thread::spawn(move || {
|
||||||
|
log.lock().unwrap().push(Start1);
|
||||||
|
let handle = get_handle();
|
||||||
|
{
|
||||||
|
let locked = handle.lock();
|
||||||
|
log.lock().unwrap().push(Acquire1);
|
||||||
|
tx.send(Acquire1).unwrap(); // notify of acquisition
|
||||||
|
tx.send(Release1).unwrap(); // wait for release command
|
||||||
|
log.lock().unwrap().push(Release1);
|
||||||
|
}
|
||||||
|
tx.send(Acquire1).unwrap(); // wait for th2 acquire
|
||||||
|
{
|
||||||
|
let locked = handle.lock();
|
||||||
|
log.lock().unwrap().push(Acquire1);
|
||||||
|
}
|
||||||
|
log.lock().unwrap().push(Release1);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let th2 = {
|
||||||
|
let (log, tx) = (Arc::clone(&log), tx2);
|
||||||
|
thread::spawn(move || {
|
||||||
|
tx.send(Start2).unwrap(); // wait for start command
|
||||||
|
let locked = get_locked();
|
||||||
|
log.lock().unwrap().push(Acquire2);
|
||||||
|
tx.send(Acquire2).unwrap(); // notify of acquisition
|
||||||
|
tx.send(Release2).unwrap(); // wait for release command
|
||||||
|
log.lock().unwrap().push(Release2);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
assert_eq!(rx1.recv().unwrap(), Acquire1); // wait for th1 acquire
|
||||||
|
log.lock().unwrap().push(Start2);
|
||||||
|
assert_eq!(rx2.recv().unwrap(), Start2); // block th2
|
||||||
|
assert_eq!(rx1.recv().unwrap(), Release1); // release th1
|
||||||
|
assert_eq!(rx2.recv().unwrap(), Acquire2); // wait for th2 acquire
|
||||||
|
assert_eq!(rx1.recv().unwrap(), Acquire1); // block th1
|
||||||
|
assert_eq!(rx2.recv().unwrap(), Release2); // release th2
|
||||||
|
th2.join().unwrap();
|
||||||
|
th1.join().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
*log.lock().unwrap(),
|
||||||
|
[Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1]
|
||||||
|
);
|
||||||
|
}
|
573
src/tests.rs
Normal file
573
src/tests.rs
Normal file
@ -0,0 +1,573 @@
|
|||||||
|
use super::{repeat, Cursor, SeekFrom};
|
||||||
|
use crate::cmp::{self, min};
|
||||||
|
use crate::io::{self, IoSlice, IoSliceMut};
|
||||||
|
use crate::io::{BufRead, BufReader, Read, Seek, Write};
|
||||||
|
use crate::ops::Deref;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
|
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, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 = 1024 * 1024;
|
||||||
|
let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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().kind(), io::ErrorKind::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().kind(), io::ErrorKind::UnexpectedEof);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_exact_slice() {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
|
||||||
|
let mut c = &b""[..];
|
||||||
|
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||||
|
|
||||||
|
let mut c = &b"123"[..];
|
||||||
|
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::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 take_eof() {
|
||||||
|
struct R;
|
||||||
|
|
||||||
|
impl Read for R {
|
||||||
|
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||||
|
Err(io::Error::new_const(io::ErrorKind::Other, &""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BufRead for R {
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
Err(io::Error::new_const(io::ErrorKind::Other, &""))
|
||||||
|
}
|
||||||
|
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<Br1: BufRead, Br2: 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[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bufreader_size_hint() {
|
||||||
|
let testdata = b"ABCDEFGHIJKL";
|
||||||
|
let mut buf_reader = BufReader::new(&testdata[..]);
|
||||||
|
assert_eq!(buf_reader.buffer().len(), 0);
|
||||||
|
|
||||||
|
let buffer_length = testdata.len();
|
||||||
|
buf_reader.fill_buf().unwrap();
|
||||||
|
|
||||||
|
// Check that size hint matches buffer contents
|
||||||
|
let mut buffered_bytes = buf_reader.bytes();
|
||||||
|
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
||||||
|
assert_eq!(lower_bound, buffer_length);
|
||||||
|
|
||||||
|
// Check that size hint matches buffer contents after advancing
|
||||||
|
buffered_bytes.next().unwrap().unwrap();
|
||||||
|
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
||||||
|
assert_eq!(lower_bound, buffer_length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_size_hint() {
|
||||||
|
let size_hint = io::empty().bytes().size_hint();
|
||||||
|
assert_eq!(size_hint, (0, Some(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn slice_size_hint() {
|
||||||
|
let size_hint = (&[1, 2, 3]).bytes().size_hint();
|
||||||
|
assert_eq!(size_hint, (3, Some(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_size_hint() {
|
||||||
|
let size_hint = (&[1, 2, 3]).take(2).bytes().size_hint();
|
||||||
|
assert_eq!(size_hint, (2, Some(2)));
|
||||||
|
|
||||||
|
let size_hint = (&[1, 2, 3]).take(4).bytes().size_hint();
|
||||||
|
assert_eq!(size_hint, (3, Some(3)));
|
||||||
|
|
||||||
|
let size_hint = io::repeat(0).take(3).bytes().size_hint();
|
||||||
|
assert_eq!(size_hint, (3, Some(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn chain_empty_size_hint() {
|
||||||
|
let chain = io::empty().chain(io::empty());
|
||||||
|
let size_hint = chain.bytes().size_hint();
|
||||||
|
assert_eq!(size_hint, (0, Some(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn chain_size_hint() {
|
||||||
|
let testdata = b"ABCDEFGHIJKL";
|
||||||
|
let mut buf_reader_1 = BufReader::new(&testdata[..6]);
|
||||||
|
let mut buf_reader_2 = BufReader::new(&testdata[6..]);
|
||||||
|
|
||||||
|
buf_reader_1.fill_buf().unwrap();
|
||||||
|
buf_reader_2.fill_buf().unwrap();
|
||||||
|
|
||||||
|
let chain = buf_reader_1.chain(buf_reader_2);
|
||||||
|
let size_hint = chain.bytes().size_hint();
|
||||||
|
assert_eq!(size_hint, (testdata.len(), Some(testdata.len())));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
|
fn bench_read_to_end(b: &mut test::Bencher) {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut lr = repeat(1).take(10000000);
|
||||||
|
let mut vec = Vec::with_capacity(1024);
|
||||||
|
super::read_to_end(&mut lr, &mut vec)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn seek_len() -> io::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() -> io::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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// A simple example reader which uses the default implementation of
|
||||||
|
// read_to_end.
|
||||||
|
struct ExampleSliceReader<'a> {
|
||||||
|
slice: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Read for ExampleSliceReader<'a> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
let len = cmp::min(self.slice.len(), buf.len());
|
||||||
|
buf[..len].copy_from_slice(&self.slice[..len]);
|
||||||
|
self.slice = &self.slice[len..];
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_to_end_capacity() -> io::Result<()> {
|
||||||
|
let input = &b"foo"[..];
|
||||||
|
|
||||||
|
// read_to_end() generally needs to over-allocate, both for efficiency
|
||||||
|
// and so that it can distinguish EOF. Assert that this is the case
|
||||||
|
// with this simple ExampleSliceReader struct, which uses the default
|
||||||
|
// implementation of read_to_end. Even though vec1 is allocated with
|
||||||
|
// exactly enough capacity for the read, read_to_end will allocate more
|
||||||
|
// space here.
|
||||||
|
let mut vec1 = Vec::with_capacity(input.len());
|
||||||
|
ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?;
|
||||||
|
assert_eq!(vec1.len(), input.len());
|
||||||
|
assert!(vec1.capacity() > input.len(), "allocated more");
|
||||||
|
|
||||||
|
// However, std::io::Take includes an implementation of read_to_end
|
||||||
|
// that will not allocate when the limit has already been reached. In
|
||||||
|
// this case, vec2 never grows.
|
||||||
|
let mut vec2 = Vec::with_capacity(input.len());
|
||||||
|
ExampleSliceReader { slice: input }.take(input.len() as u64).read_to_end(&mut vec2)?;
|
||||||
|
assert_eq!(vec2.len(), input.len());
|
||||||
|
assert_eq!(vec2.capacity(), input.len(), "did not allocate more");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_slice_mut_advance_slices() {
|
||||||
|
let mut buf1 = [1; 8];
|
||||||
|
let mut buf2 = [2; 16];
|
||||||
|
let mut buf3 = [3; 8];
|
||||||
|
let mut bufs = &mut [
|
||||||
|
IoSliceMut::new(&mut buf1),
|
||||||
|
IoSliceMut::new(&mut buf2),
|
||||||
|
IoSliceMut::new(&mut buf3),
|
||||||
|
][..];
|
||||||
|
|
||||||
|
// Only in a single buffer..
|
||||||
|
IoSliceMut::advance_slices(&mut bufs, 1);
|
||||||
|
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
||||||
|
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
||||||
|
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
||||||
|
|
||||||
|
// Removing a buffer, leaving others as is.
|
||||||
|
IoSliceMut::advance_slices(&mut bufs, 7);
|
||||||
|
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
||||||
|
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
||||||
|
|
||||||
|
// Removing a buffer and removing from the next buffer.
|
||||||
|
IoSliceMut::advance_slices(&mut bufs, 18);
|
||||||
|
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_slice_mut_advance_slices_empty_slice() {
|
||||||
|
let mut empty_bufs = &mut [][..];
|
||||||
|
// Shouldn't panic.
|
||||||
|
IoSliceMut::advance_slices(&mut empty_bufs, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_slice_mut_advance_slices_beyond_total_length() {
|
||||||
|
let mut buf1 = [1; 8];
|
||||||
|
let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..];
|
||||||
|
|
||||||
|
// Going beyond the total length should be ok.
|
||||||
|
IoSliceMut::advance_slices(&mut bufs, 9);
|
||||||
|
assert!(bufs.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_slice_advance_slices() {
|
||||||
|
let buf1 = [1; 8];
|
||||||
|
let buf2 = [2; 16];
|
||||||
|
let buf3 = [3; 8];
|
||||||
|
let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..];
|
||||||
|
|
||||||
|
// Only in a single buffer..
|
||||||
|
IoSlice::advance_slices(&mut bufs, 1);
|
||||||
|
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
||||||
|
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
||||||
|
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
||||||
|
|
||||||
|
// Removing a buffer, leaving others as is.
|
||||||
|
IoSlice::advance_slices(&mut bufs, 7);
|
||||||
|
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
||||||
|
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
||||||
|
|
||||||
|
// Removing a buffer and removing from the next buffer.
|
||||||
|
IoSlice::advance_slices(&mut bufs, 18);
|
||||||
|
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_slice_advance_slices_empty_slice() {
|
||||||
|
let mut empty_bufs = &mut [][..];
|
||||||
|
// Shouldn't panic.
|
||||||
|
IoSlice::advance_slices(&mut empty_bufs, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_slice_advance_slices_beyond_total_length() {
|
||||||
|
let buf1 = [1; 8];
|
||||||
|
let mut bufs = &mut [IoSlice::new(&buf1)][..];
|
||||||
|
|
||||||
|
// Going beyond the total length should be ok.
|
||||||
|
IoSlice::advance_slices(&mut bufs, 9);
|
||||||
|
assert!(bufs.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new writer that reads from at most `n_bufs` and reads
|
||||||
|
/// `per_call` bytes (in total) per call to write.
|
||||||
|
fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter {
|
||||||
|
TestWriter { n_bufs, per_call, written: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestWriter {
|
||||||
|
n_bufs: usize,
|
||||||
|
per_call: usize,
|
||||||
|
written: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for TestWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.write_vectored(&[IoSlice::new(buf)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
let mut left = self.per_call;
|
||||||
|
let mut written = 0;
|
||||||
|
for buf in bufs.iter().take(self.n_bufs) {
|
||||||
|
let n = min(left, buf.len());
|
||||||
|
self.written.extend_from_slice(&buf[0..n]);
|
||||||
|
left -= n;
|
||||||
|
written += n;
|
||||||
|
}
|
||||||
|
Ok(written)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_writer_read_from_one_buf() {
|
||||||
|
let mut writer = test_writer(1, 2);
|
||||||
|
|
||||||
|
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||||
|
|
||||||
|
// Read at most 2 bytes.
|
||||||
|
assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2);
|
||||||
|
let bufs = &[IoSlice::new(&[2, 2, 2])];
|
||||||
|
assert_eq!(writer.write_vectored(bufs).unwrap(), 2);
|
||||||
|
|
||||||
|
// Only read from first buf.
|
||||||
|
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])];
|
||||||
|
assert_eq!(writer.write_vectored(bufs).unwrap(), 1);
|
||||||
|
|
||||||
|
assert_eq!(writer.written, &[1, 1, 2, 2, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_writer_read_from_multiple_bufs() {
|
||||||
|
let mut writer = test_writer(3, 3);
|
||||||
|
|
||||||
|
// Read at most 3 bytes from two buffers.
|
||||||
|
let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])];
|
||||||
|
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
||||||
|
|
||||||
|
// Read at most 3 bytes from three buffers.
|
||||||
|
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])];
|
||||||
|
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
||||||
|
|
||||||
|
assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_all_vectored() {
|
||||||
|
#[rustfmt::skip] // Becomes unreadable otherwise.
|
||||||
|
let tests: Vec<(_, &'static [u8])> = vec![
|
||||||
|
(vec![], &[]),
|
||||||
|
(vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]),
|
||||||
|
(vec![IoSlice::new(&[1])], &[1]),
|
||||||
|
(vec![IoSlice::new(&[1, 2])], &[1, 2]),
|
||||||
|
(vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]),
|
||||||
|
(vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]),
|
||||||
|
(vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]),
|
||||||
|
(vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]),
|
||||||
|
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]),
|
||||||
|
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]),
|
||||||
|
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
||||||
|
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
||||||
|
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]),
|
||||||
|
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]),
|
||||||
|
(vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]),
|
||||||
|
(vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]),
|
||||||
|
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]),
|
||||||
|
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]),
|
||||||
|
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)];
|
||||||
|
|
||||||
|
for (n_bufs, per_call) in writer_configs.iter().copied() {
|
||||||
|
for (mut input, wanted) in tests.clone().into_iter() {
|
||||||
|
let mut writer = test_writer(n_bufs, per_call);
|
||||||
|
assert!(writer.write_all_vectored(&mut *input).is_ok());
|
||||||
|
assert_eq!(&*writer.written, &*wanted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
239
src/util.rs
Normal file
239
src/util.rs
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
#![allow(missing_copy_implementations)]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
use crate::{
|
||||||
|
self as io, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
|
||||||
|
};
|
||||||
|
#[cfg(feature="collections")] use crate::BufRead;
|
||||||
|
|
||||||
|
/// A reader which is always at EOF.
|
||||||
|
///
|
||||||
|
/// This struct is generally created by calling [`empty()`]. Please see
|
||||||
|
/// the documentation of [`empty()`] for more details.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
pub struct Empty;
|
||||||
|
|
||||||
|
/// Constructs a new handle to an empty reader.
|
||||||
|
///
|
||||||
|
/// All reads from the returned reader will return [`Ok`]`(0)`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A slightly sad example of not reading anything into a buffer:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{self, Read};
|
||||||
|
///
|
||||||
|
/// let mut buffer = String::new();
|
||||||
|
/// io::empty().read_to_string(&mut buffer).unwrap();
|
||||||
|
/// assert!(buffer.is_empty());
|
||||||
|
/// ```
|
||||||
|
pub const fn empty() -> Empty {
|
||||||
|
Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Empty {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="collections")]
|
||||||
|
impl BufRead for Empty {
|
||||||
|
#[inline]
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
Ok(&[])
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn consume(&mut self, _n: usize) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Seek for Empty {
|
||||||
|
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_len(&mut self) -> io::Result<u64> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_position(&mut self) -> io::Result<u64> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Empty {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("Empty").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeHint for Empty {
|
||||||
|
#[inline]
|
||||||
|
fn upper_bound(&self) -> Option<usize> {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{self, Read};
|
||||||
|
///
|
||||||
|
/// let mut buffer = [0; 3];
|
||||||
|
/// io::repeat(0b101).read_exact(&mut buffer).unwrap();
|
||||||
|
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
|
||||||
|
/// ```
|
||||||
|
pub const fn repeat(byte: u8) -> Repeat {
|
||||||
|
Repeat { byte }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Repeat {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
for slot in &mut *buf {
|
||||||
|
*slot = self.byte;
|
||||||
|
}
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
let mut nwritten = 0;
|
||||||
|
for buf in bufs {
|
||||||
|
nwritten += self.read(buf)?;
|
||||||
|
}
|
||||||
|
Ok(nwritten)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
|
Initializer::nop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeHint for Repeat {
|
||||||
|
#[inline]
|
||||||
|
fn lower_bound(&self) -> usize {
|
||||||
|
usize::MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn upper_bound(&self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Repeat {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("Repeat").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, Default)]
|
||||||
|
pub struct Sink;
|
||||||
|
|
||||||
|
/// Creates an instance of a writer which will successfully consume all data.
|
||||||
|
///
|
||||||
|
/// All calls to [`write`] on the returned instance will return `Ok(buf.len())`
|
||||||
|
/// and the contents of the buffer will not be inspected.
|
||||||
|
///
|
||||||
|
/// [`write`]: Write::write
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use std::io::{self, Write};
|
||||||
|
///
|
||||||
|
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||||
|
/// let num_bytes = io::sink().write(&buffer).unwrap();
|
||||||
|
/// assert_eq!(num_bytes, 5);
|
||||||
|
/// ```
|
||||||
|
pub const fn sink() -> Sink {
|
||||||
|
Sink
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Sink {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||||
|
Ok(total_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for &Sink {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||||
|
Ok(total_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Sink {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("Sink").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
120
src/util/tests.rs
Normal file
120
src/util/tests.rs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
use crate::cmp::{max, min};
|
||||||
|
use crate::io::prelude::*;
|
||||||
|
use crate::io::{
|
||||||
|
copy, empty, repeat, sink, BufWriter, Empty, Repeat, Result, SeekFrom, Sink, DEFAULT_BUF_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[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<usize> {
|
||||||
|
let bytes = min(self.cap, self.read_size);
|
||||||
|
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<usize> {
|
||||||
|
self.observed_buffer = max(self.observed_buffer, buf.len());
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sink_sinks() {
|
||||||
|
let mut s = sink();
|
||||||
|
assert_eq!(s.write(&[]).unwrap(), 0);
|
||||||
|
assert_eq!(s.write(&[0]).unwrap(), 1);
|
||||||
|
assert_eq!(s.write(&[0; 1024]).unwrap(), 1024);
|
||||||
|
assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_some_bytes() {
|
||||||
|
assert_eq!(repeat(4).take(100).bytes().count(), 100);
|
||||||
|
assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
|
||||||
|
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn const_utils() {
|
||||||
|
const _: Empty = empty();
|
||||||
|
const _: Repeat = repeat(b'c');
|
||||||
|
const _: Sink = sink();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user