use crate::ffi::{rust_call, ForeignBytes, RustCallStatus};
#[repr(C)]
#[derive(Debug)]
pub struct RustBuffer {
pub(crate) capacity: u64,
pub(crate) len: u64,
pub(crate) data: *mut u8,
}
unsafe impl Send for RustBuffer {}
impl RustBuffer {
pub fn new() -> Self {
Self::from_vec(Vec::new())
}
pub(crate) unsafe fn from_raw_parts(data: *mut u8, len: u64, capacity: u64) -> Self {
Self {
capacity,
len,
data,
}
}
pub fn len(&self) -> usize {
self.len
.try_into()
.expect("buffer length negative or overflowed")
}
pub fn capacity(&self) -> usize {
self.capacity
.try_into()
.expect("buffer length negative or overflowed")
}
pub fn data_pointer(&self) -> *const u8 {
self.data
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn new_with_size(size: u64) -> Self {
Self::from_vec(vec![0u8; size as usize])
}
pub fn from_vec(v: Vec<u8>) -> Self {
let capacity = u64::try_from(v.capacity()).expect("buffer capacity cannot fit into a u64.");
let len = u64::try_from(v.len()).expect("buffer length cannot fit into a u64.");
let mut v = std::mem::ManuallyDrop::new(v);
unsafe { Self::from_raw_parts(v.as_mut_ptr(), len, capacity) }
}
pub fn destroy_into_vec(self) -> Vec<u8> {
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) }
}
}
pub fn destroy(self) {
drop(self.destroy_into_vec());
}
}
impl Default for RustBuffer {
fn default() -> Self {
Self::new()
}
}
pub fn uniffi_rustbuffer_alloc(size: u64, call_status: &mut RustCallStatus) -> RustBuffer {
rust_call(call_status, || Ok(RustBuffer::new_with_size(size)))
}
pub 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()))
})
}
pub fn uniffi_rustbuffer_free(buf: RustBuffer, call_status: &mut RustCallStatus) {
rust_call(call_status, || {
RustBuffer::destroy(buf);
Ok(())
})
}
pub fn uniffi_rustbuffer_reserve(
buf: RustBuffer,
additional: u64,
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);
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() {
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() {
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() {
let rbuf = unsafe { RustBuffer::from_raw_parts(std::ptr::null_mut(), 12, 0) };
rbuf.destroy_into_vec();
}
#[test]
#[should_panic]
fn test_rustbuffer_provided_len_must_not_exceed_capacity() {
let mut v = vec![0u8, 1, 2];
let rbuf = unsafe { RustBuffer::from_raw_parts(v.as_mut_ptr(), 3, 2) };
rbuf.destroy_into_vec();
}
}