1use std::any::Any;
8use std::ffi::c_void;
9use std::ptr;
10
11use mozjs::glue::{
12 CopyJSStructuredCloneData, DeleteJSAutoStructuredCloneBuffer, GetLengthOfJSStructuredCloneData,
13 NewJSAutoStructuredCloneBuffer, WriteBytesToJSStructuredCloneData,
14};
15use mozjs::jsapi::{
16 CloneDataPolicy, JS_ReadStructuredClone, JS_ReadUint32Pair, JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone,
17 JS_WriteUint32Pair, JSAutoStructuredCloneBuffer, JSStructuredCloneCallbacks, JSStructuredCloneReader,
18 JSStructuredCloneWriter, StructuredCloneScope,
19};
20
21use crate::conversions::ToValue as _;
22use crate::{Context, Exception, Object, ResultExc, Value};
23
24pub struct StructuredCloneBuffer {
25 buf: *mut JSAutoStructuredCloneBuffer,
26 scope: StructuredCloneScope,
27 callbacks: &'static JSStructuredCloneCallbacks,
28 data: Box<Option<Box<dyn Any>>>,
29}
30
31impl StructuredCloneBuffer {
32 pub fn new(
33 scope: StructuredCloneScope, callbacks: &'static JSStructuredCloneCallbacks, data: Option<Box<dyn Any>>,
34 ) -> StructuredCloneBuffer {
35 StructuredCloneBuffer {
36 buf: unsafe { NewJSAutoStructuredCloneBuffer(scope, callbacks) },
37 scope,
38 callbacks,
39 data: Box::new(data),
40 }
41 }
42
43 pub fn write(
44 &mut self, cx: &Context, data: &Value, transfer: Option<Vec<Object>>, policy: &CloneDataPolicy,
45 ) -> ResultExc<()> {
46 let transfer = transfer.map_or_else(|| Value::undefined(cx), |t| t.as_value(cx));
47
48 unsafe {
49 let scdata = &mut (*self.buf).data_;
50
51 let res = JS_WriteStructuredClone(
52 cx.as_ptr(),
53 data.handle().into(),
54 scdata,
55 self.scope,
56 policy,
57 self.callbacks,
58 self.data_ptr(),
59 transfer.handle().into(),
60 );
61
62 if res { Ok(()) } else { Err(Exception::new(cx)?.unwrap()) }
63 }
64 }
65
66 pub fn read<'cx>(&self, cx: &'cx Context, policy: &CloneDataPolicy) -> ResultExc<Value<'cx>> {
67 let mut rval = Value::undefined(cx);
68 unsafe {
69 let scdata = &mut (*self.buf).data_;
70
71 let res = JS_ReadStructuredClone(
72 cx.as_ptr(),
73 scdata,
74 JS_STRUCTURED_CLONE_VERSION,
75 self.scope,
76 rval.handle_mut().into(),
77 policy,
78 self.callbacks,
79 self.data_ptr(),
80 );
81
82 if res {
83 Ok(rval)
84 } else {
85 Err(Exception::new(cx)?.unwrap())
86 }
87 }
88 }
89
90 pub unsafe fn to_vec(&self) -> Vec<u8> {
94 unsafe {
95 let scdata = &mut (*self.buf).data_;
96
97 let len = GetLengthOfJSStructuredCloneData(scdata);
98 let mut data = Vec::with_capacity(len);
99 CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
100 data.set_len(len);
101
102 data
103 }
104 }
105
106 pub unsafe fn write_from_bytes(&self, data: &[u8]) {
109 unsafe {
110 let scdata = &mut (*self.buf).data_;
111 WriteBytesToJSStructuredCloneData(data.as_ptr(), data.len(), scdata);
112 }
113 }
114
115 fn data_ptr(&self) -> *mut c_void {
116 ptr::from_ref(&*self.data).cast::<c_void>().cast_mut()
117 }
118}
119
120impl Drop for StructuredCloneBuffer {
121 fn drop(&mut self) {
122 unsafe {
123 DeleteJSAutoStructuredCloneBuffer(self.buf);
124 }
125 }
126}
127
128pub unsafe fn read_uint64(r: *mut JSStructuredCloneReader) -> Option<u64> {
129 let mut high = 0;
130 let mut low = 0;
131 let res = unsafe { JS_ReadUint32Pair(r, &raw mut high, &raw mut low) };
132 res.then_some((u64::from(high) << 32) | u64::from(low))
133}
134
135pub unsafe fn write_uint64(w: *mut JSStructuredCloneWriter, data: u64) -> bool {
136 unsafe { JS_WriteUint32Pair(w, (data >> 32) as u32, (data & 0xFFFFFFFF) as u32) }
137}