github.com/apache/beam/sdks/v2@v2.48.2/go/examples/wasm/greet.rs (about) 1 // Licensed to the Apache Software Foundation (ASF) under one or more 2 // contributor license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright ownership. 4 // The ASF licenses this file to You under the Apache License, Version 2.0 5 // (the "License"); you may not use this file except in compliance with 6 // the License. You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 // Copied from: 17 // https://github.com/tetratelabs/wazero/blob/v1.0.0-pre.3/examples/allocation/rust/testdata/greet.rs 18 extern crate alloc; 19 extern crate core; 20 extern crate wee_alloc; 21 22 use alloc::vec::Vec; 23 use std::mem::MaybeUninit; 24 use std::slice; 25 26 /// Prints a greeting to the console using [`log`]. 27 fn greet(name: &String) { 28 log(&["wasm >> ", &greeting(name)].concat()); 29 } 30 31 /// Gets a greeting for the name. 32 fn greeting(name: &String) -> String { 33 return ["Hello, ", &name, "!"].concat(); 34 } 35 36 /// Logs a message to the console using [`_log`]. 37 fn log(message: &String) { 38 unsafe { 39 let (ptr, len) = string_to_ptr(message); 40 _log(ptr, len); 41 } 42 } 43 44 #[link(wasm_import_module = "env")] 45 extern "C" { 46 /// WebAssembly import which prints a string (linear memory offset, 47 /// byteCount) to the console. 48 /// 49 /// Note: This is not an ownership transfer: Rust still owns the pointer 50 /// and ensures it isn't deallocated during this call. 51 #[link_name = "log"] 52 fn _log(ptr: u32, size: u32); 53 } 54 55 /// WebAssembly export that accepts a string (linear memory offset, byteCount) 56 /// and calls [`greet`]. 57 /// 58 /// Note: The input parameters were returned by [`allocate`]. This is not an 59 /// ownership transfer, so the inputs can be reused after this call. 60 #[cfg_attr(all(target_arch = "wasm32"), export_name = "greet")] 61 #[no_mangle] 62 pub unsafe extern "C" fn _greet(ptr: u32, len: u32) { 63 greet(&ptr_to_string(ptr, len)); 64 } 65 66 /// WebAssembly export that accepts a string (linear memory offset, byteCount) 67 /// and returns a pointer/size pair packed into a u64. 68 /// 69 /// Note: The return value is leaked to the caller, so it must call 70 /// [`deallocate`] when finished. 71 /// Note: This uses a u64 instead of two result values for compatibility with 72 /// WebAssembly 1.0. 73 #[cfg_attr(all(target_arch = "wasm32"), export_name = "greeting")] 74 #[no_mangle] 75 pub unsafe extern "C" fn _greeting(ptr: u32, len: u32) -> u64 { 76 let name = &ptr_to_string(ptr, len); 77 let g = greeting(name); 78 let (ptr, len) = string_to_ptr(&g); 79 // Note: This changes ownership of the pointer to the external caller. If 80 // we didn't call forget, the caller would read back a corrupt value. Since 81 // we call forget, the caller must deallocate externally to prevent leaks. 82 std::mem::forget(g); 83 return ((ptr as u64) << 32) | len as u64; 84 } 85 86 /// Returns a string from WebAssembly compatible numeric types representing 87 /// its pointer and length. 88 unsafe fn ptr_to_string(ptr: u32, len: u32) -> String { 89 let slice = slice::from_raw_parts_mut(ptr as *mut u8, len as usize); 90 let utf8 = std::str::from_utf8_unchecked_mut(slice); 91 return String::from(utf8); 92 } 93 94 /// Returns a pointer and size pair for the given string in a way compatible 95 /// with WebAssembly numeric types. 96 /// 97 /// Note: This doesn't change the ownership of the String. To intentionally 98 /// leak it, use [`std::mem::forget`] on the input after calling this. 99 unsafe fn string_to_ptr(s: &String) -> (u32, u32) { 100 return (s.as_ptr() as u32, s.len() as u32); 101 } 102 103 /// Set the global allocator to the WebAssembly optimized one. 104 #[global_allocator] 105 static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 106 107 /// WebAssembly export that allocates a pointer (linear memory offset) that can 108 /// be used for a string. 109 /// 110 /// This is an ownership transfer, which means the caller must call 111 /// [`deallocate`] when finished. 112 #[cfg_attr(all(target_arch = "wasm32"), export_name = "allocate")] 113 #[no_mangle] 114 pub extern "C" fn _allocate(size: u32) -> *mut u8 { 115 allocate(size as usize) 116 } 117 118 /// Allocates size bytes and leaks the pointer where they start. 119 fn allocate(size: usize) -> *mut u8 { 120 // Allocate the amount of bytes needed. 121 let vec: Vec<MaybeUninit<u8>> = Vec::with_capacity(size); 122 123 // into_raw leaks the memory to the caller. 124 Box::into_raw(vec.into_boxed_slice()) as *mut u8 125 } 126 127 128 /// WebAssembly export that deallocates a pointer of the given size (linear 129 /// memory offset, byteCount) allocated by [`allocate`]. 130 #[cfg_attr(all(target_arch = "wasm32"), export_name = "deallocate")] 131 #[no_mangle] 132 pub unsafe extern "C" fn _deallocate(ptr: u32, size: u32) { 133 deallocate(ptr as *mut u8, size as usize); 134 } 135 136 /// Retakes the pointer which allows its memory to be freed. 137 unsafe fn deallocate(ptr: *mut u8, size: usize) { 138 let _ = Vec::from_raw_parts(ptr, 0, size); 139 }