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