1#![allow(unsafe_code)]
6
7#[cfg(feature = "webgpu")]
8use std::ffi::c_void;
9use std::marker::PhantomData;
10#[cfg(feature = "webgpu")]
11use std::ops::Range;
12use std::ptr;
13#[cfg(feature = "webgpu")]
14use std::sync::Arc;
15
16#[cfg(feature = "webgpu")]
17use js::jsapi::NewExternalArrayBuffer;
18use js::jsapi::{
19 ArrayBufferClone, ArrayBufferCopyData, GetArrayBufferByteLength,
20 HasDefinedArrayBufferDetachKey, Heap, IsArrayBufferObject, IsDetachedArrayBufferObject,
21 JS_ClearPendingException, JS_GetArrayBufferViewBuffer, JS_GetArrayBufferViewByteLength,
22 JS_GetArrayBufferViewByteOffset, JS_GetArrayBufferViewType, JS_GetPendingException,
23 JS_GetTypedArrayLength, JS_IsArrayBufferViewObject, JS_IsTypedArrayObject,
24 JS_NewBigInt64ArrayWithBuffer, JS_NewBigUint64ArrayWithBuffer, JS_NewDataView,
25 JS_NewFloat16ArrayWithBuffer, JS_NewFloat32ArrayWithBuffer, JS_NewFloat64ArrayWithBuffer,
26 JS_NewInt8ArrayWithBuffer, JS_NewInt16ArrayWithBuffer, JS_NewInt32ArrayWithBuffer,
27 JS_NewUint8ArrayWithBuffer, JS_NewUint8ClampedArrayWithBuffer, JS_NewUint16ArrayWithBuffer,
28 JS_NewUint32ArrayWithBuffer, JSObject, NewArrayBuffer, NewArrayBufferWithContents,
29 StealArrayBufferContents, Type,
30};
31use js::jsval::{ObjectValue, UndefinedValue};
32use js::rust::wrappers::DetachArrayBuffer;
33use js::rust::{
34 CustomAutoRooterGuard, Handle, MutableHandleObject,
35 MutableHandleValue as SafeMutableHandleValue,
36};
37#[cfg(feature = "webgpu")]
38use js::typedarray::{ArrayBuffer, HeapArrayBuffer};
39use js::typedarray::{
40 ArrayBufferU8, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
41 TypedArrayElementCreator,
42};
43
44use crate::dom::bindings::error::{Error, Fallible};
45use crate::dom::bindings::trace::RootedTraceableBox;
46#[cfg(feature = "webgpu")]
47use crate::dom::globalscope::GlobalScope;
48use crate::script_runtime::{CanGc, JSContext};
49
50pub(crate) enum BufferSource {
57 ArrayBufferView(RootedTraceableBox<Heap<*mut JSObject>>),
60
61 ArrayBuffer(RootedTraceableBox<Heap<*mut JSObject>>),
64}
65
66pub(crate) fn create_heap_buffer_source_with_length<T>(
67 cx: JSContext,
68 len: u32,
69 can_gc: CanGc,
70) -> Fallible<HeapBufferSource<T>>
71where
72 T: TypedArrayElement + TypedArrayElementCreator,
73 T::Element: Clone + Copy,
74{
75 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
76 let typed_array_result =
77 create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
78 if typed_array_result.is_err() {
79 return Err(Error::JSFailed);
80 }
81
82 Ok(HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(
83 RootedTraceableBox::from_box(Heap::boxed(*array.handle())),
84 )))
85}
86
87pub(crate) struct HeapBufferSource<T> {
88 buffer_source: BufferSource,
89 phantom: PhantomData<T>,
90}
91
92impl<T> Eq for HeapBufferSource<T> where T: TypedArrayElement {}
93
94impl<T> PartialEq for HeapBufferSource<T>
95where
96 T: TypedArrayElement,
97{
98 fn eq(&self, other: &Self) -> bool {
99 match &self.buffer_source {
100 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => match &other
101 .buffer_source
102 {
103 BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => {
104 std::ptr::eq(heap.get(), from_heap.get())
105 },
106 },
107 }
108 }
109}
110
111impl<T> HeapBufferSource<T>
112where
113 T: TypedArrayElement,
114{
115 pub(crate) fn new(buffer_source: BufferSource) -> HeapBufferSource<T> {
116 HeapBufferSource {
117 buffer_source,
118 phantom: PhantomData,
119 }
120 }
121
122 pub(crate) fn from_view(
123 chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
124 ) -> HeapBufferSource<T> {
125 HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(RootedTraceableBox::from_box(
126 Heap::boxed(unsafe { *chunk.underlying_object() }),
127 )))
128 }
129
130 pub(crate) fn default() -> Self {
131 HeapBufferSource {
132 buffer_source: BufferSource::ArrayBufferView(RootedTraceableBox::from_box(
133 Heap::boxed(std::ptr::null_mut()),
134 )),
135 phantom: PhantomData,
136 }
137 }
138
139 pub(crate) fn is_initialized(&self) -> bool {
140 match &self.buffer_source {
141 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
142 !buffer.get().is_null()
143 },
144 }
145 }
146
147 pub(crate) fn get_typed_array(&self) -> Result<TypedArray<T, *mut JSObject>, ()> {
148 TypedArray::from(match &self.buffer_source {
149 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
150 buffer.get()
151 },
152 })
153 }
154
155 pub(crate) fn get_buffer_view_value(
156 &self,
157 cx: JSContext,
158 mut handle_mut: SafeMutableHandleValue,
159 ) {
160 match &self.buffer_source {
161 BufferSource::ArrayBufferView(buffer) => {
162 rooted!(in(*cx) let value = ObjectValue(buffer.get()));
163 handle_mut.set(*value);
164 },
165 BufferSource::ArrayBuffer(_) => {
166 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
167 },
168 }
169 }
170
171 pub(crate) fn get_array_buffer_view_buffer(
172 &self,
173 cx: JSContext,
174 ) -> HeapBufferSource<ArrayBufferU8> {
175 match &self.buffer_source {
176 BufferSource::ArrayBufferView(buffer) => unsafe {
177 let mut is_shared = false;
178 rooted!(in (*cx) let view_buffer =
179 JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
180
181 HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(
182 RootedTraceableBox::from_box(Heap::boxed(*view_buffer.handle())),
183 ))
184 },
185 BufferSource::ArrayBuffer(_) => {
186 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
187 },
188 }
189 }
190
191 pub(crate) fn detach_buffer(&self, cx: JSContext) -> bool {
193 assert!(self.is_initialized());
194 match &self.buffer_source {
195 BufferSource::ArrayBufferView(buffer) => {
196 let mut is_shared = false;
197 unsafe {
198 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
200 rooted!(in (*cx) let view_buffer =
201 JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
202 debug_assert!(!is_shared);
204 DetachArrayBuffer(*cx, view_buffer.handle())
206 }
207 },
208 BufferSource::ArrayBuffer(buffer) => unsafe {
209 DetachArrayBuffer(*cx, Handle::from_raw(buffer.handle().into()))
210 },
211 }
212 }
213
214 pub(crate) fn typed_array_to_option(&self) -> Option<TypedArray<T, *mut JSObject>> {
215 if self.is_initialized() {
216 self.get_typed_array().ok()
217 } else {
218 warn!("Buffer not initialized.");
219 None
220 }
221 }
222
223 pub(crate) fn is_detached_buffer(&self, cx: JSContext) -> bool {
224 assert!(self.is_initialized());
225 match &self.buffer_source {
226 BufferSource::ArrayBufferView(buffer) => {
227 let mut is_shared = false;
228 unsafe {
229 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
230 rooted!(in (*cx) let view_buffer =
231 JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
232 debug_assert!(!is_shared);
233 IsDetachedArrayBufferObject(*view_buffer.handle())
234 }
235 },
236 BufferSource::ArrayBuffer(buffer) => unsafe {
237 IsDetachedArrayBufferObject(*buffer.handle())
238 },
239 }
240 }
241
242 pub(crate) fn viewed_buffer_array_byte_length(&self, cx: JSContext) -> usize {
243 assert!(self.is_initialized());
244 match &self.buffer_source {
245 BufferSource::ArrayBufferView(buffer) => {
246 let mut is_shared = false;
247 unsafe {
248 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
249 rooted!(in (*cx) let view_buffer =
250 JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
251 debug_assert!(!is_shared);
252 GetArrayBufferByteLength(*view_buffer.handle())
253 }
254 },
255 BufferSource::ArrayBuffer(buffer) => unsafe {
256 GetArrayBufferByteLength(*buffer.handle())
257 },
258 }
259 }
260
261 pub(crate) fn byte_length(&self) -> usize {
262 match &self.buffer_source {
263 BufferSource::ArrayBufferView(buffer) => unsafe {
264 JS_GetArrayBufferViewByteLength(*buffer.handle())
265 },
266 BufferSource::ArrayBuffer(buffer) => unsafe {
267 GetArrayBufferByteLength(*buffer.handle())
268 },
269 }
270 }
271
272 pub(crate) fn get_byte_offset(&self) -> usize {
273 match &self.buffer_source {
274 BufferSource::ArrayBufferView(buffer) => unsafe {
275 JS_GetArrayBufferViewByteOffset(*buffer.handle())
276 },
277 BufferSource::ArrayBuffer(_) => {
278 unreachable!("BufferSource::ArrayBuffer does not have a byte offset.")
279 },
280 }
281 }
282
283 pub(crate) fn get_typed_array_length(&self) -> usize {
284 match &self.buffer_source {
285 BufferSource::ArrayBufferView(buffer) => unsafe {
286 JS_GetTypedArrayLength(*buffer.handle())
287 },
288 BufferSource::ArrayBuffer(_) => {
289 unreachable!("BufferSource::ArrayBuffer does not have a length.")
290 },
291 }
292 }
293
294 pub(crate) fn has_typed_array_name(&self) -> bool {
296 match &self.buffer_source {
297 BufferSource::ArrayBufferView(buffer) => unsafe {
298 JS_IsTypedArrayObject(*buffer.handle())
299 },
300 BufferSource::ArrayBuffer(_) => false,
301 }
302 }
303
304 pub(crate) fn get_array_buffer_view_type(&self) -> Type {
305 match &self.buffer_source {
306 BufferSource::ArrayBufferView(buffer) => unsafe {
307 JS_GetArrayBufferViewType(*buffer.handle())
308 },
309 BufferSource::ArrayBuffer(_) => unreachable!("ArrayBuffer does not have a view type."),
310 }
311 }
312
313 pub(crate) fn is_array_buffer_object(&self) -> bool {
314 match &self.buffer_source {
315 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
316 IsArrayBufferObject(*heap.handle())
317 },
318 }
319 }
320}
321
322impl<T> HeapBufferSource<T>
323where
324 T: TypedArrayElement + TypedArrayElementCreator,
325 T::Element: Clone + Copy,
326{
327 pub(crate) fn acquire_data(&self, cx: JSContext) -> Result<Vec<T::Element>, ()> {
328 assert!(self.is_initialized());
329
330 typedarray!(in(*cx) let array: TypedArray = match &self.buffer_source {
331 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
332 => {
333 buffer.get()
334 },
335 });
336 let data = if let Ok(array) =
337 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
338 {
339 let data = array.to_vec();
340 let _ = self.detach_buffer(cx);
341 Ok(data)
342 } else {
343 Err(())
344 };
345
346 match &self.buffer_source {
347 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
348 buffer.set(ptr::null_mut());
349 },
350 }
351 data
352 }
353
354 pub(crate) fn copy_data_to(
355 &self,
356 cx: JSContext,
357 dest: &mut [T::Element],
358 source_start: usize,
359 length: usize,
360 ) -> Result<(), ()> {
361 assert!(self.is_initialized());
362 typedarray!(in(*cx) let array: TypedArray = match &self.buffer_source {
363 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
364 => {
365 buffer.get()
366 },
367 });
368 let Ok(array) =
369 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
370 else {
371 return Err(());
372 };
373 unsafe {
374 let slice = (*array).as_slice();
375 dest.copy_from_slice(&slice[source_start..length]);
376 }
377 Ok(())
378 }
379
380 pub(crate) fn copy_data_from(
381 &self,
382 cx: JSContext,
383 source: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
384 dest_start: usize,
385 length: usize,
386 ) -> Result<(), ()> {
387 assert!(self.is_initialized());
388 typedarray!(in(*cx) let mut array: TypedArray = match &self.buffer_source {
389 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
390 => {
391 buffer.get()
392 },
393 });
394 let Ok(mut array) =
395 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
396 else {
397 return Err(());
398 };
399 unsafe {
400 let slice = (*array).as_mut_slice();
401 let (_, dest) = slice.split_at_mut(dest_start);
402 dest[0..length].copy_from_slice(&source.as_slice()[0..length])
403 }
404 Ok(())
405 }
406
407 pub(crate) fn set_data(
408 &self,
409 cx: JSContext,
410 data: &[T::Element],
411 can_gc: CanGc,
412 ) -> Result<(), ()> {
413 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
414 let _: TypedArray<T, *mut JSObject> =
415 create_buffer_source(cx, data, array.handle_mut(), can_gc)?;
416
417 match &self.buffer_source {
418 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
419 buffer.set(*array);
420 },
421 }
422 Ok(())
423 }
424
425 pub(crate) fn clone_array_buffer(
427 &self,
428 cx: JSContext,
429 byte_offset: usize,
430 byte_length: usize,
431 ) -> Option<HeapBufferSource<ArrayBufferU8>> {
432 match &self.buffer_source {
433 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
434 let result = unsafe {
435 ArrayBufferClone(*cx, heap.handle().into(), byte_offset, byte_length)
436 };
437 if result.is_null() {
438 None
439 } else {
440 Some(HeapBufferSource::<ArrayBufferU8>::new(
441 BufferSource::ArrayBuffer(RootedTraceableBox::from_box(Heap::boxed(
442 result,
443 ))),
444 ))
445 }
446 },
447 }
448 }
449
450 pub(crate) fn can_copy_data_block_bytes(
453 &self,
454 cx: JSContext,
455 to_index: usize,
456 from_buffer: &HeapBufferSource<ArrayBufferU8>,
457 from_index: usize,
458 bytes_to_copy: usize,
459 ) -> bool {
460 assert!(self.is_array_buffer_object());
463
464 assert!(from_buffer.is_array_buffer_object());
467
468 match &self.buffer_source {
470 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
471 match &from_buffer.buffer_source {
472 BufferSource::ArrayBufferView(from_heap) |
473 BufferSource::ArrayBuffer(from_heap) => {
474 if std::ptr::eq(heap.get(), from_heap.get()) {
475 return false;
476 }
477 },
478 }
479 },
480 }
481
482 if self.is_detached_buffer(cx) {
484 return false;
485 }
486
487 if from_buffer.is_detached_buffer(cx) {
489 return false;
490 }
491
492 if to_index + bytes_to_copy > self.byte_length() {
494 return false;
495 }
496
497 if from_index + bytes_to_copy > from_buffer.byte_length() {
499 return false;
500 }
501
502 true
504 }
505
506 pub(crate) fn copy_data_block_bytes(
507 &self,
508 cx: JSContext,
509 dest_start: usize,
510 from_buffer: &HeapBufferSource<ArrayBufferU8>,
511 from_byte_offset: usize,
512 bytes_to_copy: usize,
513 ) -> bool {
514 match &self.buffer_source {
515 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
516 match &from_buffer.buffer_source {
517 BufferSource::ArrayBufferView(from_heap) |
518 BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData(
519 *cx,
520 heap.handle().into(),
521 dest_start,
522 from_heap.handle().into(),
523 from_byte_offset,
524 bytes_to_copy,
525 ),
526 }
527 },
528 }
529 }
530
531 pub(crate) fn can_transfer_array_buffer(&self, cx: JSContext) -> bool {
533 assert!(self.is_array_buffer_object());
536
537 if self.is_detached_buffer(cx) {
539 return false;
540 }
541
542 let mut is_defined = false;
545 match &self.buffer_source {
546 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
547 if !HasDefinedArrayBufferDetachKey(*cx, heap.handle().into(), &mut is_defined) {
548 return false;
549 }
550 },
551 }
552
553 !is_defined
554 }
555
556 pub(crate) fn transfer_array_buffer(
558 &self,
559 cx: JSContext,
560 ) -> Fallible<HeapBufferSource<ArrayBufferU8>> {
561 assert!(self.is_array_buffer_object());
562
563 assert!(!self.is_detached_buffer(cx));
565
566 let buffer_length = self.byte_length();
569
570 let buffer_data = match &self.buffer_source {
573 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => unsafe {
574 StealArrayBufferContents(*cx, buffer.handle().into())
575 },
576 };
577
578 if !self.detach_buffer(cx) {
582 rooted!(in(*cx) let mut rval = UndefinedValue());
583 unsafe {
584 assert!(JS_GetPendingException(*cx, rval.handle_mut().into()));
585 JS_ClearPendingException(*cx)
586 };
587
588 Err(Error::Type("can't transfer array buffer".to_owned()))
589 } else {
590 Ok(HeapBufferSource::<ArrayBufferU8>::new(
594 BufferSource::ArrayBuffer(RootedTraceableBox::from_box(Heap::boxed(unsafe {
595 NewArrayBufferWithContents(*cx, buffer_length, buffer_data)
596 }))),
597 ))
598 }
599 }
600}
601
602unsafe impl<T> crate::dom::bindings::trace::JSTraceable for HeapBufferSource<T> {
603 #[inline]
604 unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
605 match &self.buffer_source {
606 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
607 unsafe { buffer.trace(tracer) };
608 },
609 }
610 }
611}
612
613pub(crate) fn create_buffer_source<T>(
615 cx: JSContext,
616 data: &[T::Element],
617 mut dest: MutableHandleObject,
618 _can_gc: CanGc,
619) -> Result<TypedArray<T, *mut JSObject>, ()>
620where
621 T: TypedArrayElement + TypedArrayElementCreator,
622{
623 let res = unsafe {
624 TypedArray::<T, *mut JSObject>::create(*cx, CreateWith::Slice(data), dest.reborrow())
625 };
626
627 if res.is_err() {
628 Err(())
629 } else {
630 TypedArray::from(dest.get())
631 }
632}
633
634fn create_buffer_source_with_length<T>(
635 cx: JSContext,
636 len: usize,
637 mut dest: MutableHandleObject,
638 _can_gc: CanGc,
639) -> Result<TypedArray<T, *mut JSObject>, ()>
640where
641 T: TypedArrayElement + TypedArrayElementCreator,
642{
643 let res = unsafe {
644 TypedArray::<T, *mut JSObject>::create(*cx, CreateWith::Length(len), dest.reborrow())
645 };
646
647 if res.is_err() {
648 Err(())
649 } else {
650 TypedArray::from(dest.get())
651 }
652}
653
654pub(crate) fn byte_size(byte_type: Type) -> u64 {
655 match byte_type {
656 Type::Int8 | Type::Uint8 | Type::Uint8Clamped => 1,
657 Type::Int16 | Type::Uint16 | Type::Float16 => 2,
658 Type::Int32 | Type::Uint32 | Type::Float32 => 4,
659 Type::Int64 | Type::Float64 | Type::BigInt64 | Type::BigUint64 => 8,
660 Type::Simd128 => 16,
661 _ => unreachable!("invalid scalar type"),
662 }
663}
664
665#[derive(Clone, Eq, JSTraceable, MallocSizeOf, PartialEq)]
666pub(crate) enum Constructor {
667 DataView,
668 Name(
669 #[ignore_malloc_size_of = "mozjs"]
670 #[no_trace]
671 Type,
672 ),
673}
674
675pub(crate) fn create_buffer_source_with_constructor(
676 cx: JSContext,
677 constructor: &Constructor,
678 buffer_source: &HeapBufferSource<ArrayBufferU8>,
679 byte_offset: usize,
680 byte_length: usize,
681) -> Fallible<HeapBufferSource<ArrayBufferViewU8>> {
682 match &buffer_source.buffer_source {
683 BufferSource::ArrayBuffer(heap) => match constructor {
684 Constructor::DataView => Ok(HeapBufferSource::new(BufferSource::ArrayBufferView(
685 RootedTraceableBox::from_box(Heap::boxed(unsafe {
686 JS_NewDataView(*cx, heap.handle().into(), byte_offset, byte_length)
687 })),
688 ))),
689 Constructor::Name(name_type) => construct_typed_array(
690 cx,
691 name_type,
692 buffer_source,
693 byte_offset,
694 byte_length as i64,
695 ),
696 },
697 BufferSource::ArrayBufferView(_) => {
698 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
699 },
700 }
701}
702
703fn construct_typed_array(
705 cx: JSContext,
706 name_type: &Type,
707 buffer_source: &HeapBufferSource<ArrayBufferU8>,
708 byte_offset: usize,
709 byte_length: i64,
710) -> Fallible<HeapBufferSource<ArrayBufferViewU8>> {
711 match &buffer_source.buffer_source {
712 BufferSource::ArrayBuffer(heap) => {
713 let array_view = unsafe {
714 match name_type {
715 Type::Int8 => JS_NewInt8ArrayWithBuffer(
716 *cx,
717 heap.handle().into(),
718 byte_offset,
719 byte_length,
720 ),
721 Type::Uint8 => JS_NewUint8ArrayWithBuffer(
722 *cx,
723 heap.handle().into(),
724 byte_offset,
725 byte_length,
726 ),
727 Type::Uint16 => JS_NewUint16ArrayWithBuffer(
728 *cx,
729 heap.handle().into(),
730 byte_offset,
731 byte_length,
732 ),
733 Type::Int16 => JS_NewInt16ArrayWithBuffer(
734 *cx,
735 heap.handle().into(),
736 byte_offset,
737 byte_length,
738 ),
739 Type::Int32 => JS_NewInt32ArrayWithBuffer(
740 *cx,
741 heap.handle().into(),
742 byte_offset,
743 byte_length,
744 ),
745 Type::Uint32 => JS_NewUint32ArrayWithBuffer(
746 *cx,
747 heap.handle().into(),
748 byte_offset,
749 byte_length,
750 ),
751 Type::Float32 => JS_NewFloat32ArrayWithBuffer(
752 *cx,
753 heap.handle().into(),
754 byte_offset,
755 byte_length,
756 ),
757 Type::Float64 => JS_NewFloat64ArrayWithBuffer(
758 *cx,
759 heap.handle().into(),
760 byte_offset,
761 byte_length,
762 ),
763 Type::Uint8Clamped => JS_NewUint8ClampedArrayWithBuffer(
764 *cx,
765 heap.handle().into(),
766 byte_offset,
767 byte_length,
768 ),
769 Type::BigInt64 => JS_NewBigInt64ArrayWithBuffer(
770 *cx,
771 heap.handle().into(),
772 byte_offset,
773 byte_length,
774 ),
775 Type::BigUint64 => JS_NewBigUint64ArrayWithBuffer(
776 *cx,
777 heap.handle().into(),
778 byte_offset,
779 byte_length,
780 ),
781 Type::Float16 => JS_NewFloat16ArrayWithBuffer(
782 *cx,
783 heap.handle().into(),
784 byte_offset,
785 byte_length,
786 ),
787 Type::Int64 | Type::Simd128 | Type::MaxTypedArrayViewType => {
788 unreachable!("Invalid TypedArray type")
789 },
790 }
791 };
792
793 Ok(HeapBufferSource::new(BufferSource::ArrayBufferView(
794 RootedTraceableBox::from_box(Heap::boxed(array_view)),
795 )))
796 },
797 BufferSource::ArrayBufferView(_) => {
798 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
799 },
800 }
801}
802
803pub(crate) fn create_array_buffer_with_size(
804 cx: JSContext,
805 size: usize,
806) -> Fallible<HeapBufferSource<ArrayBufferU8>> {
807 let result = unsafe { NewArrayBuffer(*cx, size) };
808 if result.is_null() {
809 rooted!(in(*cx) let mut rval = UndefinedValue());
810 unsafe {
811 assert!(JS_GetPendingException(*cx, rval.handle_mut().into()));
812 JS_ClearPendingException(*cx)
813 };
814
815 Err(Error::Type("can't create array buffer".to_owned()))
816 } else {
817 Ok(HeapBufferSource::<ArrayBufferU8>::new(
818 BufferSource::ArrayBuffer(RootedTraceableBox::from_box(Heap::boxed(result))),
819 ))
820 }
821}
822
823#[cfg(feature = "webgpu")]
824#[derive(JSTraceable, MallocSizeOf)]
825pub(crate) struct DataBlock {
826 #[conditional_malloc_size_of]
827 data: Arc<Box<[u8]>>,
828 data_views: Vec<DataView>,
830}
831
832#[cfg(feature = "webgpu")]
835fn range_overlap<T: std::cmp::PartialOrd>(range1: &Range<T>, range2: &Range<T>) -> bool {
836 range1.start < range2.end && range2.start < range1.end
837}
838
839#[cfg(feature = "webgpu")]
840impl DataBlock {
841 pub(crate) fn new_zeroed(size: usize) -> Self {
842 let data = vec![0; size];
843 Self {
844 data: Arc::new(data.into_boxed_slice()),
845 data_views: Vec::new(),
846 }
847 }
848
849 pub(crate) fn load(&mut self, src: &[u8]) {
851 Arc::get_mut(&mut self.data).unwrap().clone_from_slice(src)
853 }
854
855 pub(crate) fn data(&mut self) -> &mut [u8] {
857 Arc::get_mut(&mut self.data).unwrap()
859 }
860
861 pub(crate) fn clear_views(&mut self) {
862 self.data_views.clear()
863 }
864
865 pub(crate) fn view(&mut self, range: Range<usize>, _can_gc: CanGc) -> Result<&DataView, ()> {
867 if self
868 .data_views
869 .iter()
870 .any(|view| range_overlap(&view.range, &range))
871 {
872 return Err(());
873 }
874 let cx = GlobalScope::get_cx();
875 unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
878 #[allow(clippy::from_raw_with_void_ptr)]
882 drop(unsafe { Arc::from_raw(free_user_data as *const _) });
883 }
884 let raw: *mut Box<[u8]> = Arc::into_raw(Arc::clone(&self.data)) as _;
885 rooted!(in(*cx) let object = unsafe {
886 NewExternalArrayBuffer(
887 *cx,
888 range.end - range.start,
889 (&mut (*raw))[range.clone()].as_mut_ptr() as _,
891 Some(free_func),
892 raw as _,
893 )
894 });
895 self.data_views.push(DataView {
896 range,
897 buffer: HeapArrayBuffer::from(*object).unwrap(),
898 });
899 Ok(self.data_views.last().unwrap())
900 }
901}
902
903#[cfg(feature = "webgpu")]
904#[derive(JSTraceable, MallocSizeOf)]
905#[cfg_attr(crown, allow(crown::unrooted_must_root))]
906pub(crate) struct DataView {
907 #[no_trace]
908 range: Range<usize>,
909 #[ignore_malloc_size_of = "defined in mozjs"]
910 buffer: HeapArrayBuffer,
911}
912
913#[cfg(feature = "webgpu")]
914impl DataView {
915 pub(crate) fn array_buffer(&self) -> ArrayBuffer {
916 unsafe { ArrayBuffer::from(self.buffer.underlying_object().get()).unwrap() }
917 }
918}
919
920#[cfg(feature = "webgpu")]
921impl Drop for DataView {
922 #[allow(unsafe_code)]
923 fn drop(&mut self) {
924 let cx = GlobalScope::get_cx();
925 assert!(unsafe {
926 js::jsapi::DetachArrayBuffer(*cx, self.buffer.underlying_object().handle())
927 })
928 }
929}