wry_bindgen/
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 base64::Engine;
13use core::fmt;
14
15/// Error type for decoding binary IPC messages.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum DecodeError {
18    /// The message is too short (less than 12 bytes for header)
19    MessageTooShort { expected: usize, actual: usize },
20    /// The u8 buffer is empty when trying to read
21    U8BufferEmpty,
22    /// The u16 buffer is empty when trying to read
23    U16BufferEmpty,
24    /// The u32 buffer is empty when trying to read
25    U32BufferEmpty,
26    /// The string buffer doesn't have enough bytes
27    StringBufferTooShort { expected: usize, actual: usize },
28    /// Invalid UTF-8 in string buffer
29    InvalidUtf8 { position: usize },
30    /// Invalid message type byte
31    InvalidMessageType { value: u8 },
32    /// Header offsets are invalid (e.g., overlapping or out of bounds)
33    InvalidHeaderOffsets {
34        u16_offset: u32,
35        u8_offset: u32,
36        str_offset: u32,
37        total_len: usize,
38    },
39    /// Generic decode failure with context
40    Custom(String),
41}
42
43impl fmt::Display for DecodeError {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        match self {
46            DecodeError::MessageTooShort { expected, actual } => {
47                write!(
48                    f,
49                    "message too short: expected at least {expected} bytes, got {actual}"
50                )
51            }
52            DecodeError::U8BufferEmpty => write!(f, "u8 buffer empty when trying to read"),
53            DecodeError::U16BufferEmpty => write!(f, "u16 buffer empty when trying to read"),
54            DecodeError::U32BufferEmpty => write!(f, "u32 buffer empty when trying to read"),
55            DecodeError::StringBufferTooShort { expected, actual } => {
56                write!(
57                    f,
58                    "string buffer too short: expected {expected} bytes, got {actual}"
59                )
60            }
61            DecodeError::InvalidUtf8 { position } => {
62                write!(f, "invalid UTF-8 at position {position}")
63            }
64            DecodeError::InvalidMessageType { value } => {
65                write!(f, "invalid message type: {value}")
66            }
67            DecodeError::InvalidHeaderOffsets {
68                u16_offset,
69                u8_offset,
70                str_offset,
71                total_len,
72            } => {
73                write!(
74                    f,
75                    "invalid header offsets: u16={u16_offset}, u8={u8_offset}, str={str_offset}, total_len={total_len}"
76                )
77            }
78            DecodeError::Custom(msg) => write!(f, "{msg}"),
79        }
80    }
81}
82
83impl core::error::Error for DecodeError {}
84
85impl From<DecodeError> for String {
86    fn from(err: DecodeError) -> String {
87        err.to_string()
88    }
89}
90
91/// Message type identifier for IPC protocol.
92#[repr(u8)]
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub(crate) enum MessageType {
95    /// Rust calling JS (supports batching - multiple operations in one message)
96    Evaluate = 0,
97    /// JS/Rust responding to a call
98    Respond = 1,
99}
100
101/// A binary IPC message.
102///
103/// Message format in the u8 buffer:
104/// - First u8: message type (0 = Evaluate, 1 = Respond)
105/// - Remaining data depends on message type
106///
107/// Evaluate format (supports batching - multiple operations in one message):
108/// - u8: message type (0)
109/// - For each operation (read until buffer exhausted):
110///   - u32: function ID
111///   - encoded arguments (varies by function)
112///
113/// Respond format:
114/// - u8: message type (1)
115/// - For each operation result:
116///   - encoded return value (varies by function)
117#[derive(Debug, Clone)]
118pub(crate) struct IPCMessage {
119    data: Vec<u8>,
120}
121
122impl IPCMessage {
123    /// Create a new IPCMessage from raw bytes.
124    pub fn new(data: Vec<u8>) -> Self {
125        Self { data }
126    }
127
128    /// Create a new respond message with the given data.
129    pub fn new_respond(push_data: impl FnOnce(&mut EncodedData)) -> Self {
130        let mut encoder = EncodedData::new();
131        encoder.push_u8(MessageType::Respond as u8);
132
133        push_data(&mut encoder);
134
135        IPCMessage::new(encoder.to_bytes())
136    }
137
138    /// Get the message type.
139    pub fn ty(&self) -> Result<MessageType, DecodeError> {
140        let mut decoded = DecodedData::from_bytes(&self.data)?;
141        let message_type = decoded.take_u8()?;
142        match message_type {
143            0 => Ok(MessageType::Evaluate),
144            1 => Ok(MessageType::Respond),
145            v => Err(DecodeError::InvalidMessageType { value: v }),
146        }
147    }
148
149    /// Decode the message into its variant form.
150    pub fn decoded(&self) -> Result<DecodedVariant<'_>, DecodeError> {
151        let mut decoded = DecodedData::from_bytes(&self.data)?;
152        let message_type = decoded.take_u8()?;
153        let message_type = match message_type {
154            0 => DecodedVariant::Evaluate { data: decoded },
155            1 => DecodedVariant::Respond { data: decoded },
156            v => return Err(DecodeError::InvalidMessageType { value: v }),
157        };
158        Ok(message_type)
159    }
160
161    /// Get the raw data bytes.
162    pub fn data(&self) -> &[u8] {
163        &self.data
164    }
165
166    /// Consume the message and return the raw data.
167    pub fn into_data(self) -> Vec<u8> {
168        self.data
169    }
170}
171
172/// Decoded message variant.
173#[derive(Debug)]
174pub(crate) enum DecodedVariant<'a> {
175    /// Response from JS/Rust
176    Respond { data: DecodedData<'a> },
177    /// Evaluation request
178    Evaluate { data: DecodedData<'a> },
179}
180
181/// Decoded binary data with aligned buffer access.
182#[derive(Debug)]
183pub struct DecodedData<'a> {
184    u8_buf: &'a [u8],
185    u16_buf: &'a [u16],
186    u32_buf: &'a [u32],
187    str_buf: &'a [u8],
188}
189
190impl<'a> DecodedData<'a> {
191    /// Parse decoded data from raw bytes.
192    pub(crate) fn from_bytes(bytes: &'a [u8]) -> Result<Self, DecodeError> {
193        if bytes.len() < 12 {
194            return Err(DecodeError::MessageTooShort {
195                expected: 12,
196                actual: bytes.len(),
197            });
198        }
199
200        let header: [u32; 3] = bytemuck::cast_slice(&bytes[0..12])
201            .try_into()
202            .map_err(|_| DecodeError::Custom("failed to parse header".to_string()))?;
203        let [u16_offset, u8_offset, str_offset] = header;
204
205        // Validate offsets
206        let total_len = bytes.len();
207        if u16_offset as usize > total_len
208            || u8_offset as usize > total_len
209            || str_offset as usize > total_len
210            || u16_offset < 12
211            || u8_offset < u16_offset
212            || str_offset < u8_offset
213        {
214            return Err(DecodeError::InvalidHeaderOffsets {
215                u16_offset,
216                u8_offset,
217                str_offset,
218                total_len,
219            });
220        }
221
222        let u32_buf = bytemuck::cast_slice(&bytes[12..u16_offset as usize]);
223        let u16_buf = bytemuck::cast_slice(&bytes[u16_offset as usize..u8_offset as usize]);
224        let u8_buf = &bytes[u8_offset as usize..str_offset as usize];
225        let str_buf = &bytes[str_offset as usize..];
226
227        Ok(Self {
228            u8_buf,
229            u16_buf,
230            u32_buf,
231            str_buf,
232        })
233    }
234
235    /// Take a u8 from the buffer.
236    pub(crate) fn take_u8(&mut self) -> Result<u8, DecodeError> {
237        let [first, rest @ ..] = &self.u8_buf else {
238            return Err(DecodeError::U8BufferEmpty);
239        };
240        self.u8_buf = rest;
241        Ok(*first)
242    }
243
244    /// Take a u16 from the buffer.
245    pub(crate) fn take_u16(&mut self) -> Result<u16, DecodeError> {
246        let [first, rest @ ..] = &self.u16_buf else {
247            return Err(DecodeError::U16BufferEmpty);
248        };
249        self.u16_buf = rest;
250        Ok(*first)
251    }
252
253    /// Take a u32 from the buffer.
254    pub(crate) fn take_u32(&mut self) -> Result<u32, DecodeError> {
255        let [first, rest @ ..] = &self.u32_buf else {
256            return Err(DecodeError::U32BufferEmpty);
257        };
258        self.u32_buf = rest;
259        Ok(*first)
260    }
261
262    /// Take a u64 from the buffer (stored as two u32s).
263    pub(crate) fn take_u64(&mut self) -> Result<u64, DecodeError> {
264        let low = self.take_u32()? as u64;
265        let high = self.take_u32()? as u64;
266        Ok((high << 32) | low)
267    }
268
269    /// Take a u128 from the buffer
270    pub(crate) fn take_u128(&mut self) -> Result<u128, DecodeError> {
271        let low = self.take_u64()? as u128;
272        let high = self.take_u64()? as u128;
273        Ok((high << 64) | low)
274    }
275
276    /// Take a string from the buffer.
277    pub(crate) fn take_str(&mut self) -> Result<&'a str, DecodeError> {
278        let len = self.take_u32()? as usize;
279        let actual_len = self.str_buf.len();
280        let Some((buf, rem)) = self.str_buf.split_at_checked(len) else {
281            return Err(DecodeError::StringBufferTooShort {
282                expected: len,
283                actual: actual_len,
284            });
285        };
286        let s = core::str::from_utf8(buf).map_err(|e| DecodeError::InvalidUtf8 {
287            position: e.valid_up_to(),
288        })?;
289        self.str_buf = rem;
290        Ok(s)
291    }
292
293    /// Check if the decoded data is empty.
294    pub(crate) fn is_empty(&self) -> bool {
295        self.u8_buf.is_empty()
296            && self.u16_buf.is_empty()
297            && self.u32_buf.is_empty()
298            && self.str_buf.is_empty()
299    }
300}
301
302/// Encoder for building binary messages.
303#[derive(Debug, Default)]
304pub struct EncodedData {
305    pub(crate) u8_buf: Vec<u8>,
306    pub(crate) u16_buf: Vec<u16>,
307    pub(crate) u32_buf: Vec<u32>,
308    pub(crate) str_buf: Vec<u8>,
309    /// Flag indicating that this batch must be flushed before returning.
310    /// Used for stack-allocated callbacks that need synchronous invocation.
311    pub(crate) needs_flush: bool,
312}
313
314impl EncodedData {
315    /// Create a new empty encoder.
316    pub fn new() -> Self {
317        Self {
318            u8_buf: Vec::new(),
319            u16_buf: Vec::new(),
320            u32_buf: Vec::new(),
321            str_buf: Vec::new(),
322            needs_flush: false,
323        }
324    }
325
326    /// Mark that this batch needs to be flushed before returning.
327    /// Used for stack-allocated callbacks that require synchronous invocation.
328    pub fn mark_needs_flush(&mut self) {
329        self.needs_flush = true;
330    }
331
332    /// Get the total byte length of the encoded data.
333    pub(crate) fn byte_len(&self) -> usize {
334        12 + self.u32_buf.len() * 4
335            + self.u16_buf.len() * 2
336            + self.u8_buf.len()
337            + self.str_buf.len()
338    }
339
340    /// Push a u8 to the buffer.
341    pub(crate) fn push_u8(&mut self, value: u8) {
342        self.u8_buf.push(value);
343    }
344
345    /// Push a u16 to the buffer.
346    pub(crate) fn push_u16(&mut self, value: u16) {
347        self.u16_buf.push(value);
348    }
349
350    /// Push a u32 to the buffer.
351    pub(crate) fn push_u32(&mut self, value: u32) {
352        self.u32_buf.push(value);
353    }
354
355    /// Prepend a u32 to the beginning of the buffer.
356    /// Used to add the reserved placeholder count at the start of batch messages.
357    pub fn prepend_u32(&mut self, value: u32) {
358        self.u32_buf.insert(0, value);
359    }
360
361    /// Push a u64 to the buffer (stored as two u32s).
362    pub(crate) fn push_u64(&mut self, value: u64) {
363        self.push_u32((value & 0xFFFFFFFF) as u32);
364        self.push_u32((value >> 32) as u32);
365    }
366
367    /// Push a u128 to the buffer
368    pub(crate) fn push_u128(&mut self, value: u128) {
369        self.push_u64((value & 0xFFFFFFFFFFFFFFFF) as u64);
370        self.push_u64((value >> 64) as u64);
371    }
372
373    /// Push a string to the buffer.
374    pub(crate) fn push_str(&mut self, value: &str) {
375        self.push_u32(value.len() as u32);
376        self.str_buf.extend_from_slice(value.as_bytes());
377    }
378
379    /// Convert the encoded data to bytes.
380    pub(crate) fn to_bytes(&self) -> Vec<u8> {
381        let u16_offset = 12 + self.u32_buf.len() * 4;
382        let u8_offset = u16_offset + self.u16_buf.len() * 2;
383        let str_offset = u8_offset + self.u8_buf.len();
384
385        let total_len = str_offset + self.str_buf.len();
386        let mut bytes = Vec::with_capacity(total_len);
387
388        // Write header offsets
389        bytes.extend_from_slice(&(u16_offset as u32).to_le_bytes());
390        bytes.extend_from_slice(&(u8_offset as u32).to_le_bytes());
391        bytes.extend_from_slice(&(str_offset as u32).to_le_bytes());
392
393        // Write u32 buffer
394        for &u in &self.u32_buf {
395            bytes.extend_from_slice(&u.to_le_bytes());
396        }
397
398        // Write u16 buffer
399        for &u in &self.u16_buf {
400            bytes.extend_from_slice(&u.to_le_bytes());
401        }
402
403        // Write u8 buffer
404        bytes.extend_from_slice(&self.u8_buf);
405
406        // Write string buffer
407        bytes.extend_from_slice(&self.str_buf);
408
409        bytes
410    }
411
412    /// Extend this encoder with data from another encoder.
413    pub(crate) fn extend(&mut self, other: &EncodedData) {
414        self.u8_buf.extend_from_slice(&other.u8_buf);
415        self.u16_buf.extend_from_slice(&other.u16_buf);
416        self.u32_buf.extend_from_slice(&other.u32_buf);
417        self.str_buf.extend_from_slice(&other.str_buf);
418    }
419}
420
421/// Decode base64-encoded IPC data.
422pub(crate) fn decode_data(bytes: &[u8]) -> Option<IPCMessage> {
423    let engine = base64::engine::general_purpose::STANDARD;
424    let data = engine.decode(bytes).ok()?;
425    Some(IPCMessage { data })
426}