use crate::CoreError;
use crate::reactor::Fd;
const READ_CHUNK: usize = 65536;
#[derive(Default)]
#[repr(align(64))]
pub(crate) struct BufferState {
stdout: Vec<u8>,
stderr: Vec<u8>,
limit: usize,
output_limit_exceeded: bool,
stdout_early_exited: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ReadState {
Open,
Eof,
EarlyExit,
}
impl BufferState {
pub(crate) fn new(limit: usize) -> Self {
Self {
stdout: Vec::with_capacity(1024),
stderr: Vec::with_capacity(1024),
limit,
output_limit_exceeded: false,
stdout_early_exited: false,
}
}
#[inline(always)]
pub(crate) fn read_from_fd(
&mut self,
fd: &Fd,
is_stdout: bool,
early_exit: &mut Option<impl FnMut(&[u8]) -> bool>,
) -> Result<ReadState, CoreError> {
loop {
let current_total = self.stdout.len().saturating_add(self.stderr.len());
let remaining_limit = self.limit.saturating_sub(current_total);
if remaining_limit == 0 {
let mut drop_buf = [0u8; 8192];
match fd.read_slice(&mut drop_buf) {
Ok(Some(n)) if n > 0 => {
self.output_limit_exceeded = true;
continue;
}
Ok(Some(_)) => return Ok(ReadState::Eof),
Ok(None) => return Ok(ReadState::Open),
Err(e) => return Err(e),
}
}
let dest = if is_stdout {
&mut self.stdout
} else {
&mut self.stderr
};
let len = dest.len();
let to_read = remaining_limit.min(READ_CHUNK);
dest.resize(len + to_read, 0);
match fd.read_slice(&mut dest[len..len + to_read]) {
Ok(Some(n)) if n > 0 => {
dest.truncate(len + n);
if is_stdout
&& let Some(f) = early_exit
&& f(&dest[len..len + n])
{
self.stdout_early_exited = true;
return Ok(ReadState::EarlyExit);
}
}
Ok(Some(_)) => {
dest.truncate(len);
return Ok(ReadState::Eof);
}
Ok(None) => {
dest.truncate(len);
return Ok(ReadState::Open);
}
Err(e) => {
dest.truncate(len);
return Err(e);
}
}
}
}
#[inline(always)]
pub(crate) fn output_limit_exceeded(&self) -> bool {
self.output_limit_exceeded
}
#[inline(always)]
pub(crate) fn stdout_early_exited(&self) -> bool {
self.stdout_early_exited
}
pub(crate) fn into_parts(mut self) -> (Vec<u8>, Vec<u8>, bool, bool) {
(
std::mem::take(&mut self.stdout),
std::mem::take(&mut self.stderr),
self.output_limit_exceeded,
self.stdout_early_exited,
)
}
}