ion/object/typedarray/
buffer.rs1use std::ffi::c_void;
8use std::ops::{Deref, DerefMut};
9use std::{ptr, slice};
10
11use mozjs::jsapi::{
12 ArrayBufferClone, ArrayBufferCopyData, DetachArrayBuffer, GetArrayBufferMaybeSharedLengthAndData,
13 IsArrayBufferObjectMaybeShared, IsDetachedArrayBufferObject, JSObject, NewArrayBufferWithContents,
14 NewExternalArrayBuffer, StealArrayBufferContents,
15};
16use mozjs::typedarray::CreateWith;
17
18use crate::utils::BoxExt as _;
19use crate::{Context, Error, ErrorKind, Local, Object, Result};
20
21#[derive(Debug)]
22pub struct ArrayBuffer<'ab> {
23 buffer: Local<'ab, *mut JSObject>,
24}
25
26impl<'ab> ArrayBuffer<'ab> {
27 fn create_with(cx: &'ab Context, with: CreateWith<u8>) -> Option<ArrayBuffer<'ab>> {
28 let mut buffer = Object::null(cx);
29 unsafe { mozjs::typedarray::ArrayBuffer::create(cx.as_ptr(), with, buffer.handle_mut()).ok()? };
30 Some(ArrayBuffer { buffer: buffer.into_local() })
31 }
32
33 pub fn new(cx: &Context, len: usize) -> Option<ArrayBuffer<'_>> {
35 ArrayBuffer::create_with(cx, CreateWith::Length(len))
36 }
37
38 pub fn copy_from_bytes(cx: &'ab Context, bytes: &[u8]) -> Option<ArrayBuffer<'ab>> {
40 ArrayBuffer::create_with(cx, CreateWith::Slice(bytes))
41 }
42
43 pub fn from_vec(cx: &Context, bytes: Vec<u8>) -> Option<ArrayBuffer<'_>> {
45 ArrayBuffer::from_boxed_slice(cx, bytes.into_boxed_slice())
46 }
47
48 pub fn from_boxed_slice(cx: &Context, bytes: Box<[u8]>) -> Option<ArrayBuffer<'_>> {
50 unsafe extern "C" fn free_external_array_buffer(contents: *mut c_void, data: *mut c_void) {
51 let _ = unsafe { Box::from_raw_parts(contents.cast::<u8>(), data as usize) };
52 }
53
54 let (ptr, len) = Box::into_raw_parts(bytes);
55 let buffer = unsafe {
56 NewExternalArrayBuffer(
57 cx.as_ptr(),
58 len,
59 ptr.cast(),
60 Some(free_external_array_buffer),
61 len as *mut c_void,
62 )
63 };
64
65 if buffer.is_null() {
66 None
67 } else {
68 Some(ArrayBuffer { buffer: cx.root(buffer) })
69 }
70 }
71
72 pub fn from(object: Local<*mut JSObject>) -> Option<ArrayBuffer> {
73 ArrayBuffer::is_array_buffer(object.get()).then_some(ArrayBuffer { buffer: object })
74 }
75
76 pub unsafe fn from_unchecked(object: Local<*mut JSObject>) -> ArrayBuffer {
77 ArrayBuffer { buffer: object }
78 }
79
80 pub fn data(&self) -> (*mut u8, usize, bool) {
84 let mut len = 0;
85 let mut shared = false;
86 let mut data = ptr::null_mut();
87 unsafe { GetArrayBufferMaybeSharedLengthAndData(self.get(), &raw mut len, &raw mut shared, &raw mut data) };
88 (data, len, shared)
89 }
90
91 pub fn is_empty(&self) -> bool {
92 self.len() == 0
93 }
94
95 pub fn len(&self) -> usize {
96 self.data().1
97 }
98
99 pub unsafe fn as_slice(&self) -> &[u8] {
103 let (ptr, len, _) = self.data();
104 unsafe { slice::from_raw_parts(ptr, len) }
105 }
106
107 #[expect(clippy::mut_from_ref)]
111 pub unsafe fn as_mut_slice(&self) -> &mut [u8] {
112 let (ptr, len, _) = self.data();
113 unsafe { slice::from_raw_parts_mut(ptr, len) }
114 }
115
116 pub fn clone<'cx>(&self, cx: &'cx Context, offset: usize, len: usize) -> Option<ArrayBuffer<'cx>> {
118 let buffer = unsafe { ArrayBufferClone(cx.as_ptr(), self.handle().into(), offset, len) };
119 if buffer.is_null() {
120 None
121 } else {
122 Some(ArrayBuffer { buffer: cx.root(buffer) })
123 }
124 }
125
126 pub fn copy_data_to(
129 &self, cx: &Context, to: &ArrayBuffer, from_index: usize, to_index: usize, count: usize,
130 ) -> bool {
131 unsafe {
132 ArrayBufferCopyData(
133 cx.as_ptr(),
134 to.handle().into(),
135 to_index,
136 self.handle().into(),
137 from_index,
138 count,
139 )
140 }
141 }
142
143 pub fn detach(&self, cx: &Context) -> bool {
144 unsafe { DetachArrayBuffer(cx.as_ptr(), self.handle().into()) }
145 }
146
147 pub fn transfer<'cx>(&self, cx: &'cx Context) -> Result<ArrayBuffer<'cx>> {
148 let len = self.len();
149 let data = unsafe { StealArrayBufferContents(cx.as_ptr(), self.handle().into()) };
150 if data.is_null() {
151 return Err(Error::new("ArrayBuffer transfer failed", ErrorKind::Normal));
152 }
153 let buffer = cx.root(unsafe { NewArrayBufferWithContents(cx.as_ptr(), len, data) });
154 if buffer.handle().is_null() {
155 return Err(Error::new("ArrayBuffer transfer failed", ErrorKind::Normal));
156 }
157 Ok(ArrayBuffer { buffer })
158 }
159
160 pub fn is_detached(&self) -> bool {
161 unsafe { IsDetachedArrayBufferObject(self.get()) }
162 }
163
164 pub fn is_shared(&self) -> bool {
165 self.data().2
166 }
167
168 #[expect(clippy::not_unsafe_ptr_arg_deref)]
170 pub fn is_array_buffer(object: *mut JSObject) -> bool {
171 unsafe { IsArrayBufferObjectMaybeShared(object) }
172 }
173}
174
175impl<'ab> Deref for ArrayBuffer<'ab> {
176 type Target = Local<'ab, *mut JSObject>;
177
178 fn deref(&self) -> &Self::Target {
179 &self.buffer
180 }
181}
182
183impl DerefMut for ArrayBuffer<'_> {
184 fn deref_mut(&mut self) -> &mut Self::Target {
185 &mut self.buffer
186 }
187}