diff --git a/Cargo.toml b/Cargo.toml index a75cfbb..a28469c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ memory_units = "0.3.0" libm = { version = "0.1.2", optional = true } num-rational = "0.2.2" num-traits = "0.2.8" +libc = "0.2.58" [dev-dependencies] assert_matches = "1.1" diff --git a/src/memory/mmap.rs b/src/memory/mmap.rs new file mode 100644 index 0000000..56b140f --- /dev/null +++ b/src/memory/mmap.rs @@ -0,0 +1,94 @@ +//! An implementation of a `ByteBuf` based on virtual memory. +//! +//! This implementation uses `mmap` on POSIX systems (and should use `VirtualAlloc` on windows). + +use std::ptr::{self, NonNull}; +use std::slice; + +struct Mmap { + ptr: NonNull, + len: usize, +} + +impl Mmap { + fn new(len: usize) -> Self { + assert!(len > 0); + unsafe { + let ptr = libc::mmap( + ptr::null_mut(), + len, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, + 0, + );; + assert!(ptr as isize != -1); + Self { + ptr: NonNull::new(ptr as *mut u8).unwrap(), + len, + } + } + } + + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + } + + pub 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 r = unsafe { libc::munmap(self.ptr.as_ptr() as *mut libc::c_void, self.len) }; + assert_eq!(r, 0, "munmap failed"); + } +} + +pub struct ByteBuf { + mmap: Option, +} + +impl ByteBuf { + pub fn new(len: usize) -> Self { + let mmap = if len == 0 { None } else { Some(Mmap::new(len)) }; + + Self { mmap } + } + + pub fn realloc(&mut self, new_len: usize) { + let new_mmap = if new_len == 0 { + None + } else { + if self.len() == 0 { + Some(Mmap::new(new_len)) + } else { + let mut new_mmap = Mmap::new(new_len); + + unsafe { + let src = self.mmap.as_ref().unwrap().as_slice(); + let dst = new_mmap.as_slice_mut(); + + ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len()); + } + + Some(new_mmap) + } + }; + + self.mmap = new_mmap; + } + + 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 []) + } +} diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 50c2e44..ddbb9c3 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -69,13 +69,15 @@ impl fmt::Debug for MemoryInstance { } } -mod rust_alloc as byte_buf; -use self::rust_alloc::ByteBuf; +mod mmap; +use self::mmap::ByteBuf; + +// mod rust_alloc as byte_buf; +// use self::rust_alloc::ByteBuf; // mod vec_backed; // use self::vec_backed::ByteBuf; - struct CheckedRegion { offset: usize, size: usize, @@ -196,7 +198,10 @@ impl MemoryInstance { let mut buffer = self.buffer.borrow_mut(); let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::())?; - Ok(T::from_little_endian(&buffer.as_slice_mut()[region.range()]).expect("Slice size is checked")) + Ok( + T::from_little_endian(&buffer.as_slice_mut()[region.range()]) + .expect("Slice size is checked"), + ) } /// Copy data from memory at given offset. @@ -289,8 +294,7 @@ impl MemoryInstance { buffer: &mut ByteBuf, offset: usize, size: usize, - ) -> Result - { + ) -> Result { let end = offset.checked_add(size).ok_or_else(|| { Error::Memory(format!( "trying to access memory block of size {} from offset {}",