1 | use crate::{bindgen_prelude::*, check_status, check_status_and_type, sys, Error, Result, Status}; |
2 | |
3 | use std::ffi::{c_void, CStr}; |
4 | use std::fmt::Display; |
5 | use std::mem; |
6 | use std::ops::Deref; |
7 | use std::ptr; |
8 | |
9 | impl TypeName for String { |
10 | fn type_name() -> &'static str { |
11 | "String" |
12 | } |
13 | |
14 | fn value_type() -> ValueType { |
15 | ValueType::String |
16 | } |
17 | } |
18 | |
19 | impl ValidateNapiValue for String {} |
20 | |
21 | impl ToNapiValue for &String { |
22 | unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> { |
23 | let mut ptr: *mut napi_value__ = ptr::null_mut(); |
24 | |
25 | check_status!( |
26 | unsafe { sys::napi_create_string_utf8(env, val.as_ptr().cast(), val.len(), &mut ptr) }, |
27 | "Failed to convert rust `String` into napi `string`" |
28 | )?; |
29 | |
30 | Ok(ptr) |
31 | } |
32 | } |
33 | |
34 | impl ToNapiValue for String { |
35 | #[inline ] |
36 | unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> { |
37 | #[allow (clippy::needless_borrows_for_generic_args)] |
38 | unsafe { |
39 | ToNapiValue::to_napi_value(env, &val) |
40 | } |
41 | } |
42 | } |
43 | |
44 | impl FromNapiValue for String { |
45 | unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> { |
46 | let mut len = 0; |
47 | |
48 | check_status_and_type!( |
49 | unsafe { sys::napi_get_value_string_utf8(env, napi_val, ptr::null_mut(), 0, &mut len) }, |
50 | env, |
51 | napi_val, |
52 | "Failed to convert JavaScript value ` {}` into rust type `String`" |
53 | )?; |
54 | |
55 | // end char len in C |
56 | len += 1; |
57 | let mut ret = Vec::with_capacity(len); |
58 | let buf_ptr = ret.as_mut_ptr(); |
59 | |
60 | let mut written_char_count = 0; |
61 | |
62 | check_status_and_type!( |
63 | unsafe { |
64 | sys::napi_get_value_string_utf8(env, napi_val, buf_ptr, len, &mut written_char_count) |
65 | }, |
66 | env, |
67 | napi_val, |
68 | "Failed to convert napi ` {}` into rust type `String`" |
69 | )?; |
70 | |
71 | let mut ret = mem::ManuallyDrop::new(ret); |
72 | let buf_ptr = ret.as_mut_ptr(); |
73 | let bytes = unsafe { Vec::from_raw_parts(buf_ptr as *mut u8, written_char_count, len) }; |
74 | Ok(unsafe { String::from_utf8_unchecked(bytes) }) |
75 | } |
76 | } |
77 | |
78 | impl TypeName for &str { |
79 | fn type_name() -> &'static str { |
80 | "String" |
81 | } |
82 | |
83 | fn value_type() -> ValueType { |
84 | ValueType::String |
85 | } |
86 | } |
87 | |
88 | impl ValidateNapiValue for &str {} |
89 | |
90 | impl FromNapiValue for &str { |
91 | unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> { |
92 | let mut len = 0; |
93 | |
94 | check_status_and_type!( |
95 | unsafe { sys::napi_get_value_string_utf8(env, napi_val, ptr::null_mut(), 0, &mut len) }, |
96 | env, |
97 | napi_val, |
98 | "Failed to convert napi ` {}` into rust type `String`" |
99 | )?; |
100 | |
101 | // end char len in C |
102 | len += 1; |
103 | let mut ret = Vec::with_capacity(len); |
104 | let buf_ptr = ret.as_mut_ptr(); |
105 | let mut written_char_count = 0; |
106 | |
107 | check_status_and_type!( |
108 | unsafe { |
109 | sys::napi_get_value_string_utf8(env, napi_val, buf_ptr, len, &mut written_char_count) |
110 | }, |
111 | env, |
112 | napi_val, |
113 | "Failed to convert JavaScript value ` {}` into rust type `String`" |
114 | )?; |
115 | |
116 | // The `&str` should only be accepted from function arguments. |
117 | // We shouldn't implement `FromNapiValue` for it before. |
118 | // When it's used with `Object.get` scenario, the memory which `&str` point to will be invalid. |
119 | // For this scenario, we create a temporary empty `Object` here and assign the `Vec<u8>` under `&str` to it. |
120 | // So we can safely forget the `Vec<u8>` here which could fix the memory issue here. |
121 | // FIXME: This implementation should be removed in next major release. |
122 | let mut temporary_external_object = ptr::null_mut(); |
123 | check_status!(unsafe { |
124 | sys::napi_create_external( |
125 | env, |
126 | buf_ptr as *mut c_void, |
127 | Some(release_string), |
128 | Box::into_raw(Box::new(len)) as *mut c_void, |
129 | &mut temporary_external_object, |
130 | ) |
131 | })?; |
132 | |
133 | std::mem::forget(ret); |
134 | match unsafe { CStr::from_ptr(buf_ptr) }.to_str() { |
135 | Err(e) => Err(Error::new( |
136 | Status::InvalidArg, |
137 | format!("Failed to read utf8 string, {}" , e), |
138 | )), |
139 | Ok(s) => Ok(s), |
140 | } |
141 | } |
142 | } |
143 | |
144 | impl ToNapiValue for &str { |
145 | unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> { |
146 | unsafe { String::to_napi_value(env, val.to_owned()) } |
147 | } |
148 | } |
149 | |
150 | #[derive (Debug)] |
151 | pub struct Utf16String(String); |
152 | |
153 | impl ValidateNapiValue for Utf16String {} |
154 | |
155 | impl From<String> for Utf16String { |
156 | fn from(s: String) -> Self { |
157 | Utf16String(s) |
158 | } |
159 | } |
160 | |
161 | impl Display for Utf16String { |
162 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
163 | write!(f, " {}" , self.0) |
164 | } |
165 | } |
166 | |
167 | impl Deref for Utf16String { |
168 | type Target = str; |
169 | |
170 | fn deref(&self) -> &Self::Target { |
171 | self.0.as_ref() |
172 | } |
173 | } |
174 | |
175 | impl TypeName for Utf16String { |
176 | fn type_name() -> &'static str { |
177 | "String(utf16)" |
178 | } |
179 | |
180 | fn value_type() -> ValueType { |
181 | ValueType::String |
182 | } |
183 | } |
184 | |
185 | impl FromNapiValue for Utf16String { |
186 | unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> { |
187 | let mut len = 0; |
188 | |
189 | check_status!( |
190 | unsafe { sys::napi_get_value_string_utf16(env, napi_val, ptr::null_mut(), 0, &mut len) }, |
191 | "Failed to convert napi `utf16 string` into rust type `String`" , |
192 | )?; |
193 | |
194 | // end char len in C |
195 | len += 1; |
196 | let mut ret = vec![0; len]; |
197 | let mut written_char_count = 0; |
198 | |
199 | check_status!( |
200 | unsafe { |
201 | sys::napi_get_value_string_utf16( |
202 | env, |
203 | napi_val, |
204 | ret.as_mut_ptr(), |
205 | len, |
206 | &mut written_char_count, |
207 | ) |
208 | }, |
209 | "Failed to convert napi `utf16 string` into rust type `String`" , |
210 | )?; |
211 | |
212 | let (_, ret) = ret.split_last().unwrap_or((&0, &[])); |
213 | |
214 | match String::from_utf16(ret) { |
215 | Err(e) => Err(Error::new( |
216 | Status::InvalidArg, |
217 | format!("Failed to read utf16 string, {}" , e), |
218 | )), |
219 | Ok(s) => Ok(Utf16String(s)), |
220 | } |
221 | } |
222 | } |
223 | |
224 | impl ToNapiValue for Utf16String { |
225 | unsafe fn to_napi_value(env: sys::napi_env, val: Utf16String) -> Result<sys::napi_value> { |
226 | let mut ptr: *mut napi_value__ = ptr::null_mut(); |
227 | |
228 | let encoded: Vec = val.0.encode_utf16().collect::<Vec<_>>(); |
229 | |
230 | check_status!( |
231 | unsafe { |
232 | sys::napi_create_string_utf16(env, encoded.as_ptr() as *const _, encoded.len(), &mut ptr) |
233 | }, |
234 | "Failed to convert napi `string` into rust type `String`" |
235 | )?; |
236 | |
237 | Ok(ptr) |
238 | } |
239 | } |
240 | |
241 | #[cfg (feature = "latin1" )] |
242 | pub mod latin1_string { |
243 | use super::*; |
244 | |
245 | #[derive (Debug)] |
246 | pub struct Latin1String(String); |
247 | |
248 | impl ValidateNapiValue for Latin1String {} |
249 | |
250 | impl From<String> for Latin1String { |
251 | fn from(s: String) -> Self { |
252 | Latin1String(s) |
253 | } |
254 | } |
255 | |
256 | impl Display for Latin1String { |
257 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
258 | write!(f, "{}" , self.0) |
259 | } |
260 | } |
261 | |
262 | impl Deref for Latin1String { |
263 | type Target = str; |
264 | |
265 | fn deref(&self) -> &Self::Target { |
266 | self.0.as_ref() |
267 | } |
268 | } |
269 | |
270 | impl TypeName for Latin1String { |
271 | fn type_name() -> &'static str { |
272 | "String(latin1)" |
273 | } |
274 | |
275 | fn value_type() -> ValueType { |
276 | ValueType::String |
277 | } |
278 | } |
279 | |
280 | impl FromNapiValue for Latin1String { |
281 | unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> { |
282 | let mut len = 0; |
283 | |
284 | check_status!( |
285 | unsafe { sys::napi_get_value_string_latin1(env, napi_val, ptr::null_mut(), 0, &mut len) }, |
286 | "Failed to convert napi `latin1 string` into rust type `String`" , |
287 | )?; |
288 | |
289 | // end char len in C |
290 | len += 1; |
291 | let mut buf = Vec::with_capacity(len); |
292 | let buf_ptr = buf.as_mut_ptr(); |
293 | |
294 | let mut written_char_count = 0; |
295 | |
296 | mem::forget(buf); |
297 | |
298 | check_status!( |
299 | unsafe { |
300 | sys::napi_get_value_string_latin1(env, napi_val, buf_ptr, len, &mut written_char_count) |
301 | }, |
302 | "Failed to convert napi `latin1 string` into rust type `String`" |
303 | )?; |
304 | |
305 | let buf = |
306 | unsafe { Vec::from_raw_parts(buf_ptr as *mut _, written_char_count, written_char_count) }; |
307 | let mut dst_slice = vec![0; buf.len() * 2]; |
308 | let written = |
309 | encoding_rs::mem::convert_latin1_to_utf8(buf.as_slice(), dst_slice.as_mut_slice()); |
310 | dst_slice.truncate(written); |
311 | |
312 | Ok(Latin1String(unsafe { |
313 | String::from_utf8_unchecked(dst_slice) |
314 | })) |
315 | } |
316 | } |
317 | |
318 | impl ToNapiValue for Latin1String { |
319 | unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> { |
320 | let mut ptr = ptr::null_mut(); |
321 | |
322 | let mut dst = vec![0; val.len()]; |
323 | encoding_rs::mem::convert_utf8_to_latin1_lossy(val.0.as_bytes(), dst.as_mut_slice()); |
324 | |
325 | check_status!( |
326 | unsafe { |
327 | sys::napi_create_string_latin1(env, dst.as_ptr() as *const _, dst.len(), &mut ptr) |
328 | }, |
329 | "Failed to convert rust type `String` into napi `latin1 string`" |
330 | )?; |
331 | |
332 | Ok(ptr) |
333 | } |
334 | } |
335 | } |
336 | |
337 | unsafe extern "C" fn release_string(_env: sys::napi_env, data: *mut c_void, len: *mut c_void) { |
338 | let len: usize = unsafe { *Box::from_raw(len as *mut usize) }; |
339 | unsafe { Vec::from_raw_parts(ptr:data as *mut u8, length:len, capacity:len) }; |
340 | } |
341 | |