use std::collections::VecDeque;
use dambi::Bytes;
use dope_core::IoVec;
pub(crate) struct SendRing<const IOV: usize> {
queue: VecDeque<Bytes>,
partial_sent: u32,
iov_buf: [IoVec; IOV],
msghdr: libc::msghdr,
}
impl<const IOV: usize> SendRing<IOV> {
#[inline(always)]
pub(crate) fn new() -> Self {
Self {
queue: VecDeque::new(),
partial_sent: 0,
iov_buf: [IoVec::empty(); IOV],
msghdr: unsafe { std::mem::zeroed() },
}
}
#[inline(always)]
pub(crate) fn push(&mut self, bytes: Bytes) {
self.queue.push_back(bytes);
}
#[inline(always)]
pub(crate) fn is_empty(&self) -> bool {
self.queue.is_empty()
}
#[inline(always)]
pub(crate) fn fill_msghdr(&mut self, bytes_cap: usize) -> Option<&libc::msghdr> {
if self.queue.is_empty() {
return None;
}
let cap = bytes_cap.min(u32::MAX as usize);
let mut n = 0usize;
let mut bytes = 0usize;
for (i, b) in self.queue.iter().enumerate() {
if n == IOV || bytes >= cap {
break;
}
let off = if i == 0 {
self.partial_sent as usize
} else {
0
};
let slice = b.as_slice();
if off >= slice.len() {
continue;
}
let avail = slice.len() - off;
let take = avail.min(cap - bytes);
self.iov_buf[n] = IoVec::from_slice(&slice[off..off + take], 0);
bytes += take;
n += 1;
if take < avail {
break;
}
}
if n == 0 {
return None;
}
self.msghdr.msg_iov = self.iov_buf.as_mut_ptr() as *mut libc::iovec;
self.msghdr.msg_iovlen = n as _;
Some(&self.msghdr)
}
#[inline(always)]
pub(crate) fn ack(&mut self, n: u32) {
let mut left = n as u64;
while left > 0 {
let Some(head) = self.queue.front() else {
break;
};
let head_len = head.len() as u64;
let already = self.partial_sent as u64;
let remaining = head_len - already;
if left >= remaining {
left -= remaining;
self.partial_sent = 0;
self.queue.pop_front();
} else {
self.partial_sent += left as u32;
left = 0;
}
}
}
}
impl<const IOV: usize> Default for SendRing<IOV> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}