-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lib(rust): Draft initial implementation of Rust binding
TODO: * Re-export function that compatible with C ABI without +multivalue return * Implement buffer address allocation to store temporary computation + Buffer address should be reserved at the end of wasm linear memory on every computation + Computation should be eager so buffer always ready to be used (akin to __stack_pointer frame)
- Loading branch information
Showing
5 changed files
with
224 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "libnusa" | ||
version = "0.0.1-Jan.2023" | ||
edition = "2021" | ||
repository = "https://github.com/DrSensor/nusa/tree/main/libs/rust" | ||
keywords = ["webdev", "wasm", "no_std", "nusa"] | ||
categories = ["web-programming", "api-bindings", "no-std::no-alloc", "wasm"] | ||
license = "MIT" |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use crate::{host, number, types, Series}; | ||
use core::ops; | ||
use std::primitive; | ||
use types::number::{JSNumber, Type}; | ||
|
||
#[repr(transparent)] | ||
pub struct All<T: Series>(T); | ||
|
||
pub fn for_all<T, F, E, const N: usize>(from: F, exec: E) | ||
where | ||
T: Series, | ||
F: Into<[T; N]>, | ||
E: Fn([All<T>; N]), | ||
{ | ||
exec(from.into().map(All)) | ||
} | ||
|
||
macro_rules! bridge { | ||
($ty:ident, $Ty:ident) => { | ||
impl ops::AddAssign<primitive::$ty> for All<number::$ty> { | ||
fn add_assign(&mut self, rhs: primitive::$ty) { | ||
let All(data) = self; | ||
unsafe { | ||
host::num::mutate::addVAL( | ||
Type::$Ty, | ||
data.len(), | ||
false, | ||
data.ptr().into(), | ||
rhs as JSNumber, | ||
) | ||
} | ||
} | ||
} | ||
|
||
// TODO: is this correct? | ||
impl ops::Add<primitive::$ty> for All<number::$ty> { | ||
type Output = host::BufAddr; | ||
fn add(self, rhs: primitive::$ty) -> Self::Output { | ||
let All(data) = self; | ||
unsafe { | ||
host::num::compute::addVAL( | ||
Type::$Ty, | ||
data.len(), | ||
false, | ||
data.ptr().into(), | ||
rhs as JSNumber, | ||
) as Self::Output | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
bridge!(u8, Uint8); | ||
bridge!(i8, Int8); | ||
|
||
bridge!(u16, Uint16); | ||
bridge!(i16, Int16); | ||
|
||
bridge!(u32, Uint32); | ||
bridge!(i32, Int32); | ||
bridge!(f32, Float32); | ||
|
||
bridge!(u64, Uint64); | ||
bridge!(i64, Int64); | ||
bridge!(f64, Float64); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
pub mod iter; | ||
pub mod number; | ||
mod types; | ||
|
||
trait Accessor<T> { | ||
fn set(self, value: T); | ||
fn get(self) -> T; | ||
} | ||
|
||
pub trait Series { | ||
type As; | ||
fn ptr(&self) -> *const Self::As; | ||
fn allocate(len: host::Size) -> (usize, usize); | ||
fn len(&self) -> host::Size; | ||
fn is_empty(&self) -> bool { | ||
self.len() == 0 | ||
} | ||
} | ||
|
||
mod host { | ||
pub type Size = u16; | ||
pub type BufAddr = usize; // buffer to store temporary array from all `host::*::compute` module 🤔 | ||
|
||
pub mod scope { | ||
#[link(wasm_import_module = "nusa")] | ||
extern "C" { | ||
#[link_name = "scope.size"] | ||
pub fn size() -> u16; | ||
} | ||
} | ||
|
||
pub mod num { | ||
use crate::types::{number::Type, JSNumber, Null, Number}; | ||
pub type Setter = extern "C" fn(Number, JSNumber); | ||
pub type Getter = extern "C" fn(Number) -> JSNumber; | ||
|
||
#[link(wasm_import_module = "nusa")] | ||
#[allow(improper_ctypes)] | ||
extern "C" { | ||
#[link_name = "num.allocate"] | ||
pub fn allocate(ty: Type, len: u16, nullable: bool) -> (Number, Null); | ||
#[link_name = "num.accessor"] | ||
pub fn accessor(ty: Type) -> (Getter, Setter); | ||
} | ||
|
||
#[link(wasm_import_module = "nusa")] | ||
extern "C" { | ||
#[link_name = "num.set"] | ||
pub fn set(setter: Setter, ptr: usize, value: JSNumber); | ||
#[link_name = "num.get"] | ||
pub fn get(getter: Getter, ptr: usize) -> JSNumber; | ||
} | ||
|
||
pub mod mutate { | ||
use super::*; | ||
|
||
#[link(wasm_import_module = "nusa")] | ||
extern "C" { | ||
#[link_name = "num.bulk.mut.addVAL"] | ||
pub fn addVAL(ty: Type, len: u16, nullable: bool, this: Number, val: JSNumber); | ||
} | ||
} | ||
|
||
pub mod compute { | ||
use super::*; | ||
use crate::host; | ||
|
||
#[link(wasm_import_module = "nusa")] | ||
extern "C" { | ||
#[link_name = "num.bulk.calc.addVAL"] // TODO | ||
pub fn addVAL( | ||
ty: Type, | ||
len: u16, | ||
nullable: bool, | ||
this: Number, | ||
val: JSNumber, | ||
) -> host::BufAddr; | ||
} | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use crate::{host, types, Accessor, Series}; | ||
use core::primitive; | ||
use types::number::{JSNumber, Type}; | ||
|
||
macro_rules! bridge { | ||
($ty:ident, $Ty:ident) => { | ||
#[allow(non_camel_case_types)] | ||
pub struct $ty { | ||
len: host::Size, | ||
addr: usize, | ||
accr: (host::num::Getter, host::num::Setter), | ||
} | ||
|
||
impl Series for self::$ty { | ||
type As = primitive::$ty; | ||
|
||
fn ptr(&self) -> *const primitive::$ty { | ||
self.addr as *const primitive::$ty | ||
} | ||
|
||
fn allocate(len: host::Size) -> (usize, usize) { | ||
let (number, null) = unsafe { host::num::allocate(Type::$Ty, len, false) }; | ||
(number.addr, null.addr) | ||
} | ||
|
||
fn len(&self) -> host::Size { | ||
self.len | ||
} | ||
} | ||
|
||
impl self::$ty { | ||
#[allow(clippy::new_without_default)] | ||
pub fn new() -> Self { | ||
let len = unsafe { host::scope::size() }; | ||
let (addr, _) = Self::allocate(len); | ||
let (getter, setter) = unsafe { host::num::accessor(Type::$Ty) }; | ||
let accr = (getter, setter); | ||
$ty { len, addr, accr } | ||
} | ||
} | ||
|
||
impl Accessor<primitive::$ty> for self::$ty { | ||
fn set(self, value: primitive::$ty) { | ||
let (_, setter) = self.accr; | ||
unsafe { host::num::set(setter, self.addr, value as JSNumber) } | ||
} | ||
|
||
fn get(self) -> primitive::$ty { | ||
let (getter, _) = self.accr; | ||
unsafe { host::num::get(getter, self.addr) as primitive::$ty } | ||
} | ||
} | ||
}; | ||
} | ||
|
||
bridge!(u8, Uint8); | ||
bridge!(i8, Int8); | ||
|
||
bridge!(u16, Uint16); | ||
bridge!(i16, Int16); | ||
|
||
bridge!(u32, Uint32); | ||
bridge!(i32, Int32); | ||
bridge!(f32, Float32); | ||
|
||
bridge!(u64, Uint64); | ||
bridge!(i64, Int64); | ||
bridge!(f64, Float64); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../core/wasm/types |