use std::ptr::{self, NonNull};
use std::slice;
struct Mmap {
ptr: NonNull<u8>,
len: usize,
}
impl Mmap {
fn new(len: usize) -> Result<Self, String> {
if len > isize::max_value() as usize {
return Err("`len` should not exceed `isize::max_value()`".into());
}
if len == 0 {
return Err("`len` should be greater than 0".into());
}
let ptr_or_err = unsafe {
libc::mmap(
ptr::null_mut(),
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_ANON | libc::MAP_PRIVATE,
-1,
0,
)
};
match ptr_or_err {
#[cfg(feature = "errno")]
libc::MAP_FAILED => {
let errno = errno::errno();
Err(format!("mmap returned an error ({}): {}", errno.0, errno))
}
#[cfg(not(feature = "errno"))]
libc::MAP_FAILED => Err("mmap returned an error".into()),
_ => {
let ptr = NonNull::new(ptr_or_err as *mut u8)
.ok_or_else(|| "mmap returned 0".to_string())?;
Ok(Self { ptr, len })
}
}
}
fn as_slice(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self.ptr.as_ptr(), self.len)
}
}
fn as_slice_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len)
}
}
}
impl Drop for Mmap {
fn drop(&mut self) {
let ret_val = unsafe {
libc::munmap(self.ptr.as_ptr() as *mut libc::c_void, self.len)
};
assert_eq!(ret_val, 0, "munmap failed");
}
}
pub struct ByteBuf {
mmap: Option<Mmap>,
}
impl ByteBuf {
pub fn new(len: usize) -> Result<Self, String> {
let mmap = if len == 0 {
None
} else {
Some(Mmap::new(len)?)
};
Ok(Self { mmap })
}
pub fn realloc(&mut self, new_len: usize) -> Result<(), String> {
let new_mmap = if new_len == 0 {
None
} else {
let mut new_mmap = Mmap::new(new_len)?;
if let Some(cur_mmap) = self.mmap.take() {
let src = cur_mmap.as_slice();
let dst = new_mmap.as_slice_mut();
let amount = src.len().min(dst.len());
dst[..amount].copy_from_slice(&src[..amount]);
}
Some(new_mmap)
};
self.mmap = new_mmap;
Ok(())
}
pub fn len(&self) -> usize {
self.mmap.as_ref().map(|m| m.len).unwrap_or(0)
}
pub fn as_slice(&self) -> &[u8] {
self.mmap.as_ref().map(|m| m.as_slice()).unwrap_or(&[])
}
pub fn as_slice_mut(&mut self) -> &mut [u8] {
self.mmap
.as_mut()
.map(|m| m.as_slice_mut())
.unwrap_or(&mut [])
}
pub fn erase(&mut self) -> Result<(), String> {
let len = self.len();
if len > 0 {
self.mmap = None;
self.mmap = Some(Mmap::new(len)?);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::ByteBuf;
const PAGE_SIZE: usize = 4096;
#[test]
fn byte_buf_shrink() {
let mut byte_buf = ByteBuf::new(PAGE_SIZE * 3).unwrap();
byte_buf.realloc(PAGE_SIZE * 2).unwrap();
}
}