Last active
August 24, 2017 21:12
-
-
Save thomas-jeepe/3eabc0c1d245b5604099ff2b96d2ddcb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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