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(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
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: *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
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}
146