use core::ptr::NonNull;
use crate::class;
use crate::segment::{self, Segment};
const CAP: usize = 128;
const GROUPS: usize = 8;
#[derive(Clone, Copy)]
struct Pending {
addr: usize,
requested: u32,
class: u8,
}
pub(crate) struct Outbound {
entries: [Pending; CAP],
len: u16,
}
impl Outbound {
pub(crate) const fn new() -> Self {
Self { entries: [Pending { addr: 0, requested: 0, class: 0 }; CAP], len: 0 }
}
pub(crate) fn push(&mut self, ptr: NonNull<u8>, requested: usize, class: usize) -> bool {
if self.len as usize == CAP {
return false;
}
self.entries[self.len as usize] = Pending {
addr: ptr.as_ptr() as usize,
requested: requested as u32,
class: class as u8,
};
self.len += 1;
true
}
pub(crate) fn is_empty(&self) -> bool {
self.len == 0
}
pub(crate) fn flush(&mut self) {
let mut groups: [Group; GROUPS] = [Group::EMPTY; GROUPS];
for i in 0..self.len as usize {
let e = self.entries[i];
let seg = unsafe {
segment::segment_of(NonNull::new_unchecked(e.addr as *mut u8))
};
let slot = groups
.iter_mut()
.find(|g| g.seg == seg.as_ptr() || g.seg.is_null());
let g = match slot {
Some(g) => g,
None => {
let g = groups.iter_mut().max_by_key(|g| g.count).unwrap();
g.ship();
g
}
};
if g.seg.is_null() {
g.seg = seg.as_ptr();
}
g.link(e);
}
for g in &mut groups {
g.ship();
}
self.len = 0;
}
}
#[derive(Clone, Copy)]
struct Group {
seg: *mut Segment,
head: *mut u8,
tail: *mut u8,
live_sum: usize,
bytes_sum: usize,
count: u32,
}
impl Group {
const EMPTY: Self = Self {
seg: core::ptr::null_mut(),
head: core::ptr::null_mut(),
tail: core::ptr::null_mut(),
live_sum: 0,
bytes_sum: 0,
count: 0,
};
fn link(&mut self, e: Pending) {
let p = e.addr as *mut u8;
unsafe {
p.cast::<*mut u8>().write(self.head);
p.add(segment::FOREIGN_SIZE_OFFSET).cast::<u32>().write(e.requested);
}
if self.head.is_null() {
self.tail = p;
}
self.head = p;
self.live_sum += e.requested as usize;
self.bytes_sum += class::size_of(e.class as usize);
self.count += 1;
}
fn ship(&mut self) {
if self.seg.is_null() || self.head.is_null() {
*self = Self::EMPTY;
return;
}
let seg = unsafe { &*self.seg };
unsafe {
segment::splice_foreign(seg, self.head, self.tail, self.live_sum, self.bytes_sum);
}
*self = Self::EMPTY;
}
}