#![allow(unsafe_code)]
use bytes::Bytes;
use compio::buf::{IoBufMut, SetBufInit};
use std::alloc::{alloc, dealloc, Layout};
use std::ptr::NonNull;
use std::sync::Arc;
pub const PAGE_SIZE: usize = 64 * 1024;
pub const PAGE_ALIGN: usize = 128;
struct Page {
ptr: NonNull<u8>,
}
unsafe impl Send for Page {}
unsafe impl Sync for Page {}
impl Drop for Page {
fn drop(&mut self) {
unsafe {
let layout = Layout::from_size_align_unchecked(PAGE_SIZE, PAGE_ALIGN);
dealloc(self.ptr.as_ptr(), layout);
}
}
}
struct PageOwner {
page: Arc<Page>,
}
impl AsRef<[u8]> for PageOwner {
fn as_ref(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.page.ptr.as_ptr(), PAGE_SIZE) }
}
}
pub struct SlabMut {
page: Arc<Page>,
ptr: NonNull<u8>,
cap: usize,
len: usize,
}
unsafe impl Send for SlabMut {}
unsafe impl Sync for SlabMut {}
unsafe impl compio::buf::IoBuf for SlabMut {
#[inline]
fn as_buf_ptr(&self) -> *const u8 {
self.ptr.as_ptr()
}
#[inline]
fn buf_len(&self) -> usize {
self.len
}
#[inline]
fn buf_capacity(&self) -> usize {
self.cap
}
}
unsafe impl IoBufMut for SlabMut {
#[inline]
fn as_buf_mut_ptr(&mut self) -> *mut u8 {
self.ptr.as_ptr()
}
}
impl SetBufInit for SlabMut {
#[inline]
unsafe fn set_buf_init(&mut self, len: usize) {
debug_assert!(len <= self.cap);
self.len = len;
}
}
impl SlabMut {
#[must_use]
pub fn freeze(self) -> Bytes {
let base = self.page.ptr.as_ptr();
let offset = unsafe { self.ptr.as_ptr().offset_from(base) } as usize;
debug_assert!(offset + self.len <= PAGE_SIZE);
let owner = PageOwner { page: self.page };
let full = Bytes::from_owner(owner);
full.slice(offset..offset + self.len)
}
}
pub struct IoArena {
current: Option<Arc<Page>>,
offset: usize,
}
impl Default for IoArena {
fn default() -> Self {
Self::new()
}
}
impl IoArena {
#[must_use]
pub const fn new() -> Self {
Self {
current: None,
offset: PAGE_SIZE, }
}
pub fn alloc_mut(&mut self, size: usize) -> SlabMut {
debug_assert!(size <= PAGE_SIZE);
if self.current.is_none() || self.offset + size > PAGE_SIZE {
self.alloc_page();
}
let page = self.current.as_ref().unwrap().clone();
let ptr = unsafe { NonNull::new_unchecked(page.ptr.as_ptr().add(self.offset)) };
self.offset += size;
SlabMut {
page,
ptr,
cap: size,
len: 0,
}
}
#[inline(never)]
fn alloc_page(&mut self) {
unsafe {
let layout = Layout::from_size_align_unchecked(PAGE_SIZE, PAGE_ALIGN);
let ptr = alloc(layout);
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
self.current = Some(Arc::new(Page {
ptr: NonNull::new_unchecked(ptr),
}));
self.offset = 0;
}
}
}