napi_calm_down/bindgen_runtime/js_values/
string.rs

1use crate::{bindgen_prelude::*, check_status, check_status_and_type, sys, Error, Result, Status};
2
3use std::ffi::{c_void, CStr};
4use std::fmt::Display;
5use std::mem;
6use std::ops::Deref;
7use std::ptr;
8
9impl TypeName for String {
10  fn type_name() -> &'static str {
11    "String"
12  }
13
14  fn value_type() -> ValueType {
15    ValueType::String
16  }
17}
18
19impl ValidateNapiValue for String {}
20
21impl ToNapiValue for &String {
22  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
23    let mut ptr = 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
34impl 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
44impl 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
78impl TypeName for &str {
79  fn type_name() -> &'static str {
80    "String"
81  }
82
83  fn value_type() -> ValueType {
84    ValueType::String
85  }
86}
87
88impl ValidateNapiValue for &str {}
89
90impl 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
144impl 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)]
151pub struct Utf16String(String);
152
153impl ValidateNapiValue for Utf16String {}
154
155impl From<String> for Utf16String {
156  fn from(s: String) -> Self {
157    Utf16String(s)
158  }
159}
160
161impl Display for Utf16String {
162  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163    write!(f, "{}", self.0)
164  }
165}
166
167impl Deref for Utf16String {
168  type Target = str;
169
170  fn deref(&self) -> &Self::Target {
171    self.0.as_ref()
172  }
173}
174
175impl 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
185impl 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
224impl ToNapiValue for Utf16String {
225  unsafe fn to_napi_value(env: sys::napi_env, val: Utf16String) -> Result<sys::napi_value> {
226    let mut ptr = ptr::null_mut();
227
228    let encoded = 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")]
242pub 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
337unsafe extern "C" fn release_string(_env: sys::napi_env, data: *mut c_void, len: *mut c_void) {
338  let len = unsafe { *Box::from_raw(len as *mut usize) };
339  unsafe { Vec::from_raw_parts(data as *mut u8, len, len) };
340}