to save it

This commit is contained in:
vms
2021-04-20 23:39:16 +03:00
parent 2c40ced55d
commit 5d219e963b
18 changed files with 551 additions and 236 deletions

View File

@ -0,0 +1,29 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum MemoryAccessError {
#[error(
"Out-of-bound Wasm memory access: offset {offset}, size {size}, while memory_size {memory_size}"
)]
InvalidAccess {
offset: usize,
size: usize,
memory_size: usize,
},
}

View File

@ -0,0 +1,7 @@
pub mod error;
pub mod memory_reader;
pub mod memory_writer;
pub use fluence_it_types::IValue;
pub type MResult<T> = std::result::Result<T, error::MemoryAccessError>;

View File

@ -0,0 +1,213 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::error::MemoryAccessError;
use crate::IValue;
use crate::MResult;
use std::cell::Cell;
pub struct MemoryReader<'m> {
pub(self) memory: &'m [Cell<u8>],
}
/// Reads values of basic types sequentially from the provided reader.
/// It don't check memory limits for the optimization purposes,
/// so it could created by the MemoryReader::sequential_reader method.
pub struct SequentialReader<'r, 'm> {
reader: &'r MemoryReader<'m>,
offset: Cell<usize>,
}
macro_rules! value_der {
($self:expr, $offset:expr, @seq_start $($ids:tt),* @seq_end) => {
[$($self.reader.memory[$offset + $ids].get()),+]
};
($self:expr, $offset:expr, 1) => {
value_der!($self, $offset, @seq_start 0 @seq_end);
};
($self:expr, $offset:expr, 2) => {
value_der!($self, $offset, @seq_start 0, 1 @seq_end);
};
($self:expr, $offset:expr, 4) => {
value_der!($self, $offset, @seq_start 0, 1, 2, 3 @seq_end);
};
($self:expr, $offset:expr, 8) => {
value_der!($self, $offset, @seq_start 0, 1, 2, 3, 4, 5, 6, 7 @seq_end);
};
}
macro_rules! read_ty {
($func_name:ident, $ty:ty, 1) => {
pub fn $func_name(&self) -> $ty {
let offset = self.offset.get();
let result = <$ty>::from_le_bytes(value_der!(self, offset, 1));
self.offset.set(offset + 1);
result
}
};
($func_name:ident, $ty:ty, 2) => {
pub fn $func_name(&self) -> $ty {
let offset = self.offset.get();
let result = <$ty>::from_le_bytes(value_der!(self, offset, 2));
self.offset.set(offset + 2);
result
}
};
($func_name:ident, $ty:ty, 4) => {
pub fn $func_name(&self) -> $ty {
let offset = self.offset.get();
let result = <$ty>::from_le_bytes(value_der!(self, offset, 4));
self.offset.set(offset + 4);
result
}
};
($func_name:ident, $ty:ty, 8) => {
pub fn $func_name(&self) -> $ty {
let offset = self.offset.get();
let result = <$ty>::from_le_bytes(value_der!(self, offset, 8));
self.offset.set(offset + 8);
result
}
};
}
macro_rules! read_array_ty {
($func_name:ident, $ty:ident, $ity:ident) => {
pub fn $func_name(
&self,
offset: usize,
elements_count: usize,
) -> crate::MResult<Vec<crate::IValue>> {
let reader =
self.sequential_reader(offset, std::mem::size_of::<$ty>() * elements_count)?;
let mut result = Vec::with_capacity(elements_count);
for _ in 0..elements_count {
let value = paste::paste! { reader.[<read_ $ty>]()};
result.push(IValue::$ity(value));
}
Ok(result)
}
};
}
impl<'m> MemoryReader<'m> {
pub fn new(memory: &'m [Cell<u8>]) -> Self {
Self { memory }
}
/// Returns reader that allows read sequentially. It's important that memory limit is checked
/// only inside this function. All others functions of the returned reader don't have any
/// checks assuming that reader is well-formed.
pub fn sequential_reader(
&self,
offset: usize,
size: usize,
) -> MResult<SequentialReader<'_, '_>> {
self.check_access(offset, size)?;
Ok(SequentialReader::new(&self, offset))
}
pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> MResult<Vec<u8>> {
let reader = self.sequential_reader(offset, elements_count)?;
let mut result = Vec::with_capacity(elements_count);
for _ in 0..elements_count {
let value = reader.read_u8();
result.push(value);
}
Ok(result)
}
pub fn read_bool_array(&self, offset: usize, elements_count: usize) -> MResult<Vec<IValue>> {
let reader = self.sequential_reader(offset, elements_count)?;
let mut result = Vec::with_capacity(elements_count);
for _ in 0..elements_count {
let value = reader.read_u8();
result.push(IValue::Boolean(value != 0));
}
Ok(result)
}
pub fn check_access(&self, offset: usize, size: usize) -> MResult<()> {
let right = offset + size;
if right < offset || right >= self.memory.len() {
return Err(MemoryAccessError::InvalidAccess {
offset,
size,
memory_size: self.memory.len(),
});
}
Ok(())
}
read_array_ty!(read_u8_array, u8, U8);
read_array_ty!(read_s8_array, i8, S8);
read_array_ty!(read_u16_array, u16, U16);
read_array_ty!(read_s16_array, i16, S16);
read_array_ty!(read_u32_array, u32, U32);
read_array_ty!(read_s32_array, i32, S32);
read_array_ty!(read_i32_array, i32, I32);
read_array_ty!(read_f32_array, f32, F32);
read_array_ty!(read_u64_array, u64, U64);
read_array_ty!(read_s64_array, i64, S64);
read_array_ty!(read_i64_array, i64, I64);
read_array_ty!(read_f64_array, f64, F64);
}
impl<'r, 'm> SequentialReader<'r, 'm> {
pub(self) fn new(reader: &'r MemoryReader<'m>, offset: usize) -> Self {
let offset = Cell::new(offset);
Self { reader, offset }
}
pub fn read_bool(&self) -> bool {
let offset = self.offset.get();
let result = self.reader.memory[offset].get() != 0;
self.offset.set(offset + 1);
result
}
read_ty!(read_u8, u8, 1);
read_ty!(read_i8, i8, 1);
read_ty!(read_u16, u16, 2);
read_ty!(read_i16, i16, 2);
read_ty!(read_u32, u32, 4);
read_ty!(read_i32, i32, 4);
read_ty!(read_f32, f32, 4);
read_ty!(read_u64, u64, 8);
read_ty!(read_i64, i64, 8);
read_ty!(read_f64, f64, 8);
}

