Skip to main content

wry_bindgen_runtime/wire/
ipc.rs

1//! Binary IPC protocol types for communicating between Rust and JavaScript.
2//!
3//! The binary format uses aligned buffers for efficient memory access:
4//! - First 12 bytes: three u32 offsets (u16_offset, u8_offset, str_offset)
5//! - u32 buffer: from byte 12 to u16_offset
6//! - u16 buffer: from u16_offset to u8_offset
7//! - u8 buffer: from u8_offset to str_offset
8//! - string buffer: from str_offset to end
9
10use alloc::string::{String, ToString};
11use alloc::vec::Vec;
12use core::fmt;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct DecodeError {
16    message: String,
17}
18
19impl DecodeError {
20    pub fn custom(message: impl Into<String>) -> Self {
21        Self {
22            message: message.into(),
23        }
24    }
25
26    pub(crate) fn message_too_short(expected: usize, actual: usize) -> Self {
27        Self::custom(
28            format_args!("message too short: expected at least {expected} bytes, got {actual}")
29                .to_string(),
30        )
31    }
32
33    pub(crate) fn u8_buffer_empty() -> Self {
34        Self::custom("u8 buffer empty when trying to read")
35    }
36
37    pub(crate) fn u16_buffer_empty() -> Self {
38        Self::custom("u16 buffer empty when trying to read")
39    }
40
41    pub(crate) fn u32_buffer_empty() -> Self {
42        Self::custom("u32 buffer empty when trying to read")
43    }
44
45    pub(crate) fn string_buffer_too_short(expected: usize, actual: usize) -> Self {
46        Self::custom(
47            format_args!("string buffer too short: expected {expected} bytes, got {actual}")
48                .to_string(),
49        )
50    }
51
52    pub(crate) fn invalid_utf8(position: usize) -> Self {
53        Self::custom(format_args!("invalid UTF-8 at position {position}").to_string())
54    }
55
56    pub(crate) fn invalid_header_offsets(
57        u16_offset: u32,
58        u8_offset: u32,
59        str_offset: u32,
60        total_len: usize,
61    ) -> Self {
62        Self::custom(format_args!(
63            "invalid header offsets: u16={u16_offset}, u8={u8_offset}, str={str_offset}, total_len={total_len}"
64        ).to_string())
65    }
66}
67
68impl fmt::Display for DecodeError {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        self.message.fmt(f)
71    }
72}
73
74impl core::error::Error for DecodeError {}
75
76impl From<DecodeError> for String {
77    fn from(err: DecodeError) -> String {
78        err.to_string()
79    }
80}
81
82pub struct DecodedData<'a> {
83    u8_buf: &'a [u8],
84    u16_buf: &'a [u16],
85    u32_buf: &'a [u32],
86    str_buf: &'a [u8],
87}
88
89impl<'a> DecodedData<'a> {
90    /// Parse decoded data from raw bytes.
91    pub(crate) fn from_bytes(bytes: &'a [u8]) -> Result<Self, DecodeError> {
92        if bytes.len() < 12 {
93            return Err(DecodeError::message_too_short(12, bytes.len()));
94        }
95
96        let header: [u32; 3] = bytemuck::cast_slice(&bytes[0..12])
97            .try_into()
98            .map_err(|_| DecodeError::custom("failed to parse header"))?;
99        let [u16_offset, u8_offset, str_offset] = header;
100
101        // Validate offsets
102        let total_len = bytes.len();
103        if u16_offset as usize > total_len
104            || u8_offset as usize > total_len
105            || str_offset as usize > total_len
106            || u16_offset < 12
107            || u8_offset < u16_offset
108            || str_offset < u8_offset
109        {
110            return Err(DecodeError::invalid_header_offsets(
111                u16_offset, u8_offset, str_offset, total_len,
112            ));
113        }
114
115        let u32_buf = bytemuck::cast_slice(&bytes[12..u16_offset as usize]);
116        let u16_buf = bytemuck::cast_slice(&bytes[u16_offset as usize..u8_offset as usize]);
117        let u8_buf = &bytes[u8_offset as usize..str_offset as usize];
118        let str_buf = &bytes[str_offset as usize..];
119
120        Ok(Self {
121            u8_buf,
122            u16_buf,
123            u32_buf,
124            str_buf,
125        })
126    }
127
128    /// Take a u8 from the buffer.
129    pub(crate) fn take_u8(&mut self) -> Result<u8, DecodeError> {
130        let [first, rest @ ..] = &self.u8_buf else {
131            return Err(DecodeError::u8_buffer_empty());
132        };
133        self.u8_buf = rest;
134        Ok(*first)
135    }
136
137    /// Take a u16 from the buffer.
138    pub(crate) fn take_u16(&mut self) -> Result<u16, DecodeError> {
139        let [first, rest @ ..] = &self.u16_buf else {
140            return Err(DecodeError::u16_buffer_empty());
141        };
142        self.u16_buf = rest;
143        Ok(*first)
144    }
145
146    /// Take a u32 from the buffer.
147    pub(crate) fn take_u32(&mut self) -> Result<u32, DecodeError> {
148        let [first, rest @ ..] = &self.u32_buf else {
149            return Err(DecodeError::u32_buffer_empty());
150        };
151        self.u32_buf = rest;
152        Ok(*first)
153    }
154
155    /// Take a u64 from the buffer (stored as two u32s).
156    pub(crate) fn take_u64(&mut self) -> Result<u64, DecodeError> {
157        let low = self.take_u32()? as u64;
158        let high = self.take_u32()? as u64;
159        Ok((high << 32) | low)
160    }
161
162    /// Take a u128 from the buffer
163    pub(crate) fn take_u128(&mut self) -> Result<u128, DecodeError> {
164        let low = self.take_u64()? as u128;
165        let high = self.take_u64()? as u128;
166        Ok((high << 64) | low)
167    }
168
169    /// Take a string from the buffer.
170    pub(crate) fn take_str(&mut self) -> Result<&'a str, DecodeError> {
171        let len = self.take_u32()? as usize;
172        let actual_len = self.str_buf.len();
173        let Some((buf, rem)) = self.str_buf.split_at_checked(len) else {
174            return Err(DecodeError::string_buffer_too_short(len, actual_len));
175        };
176        let s =
177            core::str::from_utf8(buf).map_err(|e| DecodeError::invalid_utf8(e.valid_up_to()))?;
178        self.str_buf = rem;
179        Ok(s)
180    }
181}
182
183#[derive(Default)]
184pub struct EncodedData {
185    u8_buf: Vec<u8>,
186    u16_buf: Vec<u16>,
187    u32_buf: Vec<u32>,
188    str_buf: Vec<u8>,
189    /// Flag indicating that this batch must be flushed before returning.
190    /// Used for stack-allocated callbacks that need synchronous invocation.
191    needs_flush: bool,
192}
193
194impl EncodedData {
195    #[doc(hidden)]
196    pub fn mark_needs_flush(&mut self) {
197        self.needs_flush = true;
198    }
199
200    pub(crate) fn take_needs_flush(&mut self) -> bool {
201        core::mem::take(&mut self.needs_flush)
202    }
203
204    pub(crate) fn push_u8(&mut self, value: u8) {
205        self.u8_buf.push(value);
206    }
207
208    pub(crate) fn push_u16(&mut self, value: u16) {
209        self.u16_buf.push(value);
210    }
211
212    pub(crate) fn push_u32(&mut self, value: u32) {
213        self.u32_buf.push(value);
214    }
215
216    pub(crate) fn push_u64(&mut self, value: u64) {
217        self.push_u32((value & 0xFFFFFFFF) as u32);
218        self.push_u32((value >> 32) as u32);
219    }
220
221    pub(crate) fn push_u128(&mut self, value: u128) {
222        self.push_u64((value & 0xFFFFFFFFFFFFFFFF) as u64);
223        self.push_u64((value >> 64) as u64);
224    }
225
226    pub(crate) fn push_str(&mut self, value: &str) {
227        let len = u32::try_from(value.len()).expect("string length exceeds u32::MAX");
228        self.push_u32(len);
229        self.str_buf.extend_from_slice(value.as_bytes());
230    }
231
232    /// Convert the encoded data to bytes.
233    pub(crate) fn into_bytes(self) -> Vec<u8> {
234        let u16_offset = 12 + self.u32_buf.len() * 4;
235        let u8_offset = u16_offset + self.u16_buf.len() * 2;
236        let str_offset = u8_offset + self.u8_buf.len();
237
238        let total_len = str_offset + self.str_buf.len();
239        let mut bytes = Vec::with_capacity(total_len);
240
241        // Write header offsets
242        bytes.extend_from_slice(&(u16_offset as u32).to_le_bytes());
243        bytes.extend_from_slice(&(u8_offset as u32).to_le_bytes());
244        bytes.extend_from_slice(&(str_offset as u32).to_le_bytes());
245
246        // Write u32 buffer
247        for &u in &self.u32_buf {
248            bytes.extend_from_slice(&u.to_le_bytes());
249        }
250
251        // Write u16 buffer
252        for &u in &self.u16_buf {
253            bytes.extend_from_slice(&u.to_le_bytes());
254        }
255
256        // Write u8 buffer
257        bytes.extend_from_slice(&self.u8_buf);
258
259        // Write string buffer
260        bytes.extend_from_slice(&self.str_buf);
261
262        bytes
263    }
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    #[test]
271    fn encoded_data_roundtrips_buffer_sections() {
272        let mut encoder = EncodedData::default();
273        encoder.push_u8(7);
274        encoder.push_u32(99);
275
276        let bytes = encoder.into_bytes();
277        let mut data = DecodedData::from_bytes(&bytes).unwrap();
278        assert_eq!(data.take_u8().unwrap(), 7);
279        assert_eq!(data.take_u32().unwrap(), 99);
280    }
281
282    #[test]
283    fn flush_flag_is_consumed_once() {
284        let mut encoder = EncodedData::default();
285        encoder.mark_needs_flush();
286        assert!(encoder.take_needs_flush());
287        assert!(!encoder.take_needs_flush());
288    }
289}