update the main part to the new sdk

This commit is contained in:
vms 2020-07-01 00:50:56 +03:00
parent d596f5034d
commit aa73411663
13 changed files with 120 additions and 407 deletions

View File

@ -22,15 +22,8 @@ fluence-sdk-macro = { path = "crates/macro", version = "=0.1.11" }
fluence-sdk-main = { path = "crates/main", version = "=0.1.11" }
[features]
default = ["export_allocator"]
# Turn on a compilation for the module that contains Wasm logger realization.
wasm_logger = ["fluence-sdk-main/wasm_logger"]
# Turn on a compilation for the module that contains the standard implementation of allocate/deallocate functions.
export_allocator = ["fluence-sdk-main/export_allocator"]
# Provides additional imports to allow this module work as a side module
side_module = ["fluence-sdk-main/side_module"]
# Print some internal logs by log_utf8_string
print_logs = ["fluence-sdk-main/print_logs"]
[workspace]
members = [

View File

@ -201,7 +201,7 @@ fn invoke_handler_impl(
}
#[proc_macro_attribute]
pub fn invocation_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
pub fn faas_export(attr: TokenStream, input: TokenStream) -> TokenStream {
let fn_item = parse_macro_input!(input as ItemFn);
match invoke_handler_impl(attr.into(), fn_item) {
Ok(v) => v,

View File

@ -20,21 +20,11 @@ crate-type = ["rlib"]
[dependencies]
log = { version = "0.4.8", features = ["std"] }
syn = { version = '0.15.44', features = ['full'] }
[dev-dependencies]
simple_logger = "1.6.0" # used in doc test
lazy_static = "1.4.0" # used in doc test
[features]
# Turn on the Wasm logger.
wasm_logger = []
# Make this module as side module.
side_module = []
# Notify the VM that this module expects Ethereum blocks.
expect_eth = []
# Turn on the module contained implementation of allocate/deallocate functions.
export_allocator = []
# Print some internal logs by log_utf8_string
print_logs = []

View File

@ -1,24 +0,0 @@
/*
* Copyright 2019 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.
*/
//! Notifies the VM that this module expects Ethereum blocks.
//!
//! This module contains functions for loading from and storing to memory.
/// A temporary solution to let users configure their ethereum expectations via WASM bytecode:
/// to enable block uploading via invoke method, just export expects_eth method from the module.
#[no_mangle]
pub unsafe fn expects_eth() {}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Fluence Labs Limited
* Copyright 2020 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.
@ -14,32 +14,42 @@
* limitations under the License.
*/
//! This module provides default implementations of [`allocate`] and [`deallocate`] functions that
//! can be used for array passing and returning.
//!
//! [`allocate`]: fn.allocate.html
//! [`deallocate`]: fn.deallocate.html
use crate::log_utf8_string;
use crate::memory::{alloc, dealloc};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
use std::alloc::alloc as global_alloc;
use std::alloc::dealloc as global_dealloc;
use std::alloc::Layout;
/// Allocates memory area of specified size and returns its address.
/// Used from the host environment for memory allocation while parameters passing.
/// Returns 0 if supplied size is too long.
#[no_mangle]
pub unsafe fn allocate(size: usize) -> NonNull<u8> {
let non_zero_size = NonZeroUsize::new(size)
.unwrap_or_else(|| panic!("[Error] Allocation of zero bytes is not allowed."));
alloc(non_zero_size).unwrap_or_else(|_| panic!("[Error] Allocation of {} bytes failed.", size))
pub unsafe fn allocate(size: usize) -> usize {
let layout = match Layout::from_size_align(size, std::mem::align_of::<u8>()) {
Ok(layout) => layout,
// in this case a err could be only in a case of too long allocated size,
// so just return 0
Err(_) => return 0,
};
let msg = format!("sdk.allocate: {:?}\n", size);
log_utf8_string(msg.as_ptr() as _, msg.len() as _);
global_alloc(layout) as _
}
/// Deallocates memory area for provided memory pointer and size.
/// Used from the host environment for memory deallocation after reading results of function from
/// Wasm memory.
/// Does nothing if supplied size is too long.
#[no_mangle]
pub unsafe fn deallocate(ptr: NonNull<u8>, size: usize) {
let non_zero_size = NonZeroUsize::new(size)
.unwrap_or_else(|| panic!("[Error] Deallocation of zero bytes is not allowed."));
dealloc(ptr, non_zero_size)
.unwrap_or_else(|_| panic!("[Error] Deallocate failed for ptr={:?} size={}.", ptr, size));
pub unsafe fn deallocate(ptr: *mut u8, size: usize) {
let layout = match Layout::from_size_align(size, std::mem::align_of::<u8>()) {
Ok(layout) => layout,
// in this case a err could be only in a case of too long allocated size,
// so just done nothing
Err(_) => return,
};
let msg = format!("sdk.deallocate: {:?} {}\n", ptr, size);
log_utf8_string(msg.as_ptr() as _, msg.len() as _);
global_dealloc(ptr, layout);
}

View File

@ -14,12 +14,12 @@
* limitations under the License.
*/
//! The main part of Fluence backend SDK. Contains `export_allocator` (is turned on by the
//! `export_allocator` feature), `logger` (is turned on by the `wasm_logger` feature), and `memory`
//! The main part of Fluence backend SDK. Contains `export_allocator`, `logger` and `result`
//! modules.
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::needless_doctest_main)]
#![doc(html_root_url = "https://docs.rs/fluence-sdk-main/0.1.11")]
#![feature(allocator_api)]
#![deny(
dead_code,
nonstandard_style,
@ -31,16 +31,16 @@
)]
#![warn(rust_2018_idioms)]
pub mod memory;
mod export_allocator;
mod logger;
mod result;
#[cfg(feature = "wasm_logger")]
pub mod logger;
pub(crate) use logger::log_utf8_string;
#[cfg(feature = "side_module")]
pub mod side_module;
#[cfg(feature = "expect_eth")]
pub mod eth;
#[cfg(feature = "export_allocator")]
pub mod export_allocator;
pub use export_allocator::allocate;
pub use export_allocator::deallocate;
pub use logger::WasmLogger;
pub use result::get_result_ptr;
pub use result::get_result_size;
pub use result::set_result_ptr;
pub use result::set_result_size;

View File

@ -73,8 +73,6 @@
//! [`lazy_static::initialize()`]: https://docs.rs/lazy_static/1.3.0/lazy_static/fn.initialize.html
//! [`backend app debugging`]: https://fluence.dev/docs/debugging
use log;
/// The Wasm Logger.
///
/// This struct implements the [`Log`] trait from the [`log`] crate, which allows it to act as a
@ -92,8 +90,8 @@ pub struct WasmLogger {
}
impl WasmLogger {
/// Initializes the global logger with a [`WasmLogger`] instance with
/// `max_log_level` set to a specific log level.
/// Initializes the global logger with a [`WasmLogger`] instance, sets
/// `max_log_level` to a given log level.
///
/// ```
/// # use fluence::sdk::*;
@ -114,8 +112,8 @@ impl WasmLogger {
Ok(())
}
/// Initializes the global logger with a [`WasmLogger`] instance with `max_log_level` set to
/// `Level::Info`.
/// Initializes the global logger with a [`WasmLogger`] instance, sets
/// `max_log_level` to `Level::Info`.
///
/// ```
/// # use fluence::sdk::*;
@ -162,9 +160,9 @@ impl log::Log for WasmLogger {
fn flush(&self) {}
}
/// logger is a module provided by a VM that can process log messages.
#[link(wasm_import_module = "logger")]
/// log_utf8_string should be provided directly by a host.
#[link(wasm_import_module = "host")]
extern "C" {
// Writes a byte string of size bytes that starts from ptr to a logger
fn log_utf8_string(ptr: i32, size: i32);
pub fn log_utf8_string(ptr: i32, size: i32);
}

View File

@ -1,77 +0,0 @@
/*
* Copyright 2018 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.
*/
//! Contains definitions for memory errors.
use core::alloc::LayoutErr;
use std::alloc::AllocErr;
use std::error::Error;
use std::fmt::{self, Display};
use std::io;
#[derive(Debug, Clone)]
pub struct MemError(String);
impl Display for MemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "MemError({:?})", self)
}
}
impl Error for MemError {}
impl MemError {
pub fn new(message: &str) -> Self {
MemError(message.to_string())
}
pub fn from_err<E: Error + Display>(err: &E) -> Self {
MemError::new(&err.to_string())
}
}
/// Creates `From` instance for MemError for each specified error types.
///
/// Example:
///
/// ```
/// // usage of macro:
///
/// mem_error_from![io::Error]
///
/// // code will be generated:
///
/// impl From<io::Error> for MemError {
/// fn from(err: io::Error) -> Self {
/// MemError::from_err(err)
/// }
/// }
///
/// ```
// TODO: move this macro to utils or use 'quick-error' or 'error-chain' crates
macro_rules! mem_error_from {
( $( $x:ty );* ) => {
$(
impl From<$x> for MemError {
fn from(err: $x) -> Self {
MemError::from_err(&err)
}
}
)*
}
}
mem_error_from! [LayoutErr; AllocErr; io::Error];

