edon/napi/js_values/
arraybuffer.rs

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