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  }