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 fn from_raw(raw: *mut JSObject) -> Self {
92 let boxed = Box::new(Heap::default());
93 boxed.set(raw);
94 boxed
95 }
96}
97
98impl<T: TypedArrayElement, S: JSObjectStorage> FromJSValConvertible for TypedArray<T, S> {
99 type Config = ();
100 unsafe fn from_jsval(
101 _cx: *mut JSContext,
102 value: HandleValue,
103 _option: (),
104 ) -> Result<ConversionResult<Self>, ()> {
105 if value.get().is_object() {
106 Self::from(value.get().to_object()).map(ConversionResult::Success)
107 } else {
108 Err(())
109 }
110 }
111}
112
113impl<T: TypedArrayElement, S: JSObjectStorage> ToJSValConvertible for TypedArray<T, S> {
114 #[inline]
115 unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
116 ToJSValConvertible::to_jsval(&self.object.as_raw(), cx, rval);
117 }
118}
119
120pub enum CreateWith<'a, T: 'a> {
121 Length(usize),
122 Slice(&'a [T]),
123}
124
125pub struct TypedArray<T: TypedArrayElement, S: JSObjectStorage> {
127 object: S,
128 computed: Cell<Option<(*mut T::Element, usize)>>,
129}
130
131unsafe impl<T> CustomTrace for TypedArray<T, *mut JSObject>
132where
133 T: TypedArrayElement,
134{
135 fn trace(&self, trc: *mut JSTracer) {
136 self.object.trace(trc);
137 }
138}
139
140impl<T: TypedArrayElement, S: JSObjectStorage> TypedArray<T, S> {
141 pub fn from(object: *mut JSObject) -> Result<Self, ()> {
145 if object.is_null() {
146 return Err(());
147 }
148 unsafe {
149 let unwrapped = T::unwrap_array(object);
150 if unwrapped.is_null() {
151 return Err(());
152 }
153
154 Ok(TypedArray {
155 object: S::from_raw(unwrapped),
156 computed: Cell::new(None),
157 })
158 }
159 }
160
161 fn data(&self) -> (*mut T::Element, usize) {
162 if let Some(data) = self.computed.get() {
163 return data;
164 }
165
166 let data = unsafe { T::length_and_data(self.object.as_raw()) };
167 self.computed.set(Some(data));
168 data
169 }
170
171 pub fn len(&self) -> usize {
173 self.data().1 as usize
174 }
175
176 pub unsafe fn underlying_object(&self) -> &S {
185 &self.object
186 }
187
188 pub fn to_vec(&self) -> Vec<T::Element>
190 where
191 T::Element: Clone,
192 {
193 unsafe { self.as_slice().to_vec() }
199 }
200
201 pub unsafe fn as_slice(&self) -> &[T::Element] {
206 let (pointer, length) = self.data();
207 slice::from_raw_parts(pointer as *const T::Element, length as usize)
208 }
209
210 pub unsafe fn as_mut_slice(&mut self) -> &mut [T::Element] {
218 let (pointer, length) = self.data();
219 slice::from_raw_parts_mut(pointer, length as usize)
220 }
221
222 pub fn is_shared(&self) -> bool {
225 unsafe { JS_GetTypedArraySharedness(self.object.as_raw()) }
226 }
227}
228
229impl<T: TypedArrayElementCreator + TypedArrayElement, S: JSObjectStorage> TypedArray<T, S> {
230 pub unsafe fn create(
233 cx: *mut JSContext,
234 with: CreateWith<T::Element>,
235 mut result: MutableHandleObject,
236 ) -> Result<(), ()> {
237 let length = match with {
238 CreateWith::Length(len) => len,
239 CreateWith::Slice(slice) => slice.len(),
240 };
241
242 result.set(T::create_new(cx, length));
243 if result.get().is_null() {
244 return Err(());
245 }
246
247 if let CreateWith::Slice(data) = with {
248 Self::update_raw(data, result.get());
249 }
250
251 Ok(())
252 }
253
254 pub fn update(&mut self, data: &[T::Element]) {
256 unsafe {
257 Self::update_raw(data, self.object.as_raw());
258 }
259 }
260
261 unsafe fn update_raw(data: &[T::Element], result: *mut JSObject) {
262 let (buf, length) = T::length_and_data(result);
263 assert!(data.len() <= length as usize);
264 ptr::copy_nonoverlapping(data.as_ptr(), buf, data.len());
265 }
266}
267
268pub trait TypedArrayElement {
271 type Element;
273 unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject;
275 unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, usize);
277}
278
279pub trait TypedArrayElementCreator: TypedArrayElement {
281 unsafe fn create_new(cx: *mut JSContext, length: usize) -> *mut JSObject;
283 unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element;
285}
286
287macro_rules! typed_array_element {
288 ($t: ident,
289 $element: ty,
290 $unwrap: ident,
291 $length_and_data: ident) => {
292 pub struct $t;
294
295 impl TypedArrayElement for $t {
296 type Element = $element;
297 unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject {
298 $unwrap(obj)
299 }
300
301 unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, usize) {
302 let mut len = 0;
303 let mut shared = false;
304 let mut data = ptr::null_mut();
305 $length_and_data(obj, &mut len, &mut shared, &mut data);
306 assert!(!shared);
307 (data, len)
308 }
309 }
310 };
311
312 ($t: ident,
313 $element: ty,
314 $unwrap: ident,
315 $length_and_data: ident,
316 $create_new: ident,
317 $get_data: ident) => {
318 typed_array_element!($t, $element, $unwrap, $length_and_data);
319
320 impl TypedArrayElementCreator for $t {
321 unsafe fn create_new(cx: *mut JSContext, length: usize) -> *mut JSObject {
322 $create_new(cx, length)
323 }
324
325 unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element {
326 let mut shared = false;
327 let data = $get_data(obj, &mut shared, ptr::null_mut());
328 assert!(!shared);
329 data
330 }
331 }
332 };
333}
334
335typed_array_element!(
336 Uint8,
337 u8,
338 UnwrapUint8Array,
339 GetUint8ArrayLengthAndData,
340 JS_NewUint8Array,
341 JS_GetUint8ArrayData
342);
343typed_array_element!(
344 Uint16,
345 u16,
346 UnwrapUint16Array,
347 GetUint16ArrayLengthAndData,
348 JS_NewUint16Array,
349 JS_GetUint16ArrayData
350);
351typed_array_element!(
352 Uint32,
353 u32,
354 UnwrapUint32Array,
355 GetUint32ArrayLengthAndData,
356 JS_NewUint32Array,
357 JS_GetUint32ArrayData
358);
359typed_array_element!(
360 Int8,
361 i8,
362 UnwrapInt8Array,
363 GetInt8ArrayLengthAndData,
364 JS_NewInt8Array,
365 JS_GetInt8ArrayData
366);
367typed_array_element!(
368 Int16,
369 i16,
370 UnwrapInt16Array,
371 GetInt16ArrayLengthAndData,
372 JS_NewInt16Array,
373 JS_GetInt16ArrayData
374);
375typed_array_element!(
376 Int32,
377 i32,
378 UnwrapInt32Array,
379 GetInt32ArrayLengthAndData,
380 JS_NewInt32Array,
381 JS_GetInt32ArrayData
382);
383typed_array_element!(
384 Float32,
385 f32,
386 UnwrapFloat32Array,
387 GetFloat32ArrayLengthAndData,
388 JS_NewFloat32Array,
389 JS_GetFloat32ArrayData
390);
391typed_array_element!(
392 Float64,
393 f64,
394 UnwrapFloat64Array,
395 GetFloat64ArrayLengthAndData,
396 JS_NewFloat64Array,
397 JS_GetFloat64ArrayData
398);
399typed_array_element!(
400 ClampedU8,
401 u8,
402 UnwrapUint8ClampedArray,
403 GetUint8ClampedArrayLengthAndData,
404 JS_NewUint8ClampedArray,
405 JS_GetUint8ClampedArrayData
406);
407typed_array_element!(
408 ArrayBufferU8,
409 u8,
410 UnwrapArrayBuffer,
411 GetArrayBufferLengthAndData,
412 NewArrayBuffer,
413 GetArrayBufferData
414);
415typed_array_element!(
416 ArrayBufferViewU8,
417 u8,
418 UnwrapArrayBufferView,
419 GetArrayBufferViewLengthAndData
420);
421
422macro_rules! array_alias {
425 ($arr: ident, $heap_arr: ident, $elem: ty) => {
426 pub type $arr = TypedArray<$elem, *mut JSObject>;
427 pub type $heap_arr = TypedArray<$elem, Box<Heap<*mut JSObject>>>;
428 };
429}
430
431array_alias!(Uint8ClampedArray, HeapUint8ClampedArray, ClampedU8);
432array_alias!(Uint8Array, HeapUint8Array, Uint8);
433array_alias!(Int8Array, HeapInt8Array, Int8);
434array_alias!(Uint16Array, HeapUint16Array, Uint16);
435array_alias!(Int16Array, HeapInt16Array, Int16);
436array_alias!(Uint32Array, HeapUint32Array, Uint32);
437array_alias!(Int32Array, HeapInt32Array, Int32);
438array_alias!(Float32Array, HeapFloat32Array, Float32);
439array_alias!(Float64Array, HeapFloat64Array, Float64);
440array_alias!(ArrayBuffer, HeapArrayBuffer, ArrayBufferU8);
441array_alias!(ArrayBufferView, HeapArrayBufferView, ArrayBufferViewU8);
442
443impl<S: JSObjectStorage> TypedArray<ArrayBufferViewU8, S> {
444 pub fn get_array_type(&self) -> Type {
445 unsafe { JS_GetArrayBufferViewType(self.object.as_raw()) }
446 }
447}
448
449#[macro_export]
450macro_rules! typedarray {
451 (in($cx:expr) let $name:ident : $ty:ident = $init:expr) => {
452 let mut __array =
453 $crate::typedarray::$ty::from($init).map($crate::rust::CustomAutoRooter::new);
454
455 let $name = __array.as_mut().map(|ok| ok.root($cx));
456 };
457 (in($cx:expr) let mut $name:ident : $ty:ident = $init:expr) => {
458 let mut __array =
459 $crate::typedarray::$ty::from($init).map($crate::rust::CustomAutoRooter::new);
460
461 let mut $name = __array.as_mut().map(|ok| ok.root($cx));
462 };
463}