use core::sync::atomic::{AtomicBool, Ordering};
use crate::buf::owned::OwnedBuf;
use crate::io::raw::Raw;
static CALL: AtomicBool = AtomicBool::new(true);
#[cfg(test)]
pub(super) fn has_call() -> bool {
CALL.load(Ordering::Relaxed)
}
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
pub(super) enum Cache {
#[default]
Try,
Cold,
Never,
}
pub(super) enum Now {
Read(usize),
Missed,
Job,
}
#[inline]
pub(super) fn read(fd: Raw, buf: &mut OwnedBuf, at: u64, cache: &mut Cache) -> Now {
if *cache != Cache::Try || !CALL.load(Ordering::Relaxed) || buf.vectored_mut().is_some() {
return Now::Job;
}
let Ok(offset) = i64::try_from(at) else {
return Now::Job;
};
let spare = buf.spare_capacity_mut();
#[cfg(test)]
crate::io::sys::file_calls::note(fd, (b'r', spare.len(), at));
let iov = libc::iovec {
iov_base: spare.as_mut_ptr().cast(),
iov_len: spare.len(),
};
let res = unsafe {
crate::io::sys::syscall(
libc::SYS_preadv2,
[
fd as usize,
(&iov as *const libc::iovec) as usize,
1,
offset as usize,
0,
libc::RWF_NOWAIT as usize,
],
)
};
match res {
Ok(n) => {
unsafe { buf.add_len(n) };
Now::Read(n)
}
Err(err) => match err.raw_os_error() {
Some(libc::EAGAIN) => {
*cache = Cache::Cold;
Now::Missed
}
Some(libc::ENOSYS) => {
CALL.store(false, Ordering::Relaxed);
Now::Job
}
Some(libc::EOPNOTSUPP | libc::EINVAL) => {
*cache = Cache::Never;
Now::Job
}
_ => Now::Job,
},
}
}