Skip to content

Commit

Permalink
Add conversions between typed arrays and Rust
Browse files Browse the repository at this point in the history
For all typed arrays, this commit adds:

* `TypedArray::view(src: &[Type])`
* `TypedArray::copy_to(&self, dst: &mut [Type])`

The `view` function is unsafe because it doesn't provide any guarantees
about lifetimes or mutability. The `copy_to` function is, however, safe.

Closes rustwasm#811
  • Loading branch information
alexcrichton committed Jan 4, 2019
1 parent 1758c8d commit 2d7f601
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
112 changes: 112 additions & 0 deletions crates/js-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Float32Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Float32Array, src: &JsValue, offset: u32);
}

// Float64Array
Expand Down Expand Up @@ -938,6 +943,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Float64Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Float64Array, src: &JsValue, offset: u32);
}

// Function
Expand Down Expand Up @@ -1157,6 +1167,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Int8Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Int8Array, src: &JsValue, offset: u32);
}

// Int16Array
Expand Down Expand Up @@ -1241,6 +1256,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Int16Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Int16Array, src: &JsValue, offset: u32);
}

// Int32Array
Expand Down Expand Up @@ -1325,6 +1345,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Int32Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Int32Array, src: &JsValue, offset: u32);
}

// Map
Expand Down Expand Up @@ -3093,6 +3118,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Uint8Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Uint8Array, src: &JsValue, offset: u32);
}

// Uint8ClampedArray
Expand Down Expand Up @@ -3179,6 +3209,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Uint8ClampedArray) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Uint8ClampedArray, src: &JsValue, offset: u32);
}

// Uint16Array
Expand Down Expand Up @@ -3263,6 +3298,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Uint16Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Uint16Array, src: &JsValue, offset: u32);
}

// Uint32Array
Expand Down Expand Up @@ -3347,6 +3387,11 @@ extern "C" {
/// typed array from the start of its `ArrayBuffer`.
#[wasm_bindgen(method, getter, js_name = byteOffset)]
pub fn byte_offset(this: &Uint32Array) -> u32;

/// The `set()` method stores multiple values in the typed array, reading
/// input values from a specified array.
#[wasm_bindgen(method)]
pub fn set(this: &Uint32Array, src: &JsValue, offset: u32);
}

// URIError
Expand Down Expand Up @@ -4742,3 +4787,70 @@ pub fn global() -> Object {

GLOBAL.with(|g| g.clone())
}

macro_rules! arrays {
($($name:ident: $ty:ident,)*) => ($(
impl $name {
/// Creates a JS typed array which is a few into wasm's linear
/// memory at the slice specified.
///
/// This function returns a new typed array which is a view into
/// wasm's memory. This view does not copy the underlying data.
///
/// # Unsafety
///
/// Views into WebAssembly memory are only valid so long as the
/// backing buffer isn't resized in JS. Once this function is called
/// any future calls to `Box::new` (or malloc of any form) may cause
/// the returned value here to be invalidated. Use with caution!
///
/// Additionally the returned object can be safely mutated but the
/// input slice isn't guaranteed to be mutable.
///
/// Finally, the returned objet is disconnected from the input
/// slice's lifetime, so there's no guarantee that the data is read
/// at the right time.
pub unsafe fn view(rust: &[$ty]) -> $name {
let buf = wasm_bindgen::memory();
let mem = buf.unchecked_ref::<WebAssembly::Memory>();
$name::new_with_byte_offset_and_length(
&mem.buffer(),
rust.as_ptr() as u32,
rust.len() as u32,
)
}

/// Copy the contents of this JS typed array into the destination
/// Rust slice.
///
/// This function will efficiently copy the memory from a typed
/// array into this wasm module's own linear memory, initializing
/// the memory destination provided.
///
/// # Panics
///
/// This function will panic if this typed array's length is
/// different than the length of the provided `dst` array.
pub fn copy_to(&self, dst: &mut [$ty]) {
assert_eq!(self.length() as usize, dst.len());
let buf = wasm_bindgen::memory();
let mem = buf.unchecked_ref::<WebAssembly::Memory>();
let all_wasm_memory = $name::new(&mem.buffer());
let offset = dst.as_ptr() as usize / mem::size_of::<$ty>();
all_wasm_memory.set(self, offset as u32);
}
}
)*)
}

arrays! {
Int8Array: i8,
Int16Array: i16,
Int32Array: i32,
Uint8Array: u8,
Uint8ClampedArray: u8,
Uint16Array: u16,
Uint32Array: u32,
Float32Array: f32,
Float64Array: f64,
}
21 changes: 21 additions & 0 deletions crates/js-sys/tests/wasm/TypedArray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,24 @@ macro_rules! test_slice {
fn new_slice() {
each!(test_slice);
}

#[wasm_bindgen_test]
fn view() {
let x = [1, 2, 3];
let array = unsafe { Int32Array::view(&x) };
assert_eq!(array.length(), 3);
array.for_each(&mut |x, i, _| {
assert_eq!(x, (i + 1) as i32);
});
}

#[wasm_bindgen_test]
fn copy_to() {
let mut x = [0; 10];
let array = Int32Array::new(&10.into());
array.fill(5, 0, 10);
array.copy_to(&mut x);
for i in x.iter() {
assert_eq!(*i, 5);
}
}

0 comments on commit 2d7f601

Please sign in to comment.