View File

@ -0,0 +1,149 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::MResult;
use crate::error::MemoryAccessError;
use std::cell::Cell;
pub struct MemoryWriter<'m> {
memory: &'m [Cell<u8>],
}
/// Writes values of basic types sequentially to the provided writer.
/// It don't check memory limits for the optimization purposes,
/// so it could created by the MemoryReader::sequential_reader method.
pub struct SequentialWriter<'w, 'm> {
writer: &'w MemoryWriter<'m>,
offset: Cell<usize>,
}
impl<'m> MemoryWriter<'m> {
pub fn new(memory: &'m [Cell<u8>]) -> Self {
Self { memory }
}
pub fn write_array<const N: usize>(&self, offset: usize, values: [u8; N]) -> MResult<()> {
self.check_access(offset, values.len())?;
self.memory[offset..offset + N]
.iter()
.zip(values.iter())
.for_each(|(cell, &byte)| cell.set(byte));
Ok(())
}
// specialization of write_array for u8
pub fn write_u8(&self, offset: usize, value: u8) -> MResult<()> {
self.check_access(offset, 1)?;
self.memory[offset].set(value);
Ok(())
}
// specialization of write_array for u32
pub fn write_u32(&self, offset: usize, value: u32) -> MResult<()> {
self.check_access(offset, 4)?;
let value = value.to_le_bytes();
self.memory[offset].set(value[0]);
self.memory[offset + 1].set(value[1]);
self.memory[offset + 2].set(value[2]);
self.memory[offset + 3].set(value[3]);
Ok(())
}
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) -> MResult<()> {
let writer = self.sequential_writer(offset, bytes.len())?;
writer.write_bytes(bytes);
Ok(())
}
pub fn sequential_writer(&self, offset: usize, size: usize) -> MResult<SequentialWriter<'_, '_>> {
self.check_access(offset, size)?;
Ok(SequentialWriter::new(&self, offset))
}
pub fn check_access(&self, offset: usize, size: usize) -> MResult<()> {
let right = offset + size;
if right < offset || right >= self.memory.len() {
return Err(MemoryAccessError::InvalidAccess {
offset,
size,
memory_size: self.memory.len(),
});
}
Ok(())
}
}
impl<'w, 'm> SequentialWriter<'w, 'm> {
pub(super) fn new(writer: &'w MemoryWriter<'m>, offset: usize) -> Self {
let offset = Cell::new(offset);
Self { writer, offset }
}
pub fn write_array<const N: usize>(&self, values: [u8; N]) {
let offset = self.offset.get();
self.writer.memory[offset..offset + N]
.iter()
.zip(values.iter())
.for_each(|(cell, &byte)| cell.set(byte));
self.offset.set(offset + N);
}
// specialization of write_array for u8
pub fn write_u8(&self, value: u8) {
let offset = self.offset.get();
self.writer.memory[offset].set(value);
self.offset.set(offset + 1);
}
// specialization of write_array for u32
pub fn write_u32(&self, value: u32) {
let offset = self.offset.get();
let value = value.to_le_bytes();
self.writer.memory[offset].set(value[0]);
self.writer.memory[offset + 1].set(value[1]);
self.writer.memory[offset + 2].set(value[2]);
self.writer.memory[offset + 3].set(value[3]);
self.offset.set(offset + 4);
}
#[allow(dead_code)]
pub fn write_bytes(&self, bytes: &[u8]) {
let offset = self.offset.get();
self.writer.memory[offset..offset + bytes.len()]
.iter()
.zip(bytes)
.for_each(|(cell, &byte)| cell.set(byte));
self.offset.set(offset + bytes.len());
}
}