Results and polishing.

This commit is contained in:
Sergey Pepyakin 2019-07-03 14:00:02 +02:00
parent a0776876c1
commit 9f4cc26c02
3 changed files with 95 additions and 57 deletions

View File

@ -3,7 +3,7 @@
//! This implementation uses `mmap` on POSIX systems (and should use `VirtualAlloc` on windows).
//! There are possibilities to improve the performance for the reallocating case by reserving
//! memory up to maximum. This might be a problem for systems that don't have a lot of virtual
//! memory.
//! memory (i.e. 32-bit platforms).
use std::ptr::{self, NonNull};
use std::slice;
@ -20,9 +20,20 @@ struct Mmap {
}
impl Mmap {
fn new(len: usize) -> Self {
assert!(len < isize::max_value() as usize);
assert!(len > 0);
/// Create a new mmap mapping
///
/// Returns `Err` if:
/// - `len` should not exceed `isize::max_value()`
/// - `len` should be greater than 0.
/// - `mmap` returns an error (almost certainly means out of memory).
fn new(len: usize) -> Result<Self, &'static str> {
if len >= isize::max_value() as usize {
return Err("`len` should not exceed `isize::max_value()`");
}
if len == 0 {
return Err("`len` should be greater than 0");
}
let ptr_or_err = unsafe {
// Safety Proof:
// There are not specific safety proofs are required for this call, since the call
@ -48,15 +59,19 @@ impl Mmap {
};
match ptr_or_err as usize {
// `mmap` shouldn't return 0 since it has a special meaning.
x if x == 0 || x as isize == -1 => panic!(),
// `mmap` returns -1 in case of an error.
// `mmap` shouldn't return 0 since it has a special meaning for compilers.
//
// With the current parameters, the error can only be returned in case of insufficient
// memory.
x if x == 0 || x as isize == -1 => Err("mmap returned error"),
_ => {
let ptr = unsafe {
// Safety Proof:
// the ptr cannot be null as checked within the enclosing match.
NonNull::new_unchecked(ptr_or_err as *mut u8)
};
Self { ptr, len }
Ok(Self { ptr, len })
}
}
}
@ -108,19 +123,23 @@ pub struct ByteBuf {
}
impl ByteBuf {
pub fn new(len: usize) -> Self {
let mmap = if len == 0 { None } else { Some(Mmap::new(len)) };
Self { mmap }
pub fn new(len: usize) -> Result<Self, &'static str> {
let mmap = if len == 0 {
None
} else {
Some(Mmap::new(len)?)
};
Ok(Self { mmap })
}
pub fn realloc(&mut self, new_len: usize) {
pub fn realloc(&mut self, new_len: usize) -> Result<(), &'static str> {
let new_mmap = if new_len == 0 {
None
} else {
if self.len() == 0 {
Some(Mmap::new(new_len))
Some(Mmap::new(new_len)?)
} else {
let mut new_mmap = Mmap::new(new_len);
let mut new_mmap = Mmap::new(new_len)?;
{
let src = self.mmap.as_ref().unwrap().as_slice();
@ -131,8 +150,8 @@ impl ByteBuf {
Some(new_mmap)
}
};
self.mmap = new_mmap;
Ok(())
}
pub fn len(&self) -> usize {
@ -149,4 +168,23 @@ impl ByteBuf {
.map(|m| m.as_slice_mut())
.unwrap_or(&mut [])
}
pub fn erase(&mut self) -> Result<(), &'static str> {
let cur_len = match self.mmap {
// Nothing to do here...
None => return Ok(()),
Some(Mmap { len: cur_len, .. }) => cur_len,
};
// The order is important.
//
// 1. First we clear, and thus drop, the current mmap if any.
// 2. And then we create a new one.
//
// Otherwise we double the peak memory consumption.
self.mmap = None;
self.mmap = Some(Mmap::new(cur_len)?);
Ok(())
}
}

View File

@ -69,22 +69,16 @@ impl fmt::Debug for MemoryInstance {
}
}
#[cfg(all(unix, not(feature="vec_memory")))]
#[path="mmap_bytebuf.rs"]
#[cfg(all(unix, not(feature = "vec_memory")))]
#[path = "mmap_bytebuf.rs"]
mod bytebuf;
#[cfg(any(not(unix), feature="vec_memory"))]
#[path="vec_bytebuf.rs"]
#[cfg(any(not(unix), feature = "vec_memory"))]
#[path = "vec_bytebuf.rs"]
mod bytebuf;
use self::bytebuf::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,
@ -141,22 +135,24 @@ impl MemoryInstance {
validation::validate_memory(initial_u32, maximum_u32).map_err(Error::Memory)?;
}
let memory = MemoryInstance::new(initial, maximum);
let memory = MemoryInstance::new(initial, maximum)?;
Ok(MemoryRef(Rc::new(memory)))
}
/// Create new linear memory instance.
fn new(initial: Pages, maximum: Option<Pages>) -> Self {
fn new(initial: Pages, maximum: Option<Pages>) -> Result<Self, Error> {
let limits = ResizableLimits::new(initial.0 as u32, maximum.map(|p| p.0 as u32));
let initial_size: Bytes = initial.into();
MemoryInstance {
Ok(MemoryInstance {
limits: limits,
buffer: RefCell::new(ByteBuf::new(initial_size.0)),
buffer: RefCell::new(
ByteBuf::new(initial_size.0).map_err(|err| Error::Memory(err.to_string()))?,
),
initial: initial,
current_size: Cell::new(initial_size.0),
maximum: maximum,
}
})
}
/// Return linear memory limits.
@ -290,8 +286,12 @@ impl MemoryInstance {
}
let new_buffer_length: Bytes = new_size.into();
self.buffer
.borrow_mut()
.realloc(new_buffer_length.0)
.map_err(|err| Error::Memory(err.to_string()))?;
self.current_size.set(new_buffer_length.0);
self.buffer.borrow_mut().realloc(new_buffer_length.0);
Ok(size_before_grow)
}
@ -499,12 +499,14 @@ impl MemoryInstance {
self.clear(offset, 0, len)
}
/// Set every byte in the entire linear memory to 0.
/// Set every byte in the entire linear memory to 0, preserving its size.
///
/// Might be useful for some optimization shenanigans.
pub fn erase(&self) {
let cur_size = self.buffer.borrow().len();
*self.buffer.borrow_mut() = ByteBuf::new(cur_size);
pub fn erase(&self) -> Result<(), Error> {
self.buffer
.borrow_mut()
.erase()
.map_err(|err| Error::Memory(err.to_string()))
}
}
@ -518,30 +520,22 @@ mod tests {
#[test]
fn alloc() {
#[cfg(target_pointer_width = "64")]
let fixtures = &[
let mut fixtures = vec![
(0, None, true),
(0, Some(0), true),
(1, None, true),
(1, Some(1), true),
(0, Some(1), true),
(1, Some(0), false),
(0, Some(65536), true),
// TODO: Only use it for rust-alloc/mmap
// (65536, Some(65536), true),
// (65536, Some(0), false),
// (65536, None, true),
];
#[cfg(target_pointer_width = "32")]
let fixtures = &[
(0, None, true),
(0, Some(0), true),
(1, None, true),
(1, Some(1), true),
(0, Some(1), true),
(1, Some(0), false),
];
#[cfg(target_pointer_width = "64")]
fixtures.extend(&[
(65536, Some(65536), true),
(65536, Some(0), false),
(65536, None, true),
]);
for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() {
let initial: Pages = Pages(initial);
@ -563,7 +557,7 @@ mod tests {
}
fn create_memory(initial_content: &[u8]) -> MemoryInstance {
let mem = MemoryInstance::new(Pages(1), Some(Pages(1)));
let mem = MemoryInstance::new(Pages(1), Some(Pages(1))).unwrap();
mem.set(0, initial_content)
.expect("Successful initialize the memory");
mem
@ -676,7 +670,7 @@ mod tests {
#[test]
fn get_into() {
let mem = MemoryInstance::new(Pages(1), None);
let mem = MemoryInstance::new(Pages(1), None).unwrap();
mem.set(6, &[13, 17, 129])
.expect("memory set should not fail");

View File

@ -7,16 +7,15 @@ pub struct ByteBuf {
}
impl ByteBuf {
pub fn new(len: usize) -> Self {
pub fn new(len: usize) -> Result<Self, &'static str> {
let mut buf = Vec::new();
buf.resize(len, 0u8);
Self {
buf,
}
Ok(Self { buf })
}
pub fn realloc(&mut self, new_len: usize) {
pub fn realloc(&mut self, new_len: usize) -> Result<(), &'static str> {
self.buf.resize(new_len, 0u8);
Ok(())
}
pub fn len(&self) -> usize {
@ -30,4 +29,11 @@ impl ByteBuf {
pub fn as_slice_mut(&mut self) -> &mut [u8] {
self.buf.as_mut()
}
pub fn erase(&mut self) -> Result<(), &'static str> {
for v in &mut self.buf {
*v = 0;
}
Ok(())
}
}