use std::cell::Cell;
use std::ops::{Deref, DerefMut};
use std::panic::{RefUnwindSafe, UnwindSafe};
pub fn type_of_this<T>(_: &T) -> String {
std::any::type_name::<T>().to_string()
}
pub struct IoBuf<T> {
ptr: *mut T,
size: usize,
mlocked: Cell<bool>,
}
unsafe impl<T> Send for IoBuf<T> {}
unsafe impl<T> Sync for IoBuf<T> {}
impl<T> RefUnwindSafe for IoBuf<T> {}
impl<T> UnwindSafe for IoBuf<T> {}
impl<T> IoBuf<T> {
pub fn new(size: usize) -> Self {
assert!(size != 0);
let layout = std::alloc::Layout::from_size_align(size, 4096).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) } as *mut T;
IoBuf {
ptr,
size,
mlocked: Cell::new(false),
}
}
pub fn is_mlocked(&self) -> bool {
self.mlocked.get()
}
pub fn mlock(&self) -> bool {
if self.mlocked.get() {
return true; }
let mlock_result = unsafe { libc::mlock(self.ptr as *const libc::c_void, self.size) };
if mlock_result == 0 {
self.mlocked.set(true);
true
} else {
false
}
}
pub fn munlock(&self) -> bool {
if !self.mlocked.get() {
return true; }
let munlock_result = unsafe { libc::munlock(self.ptr as *const libc::c_void, self.size) };
if munlock_result == 0 {
self.mlocked.set(false);
true
} else {
false
}
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
let elem_size = core::mem::size_of::<T>();
self.size / elem_size
}
pub fn as_ptr(&self) -> *const T {
self.ptr
}
pub fn as_mut_ptr(&self) -> *mut T {
self.ptr
}
pub fn zero_buf(&mut self) {
unsafe {
std::ptr::write_bytes(self.as_mut_ptr(), 0, self.len());
}
}
pub fn as_slice(&self) -> &[T] {
&*self
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut *self
}
pub fn subslice<R>(&self, range: R) -> &[T]
where
R: std::slice::SliceIndex<[T], Output = [T]>,
{
&self[range]
}
pub fn subslice_mut<R>(&mut self, range: R) -> &mut [T]
where
R: std::slice::SliceIndex<[T], Output = [T]>,
{
&mut self[range]
}
}
impl<T> std::fmt::Debug for IoBuf<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ptr {:?} size {} element type {}",
self.ptr,
self.size,
type_of_this(unsafe { &*self.ptr })
)
}
}
impl<T> Deref for IoBuf<T> {
type Target = [T];
fn deref(&self) -> &[T] {
let elem_size = core::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts(self.ptr, self.size / elem_size) }
}
}
impl<T> DerefMut for IoBuf<T> {
fn deref_mut(&mut self) -> &mut [T] {
let elem_size = core::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size / elem_size) }
}
}
impl<T> Drop for IoBuf<T> {
fn drop(&mut self) {
if self.mlocked.get() {
unsafe {
libc::munlock(self.ptr as *const libc::c_void, self.size);
}
}
let layout = std::alloc::Layout::from_size_align(self.size, 4096).unwrap();
unsafe { std::alloc::dealloc(self.ptr as *mut u8, layout) };
}
}
#[macro_export]
macro_rules! zero_io_buf {
($buffer:expr) => {{
unsafe {
std::ptr::write_bytes($buffer.as_mut_ptr(), 0, $buffer.len());
}
}};
}
#[cfg(test)]
mod tests {
use super::IoBuf;
#[test]
#[should_panic]
fn io_buf_rejects_zero_size() {
let _ = IoBuf::<u8>::new(0);
}
#[test]
fn io_buf_is_page_aligned_and_usable() {
let mut buf = IoBuf::<u8>::new(8192);
assert!(!buf.as_ptr().is_null());
assert_eq!(buf.as_ptr() as usize % 4096, 0);
assert_eq!(buf.len(), 8192);
buf.zero_buf();
let slice = buf.as_mut_slice();
slice[0] = 0xa5;
slice[8191] = 0x5a;
assert_eq!(buf.as_slice()[0], 0xa5);
assert_eq!(buf.as_slice()[8191], 0x5a);
assert_eq!(buf.as_slice()[4096], 0);
}
}