Skip to content

Commit

Permalink
Avoid allocations on memory operations (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
arkpar authored and pepyakin committed Jul 26, 2018
1 parent a605175 commit 9ed95e4
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 49 deletions.
16 changes: 16 additions & 0 deletions src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::cell::RefCell;
use parity_wasm::elements::ResizableLimits;
use Error;
use memory_units::{RoundUpTo, Pages, Bytes};
use value::LittleEndianConvert;

/// Size of a page of [linear memory][`MemoryInstance`] - 64KiB.
///
Expand Down Expand Up @@ -171,6 +172,13 @@ impl MemoryInstance {
Bytes(self.buffer.borrow().len()).round_up_to()
}

/// Get value from memory at given offset.
pub fn get_value<T: LittleEndianConvert>(&self, offset: u32) -> Result<T, Error> {
let buffer = self.buffer.borrow();
let region = self.checked_region(&buffer, offset as usize, ::std::mem::size_of::<T>())?;
Ok(T::from_little_endian(region.slice()).expect("Slice size is checked"))
}

/// Copy data from memory at given offset.
///
/// This will allocate vector for you.
Expand Down Expand Up @@ -208,6 +216,14 @@ impl MemoryInstance {
Ok(())
}

/// Copy value in the memory at given offset.
pub fn set_value<T: LittleEndianConvert>(&self, offset: u32, value: T) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut();
let range = self.checked_region(&buffer, offset as usize, ::std::mem::size_of::<T>())?.range();
value.into_little_endian(&mut buffer[range]);
Ok(())
}