View File

@ -1,187 +0,0 @@
/*
* Copyright 2018 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.
*/
//! Raw API for dealing with Wasm memory.
//!
//! This module contains functions for memory initializing and manipulating.
pub mod errors;
use self::errors::MemError;
use std::alloc::{alloc as global_alloc, dealloc as global_dealloc, Layout};
use std::mem;
use std::num::NonZeroUsize;
use std::ptr::{self, NonNull};
/// Result type for this module.
pub type MemResult<T> = ::std::result::Result<T, MemError>;
/// Bytes count occupied by a length of resulted array.
pub const RESPONSE_SIZE_BYTES: usize = 4;
/// Allocates memory area of specified size and returns its address. Actually is just a wrapper for
/// [`GlobalAlloc::alloc`].
///
/// # Safety
///
/// See [`GlobalAlloc::alloc`].
///
/// [`GlobalAlloc::alloc`]: https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html#tymethod.alloc
///
pub unsafe fn alloc(size: NonZeroUsize) -> MemResult<NonNull<u8>> {
let layout: Layout = Layout::from_size_align(size.get(), mem::align_of::<u8>())?;
Ok(NonNull::new_unchecked(global_alloc(layout)))
}
/// Deallocates memory area for current memory pointer and size. Actually is just a wrapper for
/// [`GlobalAlloc::dealloc`].
///
/// # Safety
///
/// See [`GlobalAlloc::dealloc`].
///
/// [`GlobalAlloc::dealloc`]: https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html#tymethod.dealloc
///
pub unsafe fn dealloc(ptr: NonNull<u8>, size: NonZeroUsize) -> MemResult<()> {
let layout = Layout::from_size_align(size.get(), mem::align_of::<u8>())?;
global_dealloc(ptr.as_ptr(), layout);
Ok(())
}
/// Allocates 'RESPONSE_SIZE_BYTES + result.len()' bytes, writes length of the result as little
/// endian RESPONSE_SIZE_BYTES bytes, and finally writes content of 'result'. So the final layout of
/// the result in memory is following:
/// `
/// | array_length: RESPONSE_SIZE_BYTES bytes (little-endian) | array: $array_length bytes |
/// `
/// This function should normally be used to return result of `invoke` function. Vm wrapper
/// expects result in this format.
///
pub unsafe fn write_response_to_mem(result: &[u8]) -> MemResult<NonNull<u8>> {
let result_len = result.len();
let total_len = result_len
.checked_add(RESPONSE_SIZE_BYTES)
.ok_or_else(|| MemError::new("usize overflow occurred"))?;
// converts array size to bytes in little-endian
let len_as_bytes: [u8; RESPONSE_SIZE_BYTES] = mem::transmute((result_len as u32).to_le());
// allocates a new memory region for the result
let result_ptr = alloc(NonZeroUsize::new_unchecked(total_len))?;
// copies length of array to memory
ptr::copy_nonoverlapping(
len_as_bytes.as_ptr(),
result_ptr.as_ptr(),
RESPONSE_SIZE_BYTES,
);
// copies array to memory
ptr::copy_nonoverlapping(
result.as_ptr(),
result_ptr.as_ptr().add(RESPONSE_SIZE_BYTES),
result_len,
);
Ok(result_ptr)
}
/// Reads array of bytes from a given `ptr` that must to have `len` bytes size.
///
/// # Safety
///
/// The ownership of `ptr` is effectively (without additional allocation) transferred to the
/// resulted `Vec` which can be then safely deallocated, reallocated or so on.
/// **There have to be only one instance of `Vec` constructed from one of such pointer since
/// there isn't any memory copied.**
///
pub unsafe fn read_request_from_mem(ptr: *mut u8, len: usize) -> Vec<u8> {
Vec::from_raw_parts(ptr, len, len)
}
/// Reads `u32` (assuming that it is given in little endianness order) from a specified pointer.
pub unsafe fn read_len(ptr: *mut u8) -> u32 {
let mut len_as_bytes: [u8; RESPONSE_SIZE_BYTES] = [0; RESPONSE_SIZE_BYTES];
ptr::copy_nonoverlapping(ptr, len_as_bytes.as_mut_ptr(), RESPONSE_SIZE_BYTES);
mem::transmute(len_as_bytes)
}
#[cfg(test)]
pub mod test {
use super::*;
use std::num::NonZeroUsize;
pub unsafe fn read_result_from_mem(ptr: NonNull<u8>) -> MemResult<Vec<u8>> {
// read string length from current pointer
let input_len = super::read_len(ptr.as_ptr()) as usize;
let total_len = RESPONSE_SIZE_BYTES
.checked_add(input_len)
.ok_or_else(|| MemError::new("usize overflow occurred"))?;
// creates object from raw bytes
let mut input = Vec::from_raw_parts(ptr.as_ptr(), total_len, total_len);
// drains RESPONSE_SIZE_BYTES from the beginning of created vector, it allows to effectively
// skips (without additional allocations) length of the result.
{
input.drain(0..RESPONSE_SIZE_BYTES);
}
Ok(input)
}
#[test]
fn alloc_dealloc_test() {
unsafe {
let size = NonZeroUsize::new_unchecked(123);
let ptr = alloc(size).unwrap();
assert_eq!(dealloc(ptr, size).unwrap(), ());
}
}
#[test]
fn write_and_read_str_test() {
unsafe {
let src_str = "some string Ω";
let ptr = write_response_to_mem(src_str.as_bytes()).unwrap();
let result_array = read_result_from_mem(ptr).unwrap();
let result_str = String::from_utf8(result_array).unwrap();
assert_eq!(src_str, result_str);
}
}
/// Creates a big string with pattern "Q..Q" of the specified length.
fn create_big_str(len: usize) -> String {
unsafe { String::from_utf8_unchecked(vec!['Q' as u8; len]) }
}
#[test]
fn lot_of_write_and_read_str_test() {
unsafe {
let mb_str = create_big_str(1024 * 1024);
// writes and read 1mb string (takes several seconds)
for _ in 1..10_000 {
let ptr = write_response_to_mem(mb_str.as_bytes()).unwrap();
let result_array = read_result_from_mem(ptr).unwrap();
let result_str = String::from_utf8(result_array).unwrap();
assert_eq!(mb_str, result_str);
}
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2020 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::log_utf8_string;
use std::sync::atomic::AtomicUsize;
static mut RESULT_PTR: AtomicUsize = AtomicUsize::new(0);
static mut RESULT_SIZE: AtomicUsize = AtomicUsize::new(0);
#[no_mangle]
pub unsafe fn get_result_ptr() -> usize {
let msg = "sdk.get_result_ptr\n";
log_utf8_string(msg.as_ptr() as _, msg.len() as _);
*RESULT_PTR.get_mut()
}
#[no_mangle]
pub unsafe fn get_result_size() -> usize {
let msg = "sdk.get_result_size\n";
log_utf8_string(msg.as_ptr() as _, msg.len() as _);
*RESULT_SIZE.get_mut()
}
#[no_mangle]
pub unsafe fn set_result_ptr(ptr: usize) {
let msg = format!("sdk.set_result_ptr: {}\n", ptr);
log_utf8_string(msg.as_ptr() as _, msg.len() as _);
*RESULT_PTR.get_mut() = ptr;
}
#[no_mangle]
pub unsafe fn set_result_size(size: usize) {
let msg = format!("sdk.set_result_size: {}\n", size);
log_utf8_string(msg.as_ptr() as _, msg.len() as _);
*RESULT_SIZE.get_mut() = size;
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2019 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.
*/
//! Allows a module be side module.
//!
//! This module contains functions for loading from and storing to memory.
/// Stores one byte into module memory by the given address.
/// Could be used from other modules for parameter passing.
#[no_mangle]
pub unsafe fn store(address: *mut u8, byte: u8) {
*address = byte;
}
/// Loads one byte from module memory by the given address.
/// Could be used from other modules for obtaining results.
#[no_mangle]
pub unsafe fn load(address: *mut u8) -> u8 {
return *address;
}

View File

@ -1,2 +1,4 @@
match_block_trailing_comma = true
binop_separator = "Back"
edition = "2018"
reorder_imports = false
reorder_modules = false

View File

@ -23,7 +23,6 @@
//! please import this crate with `default-features = false`.
//!
#![doc(html_root_url = "https://docs.rs/fluence/0.1.11")]
#![feature(allocator_api)]
#![deny(
dead_code,
nonstandard_style,
@ -38,20 +37,8 @@
extern crate fluence_sdk_macro;
extern crate fluence_sdk_main;
/// A module which should be typically globally imported:
///
/// ```
/// use fluence::sdk::*;
/// ```
pub mod sdk {
// TODO: need to introduce macros to avoid code duplication with crates/main/lib.rs
pub use fluence_sdk_main::memory;
#[cfg(feature = "wasm_logger")]
pub use fluence_sdk_main::logger;
#[cfg(feature = "export_allocator")]
pub use fluence_sdk_main::export_allocator;
pub use fluence_sdk_macro::invocation_handler;
}
pub use fluence_sdk_macro::faas_export;
pub use fluence_sdk_main::get_result_ptr;
pub use fluence_sdk_main::get_result_size;
pub use fluence_sdk_main::set_result_ptr;
pub use fluence_sdk_main::set_result_size;