1use std::ptr::NonNull;
10
11use crate::context::NoGC;
12use crate::conversions::ConversionResult;
13use crate::conversions::FromJSValConvertible;
14use crate::conversions::ToJSValConvertible;
15use crate::glue::GetFloat32ArrayLengthAndData;
16use crate::glue::GetFloat64ArrayLengthAndData;
17use crate::glue::GetInt16ArrayLengthAndData;
18use crate::glue::GetInt32ArrayLengthAndData;
19use crate::glue::GetInt8ArrayLengthAndData;
20use crate::glue::GetUint16ArrayLengthAndData;
21use crate::glue::GetUint32ArrayLengthAndData;
22use crate::glue::GetUint8ArrayLengthAndData;
23use crate::glue::GetUint8ClampedArrayLengthAndData;
24use crate::jsapi::GetArrayBufferData;
25use crate::jsapi::GetArrayBufferLengthAndData;
26use crate::jsapi::GetArrayBufferViewLengthAndData;
27use crate::jsapi::Heap;
28use crate::jsapi::JSContext;
29use crate::jsapi::JSObject;
30use crate::jsapi::JSTracer;
31use crate::jsapi::JS_GetArrayBufferViewType;
32use crate::jsapi::JS_GetFloat32ArrayData;
33use crate::jsapi::JS_GetFloat64ArrayData;
34use crate::jsapi::JS_GetInt16ArrayData;
35use crate::jsapi::JS_GetInt32ArrayData;
36use crate::jsapi::JS_GetInt8ArrayData;
37use crate::jsapi::JS_GetTypedArraySharedness;
38use crate::jsapi::JS_GetUint16ArrayData;
39use crate::jsapi::JS_GetUint32ArrayData;
40use crate::jsapi::JS_GetUint8ArrayData;
41use crate::jsapi::JS_GetUint8ClampedArrayData;
42use crate::jsapi::JS_NewFloat32Array;
43use crate::jsapi::JS_NewFloat64Array;
44use crate::jsapi::JS_NewInt16Array;
45use crate::jsapi::JS_NewInt32Array;
46use crate::jsapi::JS_NewInt8Array;
47use crate::jsapi::JS_NewUint16Array;
48use crate::jsapi::JS_NewUint32Array;
49use crate::jsapi::JS_NewUint8Array;
50use crate::jsapi::JS_NewUint8ClampedArray;
51use crate::jsapi::NewArrayBuffer;
52use crate::jsapi::Type;
53use crate::jsapi::UnwrapArrayBuffer;
54use crate::jsapi::UnwrapArrayBufferView;
55use crate::jsapi::UnwrapFloat32Array;
56use crate::jsapi::UnwrapFloat64Array;
57use crate::jsapi::UnwrapInt16Array;
58use crate::jsapi::UnwrapInt32Array;
59use crate::jsapi::UnwrapInt8Array;
60use crate::jsapi::UnwrapUint16Array;
61use crate::jsapi::UnwrapUint32Array;
62use crate::jsapi::UnwrapUint8Array;
63use crate::jsapi::UnwrapUint8ClampedArray;
64use crate::rust::CustomTrace;
65use crate::rust::{HandleValue, MutableHandleObject, MutableHandleValue};
66
67use std::cell::Cell;
68use std::ptr;
69
70pub trait JSObjectStorage {
76 fn as_raw(&self) -> *mut JSObject;
77 fn from_raw(raw: *mut JSObject) -> Self;
78}
79
80impl JSObjectStorage for *mut JSObject {
81 fn as_raw(&self) -> *mut JSObject {
82 *self
83 }
84 fn from_raw(raw: *mut JSObject) -> Self {
85 raw
86 }
87}
88
89impl JSObjectStorage for Box<Heap<*mut JSObject>> {
90 fn as_raw(&self) -> *mut JSObject {
91 self.get()
92 }
93 #[cfg_attr(feature = "crown", expect(crown::unrooted_must_root))]
94 fn from_raw(raw: *mut JSObject) -> Self {
95 let boxed = Box::new(Heap::default());
96 boxed.set(raw);
97 boxed
98 }
99}
100
101impl<T: TypedArrayElement, S: JSObjectStorage> FromJSValConvertible for TypedArray<T, S> {
102 type Config = ();
103
104 fn safe_from_jsval(
105 _cx: &mut crate::context::JSContext,
106 value: HandleValue,
107 _option: (),
108 ) -> Result<ConversionResult<Self>, ()> {
109 if value.get().is_object() {
110 Self::from(value.get().to_object()).map(ConversionResult::Success)
111 } else {
112 Err(())
113 }
114 }
115}
116
117impl<T: TypedArrayElement, S: JSObjectStorage> ToJSValConvertible for TypedArray<T, S> {
118 #[inline]
119 unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
120 ToJSValConvertible::to_jsval(&self.object.as_raw(), cx, rval);
121 }
122}
123
124pub enum CreateWith<'a, T: 'a> {
125 Length(usize),
126 Slice(&'a [T]),
127}
128
129#[derive(Clone, Copy)]
130enum ArrayData<T> {
131 NotYetComputed,
132 Detached,
133 Computed(NonNull<[T]>),
134}
135
136pub struct TypedArray<T: TypedArrayElement, S: JSObjectStorage> {
138 object: S,
139 computed: Cell<ArrayData<T::Element>>,
140}
141
142unsafe impl<T> CustomTrace for TypedArray<T, *mut JSObject>
143where
144 T: TypedArrayElement,
145{
146 fn trace(&self, trc: *mut JSTracer) {
147 self.object.trace(trc);
148 }
149}
150
151impl<T: TypedArrayElement, S: JSObjectStorage> TypedArray<T, S> {
152 pub fn from(object: *mut JSObject) -> Result<Self, ()> {
156 if object.is_null() {
157 return Err(());
158 }
159 unsafe {
160 let unwrapped = T::unwrap_array(object);
161 if unwrapped.is_null() {
162 return Err(());
163 }
164
165 Ok(TypedArray {
166 object: S::from_raw(unwrapped),
167 computed: Cell::new(ArrayData::NotYetComputed),
168 })
169 }
170 }
171
172 fn data(&self) -> Option<NonNull<[T::Element]>> {
173 if let ArrayData::Computed(data) = self.computed.get() {
174 return Some(data);
175 }
176
177 let data = unsafe { T::length_and_data(self.object.as_raw()) };
178 self.computed.set(if let Some(data) = data {
179 ArrayData::Computed(data)
180 } else {
181 ArrayData::Detached
182 });
183 data
184 }
185
186 pub fn len(&self) -> usize {
188 self.data().map_or(0, |data| data.len())
189 }
190
191 pub unsafe fn underlying_object(&self) -> &S {
200 &self.object
201 }
202
203 #[allow(deprecated)]
206 pub fn to_vec(&self) -> Option<Vec<T::Element>>
207 where
208 T::Element: Clone,
209 {
210 unsafe { self.as_slice().map(|slice| slice.to_vec()) }
216 }
217
218 #[deprecated = "use as_slice_safe instead"]
227 pub unsafe fn as_slice(&self) -> Option<&[T::Element]> {
228 self.data().map(|data| data.as_ref())
229 }
230
231 pub fn as_slice_safe<'a>(&self, _no_gc: &'a NoGC) -> Option<&'a [T::Element]> {
234 self.data().map(|data| unsafe { data.as_ref() })
239 }
240
241 #[deprecated = "use as_mut_slice_safe instead"]
253 pub unsafe fn as_mut_slice(&mut self) -> Option<&mut [T::Element]> {
254 self.data().map(|mut data| data.as_mut())
255 }
256
257 pub fn as_mut_slice_safe<'a>(&mut self, _no_gc: &'a NoGC) -> Option<&'a mut [T::Element]> {
258 self.data().map(|mut data| unsafe { data.as_mut() })
263 }
264
265 pub fn is_shared(&self) -> bool {
268 unsafe { JS_GetTypedArraySharedness(self.object.as_raw()) }
269 }
270}
271
272impl<T: TypedArrayElementCreator + TypedArrayElement, S: JSObjectStorage> TypedArray<T, S> {
273 pub unsafe fn create(
276 cx: *mut JSContext,
277 with: CreateWith<T::Element>,
278 mut result: MutableHandleObject,
279 ) -> Result<(), ()> {
280 let length = match with {
281 CreateWith::Length(len) => len,
282 CreateWith::Slice(slice) => slice.len(),
283 };
284
285 result.set(T::create_new(cx, length));
286 if result.get().is_null() {
287 return Err(());
288 }
289
290 if let CreateWith::Slice(data) = with {
291 Self::update_raw(data, result.get());
292 }
293
294 Ok(())
295 }
296
297 pub fn update(&mut self, data: &[T::Element]) {
299 unsafe {
300 Self::update_raw(data, self.object.as_raw());
301 }
302 }
303
304 unsafe fn update_raw(data: &[T::Element], result: *mut JSObject) {
305 let Some(mut buffer) = T::length_and_data(result) else {
306 return;
307 };
308 assert!(data.len() <= buffer.len());
309 ptr::copy_nonoverlapping(
310 data.as_ptr(),
311 buffer.as_mut().as_mut_ptr(), data.len(),
313 );
314 }
315}
316
317pub trait TypedArrayElement {
320 type Element: Copy;
322 unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject;
324 unsafe fn length_and_data(obj: *mut JSObject) -> Option<NonNull<[Self::Element]>>;
326}
327
328pub trait TypedArrayElementCreator: TypedArrayElement {
330 unsafe fn create_new(cx: *mut JSContext, length: usize) -> *mut JSObject;
332 unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element;
334}
335
336macro_rules! typed_array_element {
337 ($t: ident,
338 $element: ty,
339 $unwrap: ident,
340 $length_and_data: ident) => {
341 pub struct $t;
343
344 impl TypedArrayElement for $t {
345 type Element = $element;
346 unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject {
347 $unwrap(obj)
348 }
349
350 unsafe fn length_and_data(obj: *mut JSObject) -> Option<NonNull<[Self::Element]>> {
351 let mut len = 0;
352 let mut shared = false;
353 let mut data = ptr::null_mut();
354 $length_and_data(obj, &mut len, &mut shared, &mut data);
355 assert!(!shared);
356 NonNull::new(data).map(|data| NonNull::slice_from_raw_parts(data, len))
357 }
358 }
359 };
360
361 ($t: ident,
362 $element: ty,
363 $unwrap: ident,
364 $length_and_data: ident,
365 $create_new: ident,
366 $get_data: ident) => {
367 typed_array_element!($t, $element, $unwrap, $length_and_data);
368
369 impl TypedArrayElementCreator for $t {
370 unsafe fn create_new(cx: *mut JSContext, length: usize) -> *mut JSObject {
371 $create_new(cx, length)
372 }
373
374 unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element {
375 let mut shared = false;
376 let data = $get_data(obj, &mut shared, ptr::null_mut());
377 assert!(!shared);
378 data
379 }
380 }
381 };
382}
383
384typed_array_element!(
385 Uint8,
386 u8,
387 UnwrapUint8Array,
388 GetUint8ArrayLengthAndData,
389 JS_NewUint8Array,
390 JS_GetUint8ArrayData
391);
392typed_array_element!(
393 Uint16,
394 u16,
395 UnwrapUint16Array,
396 GetUint16ArrayLengthAndData,
397 JS_NewUint16Array,
398 JS_GetUint16ArrayData
399);
400typed_array_element!(
401 Uint32,
402 u32,
403 UnwrapUint32Array,
404 GetUint32ArrayLengthAndData,
405 JS_NewUint32Array,
406 JS_GetUint32ArrayData
407);
408typed_array_element!(
409 Int8,
410 i8,
411 UnwrapInt8Array,
412 GetInt8ArrayLengthAndData,
413 JS_NewInt8Array,
414 JS_GetInt8ArrayData
415);
416typed_array_element!(
417 Int16,
418 i16,
419 UnwrapInt16Array,
420 GetInt16ArrayLengthAndData,
421 JS_NewInt16Array,
422 JS_GetInt16ArrayData
423);
424typed_array_element!(
425 Int32,
426 i32,
427 UnwrapInt32Array,
428 GetInt32ArrayLengthAndData,
429 JS_NewInt32Array,
430 JS_GetInt32ArrayData
431);
432typed_array_element!(
433 Float32,
434 f32,
435 UnwrapFloat32Array,
436 GetFloat32ArrayLengthAndData,
437 JS_NewFloat32Array,
438 JS_GetFloat32ArrayData
439);
440typed_array_element!(
441 Float64,
442 f64,
443 UnwrapFloat64Array,
444 GetFloat64ArrayLengthAndData,
445 JS_NewFloat64Array,
446 JS_GetFloat64ArrayData
447);
448typed_array_element!(
449 ClampedU8,
450 u8,
451 UnwrapUint8ClampedArray,
452 GetUint8ClampedArrayLengthAndData,
453 JS_NewUint8ClampedArray,
454 JS_GetUint8ClampedArrayData
455);
456typed_array_element!(
457 ArrayBufferU8,
458 u8,
459 UnwrapArrayBuffer,
460 GetArrayBufferLengthAndData,
461 NewArrayBuffer,
462 GetArrayBufferData
463);
464typed_array_element!(
465 ArrayBufferViewU8,
466 u8,
467 UnwrapArrayBufferView,
468 GetArrayBufferViewLengthAndData
469);
470
471macro_rules! array_alias {
474 ($arr: ident, $heap_arr: ident, $elem: ty) => {
475 pub type $arr = TypedArray<$elem, *mut JSObject>;
476 pub type $heap_arr = TypedArray<$elem, Box<Heap<*mut JSObject>>>;
477 };
478}
479
480array_alias!(Uint8ClampedArray, HeapUint8ClampedArray, ClampedU8);
481array_alias!(Uint8Array, HeapUint8Array, Uint8);
482array_alias!(Int8Array, HeapInt8Array, Int8);
483array_alias!(Uint16Array, HeapUint16Array, Uint16);
484array_alias!(Int16Array, HeapInt16Array, Int16);
485array_alias!(Uint32Array, HeapUint32Array, Uint32);
486array_alias!(Int32Array, HeapInt32Array, Int32);
487array_alias!(Float32Array, HeapFloat32Array, Float32);
488array_alias!(Float64Array, HeapFloat64Array, Float64);
489array_alias!(ArrayBuffer, HeapArrayBuffer, ArrayBufferU8);
490array_alias!(ArrayBufferView, HeapArrayBufferView, ArrayBufferViewU8);
491
492impl<S: JSObjectStorage> TypedArray<ArrayBufferViewU8, S> {
493 pub fn get_array_type(&self) -> Type {
494 unsafe { JS_GetArrayBufferViewType(self.object.as_raw()) }
495 }
496}
497
498#[macro_export]
499macro_rules! typedarray {
500 (&in($cx:expr) $($t:tt)*) => {
501 typedarray!(in(unsafe {$cx.raw_cx_no_gc()}) $($t)*);
502 };
503 (in($cx:expr) let $name:ident : $ty:ident = $init:expr) => {
504 let mut __array =
505 $crate::typedarray::$ty::from($init).map($crate::rust::CustomAutoRooter::new);
506
507 let $name = __array.as_mut().map(|ok| ok.root($cx));
508 };
509 (in($cx:expr) let mut $name:ident : $ty:ident = $init:expr) => {
510 let mut __array =
511 $crate::typedarray::$ty::from($init).map($crate::rust::CustomAutoRooter::new);
512
513 let mut $name = __array.as_mut().map(|ok| ok.root($cx));
514 };
515}