Skip to content

Instantly share code, notes, and snippets.

@thomas-jeepe
Last active August 24, 2017 21:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomas-jeepe/3eabc0c1d245b5604099ff2b96d2ddcb to your computer and use it in GitHub Desktop.
Save thomas-jeepe/3eabc0c1d245b5604099ff2b96d2ddcb to your computer and use it in GitHub Desktop.
extern crate libc;
use std::ffi::{CString, CStr};
use std::mem;
trait Interop {
fn as_int(self, _: &mut Vec<CString>) -> libc::c_int;
}
impl Interop for i32 {
fn as_int(self, _: &mut Vec<CString>) -> libc::c_int {
return self;
}
}
impl Interop for Vec<u8> {
fn as_int(self, _: &mut Vec<CString>) -> libc::c_int {
JsBytes::new(self) as libc::c_int
}
}
impl<'a> Interop for &'a str {
fn as_int(self, arena: &mut Vec<CString>) -> libc::c_int {
let c = CString::new(self).unwrap();
let ret = c.as_ptr() as libc::c_int;
arena.push(c);
return ret;
}
}
impl<'a> Interop for *const libc::c_void {
fn as_int(self, _: &mut Vec<CString>) -> libc::c_int {
return self as libc::c_int;
}
}
#[macro_export]
macro_rules! js {
( ($( $x:expr ),*) $y:expr ) => {
{
let mut arena:Vec<CString> = Vec::new();
const LOCAL: &'static [u8] = $y;
unsafe { emscripten_asm_const_int(&LOCAL[0] as *const _ as *const libc::c_char, $(Interop::as_int($x, &mut arena)),*) }
}
};
( $y:expr ) => {
{
const LOCAL: &'static [u8] = $y;
unsafe { emscripten_asm_const_int(&LOCAL[0] as *const _ as *const libc::c_char) }
}
};
}
extern "C" {
pub fn emscripten_asm_con(s: *const libc::c_char);
pub fn emscripten_asm_const(s: *const libc::c_char);
pub fn emscripten_asm_const_int(s: *const libc::c_char, ...) -> libc::c_int;
pub fn emscripten_pause_main_loop();
pub fn emscripten_set_main_loop(m: extern fn(), fps: libc::c_int, infinite: libc::c_int);
}
/// Struct that can be used to store a vector of bytes and sent
/// to JS
#[repr(C)]
#[derive(Debug)]
pub struct JsBytes {
ptr: u32,
len: u32,
cap: u32,
}
impl JsBytes {
/// Create a new `JsBytes` wrapper consuming the bytes
pub fn new(mut bytes: Vec<u8>) -> *mut JsBytes {
let ptr = bytes.as_mut_ptr() as u32;
let len = bytes.len() as u32;
let cap = bytes.capacity() as u32;
mem::forget(bytes);
let boxed = Box::new(JsBytes { ptr, len, cap });
Box::into_raw(boxed)
}
}
/// Drop the vector of bytes. Should be called from JS
#[no_mangle]
pub fn drop_bytes(ptr: *mut JsBytes) {
unsafe {
let boxed: Box<JsBytes> = Box::from_raw(ptr);
Vec::from_raw_parts(boxed.ptr as *mut u8, boxed.len as usize, boxed.cap as usize);
}
}
struct Idb {}
fn no_args_caller<F: Fn()>(f: *const libc::c_char) {
let f: &mut F = unsafe { mem::transmute(f) };
f()
}
impl Idb {
fn new<F: Fn(), G: Fn()>(on_success: F, on_error: G) {
let on_success = &*Box::new(on_success) as *const _ as *const libc::c_void;
let on_error = &*Box::new(on_error) as *const _ as *const libc::c_void;
let on_success_caller = no_args_caller::<F> as *const libc::c_void;
let on_error_caller = no_args_caller::<G> as *const libc::c_void;
js! {(on_success_caller, on_success, on_error_caller, on_error) b"\
var req = indexedDB.open('macaw');\
req.onerror = function () {\
Runtime.dynCall('vi', $2, [$3]);\
};\
req.onsuccess = function (ev) {\
__idb = req.result;\
Runtime.dynCall('vi', $0, [$1]);\
};\
req.onupgradeneeded = function (ev) {\
var db = this.result;\
db.createObjectStore('count', {});\
};\
\0"};
}
fn put(store: &str, key: &str, val: Vec<u8>) {
js! {(store, key, val) b"\
var store = UTF8ToString($0);\
var transaction = __idb.transaction(store, 'readwrite');\
var store = transaction.objectStore(store);\
var ptr = Module.HEAPU32[$2 / 4];\
var len = Module.HEAPU32[$2 / 4 + 1];\
var val = Module.HEAPU8.subarray(ptr, ptr + len);\
store.put(val, UTF8ToString($1));\
Module._drop_bytes($2);\
\0"};
}
}
fn init() {
js! { b"\
self.ws = {};\
var __idb;\
\0"};
}
fn on_message_caller<F: Fn(String)>(f: *const libc::c_void, msg: *const libc::c_char) {
let f: &mut F = unsafe { mem::transmute(f) };
let msg = unsafe { CStr::from_ptr(msg) };
f(msg.to_string_lossy().into_owned())
}
fn on_message<F: Fn(String)>(f: F) {
let f = &*Box::new(f) as *const _ as *const libc::c_void;
js! { (f, on_message_caller::<F> as *const libc::c_void) b"\
self.onmessage = function (e) {\
var msg = JSON.stringify(e.data);\
var len = lengthBytesUTF8(msg);\
var buffer = Module._malloc(len);\
Module.stringToUTF8(msg, buffer, len + 1);\
Runtime.dynCall('vii', $1, [$0, buffer]);\
};\
\0"};
}
fn main() {
init();
Idb::new(|| {
js! { b"\
console.log('success');\
\0"};
Idb::put("count", "key", vec![1,2,3]);
}, || {
js! { b"\
console.log('failure');\
\0"};
});
on_message(|v| {
let v: &str = &v;
js! { (v) b"\
console.log(UTF8ToString($0));\
\0" };
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment