use std::io::{
BufRead,
Error,
ErrorKind,
Read,
Result,
Seek,
SeekFrom,
};
use crate::Buffer;
use crate::ReadExt;
use crate::buffered::DEFAULT_BUFFER_CAPACITY;
#[derive(Debug)]
pub struct BufferedByteInput<R> {
inner: R,
buffer: Buffer<u8>,
}
impl<R> BufferedByteInput<R> {
#[inline(always)]
#[must_use]
pub fn new(inner: R) -> Self {
Self::with_capacity(inner, DEFAULT_BUFFER_CAPACITY)
}
#[inline]
#[must_use]
pub fn with_capacity(inner: R, capacity: usize) -> Self {
Self {
inner,
buffer: Buffer::with_capacity(capacity),
}
}
#[inline(always)]
pub const fn inner(&self) -> &R {
&self.inner
}
#[inline(always)]
pub fn inner_mut(&mut self) -> &mut R {
&mut self.inner
}
#[inline(always)]
#[must_use]
pub fn into_parts(self) -> (R, Vec<u8>) {
let unread = self.unread_slice().to_vec();
(self.inner, unread)
}
#[inline(always)]
#[must_use]
pub fn capacity(&self) -> usize {
self.buffer.capacity()
}
#[inline(always)]
#[must_use]
pub fn available(&self) -> usize {
self.buffer.available()
}
#[inline(always)]
#[must_use]
pub fn unread_slice(&self) -> &[u8] {
&self.buffer.data()[self.buffer.position()..self.buffer.limit()]
}
#[inline(always)]
#[must_use]
pub fn unread_raw_parts(&self) -> (&[u8], usize, usize) {
(
self.buffer.data(),
self.buffer.position(),
self.buffer.available(),
)
}
#[inline(always)]
pub fn consume(&mut self, count: usize) {
assert!(
count <= self.available(),
"cannot consume beyond buffered input"
);
unsafe {
self.buffer.consume_unchecked(count);
}
}
#[inline(always)]
pub unsafe fn consume_unchecked(&mut self, count: usize) {
unsafe {
self.buffer.consume_unchecked(count);
}
}
#[inline(always)]
fn tail_capacity(&self) -> usize {
self.buffer.spare_capacity()
}
#[inline(always)]
fn discard_buffer(&mut self) {
self.buffer.clear();
}
#[inline(always)]
fn backshift(&mut self) {
self.buffer.compact();
}
}
impl<R> BufferedByteInput<R>
where
R: Read,
{
fn read_more(&mut self) -> Result<bool> {
let count = self.tail_capacity();
debug_assert!(count > 0, "buffer has no tail capacity");
loop {
let limit = self.buffer.limit();
match unsafe {
self.inner
.read_unchecked(self.buffer.data_mut(), limit, count)
} {
Ok(0) => return Ok(false),
Ok(read) => {
validate_read_count(read, count)?;
unsafe {
self.buffer.advance_unchecked(read);
}
return Ok(true);
}
Err(error) if error.kind() == ErrorKind::Interrupted => {
continue;
}
Err(error) => return Err(error),
}
}
}
pub fn fill_more(&mut self) -> Result<bool> {
if self.available() == 0 {
self.discard_buffer();
} else if self.tail_capacity() == 0 {
self.backshift();
}
self.read_more()
}
#[inline]
pub fn fill_until(&mut self, count: usize) -> Result<bool> {
if count > self.capacity() {
return Err(Error::new(
ErrorKind::InvalidInput,
"requested available bytes exceed buffered input capacity",
));
}
while self.available() < count {
let available = self.available();
if available == 0 {
self.discard_buffer();
} else {
let missing = count - available;
if self.tail_capacity() < missing {
self.backshift();
}
}
if !self.read_more()? {
return Ok(false);
}
}
Ok(true)
}
#[inline]
pub fn ensure_available(&mut self, count: usize) -> Result<()> {
if self.fill_until(count)? {
return Ok(());
}
let available = self.available();
unsafe {
self.consume_unchecked(available);
}
Err(Error::new(
ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
#[inline(always)]
pub unsafe fn read_into_unchecked(
&mut self,
output: &mut [u8],
output_index: usize,
count: usize,
) -> Result<usize> {
debug_assert!(
output_index
.checked_add(count)
.is_some_and(|end| end <= output.len()),
"unchecked read output range exceeds destination buffer"
);
if count == 0 {
return Ok(0);
}
if self.available() == 0 {
self.discard_buffer();
if count >= self.buffer.capacity() {
let read = unsafe {
self.inner.read_unchecked(output, output_index, count)
}?;
validate_read_count(read, count)?;
return Ok(read);
}
if !self.read_more()? {
return Ok(0);
}
}
let read_count = count.min(self.available());
unsafe {
self.buffer
.copy_to_unchecked(output, output_index, read_count);
}
Ok(read_count)
}
fn seek_logical(&mut self, position: SeekFrom) -> Result<u64>
where
R: Seek,
{
let position = match position {
SeekFrom::Current(offset) => {
let unread = self.available() as i64;
let adjusted = offset.checked_sub(unread).ok_or_else(|| {
Error::new(
ErrorKind::InvalidInput,
"current seek offset underflows after buffered adjustment",
)
})?;
self.inner.seek(SeekFrom::Current(adjusted))
}
other => self.inner.seek(other),
}?;
self.discard_buffer();
Ok(position)
}
}
impl<R> Read for BufferedByteInput<R>
where
R: Read,
{
#[inline(always)]
fn read(&mut self, output: &mut [u8]) -> Result<usize> {
unsafe { self.read_into_unchecked(output, 0, output.len()) }
}
}
impl<R> BufRead for BufferedByteInput<R>
where
R: Read,
{
#[inline]
fn fill_buf(&mut self) -> Result<&[u8]> {
if self.available() == 0 {
self.discard_buffer();
if !self.read_more()? {
return Ok(&[]);
}
}
Ok(self.unread_slice())
}
#[inline(always)]
fn consume(&mut self, amount: usize) {
BufferedByteInput::consume(self, amount);
}
}
impl<R> Seek for BufferedByteInput<R>
where
R: Read + Seek,
{
#[inline(always)]
fn seek(&mut self, position: SeekFrom) -> Result<u64> {
self.seek_logical(position)
}
}
#[inline(always)]
fn validate_read_count(read: usize, requested: usize) -> Result<()> {
if read > requested {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"reader reported {read} bytes for a {requested}-byte buffer"
),
));
}
Ok(())
}