use std::{
collections::TryReserveError,
io::{
Error,
ErrorKind,
Result,
SeekFrom,
},
};
use crate::buffered::{
DEFAULT_BUFFER_CAPACITY,
EnsuredBufferedInput,
};
use crate::traits::validate_read_count;
use crate::util::SliceRange;
use crate::{
Buffer,
Input,
Seekable,
SeekableInput,
};
#[must_use]
#[derive(Debug)]
pub struct BufferedInput<I>
where
I: Input,
I::Item: Clone + Default,
{
inner: I,
buffer: Buffer<I::Item>,
}
fn read_more_impl<T>(
inner: &mut dyn Input<Item = T>,
buffer: &mut Buffer<T>,
) -> Result<bool>
where
T: Clone + Default,
{
let count = buffer.spare_capacity();
debug_assert!(count > 0, "buffer has no tail capacity");
loop {
let limit = buffer.limit();
match unsafe { inner.read_unchecked(buffer.data_mut(), limit, count) } {
Ok(0) => return Ok(false),
Ok(read) => {
validate_read_count(read, count)?;
unsafe {
buffer.advance(read);
}
return Ok(true);
}
Err(error) if error.kind() == ErrorKind::Interrupted => continue,
Err(error) => return Err(error),
}
}
}
fn fill_more_impl<T>(
inner: &mut dyn Input<Item = T>,
buffer: &mut Buffer<T>,
) -> Result<bool>
where
T: Clone + Default,
{
if buffer.available() == 0 {
buffer.clear();
} else if buffer.spare_capacity() == 0 {
buffer.compact();
if buffer.spare_capacity() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
"buffered input is full; consume buffered items before refilling",
));
}
}
read_more_impl(inner, buffer)
}
fn fill_until_impl<T>(
inner: &mut dyn Input<Item = T>,
buffer: &mut Buffer<T>,
count: usize,
) -> Result<bool>
where
T: Clone + Default,
{
if count > buffer.capacity() {
return Err(Error::new(
ErrorKind::InvalidInput,
"requested available items exceed buffered input capacity",
));
}
while buffer.available() < count {
let available = buffer.available();
if available == 0 {
buffer.clear();
} else {
let missing = count - available;
if buffer.spare_capacity() < missing {
buffer.compact();
}
}
if !read_more_impl(inner, buffer)? {
return Ok(false);
}
}
Ok(true)
}
fn ensure_available_impl<T>(
inner: &mut dyn Input<Item = T>,
buffer: &mut Buffer<T>,
count: usize,
) -> Result<()>
where
T: Clone + Default,
{
if fill_until_impl(inner, buffer, count)? {
return Ok(());
}
let available = buffer.available();
unsafe {
buffer.consume(available);
}
Err(Error::new(
ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
unsafe fn read_unchecked_impl<T>(
inner: &mut dyn Input<Item = T>,
buffer: &mut Buffer<T>,
output: &mut [T],
output_index: usize,
count: usize,
) -> Result<usize>
where
T: Clone + Default,
{
debug_assert!(
SliceRange::range_fits(output.len(), output_index, count),
"unchecked read output range exceeds destination buffer"
);
if count == 0 {
return Ok(0);
}
if buffer.available() == 0 {
buffer.clear();
if count >= buffer.capacity() {
let read =
unsafe { inner.read_unchecked(output, output_index, count) }?;
validate_read_count(read, count)?;
return Ok(read);
}
if !read_more_impl(inner, buffer)? {
return Ok(0);
}
}
let read_count = count.min(buffer.available());
unsafe {
buffer.copy_to(output, output_index, read_count);
}
Ok(read_count)
}
unsafe fn copy_available_to_output<T>(
buffer: &mut Buffer<T>,
output: &mut [T],
output_index: usize,
count: usize,
) -> usize
where
T: Clone + Default,
{
let copied = count.min(buffer.available());
if copied != 0 {
unsafe {
buffer.copy_to(output, output_index, copied);
}
}
copied
}
unsafe fn read_direct_fully<T>(
inner: &mut dyn Input<Item = T>,
output: &mut [T],
output_index: usize,
count: usize,
) -> Result<usize> {
loop {
match unsafe { inner.read_fully_unchecked(output, output_index, count) }
{
Ok(read) => {
validate_read_count(read, count)?;
return Ok(read);
}
Err(error) if error.kind() == ErrorKind::Interrupted => continue,
Err(error) => return Err(error),
}
}
}
unsafe fn read_buffered_remainder<T>(
inner: &mut dyn Input<Item = T>,
buffer: &mut Buffer<T>,
output: &mut [T],
output_index: usize,
count: usize,
) -> Result<usize>
where
T: Clone + Default,
{
let mut total = 0;
while total < count {
let remaining = count - total;
match unsafe {
read_unchecked_impl(
inner,
buffer,
output,
output_index + total,
remaining,
)
} {
Ok(0) => break,
Ok(read) => total += read,
Err(error) => return Err(error),
}
}
Ok(total)
}
unsafe fn read_fully_unchecked_impl<T>(
inner: &mut dyn Input<Item = T>,
buffer: &mut Buffer<T>,
output: &mut [T],
output_index: usize,
count: usize,
) -> Result<usize>
where
T: Clone + Default,
{
debug_assert!(
SliceRange::range_fits(output.len(), output_index, count),
"unchecked read-fully output range exceeds destination buffer"
);
if count == 0 {
return Ok(0);
}
let total = unsafe {
copy_available_to_output(buffer, output, output_index, count)
};
if total == count {
return Ok(total);
}
let remaining = count - total;
if remaining >= buffer.capacity() {
buffer.clear();
let read = unsafe {
read_direct_fully(inner, output, output_index + total, remaining)
}?;
return Ok(total + read);
}
let read = unsafe {
read_buffered_remainder(
inner,
buffer,
output,
output_index + total,
remaining,
)
}?;
Ok(total + read)
}
impl<I> BufferedInput<I>
where
I: Input,
I::Item: Clone + Default,
{
#[inline(always)]
pub fn new(inner: I) -> Self {
Self::with_capacity(inner, DEFAULT_BUFFER_CAPACITY)
}
#[inline]
pub fn with_capacity(inner: I, capacity: usize) -> Self {
Self {
inner,
buffer: Buffer::with_capacity(capacity),
}
}
#[inline]
pub fn try_with_capacity(
inner: I,
capacity: usize,
) -> std::result::Result<Self, TryReserveError> {
Ok(Self {
inner,
buffer: Buffer::try_with_capacity(capacity)?,
})
}
#[inline]
pub fn ensure(input: I) -> EnsuredBufferedInput<I> {
if input.is_buffered() {
EnsuredBufferedInput::AlreadyBuffered(input)
} else {
EnsuredBufferedInput::Buffered(Self::new(input))
}
}
#[inline]
#[must_use]
pub fn ensure_boxed<'a>(input: I) -> Box<dyn Input<Item = I::Item> + 'a>
where
I: 'a,
I::Item: 'a,
{
if input.is_buffered() {
Box::new(input)
} else {
Box::new(Self::new(input))
}
}
#[inline(always)]
#[must_use]
pub const fn inner(&self) -> &I {
&self.inner
}
#[inline(always)]
#[must_use]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.inner
}
#[inline(always)]
#[must_use = "the returned inner input and unread buffer must be handled"]
pub fn into_parts(self) -> (I, Buffer<I::Item>) {
(self.inner, self.buffer)
}
#[inline(always)]
#[must_use]
pub fn capacity(&self) -> usize {
self.buffer.capacity()
}
#[inline(always)]
pub fn try_reserve_capacity(
&mut self,
capacity: usize,
) -> std::result::Result<(), std::collections::TryReserveError> {
self.buffer.try_reserve_capacity(capacity)
}
#[inline(always)]
#[must_use]
pub fn unread_len(&self) -> usize {
self.buffer.available()
}
#[inline(always)]
#[must_use]
pub fn unread(&self) -> &[I::Item] {
self.buffer.readable()
}
#[inline(always)]
pub unsafe fn consume(&mut self, count: usize) {
unsafe {
self.buffer.consume(count);
}
}
#[inline]
pub unsafe fn copy_unread_to(
&self,
output: &mut [I::Item],
output_index: usize,
count: usize,
) {
debug_assert!(
SliceRange::range_fits(output.len(), output_index, count),
"unchecked unread destination range exceeds output buffer"
);
debug_assert!(
count <= self.buffer.available(),
"unchecked unread copy exceeds available buffer items"
);
unsafe {
let source = self.buffer.readable().get_unchecked(..count);
let output =
output.get_unchecked_mut(output_index..output_index + count);
output.clone_from_slice(source);
}
}
#[inline(always)]
pub fn fill_more(&mut self) -> Result<bool> {
fill_more_impl(&mut self.inner, &mut self.buffer)
}
#[inline(always)]
pub fn fill_until(&mut self, count: usize) -> Result<bool> {
fill_until_impl(&mut self.inner, &mut self.buffer, count)
}
#[inline(always)]
pub fn ensure_available(&mut self, count: usize) -> Result<()> {
ensure_available_impl(&mut self.inner, &mut self.buffer, count)
}
#[inline(always)]
pub unsafe fn read_unchecked(
&mut self,
output: &mut [I::Item],
output_index: usize,
count: usize,
) -> Result<usize> {
unsafe {
read_unchecked_impl(
&mut self.inner,
&mut self.buffer,
output,
output_index,
count,
)
}
}
#[inline(always)]
pub fn read(&mut self, output: &mut [I::Item]) -> Result<usize> {
unsafe { self.read_unchecked(output, 0, output.len()) }
}
#[inline(always)]
pub unsafe fn read_fully_unchecked(
&mut self,
output: &mut [I::Item],
output_index: usize,
count: usize,
) -> Result<usize> {
unsafe {
read_fully_unchecked_impl(
&mut self.inner,
&mut self.buffer,
output,
output_index,
count,
)
}
}
#[inline(always)]
pub fn read_fully(&mut self, output: &mut [I::Item]) -> Result<usize> {
unsafe { self.read_fully_unchecked(output, 0, output.len()) }
}
pub fn seek_to(&mut self, position: SeekFrom) -> Result<u64>
where
I: SeekableInput,
{
match position {
SeekFrom::Current(offset) => {
if self.seek_within_buffer(offset) {
return self.stream_position();
}
let position = self.seek_relative_slow(offset)?;
self.discard_buffer();
Ok(position)
}
other => {
let position = Seekable::seek_to(&mut self.inner, other)?;
self.discard_buffer();
Ok(position)
}
}
}
#[inline]
pub fn stream_position(&mut self) -> Result<u64>
where
I: SeekableInput,
{
let position =
Seekable::seek_to(&mut self.inner, SeekFrom::Current(0))?;
let unread = self.unread_len() as u64;
position.checked_sub(unread).ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
"buffered unread items exceed wrapped input position",
)
})
}
#[inline]
fn seek_relative_slow(&mut self, offset: i64) -> Result<u64>
where
I: SeekableInput,
{
let unread = i64::try_from(self.unread_len()).map_err(|_| {
Error::new(
ErrorKind::InvalidInput,
"buffered unread item count exceeds i64",
)
})?;
let adjusted = offset.checked_sub(unread).ok_or_else(|| {
Error::new(
ErrorKind::InvalidInput,
"current seek offset underflows after buffered adjustment",
)
})?;
Seekable::seek_to(&mut self.inner, SeekFrom::Current(adjusted))
}
#[must_use]
fn seek_within_buffer(&mut self, offset: i64) -> bool {
if offset >= 0 {
let count = offset as u64;
if count <= self.unread_len() as u64 {
let count = count as usize;
unsafe {
self.buffer.consume(count);
}
return true;
}
return false;
}
let count = offset.unsigned_abs();
if count <= self.buffer.position() as u64 {
let count = count as usize;
unsafe {
self.buffer.rewind(count);
}
return true;
}
false
}
#[inline(always)]
fn discard_buffer(&mut self) {
self.buffer.clear();
}
}
impl<I> Input for BufferedInput<I>
where
I: Input,
I::Item: Clone + Default,
{
type Item = I::Item;
#[inline(always)]
fn is_buffered(&self) -> bool {
true
}
#[inline(always)]
unsafe fn read_unchecked(
&mut self,
output: &mut [I::Item],
output_index: usize,
count: usize,
) -> Result<usize> {
unsafe {
BufferedInput::read_unchecked(self, output, output_index, count)
}
}
#[inline(always)]
fn read(&mut self, output: &mut [I::Item]) -> Result<usize> {
BufferedInput::read(self, output)
}
#[inline(always)]
unsafe fn read_fully_unchecked(
&mut self,
output: &mut [Self::Item],
index: usize,
count: usize,
) -> Result<usize> {
unsafe {
BufferedInput::read_fully_unchecked(self, output, index, count)
}
}
#[inline(always)]
fn read_fully(&mut self, output: &mut [Self::Item]) -> Result<usize> {
BufferedInput::read_fully(self, output)
}
}
impl<I> Seekable for BufferedInput<I>
where
I: SeekableInput,
<I as Input>::Item: Clone + Default,
{
type Unit = <I as Input>::Item;
#[inline(always)]
fn seek_to(&mut self, position: SeekFrom) -> Result<u64> {
BufferedInput::seek_to(self, position)
}
}