uniffi_core/ffi/rustbuffer.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::ffi::{rust_call, ForeignBytes, RustCallStatus};
6
7/// Support for passing an allocated-by-Rust buffer of bytes over the FFI.
8///
9/// We can pass a `Vec<u8>` to foreign language code by decomposing it into
10/// its raw parts (buffer pointer, length, and capacity) and passing those
11/// around as a struct. Naturally, this can be tremendously unsafe! So here
12/// are the details:
13///
14///   * `RustBuffer` structs must only ever be constructed from a `Vec<u8>`,
15///     either explicitly via `RustBuffer::from_vec` or indirectly by calling
16///     one of the `RustBuffer::new*` constructors.
17///
18///   * `RustBuffer` structs do not implement `Drop`, since they are intended
19///     to be passed to foreign-language code outside of the control of Rust's
20///     ownership system. To avoid memory leaks they *must* passed back into
21///     Rust and either explicitly destroyed using `RustBuffer::destroy`, or
22///     converted back to a `Vec<u8>` using `RustBuffer::destroy_into_vec`
23///     (which will then be dropped via Rust's usual ownership-tracking system).
24///
25/// Foreign-language code should not construct `RustBuffer` structs other than
26/// by receiving them from a call into the Rust code, and should not modify them
27/// apart from the following safe operations:
28///
29///   * Writing bytes into the buffer pointed to by `data`, without writing
30///     beyond the indicated `capacity`.
31///
32///   * Adjusting the `len` property to indicate the amount of data written,
33///     while ensuring that 0 <= `len` <= `capacity`.
34///
35///   * As a special case, constructing a `RustBuffer` with zero capacity, zero
36///     length, and a null `data` pointer to indicate an empty buffer.
37///
38/// In particular, it is not safe for foreign-language code to construct a `RustBuffer`
39/// that points to its own allocated memory; use the `ForeignBytes` struct to
40/// pass a view of foreign-owned memory in to Rust code.
41///
42/// Implementation note: all the fields of this struct are private, so you can't
43/// manually construct instances that don't come from a `Vec<u8>`. If you've got
44/// a `RustBuffer` then it either came from a public constructor (all of which
45/// are safe) or it came from foreign-language code (which should have in turn
46/// received it by calling some Rust function, and should be respecting the
47/// invariants listed above).
48///
49/// This struct is based on `ByteBuffer` from the `ffi-support` crate, but modified
50/// to retain unallocated capacity rather than truncating to the occupied length.
51#[repr(C)]
52#[derive(Debug)]
53pub struct RustBuffer {
54    /// The allocated capacity of the underlying `Vec<u8>`.
55    /// In Rust this is a `usize`, but we use an `u64` to keep the foreign binding code simple.
56    pub(crate) capacity: u64,
57    /// The occupied length of the underlying `Vec<u8>`.
58    /// In Rust this is a `usize`, but we use an `u64` to keep the foreign binding code simple.
59    pub(crate) len: u64,
60    /// The pointer to the allocated buffer of the `Vec<u8>`.
61    pub(crate) data: *mut u8,
62}
63
64// Mark `RustBuffer` as safe to send between threads, despite the `u8` pointer.  The only mutable
65// use of that pointer is in `destroy_into_vec()` which requires a &mut on the `RustBuffer`.  This
66// is required to send `RustBuffer` inside a `RustFuture`
67unsafe impl Send for RustBuffer {}
68
69impl RustBuffer {
70    /// Creates an empty `RustBuffer`.
71    ///
72    /// The buffer will not allocate.
73    /// The resulting vector will not be automatically dropped; you must
74    /// arrange to call `destroy` or `destroy_into_vec` when finished with it.
75    pub fn new() -> Self {
76        Self::from_vec(Vec::new())
77    }
78
79    /// Creates a `RustBuffer` from its constituent fields.
80    ///
81    /// # Safety
82    ///
83    /// You must ensure that the raw parts uphold the documented invariants of this class.
84    pub(crate) unsafe fn from_raw_parts(data: *mut u8, len: u64, capacity: u64) -> Self {
85        Self {
86            capacity,
87            len,
88            data,
89        }
90    }
91
92    /// Get the current length of the buffer, as a `usize`.
93    ///
94    /// This is mostly a helper function to convert the `u64` length field
95    /// into a `usize`, which is what Rust code usually expects.
96    ///
97    /// # Panics
98    ///
99    /// Panics if called on an invalid struct obtained from foreign-language code,
100    /// in which the `len` field is larger than what `u32` can represent and the
101    /// platform has `usize` being `u32` or smaller.
102    pub fn len(&self) -> usize {
103        self.len
104            .try_into()
105            .expect("buffer length negative or overflowed")
106    }
107
108    pub fn capacity(&self) -> usize {
109        self.capacity
110            .try_into()
111            .expect("buffer length negative or overflowed")
112    }
113
114    /// Get a pointer to the data
115    pub fn data_pointer(&self) -> *const u8 {
116        self.data
117    }
118
119    /// Returns true if the length of the buffer is 0.
120    pub fn is_empty(&self) -> bool {
121        self.len == 0
122    }
123
124    /// Creates a `RustBuffer` zero-filed to the requested size.
125    ///
126    /// The resulting vector will not be automatically dropped; you must
127    /// arrange to call `destroy` or `destroy_into_vec` when finished with it.
128    ///
129    /// # Panics
130    ///
131    /// Panics if the requested size is too large to fit in an `u64`, and
132    /// hence would risk incompatibility with some foreign-language code.
133    pub fn new_with_size(size: u64) -> Self {
134        Self::from_vec(vec![0u8; size as usize])
135    }
136
137    /// Consumes a `Vec<u8>` and returns its raw parts as a `RustBuffer`.
138    ///
139    /// The resulting vector will not be automatically dropped; you must
140    /// arrange to call `destroy` or `destroy_into_vec` when finished with it.
141    ///
142    /// # Panics
143    ///
144    /// Panics if the vector's length or capacity are too large to fit in an `u64`,
145    /// and hence would risk incompatibility with some foreign-language code.
146    pub fn from_vec(v: Vec<u8>) -> Self {
147        let capacity = u64::try_from(v.capacity()).expect("buffer capacity cannot fit into a u64.");
148        let len = u64::try_from(v.len()).expect("buffer length cannot fit into a u64.");
149        let mut v = std::mem::ManuallyDrop::new(v);
150        unsafe { Self::from_raw_parts(v.as_mut_ptr(), len, capacity) }
151    }
152
153    /// Converts this `RustBuffer` back into an owned `Vec<u8>`.
154    ///
155    /// This restores ownership of the underlying buffer to Rust, meaning it will
156    /// be dropped when the `Vec<u8>` is dropped. The `RustBuffer` *must* have been
157    /// previously obtained from a valid `Vec<u8>` owned by this Rust code.
158    ///
159    /// # Panics
160    ///
161    /// Panics if called on an invalid struct obtained from foreign-language code,
162    /// which does not respect the invairiants on `len` and `capacity`.
163    pub fn destroy_into_vec(self) -> Vec<u8> {
164        // Rust will never give us a null `data` pointer for a `Vec`, but
165        // foreign-language code can use it to cheaply pass an empty buffer.
166        if self.data.is_null() {
167            assert!(self.capacity == 0, "null RustBuffer had non-zero capacity");
168            assert!(self.len == 0, "null RustBuffer had non-zero length");
169            vec![]
170        } else {
171            let capacity: usize = self
172                .capacity
173                .try_into()
174                .expect("buffer capacity negative or overflowed");
175            let len: usize = self
176                .len
177                .try_into()
178                .expect("buffer length negative or overflowed");
179            assert!(len <= capacity, "RustBuffer length exceeds capacity");
180            unsafe { Vec::from_raw_parts(self.data, len, capacity) }
181        }
182    }
183
184    /// Reclaim memory stored in this `RustBuffer`.
185    ///
186    /// # Panics
187    ///
188    /// Panics if called on an invalid struct obtained from foreign-language code,
189    /// which does not respect the invairiants on `len` and `capacity`.
190    pub fn destroy(self) {
191        drop(self.destroy_into_vec());
192    }
193}
194
195impl Default for RustBuffer {
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201// Functions for the RustBuffer functionality.
202//
203// The scaffolding code re-exports these functions, prefixed with the component name and UDL hash
204// This creates a separate set of functions for each UniFFIed component, which is needed in the
205// case where we create multiple dylib artifacts since each dylib will have its own allocator.
206
207/// This helper allocates a new byte buffer owned by the Rust code, and returns it
208/// to the foreign-language code as a `RustBuffer` struct. Callers must eventually
209/// free the resulting buffer, either by explicitly calling [`uniffi_rustbuffer_free`] defined
210/// below, or by passing ownership of the buffer back into Rust code.
211pub fn uniffi_rustbuffer_alloc(size: u64, call_status: &mut RustCallStatus) -> RustBuffer {
212    rust_call(call_status, || Ok(RustBuffer::new_with_size(size)))
213}
214
215/// This helper copies bytes owned by the foreign-language code into a new byte buffer owned
216/// by the Rust code, and returns it as a `RustBuffer` struct. Callers must eventually
217/// free the resulting buffer, either by explicitly calling the destructor defined below,
218/// or by passing ownership of the buffer back into Rust code.
219///
220/// # Safety
221/// This function will dereference a provided pointer in order to copy bytes from it, so
222/// make sure the `ForeignBytes` struct contains a valid pointer and length.
223pub fn uniffi_rustbuffer_from_bytes(
224    bytes: ForeignBytes,
225    call_status: &mut RustCallStatus,
226) -> RustBuffer {
227    rust_call(call_status, || {
228        let bytes = bytes.as_slice();
229        Ok(RustBuffer::from_vec(bytes.to_vec()))
230    })
231}
232
233/// Free a byte buffer that had previously been passed to the foreign language code.
234///
235/// # Safety
236/// The argument *must* be a uniquely-owned `RustBuffer` previously obtained from a call
237/// into the Rust code that returned a buffer, or you'll risk freeing unowned memory or
238/// corrupting the allocator state.
239pub fn uniffi_rustbuffer_free(buf: RustBuffer, call_status: &mut RustCallStatus) {
240    rust_call(call_status, || {
241        RustBuffer::destroy(buf);
242        Ok(())
243    })
244}
245
246/// Reserve additional capacity in a byte buffer that had previously been passed to the
247/// foreign language code.
248///
249/// The first argument *must* be a uniquely-owned `RustBuffer` previously
250/// obtained from a call into the Rust code that returned a buffer. Its underlying data pointer
251/// will be reallocated if necessary and returned in a new `RustBuffer` struct.
252///
253/// The second argument must be the minimum number of *additional* bytes to reserve
254/// capacity for in the buffer; it is likely to reserve additional capacity in practice
255/// due to amortized growth strategy of Rust vectors.
256///
257/// # Safety
258/// The first argument *must* be a uniquely-owned `RustBuffer` previously obtained from a call
259/// into the Rust code that returned a buffer, or you'll risk freeing unowned memory or
260/// corrupting the allocator state.
261pub fn uniffi_rustbuffer_reserve(
262    buf: RustBuffer,
263    additional: u64,
264    call_status: &mut RustCallStatus,
265) -> RustBuffer {
266    rust_call(call_status, || {
267        let additional: usize = additional
268            .try_into()
269            .expect("additional buffer length negative or overflowed");
270        let mut v = buf.destroy_into_vec();
271        v.reserve(additional);
272        Ok(RustBuffer::from_vec(v))
273    })
274}
275
276#[cfg(test)]
277mod test {
278    use super::*;
279    #[test]
280    fn test_rustbuffer_from_vec() {
281        let rbuf = RustBuffer::from_vec(vec![1u8, 2, 3]);
282        assert_eq!(rbuf.len(), 3);
283        assert_eq!(rbuf.destroy_into_vec(), vec![1u8, 2, 3]);
284    }
285
286    #[test]
287    fn test_rustbuffer_empty() {
288        let rbuf = RustBuffer::new();
289        assert_eq!(rbuf.len(), 0);
290        // Rust will never give us a null pointer, even for an empty buffer.
291        assert!(!rbuf.data.is_null());
292        assert_eq!(rbuf.destroy_into_vec(), Vec::<u8>::new());
293    }
294
295    #[test]
296    fn test_rustbuffer_new_with_size() {
297        let rbuf = RustBuffer::new_with_size(5);
298        assert_eq!(rbuf.destroy_into_vec().as_slice(), &[0u8, 0, 0, 0, 0]);
299
300        let rbuf = RustBuffer::new_with_size(0);
301        assert!(!rbuf.data.is_null());
302        assert_eq!(rbuf.destroy_into_vec().as_slice(), &[0u8; 0]);
303    }
304
305    #[test]
306    fn test_rustbuffer_null_means_empty() {
307        // This is how foreign-language code might cheaply indicate an empty buffer.
308        let rbuf = unsafe { RustBuffer::from_raw_parts(std::ptr::null_mut(), 0, 0) };
309        assert_eq!(rbuf.destroy_into_vec().as_slice(), &[0u8; 0]);
310    }
311
312    #[test]
313    #[should_panic]
314    fn test_rustbuffer_null_must_have_no_capacity() {
315        // We guard against foreign-language code providing this kind of invalid struct.
316        let rbuf = unsafe { RustBuffer::from_raw_parts(std::ptr::null_mut(), 0, 1) };
317        rbuf.destroy_into_vec();
318    }
319    #[test]
320    #[should_panic]
321    fn test_rustbuffer_null_must_have_zero_length() {
322        // We guard against foreign-language code providing this kind of invalid struct.
323        let rbuf = unsafe { RustBuffer::from_raw_parts(std::ptr::null_mut(), 12, 0) };
324        rbuf.destroy_into_vec();
325    }
326
327    #[test]
328    #[should_panic]
329    fn test_rustbuffer_provided_len_must_not_exceed_capacity() {
330        // We guard against foreign-language code providing this kind of invalid struct.
331        let mut v = vec![0u8, 1, 2];
332        let rbuf = unsafe { RustBuffer::from_raw_parts(v.as_mut_ptr(), 3, 2) };
333        rbuf.destroy_into_vec();
334    }
335}