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