Skip to content

Commit

Permalink
Merge pull request #407 from richarddd/chore/typed-array-optimization
Browse files Browse the repository at this point in the history
Use new APIs from QJS to create and lookup typed arrays
  • Loading branch information
DelSkayn authored Jan 17, 2025
2 parents ff980e2 + ce24343 commit 05bc424
Show file tree
Hide file tree
Showing 10 changed files with 904 additions and 780 deletions.
17 changes: 8 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
- name: Documentation
env:
DOCS_RS: 1
run: cargo doc --no-deps --features full-async,parallel,doc-cfg
run: cargo doc --no-deps --features full-async,parallel,doc-cfg,bindgen
- name: Upload docs
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
path: target
key: ${{ runner.os }}-build-rust_nightly-check-${{ hashFiles('**/Cargo.lock') }}
- name: Cargo clippy
run: cargo clippy --all --all-targets --features full-async
run: cargo clippy --all --all-targets --features full-async,bindgen

msrv:
# Check to see if rquickjs builds on minimal supported Rust version.
Expand Down Expand Up @@ -131,7 +131,7 @@ jobs:
if: hashFiles('Cargo.lock') == ''
run: cargo generate-lockfile
- name: cargo llvm-cov
run: cargo llvm-cov --locked --no-default-features --features full-async,compile-tests --workspace --lcov --output-path lcov.info
run: cargo llvm-cov --locked --no-default-features --features full-async,compile-tests,bindgen --workspace --lcov --output-path lcov.info
- name: Record Rust version
run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV"
- name: Upload to codecov.io
Expand All @@ -157,12 +157,12 @@ jobs:
os: ubuntu-latest
rust: stable
target: i686-unknown-linux-gnu
features: full-async
features: full-async
- task: bindings
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-gnu
features: full-async
features: full-async
- task: bindings
os: macos-latest
rust: stable
Expand All @@ -184,7 +184,7 @@ jobs:
rust: stable
target: x86_64-pc-windows-msvc
features: full-async
- task: test
- task: test
os: windows-latest
rust: stable
target: x86_64-pc-windows-msvc
Expand Down Expand Up @@ -223,7 +223,7 @@ jobs:
no-build: true
- task: bindings
os: ubuntu-latest
rust: nightly
rust: nightly
target: wasm32-wasip2
features: full-async
no-build: true
Expand All @@ -237,7 +237,7 @@ jobs:
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-gnu
features: loader
features: loader
- task: features
os: ubuntu-latest
rust: stable
Expand Down Expand Up @@ -437,7 +437,6 @@ jobs:
RUST_BACKTRACE: full
run: cargo test ${{ matrix.optimization && '--release' || '' }} --all --target ${{ matrix.target }} --no-default-features --features ${{ matrix.features }}


update-bindings:
if: ${{ github.event_name != 'pull_request' && !startsWith(github.ref, 'refs/tags/') }}
needs:
Expand Down
18 changes: 14 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ members = [
[workspace.dependencies]
rquickjs-core = { version = "0.8.1", path = "core" }
rquickjs-macro = { version = "0.8.1", path = "macro" }
rquickjs-sys= { version = "0.8.1", path = "sys" }
rquickjs-sys = { version = "0.8.1", path = "sys" }
rquickjs = { version = "0.8.1", path = "./" }

[dependencies]
Expand All @@ -35,12 +35,23 @@ rquickjs-core = { workspace = true }
rquickjs-macro = { workspace = true, optional = true }



[features]
default = ["classes", "properties"]

# Almost all features excluding "parallel" and support for async runtimes
full = ["chrono", "loader", "allocator", "dyn-load", "either", "indexmap", "classes", "properties", "array-buffer", "macro", "phf"]
full = [
"chrono",
"loader",
"allocator",
"dyn-load",
"either",
"indexmap",
"classes",
"properties",
"array-buffer",
"macro",
"phf",
]

# Almost all features excluding "parallel"
full-async = ["full", "futures"]
Expand Down Expand Up @@ -122,4 +133,3 @@ trybuild = "1.0.82"

[package.metadata.docs.rs]
features = ["full-async", "parallel", "doc-cfg"]

3 changes: 1 addition & 2 deletions core/src/value/array_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ impl<'js> ArrayBuffer<'js> {
capacity as _,
0,
);
ctx.handle_exception(val).map_err(|error| {
ctx.handle_exception(val).inspect_err(|_| {
// don't forget to free data when error occurred
Vec::from_raw_parts(ptr, capacity, capacity);
error
})?;
Value::from_js_value(ctx, val)
})))
Expand Down
67 changes: 39 additions & 28 deletions core/src/value/typed_array.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
atom::PredefinedAtom, qjs, ArrayBuffer, Ctx, Error, FromJs, Function, IntoJs, JsLifetime,
Object, Result, Value,
atom::PredefinedAtom, qjs, ArrayBuffer, Ctx, Error, FromJs, IntoJs, JsLifetime, Object, Result,
Value,
};
use std::{
fmt,
Expand All @@ -11,34 +11,38 @@ use std::{
slice,
};

use super::{array_buffer::RawArrayBuffer, Constructor};
use super::array_buffer::RawArrayBuffer;

/// The trait which implements types which capable to be TypedArray items
///
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "array-buffer")))]
pub trait TypedArrayItem: Copy {
const CLASS_NAME: PredefinedAtom;
const ARRAY_TYPE: qjs::JSTypedArrayEnum;
}

