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