From 97661cf3fe97ec6e801eedce0651b9e802e02a13 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 15 Jul 2019 22:17:25 +0300 Subject: [PATCH] CoW --- src/lib.rs | 2 +- src/memory/mmap_bytebuf.rs | 29 +++++++++++----- src/memory/mod.rs | 37 +++++++++++++------- src/memory/raw_bytebuf.rs | 71 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 src/memory/raw_bytebuf.rs diff --git a/src/lib.rs b/src/lib.rs index 610723f..d14e47c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -404,7 +404,7 @@ pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError}; pub use self::global::{GlobalInstance, GlobalRef}; pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs}; pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver}; -pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; +pub use self::memory::{MemoryBackend, MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef}; pub use self::runner::{StackRecycler, DEFAULT_CALL_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT}; pub use self::table::{TableInstance, TableRef}; diff --git a/src/memory/mmap_bytebuf.rs b/src/memory/mmap_bytebuf.rs index f9c120e..aa3a6b5 100644 --- a/src/memory/mmap_bytebuf.rs +++ b/src/memory/mmap_bytebuf.rs @@ -7,6 +7,7 @@ use std::ptr::{self, NonNull}; use std::slice; +use super::MemoryBackend; struct Mmap { /// The pointer that points to the start of the mapping. @@ -111,11 +112,15 @@ impl Drop for Mmap { } } -pub struct ByteBuf { +pub struct MmapByteBuf { mmap: Option, } -impl ByteBuf { +impl MmapByteBuf { + pub fn empty() -> Self { + MmapByteBuf { mmap: None } + } + pub fn new(len: usize) -> Result { let mmap = if len == 0 { None @@ -124,8 +129,14 @@ impl ByteBuf { }; Ok(Self { mmap }) } +} - pub fn realloc(&mut self, new_len: usize) -> Result<(), &'static str> { +impl MemoryBackend for MmapByteBuf { + fn alloc(&mut self, initial: usize, _maximum: usize) -> Result<(), &'static str> { + self.realloc(initial) + } + + fn realloc(&mut self, new_len: usize) -> Result<(), &'static str> { let new_mmap = if new_len == 0 { None } else { @@ -143,22 +154,22 @@ impl ByteBuf { Ok(()) } - pub fn len(&self) -> usize { + fn len(&self) -> usize { self.mmap.as_ref().map(|m| m.len).unwrap_or(0) } - pub fn as_slice(&self) -> &[u8] { + 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] { + 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<(), &'static str> { + fn erase(&mut self) -> Result<(), &'static str> { let len = self.len(); if len > 0 { // The order is important. @@ -176,14 +187,14 @@ impl ByteBuf { #[cfg(test)] mod tests { - use super::ByteBuf; + use super::{MmapByteBuf, MemoryBackend}; const PAGE_SIZE: usize = 4096; // This is not required since wasm memories can only grow but nice to have. #[test] fn byte_buf_shrink() { - let mut byte_buf = ByteBuf::new(PAGE_SIZE * 3).unwrap(); + let mut byte_buf = MmapByteBuf::new(PAGE_SIZE * 3).unwrap(); byte_buf.realloc(PAGE_SIZE * 2).unwrap(); } } diff --git a/src/memory/mod.rs b/src/memory/mod.rs index f438234..8d6ed56 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -11,14 +11,14 @@ use value::LittleEndianConvert; use Error; #[cfg(all(unix, not(feature = "vec_memory")))] -#[path = "mmap_bytebuf.rs"] -mod bytebuf; +#[path="mmap_bytebuf.rs"] +mod mmap_bytebuf; -#[cfg(any(not(unix), feature = "vec_memory"))] -#[path = "vec_bytebuf.rs"] -mod bytebuf; +#[cfg(all(unix, not(feature = "vec_memory")))] +use self::mmap_bytebuf::MmapByteBuf; -use self::bytebuf::ByteBuf; +// #[cfg(any(not(unix), feature = "vec_memory"))] +// mod bytebuf; /// Size of a page of [linear memory][`MemoryInstance`] - 64KiB. /// @@ -43,6 +43,15 @@ impl ::core::ops::Deref for MemoryRef { } } +pub trait MemoryBackend { + fn alloc(&mut self, initial: usize, maximum: usize) -> Result<(), &'static str>; + fn realloc(&mut self, new_len: usize) -> Result<(), &'static str>; + fn len(&self) -> usize; + fn as_slice(&self) -> &[u8]; + fn as_slice_mut(&mut self) -> &mut [u8]; + fn erase(&mut self) -> Result<(), &'static str>; +} + /// Runtime representation of a linear memory (or `memory` for short). /// /// A memory is a contiguous, mutable array of raw bytes. Wasm code can load and store values @@ -60,7 +69,7 @@ pub struct MemoryInstance { /// Memory limits. limits: ResizableLimits, /// Linear memory buffer with lazy allocation. - buffer: RefCell, + buffer: RefCell>, initial: Pages, current_size: Cell, maximum: Option, @@ -142,17 +151,21 @@ impl MemoryInstance { let limits = ResizableLimits::new(initial.0 as u32, maximum.map(|p| p.0 as u32)); let initial_size: Bytes = initial.into(); + let bytebuf = MmapByteBuf::new(initial_size.0).map_err(|err| Error::Memory(err.to_string()))?; + Ok(MemoryInstance { limits: limits, - buffer: RefCell::new( - ByteBuf::new(initial_size.0).map_err(|err| Error::Memory(err.to_string()))?, - ), + buffer: RefCell::new(Box::new(bytebuf)), initial: initial, current_size: Cell::new(initial_size.0), maximum: maximum, }) } + pub fn set_backend(&self, backend: Box) { + *self.buffer.borrow_mut() = backend; + } + /// Return linear memory limits. pub(crate) fn limits(&self) -> &ResizableLimits { &self.limits @@ -296,7 +309,7 @@ impl MemoryInstance { fn checked_region( &self, - buffer: &mut ByteBuf, + buffer: &mut Box, offset: usize, size: usize, ) -> Result { @@ -324,7 +337,7 @@ impl MemoryInstance { fn checked_region_pair( &self, - buffer: &mut ByteBuf, + buffer: &mut Box, offset1: usize, size1: usize, offset2: usize, diff --git a/src/memory/raw_bytebuf.rs b/src/memory/raw_bytebuf.rs new file mode 100644 index 0000000..adbde58 --- /dev/null +++ b/src/memory/raw_bytebuf.rs @@ -0,0 +1,71 @@ +//! An implementation of `ByteBuf` based on a plain `Vec`. + +use alloc::vec::Vec; +use std::{ + slice, + mem, +}; +use super::MemoryBackend; + +pub struct RawByteBuf { + ptr: *mut u8, + len: usize, + cap: usize, +} + +impl RawByteBuf { + pub fn from_raw_parts(ptr: *mut u8, len: usize, cap: usize) -> Self { + Self { + ptr, + len, + cap, + } + } + + pub fn new(len: usize) -> Result { + let mut v = vec![0u8; len]; + let cap = len; + let ptr = v.as_mut_ptr(); + mem::forget(v); + + Ok(Self { + ptr, + len, + cap, + }) + } +} + +impl MemoryBackend for RawByteBuf { + + pub fn realloc(&mut self, new_len: usize) -> Result<(), &'static str> { + if new_len > self.cap { + return Err("exceeds cap"); + } + self.len = new_len; + Ok(()) + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn as_slice(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self.ptr, self.len) + } + } + + pub fn as_slice_mut(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut(self.ptr, self.len) + } + } + + pub fn erase(&mut self) -> Result<(), &'static str> { + for v in self.as_slice_mut() { + *v = 0; + } + Ok(()) + } +}