Avoid allocations on memory operations (#112)

This commit is contained in:
Arkadiy Paronyan 2018-07-26 13:50:05 +02:00 committed by Sergey Pepyakin
parent a605175abe
commit 9ed95e49c1
3 changed files with 45 additions and 49 deletions

View File

@ -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.
///
@ -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.
@ -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.
///

View File

@ -1,4 +1,3 @@
use std::mem;
use std::ops;
use std::{u32, usize};
use std::fmt;
@ -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)
}
@ -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
@ -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>();
@ -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)
}
@ -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>();
@ -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)
}

View File

@ -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>;
}
@ -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> {
@ -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> {
@ -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> {
@ -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> {
@ -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> {
@ -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> {
@ -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> {
@ -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> {
@ -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> {
@ -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> {
@ -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> {