macro_rules! typedarray_items {
($($name:ident: $type:ty,)*) => {
($($name:ident: $type:ty, $array_type:expr,)*) => {
$(impl TypedArrayItem for $type {
const CLASS_NAME: PredefinedAtom = PredefinedAtom::$name;
const ARRAY_TYPE: qjs::JSTypedArrayEnum = $array_type;
})*
};
}

typedarray_items! {
Int8Array: i8,
Uint8Array: u8,
Int16Array: i16,
Uint16Array: u16,
Int32Array: i32,
Uint32Array: u32,
Float32Array: f32,
Float64Array: f64,
BigInt64Array: i64,
BigUint64Array: u64,
Int8Array: i8, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_INT8,
Uint8Array: u8, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_UINT8,
Int16Array: i16, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_INT16,
Uint16Array: u16, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_UINT16,
Int32Array: i32, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_INT32,
Uint32Array: u32, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_UINT32,
// XXX introduce when f16 is
// Float16Array: f16, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_FLOAT16,
Float32Array: f32, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_FLOAT32,
Float64Array: f64, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_FLOAT64,
BigInt64Array: i64, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_BIG_INT64,
BigUint64Array: u64, qjs::JSTypedArrayEnum_JS_TYPED_ARRAY_BIG_UINT64,
}

/// Rust representation of a JavaScript objects of TypedArray classes.
Expand Down Expand Up @@ -155,12 +159,10 @@ impl<'js, T> TypedArray<'js, T> {
where
T: TypedArrayItem,
{
let class: Function = object.ctx.globals().get(T::CLASS_NAME)?;
if object.is_instance_of(class) {
Ok(Self(object, PhantomData))
} else {
Err(Error::new_from_js("object", T::CLASS_NAME.to_str()))
if object.is_typed_array::<T>() {
return Ok(Self(object, PhantomData));
}
Err(Error::new_from_js("object", T::CLASS_NAME.to_str()))
}

/// Returns the underlying bytes of the buffer,
Expand Down Expand Up @@ -195,8 +197,20 @@ impl<'js, T> TypedArray<'js, T> {
T: TypedArrayItem,
{
let ctx = &arraybuffer.0.ctx;
let ctor: Constructor = ctx.globals().get(T::CLASS_NAME)?;
ctor.construct((arraybuffer,))

let val = unsafe {
let mut argv: [qjs::JSValue; 3] =
[arraybuffer.0.as_raw(), qjs::JS_UNDEFINED, qjs::JS_UNDEFINED];
let val = qjs::JS_NewTypedArray(
ctx.as_ptr(),
argv.len() as i32,
argv.as_mut_ptr(),
T::ARRAY_TYPE,
);
ctx.handle_exception(val)?;
Value::from_js_value(ctx.clone(), val)
};
Ok(Self(Object(val), PhantomData))
}

pub(crate) fn get_raw_bytes(val: &Value<'js>) -> Option<(usize, usize, NonNull<u8>)> {
Expand Down Expand Up @@ -309,12 +323,8 @@ impl<'js, T> IntoJs<'js> for TypedArray<'js, T> {

impl<'js> Object<'js> {
pub fn is_typed_array<T: TypedArrayItem>(&self) -> bool {
// This should not error unless the global ArrayBuffer object suddenly isn't a Function
// anymore.
let Ok(class) = self.ctx.globals().get::<_, Function>(T::CLASS_NAME) else {
return false;
};
self.is_instance_of(class)
let array_type = unsafe { qjs::JS_GetTypedArrayType(self.value) };
T::ARRAY_TYPE == array_type.try_into().unwrap()
}

/// Interpret as [`TypedArray`]
Expand All @@ -325,7 +335,7 @@ impl<'js> Object<'js> {
mem::transmute(self)
}

pub fn as_typed_array<'a, T: TypedArrayItem>(&self) -> Option<&TypedArray<'js, T>> {
pub fn as_typed_array<T: TypedArrayItem>(&self) -> Option<&TypedArray<'js, T>> {
self.is_typed_array::<T>()
.then(|| unsafe { self.ref_typed_array() })
}
Expand Down Expand Up @@ -358,6 +368,7 @@ mod test {
let res: i8 = ctx
.eval(
r#"
v.length
v.length != 4 ? 1 :
v[0] != -1 ? 2 :
v[1] != 0 ? 3 :
Expand Down
25 changes: 23 additions & 2 deletions sys/src/bindings/aarch64-apple-darwin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub const JS_PROP_NO_ADD: u32 = 65536;
pub const JS_PROP_NO_EXOTIC: u32 = 131072;
pub const JS_PROP_DEFINE_PROPERTY: u32 = 262144;
pub const JS_PROP_REFLECT_DEFINE_PROPERTY: u32 = 524288;
pub const JS_DEFAULT_STACK_SIZE: u32 = 262144;
pub const JS_DEFAULT_STACK_SIZE: u32 = 1048576;
pub const JS_EVAL_TYPE_GLOBAL: u32 = 0;
pub const JS_EVAL_TYPE_MODULE: u32 = 1;
pub const JS_EVAL_TYPE_DIRECT: u32 = 2;
Expand Down Expand Up @@ -1733,6 +1733,27 @@ extern "C" {
extern "C" {
pub fn JS_GetUint8Array(ctx: *mut JSContext, psize: *mut size_t, obj: JSValue) -> *mut u8;
}
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_UINT8C: JSTypedArrayEnum = 0;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_INT8: JSTypedArrayEnum = 1;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_UINT8: JSTypedArrayEnum = 2;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_INT16: JSTypedArrayEnum = 3;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_UINT16: JSTypedArrayEnum = 4;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_INT32: JSTypedArrayEnum = 5;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_UINT32: JSTypedArrayEnum = 6;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_BIG_INT64: JSTypedArrayEnum = 7;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_BIG_UINT64: JSTypedArrayEnum = 8;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_FLOAT16: JSTypedArrayEnum = 9;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_FLOAT32: JSTypedArrayEnum = 10;
pub const JSTypedArrayEnum_JS_TYPED_ARRAY_FLOAT64: JSTypedArrayEnum = 11;
pub type JSTypedArrayEnum = ::std::os::raw::c_uint;
extern "C" {
pub fn JS_NewTypedArray(
ctx: *mut JSContext,
argc: ::std::os::raw::c_int,
argv: *mut JSValue,
array_type: JSTypedArrayEnum,
) -> JSValue;
}
extern "C" {
pub fn JS_GetTypedArrayBuffer(
ctx: *mut JSContext,
Expand All @@ -1753,7 +1774,7 @@ extern "C" {
) -> JSValue;
}
extern "C" {
pub fn JS_IsUint8Array(obj: JSValue) -> ::std::os::raw::c_int;
pub fn JS_GetTypedArrayType(obj: JSValue) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn JS_NewUint8ArrayCopy(ctx: *mut JSContext, buf: *const u8, len: size_t) -> JSValue;
Expand Down
Loading

0 comments on commit 05bc424

Please sign in to comment.