use crate::buf::IoBufMut;
use crate::buf::fixed::{FixedBuffers, handle::CheckedOutBuf};
use libc::{UIO_MAXIOV, iovec};
use std::cmp;
use std::mem;
use std::ptr;
use std::slice;
pub(crate) struct Registry<T: IoBufMut> {
raw_bufs: ptr::NonNull<iovec>,
orig_cap: usize,
states: Vec<BufState>,
buffers: Vec<T>,
}
enum BufState {
Free { init_len: usize },
CheckedOut,
}
impl<T: IoBufMut> Registry<T> {
pub(crate) fn new(bufs: impl Iterator<Item = T>) -> Self {
let bufs = bufs.take(cmp::min(UIO_MAXIOV as usize, u16::MAX as usize));
let mut buffers = bufs.collect::<Vec<T>>();
let mut iovecs = Vec::with_capacity(buffers.len());
let mut states = Vec::with_capacity(buffers.len());
for buf in buffers.iter_mut() {
iovecs.push(iovec {
iov_base: buf.stable_mut_ptr() as *mut _,
iov_len: buf.bytes_total(),
});
states.push(BufState::Free {
init_len: buf.bytes_init(),
});
}
debug_assert_eq!(iovecs.len(), states.len());
debug_assert_eq!(iovecs.len(), buffers.len());
let raw_bufs = unsafe { ptr::NonNull::new_unchecked(iovecs.as_mut_ptr()) };
let orig_cap = iovecs.capacity();
mem::forget(iovecs);
Registry {
raw_bufs,
orig_cap,
states,
buffers,
}
}
pub(crate) fn check_out(&mut self, index: usize) -> Option<CheckedOutBuf> {
let state = self.states.get_mut(index)?;
let BufState::Free { init_len } = *state else {
return None;
};
*state = BufState::CheckedOut;
let iovec = unsafe { self.raw_bufs.as_ptr().add(index).read() };
debug_assert!(index <= u16::MAX as usize);
Some(CheckedOutBuf {
iovec,
init_len,
index: index as u16,
})
}
fn check_in_internal(&mut self, index: u16, init_len: usize) {
let state = self.states.get_mut(index as usize).expect("invalid buffer index");
debug_assert!(matches!(state, BufState::CheckedOut), "the buffer must be checked out");
*state = BufState::Free { init_len };
}
}
impl<T: IoBufMut> FixedBuffers for Registry<T> {
fn iovecs(&self) -> &[iovec] {
unsafe { slice::from_raw_parts(self.raw_bufs.as_ptr(), self.states.len()) }
}
unsafe fn check_in(&mut self, index: u16, init_len: usize) {
self.check_in_internal(index, init_len)
}
}
impl<T: IoBufMut> Drop for Registry<T> {
fn drop(&mut self) {
for (i, state) in self.states.iter().enumerate() {
match state {
BufState::Free { init_len, .. } => {
unsafe { self.buffers[i].set_init(*init_len) };
}
BufState::CheckedOut => unreachable!("all buffers must be checked in"),
}
}
let _ = unsafe { Vec::from_raw_parts(self.raw_bufs.as_ptr(), self.states.len(), self.orig_cap) };
}
}