refactoring

This commit is contained in:
vms
2021-04-24 21:55:14 +03:00
parent 6116c5c252
commit cc44e387f7
24 changed files with 704 additions and 450 deletions

View File

@ -14,90 +14,23 @@
* limitations under the License.
*/
pub mod error;
mod macros;
pub mod memory_reader;
pub mod memory_writer;
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
#![warn(rust_2018_idioms)]
pub mod lifter;
pub mod lowerer;
pub mod traits;
pub mod utils;
pub use fluence_it_types::ne_vec::NEVec;
pub use fluence_it_types::IRecordType;
pub use fluence_it_types::IType;
pub use fluence_it_types::IValue;
pub type ReadResult<T> = std::result::Result<T, error::MemoryAccessError>;
pub type WriteResult<T> = std::result::Result<T, error::MemoryWriteError>;
/// Size of a value in a serialized view.
pub fn ser_type_size(ty: &IType) -> usize {
const WASM_POINTER_SIZE: usize = 4;
match ty {
IType::Boolean | IType::S8 | IType::U8 => 1,
IType::S16 | IType::U16 => 2,
IType::S32 | IType::U32 | IType::I32 | IType::F32 => 4,
IType::Record(_) => 4,
// Vec-like types are passed by pointer and size
IType::String | IType::ByteArray | IType::Array(_) => 2 * WASM_POINTER_SIZE,
IType::S64 | IType::U64 | IType::I64 | IType::F64 => 8,
}
}
/// Size of a value in a serialized view.
pub fn ser_value_size(value: &IValue) -> u32 {
match value {
IValue::Boolean(_) | IValue::S8(_) | IValue::U8(_) => 1,
IValue::S16(_) | IValue::U16(_) => 2,
IValue::S32(_) | IValue::U32(_) | IValue::F32(_) | IValue::I32(_) => 4,
IValue::S64(_) | IValue::U64(_) | IValue::F64(_) | IValue::I64(_) => 8,
IValue::String(_) | IValue::ByteArray(_) | IValue::Array(_) => 2 * 4,
IValue::Record(_) => 4,
}
}
/// Returns the record size in bytes.
pub fn record_size(record_type: &IRecordType) -> usize {
record_type
.fields
.iter()
.map(|f| ser_type_size(&f.ty))
.sum()
}
pub fn type_tag_form_itype(itype: &IType) -> u32 {
const POINTER_CODE: u32 = 3; // u32 in the sdk
match itype {
IType::Boolean => 0, // u8
IType::U8 => 1, // u8
IType::U16 => 2, // u16
IType::U32 => 3, // u32
IType::U64 => 4, // u64
IType::S8 => 6, // i8
IType::S16 => 7, // i16
IType::S32 | IType::I32 => 8, // i32
IType::S64 | IType::I64 => 9, // i64
IType::F32 => 10, // f32
IType::F64 => 11, // f64
IType::ByteArray | IType::Array(_) | IType::Record(_) | IType::String => POINTER_CODE,
}
}
pub fn type_tag_form_ivalue(itype: &IValue) -> u32 {
const POINTER_CODE: u32 = 3; // u32 in the sdk
match itype {
IValue::Boolean(_) => 0, // u8
IValue::U8(_) => 1, // u8
IValue::U16(_) => 2, // u16
IValue::U32(_) => 3, // u32
IValue::U64(_) => 4, // u64
IValue::S8(_) => 6, // i8
IValue::S16(_) => 7, // i16
IValue::S32(_) | IValue::I32(_) => 8, // i32
IValue::S64(_) | IValue::I64(_) => 9, // i64
IValue::F32(_) => 10, // f32
IValue::F64(_) => 11, // f64
IValue::ByteArray(_) | IValue::Array(_) | IValue::Record(_) | IValue::String(_) => {
POINTER_CODE
}
}
}

View File

