napi_calm_down/js_values/
arraybuffer.rs

1use std::ops::{Deref, DerefMut};
2use std::os::raw::c_void;
3use std::ptr;
4use std::slice;
5
6use crate::bindgen_runtime::{TypeName, ValidateNapiValue};
7use crate::{
8  check_status, sys, Error, JsUnknown, NapiValue, Ref, Result, Status, Value, ValueType,
9};
10
11pub struct JsArrayBuffer(pub(crate) Value);
12
13impl TypeName for JsArrayBuffer {
14  fn type_name() -> &'static str {
15    "ArrayBuffer"
16  }
17
18  fn value_type() -> ValueType {
19    ValueType::Object
20  }
21}
22
23impl ValidateNapiValue for JsArrayBuffer {
24  unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
25    let mut is_array_buffer = false;
26    check_status!(unsafe { sys::napi_is_arraybuffer(env, napi_val, &mut is_array_buffer) })?;
27    if !is_array_buffer {
28      return Err(Error::new(
29        Status::InvalidArg,
30        "Value is not an array buffer".to_owned(),
31      ));
32    }
33    Ok(ptr::null_mut())
34  }
35}
36
37pub struct JsArrayBufferValue {
38  pub value: JsArrayBuffer,
39  len: usize,
40  data: *mut c_void,
41}
42
43pub struct JsTypedArray(pub(crate) Value);
44
45impl TypeName for JsTypedArray {
46  fn type_name() -> &'static str {
47    "TypedArray"
48  }
49
50  fn value_type() -> ValueType {
51    ValueType::Object
52  }
53}
54
55pub struct JsTypedArrayValue {
56  pub arraybuffer: JsArrayBuffer,
57  data: *mut c_void,
58  pub byte_offset: usize,
59  pub length: usize,
60  pub typedarray_type: TypedArrayType,
61}
62
63pub struct JsDataView(pub(crate) Value);
64
65impl TypeName for JsDataView {
66  fn type_name() -> &'static str {
67    "DataView"
68  }
69
70  fn value_type() -> ValueType {
71    ValueType::Object
72  }
73}
74
75pub struct JsDataViewValue {
76  pub arraybuffer: JsArrayBuffer,
77  _data: *mut c_void,
78  pub byte_offset: u64,
79  pub length: u64,
80}
81
82#[repr(i32)]
83#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
84#[non_exhaustive]
85pub enum TypedArrayType {
86  Int8 = 0,
87  Uint8,
88  Uint8Clamped,
89  Int16,
90  Uint16,
91  Int32,
92  Uint32,
93  Float32,
94  Float64,
95  #[cfg(feature = "napi6")]
96  BigInt64,
97  #[cfg(feature = "napi6")]
98  BigUint64,
99
100  /// compatible with higher versions
101  Unknown = 1024,
102}
103
104impl From<sys::napi_typedarray_type> for TypedArrayType {
105  fn from(value: sys::napi_typedarray_type) -> Self {
106    match value {
107      sys::TypedarrayType::int8_array => Self::Int8,
108      sys::TypedarrayType::uint8_array => Self::Uint8,
109      sys::TypedarrayType::uint8_clamped_array => Self::Uint8Clamped,
110      sys::TypedarrayType::int16_array => Self::Int16,
111      sys::TypedarrayType::uint16_array => Self::Uint16,
112      sys::TypedarrayType::int32_array => Self::Int32,
113      sys::TypedarrayType::uint32_array => Self::Uint32,
114      sys::TypedarrayType::float32_array => Self::Float32,
115      sys::TypedarrayType::float64_array => Self::Float64,
116      #[cfg(feature = "napi6")]
117      sys::TypedarrayType::bigint64_array => Self::BigInt64,
118      #[cfg(feature = "napi6")]
119      sys::TypedarrayType::biguint64_array => Self::BigUint64,
120      _ => Self::Unknown,
121    }
122  }
123}
124
125impl From<TypedArrayType> for sys::napi_typedarray_type {
126  fn from(value: TypedArrayType) -> sys::napi_typedarray_type {
127    value as i32
128  }
129}
130
131impl JsArrayBuffer {
132  #[cfg(feature = "napi7")]
133  pub fn detach(self) -> Result<()> {
134    check_status!(unsafe { sys::napi_detach_arraybuffer(self.0.env, self.0.value) })
135  }
136
137  #[cfg(feature = "napi7")]
138  pub fn is_detached(&self) -> Result<bool> {
139    let mut is_detached = false;
140    check_status!(unsafe {
141      sys::napi_is_detached_arraybuffer(self.0.env, self.0.value, &mut is_detached)
142    })?;
143    Ok(is_detached)
144  }
145
146  pub fn into_value(self) -> Result<JsArrayBufferValue> {
147    let mut data = ptr::null_mut();
148    let mut len: usize = 0;
149    check_status!(unsafe {
150      sys::napi_get_arraybuffer_info(self.0.env, self.0.value, &mut data, &mut len)
151    })?;
152    Ok(JsArrayBufferValue {
153      data,
154      value: self,
155      len,
156    })
157  }
158
159  pub fn into_typedarray(
160    self,
161    typedarray_type: TypedArrayType,
162    length: usize,
163    byte_offset: usize,
164  ) -> Result<JsTypedArray> {
165    let mut typedarray_value = ptr::null_mut();
166    check_status!(unsafe {
167      sys::napi_create_typedarray(
168        self.0.env,
169        typedarray_type.into(),
170        length,
171        self.0.value,
172        byte_offset,
173        &mut typedarray_value,
174      )
175    })?;
176    Ok(JsTypedArray(Value {
177      env: self.0.env,
178      value: typedarray_value,
179      value_type: ValueType::Object,
180    }))
181  }
182
183  pub fn into_dataview(self, length: usize, byte_offset: usize) -> Result<JsDataView> {
184    let mut dataview_value = ptr::null_mut();
185    check_status!(unsafe {
186      sys::napi_create_dataview(
187        self.0.env,
188        length,
189        self.0.value,
190        byte_offset,
191        &mut dataview_value,
192      )
193    })?;
194    Ok(JsDataView(Value {
195      env: self.0.env,
196      value: dataview_value,
197      value_type: ValueType::Object,
198    }))
199  }
200
201  pub fn into_ref(self) -> Result<Ref<JsArrayBufferValue>> {
202    Ref::new(self.0, 1, self.into_value()?)
203  }
204}
205
206impl JsArrayBufferValue {
207  pub fn new(value: JsArrayBuffer, data: *mut c_void, len: usize) -> Self {
208    JsArrayBufferValue { value, len, data }
209  }
210
211  pub fn into_raw(self) -> JsArrayBuffer {
212    self.value
213  }
214
215  pub fn into_unknown(self) -> JsUnknown {
216    unsafe { JsUnknown::from_raw_unchecked(self.value.0.env, self.value.0.value) }
217  }
218}
219
220impl AsRef<[u8]> for JsArrayBufferValue {
221  fn as_ref(&self) -> &[u8] {
222    if self.data.is_null() {
223      return &[];
224    }
225    unsafe { slice::from_raw_parts(self.data as *const u8, self.len) }
226  }
227}
228
229impl AsMut<[u8]> for JsArrayBufferValue {
230  fn as_mut(&mut self) -> &mut [u8] {
231    if self.data.is_null() {
232      return &mut [];
233    }
234    unsafe { slice::from_raw_parts_mut(self.data as *mut u8, self.len) }
235  }
236}
237
238impl Deref for JsArrayBufferValue {
239  type Target = [u8];
240
241  fn deref(&self) -> &Self::Target {
242    self.as_ref()
243  }
244}
245
246impl DerefMut for JsArrayBufferValue {
247  fn deref_mut(&mut self) -> &mut Self::Target {
248    self.as_mut()
249  }
250}
251
252impl JsTypedArray {
253  /// get TypeArray info
254  /// <https://nodejs.org/api/n-api.html#n_api_napi_get_typedarray_info>
255  ///
256  /// ***Warning***: Use caution while using this API since the underlying data buffer is managed by the VM.
257  pub fn into_value(self) -> Result<JsTypedArrayValue> {
258    let mut typedarray_type = 0;
259    let mut len = 0;
260    let mut data = ptr::null_mut();
261    let mut arraybuffer_value = ptr::null_mut();
262    let mut byte_offset = 0;
263    check_status!(unsafe {
264      sys::napi_get_typedarray_info(
265        self.0.env,
266        self.0.value,
267        &mut typedarray_type,
268        &mut len,
269        &mut data,
270        &mut arraybuffer_value,
271        &mut byte_offset,
272      )
273    })?;
274
275    Ok(JsTypedArrayValue {
276      data,
277      length: len,
278      byte_offset,
279      typedarray_type: typedarray_type.into(),
280      arraybuffer: unsafe { JsArrayBuffer::from_raw_unchecked(self.0.env, arraybuffer_value) },
281    })
282  }
283}
284
285impl JsTypedArrayValue {
286  #[inline]
287  fn is_valid_as_ref(&self, dest_type: TypedArrayType) {
288    // deref `Uint8ClampedArray` as `&[u8]` is valid
289    if self.typedarray_type == TypedArrayType::Uint8Clamped && dest_type == TypedArrayType::Uint8 {
290      return;
291    }
292    if self.typedarray_type != dest_type {
293      panic!(
294        "invalid typedarray type: expected {:?}, got {:?}",
295        dest_type, self.typedarray_type
296      );
297    }
298  }
299}
300
301macro_rules! impl_as_ref {
302  ($ref_type:ident, $expect_type:expr) => {
303    impl AsRef<[$ref_type]> for JsTypedArrayValue {
304      fn as_ref(&self) -> &[$ref_type] {
305        self.is_valid_as_ref($expect_type);
306        unsafe { slice::from_raw_parts(self.data as *const $ref_type, self.length) }
307      }
308    }
309
310    impl AsMut<[$ref_type]> for JsTypedArrayValue {
311      fn as_mut(&mut self) -> &mut [$ref_type] {
312        self.is_valid_as_ref($expect_type);
313        unsafe { slice::from_raw_parts_mut(self.data as *mut $ref_type, self.length) }
314      }
315    }
316  };
317}
318
319impl_as_ref!(u8, TypedArrayType::Uint8);
320impl_as_ref!(i8, TypedArrayType::Int8);
321impl_as_ref!(u16, TypedArrayType::Uint16);
322impl_as_ref!(i16, TypedArrayType::Int16);
323impl_as_ref!(u32, TypedArrayType::Uint32);
324impl_as_ref!(i32, TypedArrayType::Int32);
325impl_as_ref!(f32, TypedArrayType::Float32);
326impl_as_ref!(f64, TypedArrayType::Float64);
327#[cfg(feature = "napi6")]
328impl_as_ref!(i64, TypedArrayType::BigInt64);
329#[cfg(feature = "napi6")]
330impl_as_ref!(u64, TypedArrayType::BigUint64);
331
332impl JsDataView {
333  pub fn into_value(self) -> Result<JsDataViewValue> {
334    let mut length = 0u64;
335    let mut byte_offset = 0u64;
336    let mut arraybuffer_value = ptr::null_mut();
337    let mut data = ptr::null_mut();
338
339    check_status!(unsafe {
340      sys::napi_get_dataview_info(
341        self.0.env,
342        self.0.value,
343        &mut length as *mut u64 as *mut _,
344        &mut data,
345        &mut arraybuffer_value,
346        &mut byte_offset as *mut u64 as *mut _,
347      )
348    })?;
349    Ok(JsDataViewValue {
350      arraybuffer: unsafe { JsArrayBuffer::from_raw_unchecked(self.0.env, arraybuffer_value) },
351      byte_offset,
352      length,
353      _data: data,
354    })
355  }
356}