| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| failure::{ensure, Error}, |
| std::{ffi::c_void, ops::{Deref, DerefMut}, ptr, slice}, |
| }; |
| |
| #[repr(C)] |
| pub struct BufferProvider { |
| /// Acquire a `InBuf` with a given minimum length from the provider. |
| /// The provider must release the underlying buffer's ownership and transfer it to this crate. |
| /// The buffer will be returned via the `free_buffer` callback when it's no longer used. |
| take_buffer: unsafe extern "C" fn(min_len: usize) -> InBuf, |
| } |
| |
| impl BufferProvider { |
| pub fn take_buffer(&self, min_len: usize) -> Result<InBuf, Error> { |
| // Resulting buffer is checked for null pointers. |
| let buf = unsafe { (self.take_buffer)(min_len) }; |
| ensure!(!buf.raw.is_null(), "buffer's raw ptr must not be null"); |
| ensure!(!buf.data.is_null(), "buffer's data ptr must not be null"); |
| Ok(buf) |
| } |
| } |
| |
| /// An input buffer will always be returned to its original owner when no longer being used. |
| /// An input buffer is used for every buffer handed from C++ to Rust. |
| #[derive(Debug)] |
| #[repr(C)] |
| pub struct InBuf { |
| /// Returns the buffer's ownership and free it. |
| free_buffer: unsafe extern "C" fn(raw: *mut c_void), |
| /// Pointer to the buffer's underlying data structure. |
| raw: *mut c_void, |
| /// Pointer to the start of the buffer's data portion and its length. |
| data: *mut u8, |
| len: usize, |
| } |
| |
| impl Drop for InBuf { |
| fn drop(&mut self) { |
| if !self.raw.is_null() { |
| // A buffer can never be dropped twice as it'll be reset once dropped. |
| unsafe { (self.free_buffer)(self.raw) }; |
| self.reset(); |
| } |
| } |
| } |
| |
| impl InBuf { |
| pub fn as_slice(&self) -> &[u8] { |
| if self.data.is_null() { |
| &[] |
| } else { |
| // `data` pointer is never null. This could still be problematic if the buffer was |
| // already destroyed, however, that's out of this code's control. |
| unsafe { slice::from_raw_parts(self.data, self.len) } |
| } |
| } |
| |
| pub fn as_mut_slice(&mut self) -> &mut [u8] { |
| if self.data.is_null() { |
| &mut [] |
| } else { |
| // `data` pointer is never null. This could still be problematic if the buffer was |
| // already destroyed, however, that's out of this code's control. |
| unsafe { slice::from_raw_parts_mut(self.data, self.len) } |
| } |
| } |
| |
| fn reset(&mut self) { |
| self.raw = ptr::null_mut(); |
| self.data = ptr::null_mut(); |
| self.len = 0; |
| } |
| } |
| |
| impl Deref for InBuf { |
| type Target = [u8]; |
| |
| fn deref(&self) -> &[u8] { |
| self.as_slice() |
| } |
| } |
| |
| impl DerefMut for InBuf { |
| fn deref_mut(&mut self) -> &mut [u8] { |
| self.as_mut_slice() |
| } |
| } |
| |
| |
| /// An output buffer requires its owner to manage the underlying buffer's memory themselves. |
| /// An output buffer is used for every buffer handed from Rust to C++. |
| #[derive(Debug)] |
| #[repr(C)] |
| pub struct OutBuf { |
| /// Pointer to the buffer's underlying data structure. |
| raw: *mut c_void, |
| /// Pointer to the start of the buffer's data portion and the amount of bytes written. |
| data: *mut u8, |
| written_bytes: usize, |
| } |
| |
| impl OutBuf { |
| fn from(buf: InBuf, written_bytes: usize) -> Self { |
| OutBuf { |
| raw: buf.raw, |
| data: buf.data, |
| written_bytes, |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use {super::*, std::ptr}; |
| |
| extern "C" fn default_free_buffer(_raw: *mut c_void) {} |
| |
| #[test] |
| fn return_out_of_scope_buffer() { |
| static mut RETURNED_RAW_BUFFER: *mut c_void = ptr::null_mut(); |
| |
| unsafe extern "C" fn assert_free_buffer(raw: *mut c_void) { |
| // Safe, this is a function specific static field. |
| unsafe { |
| RETURNED_RAW_BUFFER = raw; |
| } |
| } |
| |
| // Once buffer goes out of scope the raw pointer should be returned to the provider. |
| { |
| InBuf { |
| free_buffer: assert_free_buffer, |
| raw: 42 as *mut c_void, |
| data: ptr::null_mut(), |
| len: 0, |
| }; |
| } |
| |
| // Safe, this is a function specific static field. |
| unsafe { |
| assert_eq!(42 as *mut c_void, RETURNED_RAW_BUFFER); |
| } |
| } |
| |
| #[test] |
| fn as_slice_null_data() { |
| let buf = InBuf { |
| free_buffer: default_free_buffer, |
| raw: 42 as *mut c_void, |
| data: ptr::null_mut(), |
| len: 10, |
| }; |
| assert_eq!(buf.as_slice(), []); |
| } |
| |
| #[test] |
| fn as_slice() { |
| let mut data = [0u8, 1, 2, 3]; |
| let buf = InBuf { |
| free_buffer: default_free_buffer, |
| raw: 42 as *mut c_void, |
| data: data.as_mut_ptr(), |
| len: data.len(), |
| }; |
| assert_eq!(buf.as_slice(), &data[..]); |
| assert_eq!(&buf[..], &data[..]); |
| } |
| |
| #[test] |
| fn as_mut_slice_null_data() { |
| let mut buf = InBuf { |
| free_buffer: default_free_buffer, |
| raw: 42 as *mut c_void, |
| data: ptr::null_mut(), |
| len: 10, |
| }; |
| assert_eq!(buf.as_mut_slice(), []); |
| } |
| |
| #[test] |
| fn as_mut_slice() { |
| let mut data = [0u8, 1, 2, 3]; |
| let mut buf = InBuf { |
| free_buffer: default_free_buffer, |
| raw: 42 as *mut c_void, |
| data: data.as_mut_ptr(), |
| len: data.len(), |
| }; |
| assert_eq!(buf.as_mut_slice(), &data[..]); |
| assert_eq!(&buf[..], &data[..]); |
| } |
| |
| #[test] |
| fn from_in_buf() { |
| let inbuf = InBuf { |
| free_buffer: default_free_buffer, |
| raw: 42 as *mut c_void, |
| data: 43 as *mut u8, |
| len: 20, |
| }; |
| let outbuf = OutBuf::from(inbuf, 10); |
| assert_eq!(42 as *mut c_void, outbuf.raw); |
| assert_eq!(43 as *mut u8, outbuf.data); |
| assert_eq!(10, outbuf.written_bytes); |
| } |
| |
| } |