@ -0,0 +1,40 @@
/*
* 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::traits::RecordResolvableError;
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum LiError {
#[error(
"Out-of-bound Wasm memory access: offset {offset}, size {size}, while memory_size {memory_size}"
)]
InvalidAccess {
offset: usize,
size: usize,
memory_size: usize,
},
#[error("{0}")]
RecordResolvableError(#[from] RecordResolvableError),
#[error("{0}")]
InvalidUTF8String(#[from] std::string::FromUtf8Error),
/// This error occurred when a record is created from empty values array.
#[error("Record with name '{0}' can't be empty")]
EmptyRecord(String),
}

View File

@ -0,0 +1,126 @@
/*
* 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 super::record_lift_memory;
use super::ILifter;
use super::LiResult;
use crate::traits::RecordResolvable;
use crate::utils::ser_type_size;
use crate::IType;
use crate::IValue;
pub fn array_lift_memory<R: RecordResolvable>(
lifter: &ILifter<'_, '_, R>,
value_type: &IType,
offset: usize,
elements_count: usize,
) -> LiResult<IValue> {
if elements_count == 0 {
return Ok(IValue::Array(vec![]));
}
let reader = &lifter.reader;
let ivalues = match value_type {
IType::Boolean => reader.read_bool_array(offset, elements_count)?,
IType::S8 => reader.read_s8_array(offset, elements_count)?,
IType::S16 => reader.read_s16_array(offset, elements_count)?,
IType::S32 => reader.read_s32_array(offset, elements_count)?,
IType::S64 => reader.read_s64_array(offset, elements_count)?,
IType::I32 => reader.read_i32_array(offset, elements_count)?,
IType::I64 => reader.read_i64_array(offset, elements_count)?,
IType::U8 => reader.read_u8_array(offset, elements_count)?,
IType::U16 => reader.read_u16_array(offset, elements_count)?,
IType::U32 => reader.read_u32_array(offset, elements_count)?,
IType::U64 => reader.read_u64_array(offset, elements_count)?,
IType::F32 => reader.read_f32_array(offset, elements_count)?,
IType::F64 => reader.read_f64_array(offset, elements_count)?,
IType::String => read_string_array(lifter, offset, elements_count)?,
IType::ByteArray => read_array_array(lifter, &IType::ByteArray, offset, elements_count)?,
IType::Array(ty) => read_array_array(lifter, &ty, offset, elements_count)?,
IType::Record(record_type_id) => {
read_record_array(lifter, *record_type_id, offset, elements_count)?
}
};
Ok(IValue::Array(ivalues))
}
fn read_string_array<R: RecordResolvable>(
lifter: &ILifter<'_, '_, R>,
offset: usize,
elements_count: usize,
) -> LiResult<Vec<IValue>> {
let mut result = Vec::with_capacity(elements_count);
let seq_reader = lifter
.reader
.sequential_reader(offset, ser_type_size(&IType::String) * elements_count)?;
for _ in 0..elements_count {
let offset = seq_reader.read_u32();
let size = seq_reader.read_u32();
let raw_str = lifter.reader.read_raw_u8_array(offset as _, size as _)?;
let str = String::from_utf8(raw_str)?;
result.push(IValue::String(str));
}
Ok(result)
}
fn read_array_array<R: RecordResolvable>(
lifter: &ILifter<'_, '_, R>,
ty: &IType,
offset: usize,
elements_count: usize,
) -> LiResult<Vec<IValue>> {
let mut result = Vec::with_capacity(elements_count);
let seq_reader = lifter
.reader
.sequential_reader(offset, ser_type_size(ty) * elements_count)?;
for _ in 0..elements_count {
let offset = seq_reader.read_u32();
let size = seq_reader.read_u32();
let array = array_lift_memory(lifter, ty, offset as _, size as _)?;
result.push(array);
}
Ok(result)
}
fn read_record_array<R: RecordResolvable>(
lifter: &ILifter<'_, '_, R>,
record_type_id: u64,
offset: usize,
elements_count: usize,
) -> LiResult<Vec<IValue>> {
let mut result = Vec::with_capacity(elements_count);
let seq_reader = lifter
.reader
.sequential_reader(offset, ser_type_size(&IType::Record(0)) * elements_count)?;
for _ in 0..elements_count {
let offset = seq_reader.read_u32();
let record_ty = lifter.resolver.resolve_record(record_type_id)?;
let record = record_lift_memory(lifter, &record_ty, offset as _)?;
result.push(record);
}
Ok(result)
}

View File

@ -0,0 +1,116 @@
/*
* 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 super::ILifter;
use super::LiError;
use super::LiResult;
use super::MemoryReader;
use super::SequentialReader;
use crate::traits::RecordResolvable;
use crate::utils::record_size;
use crate::IRecordType;
use crate::IType;
use crate::IValue;
use crate::NEVec;
pub fn record_lift_memory<R: RecordResolvable>(
lifter: &ILifter<'_, '_, R>,
record_type: &IRecordType,
offset: usize,
) -> LiResult<IValue> {
let mut values = Vec::with_capacity(record_type.fields.len());
let size = record_size(record_type);
let reader = &lifter.reader;
let seq_reader = reader.sequential_reader(offset, size)?;
for field in (*record_type.fields).iter() {
match &field.ty {
IType::Boolean => values.push(IValue::Boolean(seq_reader.read_u8() != 0)),
IType::S8 => values.push(IValue::S8(seq_reader.read_i8())),
IType::S16 => values.push(IValue::S16(seq_reader.read_i16())),
IType::S32 => values.push(IValue::S32(seq_reader.read_i32())),
IType::S64 => values.push(IValue::S64(seq_reader.read_i64())),
IType::I32 => values.push(IValue::I32(seq_reader.read_i32())),
IType::I64 => values.push(IValue::I64(seq_reader.read_i64())),
IType::U8 => values.push(IValue::U8(seq_reader.read_u8())),
IType::U16 => values.push(IValue::U16(seq_reader.read_u16())),
IType::U32 => values.push(IValue::U32(seq_reader.read_u32())),
IType::U64 => values.push(IValue::U64(seq_reader.read_u64())),
IType::F32 => values.push(IValue::F32(seq_reader.read_f32())),
IType::F64 => values.push(IValue::F64(seq_reader.read_f64())),
IType::String => values.push(IValue::String(read_string(reader, &seq_reader)?)),
IType::ByteArray => values.push(read_byte_array(reader, &seq_reader)?),
IType::Array(ty) => values.push(read_array(&lifter, &seq_reader, &**ty)?),
IType::Record(record_type_id) => {
values.push(read_record(lifter, &seq_reader, *record_type_id)?)
}
}
}
let record = NEVec::new(values.into_iter().collect())
.map_err(|_| LiError::EmptyRecord(record_type.name.clone()))?;
Ok(IValue::Record(record))
}
fn read_string(
reader: &MemoryReader<'_>,
seq_reader: &SequentialReader<'_, '_>,
) -> LiResult<String> {
let offset = seq_reader.read_u32();
let size = seq_reader.read_u32();
let string_mem = reader.read_raw_u8_array(offset as _, size as _)?;
let string = String::from_utf8(string_mem)?;
Ok(string)
}
fn read_byte_array(
reader: &MemoryReader<'_>,
seq_reader: &SequentialReader<'_, '_>,
) -> LiResult<IValue> {
let offset = seq_reader.read_u32();
let size = seq_reader.read_u32();
let array = reader.read_raw_u8_array(offset as _, size as _)?;
Ok(IValue::ByteArray(array))
}
fn read_array<R: RecordResolvable>(
lifter: &ILifter<'_, '_, R>,
seq_reader: &SequentialReader<'_, '_>,
value_type: &IType,
) -> LiResult<IValue> {
let offset = seq_reader.read_u32();
let size = seq_reader.read_u32();
super::array_lift_memory(lifter, value_type, offset as _, size as _)
}
fn read_record<R: RecordResolvable>(
lifter: &ILifter<'_, '_, R>,
seq_reader: &SequentialReader<'_, '_>,
record_type_id: u64,
) -> LiResult<IValue> {
let offset = seq_reader.read_u32();
let record_type = lifter.resolver.resolve_record(record_type_id)?;
record_lift_memory(lifter, &record_type, offset as _)
}

View File

@ -101,7 +101,7 @@ macro_rules! read_array_ty {
&self,
offset: usize,
elements_count: usize,
) -> crate::ReadResult<Vec<crate::IValue>> {
) -> super::LiResult<Vec<crate::IValue>> {
let reader =
self.sequential_reader(offset, std::mem::size_of::<$ty>() * elements_count)?;
let mut result = Vec::with_capacity(elements_count);

View File

@ -14,11 +14,11 @@
* limitations under the License.
*/
use crate::error::MemoryAccessError;
use super::LiError;
use super::LiResult;
use crate::read_array_ty;
use crate::read_ty;
use crate::IValue;
use crate::ReadResult;
use std::cell::Cell;
@ -46,13 +46,13 @@ impl<'m> MemoryReader<'m> {
&self,
offset: usize,
size: usize,
) -> ReadResult<SequentialReader<'_, '_>> {
) -> LiResult<SequentialReader<'_, '_>> {
self.check_access(offset, size)?;
Ok(SequentialReader::new(&self, offset))
}
pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> ReadResult<Vec<u8>> {
pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> LiResult<Vec<u8>> {
let reader = self.sequential_reader(offset, elements_count)?;
let mut result = Vec::with_capacity(elements_count);
@ -64,7 +64,7 @@ impl<'m> MemoryReader<'m> {
Ok(result)
}
pub fn read_bool_array(&self, offset: usize, elements_count: usize) -> ReadResult<Vec<IValue>> {
pub fn read_bool_array(&self, offset: usize, elements_count: usize) -> LiResult<Vec<IValue>> {
let reader = self.sequential_reader(offset, elements_count)?;
let mut result = Vec::with_capacity(elements_count);
@ -76,12 +76,12 @@ impl<'m> MemoryReader<'m> {
Ok(result)
}
pub fn check_access(&self, offset: usize, size: usize) -> ReadResult<()> {
pub fn check_access(&self, offset: usize, size: usize) -> LiResult<()> {
let right = offset + size;
// the first condition is a check for overflow
if right < offset || right >= self.memory.len() {
return Err(MemoryAccessError::InvalidAccess {
return Err(LiError::InvalidAccess {
offset,
size,
memory_size: self.memory.len(),

View File

@ -0,0 +1,45 @@
/*
* 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.
*/
mod error;
mod lift_array;
mod lift_record;
mod macros;
mod memory_reader;
pub use error::LiError;
pub use lift_array::array_lift_memory;
pub use lift_record::record_lift_memory;
pub use memory_reader::MemoryReader;
pub use memory_reader::SequentialReader;
use super::traits::RecordResolvable;
use std::cell::Cell;
pub type LiResult<T> = std::result::Result<T, error::LiError>;
pub struct ILifter<'m, 'r, R: RecordResolvable> {
pub reader: MemoryReader<'m>,
pub resolver: &'r R,
}
impl<'m, 'r, R: RecordResolvable> ILifter<'m, 'r, R> {
pub fn new(memory: &'m [Cell<u8>], resolver: &'r R) -> Self {
let reader = MemoryReader::new(memory);
Self { reader, resolver }
}
}

View File

@ -0,0 +1,28 @@
/*
* 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::traits::AllocatableError;
use crate::traits::RecordResolvableError;
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum LoError {
#[error("{0}")]
AllocatableError(#[from] AllocatableError),
#[error("{0}")]
RecordResolvableError(#[from] RecordResolvableError),
}

View File

@ -0,0 +1,96 @@
/*
* 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 super::ILowerer;
use super::LoResult;
use crate::traits::Allocatable;
use crate::utils::ser_value_size;
use crate::utils::type_tag_form_ivalue;
use crate::IValue;
pub struct LoweredArray {
pub offset: usize,
pub size: usize,
}
impl LoweredArray {
pub fn new(offset: usize, size: usize) -> Self {
Self { offset, size }
}
pub fn empty() -> Self {
Self { offset: 0, size: 0 }
}
}
pub fn array_lower_memory<A: Allocatable>(
lowerer: &ILowerer<'_, A>,
array_values: Vec<IValue>,
) -> LoResult<LoweredArray> {
if array_values.is_empty() {
return Ok(LoweredArray::empty());
}
let elements_count = array_values.len() as u32;
let size = ser_value_size(&array_values[0]) * elements_count;
let type_tag = type_tag_form_ivalue(&array_values[0]);
let seq_writer = lowerer.writer.sequential_writer(size, type_tag)?;
// here it's known that all interface values have the same type
for value in array_values {
match value {
IValue::Boolean(value) => seq_writer.write_u8(&lowerer.writer, value as _),
IValue::S8(value) => seq_writer.write_u8(&lowerer.writer, value as _),
IValue::S16(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::S32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::S64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::U8(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::U16(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::U32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::U64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::I32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::I64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::F32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::F64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()),
IValue::String(value) => {
let offset = lowerer.writer.write_bytes(value.as_bytes())? as u32;
seq_writer.write_array(&lowerer.writer, offset.to_le_bytes());
seq_writer.write_array(&lowerer.writer, (value.len() as u32).to_le_bytes());
}
IValue::ByteArray(values) => {
let offset = lowerer.writer.write_bytes(&values)? as u32;
seq_writer.write_array(&lowerer.writer, offset.to_le_bytes());
seq_writer.write_array(&lowerer.writer, (values.len() as u32).to_le_bytes());
}
IValue::Array(values) => {
let LoweredArray { offset, size } = array_lower_memory(lowerer, values)?;
seq_writer.write_array(&lowerer.writer, (offset as u32).to_le_bytes());
seq_writer.write_array(&lowerer.writer, (size as u32).to_le_bytes());
}
IValue::Record(values) => {
let offset = super::record_lower_memory(lowerer, values)? as u32;
seq_writer.write_array(&lowerer.writer, offset.to_le_bytes());
}
}
}
let offset = seq_writer.start_offset();
let lowered_array = LoweredArray::new(offset as _, elements_count as _);
Ok(lowered_array)
}

View File

@ -0,0 +1,78 @@
/*
* 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 super::ILowerer;
use super::LoResult;
use super::LoweredArray;
use crate::traits::Allocatable;
use crate::IValue;
use crate::NEVec;
pub fn record_lower_memory<A: Allocatable>(
lowerer: &ILowerer<'_, A>,
values: NEVec<IValue>,
) -> LoResult<i32> {
let average_field_size = 4;
// TODO: avoid this additional allocation after fixing github.com/fluencelabs/fce/issues/77
let mut result: Vec<u8> = Vec::with_capacity(average_field_size * values.len());
for value in values.into_vec() {
match value {
IValue::Boolean(value) => result.push(value as _),
IValue::S8(value) => result.push(value as _),
IValue::S16(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::S32(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::S64(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::U8(value) => result.push(value),
IValue::U16(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::U32(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::U64(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::I32(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::I64(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::F32(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::F64(value) => result.extend_from_slice(&value.to_le_bytes()),
IValue::String(value) => {
let offset = lowerer.writer.write_bytes(value.as_bytes())? as u32;
result.extend_from_slice(&offset.to_le_bytes());
result.extend_from_slice(&(value.len() as u32).to_le_bytes());
}
IValue::ByteArray(value) => {
let offset = lowerer.writer.write_bytes(&value)? as u32;
result.extend_from_slice(&offset.to_le_bytes());
result.extend_from_slice(&(value.len() as u32).to_le_bytes());
}
IValue::Array(values) => {
let LoweredArray { offset, size } = super::array_lower_memory(lowerer, values)?;
result.extend_from_slice(&(offset as u32).to_le_bytes());
result.extend_from_slice(&(size as u32).to_le_bytes());
}
IValue::Record(values) => {
let offset = record_lower_memory(lowerer, values)? as u32;
result.extend_from_slice(&offset.to_le_bytes());
}
}
}
let result_pointer = lowerer.writer.write_bytes(&result)?;
Ok(result_pointer as _)
}

View File

@ -14,21 +14,16 @@
* limitations under the License.
*/
use crate::WriteResult;
use super::LoResult;
use crate::traits::Allocatable;
use crate::traits::MemSlice;
use crate::traits::DEFAULT_MEMORY_INDEX;
use crate::utils::type_tag_form_itype;
use std::cell::Cell;
pub type MemSlice<'m> = &'m [Cell<u8>];
const MEMORY_INDEX: usize = 0;
pub trait Heapable {
fn allocate(&self, size: u32, type_tag: u32) -> WriteResult<usize>;
fn memory_slice(&self, memory_index: usize) -> WriteResult<MemSlice<'_>>;
}
pub struct MemoryWriter<'i, T: Heapable> {
heap_manager: &'i T,
pub struct MemoryWriter<'i, R: Allocatable> {
heap_manager: &'i R,
pub(self) memory: Cell<MemSlice<'i>>,
}
@ -37,9 +32,9 @@ pub struct SequentialWriter {
offset: Cell<usize>,
}
impl<'i, T: Heapable> MemoryWriter<'i, T> {
pub fn new(heap_manager: &'i T) -> WriteResult<Self> {
let mem_slice = heap_manager.memory_slice(MEMORY_INDEX)?;
impl<'i, A: Allocatable> MemoryWriter<'i, A> {
pub fn new(heap_manager: &'i A) -> LoResult<Self> {
let mem_slice = heap_manager.memory_slice(DEFAULT_MEMORY_INDEX)?;
let memory = Cell::new(mem_slice);
let writer = Self {
@ -49,17 +44,17 @@ impl<'i, T: Heapable> MemoryWriter<'i, T> {
Ok(writer)
}
pub fn write_bytes(&self, bytes: &[u8]) -> WriteResult<usize> {
let byte_type_tag = crate::type_tag_form_itype(&crate::IType::U8);
pub fn write_bytes(&self, bytes: &[u8]) -> LoResult<usize> {
let byte_type_tag = type_tag_form_itype(&crate::IType::U8);
let seq_writer = self.sequential_writer(bytes.len() as _, byte_type_tag)?;
seq_writer.write_bytes(self, bytes);
Ok(seq_writer.start_offset())
}
pub fn sequential_writer(&self, size: u32, type_tag: u32) -> WriteResult<SequentialWriter> {
pub fn sequential_writer(&self, size: u32, type_tag: u32) -> LoResult<SequentialWriter> {
let offset = self.heap_manager.allocate(size, type_tag)?;
let new_mem_slice = self.heap_manager.memory_slice(MEMORY_INDEX)?;
let new_mem_slice = self.heap_manager.memory_slice(DEFAULT_MEMORY_INDEX)?;
self.memory.set(new_mem_slice);
Ok(SequentialWriter::new(offset))
@ -78,9 +73,9 @@ impl SequentialWriter {
self.start_offset
}
pub fn write_array<T: Heapable, const N: usize>(
pub fn write_array<A: Allocatable, const N: usize>(
&self,
writer: &MemoryWriter<T>,
writer: &MemoryWriter<'_, A>,
values: [u8; N],
) {
let offset = self.offset.get();
@ -94,7 +89,7 @@ impl SequentialWriter {
}
// specialization of write_array for u8
pub fn write_u8<T: Heapable>(&self, writer: &MemoryWriter<T>, value: u8) {
pub fn write_u8<A: Allocatable>(&self, writer: &MemoryWriter<'_, A>, value: u8) {
let offset = self.offset.get();
writer.memory.get()[offset].set(value);
@ -103,7 +98,7 @@ impl SequentialWriter {
}
// specialization of write_array for u32
pub fn write_u32<T: Heapable>(&self, writer: &MemoryWriter<T>, value: u32) {
pub fn write_u32<A: Allocatable>(&self, writer: &MemoryWriter<'_, A>, value: u32) {
let offset = self.offset.get();
let value = value.to_le_bytes();
@ -118,7 +113,7 @@ impl SequentialWriter {
}
#[allow(dead_code)]
pub fn write_bytes<T: Heapable>(&self, writer: &MemoryWriter<T>, bytes: &[u8]) {
pub fn write_bytes<A: Allocatable>(&self, writer: &MemoryWriter<'_, A>, bytes: &[u8]) {
let offset = self.offset.get();
let memory = writer.memory.get();

View File

@ -0,0 +1,43 @@
/*
* 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.
*/
mod error;
mod lower_array;
mod lower_record;
mod memory_writer;
use crate::lowerer::memory_writer::MemoryWriter;
use crate::traits::Allocatable;
pub use error::LoError;
pub use lower_array::array_lower_memory;
pub use lower_array::LoweredArray;
pub use lower_record::record_lower_memory;
pub type LoResult<T> = std::result::Result<T, error::LoError>;
pub struct ILowerer<'m, A: Allocatable> {
pub writer: MemoryWriter<'m, A>,
}
impl<'m, A: Allocatable> ILowerer<'m, A> {
pub fn new(allocatable: &'m A) -> LoResult<Self> {
let writer = MemoryWriter::new(allocatable)?;
let lowerer = Self { writer };
Ok(lowerer)
}
}

View File

@ -14,22 +14,21 @@
* limitations under the License.
*/
use std::cell::Cell;
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,
},
pub const DEFAULT_MEMORY_INDEX: usize = 0;
pub type MemSlice<'m> = &'m [Cell<u8>];
pub trait Allocatable {
fn allocate(&self, size: u32, type_tag: u32) -> Result<usize, AllocatableError>;
fn memory_slice(&self, memory_index: usize) -> Result<MemSlice<'_>, AllocatableError>;
}
#[derive(Debug, ThisError)]
pub enum MemoryWriteError {
pub enum AllocatableError {
/// The memory doesn't exist.
#[error("memory `{memory_index}` does not exist")]
MemoryIsMissing {
@ -61,4 +60,9 @@ pub enum MemoryWriteError {
probably a Wasm module's built with unsupported sdk version"
)]
AllocateFuncIncompatibleOutput,
// TODO: make it generic in future.
/// User defined error.
#[error("{0}")]
UserDefinedError(String),
}

View File

@ -0,0 +1,21 @@
/*
* 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.
*/
mod heapable;
mod record_resolvable;
pub use heapable::*;
pub use record_resolvable::*;

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 crate::IRecordType;
use thiserror::Error as ThisError;
pub trait RecordResolvable {
fn resolve_record(&self, record_type_id: u64) -> Result<&IRecordType, RecordResolvableError>;
}
#[derive(Debug, ThisError)]
pub enum RecordResolvableError {
/// Record for such type is wasn't found.
#[error("Record with type id '{0}' not found")]
RecordNotFound(u64),
}

View File

@ -0,0 +1,95 @@
/*
* 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::IRecordType;
use crate::IType;
use crate::IValue;
/// Size of a value in a serialized view.
pub fn ser_type_size(ty: &IType) -> usize {
const WASM_POINTER_SIZE: usize = 4;
match ty {
IType::Boolean | IType::S8 | IType::U8 => 1,
IType::S16 | IType::U16 => 2,
IType::S32 | IType::U32 | IType::I32 | IType::F32 => 4,
IType::Record(_) => 4,
// Vec-like types are passed by pointer and size
IType::String | IType::ByteArray | IType::Array(_) => 2 * WASM_POINTER_SIZE,
IType::S64 | IType::U64 | IType::I64 | IType::F64 => 8,
}
}
/// Size of a value in a serialized view.
pub fn ser_value_size(value: &IValue) -> u32 {
match value {
IValue::Boolean(_) | IValue::S8(_) | IValue::U8(_) => 1,
IValue::S16(_) | IValue::U16(_) => 2,
IValue::S32(_) | IValue::U32(_) | IValue::F32(_) | IValue::I32(_) => 4,
IValue::S64(_) | IValue::U64(_) | IValue::F64(_) | IValue::I64(_) => 8,
IValue::String(_) | IValue::ByteArray(_) | IValue::Array(_) => 2 * 4,
IValue::Record(_) => 4,
}
}
/// Returns the record size in bytes.
pub fn record_size(record_type: &IRecordType) -> usize {
record_type
.fields
.iter()
.map(|f| ser_type_size(&f.ty))
.sum()
}
pub fn type_tag_form_itype(itype: &IType) -> u32 {
const POINTER_CODE: u32 = 3; // u32 in the sdk
match itype {
IType::Boolean => 0, // u8
IType::U8 => 1, // u8
IType::U16 => 2, // u16
IType::U32 => 3, // u32
IType::U64 => 4, // u64
IType::S8 => 6, // i8
IType::S16 => 7, // i16
IType::S32 | IType::I32 => 8, // i32
IType::S64 | IType::I64 => 9, // i64
IType::F32 => 10, // f32
IType::F64 => 11, // f64
IType::ByteArray | IType::Array(_) | IType::Record(_) | IType::String => POINTER_CODE,
}
}
pub fn type_tag_form_ivalue(itype: &IValue) -> u32 {
const POINTER_CODE: u32 = 3; // u32 in the sdk
match itype {
IValue::Boolean(_) => 0, // u8
IValue::U8(_) => 1, // u8
IValue::U16(_) => 2, // u16
IValue::U32(_) => 3, // u32
IValue::U64(_) => 4, // u64
IValue::S8(_) => 6, // i8
IValue::S16(_) => 7, // i16
IValue::S32(_) | IValue::I32(_) => 8, // i32
IValue::S64(_) | IValue::I64(_) => 9, // i64
IValue::F32(_) => 10, // f32
IValue::F64(_) => 11, // f64
IValue::ByteArray(_) | IValue::Array(_) | IValue::Record(_) | IValue::String(_) => {
POINTER_CODE
}
}
}