use hrx::{Buffer, Result, Stream};
use std::{
alloc::{Layout, alloc_zeroed, dealloc},
ptr::NonNull,
};
pub(crate) struct HostBuffer {
buffer: Option<Buffer>,
pointer: NonNull<u8>,
layout: Layout,
leaked: bool,
}
unsafe impl Send for HostBuffer where Buffer: Send {}
impl HostBuffer {
pub(crate) fn new(stream: &Stream, bytes: usize) -> Result<Self> {
let page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
if page <= 0 {
return Err(crate::invalid("cannot determine host page size"));
}
let page = page as usize;
let size = bytes.max(1).div_ceil(page) * page;
let layout = Layout::from_size_align(size, page)
.map_err(|_| crate::invalid("invalid host allocation layout"))?;
let pointer = NonNull::new(unsafe { alloc_zeroed(layout) })
.ok_or_else(|| crate::invalid("host allocation failed"))?;
let imported = unsafe { stream.import_host(pointer.as_ptr().cast(), size) };
match imported {
Ok(buffer) => Ok(Self {
buffer: Some(buffer),
pointer,
layout,
leaked: false,
}),
Err(error) => {
unsafe {
dealloc(pointer.as_ptr(), layout);
}
Err(error)
}
}
}
pub(crate) fn buffer(&self) -> &Buffer {
self.buffer.as_ref().unwrap()
}
pub(crate) unsafe fn bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.pointer.as_ptr(), self.layout.size()) }
}
pub(crate) unsafe fn bytes_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.pointer.as_ptr(), self.layout.size()) }
}
pub(crate) fn abandon(&mut self) {
if let Some(buffer) = self.buffer.take() {
std::mem::forget(buffer);
}
self.leaked = true;
}
}
impl Drop for HostBuffer {
fn drop(&mut self) {
drop(self.buffer.take());
if !self.leaked {
unsafe {
dealloc(self.pointer.as_ptr(), self.layout);
}
}
}
}