-
Notifications
You must be signed in to change notification settings - Fork 824
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bf7031a
commit 5e16f6d
Showing
3 changed files
with
148 additions
and
16 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use anyhow::Result; | ||
use wasmer::{wat2wasm, imports, Instance, Module, Store, Memory, AsStoreRef, MemoryView, FunctionEnvMut, WasmPtr, FunctionEnv, Function}; | ||
|
||
// Utils | ||
pub fn read_string(view: &MemoryView, start: u32, len: u32) -> Result<String> { | ||
Ok(WasmPtr::<u8>::new(start).read_utf8_string(view, len)?) | ||
} | ||
|
||
// Environment | ||
pub struct ExampleEnv { | ||
memory: Option<Memory>, | ||
} | ||
|
||
impl ExampleEnv { | ||
fn set_memory(&mut self, memory: Memory) { | ||
self.memory = Some(memory); | ||
} | ||
|
||
fn get_memory(&self) -> &Memory { | ||
self.memory.as_ref().unwrap() | ||
} | ||
|
||
fn view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> { | ||
self.get_memory().view(store) | ||
} | ||
} | ||
|
||
fn http_get(mut ctx: FunctionEnvMut<ExampleEnv>, url: u32, url_len: u32) -> u32 { | ||
// Setup environment | ||
let (response, memory_size) = { | ||
// Read url from memory | ||
let view = ctx.data().view(&ctx); | ||
let memory_size = view.data_size() as usize; | ||
let address = read_string(&view, url, url_len).unwrap(); | ||
|
||
// Get request | ||
let response = ureq::get(&address).call().unwrap(); | ||
let capacity = match response.header("Content-Length").map(|it| it.parse::<usize>()) { | ||
Some(Ok(len)) => len, | ||
_ => 1024, | ||
}; | ||
let mut buffer = Vec::with_capacity(capacity); | ||
let mut reader = response.into_reader(); | ||
reader.read_to_end(&mut buffer).unwrap(); | ||
(buffer, memory_size) | ||
}; | ||
|
||
// If the response is too big, grow memory | ||
if 1114112 + response.len() > memory_size { | ||
let delta = (1114112 + response.len() - memory_size) / wasmer::WASM_PAGE_SIZE + 1; | ||
let memory = ctx.data().get_memory().clone(); | ||
memory.grow(&mut ctx, delta as u32).unwrap(); | ||
} | ||
|
||
// Write response as string [ptr, cap, len] to wasm memory and return pointer | ||
let view = ctx.data().view(&ctx); | ||
view.write(1114112, &u32::to_le_bytes(1114124)).unwrap(); | ||
view.write(1114116, &u32::to_le_bytes(response.len() as u32)).unwrap(); | ||
view.write(1114120, &u32::to_le_bytes(response.len() as u32)).unwrap(); | ||
view.write(1114124, &response).unwrap(); | ||
1114112 | ||
} | ||
|
||
fn main() -> Result<()> { | ||
let wasm_bytes = wat2wasm( | ||
br#" | ||
(module | ||
(type (;0;) (func (param i32 i32) (result i32))) | ||
(type (;1;) (func (result i32))) | ||
(type (;2;) (func)) | ||
(import "env" "http_get" (func (;0;) (type 0))) | ||
(func (;1;) (type 1) (result i32) | ||
i32.const 1048576 | ||
i32.const 45 | ||
call 0 | ||
i32.const 8 | ||
i32.add | ||
i32.load) | ||
(func (;2;) (type 2)) | ||
(func (;3;) (type 2) | ||
call 2 | ||
call 2) | ||
(func (;4;) (type 1) (result i32) | ||
call 1 | ||
call 3) | ||
(table (;0;) 1 1 funcref) | ||
(memory (;0;) 17) | ||
(global (;0;) (mut i32) (i32.const 1048576)) | ||
(export "memory" (memory 0)) | ||
(export "fetch" (func 4)) | ||
(data (;0;) (i32.const 1048576) "https://postman-echo.com/bytes/5/mb?type=json")) | ||
"#, | ||
)?; | ||
|
||
// Load module | ||
let mut store = Store::default(); | ||
let module = Module::new(&store, wasm_bytes)?; | ||
|
||
// Add host functions | ||
let function_env = FunctionEnv::new(&mut store, ExampleEnv { memory: None }); | ||
let import_object = imports! { | ||
// We use the default namespace "env". | ||
"env" => { | ||
// And call our function "http_get". | ||
"http_get" => Function::new_typed_with_env(&mut store, &function_env, http_get), | ||
} | ||
}; | ||
|
||
// Create instance | ||
let instance = Instance::new(&mut store, &module, &import_object)?; | ||
let memory = instance.exports.get_memory("memory")?; | ||
|
||
// Give reference to memory | ||
function_env.as_mut(&mut store).set_memory(memory.clone()); | ||
|
||
// Call function | ||
let fetch = instance.exports.get_function("fetch")?; | ||
let result = fetch.call(&mut store, &[])?; | ||
println!("Response size: {result:?}"); | ||
|
||
Ok(()) | ||
} |