#[cfg(coverage)]
use std::cell::Cell;
use std::io::{
Error,
ErrorKind,
Result,
};
use crate::capacity_const::DEFAULT_BUFFER_CAPACITY;
use crate::ext::output_ext::OutputExt;
use crate::traits::validate_read_count;
use crate::util::{
UncheckedSlice,
create_vec,
try_reserve_vec,
};
use crate::{
Input,
Output,
};
pub trait InputExt: Input {
#[inline]
fn read_exact(&mut self, output: &mut [Self::Item]) -> Result<()> {
let read = self.read_exact_or_eof(output)?;
if read == output.len() {
Ok(())
} else {
Err(Error::new(
ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
}
#[inline]
unsafe fn read_exact_unchecked(
&mut self,
output: &mut [Self::Item],
index: usize,
count: usize,
) -> Result<()> {
let read =
unsafe { self.read_exact_or_eof_unchecked(output, index, count)? };
if read == count {
Ok(())
} else {
Err(Error::new(
ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
}
fn read_exact_or_eof(
&mut self,
output: &mut [Self::Item],
) -> Result<usize> {
let mut total = 0;
while total < output.len() {
let remaining = output.len() - total;
match unsafe { self.read_unchecked(output, total, remaining) } {
Ok(0) => break,
Ok(read) => {
validate_read_count(read, remaining)?;
total += read;
}
Err(error) if error.kind() == ErrorKind::Interrupted => {}
Err(error) => return Err(error),
}
}
Ok(total)
}
unsafe fn read_exact_or_eof_unchecked(
&mut self,
output: &mut [Self::Item],
index: usize,
count: usize,
) -> Result<usize> {
debug_assert!(
UncheckedSlice::range_fits(output.len(), index, count),
"unchecked read range exceeds output buffer"
);
let mut total = 0;
while total < count {
let remaining = count - total;
match unsafe {
self.read_unchecked(output, index + total, remaining)
} {
Ok(0) => break,
Ok(read) => {
validate_read_count(read, remaining)?;
total += read;
}
Err(error) if error.kind() == ErrorKind::Interrupted => {}
Err(error) => return Err(error),
}
}
Ok(total)
}
fn copy_to<O>(&mut self, output: &mut O) -> Result<u64>
where
O: Output<Item = Self::Item> + ?Sized,
Self::Item: Copy + Default,
{
let mut buffer =
create_vec(DEFAULT_BUFFER_CAPACITY, Self::Item::default())?;
let mut copied = 0_u64;
loop {
let requested = buffer.len();
let read = read_retrying_interrupted_limited(
self,
&mut buffer,
requested,
)?;
if read == 0 {
return Ok(copied);
}
unsafe {
output.write_all_unchecked(&buffer, 0, read)?;
}
copied = add_copied(copied, read)?;
}
}
fn copy_to_at_most<O>(
&mut self,
output: &mut O,
max_units: u64,
) -> Result<u64>
where
O: Output<Item = Self::Item> + ?Sized,
Self::Item: Copy + Default,
{
if max_units == 0 {
return Ok(0);
}
let mut buffer =
create_vec(DEFAULT_BUFFER_CAPACITY, Self::Item::default())?;
let mut remaining = max_units;
let mut copied = 0_u64;
while remaining > 0 {
let requested = remaining.min(buffer.len() as u64) as usize;
let read = read_retrying_interrupted_limited(
self,
&mut buffer,
requested,
)?;
if read == 0 {
break;
}
unsafe {
output.write_all_unchecked(&buffer, 0, read)?;
}
let read = read as u64;
remaining -= read;
copied += read;
}
Ok(copied)
}
fn copy_to_end_limited<O>(
&mut self,
output: &mut O,
max_units: u64,
) -> Result<u64>
where
O: Output<Item = Self::Item> + ?Sized,
Self::Item: Copy + Default,
{
let mut buffer =
create_vec(DEFAULT_BUFFER_CAPACITY, Self::Item::default())?;
let mut collected = Vec::new();
let mut remaining = max_units;
let mut copied = 0_u64;
loop {
let requested =
remaining.saturating_add(1).min(buffer.len() as u64) as usize;
let read = read_retrying_interrupted_limited(
self,
&mut buffer,
requested,
)?;
if read == 0 {
if !collected.is_empty() {
unsafe {
output.write_all_unchecked(
&collected,
0,
collected.len(),
)?;
}
}
return Ok(copied);
}
if (read as u64) > remaining {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"input exceeds maximum length of {max_units} items"
),
));
}
try_reserve_vec(&mut collected, read)?;
collected.extend_from_slice(&buffer[..read]);
let read = read as u64;
remaining -= read;
copied = add_copied(copied, read as usize)?;
}
}
}
impl<T> InputExt for T where T: Input + ?Sized {}
fn read_retrying_interrupted_limited<I>(
input: &mut I,
buffer: &mut [I::Item],
requested: usize,
) -> Result<usize>
where
I: Input + ?Sized,
{
loop {
match unsafe { input.read_unchecked(buffer, 0, requested) } {
Ok(read) => {
validate_read_count(read, requested)?;
return Ok(read);
}
Err(error) if error.kind() == ErrorKind::Interrupted => {}
Err(error) => return Err(error),
}
}
}
#[cfg(coverage)]
thread_local! {
static COVERAGE_FAIL_NEXT_ADD_COPIED: Cell<bool> = const { Cell::new(false) };
}
#[cfg(coverage)]
#[doc(hidden)]
pub fn coverage_fail_next_add_copied() {
COVERAGE_FAIL_NEXT_ADD_COPIED.with(|state| state.set(true));
}
#[cfg(coverage)]
#[doc(hidden)]
pub fn coverage_reset_add_copied_hooks() {
COVERAGE_FAIL_NEXT_ADD_COPIED.with(|state| state.set(false));
}
#[cfg(coverage)]
#[doc(hidden)]
pub fn coverage_natural_add_copied_overflow() -> Result<u64> {
add_copied(u64::MAX, 1)
}
#[inline(always)]
fn add_copied(copied: u64, read: usize) -> Result<u64> {
#[cfg(coverage)]
if COVERAGE_FAIL_NEXT_ADD_COPIED.with(|state| {
let fail = state.get();
if fail {
state.set(false);
}
fail
}) {
return Err(Error::new(
ErrorKind::InvalidData,
"copied item count overflows u64",
));
}
copied.checked_add(read as u64).ok_or_else(|| {
Error::new(ErrorKind::InvalidData, "copied item count overflows u64")
})
}