diff --git a/cvm-rs/Cargo.lock b/cvm-rs/Cargo.lock index 1bdf8c0..76f17f8 100644 --- a/cvm-rs/Cargo.lock +++ b/cvm-rs/Cargo.lock @@ -2,12 +2,57 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "cc" version = "1.0.99" @@ -29,6 +74,15 @@ dependencies = [ "cc", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -55,11 +109,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] -name = "cvm-rs" -version = "0.1.1" +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "cvm-rs" +version = "0.2.0" +dependencies = [ + "anyhow", "libc", - "neon", + "napi", + "napi-build", + "napi-derive", "once_cell", "rayon", "turbojpeg-sys", @@ -72,15 +139,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "getrandom" -version = "0.2.15" +name = "gimli" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "libc" @@ -99,29 +161,86 @@ dependencies = [ ] [[package]] -name = "neon" -version = "1.0.0" +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d75440242411c87dc39847b0e33e961ec1f10326a9d8ecf9c1ea64a3b3c13dc" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "getrandom", - "libloading", - "neon-macros", - "once_cell", - "semver", - "send_wrapper", - "smallvec", + "adler", ] [[package]] -name = "neon-macros" -version = "1.0.0" +name = "napi" +version = "2.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b" +checksum = "1277600d452e570cc83cf5f4e8efb389cc21e5cbefadcfba7239f4551e2e3e99" dependencies = [ + "anyhow", + "bitflags", + "ctor", + "napi-derive", + "napi-sys", + "once_cell", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a" + +[[package]] +name = "napi-derive" +version = "2.16.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "150d87c4440b9f4815cb454918db498b5aae9a57aa743d20783fe75381181d01" +dependencies = [ + "cfg-if", + "convert_case", + "napi-derive-backend", + "proc-macro2", "quote", "syn", - "syn-mid", +] + +[[package]] +name = "napi-derive-backend" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cd81b794fc1d6051acf8c4f3cb4f82833b0621272a232b4ff0cf3df1dbddb61" +dependencies = [ + "convert_case", + "once_cell", + "proc-macro2", + "quote", + "regex", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" +dependencies = [ + "libloading", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", ] [[package]] @@ -130,6 +249,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "pkg-config" version = "0.3.30" @@ -174,24 +299,47 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "syn" version = "2.0.66" @@ -204,14 +352,13 @@ dependencies = [ ] [[package]] -name = "syn-mid" -version = "0.6.0" +name = "tokio" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ - "proc-macro2", - "quote", - "syn", + "backtrace", + "pin-project-lite", ] [[package]] @@ -233,10 +380,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "unicode-segmentation" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "windows-targets" diff --git a/cvm-rs/Cargo.toml b/cvm-rs/Cargo.toml index 8ad26c2..29cbe50 100644 --- a/cvm-rs/Cargo.toml +++ b/cvm-rs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cvm-rs" description = "Rust utility library for cvmts. Runs all the high performance code" -version = "0.1.1" +version = "0.2.0" edition = "2021" exclude = ["index.node"] @@ -9,10 +9,17 @@ exclude = ["index.node"] crate-type = ["cdylib"] [dependencies] -neon = "1" libc = "0.2.155" # Required for JPEG once_cell = "1.19.0" turbojpeg-sys = "1.0.0" rayon = "1.10.0" + +# node sex +napi = { version = "2.16.9", features = [ "async", "napi8", "error_anyhow" ] } +napi-derive = "2.16.11" +anyhow = "1.0.86" + +[build-dependencies] +napi-build = "2.1.3" diff --git a/cvm-rs/index.js b/cvm-rs/index.js index fc02f40..3c3b5bd 100644 --- a/cvm-rs/index.js +++ b/cvm-rs/index.js @@ -2,5 +2,11 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); -export let {guacDecode, guacEncode, jpegEncode} = require('./index.node'); +let {guacDecode, guacEncodeImpl, jpegEncode} = require('./index.node'); +export { guacDecode, jpegEncode }; + +// shim for js->rust interop, because napi-rs kind of blows in this regard +export function guacEncode(...args) { + return guacEncodeImpl(args); +} diff --git a/cvm-rs/src/guac_js.rs b/cvm-rs/src/guac_js.rs index dde9690..30ef290 100644 --- a/cvm-rs/src/guac_js.rs +++ b/cvm-rs/src/guac_js.rs @@ -1,47 +1,20 @@ use crate::guac; -use neon::prelude::*; -fn guac_decode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsArray> { - let input = cx.argument::(0)?.value(cx); +use napi_derive::napi; +#[napi(js_name = "guacDecode")] +#[allow(unused)] +pub fn guac_decode(input: String) -> napi::anyhow::Result> { match guac::decode_instruction(&input) { - Ok(data) => { - let array = JsArray::new(cx, data.len()); + Ok(elements) => Ok(elements), - let conv = data - .iter() - .map(|v| cx.string(v)) - .collect::>>(); - - for (i, str) in conv.iter().enumerate() { - array.set(cx, i as u32, *str)?; - } - - return Ok(array); - } - - Err(e) => { - return cx.throw_error(format!("{}", e)); - } + Err(err) => Err(anyhow::anyhow!("Error decoding Guacamole frame: {}", err)), } } -fn guac_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsString> { - let mut elements: Vec = Vec::with_capacity(cx.len()); - - // Capture varadic arguments - for i in 0..cx.len() { - let input = cx.argument::(i)?.value(cx); - elements.push(input); - } - - Ok(cx.string(guac::encode_instruction(&elements))) -} - -pub fn guac_decode(mut cx: FunctionContext) -> JsResult { - guac_decode_impl(&mut cx) -} - -pub fn guac_encode(mut cx: FunctionContext) -> JsResult { - guac_encode_impl(&mut cx) +// ... this is ugly, but works +#[napi(js_name = "guacEncodeImpl")] +#[allow(unused)] +pub fn guac_encode(items: Vec) -> napi::anyhow::Result { + Ok(guac::encode_instruction(&items)) } diff --git a/cvm-rs/src/jpeg_js.rs b/cvm-rs/src/jpeg_js.rs index 8aac0b5..786db4a 100644 --- a/cvm-rs/src/jpeg_js.rs +++ b/cvm-rs/src/jpeg_js.rs @@ -1,7 +1,5 @@ -use std::sync::{Arc, Mutex}; - -use neon::prelude::*; -use neon::types::buffer::TypedArray; +use napi::Env; +use napi_derive::napi; use once_cell::sync::OnceCell; @@ -12,23 +10,22 @@ use rayon::{ThreadPool, ThreadPoolBuilder}; use crate::jpeg_compressor::*; /// Gives a Rayon thread pool we use for parallelism -fn rayon_pool<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static ThreadPool> { +fn rayon_pool() -> &'static ThreadPool { static RUNTIME: OnceCell = OnceCell::new(); - RUNTIME - .get_or_try_init(|| { - // spawn at least 4 threads - let mut nr_threads = std::thread::available_parallelism().expect("??").get() / 8; - if nr_threads == 0 { - nr_threads = 4; - } + RUNTIME.get_or_init(|| { + // spawn at least 4 threads + let mut nr_threads = std::thread::available_parallelism().expect("??").get() / 8; + if nr_threads == 0 { + nr_threads = 4; + } - ThreadPoolBuilder::new() - .num_threads(nr_threads) - .thread_name(|index| format!("cvmrs_jpeg_{}", index + 1)) - .build() - }) - .or_else(|err| cx.throw_error(&err.to_string())) + ThreadPoolBuilder::new() + .num_threads(nr_threads) + .thread_name(|index| format!("cvmrs_jpeg_{}", index + 1)) + .build() + .unwrap() + }) } thread_local! { @@ -38,43 +35,29 @@ thread_local! { // TODO: We should probably allow passing an array of images to encode, which would // increase parallelism heavily. -fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> { - let input = cx.argument::(0)?; +#[napi(object)] +pub struct JpegInputArgs { + pub width: u32, + pub height: u32, + pub stride: u32, + pub buffer: napi::JsBuffer, +} - // Get our input arguments here - let width: u64 = input.get::(cx, "width")?.value(cx) as u64; - let height: u64 = input.get::(cx, "height")?.value(cx) as u64; - let stride: u64 = input.get::(cx, "stride")?.value(cx) as u64; - let buffer: Handle = input.get(cx, "buffer")?; - - let (deferred, promise) = cx.promise(); - let channel = cx.channel(); - let pool = rayon_pool(cx)?; - - let buf = buffer.as_slice(cx); - - let copy: Arc>> = Arc::new(Mutex::new(Vec::with_capacity(buf.len()))); - - // Copy from the node buffer to our temporary buffer - { - let mut locked = copy.lock().unwrap(); - let cap = locked.capacity(); - locked.resize(cap, 0); - locked.copy_from_slice(buf); - } +#[napi(js_name = "jpegEncode")] +#[allow(unused)] +pub fn jpeg_encode(env: Env, input: JpegInputArgs) -> napi::Result { + let (deferred_resolver, promise) = env.create_deferred::()?; + let buf = input.buffer.into_ref()?; // Spawn a task on the rayon pool that encodes the JPEG and fufills the promise // once it is done encoding. - pool.spawn_fifo(move || { - let clone = Arc::clone(©); - let locked = clone.lock().unwrap(); - + rayon_pool().spawn_fifo(move || { let image: Image = Image { - buffer: locked.as_slice(), - width: width as u32, - height: height as u32, + buffer: &buf, + width: input.width as u32, + height: input.height as u32, - stride: (stride * 4u64) as u32, // I think? + stride: (input.stride as u64 * 4u64) as u32, format: turbojpeg_sys::TJPF_TJPF_RGBA, }; @@ -85,18 +68,13 @@ fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> b.compress_buffer(&image) }); - // Fulfill the Javascript promise with our encoded buffer - deferred.settle_with(&channel, move |mut cx| { - let mut buf = cx.buffer(vec.len())?; - let slice = buf.as_mut_slice(&mut cx); - slice.copy_from_slice(vec.as_slice()); - Ok(buf) + deferred_resolver.resolve(move |env| { + let buffer = env + .create_buffer_with_data(vec) + .expect("Couldn't create node Buffer, things are probably very broken by this point"); + Ok(buffer.into_unknown()) }); }); Ok(promise) } - -pub fn jpeg_encode(mut cx: FunctionContext) -> JsResult { - jpeg_encode_impl(&mut cx) -} diff --git a/cvm-rs/src/lib.rs b/cvm-rs/src/lib.rs index b96db56..a697a22 100644 --- a/cvm-rs/src/lib.rs +++ b/cvm-rs/src/lib.rs @@ -3,15 +3,3 @@ mod guac_js; mod jpeg_compressor; mod jpeg_js; - -use neon::prelude::*; - -#[neon::main] -fn main(mut cx: ModuleContext) -> NeonResult<()> { - // Mostly transitionary, later on API should change - cx.export_function("jpegEncode", jpeg_js::jpeg_encode)?; - - cx.export_function("guacDecode", guac_js::guac_decode)?; - cx.export_function("guacEncode", guac_js::guac_encode)?; - Ok(()) -}