Rust
#[no_mangle]
pub extern "C" fn add_two(a: usize) -> usize {
a + 2
}
#[no_mangle]
pub extern "C" fn foo(...) {
...
}
#[no_mangle]
pub extern "C" fn bar(...) -> Z {
...
}
Node.js
const ffi = require('ffi');
const rustCoolLib = ffi.Library(
'rustCoolLib',
{
add: [ 'int', [ 'int' ] ]
}
);
rustCoolLib.add_two(1); // 3
rustCoolLib.foo(...)
const z = rustCoolLib.bar(...)
pub extern "C" fn abc() {
...
}
extern "C" {
fn random() -> f64;
}
#[no_mangle]
pub extern "C" fn abc() {
...
}
#[repr(C)]
pub enum Weather {
Hot,
Cold,
}
Rust library (crate)
*.so, *.a, *.wasm, ...
cargo build --target xxx
Program in another language
comunicates via FFI
cargo build --target
aarch64-linux-android armv7-linux-androideabi i686-linux-android
target/
-aarch64 -armv7 -i686
*.so
jniLibs/
link
pub struct MyCoolStruct;
impl MyCoolStruct {
pub fn new() -> Self { MyCoolStruct }
}
pub struct MyCoolStruct;
impl MyCoolStruct {
pub fn new() -> Self { MyCoolStruct }
}
use jni::objects::JObject;
use jni::sys::jlong;
use jni::JNIEnv;
#[no_mangle]
pub extern "C" fn Java_com_cool_coolandroidproject_Cool_create(
_env: JNIEnv,
_this: JObject
) -> jlong {
let cool_struct = MyCoolStruct::new();
let boxed_cool_struct = Box::new(cool_struct);
Box::into_raw(boxed_cool_struct) as jlong
}
package com.otaviopace.coolandroidproject
class Cool {
private external fun create(): Long
}
package com.otaviopace.coolandroidproject
class Cool {
private external fun create(): Long
// cool struct memory address
private val coolStruct: Long
}
cargo lipo
target/
-universal
...
*.a
add as a library
add bridging header
pub struct MyCoolStruct;
impl MyCoolStruct {
pub fn new() -> Self { MyCoolStruct }
}
#[no_mangle]
pub extern "C" fn create_cool() -> *mut MyCoolStruct {
let cool_struct = MyCoolStruct::new();
let boxed_cool_struct = Box::new(cool_struct);
Box::into_raw(boxed_cool_struct)
}
#ifndef cool_h
#define cool_h
// opaque pointer struct definition
typedef struct MyCoolStruct MyCoolStruct;
MyCoolStruct* create_cool();
#endif
pkg/
*.wasm
*.js
www/
wasm-pack build
consume as npm package
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct MyCoolStruct;
impl MyCoolStruct {
pub fn new() -> Self { MyCoolStruct }
}
#[wasm_bindgen]
pub fn create_cool() -> MyCoolStruct {
MyCoolStruct::new()
}
#[cfg(target_arch = "wasm32")]
mod wasm;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub struct Person {
name: String,
}
[target.'cfg(target_arch="wasm32")'.dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3.16"
// lib.rs
mod common;
// ... any other common modules
#[cfg(target_arch = "wasm32")]
mod wasm;
#[cfg(target_os = "ios")]
mod ios;
#[cfg(target_os = "android")]
mod android;
// wasm.rs
use crate::common::*;
// public WebAssembly interface
// ios.rs
use crate::common::*;
// public iOS interface
// android.rs
use crate::common::*;
// public Android interface
0
36
let mut pixels: Vec<u8>;
vec![26, 22, 24, 36, ...];
// lib.rs
// two common modules
mod pixel_board;
mod random;
#[cfg(target_arch = "wasm32")]
mod wasm;
#[cfg(not(target_arch = "wasm32"))]
mod standard_ffi;
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
mod android;
// pixel_board.rs
use crate::random::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub struct PixelBoard {
pixels: Vec<u8>,
width: usize,
height: usize,
}
impl PixelBoard {
// all the algorithm's logic
...
}
// wasm.rs
use js_sys::Function;
use wasm_bindgen::prelude::*;
use crate::PixelBoard;
#[wasm_bindgen]
pub fn create_board(fire_width: usize, fire_height: usize) -> PixelBoard {
PixelBoard::new(fire_width, fire_height)
}
#[wasm_bindgen]
pub fn create_fire_source(board: &mut PixelBoard) {
board.create_fire_source();
}
#[wasm_bindgen]
pub fn calculate_fire_propagation(board: &mut PixelBoard, render_callback: Function) {
board.calculate_fire_propagation(convert_render_callback(render_callback));
}
board.calculate_fire_propagation(Box::new(|pixels_vec| {
// do my rendering by reading pixels_vec
}));
use js_sys::Function;
use wasm_bindgen::JsValue;
fn convert_render_callback(render: Function) -> Box<Fn(&[u8])> {
Box::new(move |pixels: &[u8]| {
render
.call1(&JsValue::NULL, &to_uint8_array(pixels))
.expect("Error calling JS `render` function");
})
}
// standard_ffi.rs
use crate::pixel_board::PixelBoard;
#[no_mangle]
pub extern "C" fn create_board(fire_width: usize, fire_height: usize) -> *mut PixelBoard {
let pixel_board = PixelBoard::new(fire_width, fire_height);
let boxed_pixel_board = Box::new(pixel_board);
Box::into_raw(boxed_pixel_board)
}
#[no_mangle]
pub extern "C" fn create_fire_source(pixel_board: *mut PixelBoard) {
if pixel_board.is_null() {
return;
}
let pixel_board = unsafe { &mut *pixel_board };
pixel_board.create_fire_source();
}
#[no_mangle]
pub extern "C" fn free_board(pixel_board: *mut PixelBoard) {
unsafe {
Box::from_raw(pixel_board);
}
}
// android.rs
use jni::objects::JObject;
use jni::sys::{jint, jlong};
use jni::JNIEnv;
use crate::pixel_board::PixelBoard;
use crate::standard_ffi;
#[no_mangle]
pub extern "C" fn Java_com_otaviopace_doomfireandroid_DoomFire_createBoard(
_env: JNIEnv,
_this: JObject,
width: jint,
height: jint,
) -> jlong {
standard_ffi::create_board(width as usize, height as usize) as jlong
}
#[no_mangle]
pub extern "C" fn Java_com_otaviopace_doomfireandroid_DoomFire_createFireSource(
_env: JNIEnv,
_this: JObject,
board_ptr: jlong,
) {
standard_ffi::create_fire_source(board_ptr as *mut PixelBoard);
}
Marcela Zilliotto
Philipe Paixão
Pagar.me
Deivis Wingert
Mateus Moog
Leonam Dias
Rodrigo Melo
Allan Jorge
Kassiano
Murilo da Paixão
Filipe Deschamps