/// Increases the size of the linear memory by given number of pages.
/// Returns previous memory size if succeeds.
///
Expand Down
18 changes: 6 additions & 12 deletions src/runner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::mem;
use std::ops;
use std::{u32, usize};
use std::fmt;
Expand Down Expand Up @@ -615,10 +614,8 @@ impl Interpreter {
let m = context
.memory()
.expect("Due to validation memory should exists");
let b = m.get(address, mem::size_of::<T>())
let n: T = m.get_value(address)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
let n = T::from_little_endian(&b)
.expect("Can't fail since buffer length should be size_of::<T>");
self.value_stack.push(n.into())?;
Ok(InstructionOutcome::RunNextInstruction)
}
Expand All @@ -636,10 +633,8 @@ impl Interpreter {
let m = context
.memory()
.expect("Due to validation memory should exists");
let b = m.get(address, mem::size_of::<T>())
let v: T = m.get_value(address)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
let v = T::from_little_endian(&b)
.expect("Can't fail since buffer length should be size_of::<T>");
let stack_value: U = v.extend_into();
self
.value_stack
Expand All @@ -652,8 +647,7 @@ impl Interpreter {
where T: FromRuntimeValue, T: LittleEndianConvert {
let stack_value = self
.value_stack
.pop_as::<T>()
.into_little_endian();
.pop_as::<T>();
let raw_address = self
.value_stack
.pop_as::<u32>();
Expand All @@ -666,7 +660,7 @@ impl Interpreter {
let m = context
.memory()
.expect("Due to validation memory should exists");
m.set(address, &stack_value)
m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction)
}
Expand All @@ -686,7 +680,7 @@ impl Interpreter {
.pop()
.try_into()
.expect("Due to validation value should be of proper type");
let stack_value = stack_value.wrap_into().into_little_endian();
let stack_value = stack_value.wrap_into();
let raw_address = self
.value_stack
.pop_as::<u32>();
Expand All @@ -698,7 +692,7 @@ impl Interpreter {
let m = context
.memory()
.expect("Due to validation memory should exists");
m.set(address, &stack_value)
m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction)
}
Expand Down
60 changes: 23 additions & 37 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub trait TransmuteInto<T> {
/// Convert from and to little endian.
pub trait LittleEndianConvert where Self: Sized {
/// Convert to little endian buffer.
fn into_little_endian(self) -> Vec<u8>;
fn into_little_endian(self, buffer: &mut[u8]);
/// Convert from little endian buffer.
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error>;
}
Expand Down Expand Up @@ -555,8 +555,8 @@ impl TransmuteInto<i64> for u64 {
}

impl LittleEndianConvert for i8 {
fn into_little_endian(self) -> Vec<u8> {
vec![self as u8]
fn into_little_endian(self, buffer: &mut[u8]) {
buffer[0] = self as u8;
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -567,8 +567,8 @@ impl LittleEndianConvert for i8 {
}

impl LittleEndianConvert for u8 {
fn into_little_endian(self) -> Vec<u8> {
vec![self]
fn into_little_endian(self, buffer: &mut[u8]) {
buffer[0] = self;
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -579,11 +579,9 @@ impl LittleEndianConvert for u8 {
}

impl LittleEndianConvert for i16 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(2);
vec.write_i16::<LittleEndian>(self)
fn into_little_endian(self, mut buffer: &mut[u8]) {
buffer.write_i16::<LittleEndian>(self)
.expect("i16 is written without any errors");
vec
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -593,11 +591,9 @@ impl LittleEndianConvert for i16 {
}

impl LittleEndianConvert for u16 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(2);
vec.write_u16::<LittleEndian>(self)
fn into_little_endian(self, mut buffer: &mut[u8]) {
buffer.write_u16::<LittleEndian>(self)
.expect("u16 is written without any errors");
vec
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -607,11 +603,9 @@ impl LittleEndianConvert for u16 {
}

impl LittleEndianConvert for i32 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(4);
vec.write_i32::<LittleEndian>(self)
fn into_little_endian(self, mut buffer: &mut[u8]) {
buffer.write_i32::<LittleEndian>(self)
.expect("i32 is written without any errors");
vec
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -621,11 +615,9 @@ impl LittleEndianConvert for i32 {
}

impl LittleEndianConvert for u32 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(4);
vec.write_u32::<LittleEndian>(self)
fn into_little_endian(self, mut buffer: &mut[u8]) {
buffer.write_u32::<LittleEndian>(self)
.expect("u32 is written without any errors");
vec
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -635,11 +627,9 @@ impl LittleEndianConvert for u32 {
}

impl LittleEndianConvert for i64 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(8);
vec.write_i64::<LittleEndian>(self)
fn into_little_endian(self, mut buffer: &mut[u8]) {
buffer.write_i64::<LittleEndian>(self)
.expect("i64 is written without any errors");
vec
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -649,11 +639,9 @@ impl LittleEndianConvert for i64 {
}

impl LittleEndianConvert for f32 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(4);
vec.write_f32::<LittleEndian>(self)
fn into_little_endian(self, mut buffer: &mut[u8]) {
buffer.write_f32::<LittleEndian>(self)
.expect("f32 is written without any errors");
vec
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -664,11 +652,9 @@ impl LittleEndianConvert for f32 {
}

impl LittleEndianConvert for f64 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(8);
vec.write_f64::<LittleEndian>(self)
fn into_little_endian(self, mut buffer: &mut[u8]) {
buffer.write_f64::<LittleEndian>(self)
.expect("i64 is written without any errors");
vec
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -679,8 +665,8 @@ impl LittleEndianConvert for f64 {
}

impl LittleEndianConvert for F32 {
fn into_little_endian(self) -> Vec<u8> {
(self.to_bits() as i32).into_little_endian()
fn into_little_endian(self, buffer: &mut[u8]) {
(self.to_bits() as i32).into_little_endian(buffer)
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand All @@ -689,8 +675,8 @@ impl LittleEndianConvert for F32 {
}

impl LittleEndianConvert for F64 {
fn into_little_endian(self) -> Vec<u8> {
(self.to_bits() as i64).into_little_endian()
fn into_little_endian(self, buffer: &mut[u8]) {
(self.to_bits() as i64).into_little_endian(buffer)
}

fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
Expand Down

0 comments on commit 9ed95e4

Please sign in to comment.