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