1 /* Copyright 2018-2019 Mozilla Foundation
2  *
3  * Licensed under the Apache License (Version 2.0), or the MIT license,
4  * (the "Licenses") at your option. You may not use this file except in
5  * compliance with one of the Licenses. You may obtain copies of the
6  * Licenses at:
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *    http://opensource.org/licenses/MIT
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the Licenses is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the Licenses for the specific language governing permissions and
15  * limitations under the Licenses. */
16 
17 #![deny(missing_docs)]
18 #![allow(unknown_lints)]
19 #![warn(rust_2018_idioms)]
20 
21 //! # FFI Support
22 //!
23 //! This crate implements a support library to simplify implementing the patterns that the
24 //! `mozilla/application-services` repository uses for it's "Rust Component" FFI libraries.
25 //!
26 //! It is *strongly encouraged* that anybody writing FFI code in this repository read this
27 //! documentation before doing so, as it is a subtle, difficult, and error prone process.
28 //!
29 //! ## Terminology
30 //!
31 //! For each library, there are currently three parts we're concerned with. There's no clear correct
32 //! name for these, so this documentation will attempt to use the following terminology:
33 //!
34 //! - **Rust Component**: A Rust crate which does not expose an FFI directly, but may be may be
35 //!   wrapped by one that does. These have a `crate-type` in their Cargo.toml (see
36 //!   https://doc.rust-lang.org/reference/linkage.html) of `lib`, and not `staticlib` or `cdylib`
37 //!   (Note that `lib` is the default if `crate-type` is not specified). Examples include the
38 //!   `fxa-client`, and `logins` crates.
39 //!
40 //! - **FFI Component**: A wrapper crate that takes a Rust component, and exposes an FFI from it.
41 //!   These typically have `ffi` in the name, and have `crate-type = ["lib", "staticlib", "cdylib"]`
42 //!   in their Cargo.toml. For example, the `fxa-client/ffi` and `logins/ffi` crates (note:
43 //!   paths are subject to change). When built, these produce a native library that is consumed by
44 //!   the "FFI Consumer".
45 //!
46 //! - **FFI Consumer**: This is a low level library, typically implemented in Kotlin (for Android)
47 //!   or Swift (for iOS), that exposes a memory-safe wrapper around the memory-unsafe C API produced
48 //!   by the FFI component. It's expected that the maintainers of the FFI Component and FFI Consumer
49 //!   be the same (or at least, the author of the consumer should be completely comfortable with the
50 //!   API exposed by, and code in the FFI component), since the code in these is extremely tightly
51 //!   coupled, and very easy to get wrong.
52 //!
53 //! Note that while there are three parts, there may be more than three libraries relevant here, for
54 //! example there may be more than one FFI consumer (one for Android, one for iOS).
55 //!
56 //! ## Usage
57 //!
58 //! This library will typically be used in both the Rust component, and the FFI component, however
59 //! it frequently will be an optional dependency in the Rust component that's only available when a
60 //! feature flag (which the FFI component will always require) is used.
61 //!
62 //! The reason it's required inside the Rust component (and not solely in the FFI component, which
63 //! would be nice), is so that types provided by that crate may implement the traits provided by
64 //! this crate (this is because Rust does not allow crate `C` to implement a trait defined in crate
65 //! `A` for a type defined in crate `B`).
66 //!
67 //! In general, examples should be provided for the most important types and functions
68 //! ([`call_with_result`], [`IntoFfi`],
69 //! [`ExternError`], etc), but you should also look at the code of
70 //! consumers of this library.
71 //!
72 //! ### Usage in the Rust Component
73 //!
74 //! Inside the Rust component, you will implement:
75 //!
76 //! 1. [`IntoFfi`] for all types defined in that crate that you want to return
77 //!    over the FFI. For most common cases, the [`implement_into_ffi_by_json!`] and
78 //!    [`implement_into_ffi_by_protobuf!`] macros will do the job here, however you
79 //!    can see that trait's documentation for discussion and examples of
80 //!    implementing it manually.
81 //!
82 //! 2. Conversion to [`ExternError`] for the error type(s) exposed by that
83 //!    rust component, that is, `impl From<MyError> for ExternError`.
84 //!
85 //! ### Usage in the FFI Component
86 //!
87 //! Inside the FFI component, you will use this library in a few ways:
88 //!
89 //! 1. Destructors will be exposed for each types that had [`implement_into_ffi_by_pointer!`] called
90 //!    on it (using [`define_box_destructor!`]), and a destructor for strings should be exposed as
91 //!    well, using [`define_string_destructor`]
92 //!
93 //! 2. The body of every / nearly every FFI function will be wrapped in either a
94 //!    [`call_with_result`] or [`call_with_output`].
95 //!
96 //!    This is required because if we `panic!` (e.g. from an `assert!`, `unwrap()`, `expect()`, from
97 //!    indexing past the end of an array, etc) across the FFI boundary, the behavior is undefined
98 //!    and in practice very weird things tend to happen (we aren't caught by the caller, since they
99 //!    don't have the same exception behavior as us).
100 //!
101 //!    If you don't think your program (or possibly just certain calls) can handle panics, you may
102 //!    also use the versions of these functions in the [`abort_on_panic`] module, which
103 //!    do as their name suggest.
104 //!
105 //! Additionally, c strings that are passed in as arguments may be represented using [`FfiStr`],
106 //! which contains several helpful inherent methods for extracting their data.
107 //!
108 
109 use std::{panic, thread};
110 
111 mod error;
112 mod ffistr;
113 pub mod handle_map;
114 mod into_ffi;
115 #[macro_use]
116 mod macros;
117 mod string;
118 
119 pub use crate::error::*;
120 pub use crate::ffistr::FfiStr;
121 pub use crate::into_ffi::*;
122 pub use crate::macros::*;
123 pub use crate::string::*;
124 
125 // We export most of the types from this, but some constants
126 // (MAX_CAPACITY) don't make sense at the top level.
127 pub use crate::handle_map::{ConcurrentHandleMap, Handle, HandleError, HandleMap};
128 
129 /// Call a callback that returns a `Result<T, E>` while:
130 ///
131 /// - Catching panics, and reporting them to C via [`ExternError`].
132 /// - Converting `T` to a C-compatible type using [`IntoFfi`].
133 /// - Converting `E` to a C-compatible error via `Into<ExternError>`.
134 ///
135 /// This (or [`call_with_output`]) should be in the majority of the FFI functions, see the crate
136 /// top-level docs for more info.
137 ///
138 /// If your function doesn't produce an error, you may use [`call_with_output`] instead, which
139 /// doesn't require you return a Result.
140 ///
141 /// ## Example
142 ///
143 /// A few points about the following example:
144 ///
145 /// - We need to mark it as `#[no_mangle] pub extern "C"`.
146 ///
147 /// - We prefix it with a unique name for the library (e.g. `mylib_`). Foreign functions are not
148 ///   namespaced, and symbol collisions can cause a large number of problems and subtle bugs,
149 ///   including memory safety issues in some cases.
150 ///
151 /// ```rust,no_run
152 /// # use ffi_support::{ExternError, ErrorCode, FfiStr};
153 /// # use std::os::raw::c_char;
154 ///
155 /// # #[derive(Debug)]
156 /// # struct BadEmptyString;
157 /// # impl From<BadEmptyString> for ExternError {
158 /// #     fn from(e: BadEmptyString) -> Self {
159 /// #         ExternError::new_error(ErrorCode::new(1), "Bad empty string")
160 /// #     }
161 /// # }
162 ///
163 /// #[no_mangle]
164 /// pub extern "C" fn mylib_print_string(
165 ///     // Strings come in as an `FfiStr`, which is a wrapper around a null terminated C string.
166 ///     thing_to_print: FfiStr<'_>,
167 ///     // Note that taking `&mut T` and `&T` is both allowed and encouraged, so long as `T: Sized`,
168 ///     // (e.g. it can't be a trait object, `&[T]`, a `&str`, etc). Also note that `Option<&T>` and
169 ///     // `Option<&mut T>` are also allowed, if you expect the caller to sometimes pass in null, but
170 ///     // that's the only case when it's currently to use `Option` in an argument list like this).
171 ///     error: &mut ExternError
172 /// ) {
173 ///     // You should try to to do as little as possible outside the call_with_result,
174 ///     // to avoid a case where a panic occurs.
175 ///     ffi_support::call_with_result(error, || {
176 ///         let s = thing_to_print.as_str();
177 ///         if s.is_empty() {
178 ///             // This is a silly example!
179 ///             return Err(BadEmptyString);
180 ///         }
181 ///         println!("{}", s);
182 ///         Ok(())
183 ///     })
184 /// }
185 /// ```
call_with_result<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value where F: panic::UnwindSafe + FnOnce() -> Result<R, E>, E: Into<ExternError>, R: IntoFfi,186 pub fn call_with_result<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
187 where
188     F: panic::UnwindSafe + FnOnce() -> Result<R, E>,
189     E: Into<ExternError>,
190     R: IntoFfi,
191 {
192     call_with_result_impl(out_error, callback)
193 }
194 
195 /// Call a callback that returns a `T` while:
196 ///
197 /// - Catching panics, and reporting them to C via [`ExternError`]
198 /// - Converting `T` to a C-compatible type using [`IntoFfi`]
199 ///
200 /// Note that you still need to provide an [`ExternError`] to this function, to report panics.
201 ///
202 /// See [`call_with_result`] if you'd like to return a `Result<T, E>` (Note: `E` must
203 /// be convertible to [`ExternError`]).
204 ///
205 /// This (or [`call_with_result`]) should be in the majority of the FFI functions, see
206 /// the crate top-level docs for more info.
call_with_output<R, F>(out_error: &mut ExternError, callback: F) -> R::Value where F: panic::UnwindSafe + FnOnce() -> R, R: IntoFfi,207 pub fn call_with_output<R, F>(out_error: &mut ExternError, callback: F) -> R::Value
208 where
209     F: panic::UnwindSafe + FnOnce() -> R,
210     R: IntoFfi,
211 {
212     // We need something that's `Into<ExternError>`, even though we never return it, so just use
213     // `ExternError` itself.
214     call_with_result(out_error, || -> Result<_, ExternError> { Ok(callback()) })
215 }
216 
call_with_result_impl<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value where F: panic::UnwindSafe + FnOnce() -> Result<R, E>, E: Into<ExternError>, R: IntoFfi,217 fn call_with_result_impl<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
218 where
219     F: panic::UnwindSafe + FnOnce() -> Result<R, E>,
220     E: Into<ExternError>,
221     R: IntoFfi,
222 {
223     *out_error = ExternError::success();
224     let res: thread::Result<(ExternError, R::Value)> = panic::catch_unwind(|| {
225         init_panic_handling_once();
226         match callback() {
227             Ok(v) => (ExternError::default(), v.into_ffi_value()),
228             Err(e) => (e.into(), R::ffi_default()),
229         }
230     });
231     match res {
232         Ok((err, o)) => {
233             *out_error = err;
234             o
235         }
236         Err(e) => {
237             *out_error = e.into();
238             R::ffi_default()
239         }
240     }
241 }
242 
243 /// This module exists just to expose a variant of [`call_with_result`] and [`call_with_output`]
244 /// that aborts, instead of unwinding, on panic.
245 pub mod abort_on_panic {
246     use super::*;
247 
248     // Struct that exists to automatically process::abort if we don't call
249     // `std::mem::forget()` on it. This can have substantial performance
250     // benefits over calling `std::panic::catch_unwind` and aborting if a panic
251     // was caught, in addition to not requiring AssertUnwindSafe (for example).
252     struct AbortOnDrop;
253     impl Drop for AbortOnDrop {
drop(&mut self)254         fn drop(&mut self) {
255             std::process::abort();
256         }
257     }
258 
259     /// A helper function useful for cases where you'd like to abort on panic,
260     /// but aren't in a position where you'd like to return an FFI-compatible
261     /// type.
262     #[inline]
with_abort_on_panic<R, F>(callback: F) -> R where F: FnOnce() -> R,263     pub fn with_abort_on_panic<R, F>(callback: F) -> R
264     where
265         F: FnOnce() -> R,
266     {
267         let aborter = AbortOnDrop;
268         let res = callback();
269         std::mem::forget(aborter);
270         res
271     }
272 
273     /// Same as the root `call_with_result`, but aborts on panic instead of unwinding. See the
274     /// `call_with_result` documentation for more.
call_with_result<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value where F: FnOnce() -> Result<R, E>, E: Into<ExternError>, R: IntoFfi,275     pub fn call_with_result<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
276     where
277         F: FnOnce() -> Result<R, E>,
278         E: Into<ExternError>,
279         R: IntoFfi,
280     {
281         with_abort_on_panic(|| match callback() {
282             Ok(v) => {
283                 *out_error = ExternError::default();
284                 v.into_ffi_value()
285             }
286             Err(e) => {
287                 *out_error = e.into();
288                 R::ffi_default()
289             }
290         })
291     }
292 
293     /// Same as the root `call_with_output`, but aborts on panic instead of unwinding. As a result,
294     /// it doesn't require a [`ExternError`] out argument. See the `call_with_output` documentation
295     /// for more info.
call_with_output<R, F>(callback: F) -> R::Value where F: FnOnce() -> R, R: IntoFfi,296     pub fn call_with_output<R, F>(callback: F) -> R::Value
297     where
298         F: FnOnce() -> R,
299         R: IntoFfi,
300     {
301         with_abort_on_panic(callback).into_ffi_value()
302     }
303 }
304 
305 #[cfg(feature = "log_panics")]
init_panic_handling_once()306 fn init_panic_handling_once() {
307     use std::sync::Once;
308     static INIT_BACKTRACES: Once = Once::new();
309     INIT_BACKTRACES.call_once(move || {
310         #[cfg(all(feature = "log_backtraces", not(target_os = "android")))]
311         {
312             std::env::set_var("RUST_BACKTRACE", "1");
313         }
314         // Turn on a panic hook which logs both backtraces and the panic
315         // "Location" (file/line). We do both in case we've been stripped,
316         // ).
317         std::panic::set_hook(Box::new(move |panic_info| {
318             let (file, line) = if let Some(loc) = panic_info.location() {
319                 (loc.file(), loc.line())
320             } else {
321                 // Apparently this won't happen but rust has reserved the
322                 // ability to start returning None from location in some cases
323                 // in the future.
324                 ("<unknown>", 0)
325             };
326             log::error!("### Rust `panic!` hit at file '{}', line {}", file, line);
327             #[cfg(all(feature = "log_backtraces", not(target_os = "android")))]
328             {
329                 log::error!("  Complete stack trace:\n{:?}", backtrace::Backtrace::new());
330             }
331         }));
332     });
333 }
334 
335 #[cfg(not(feature = "log_panics"))]
init_panic_handling_once()336 fn init_panic_handling_once() {}
337 
338 /// ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries.
339 /// There are several cases when you might want to use this, but the primary one for us
340 /// is for returning protobuf-encoded data to Swift and Java. The type is currently rather
341 /// limited (implementing almost no functionality), however in the future it may be
342 /// more expanded.
343 ///
344 /// ## Caveats
345 ///
346 /// Note that the order of the fields is `len` (an i64) then `data` (a `*mut u8`), getting
347 /// this wrong on the other side of the FFI will cause memory corruption and crashes.
348 /// `i64` is used for the length instead of `u64` and `usize` because JNA has interop
349 /// issues with both these types.
350 ///
351 /// ### `Drop` is not implemented
352 ///
353 /// ByteBuffer does not implement Drop. This is intentional. Memory passed into it will
354 /// be leaked if it is not explicitly destroyed by calling [`ByteBuffer::destroy`], or
355 /// [`ByteBuffer::destroy_into_vec`]. This is for two reasons:
356 ///
357 /// 1. In the future, we may allow it to be used for data that is not managed by
358 ///    the Rust allocator\*, and `ByteBuffer` assuming it's okay to automatically
359 ///    deallocate this data with the Rust allocator.
360 ///
361 /// 2. Automatically running destructors in unsafe code is a
362 ///    [frequent footgun](https://without.boats/blog/two-memory-bugs-from-ringbahn/)
363 ///    (among many similar issues across many crates).
364 ///
365 /// Note that calling `destroy` manually is often not needed, as usually you should
366 /// be passing these to the function defined by [`define_bytebuffer_destructor!`] from
367 /// the other side of the FFI.
368 ///
369 /// Because this type is essentially *only* useful in unsafe or FFI code (and because
370 /// the most common usage pattern does not require manually managing the memory), it
371 /// does not implement `Drop`.
372 ///
373 /// \* Note: in the case of multiple Rust shared libraries loaded at the same time,
374 /// there may be multiple instances of "the Rust allocator" (one per shared library),
375 /// in which case we're referring to whichever instance is active for the code using
376 /// the `ByteBuffer`. Note that this doesn't occur on all platforms or build
377 /// configurations, but treating allocators in different shared libraries as fully
378 /// independent is always safe.
379 ///
380 /// ## Layout/fields
381 ///
382 /// This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so
383 /// that we can verify rust users are constructing them appropriately), the fields, their types, and
384 /// their order are *very much* a part of the public API of this type. Consumers on the other side
385 /// of the FFI will need to know its layout.
386 ///
387 /// If this were a C struct, it would look like
388 ///
389 /// ```c,no_run
390 /// struct ByteBuffer {
391 ///     // Note: This should never be negative, but values above
392 ///     // INT64_MAX / i64::MAX are not allowed.
393 ///     int64_t len;
394 ///     // Note: nullable!
395 ///     uint8_t *data;
396 /// };
397 /// ```
398 ///
399 /// In rust, there are two fields, in this order: `len: i64`, and `data: *mut u8`.
400 ///
401 /// For clarity, the fact that the data pointer is nullable means that `Option<ByteBuffer>` is not
402 /// the same size as ByteBuffer, and additionally is not FFI-safe (the latter point is not
403 /// currently guaranteed anyway as of the time of writing this comment).
404 ///
405 /// ### Description of fields
406 ///
407 /// `data` is a pointer to an array of `len` bytes. Note that data can be a null pointer and therefore
408 /// should be checked.
409 ///
410 /// The bytes array is allocated on the heap and must be freed on it as well. Critically, if there
411 /// are multiple rust shared libraries using being used in the same application, it *must be freed
412 /// on the same heap that allocated it*, or you will corrupt both heaps.
413 ///
414 /// Typically, this object is managed on the other side of the FFI (on the "FFI consumer"), which
415 /// means you must expose a function to release the resources of `data` which can be done easily
416 /// using the [`define_bytebuffer_destructor!`] macro provided by this crate.
417 #[repr(C)]
418 pub struct ByteBuffer {
419     len: i64,
420     data: *mut u8,
421 }
422 
423 impl From<Vec<u8>> for ByteBuffer {
424     #[inline]
from(bytes: Vec<u8>) -> Self425     fn from(bytes: Vec<u8>) -> Self {
426         Self::from_vec(bytes)
427     }
428 }
429 
430 impl ByteBuffer {
431     /// Creates a `ByteBuffer` of the requested size, zero-filled.
432     ///
433     /// The contents of the vector will not be dropped. Instead, `destroy` must
434     /// be called later to reclaim this memory or it will be leaked.
435     ///
436     /// ## Caveats
437     ///
438     /// This will panic if the buffer length (`usize`) cannot fit into a `i64`.
439     #[inline]
new_with_size(size: usize) -> Self440     pub fn new_with_size(size: usize) -> Self {
441         // Note: `Vec` requires this internally on 64 bit platforms (and has a
442         // stricter requirement on 32 bit ones), so this is just to be explicit.
443         assert!(size < i64::MAX as usize);
444         let mut buf = vec![];
445         buf.reserve_exact(size);
446         buf.resize(size, 0);
447         ByteBuffer::from_vec(buf)
448     }
449 
450     /// Creates a `ByteBuffer` instance from a `Vec` instance.
451     ///
452     /// The contents of the vector will not be dropped. Instead, `destroy` must
453     /// be called later to reclaim this memory or it will be leaked.
454     ///
455     /// ## Caveats
456     ///
457     /// This will panic if the buffer length (`usize`) cannot fit into a `i64`.
458     #[inline]
from_vec(bytes: Vec<u8>) -> Self459     pub fn from_vec(bytes: Vec<u8>) -> Self {
460         use std::convert::TryFrom;
461         let mut buf = bytes.into_boxed_slice();
462         let data = buf.as_mut_ptr();
463         let len = i64::try_from(buf.len()).expect("buffer length cannot fit into a i64.");
464         std::mem::forget(buf);
465         Self { data, len }
466     }
467 
468     /// View the data inside this `ByteBuffer` as a `&[u8]`.
469     // TODO: Is it worth implementing `Deref`? Patches welcome if you need this.
470     #[inline]
as_slice(&self) -> &[u8]471     pub fn as_slice(&self) -> &[u8] {
472         if self.data.is_null() {
473             &[]
474         } else {
475             unsafe { std::slice::from_raw_parts(self.data, self.len()) }
476         }
477     }
478 
479     #[inline]
len(&self) -> usize480     fn len(&self) -> usize {
481         use std::convert::TryInto;
482         self.len
483             .try_into()
484             .expect("ByteBuffer length negative or overflowed")
485     }
486 
487     /// View the data inside this `ByteBuffer` as a `&mut [u8]`.
488     // TODO: Is it worth implementing `DerefMut`? Patches welcome if you need this.
489     #[inline]
as_mut_slice(&mut self) -> &mut [u8]490     pub fn as_mut_slice(&mut self) -> &mut [u8] {
491         if self.data.is_null() {
492             &mut []
493         } else {
494             unsafe { std::slice::from_raw_parts_mut(self.data, self.len()) }
495         }
496     }
497 
498     /// Deprecated alias for [`ByteBuffer::destroy_into_vec`].
499     #[inline]
500     #[deprecated = "Name is confusing, please use `destroy_into_vec` instead"]
into_vec(self) -> Vec<u8>501     pub fn into_vec(self) -> Vec<u8> {
502         self.destroy_into_vec()
503     }
504 
505     /// Convert this `ByteBuffer` into a Vec<u8>, taking ownership of the
506     /// underlying memory, which will be freed using the rust allocator once the
507     /// `Vec<u8>`'s lifetime is done.
508     ///
509     /// If this is undesirable, you can do `bb.as_slice().to_vec()` to get a
510     /// `Vec<u8>` containing a copy of this `ByteBuffer`'s underlying data.
511     ///
512     /// ## Caveats
513     ///
514     /// This is safe so long as the buffer is empty, or the data was allocated
515     /// by Rust code, e.g. this is a ByteBuffer created by
516     /// `ByteBuffer::from_vec` or `Default::default`.
517     ///
518     /// If the ByteBuffer were allocated by something other than the
519     /// current/local Rust `global_allocator`, then calling `destroy` is
520     /// fundamentally broken.
521     ///
522     /// For example, if it were allocated externally by some other language's
523     /// runtime, or if it were allocated by the global allocator of some other
524     /// Rust shared object in the same application, the behavior is undefined
525     /// (and likely to cause problems).
526     ///
527     /// Note that this currently can only happen if the `ByteBuffer` is passed
528     /// to you via an `extern "C"` function that you expose, as opposed to being
529     /// created locally.
530     #[inline]
destroy_into_vec(self) -> Vec<u8>531     pub fn destroy_into_vec(self) -> Vec<u8> {
532         if self.data.is_null() {
533             vec![]
534         } else {
535             let len = self.len();
536             // Safety: This is correct because we convert to a Box<[u8]> first,
537             // which is a design constraint of RawVec.
538             unsafe { Vec::from_raw_parts(self.data, len, len) }
539         }
540     }
541 
542     /// Reclaim memory stored in this ByteBuffer.
543     ///
544     /// You typically should not call this manually, and instead expose a
545     /// function that does so via [`define_bytebuffer_destructor!`].
546     ///
547     /// ## Caveats
548     ///
549     /// This is safe so long as the buffer is empty, or the data was allocated
550     /// by Rust code, e.g. this is a ByteBuffer created by
551     /// `ByteBuffer::from_vec` or `Default::default`.
552     ///
553     /// If the ByteBuffer were allocated by something other than the
554     /// current/local Rust `global_allocator`, then calling `destroy` is
555     /// fundamentally broken.
556     ///
557     /// For example, if it were allocated externally by some other language's
558     /// runtime, or if it were allocated by the global allocator of some other
559     /// Rust shared object in the same application, the behavior is undefined
560     /// (and likely to cause problems).
561     ///
562     /// Note that this currently can only happen if the `ByteBuffer` is passed
563     /// to you via an `extern "C"` function that you expose, as opposed to being
564     /// created locally.
565     #[inline]
destroy(self)566     pub fn destroy(self) {
567         // Note: the drop is just for clarity, of course.
568         drop(self.destroy_into_vec())
569     }
570 }
571 
572 impl Default for ByteBuffer {
573     #[inline]
default() -> Self574     fn default() -> Self {
575         Self {
576             len: 0 as i64,
577             data: std::ptr::null_mut(),
578         }
579     }
580 }
581 
582 #[cfg(test)]
583 mod test {
584     use super::*;
585     #[test]
test_bb_access()586     fn test_bb_access() {
587         let mut bb = ByteBuffer::from(vec![1u8, 2, 3]);
588         assert_eq!(bb.as_slice(), &[1u8, 2, 3]);
589         assert_eq!(bb.as_mut_slice(), &mut [1u8, 2, 3]);
590         bb.as_mut_slice()[2] = 4;
591 
592         // Use into_vec to cover both into_vec and destroy_into_vec.
593         #[allow(deprecated)]
594         {
595             assert_eq!(bb.into_vec(), &[1u8, 2, 4]);
596         }
597     }
598 
599     #[test]
test_bb_empty()600     fn test_bb_empty() {
601         let mut bb = ByteBuffer::default();
602         assert_eq!(bb.as_slice(), &[]);
603         assert_eq!(bb.as_mut_slice(), &[]);
604         assert_eq!(bb.destroy_into_vec(), &[]);
605     }
606 
607     #[test]
test_bb_new()608     fn test_bb_new() {
609         let bb = ByteBuffer::new_with_size(5);
610         assert_eq!(bb.as_slice(), &[0u8, 0, 0, 0, 0]);
611         bb.destroy();
612 
613         let bb = ByteBuffer::new_with_size(0);
614         assert_eq!(bb.as_slice(), &[]);
615         assert!(!bb.data.is_null());
616         bb.destroy();
617 
618         let bb = ByteBuffer::from_vec(vec![]);
619         assert_eq!(bb.as_slice(), &[]);
620         assert!(!bb.data.is_null());
621         bb.destroy();
622     }
623 }
624