1 | use std::{ |
2 | any::TypeId, |
3 | ops::{Deref, DerefMut}, |
4 | }; |
5 | |
6 | use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue}; |
7 | use crate::{check_status, sys, Error, Status, TaggedObject}; |
8 | |
9 | pub struct External<T: 'static> { |
10 | obj: *mut TaggedObject<T>, |
11 | size_hint: usize, |
12 | pub adjusted_size: i64, |
13 | } |
14 | |
15 | unsafe impl<T: 'static + Send> Send for External<T> {} |
16 | unsafe impl<T: 'static + Sync> Sync for External<T> {} |
17 | |
18 | impl<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 | |
28 | impl<T: 'static> From<T> for External<T> { |
29 | fn from(t: T) -> Self { |
30 | External::new(t) |
31 | } |
32 | } |
33 | |
34 | impl<T: 'static> ValidateNapiValue for External<T> {} |
35 | |
36 | impl<T: 'static> External<T> { |
37 | pub fn new(value: T) -> Self { |
38 | Self { |
39 | obj: Box::into_raw(Box::new(TaggedObject::new(object: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(object:value))), |
53 | size_hint, |
54 | adjusted_size: 0, |
55 | } |
56 | } |
57 | } |
58 | |
59 | impl<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: *mut c_void = 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: *const TypeId = unknown_tagged_object as *const TypeId; |
68 | if unsafe { *type_id } == TypeId::of::<T>() { |
69 | let tagged_object: *mut TaggedObject = 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 | reason:"T on `get_value_external` is not the type of wrapped object" .to_owned(), |
79 | )) |
80 | } |
81 | } |
82 | } |
83 | |
84 | impl<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 | |
90 | impl<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 | |
96 | impl<T: 'static> Deref for External<T> { |
97 | type Target = T; |
98 | |
99 | fn deref(&self) -> &Self::Target { |
100 | self.as_ref() |
101 | } |
102 | } |
103 | |
104 | impl<T: 'static> DerefMut for External<T> { |
105 | fn deref_mut(&mut self) -> &mut Self::Target { |
106 | self.as_mut() |
107 | } |
108 | } |
109 | |
110 | impl<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 | } |
146 | |