use std::cell::{Cell, RefCell};
use std::io;
use std::ops::Deref;
use std::ptr::NonNull;
use std::rc::Rc;
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::Arc;
use std::task::Waker;
use io_uring::types::BufRingEntry;
pub unsafe trait IoBuf: 'static {
fn as_ptr(&self) -> *const u8;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub unsafe trait IoBufMut: IoBuf {
fn as_mut_ptr(&mut self) -> *mut u8;
fn capacity(&self) -> usize;
unsafe fn set_init(&mut self, n: usize);
}
unsafe impl IoBuf for Vec<u8> {
fn as_ptr(&self) -> *const u8 {
<[u8]>::as_ptr(self)
}
fn len(&self) -> usize {
Vec::len(self)
}
}
unsafe impl IoBufMut for Vec<u8> {
fn as_mut_ptr(&mut self) -> *mut u8 {
<[u8]>::as_mut_ptr(self)
}
fn capacity(&self) -> usize {
Vec::capacity(self)
}
unsafe fn set_init(&mut self, n: usize) {
self.set_len(n);
}
}
unsafe impl IoBuf for String {
fn as_ptr(&self) -> *const u8 {
str::as_ptr(self)
}
fn len(&self) -> usize {
String::len(self)
}
}
unsafe impl IoBuf for Box<[u8]> {
fn as_ptr(&self) -> *const u8 {
<[u8]>::as_ptr(self)
}
fn len(&self) -> usize {
<[u8]>::len(self)
}
}
unsafe impl IoBuf for Rc<[u8]> {
fn as_ptr(&self) -> *const u8 {
<[u8]>::as_ptr(self)
}
fn len(&self) -> usize {
<[u8]>::len(self)
}
}
unsafe impl IoBuf for Arc<[u8]> {
fn as_ptr(&self) -> *const u8 {
<[u8]>::as_ptr(self)
}
fn len(&self) -> usize {
<[u8]>::len(self)
}
}
unsafe impl IoBuf for &'static [u8] {
fn as_ptr(&self) -> *const u8 {
<[u8]>::as_ptr(self)
}
fn len(&self) -> usize {
<[u8]>::len(self)
}
}
unsafe impl IoBuf for &'static str {
fn as_ptr(&self) -> *const u8 {
str::as_ptr(self)
}
fn len(&self) -> usize {
str::len(self)
}
}
struct Mapping {
ptr: NonNull<u8>,
len: usize,
}
impl Mapping {
fn new(len: usize) -> io::Result<Self> {
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
if ptr == libc::MAP_FAILED {
return Err(io::Error::last_os_error());
}
Ok(Self {
ptr: NonNull::new(ptr as *mut u8).expect("mmap returned null"),
len,
})
}
}
impl Drop for Mapping {
fn drop(&mut self) {
unsafe { libc::munmap(self.ptr.as_ptr() as *mut libc::c_void, self.len) };
}
}
pub(crate) struct BufPool {
ring: Mapping,
mem: Mapping,
count: u16,
buf_len: usize,
tail: Cell<u16>,
free: Cell<usize>,
waiters: RefCell<Vec<Waker>>,
}
impl BufPool {
pub(crate) fn new(count: u16, buf_len: usize) -> io::Result<Rc<Self>> {
if count == 0 || !count.is_power_of_two() || count > 32768 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"recv buffer count must be a power of two between 1 and 32768",
));
}
if buf_len == 0 || buf_len > u32::MAX as usize {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid recv buffer size",
));
}
let ring = Mapping::new(count as usize * std::mem::size_of::<BufRingEntry>())?;
let mem_size = (count as usize).checked_mul(buf_len).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "recv buffer pool too large")
})?;
let mem = Mapping::new(mem_size)?;
let pool = Self {
ring,
mem,
count,
buf_len,
tail: Cell::new(0),
free: Cell::new(0),
waiters: RefCell::new(Vec::new()),
};
for bid in 0..count {
pool.push(bid);
}
pool.publish_tail();
pool.free.set(count as usize);
Ok(Rc::new(pool))
}
pub(crate) fn ring_addr(&self) -> u64 {
self.ring.ptr.as_ptr() as u64
}
fn ring_ptr(&self) -> *mut BufRingEntry {
self.ring.ptr.as_ptr() as *mut BufRingEntry
}
pub(crate) fn count(&self) -> u16 {
self.count
}
pub(crate) fn free(&self) -> usize {
self.free.get()
}
pub(crate) fn taken(&self) {
self.free.set(self.free.get().saturating_sub(1));
}
pub(crate) fn recycle(&self, bid: u16) {
debug_assert!(bid < self.count);
self.push(bid);
self.publish_tail();
self.free.set(self.free.get() + 1);
if !self.waiters.borrow().is_empty() {
let waiters = std::mem::take(&mut *self.waiters.borrow_mut());
for waker in waiters {
waker.wake();
}
}
}
pub(crate) fn wait(&self, waker: &Waker) {
let mut waiters = self.waiters.borrow_mut();
if !waiters.iter().any(|w| w.will_wake(waker)) {
waiters.push(waker.clone());
}
}
fn push(&self, bid: u16) {
let tail = self.tail.get();
let idx = (tail & (self.count - 1)) as usize;
unsafe {
let entry = &mut *self.ring_ptr().add(idx);
entry.set_addr(self.mem.ptr.as_ptr().add(bid as usize * self.buf_len) as u64);
entry.set_len(self.buf_len as u32);
entry.set_bid(bid);
}
self.tail.set(tail.wrapping_add(1));
}
fn publish_tail(&self) {
unsafe {
let tail = BufRingEntry::tail(self.ring_ptr()) as *mut u16;
AtomicU16::from_ptr(tail).store(self.tail.get(), Ordering::Release);
}
}
pub(crate) fn copy_out(&self, bid: u16, len: usize) -> Vec<u8> {
self.slice(bid, len).to_vec()
}
fn slice(&self, bid: u16, len: usize) -> &[u8] {
assert!(
bid < self.count && len <= self.buf_len,
"kernel returned an invalid buffer"
);
unsafe {
std::slice::from_raw_parts(self.mem.ptr.as_ptr().add(bid as usize * self.buf_len), len)
}
}
}
pub struct RecvBuf {
inner: Inner,
}
enum Inner {
Pooled(Rc<BufPool>, u16, usize),
Heap(Vec<u8>),
Eof,
}
impl RecvBuf {
pub(crate) fn pooled(pool: Rc<BufPool>, bid: u16, len: usize) -> Self {
Self {
inner: Inner::Pooled(pool, bid, len),
}
}
pub(crate) fn heap(data: Vec<u8>) -> Self {
Self {
inner: Inner::Heap(data),
}
}
pub(crate) fn eof() -> Self {
Self { inner: Inner::Eof }
}
pub fn as_slice(&self) -> &[u8] {
match &self.inner {
Inner::Pooled(pool, bid, len) => pool.slice(*bid, *len),
Inner::Heap(data) => data,
Inner::Eof => &[],
}
}
}
impl Deref for RecvBuf {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl AsRef<[u8]> for RecvBuf {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl std::fmt::Debug for RecvBuf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RecvBuf")
.field("len", &self.as_slice().len())
.finish()
}
}
impl Drop for RecvBuf {
fn drop(&mut self) {
if let Inner::Pooled(pool, bid, _) = &self.inner {
pool.recycle(*bid);
}
}
}