1use crate::conversions::ConversionResult;
10use crate::conversions::FromJSValConvertible;
11use crate::conversions::ToJSValConvertible;
12use crate::glue::GetFloat32ArrayLengthAndData;
13use crate::glue::GetFloat64ArrayLengthAndData;
14use crate::glue::GetInt16ArrayLengthAndData;
15use crate::glue::GetInt32ArrayLengthAndData;
16use crate::glue::GetInt8ArrayLengthAndData;
17use crate::glue::GetUint16ArrayLengthAndData;
18use crate::glue::GetUint32ArrayLengthAndData;
19use crate::glue::GetUint8ArrayLengthAndData;
20use crate::glue::GetUint8ClampedArrayLengthAndData;
21use crate::jsapi::GetArrayBufferData;
22use crate::jsapi::GetArrayBufferLengthAndData;
23use crate::jsapi::GetArrayBufferViewLengthAndData;
24use crate::jsapi::Heap;
25use crate::jsapi::JSContext;
26use crate::jsapi::JSObject;
27use crate::jsapi::JSTracer;
28use crate::jsapi::JS_GetArrayBufferViewType;
29use crate::jsapi::JS_GetFloat32ArrayData;
30use crate::jsapi::JS_GetFloat64ArrayData;
31use crate::jsapi::JS_GetInt16ArrayData;
32use crate::jsapi::JS_GetInt32ArrayData;
33use crate::jsapi::JS_GetInt8ArrayData;
34use crate::jsapi::JS_GetTypedArraySharedness;
35use crate::jsapi::JS_GetUint16ArrayData;
36use crate::jsapi::JS_GetUint32ArrayData;
37use crate::jsapi::JS_GetUint8ArrayData;
38use crate::jsapi::JS_GetUint8ClampedArrayData;
39use crate::jsapi::JS_NewFloat32Array;
40use crate::jsapi::JS_NewFloat64Array;
41use crate::jsapi::JS_NewInt16Array;
42use crate::jsapi::JS_NewInt32Array;
43use crate::jsapi::JS_NewInt8Array;
44use crate::jsapi::JS_NewUint16Array;
45use crate::jsapi::JS_NewUint32Array;
46use crate::jsapi::JS_NewUint8Array;
47use crate::jsapi::JS_NewUint8ClampedArray;
48use crate::jsapi::NewArrayBuffer;
49use crate::jsapi::Type;
50use crate::jsapi::UnwrapArrayBuffer;
51use crate::jsapi::UnwrapArrayBufferView;
52use crate::jsapi::UnwrapFloat32Array;
53use crate::jsapi::UnwrapFloat64Array;
54use crate::jsapi::UnwrapInt16Array;
55use crate::jsapi::UnwrapInt32Array;
56use crate::jsapi::UnwrapInt8Array;
57use crate::jsapi::UnwrapUint16Array;
58use crate::jsapi::UnwrapUint32Array;
59use crate::jsapi::UnwrapUint8Array;
60use crate::jsapi::UnwrapUint8ClampedArray;
61use crate::rust::CustomTrace;
62use crate::rust::{HandleValue, MutableHandleObject, MutableHandleValue};
63
64use std::cell::Cell;
65use std::ptr;
66use std::slice;
67
68pub trait JSObjectStorage {
74 fn as_raw(&self) -> *mut JSObject;
75 fn from_raw(raw: *mut JSObject) -> Self;
76}
77
78impl JSObjectStorage for *mut JSObject {
79 fn as_raw(&self) -> *mut JSObject {
80 *self
81 }
82 fn from_raw(raw: *mut JSObject) -> Self {
83 raw
84 }
85}
86
87impl JSObjectStorage for Box<Heap<*mut JSObject>> {
88 fn as_raw(&self) -> *mut JSObject {
89 self.get()
90 }
91 #[cfg_attr(feature = "crown", expect(crown::unrooted_must_root))]
92 fn from_raw(raw: *mut JSObject) -> Self {
93 let boxed = Box::new(Heap::default());
94 boxed.set(raw);
95 boxed
96 }
97}
98
99impl<T: TypedArrayElement, S: JSObjectStorage> FromJSValConvertible for TypedArray<T, S> {
100 type Config = ();
101 unsafe fn from_jsval(
102 _cx: *mut JSContext,
103 value: HandleValue,
104 _option: (),
105 ) -> Result<ConversionResult<Self>, ()> {
106 if value.get().is_object() {
107 Self::from(value.get().to_object()).map(ConversionResult::Success)
108 } else {
109 Err(())
110 }
111 }
112}
113
114impl<T: TypedArrayElement, S: JSObjectStorage> ToJSValConvertible for TypedArray<T, S> {
115 #[inline]
116 unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
117 ToJSValConvertible::to_jsval(&self.object.as_raw(), cx, rval);
118 }
119}
120
121pub enum CreateWith<'a, T: 'a> {
122 Length(usize),
123 Slice(&'a [T]),
124}
125
126pub struct TypedArray<T: TypedArrayElement, S: JSObjectStorage> {
128 object: S,
129 computed: Cell<Option<(*mut T::Element, usize)>>,
130}
131
132unsafe impl<T> CustomTrace for TypedArray<T, *mut JSObject>
133where
134 T: TypedArrayElement,
135{
136 fn trace(&self, trc: *mut JSTracer) {
137 self.object.trace(trc);
138 }
139}
140
141impl<T: TypedArrayElement, S: JSObjectStorage> TypedArray<T, S> {
142 pub fn from(object: *mut JSObject) -> Result<Self, ()> {
146 if object.is_null() {
147 return Err(());
148 }
149 unsafe {
150 let unwrapped = T::unwrap_array(object);
151 if unwrapped.is_null() {
152 return Err(());
153 }
154
155 Ok(TypedArray {
156 object: S::from_raw(unwrapped),
157 computed: Cell::new(None),
158 })
159 }
160 }
161
162 fn data(&self) -> (*mut T::Element, usize) {
163 if let Some(data) = self.computed.get() {
164 return data;
165 }
166
167 let data = unsafe { T::length_and_data(self.object.as_raw()) };
168 self.computed.set(Some(data));
169 data
170 }
171
172 pub fn len(&self) -> usize {
174 self.data().1 as usize
175 }
176
177 pub unsafe fn underlying_object(&self) -> &S {
186 &self.object
187 }
188
189 pub fn to_vec(&self) -> Vec<T::Element>
191 where
192 T::Element: Clone,
193 {
194 unsafe { self.as_slice().to_vec() }
200 }
201
202 pub unsafe fn as_slice(&self) -> &[T::Element] {
207 let (pointer, length) = self.data();
208 slice::from_raw_parts(pointer as *const T::Element, length as usize)
209 }
210
211 pub unsafe fn as_mut_slice(&mut self) -> &mut [T::Element] {
219 let (pointer, length) = self.data();
220 slice::from_raw_parts_mut(pointer, length as usize)
221 }
222
223 pub fn is_shared(&self) -> bool {
226 unsafe { JS_GetTypedArraySharedness(self.object.as_raw()) }
227 }
228}
229
230impl<T: TypedArrayElementCreator + TypedArrayElement, S: JSObjectStorage> TypedArray<T, S> {
231 pub unsafe fn create(
234 cx: *mut JSContext,
235 with: CreateWith<T::Element>,
236 mut result: MutableHandleObject,
237 ) -> Result<(), ()> {
238 let length = match with {
239 CreateWith::Length(len) => len,
240 CreateWith::Slice(slice) => slice.len(),
241 };
242
243 result.set(T::create_new(cx, length));
244 if result.get().is_null() {
245 return Err(());
246 }
247
248 if let CreateWith::Slice(data) = with {
249 Self::update_raw(data, result.get());
250 }
251
252 Ok(())
253 }
254
255 pub fn update(&mut self, data: &[T::Element]) {
257 unsafe {
258 Self::update_raw(data, self.object.as_raw());
259 }
260 }
261
262 unsafe fn update_raw(data: &[T::Element], result: *mut JSObject) {
263 let (buf, length) = T::length_and_data(result);
264 assert!(data.len() <= length as usize);
265 ptr::copy_nonoverlapping(data.as_ptr(), buf, data.len());
266 }
267}
268
269pub trait TypedArrayElement {
272 type Element;
274 unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject;
276 unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, usize);
278}
279
280pub trait TypedArrayElementCreator: TypedArrayElement {
282 unsafe fn create_new(cx: *mut JSContext, length: usize) -> *mut JSObject;
284 unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element;
286}
287
288macro_rules! typed_array_element {
289 ($t: ident,
290 $element: ty,
291 $unwrap: ident,
292 $length_and_data: ident) => {
293 pub struct $t;
295
296 impl TypedArrayElement for $t {
297 type Element = $element;
298 unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject {
299 $unwrap(obj)
300 }
301
302 unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, usize) {
303 let mut len = 0;
304 let mut shared = false;
305 let mut data = ptr::null_mut();
306 $length_and_data(obj, &mut len, &mut shared, &mut data);
307 assert!(!shared);
308 (data, len)
309 }
310 }
311 };
312
313 ($t: ident,
314 $element: ty,
315 $unwrap: ident,
316 $length_and_data: ident,
317 $create_new: ident,
318 $get_data: ident) => {
319 typed_array_element!($t, $element, $unwrap, $length_and_data);
320
321 impl TypedArrayElementCreator for $t {
322 unsafe fn create_new(cx: *mut JSContext, length: usize) -> *mut JSObject {
323 $create_new(cx, length)
324 }
325
326 unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element {
327 let mut shared = false;
328 let data = $get_data(obj, &mut shared, ptr::null_mut());
329 assert!(!shared);
330 data
331 }
332 }
333 };
334}
335
336typed_array_element!(
337 Uint8,
338 u8,
339 UnwrapUint8Array,
340 GetUint8ArrayLengthAndData,
341 JS_NewUint8Array,
342 JS_GetUint8ArrayData
343);
344typed_array_element!(
345 Uint16,
346 u16,
347 UnwrapUint16Array,
348 GetUint16ArrayLengthAndData,
349 JS_NewUint16Array,
350 JS_GetUint16ArrayData
351);
352typed_array_element!(
353 Uint32,
354 u32,
355 UnwrapUint32Array,
356 GetUint32ArrayLengthAndData,
357 JS_NewUint32Array,
358 JS_GetUint32ArrayData
359);
360typed_array_element!(
361 Int8,
362 i8,
363 UnwrapInt8Array,
364 GetInt8ArrayLengthAndData,
365 JS_NewInt8Array,
366 JS_GetInt8ArrayData
367);
368typed_array_element!(
369 Int16,
370 i16,
371 UnwrapInt16Array,
372 GetInt16ArrayLengthAndData,
373 JS_NewInt16Array,
374 JS_GetInt16ArrayData
375);
376typed_array_element!(
377 Int32,
378 i32,
379 UnwrapInt32Array,
380 GetInt32ArrayLengthAndData,
381 JS_NewInt32Array,
382 JS_GetInt32ArrayData
383);
384typed_array_element!(
385 Float32,
386 f32,
387 UnwrapFloat32Array,
388 GetFloat32ArrayLengthAndData,
389 JS_NewFloat32Array,
390 JS_GetFloat32ArrayData
391);
392typed_array_element!(
393 Float64,
394 f64,
395 UnwrapFloat64Array,
396 GetFloat64ArrayLengthAndData,
397 JS_NewFloat64Array,
398 JS_GetFloat64ArrayData
399);
400typed_array_element!(
401 ClampedU8,
402 u8,
403 UnwrapUint8ClampedArray,
404 GetUint8ClampedArrayLengthAndData,
405 JS_NewUint8ClampedArray,
406 JS_GetUint8ClampedArrayData
407);
408typed_array_element!(
409 ArrayBufferU8,
410 u8,
411 UnwrapArrayBuffer,
412 GetArrayBufferLengthAndData,
413 NewArrayBuffer,
414 GetArrayBufferData
415);
416typed_array_element!(
417 ArrayBufferViewU8,
418 u8,
419 UnwrapArrayBufferView,
420 GetArrayBufferViewLengthAndData
421);
422
423macro_rules! array_alias {
426 ($arr: ident, $heap_arr: ident, $elem: ty) => {
427 pub type $arr = TypedArray<$elem, *mut JSObject>;
428 pub type $heap_arr = TypedArray<$elem, Box<Heap<*mut JSObject>>>;
429 };
430}
431
432array_alias!(Uint8ClampedArray, HeapUint8ClampedArray, ClampedU8);
433array_alias!(Uint8Array, HeapUint8Array, Uint8);
434array_alias!(Int8Array, HeapInt8Array, Int8);
435array_alias!(Uint16Array, HeapUint16Array, Uint16);
436array_alias!(Int16Array, HeapInt16Array, Int16);
437array_alias!(Uint32Array, HeapUint32Array, Uint32);
438array_alias!(Int32Array, HeapInt32Array, Int32);
439array_alias!(Float32Array, HeapFloat32Array, Float32);
440array_alias!(Float64Array, HeapFloat64Array, Float64);
441array_alias!(ArrayBuffer, HeapArrayBuffer, ArrayBufferU8);
442array_alias!(ArrayBufferView, HeapArrayBufferView, ArrayBufferViewU8);
443
444impl<S: JSObjectStorage> TypedArray<ArrayBufferViewU8, S> {
445 pub fn get_array_type(&self) -> Type {
446 unsafe { JS_GetArrayBufferViewType(self.object.as_raw()) }
447 }
448}
449
450#[macro_export]
451macro_rules! typedarray {
452 (&in($cx:expr) $($t:tt)*) => {
453 typedarray!(in(unsafe {$cx.raw_cx_no_gc()}) $($t)*);
454 };
455 (in($cx:expr) let $name:ident : $ty:ident = $init:expr) => {
456 let mut __array =
457 $crate::typedarray::$ty::from($init).map($crate::rust::CustomAutoRooter::new);
458
459 let $name = __array.as_mut().map(|ok| ok.root($cx));
460 };
461 (in($cx:expr) let mut $name:ident : $ty:ident = $init:expr) => {
462 let mut __array =
463 $crate::typedarray::$ty::from($init).map($crate::rust::CustomAutoRooter::new);
464
465 let mut $name = __array.as_mut().map(|ok| ok.root($cx));
466 };
467}