From 9ed95e49c1fc0da5b4fc146bcccaeb5b758dc241 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 26 Jul 2018 13:50:05 +0200 Subject: [PATCH] Avoid allocations on memory operations (#112) --- src/memory.rs | 16 ++++++++++++++ src/runner.rs | 18 ++++++---------- src/value.rs | 60 ++++++++++++++++++++------------------------------- 3 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 339a445..5db7ed9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -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(&self, offset: u32) -> Result { + let buffer = self.buffer.borrow(); + let region = self.checked_region(&buffer, offset as usize, ::std::mem::size_of::())?; + 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(&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::())?.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. /// diff --git a/src/runner.rs b/src/runner.rs index f907673..5a9ec84 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -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::()) + 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::"); 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::()) + 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::"); 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::() - .into_little_endian(); + .pop_as::(); let raw_address = self .value_stack .pop_as::(); @@ -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::(); @@ -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) } diff --git a/src/value.rs b/src/value.rs index a9eef23..9be2d41 100644 --- a/src/value.rs +++ b/src/value.rs @@ -75,7 +75,7 @@ pub trait TransmuteInto { /// Convert from and to little endian. pub trait LittleEndianConvert where Self: Sized { /// Convert to little endian buffer. - fn into_little_endian(self) -> Vec; + fn into_little_endian(self, buffer: &mut[u8]); /// Convert from little endian buffer. fn from_little_endian(buffer: &[u8]) -> Result; } @@ -555,8 +555,8 @@ impl TransmuteInto for u64 { } impl LittleEndianConvert for i8 { - fn into_little_endian(self) -> Vec { - vec![self as u8] + fn into_little_endian(self, buffer: &mut[u8]) { + buffer[0] = self as u8; } fn from_little_endian(buffer: &[u8]) -> Result { @@ -567,8 +567,8 @@ impl LittleEndianConvert for i8 { } impl LittleEndianConvert for u8 { - fn into_little_endian(self) -> Vec { - vec![self] + fn into_little_endian(self, buffer: &mut[u8]) { + buffer[0] = self; } fn from_little_endian(buffer: &[u8]) -> Result { @@ -579,11 +579,9 @@ impl LittleEndianConvert for u8 { } impl LittleEndianConvert for i16 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(2); - vec.write_i16::(self) + fn into_little_endian(self, mut buffer: &mut[u8]) { + buffer.write_i16::(self) .expect("i16 is written without any errors"); - vec } fn from_little_endian(buffer: &[u8]) -> Result { @@ -593,11 +591,9 @@ impl LittleEndianConvert for i16 { } impl LittleEndianConvert for u16 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(2); - vec.write_u16::(self) + fn into_little_endian(self, mut buffer: &mut[u8]) { + buffer.write_u16::(self) .expect("u16 is written without any errors"); - vec } fn from_little_endian(buffer: &[u8]) -> Result { @@ -607,11 +603,9 @@ impl LittleEndianConvert for u16 { } impl LittleEndianConvert for i32 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(4); - vec.write_i32::(self) + fn into_little_endian(self, mut buffer: &mut[u8]) { + buffer.write_i32::(self) .expect("i32 is written without any errors"); - vec } fn from_little_endian(buffer: &[u8]) -> Result { @@ -621,11 +615,9 @@ impl LittleEndianConvert for i32 { } impl LittleEndianConvert for u32 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(4); - vec.write_u32::(self) + fn into_little_endian(self, mut buffer: &mut[u8]) { + buffer.write_u32::(self) .expect("u32 is written without any errors"); - vec } fn from_little_endian(buffer: &[u8]) -> Result { @@ -635,11 +627,9 @@ impl LittleEndianConvert for u32 { } impl LittleEndianConvert for i64 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(8); - vec.write_i64::(self) + fn into_little_endian(self, mut buffer: &mut[u8]) { + buffer.write_i64::(self) .expect("i64 is written without any errors"); - vec } fn from_little_endian(buffer: &[u8]) -> Result { @@ -649,11 +639,9 @@ impl LittleEndianConvert for i64 { } impl LittleEndianConvert for f32 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(4); - vec.write_f32::(self) + fn into_little_endian(self, mut buffer: &mut[u8]) { + buffer.write_f32::(self) .expect("f32 is written without any errors"); - vec } fn from_little_endian(buffer: &[u8]) -> Result { @@ -664,11 +652,9 @@ impl LittleEndianConvert for f32 { } impl LittleEndianConvert for f64 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(8); - vec.write_f64::(self) + fn into_little_endian(self, mut buffer: &mut[u8]) { + buffer.write_f64::(self) .expect("i64 is written without any errors"); - vec } fn from_little_endian(buffer: &[u8]) -> Result { @@ -679,8 +665,8 @@ impl LittleEndianConvert for f64 { } impl LittleEndianConvert for F32 { - fn into_little_endian(self) -> Vec { - (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 { @@ -689,8 +675,8 @@ impl LittleEndianConvert for F32 { } impl LittleEndianConvert for F64 { - fn into_little_endian(self) -> Vec { - (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 {