/* 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/. */ /// Support for reading a slice of foreign-language-allocated bytes over the FFI. /// /// Foreign language code can pass a slice of bytes by providing a data pointer /// and length, and this struct provides a convenient wrapper for working with /// that pair. Naturally, this can be tremendously unsafe! So here are the details: /// /// * The foreign language code must ensure the provided buffer stays alive /// and unchanged for the duration of the call to which the `ForeignBytes` /// struct was provided. /// /// To work with the bytes in Rust code, use `as_slice()` to view the data /// as a `&[u8]`. /// /// Implementation note: all the fields of this struct are private and it has no /// constructors, so consuming crates cant create instances of it. If you've /// got a `ForeignBytes`, then you received it over the FFI and are assuming that /// the foreign language code is upholding the above invariants. /// /// This struct is based on `ByteBuffer` from the `ffi-support` crate, but modified /// to give a read-only view of externally-provided bytes. #[repr(C)] pub struct ForeignBytes { /// The length of the pointed-to data. /// We use an `i32` for compatibility with JNA. len: i32, /// The pointer to the foreign-owned bytes. data: *const u8, } impl ForeignBytes { /// Creates a `ForeignBytes` 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: *const u8, len: i32) -> Self { Self { len, data } } /// View the foreign bytes as a `&[u8]`. /// /// # Panics /// /// Panics if the provided struct has a null pointer but non-zero length. /// Panics if the provided length is negative. pub fn as_slice(&self) -> &[u8] { if self.data.is_null() { assert!(self.len == 0, "null ForeignBytes had non-zero length"); &[] } else { unsafe { std::slice::from_raw_parts(self.data, self.len()) } } } /// Get the length of this slice of bytes. /// /// # Panics /// /// Panics if the provided length is negative. pub fn len(&self) -> usize { self.len .try_into() .expect("bytes length negative or overflowed") } /// Returns true if the length of this slice of bytes is 0. pub fn is_empty(&self) -> bool { self.len == 0 } } #[cfg(test)] mod test { use super::*; #[test] fn test_foreignbytes_access() { let v = vec![1u8, 2, 3]; let fbuf = unsafe { ForeignBytes::from_raw_parts(v.as_ptr(), 3) }; assert_eq!(fbuf.len(), 3); assert_eq!(fbuf.as_slice(), &[1u8, 2, 3]); } #[test] fn test_foreignbytes_empty() { let v = Vec::::new(); let fbuf = unsafe { ForeignBytes::from_raw_parts(v.as_ptr(), 0) }; assert_eq!(fbuf.len(), 0); assert_eq!(fbuf.as_slice(), &[0u8; 0]); } #[test] fn test_foreignbytes_null_means_empty() { let fbuf = unsafe { ForeignBytes::from_raw_parts(std::ptr::null_mut(), 0) }; assert_eq!(fbuf.as_slice(), &[0u8; 0]); } #[test] #[should_panic] fn test_foreignbytes_null_must_have_zero_length() { let fbuf = unsafe { ForeignBytes::from_raw_parts(std::ptr::null_mut(), 12) }; fbuf.as_slice(); } #[test] #[should_panic] fn test_foreignbytes_provided_len_must_be_non_negative() { let v = vec![0u8, 1, 2]; let fbuf = unsafe { ForeignBytes::from_raw_parts(v.as_ptr(), -1) }; fbuf.as_slice(); } }