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 parity_wasm::elements::ResizableLimits;
use Error; use Error;
use memory_units::{RoundUpTo, Pages, Bytes}; use memory_units::{RoundUpTo, Pages, Bytes};
use value::LittleEndianConvert;
/// Size of a page of [linear memory][`MemoryInstance`] - 64KiB. /// Size of a page of [linear memory][`MemoryInstance`] - 64KiB.
/// ///
@ -171,6 +172,13 @@ impl MemoryInstance {
Bytes(self.buffer.borrow().len()).round_up_to() 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. /// Copy data from memory at given offset.
/// ///
/// This will allocate vector for you. /// This will allocate vector for you.
@ -208,6 +216,14 @@ impl MemoryInstance {
Ok(()) 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. /// Increases the size of the linear memory by given number of pages.
/// Returns previous memory size if succeeds. /// Returns previous memory size if succeeds.
/// ///

View File

@ -1,4 +1,3 @@
use std::mem;
use std::ops; use std::ops;
use std::{u32, usize}; use std::{u32, usize};
use std::fmt; use std::fmt;
@ -615,10 +614,8 @@ impl Interpreter {
let m = context let m = context
.memory() .memory()
.expect("Due to validation memory should exists"); .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)?; .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())?; self.value_stack.push(n.into())?;
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
@ -636,10 +633,8 @@ impl Interpreter {
let m = context let m = context
.memory() .memory()
.expect("Due to validation memory should exists"); .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)?; .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(); let stack_value: U = v.extend_into();
self self
.value_stack .value_stack
@ -652,8 +647,7 @@ impl Interpreter {
where T: FromRuntimeValue, T: LittleEndianConvert { where T: FromRuntimeValue, T: LittleEndianConvert {
let stack_value = self let stack_value = self
.value_stack .value_stack
.pop_as::<T>() .pop_as::<T>();
.into_little_endian();
let raw_address = self let raw_address = self
.value_stack .value_stack
.pop_as::<u32>(); .pop_as::<u32>();
@ -666,7 +660,7 @@ impl Interpreter {
let m = context let m = context
.memory() .memory()
.expect("Due to validation memory should exists"); .expect("Due to validation memory should exists");
m.set(address, &stack_value) m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
@ -686,7 +680,7 @@ impl Interpreter {
.pop() .pop()
.try_into() .try_into()
.expect("Due to validation value should be of proper type"); .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 let raw_address = self
.value_stack .value_stack
.pop_as::<u32>(); .pop_as::<u32>();
@ -698,7 +692,7 @@ impl Interpreter {
let m = context let m = context
.memory() .memory()
.expect("Due to validation memory should exists"); .expect("Due to validation memory should exists");
m.set(address, &stack_value) m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }

View File

@ -75,7 +75,7 @@ pub trait TransmuteInto<T> {
/// Convert from and to little endian. /// Convert from and to little endian.
pub trait LittleEndianConvert where Self: Sized { pub trait LittleEndianConvert where Self: Sized {
/// Convert to little endian buffer. /// 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. /// Convert from little endian buffer.
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error>; fn from_little_endian(buffer: &[u8]) -> Result<Self, Error>;
} }
@ -555,8 +555,8 @@ impl TransmuteInto<i64> for u64 {
} }
impl LittleEndianConvert for i8 { impl LittleEndianConvert for i8 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, buffer: &mut[u8]) {
vec![self as u8] buffer[0] = self as u8;
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -567,8 +567,8 @@ impl LittleEndianConvert for i8 {
} }
impl LittleEndianConvert for u8 { impl LittleEndianConvert for u8 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, buffer: &mut[u8]) {
vec![self] buffer[0] = self;
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -579,11 +579,9 @@ impl LittleEndianConvert for u8 {
} }
impl LittleEndianConvert for i16 { impl LittleEndianConvert for i16 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, mut buffer: &mut[u8]) {
let mut vec = Vec::with_capacity(2); buffer.write_i16::<LittleEndian>(self)
vec.write_i16::<LittleEndian>(self)
.expect("i16 is written without any errors"); .expect("i16 is written without any errors");
vec
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -593,11 +591,9 @@ impl LittleEndianConvert for i16 {
} }
impl LittleEndianConvert for u16 { impl LittleEndianConvert for u16 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, mut buffer: &mut[u8]) {
let mut vec = Vec::with_capacity(2); buffer.write_u16::<LittleEndian>(self)
vec.write_u16::<LittleEndian>(self)
.expect("u16 is written without any errors"); .expect("u16 is written without any errors");
vec
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -607,11 +603,9 @@ impl LittleEndianConvert for u16 {
} }
impl LittleEndianConvert for i32 { impl LittleEndianConvert for i32 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, mut buffer: &mut[u8]) {
let mut vec = Vec::with_capacity(4); buffer.write_i32::<LittleEndian>(self)
vec.write_i32::<LittleEndian>(self)
.expect("i32 is written without any errors"); .expect("i32 is written without any errors");
vec
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -621,11 +615,9 @@ impl LittleEndianConvert for i32 {
} }
impl LittleEndianConvert for u32 { impl LittleEndianConvert for u32 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, mut buffer: &mut[u8]) {
let mut vec = Vec::with_capacity(4); buffer.write_u32::<LittleEndian>(self)
vec.write_u32::<LittleEndian>(self)
.expect("u32 is written without any errors"); .expect("u32 is written without any errors");
vec
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -635,11 +627,9 @@ impl LittleEndianConvert for u32 {
} }
impl LittleEndianConvert for i64 { impl LittleEndianConvert for i64 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, mut buffer: &mut[u8]) {
let mut vec = Vec::with_capacity(8); buffer.write_i64::<LittleEndian>(self)
vec.write_i64::<LittleEndian>(self)
.expect("i64 is written without any errors"); .expect("i64 is written without any errors");
vec
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -649,11 +639,9 @@ impl LittleEndianConvert for i64 {
} }
impl LittleEndianConvert for f32 { impl LittleEndianConvert for f32 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, mut buffer: &mut[u8]) {
let mut vec = Vec::with_capacity(4); buffer.write_f32::<LittleEndian>(self)
vec.write_f32::<LittleEndian>(self)
.expect("f32 is written without any errors"); .expect("f32 is written without any errors");
vec
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -664,11 +652,9 @@ impl LittleEndianConvert for f32 {
} }
impl LittleEndianConvert for f64 { impl LittleEndianConvert for f64 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, mut buffer: &mut[u8]) {
let mut vec = Vec::with_capacity(8); buffer.write_f64::<LittleEndian>(self)
vec.write_f64::<LittleEndian>(self)
.expect("i64 is written without any errors"); .expect("i64 is written without any errors");
vec
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -679,8 +665,8 @@ impl LittleEndianConvert for f64 {
} }
impl LittleEndianConvert for F32 { impl LittleEndianConvert for F32 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, buffer: &mut[u8]) {
(self.to_bits() as i32).into_little_endian() (self.to_bits() as i32).into_little_endian(buffer)
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
@ -689,8 +675,8 @@ impl LittleEndianConvert for F32 {
} }
impl LittleEndianConvert for F64 { impl LittleEndianConvert for F64 {
fn into_little_endian(self) -> Vec<u8> { fn into_little_endian(self, buffer: &mut[u8]) {
(self.to_bits() as i64).into_little_endian() (self.to_bits() as i64).into_little_endian(buffer)
} }
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> { fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {