ffi_support/
lib.rs

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
109use std::{panic, thread};
110
111mod error;
112mod ffistr;
113pub mod handle_map;
114mod into_ffi;
115#[macro_use]
116mod macros;
117mod string;
118
119pub use crate::error::*;
120pub use crate::ffistr::FfiStr;
121pub use crate::into_ffi::*;
122pub use crate::macros::*;
123pub 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.
127pub 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/// ```
186pub fn call_with_result<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
187where
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.
207pub fn call_with_output<R, F>(out_error: &mut ExternError, callback: F) -> R::Value
208where
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
217fn call_with_result_impl<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
218where
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        ensure_panic_hook_is_setup();
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.
245pub 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 {
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]
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.
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.
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/// Initialize our panic handling hook to optionally log panics
306#[cfg(feature = "log_panics")]
307pub fn ensure_panic_hook_is_setup() {
308    use std::sync::Once;
309    static INIT_BACKTRACES: Once = Once::new();
310    INIT_BACKTRACES.call_once(move || {
311        #[cfg(all(feature = "log_backtraces", not(target_os = "android")))]
312        {
313            std::env::set_var("RUST_BACKTRACE", "1");
314        }
315        // Turn on a panic hook which logs both backtraces and the panic
316        // "Location" (file/line). We do both in case we've been stripped,
317        // ).
318        std::panic::set_hook(Box::new(move |panic_info| {
319            let (file, line) = if let Some(loc) = panic_info.location() {
320                (loc.file(), loc.line())
321            } else {
322                // Apparently this won't happen but rust has reserved the
323                // ability to start returning None from location in some cases
324                // in the future.
325                ("<unknown>", 0)
326            };
327            log::error!("### Rust `panic!` hit at file '{}', line {}", file, line);
328            #[cfg(all(feature = "log_backtraces", not(target_os = "android")))]
329            {
330                log::error!("  Complete stack trace:\n{:?}", backtrace::Backtrace::new());
331            }
332        }));
333    });
334}
335
336/// Initialize our panic handling hook to optionally log panics
337#[cfg(not(feature = "log_panics"))]
338pub fn ensure_panic_hook_is_setup() {}
339
340/// ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries.
341/// There are several cases when you might want to use this, but the primary one for us
342/// is for returning protobuf-encoded data to Swift and Java. The type is currently rather
343/// limited (implementing almost no functionality), however in the future it may be
344/// more expanded.
345///
346/// ## Caveats
347///
348/// Note that the order of the fields is `len` (an i64) then `data` (a `*mut u8`), getting
349/// this wrong on the other side of the FFI will cause memory corruption and crashes.
350/// `i64` is used for the length instead of `u64` and `usize` because JNA has interop
351/// issues with both these types.
352///
353/// ### `Drop` is not implemented
354///
355/// ByteBuffer does not implement Drop. This is intentional. Memory passed into it will
356/// be leaked if it is not explicitly destroyed by calling [`ByteBuffer::destroy`], or
357/// [`ByteBuffer::destroy_into_vec`]. This is for two reasons:
358///
359/// 1. In the future, we may allow it to be used for data that is not managed by
360///    the Rust allocator\*, and `ByteBuffer` assuming it's okay to automatically
361///    deallocate this data with the Rust allocator.
362///
363/// 2. Automatically running destructors in unsafe code is a
364///    [frequent footgun](https://without.boats/blog/two-memory-bugs-from-ringbahn/)
365///    (among many similar issues across many crates).
366///
367/// Note that calling `destroy` manually is often not needed, as usually you should
368/// be passing these to the function defined by [`define_bytebuffer_destructor!`] from
369/// the other side of the FFI.
370///
371/// Because this type is essentially *only* useful in unsafe or FFI code (and because
372/// the most common usage pattern does not require manually managing the memory), it
373/// does not implement `Drop`.
374///
375/// \* Note: in the case of multiple Rust shared libraries loaded at the same time,
376/// there may be multiple instances of "the Rust allocator" (one per shared library),
377/// in which case we're referring to whichever instance is active for the code using
378/// the `ByteBuffer`. Note that this doesn't occur on all platforms or build
379/// configurations, but treating allocators in different shared libraries as fully
380/// independent is always safe.
381///
382/// ## Layout/fields
383///
384/// This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so
385/// that we can verify rust users are constructing them appropriately), the fields, their types, and
386/// their order are *very much* a part of the public API of this type. Consumers on the other side
387/// of the FFI will need to know its layout.
388///
389/// If this were a C struct, it would look like
390///
391/// ```c,no_run
392/// struct ByteBuffer {
393///     // Note: This should never be negative, but values above
394///     // INT64_MAX / i64::MAX are not allowed.
395///     int64_t len;
396///     // Note: nullable!
397///     uint8_t *data;
398/// };
399/// ```
400///
401/// In rust, there are two fields, in this order: `len: i64`, and `data: *mut u8`.
402///
403/// For clarity, the fact that the data pointer is nullable means that `Option<ByteBuffer>` is not
404/// the same size as ByteBuffer, and additionally is not FFI-safe (the latter point is not
405/// currently guaranteed anyway as of the time of writing this comment).
406///
407/// ### Description of fields
408///
409/// `data` is a pointer to an array of `len` bytes. Note that data can be a null pointer and therefore
410/// should be checked.
411///
412/// The bytes array is allocated on the heap and must be freed on it as well. Critically, if there
413/// are multiple rust shared libraries using being used in the same application, it *must be freed
414/// on the same heap that allocated it*, or you will corrupt both heaps.
415///
416/// Typically, this object is managed on the other side of the FFI (on the "FFI consumer"), which
417/// means you must expose a function to release the resources of `data` which can be done easily
418/// using the [`define_bytebuffer_destructor!`] macro provided by this crate.
419#[repr(C)]
420pub struct ByteBuffer {
421    len: i64,
422    data: *mut u8,
423}
424
425impl From<Vec<u8>> for ByteBuffer {
426    #[inline]
427    fn from(bytes: Vec<u8>) -> Self {
428        Self::from_vec(bytes)
429    }
430}
431
432impl ByteBuffer {
433    /// Creates a `ByteBuffer` of the requested size, zero-filled.
434    ///
435    /// The contents of the vector will not be dropped. Instead, `destroy` must
436    /// be called later to reclaim this memory or it will be leaked.
437    ///
438    /// ## Caveats
439    ///
440    /// This will panic if the buffer length (`usize`) cannot fit into a `i64`.
441    #[inline]
442    pub fn new_with_size(size: usize) -> Self {
443        // Note: `Vec` requires this internally on 64 bit platforms (and has a
444        // stricter requirement on 32 bit ones), so this is just to be explicit.
445        assert!(size < i64::MAX as usize);
446        let mut buf = vec![];
447        buf.reserve_exact(size);
448        buf.resize(size, 0);
449        ByteBuffer::from_vec(buf)
450    }
451
452    /// Creates a `ByteBuffer` instance from a `Vec` instance.
453    ///
454    /// The contents of the vector will not be dropped. Instead, `destroy` must
455    /// be called later to reclaim this memory or it will be leaked.
456    ///
457    /// ## Caveats
458    ///
459    /// This will panic if the buffer length (`usize`) cannot fit into a `i64`.
460    #[inline]
461    pub fn from_vec(bytes: Vec<u8>) -> Self {
462        use std::convert::TryFrom;
463        let mut buf = bytes.into_boxed_slice();
464        let data = buf.as_mut_ptr();
465        let len = i64::try_from(buf.len()).expect("buffer length cannot fit into a i64.");
466        std::mem::forget(buf);
467        Self { data, len }
468    }
469
470    /// View the data inside this `ByteBuffer` as a `&[u8]`.
471    // TODO: Is it worth implementing `Deref`? Patches welcome if you need this.
472    #[inline]
473    pub fn as_slice(&self) -> &[u8] {
474        if self.data.is_null() {
475            &[]
476        } else {
477            unsafe { std::slice::from_raw_parts(self.data, self.len()) }
478        }
479    }
480
481    #[inline]
482    fn len(&self) -> usize {
483        use std::convert::TryInto;
484        self.len
485            .try_into()
486            .expect("ByteBuffer length negative or overflowed")
487    }
488
489    /// View the data inside this `ByteBuffer` as a `&mut [u8]`.
490    // TODO: Is it worth implementing `DerefMut`? Patches welcome if you need this.
491    #[inline]
492    pub fn as_mut_slice(&mut self) -> &mut [u8] {
493        if self.data.is_null() {
494            &mut []
495        } else {
496            unsafe { std::slice::from_raw_parts_mut(self.data, self.len()) }
497        }
498    }
499
500    /// Deprecated alias for [`ByteBuffer::destroy_into_vec`].
501    #[inline]
502    #[deprecated = "Name is confusing, please use `destroy_into_vec` instead"]
503    pub fn into_vec(self) -> Vec<u8> {
504        self.destroy_into_vec()
505    }
506
507    /// Convert this `ByteBuffer` into a Vec<u8>, taking ownership of the
508    /// underlying memory, which will be freed using the rust allocator once the
509    /// `Vec<u8>`'s lifetime is done.
510    ///
511    /// If this is undesirable, you can do `bb.as_slice().to_vec()` to get a
512    /// `Vec<u8>` containing a copy of this `ByteBuffer`'s underlying data.
513    ///
514    /// ## Caveats
515    ///
516    /// This is safe so long as the buffer is empty, or the data was allocated
517    /// by Rust code, e.g. this is a ByteBuffer created by
518    /// `ByteBuffer::from_vec` or `Default::default`.
519    ///
520    /// If the ByteBuffer were allocated by something other than the
521    /// current/local Rust `global_allocator`, then calling `destroy` is
522    /// fundamentally broken.
523    ///
524    /// For example, if it were allocated externally by some other language's
525    /// runtime, or if it were allocated by the global allocator of some other
526    /// Rust shared object in the same application, the behavior is undefined
527    /// (and likely to cause problems).
528    ///
529    /// Note that this currently can only happen if the `ByteBuffer` is passed
530    /// to you via an `extern "C"` function that you expose, as opposed to being
531    /// created locally.
532    #[inline]
533    pub fn destroy_into_vec(self) -> Vec<u8> {
534        if self.data.is_null() {
535            vec![]
536        } else {
537            let len = self.len();
538            // Safety: This is correct because we convert to a Box<[u8]> first,
539            // which is a design constraint of RawVec.
540            unsafe { Vec::from_raw_parts(self.data, len, len) }
541        }
542    }
543
544    /// Reclaim memory stored in this ByteBuffer.
545    ///
546    /// You typically should not call this manually, and instead expose a
547    /// function that does so via [`define_bytebuffer_destructor!`].
548    ///
549    /// ## Caveats
550    ///
551    /// This is safe so long as the buffer is empty, or the data was allocated
552    /// by Rust code, e.g. this is a ByteBuffer created by
553    /// `ByteBuffer::from_vec` or `Default::default`.
554    ///
555    /// If the ByteBuffer were allocated by something other than the
556    /// current/local Rust `global_allocator`, then calling `destroy` is
557    /// fundamentally broken.
558    ///
559    /// For example, if it were allocated externally by some other language's
560    /// runtime, or if it were allocated by the global allocator of some other
561    /// Rust shared object in the same application, the behavior is undefined
562    /// (and likely to cause problems).
563    ///
564    /// Note that this currently can only happen if the `ByteBuffer` is passed
565    /// to you via an `extern "C"` function that you expose, as opposed to being
566    /// created locally.
567    #[inline]
568    pub fn destroy(self) {
569        // Note: the drop is just for clarity, of course.
570        drop(self.destroy_into_vec())
571    }
572}
573
574impl Default for ByteBuffer {
575    #[inline]
576    fn default() -> Self {
577        Self {
578            len: 0 as i64,
579            data: std::ptr::null_mut(),
580        }
581    }
582}
583
584#[cfg(test)]
585mod test {
586    use super::*;
587    #[test]
588    fn test_bb_access() {
589        let mut bb = ByteBuffer::from(vec![1u8, 2, 3]);
590        assert_eq!(bb.as_slice(), &[1u8, 2, 3]);
591        assert_eq!(bb.as_mut_slice(), &mut [1u8, 2, 3]);
592        bb.as_mut_slice()[2] = 4;
593
594        // Use into_vec to cover both into_vec and destroy_into_vec.
595        #[allow(deprecated)]
596        {
597            assert_eq!(bb.into_vec(), &[1u8, 2, 4]);
598        }
599    }
600
601    #[test]
602    fn test_bb_empty() {
603        let mut bb = ByteBuffer::default();
604        assert_eq!(bb.as_slice(), &[]);
605        assert_eq!(bb.as_mut_slice(), &[]);
606        assert_eq!(bb.destroy_into_vec(), &[]);
607    }
608
609    #[test]
610    fn test_bb_new() {
611        let bb = ByteBuffer::new_with_size(5);
612        assert_eq!(bb.as_slice(), &[0u8, 0, 0, 0, 0]);
613        bb.destroy();
614
615        let bb = ByteBuffer::new_with_size(0);
616        assert_eq!(bb.as_slice(), &[]);
617        assert!(!bb.data.is_null());
618        bb.destroy();
619
620        let bb = ByteBuffer::from_vec(vec![]);
621        assert_eq!(bb.as_slice(), &[]);
622        assert!(!bb.data.is_null());
623        bb.destroy();
624    }
625}