rustbridge_ffi/buffer.rs
1//! FFI buffer for passing data across the boundary
2
3use std::ptr;
4
5/// Buffer for passing data across FFI boundary
6///
7/// This structure follows the "Rust allocates, host frees" pattern:
8/// - Rust creates the buffer and populates it with data
9/// - Host copies the data to its managed heap
10/// - Host calls `plugin_free_buffer` to release the memory
11///
12/// # Memory Safety
13///
14/// The buffer owns its memory. When `plugin_free_buffer` is called, the
15/// memory is deallocated. The host must not use the buffer after freeing it.
16#[repr(C)]
17pub struct FfiBuffer {
18 /// Pointer to the data
19 pub data: *mut u8,
20 /// Length of valid data in bytes
21 pub len: usize,
22 /// Total capacity of the allocation
23 pub capacity: usize,
24 /// Error code (0 = success)
25 pub error_code: u32,
26}
27
28impl FfiBuffer {
29 /// Create an empty buffer
30 pub fn empty() -> Self {
31 Self {
32 data: ptr::null_mut(),
33 len: 0,
34 capacity: 0,
35 error_code: 0,
36 }
37 }
38
39 /// Create a buffer from a Vec
40 ///
41 /// This transfers ownership of the Vec's memory to the buffer.
42 pub fn from_vec(mut vec: Vec<u8>) -> Self {
43 let data = vec.as_mut_ptr();
44 let len = vec.len();
45 let capacity = vec.capacity();
46
47 // Prevent Vec from deallocating the memory
48 std::mem::forget(vec);
49
50 Self {
51 data,
52 len,
53 capacity,
54 error_code: 0,
55 }
56 }
57
58 /// Create an error buffer
59 ///
60 /// The error message is stored in the buffer data.
61 pub fn error(code: u32, message: &str) -> Self {
62 let mut buffer = Self::from_vec(message.as_bytes().to_vec());
63 buffer.error_code = code;
64 buffer
65 }
66
67 /// Create a success buffer with JSON data
68 pub fn success_json<T: serde::Serialize>(value: &T) -> Self {
69 match serde_json::to_vec(value) {
70 Ok(data) => Self::from_vec(data),
71 Err(e) => Self::error(5, &format!("Serialization error: {}", e)),
72 }
73 }
74
75 /// Check if this buffer represents an error
76 pub fn is_error(&self) -> bool {
77 self.error_code != 0
78 }
79
80 /// Check if this buffer is empty
81 pub fn is_empty(&self) -> bool {
82 self.data.is_null() || self.len == 0
83 }
84
85 /// Get the data as a slice (unsafe - caller must ensure buffer is valid)
86 ///
87 /// # Safety
88 ///
89 /// The buffer must contain valid data and not have been freed.
90 pub unsafe fn as_slice(&self) -> &[u8] {
91 unsafe {
92 if self.data.is_null() {
93 &[]
94 } else {
95 std::slice::from_raw_parts(self.data, self.len)
96 }
97 }
98 }
99
100 /// Free the buffer's memory
101 ///
102 /// # Safety
103 ///
104 /// This must only be called once per buffer. After calling, the buffer
105 /// is invalid and must not be used.
106 pub unsafe fn free(&mut self) {
107 unsafe {
108 if !self.data.is_null() && self.capacity > 0 {
109 // Reconstruct the Vec and let it drop
110 let _ = Vec::from_raw_parts(self.data, self.len, self.capacity);
111 }
112 self.data = ptr::null_mut();
113 self.len = 0;
114 self.capacity = 0;
115 }
116 }
117}
118
119impl Default for FfiBuffer {
120 fn default() -> Self {
121 Self::empty()
122 }
123}
124
125// FfiBuffer is not Clone because it owns memory
126// FfiBuffer is Send because it owns its data
127unsafe impl Send for FfiBuffer {}
128
129#[cfg(test)]
130#[path = "buffer/buffer_tests.rs"]
131mod buffer_tests;