nj_core/
buffer.rs

1use std::ptr;
2use std::ops::Deref;
3
4use tracing::trace;
5
6use crate::TryIntoJs;
7use crate::JSValue;
8use crate::sys::{napi_value, napi_ref, napi_env};
9use crate::val::JsEnv;
10use crate::NjError;
11
12/// pass rust byte arry as Node.js ArrayBuffer
13pub struct ArrayBuffer {
14    data: Vec<u8>,
15}
16
17use std::fmt;
18use std::fmt::Debug;
19
20impl Debug for ArrayBuffer {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        f.write_fmt(format_args!("ArrayBuffer len: {}", self.data.len()))
23    }
24}
25
26impl ArrayBuffer {
27    pub fn new(data: Vec<u8>) -> Self {
28        Self { data }
29    }
30
31    extern "C" fn finalize_buffer(
32        _env: napi_env,
33        _finalize_data: *mut ::std::os::raw::c_void,
34        finalize_hint: *mut ::std::os::raw::c_void,
35    ) {
36        trace!("finalize array buffer");
37        unsafe {
38            // use hint to reconstruct box instead of finalize data
39            let ptr: *mut Vec<u8> = finalize_hint as *mut Vec<u8>;
40            let _rust = Box::from_raw(ptr);
41        }
42    }
43}
44
45impl TryIntoJs for ArrayBuffer {
46    fn try_to_js(self, js_env: &JsEnv) -> Result<napi_value, NjError> {
47        let len = self.data.len();
48
49        let box_data = Box::new(self.data);
50
51        let mut napi_buffer = ptr::null_mut();
52
53        // get pointer to vec's buffer
54        let data_buffer = box_data.as_ptr();
55
56        // get raw pointer to box, this will be used to reconstruct box
57        let data_box_ptr = Box::into_raw(box_data) as *mut core::ffi::c_void;
58
59        crate::napi_call_result!(crate::sys::napi_create_external_arraybuffer(
60            js_env.inner(),
61            data_buffer as *mut core::ffi::c_void,
62            len,
63            Some(Self::finalize_buffer),
64            data_box_ptr,
65            &mut napi_buffer
66        ))?;
67
68        Ok(napi_buffer)
69    }
70}
71
72impl<'a> JSValue<'a> for &'a [u8] {
73    fn convert_to_rust(env: &'a JsEnv, js_value: napi_value) -> Result<Self, NjError> {
74        // check if this is really buffer
75        if !env.is_buffer(js_value)? {
76            return Err(NjError::InvalidType(
77                "Buffer".to_owned(),
78                env.value_type_string(js_value)?.to_owned(),
79            ));
80        }
81
82        let buffer = env.get_buffer_info(js_value)?;
83
84        Ok(buffer)
85    }
86}
87
88/// Rust representation of Nodejs [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
89/// This is safe to pass around rest of Rust code since this manages Node.js GC lifecycle.
90/// JSArrayBuffer is deference as `&[u8]`
91///
92/// # Examples
93///
94/// In this example, JS String is passed as array buffer.  Rust code convert to String and concate with prefix message.
95///  
96/// ```no_run
97/// use node_bindgen::derive::node_bindgen;
98/// use node_bindgen::core::buffer::JSArrayBuffer;
99///
100/// #[node_bindgen]
101/// fn hello(data: JSArrayBuffer) -> Result<String, NjError> {
102///   let message = String::from_utf8(data.to_vec())?;
103///    Ok(format!("reply {}", message))
104/// }
105/// ```
106///
107/// This can be invoked from Node.js
108/// ```text
109/// let addon = require('./your_module');
110/// console.log(Buffer.from("hello"));
111/// ```
112pub struct JSArrayBuffer {
113    env: JsEnv,
114    napi_ref: napi_ref,
115    buffer: &'static [u8],
116}
117
118unsafe impl Send for JSArrayBuffer {}
119
120impl JSArrayBuffer {
121    pub fn as_bytes(&self) -> &[u8] {
122        self.buffer
123    }
124}
125
126impl JSValue<'_> for JSArrayBuffer {
127    fn convert_to_rust(env: &JsEnv, napi_value: napi_value) -> Result<Self, NjError> {
128        use std::mem::transmute;
129
130        let napi_ref = env.create_reference(napi_value, 1)?;
131
132        // it is oky to transmute as static byte slice since we are managing slice
133        let buffer: &'static [u8] =
134            unsafe { transmute::<&[u8], &'static [u8]>(env.convert_to_rust(napi_value)?) };
135        Ok(Self {
136            env: *env,
137            napi_ref,
138            buffer,
139        })
140    }
141}
142
143impl Drop for JSArrayBuffer {
144    fn drop(&mut self) {
145        self.env
146            .delete_reference(self.napi_ref)
147            .expect("reference can't be deleted to array buf");
148    }
149}
150
151impl Deref for JSArrayBuffer {
152    type Target = [u8];
153
154    fn deref(&self) -> &Self::Target {
155        self.buffer
156    }
157}