Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

kaffeerost

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

kaffeerost

Experimental utilities for integrating Rust with WebAssembly

  • 0.0.1
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

WASM experiments

Some experiments after seeing Rust PR #45905.

Getting Rust to compile to WASM

It's quite easy now! Assuming you've installed Rust with rustup (highly recommended), it's just:

rustup update
rustup target add wasm32-unknown-unknown --toolchain nightly
rustc +nightly --target wasm32-unknown-unknown -O hello.rs

Using the wrap function

While this repo first started as a playground, over time, a generic wrapper tool was developed. Its goal is to allow you to easily call Rust functions in WASM from JS.

Let's see how we can use it to handle some trivial functions.

Boilerplate

First, we'll need a way to load Rust code as WASM:

const loadRust = require("../src/compile");

You can also load your own .wasm file, of course. Next, the wrapper helper:

const wrap = require("../src/wrap");

We'll be calling this a lot.

And, to test that this actually works (yes, this code is tested), let's import the handy ava library.

const { test } = require("ava");

Easy as 1 2 3

Okay, we should be good to go. Let's see if this works:

test("it works!", async (t) => {
    const instance = await loadRust(`
        #[no_mangle]
        pub fn is_answer(a: i32) -> bool {
            a == 42
        }
    `);
    const isAnswer = wrap(instance.exports, "is_answer", ["i32"], "bool");
    t.deepEqual(isAnswer(42), true);
});

Looking good! The thing with this wrapper tool is that it needs to be able to convert between Rust and JS types. Primitives like integers are easy, but stuff like strings or arrays are a bit more complicated.

A-B-C? Actually not that easy

Let's try to write a function that returns a Rust String:

test("get the time", async (t) => {
    const instance = await loadRust(`
        #[no_mangle]
        pub extern "C" fn time() -> String {
            String::from("Beer o'clock")
        }
    `);
    const time = wrap(instance.exports, "time", [], "String");

Surprisingly, we cannot wrap this! The error message will tell you why:

    const error = await t.throws(() => {
        const now = time();
    });
    t.is(error.message, "Ensure failed: You need to export an `alloc` function to get strings from WASM");
});

So, to get a Rust String (or any other non-primitive type basically), we need to allocate memory. And not even to store the string itself (which Rust stores on the heap), just to be able to tell the WASM function where to write the stack data of the String to (It's passed as an out paramter as a String needs three words of storage).

Let's give this another go:

test("really get the time", async (t) => {
    const instance = await loadRust(`
        use std::mem;
        use std::os::raw::c_void;

        #[no_mangle]
        pub extern "C" fn alloc(size: usize) -> *mut c_void {
            let mut buf = Vec::with_capacity(size);
            let ptr = buf.as_mut_ptr();
            mem::forget(buf); // This is JS' responsibility now
            return ptr as *mut c_void;
        }

        #[no_mangle]
        pub extern "C" fn time() -> String {
            String::from("Beer o'clock")
        }
    `);
    const time = wrap(instance.exports, "time", [], "String");
    t.deepEqual(time(), "Beer o'clock");
});

For now, this alloc export is something you'll just have to do. Sorry. It'll get better, I promise. (Deallocating memory is still an open issue -- chime in here!)

Writing code yourself is way to hard, let's just use Cargo

Okay, how about we try to do something useful? Like, I don't know, calculate a SHA1 has for some bytes. Do you know how to write implement SHA1 by hand? Well, me neither. So, let's use a crate (Rust library)! Here, I found one!

How to use it? Let's split this up a bit. Here is the Rust code to load the sha1 crate, use it to munch some string slice, and return a fancy String that contains the human-readable hash.

test("digest some yummy bytes", async (t) => {
    const rustCode = `
        extern crate sha1;
        use sha1::Sha1;

        #[no_mangle]
        pub extern "C" fn digest(data: &str) -> String {
            let mut m = Sha1::new();
            m.update(data.as_bytes());
            m.digest().to_string()
        }

Oh, and don't forget the alloc dance:

        use std::mem;
        use std::os::raw::c_void;

        #[no_mangle]
        pub extern "C" fn alloc(size: usize) -> *mut c_void {
            let mut buf = Vec::with_capacity(size);
            let ptr = buf.as_mut_ptr();
            mem::forget(buf); // This is JS' responsibility now
            return ptr as *mut c_void;
        }
    `;

and then we can compile this to WASM with a newly added list of dependencies (the format is { name: "version" }):

    const instance = await loadRust(rustCode, { sha1: "0.2" });

Et voilá, we can SHA1 all the strings now!

    const digest = wrap(instance.exports, "digest", ["&str"], "String");
    t.deepEqual(digest("foobar"), "8843d7f92416211de9ebb963ff4ce28125932878");
});

Debugging

You can enable more logging output by setting the environment variable LOG_LEVEL. The levels are error (0), warn, info, debug, trace (5). The default is 2 (info).

FAQs

Package last updated on 13 Dec 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc