glean_ffi/
byte_buffer.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 https://mozilla.org/MPL/2.0/.
4
5//! ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries.
6//!
7//! This is a copy of the same struct from [ffi-support],
8//! with the difference that the length is encoded as a `i32`.
9//!
10//! [ffi-support]: https://docs.rs/ffi-support/0.4.0/src/ffi_support/lib.rs.html#390-393
11
12/// ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries.
13/// There are several cases when you might want to use this, but the primary one for us
14/// is for returning protobuf-encoded data to Swift and Java. The type is currently rather
15/// limited (implementing almost no functionality), however in the future it may be
16/// more expanded.
17///
18/// ## Caveats
19///
20/// Note that the order of the fields is `len` (an i32) then `data` (a `*mut u8`), getting
21/// this wrong on the other side of the FFI will cause memory corruption and crashes.
22/// `i32` is used for the length instead of `u64` and `usize` because JNA has interop
23/// issues with both these types.
24///
25/// ByteBuffer does not implement Drop. This is intentional. Memory passed into it will
26/// be leaked if it is not explicitly destroyed by calling [`ByteBuffer::destroy`]. This
27/// is because in the future, we may allow it's use for passing data into Rust code.
28/// ByteBuffer assuming ownership of the data would make this a problem.
29///
30/// ## Layout/fields
31///
32/// This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so
33/// that we can verify Rust users are constructing them appropriately), the fields, their types, and
34/// their order are *very much* a part of the public API of this type. Consumers on the other side
35/// of the FFI will need to know its layout.
36///
37/// If this were a C struct, it would look like
38///
39/// ```c,no_run
40/// struct ByteBuffer {
41///     int64_t len;
42///     uint8_t *data; // note: nullable
43/// };
44/// ```
45///
46/// In Rust, there are two fields, in this order: `len: i32`, and `data: *mut u8`.
47///
48/// ### Description of fields
49///
50/// `data` is a pointer to an array of `len` bytes. Not that data can be a null pointer and therefore
51/// should be checked.
52///
53/// The bytes array is allocated on the heap and must be freed on it as well. Critically, if there
54/// are multiple rust packages using being used in the same application, it *must be freed on the
55/// same heap that allocated it*, or you will corrupt both heaps.
56#[repr(C)]
57pub struct ByteBuffer {
58    len: i32,
59    data: *mut u8,
60}
61
62impl From<Vec<u8>> for ByteBuffer {
63    #[inline]
64    fn from(bytes: Vec<u8>) -> Self {
65        Self::from_vec(bytes)
66    }
67}
68
69impl ByteBuffer {
70    /// Creates a `ByteBuffer` of the requested size, zero-filled.
71    ///
72    /// The contents of the vector will not be dropped. Instead, `destroy` must
73    /// be called later to reclaim this memory or it will be leaked.
74    ///
75    /// ## Caveats
76    ///
77    /// This will panic if the buffer length (`usize`) cannot fit into a `i32`.
78    #[inline]
79    pub fn new_with_size(size: usize) -> Self {
80        let mut buf = vec![];
81        buf.reserve_exact(size);
82        buf.resize(size, 0);
83        ByteBuffer::from_vec(buf)
84    }
85
86    /// Creates a `ByteBuffer` instance from a `Vec` instance.
87    ///
88    /// The contents of the vector will not be dropped. Instead, `destroy` must
89    /// be called later to reclaim this memory or it will be leaked.
90    ///
91    /// ## Caveats
92    ///
93    /// This will panic if the buffer length (`usize`) cannot fit into a `i32`.
94    #[inline]
95    pub fn from_vec(bytes: Vec<u8>) -> Self {
96        use std::convert::TryFrom;
97        let mut buf = bytes.into_boxed_slice();
98        let data = buf.as_mut_ptr();
99        let len = i32::try_from(buf.len()).expect("buffer length cannot fit into a i32.");
100        std::mem::forget(buf);
101        Self { len, data }
102    }
103
104    /// Convert this `ByteBuffer` into a Vec<u8>. This is the only way
105    /// to access the data from inside the buffer.
106    #[inline]
107    pub fn into_vec(self) -> Vec<u8> {
108        if self.data.is_null() {
109            vec![]
110        } else {
111            // This is correct because we convert to a Box<[u8]> first, which is
112            // a design constraint of RawVec.
113            unsafe { Vec::from_raw_parts(self.data, self.len as usize, self.len as usize) }
114        }
115    }
116
117    /// Reclaim memory stored in this ByteBuffer.
118    ///
119    /// ## Caveats
120    ///
121    /// This is safe so long as the buffer is empty, or the data was allocated
122    /// by Rust code, e.g. this is a ByteBuffer created by
123    /// `ByteBuffer::from_vec` or `Default::default`.
124    ///
125    /// If the ByteBuffer were passed into Rust (which you shouldn't do, since
126    /// theres no way to see the data in Rust currently), then calling `destroy`
127    /// is fundamentally broken.
128    #[inline]
129    pub fn destroy(self) {
130        drop(self.into_vec())
131    }
132}
133
134impl Default for ByteBuffer {
135    #[inline]
136    fn default() -> Self {
137        Self {
138            len: 0,
139            data: std::ptr::null_mut(),
140        }
141    }
142}