use std::io;
use std::sync::Arc;
use kovan_queue::array_queue::ArrayQueue;
use crate::syscore::linux::iouring::ops::NOT_AN_OP;
use crate::syscore::linux::iouring::ring::Ring;
use crate::syscore::linux::iouring::ring::buf_ring::{BufMem, BufRing};
const GROUP: u16 = 0;
const CAP_SHARE: usize = 8;
pub(crate) struct RecvGroup {
ring: BufRing,
back: Arc<Returns>,
owners: Vec<(u64, u32)>,
cap: usize,
}
unsafe impl Send for RecvGroup {}
pub(crate) struct Returns {
mem: Arc<BufMem>,
queue: ArrayQueue<u16>,
}
pub(crate) struct RingChunk {
back: Arc<Returns>,
bid: u16,
start: u32,
end: u32,
}
impl RecvGroup {
pub(crate) fn new(ring: &Ring, entries: u16, size: u32) -> io::Result<Self> {
let mut buffers = BufRing::new(ring, GROUP, entries, size)?;
for bid in 0..entries {
buffers.add(bid)?;
}
buffers.commit();
let back = Arc::new(Returns {
mem: Arc::clone(buffers.memory()),
queue: ArrayQueue::new(usize::from(entries)),
});
let total = usize::from(entries) * size as usize;
Ok(Self {
ring: buffers,
back,
owners: vec![(NOT_AN_OP, 0); usize::from(entries)],
cap: (total / CAP_SHARE).max(size as usize),
})
}
pub(crate) fn bgid(&self) -> u16 {
self.ring.bgid()
}
pub(crate) fn entries(&self) -> u16 {
self.ring.entries()
}
pub(crate) fn cap(&self) -> usize {
self.cap
}
pub(crate) fn buf_size(&self) -> u32 {
self.ring.buf_size()
}
pub(crate) fn hand_out(&mut self, user_data: u64, bid: u16, len: u32) -> Option<RingChunk> {
let owner = self.owners.get_mut(usize::from(bid))?;
if len > self.ring.buf_size() {
return None;
}
*owner = (user_data, len);
Some(RingChunk {
back: Arc::clone(&self.back),
bid,
start: 0,
end: len,
})
}
pub(crate) fn give_back(&mut self, bid: u16) {
if let Some(owner) = self.owners.get_mut(usize::from(bid)) {
*owner = (NOT_AN_OP, 0);
}
if self.ring.add(bid).is_ok() {
self.ring.commit();
}
}
pub(crate) fn take_back(&mut self, mut release: impl FnMut(u64, u32)) {
let mut any = false;
while let Some(bid) = self.back.queue.pop() {
if let Some(owner) = self.owners.get_mut(usize::from(bid)) {
let (user_data, len) = core::mem::replace(owner, (NOT_AN_OP, 0));
if user_data != NOT_AN_OP {
release(user_data, len);
}
}
if self.ring.add(bid).is_ok() {
any = true;
}
}
if any {
self.ring.commit();
}
}
}
#[cfg(test)]
impl RecvGroup {
pub(crate) fn out(&self) -> usize {
self.owners
.iter()
.filter(|(user_data, _)| *user_data != NOT_AN_OP)
.map(|(_, len)| *len as usize)
.sum()
}
}
impl RingChunk {
pub(crate) fn bytes(&self) -> &[u8] {
unsafe { self.back.mem.bytes(self.bid, self.start, self.end) }.unwrap_or_default()
}
pub(crate) fn narrow(&mut self, start: usize, end: usize) {
let (start, end) = (
u32::try_from(start).unwrap_or(u32::MAX),
u32::try_from(end).unwrap_or(u32::MAX),
);
self.end = end.min(self.end);
self.start = start.min(self.end);
}
pub(crate) fn consume(&mut self, n: usize) {
let n = u32::try_from(n).unwrap_or(u32::MAX);
self.start = self.start.saturating_add(n).min(self.end);
}
pub(crate) fn len(&self) -> usize {
(self.end - self.start) as usize
}
}
impl Drop for RingChunk {
fn drop(&mut self) {
let given_back = self.back.queue.push(self.bid);
debug_assert!(given_back.is_ok(), "buffer {} came back twice", self.bid);
}
}
impl core::fmt::Debug for RingChunk {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RingChunk")
.field("bid", &self.bid)
.field("len", &self.len())
.finish()
}
}