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