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}