diff --git a/libs/rust/Cargo.toml b/libs/rust/Cargo.toml new file mode 100644 index 0000000..634ae6b --- /dev/null +++ b/libs/rust/Cargo.toml @@ -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" diff --git a/libs/rust/src/iter.rs b/libs/rust/src/iter.rs new file mode 100644 index 0000000..f8a9969 --- /dev/null +++ b/libs/rust/src/iter.rs @@ -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); + +pub fn for_all(from: F, exec: E) +where + T: Series, + F: Into<[T; N]>, + E: Fn([All; N]), +{ + exec(from.into().map(All)) +} + +macro_rules! bridge { + ($ty:ident, $Ty:ident) => { + impl ops::AddAssign for All { + 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 for All { + 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); diff --git a/libs/rust/src/lib.rs b/libs/rust/src/lib.rs new file mode 100644 index 0000000..47b5474 --- /dev/null +++ b/libs/rust/src/lib.rs @@ -0,0 +1,81 @@ +pub mod iter; +pub mod number; +mod types; + +trait Accessor { + 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; + } + } + } +} diff --git a/libs/rust/src/number.rs b/libs/rust/src/number.rs new file mode 100644 index 0000000..e488715 --- /dev/null +++ b/libs/rust/src/number.rs @@ -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 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); diff --git a/libs/rust/src/types b/libs/rust/src/types new file mode 120000 index 0000000..22793c8 --- /dev/null +++ b/libs/rust/src/types @@ -0,0 +1 @@ +../../../core/wasm/types \ No newline at end of file