1use std::convert::TryFrom;
2#[cfg(feature = "napi10")]
3use std::ffi::c_void;
4use std::ops::Deref;
5
6use crate::{bindgen_runtime::ToNapiValue, sys, Error, JsString, Result, Status};
7
8#[cfg(feature = "napi10")]
9use crate::Env;
10
11pub struct JsStringUtf16<'env> {
12 pub(crate) inner: JsString<'env>,
13 pub(crate) buf: &'env [u16],
14 pub(crate) _inner_buf: Vec<u16>,
15}
16
17impl<'env> JsStringUtf16<'env> {
18 #[cfg(feature = "napi10")]
19 pub fn from_data(env: &'env Env, data: Vec<u16>) -> Result<JsStringUtf16<'env>> {
49 use std::mem;
50 use std::ptr;
51
52 use crate::{check_status, Value, ValueType};
53
54 if data.is_empty() {
55 return Err(Error::new(
56 Status::InvalidArg,
57 "Cannot create external string from empty data".to_owned(),
58 ));
59 }
60
61 let mut raw_value = ptr::null_mut();
62 let mut copied = false;
63 let data_ptr = data.as_ptr();
64 let len = data.len();
65 let cap = data.capacity();
66 let finalize_hint = Box::into_raw(Box::new((len, cap)));
67
68 check_status!(
69 unsafe {
70 sys::node_api_create_external_string_utf16(
71 env.0,
72 data_ptr,
73 len as isize,
74 Some(drop_utf16_string),
75 finalize_hint.cast(),
76 &mut raw_value,
77 &mut copied,
78 )
79 },
80 "Failed to create external string utf16"
81 )?;
82
83 let inner_buf = if copied {
84 unsafe {
87 let _ = Box::from_raw(finalize_hint);
88 };
89 data
90 } else {
91 mem::forget(data);
94 vec![]
95 };
96
97 Ok(Self {
98 inner: JsString(
99 Value {
100 env: env.0,
101 value: raw_value,
102 value_type: ValueType::String,
103 },
104 std::marker::PhantomData,
105 ),
106 buf: unsafe { std::slice::from_raw_parts(data_ptr.cast(), len) },
107 _inner_buf: inner_buf,
108 })
109 }
110
111 #[cfg(feature = "napi10")]
112 pub unsafe fn from_external<T: 'env, F: FnOnce(Env, T) + 'env>(
149 env: &'env Env,
150 data: *const u16,
151 len: usize,
152 finalize_hint: T,
153 finalize_callback: F,
154 ) -> Result<JsStringUtf16<'env>> {
155 use std::ptr;
156
157 use crate::{check_status, Value, ValueType};
158
159 if data.is_null() {
160 return Err(Error::new(
161 Status::InvalidArg,
162 "Data pointer should not be null".to_owned(),
163 ));
164 }
165
166 let hint_ptr = Box::into_raw(Box::new((finalize_hint, finalize_callback)));
167 let mut raw_value = ptr::null_mut();
168 let mut copied = false;
169
170 check_status!(
171 unsafe {
172 sys::node_api_create_external_string_utf16(
173 env.0,
174 data,
175 len as isize,
176 Some(finalize_with_custom_callback::<T, F>),
177 hint_ptr.cast(),
178 &mut raw_value,
179 &mut copied,
180 )
181 },
182 "Failed to create external string utf16"
183 )?;
184
185 if copied {
186 unsafe {
187 let (hint, finalize) = *Box::from_raw(hint_ptr);
188 finalize(*env, hint);
189 }
190 }
191
192 Ok(Self {
193 inner: JsString(
194 Value {
195 env: env.0,
196 value: raw_value,
197 value_type: ValueType::String,
198 },
199 std::marker::PhantomData,
200 ),
201 buf: unsafe { std::slice::from_raw_parts(data, len) },
202 _inner_buf: vec![],
203 })
204 }
205
206 #[cfg(feature = "napi10")]
207 pub fn from_static(env: &'env Env, data: &'static [u16]) -> Result<JsStringUtf16<'env>> {
208 use std::ptr;
209
210 use crate::{check_status, Value, ValueType};
211
212 if data.is_empty() {
213 return Err(Error::new(
214 Status::InvalidArg,
215 "Data should not be empty".to_owned(),
216 ));
217 }
218
219 let mut raw_value = ptr::null_mut();
220 let mut copied = false;
221
222 check_status!(
223 unsafe {
224 sys::node_api_create_external_string_utf16(
225 env.0,
226 data.as_ptr(),
227 data.len() as isize,
228 None,
229 ptr::null_mut(),
230 &mut raw_value,
231 &mut copied,
232 )
233 },
234 "Failed to create external string utf16"
235 )?;
236
237 Ok(Self {
238 inner: JsString(
239 Value {
240 env: env.0,
241 value: raw_value,
242 value_type: ValueType::String,
243 },
244 std::marker::PhantomData,
245 ),
246 buf: data,
247 _inner_buf: vec![],
248 })
249 }
250
251 pub fn as_str(&self) -> Result<String> {
252 if let Some((_, prefix)) = self.as_slice().split_last() {
253 String::from_utf16(prefix).map_err(|e| Error::new(Status::InvalidArg, format!("{e}")))
254 } else {
255 Ok(String::new())
256 }
257 }
258
259 pub fn as_slice(&self) -> &[u16] {
260 self.buf
261 }
262
263 pub fn len(&self) -> usize {
264 self.buf.len()
265 }
266
267 pub fn is_empty(&self) -> bool {
268 self.buf.is_empty()
269 }
270
271 pub fn into_value(self) -> JsString<'env> {
272 self.inner
273 }
274}
275
276impl TryFrom<JsStringUtf16<'_>> for String {
277 type Error = Error;
278
279 fn try_from(value: JsStringUtf16) -> Result<String> {
280 value.as_str()
281 }
282}
283
284impl Deref for JsStringUtf16<'_> {
285 type Target = [u16];
286
287 fn deref(&self) -> &[u16] {
288 self.buf
289 }
290}
291
292impl AsRef<[u16]> for JsStringUtf16<'_> {
293 fn as_ref(&self) -> &[u16] {
294 self.buf
295 }
296}
297
298impl From<JsStringUtf16<'_>> for Vec<u16> {
299 fn from(value: JsStringUtf16) -> Self {
300 value.as_slice().to_vec()
301 }
302}
303
304impl ToNapiValue for JsStringUtf16<'_> {
305 unsafe fn to_napi_value(_: sys::napi_env, val: JsStringUtf16) -> Result<sys::napi_value> {
306 Ok(val.inner.0.value)
307 }
308}
309
310#[cfg(feature = "napi10")]
311extern "C" fn drop_utf16_string(
312 _: sys::node_api_basic_env,
313 finalize_data: *mut c_void,
314 finalize_hint: *mut c_void,
315) {
316 let (size, capacity): (usize, usize) = unsafe { *Box::from_raw(finalize_hint.cast()) };
317 if size == 0 || finalize_data.is_null() {
318 return;
319 }
320 let data: Vec<u16> = unsafe { Vec::from_raw_parts(finalize_data.cast(), size, capacity) };
321 drop(data);
322}
323
324#[cfg(feature = "napi10")]
325extern "C" fn finalize_with_custom_callback<T, F: FnOnce(Env, T)>(
326 env: sys::node_api_basic_env,
327 _finalize_data: *mut c_void,
328 finalize_hint: *mut c_void,
329) {
330 let (hint, callback) = unsafe { *Box::from_raw(finalize_hint as *mut (T, F)) };
331 callback(Env(env.cast()), hint);
332}