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