napi_h/bindgen_runtime/js_values/
external.rs

1use std::{
2  any::TypeId,
3  ops::{Deref, DerefMut},
4};
5
6use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};
7use crate::{check_status, sys, Error, Status, TaggedObject};
8
9pub struct External<T: 'static> {
10  obj: *mut TaggedObject<T>,
11  size_hint: usize,
12  pub adjusted_size: i64,
13}
14
15unsafe impl<T: 'static + Send> Send for External<T> {}
16unsafe impl<T: 'static + Sync> Sync for External<T> {}
17
18impl<T: 'static> TypeName for External<T> {
19  fn type_name() -> &'static str {
20    "External"
21  }
22
23  fn value_type() -> crate::ValueType {
24    crate::ValueType::External
25  }
26}
27
28impl<T: 'static> From<T> for External<T> {
29  fn from(t: T) -> Self {
30    External::new(t)
31  }
32}
33
34impl<T: 'static> ValidateNapiValue for External<T> {}
35
36impl<T: 'static> External<T> {
37  pub fn new(value: T) -> Self {
38    Self {
39      obj: Box::into_raw(Box::new(TaggedObject::new(value))),
40      size_hint: 0,
41      adjusted_size: 0,
42    }
43  }
44
45  /// `size_hint` is a value to tell Node.js GC how much memory is used by this `External` object.
46  ///
47  /// If getting the exact `size_hint` is difficult, you can provide an approximate value, it's only effect to the GC.
48  ///
49  /// If your `External` object is not effect to GC, you can use `External::new` instead.
50  pub fn new_with_size_hint(value: T, size_hint: usize) -> Self {
51    Self {
52      obj: Box::into_raw(Box::new(TaggedObject::new(value))),
53      size_hint,
54      adjusted_size: 0,
55    }
56  }
57}
58
59impl<T: 'static> FromNapiValue for External<T> {
60  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
61    let mut unknown_tagged_object = std::ptr::null_mut();
62    check_status!(
63      unsafe { sys::napi_get_value_external(env, napi_val, &mut unknown_tagged_object) },
64      "Failed to get external value"
65    )?;
66
67    let type_id = unknown_tagged_object as *const TypeId;
68    if unsafe { *type_id } == TypeId::of::<T>() {
69      let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
70      Ok(Self {
71        obj: tagged_object,
72        size_hint: 0,
73        adjusted_size: 0,
74      })
75    } else {
76      Err(Error::new(
77        Status::InvalidArg,
78        "T on `get_value_external` is not the type of wrapped object".to_owned(),
79      ))
80    }
81  }
82}
83
84impl<T: 'static> AsRef<T> for External<T> {
85  fn as_ref(&self) -> &T {
86    unsafe { Box::leak(Box::from_raw(self.obj)).object.as_ref().unwrap() }
87  }
88}
89
90impl<T: 'static> AsMut<T> for External<T> {
91  fn as_mut(&mut self) -> &mut T {
92    unsafe { Box::leak(Box::from_raw(self.obj)).object.as_mut().unwrap() }
93  }
94}
95
96impl<T: 'static> Deref for External<T> {
97  type Target = T;
98
99  fn deref(&self) -> &Self::Target {
100    self.as_ref()
101  }
102}
103
104impl<T: 'static> DerefMut for External<T> {
105  fn deref_mut(&mut self) -> &mut Self::Target {
106    self.as_mut()
107  }
108}
109
110impl<T: 'static> ToNapiValue for External<T> {
111  unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> crate::Result<sys::napi_value> {
112    let mut napi_value = std::ptr::null_mut();
113    check_status!(
114      unsafe {
115        sys::napi_create_external(
116          env,
117          val.obj as *mut _,
118          Some(crate::raw_finalize::<T>),
119          Box::into_raw(Box::new(Some(val.size_hint as i64))) as *mut _,
120          &mut napi_value,
121        )
122      },
123      "Create external value failed"
124    )?;
125
126    let mut adjusted_external_memory_size = std::mem::MaybeUninit::new(0);
127
128    if val.size_hint != 0 {
129      check_status!(
130        unsafe {
131          sys::napi_adjust_external_memory(
132            env,
133            val.size_hint as i64,
134            adjusted_external_memory_size.as_mut_ptr(),
135          )
136        },
137        "Adjust external memory failed"
138      )?;
139    };
140
141    val.adjusted_size = unsafe { adjusted_external_memory_size.assume_init() };
142
143    Ok(napi_value)
144  }
145}