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 /// Turn a raw pointer (from napi) pointing to the inner `*mut TaggedObject<T>` into a reference inner object.
46 ///
47 /// # Safety
48 /// The `unknown_tagged_object` raw pointer must point to an `TaggedObject<T>` struct, which
49 /// is essentially the pointer which napi-rs returns to the NAPI api.
50 unsafe fn from_raw_impl(
51 unknown_tagged_object: *mut std::ffi::c_void,
52 ) -> Option<*mut TaggedObject<T>> {
53 let type_id = unknown_tagged_object as *const TypeId;
54 if unsafe { *type_id } == TypeId::of::<T>() {
55 let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
56 Some(tagged_object)
57 } else {
58 None
59 }
60 }
61
62 /// Turn a raw pointer (from napi) pointing to the inner `*mut TaggedObject<T>` into a reference inner object.
63 ///
64 /// # Safety
65 /// The `unknown_tagged_object` raw pointer must point to an `TaggedObject<T>` struct, which
66 /// is essentially the pointer which napi-rs returns to the NAPI api.
67 ///
68 /// Additionally, you must ensure that there are no other live mutable references to the `T` for
69 /// the duration of the lifetime of the returned mutable reference.
70 pub unsafe fn inner_from_raw_mut(
71 unknown_tagged_object: *mut std::ffi::c_void,
72 ) -> Option<&'static mut T> {
73 Self::from_raw_impl(unknown_tagged_object)
74 .and_then(|tagged_object| unsafe { (*tagged_object).object.as_mut() })
75 }
76
77 /// Turn a raw pointer (from napi) pointing to the inner `*mut TaggedObject<T>` into a reference inner object.
78 ///
79 /// # Safety
80 /// The `unknown_tagged_object` raw pointer must point to an `TaggedObject<T>` struct, which
81 /// is essentially the pointer which napi-rs returns to the NAPI api.
82 ///
83 /// Additionally, you must ensure that there are no other live mutable references to the `T` for
84 /// the duration of the lifetime of the returned immutable reference.
85 pub unsafe fn inner_from_raw(unknown_tagged_object: *mut std::ffi::c_void) -> Option<&'static T> {
86 Self::from_raw_impl(unknown_tagged_object)
87 .and_then(|tagged_object| unsafe { (*tagged_object).object.as_ref() })
88 }
89
90 /// `size_hint` is a value to tell Node.js GC how much memory is used by this `External` object.
91 ///
92 /// If getting the exact `size_hint` is difficult, you can provide an approximate value, it's only effect to the GC.
93 ///
94 /// If your `External` object is not effect to GC, you can use `External::new` instead.
95 pub fn new_with_size_hint(value: T, size_hint: usize) -> Self {
96 Self {
97 obj: Box::into_raw(Box::new(TaggedObject::new(value))),
98 size_hint,
99 adjusted_size: 0,
100 }
101 }
102}
103
104impl<T: 'static> FromNapiValue for External<T> {
105 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
106 let mut unknown_tagged_object: *mut c_void = std::ptr::null_mut();
107 check_status!(
108 unsafe { sys::napi_get_value_external(env, napi_val, &mut unknown_tagged_object) },
109 "Failed to get external value"
110 )?;
111
112 let type_id: *const TypeId = unknown_tagged_object as *const TypeId;
113 if unsafe { *type_id } == TypeId::of::<T>() {
114 let tagged_object: *mut TaggedObject = unknown_tagged_object as *mut TaggedObject<T>;
115 Ok(Self {
116 obj: tagged_object,
117 size_hint: 0,
118 adjusted_size: 0,
119 })
120 } else {
121 Err(Error::new(
122 Status::InvalidArg,
123 reason:"T on `get_value_external` is not the type of wrapped object".to_owned(),
124 ))
125 }
126 }
127}
128
129impl<T: 'static> AsRef<T> for External<T> {
130 fn as_ref(&self) -> &T {
131 unsafe { Box::leak(Box::from_raw(self.obj)).object.as_ref().unwrap() }
132 }
133}
134
135impl<T: 'static> AsMut<T> for External<T> {
136 fn as_mut(&mut self) -> &mut T {
137 unsafe { Box::leak(Box::from_raw(self.obj)).object.as_mut().unwrap() }
138 }
139}
140
141impl<T: 'static> Deref for External<T> {
142 type Target = T;
143
144 fn deref(&self) -> &Self::Target {
145 self.as_ref()
146 }
147}
148
149impl<T: 'static> DerefMut for External<T> {
150 fn deref_mut(&mut self) -> &mut Self::Target {
151 self.as_mut()
152 }
153}
154
155impl<T: 'static> ToNapiValue for External<T> {
156 unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> crate::Result<sys::napi_value> {
157 let mut napi_value = std::ptr::null_mut();
158 check_status!(
159 unsafe {
160 sys::napi_create_external(
161 env,
162 val.obj as *mut _,
163 Some(crate::raw_finalize::<T>),
164 Box::into_raw(Box::new(Some(val.size_hint as i64))) as *mut _,
165 &mut napi_value,
166 )
167 },
168 "Create external value failed"
169 )?;
170 #[cfg(not(target_family = "wasm"))]
171 {
172 let mut adjusted_external_memory_size = std::mem::MaybeUninit::new(0);
173
174 if val.size_hint != 0 {
175 check_status!(
176 unsafe {
177 sys::napi_adjust_external_memory(
178 env,
179 val.size_hint as i64,
180 adjusted_external_memory_size.as_mut_ptr(),
181 )
182 },
183 "Adjust external memory failed"
184 )?;
185 };
186
187 val.adjusted_size = unsafe { adjusted_external_memory_size.assume_init() };
188 }
189
190 Ok(napi_value)
191 }
192}
193