Add a Rust type that owns flatbuffers
Change-Id: If834a3ce800625be8f1aaf4bc35c6538a4cab7a8
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
diff --git a/aos/flatbuffers.rs b/aos/flatbuffers.rs
new file mode 100644
index 0000000..073d206
--- /dev/null
+++ b/aos/flatbuffers.rs
@@ -0,0 +1,602 @@
+//! Utilities for working with FlatBuffers types.
+//!
+//! These address similar use cases as the C++ library with the same name, but unlike most of these
+//! wrappers the APIs look very different in most cases to be ergnomic in Rust.
+
+use std::marker::PhantomData;
+
+use flatbuffers::{
+ FlatBufferBuilder, Follow, FollowWith, ForwardsUOffset, InvalidFlatbuffer, SkipSizePrefix,
+ Verifiable, Verifier, WIPOffset,
+};
+
+/// Represents the various forms of buffers we can store tables in.
+///
+/// We need this trait to mark types which won't change the data in a flatbuffer after we verify
+/// it. Rust's type system cannot express this constraint directly, so we use an unsafe trait to
+/// call attention to what implementing types are promising.
+///
+/// # Safety
+///
+/// Implementors of this trait must return a slice with the same contents every time
+/// [`AsRef.as_ref`] is called without any intervening mutable access to the object. In other words,
+/// types implementing this trait must have exclusive ownership of the underlying storage, and not
+/// do anything unusal like returning different pieces of storage each time.
+///
+/// Note that the slice is not required to have the same address each time [`AsRef.as_ref`] is
+/// called, just the same contents. This means types using inline storage can implement this trait
+/// without needing pinning.
+pub unsafe trait FlatbufferStorage: AsRef<[u8]> {}
+
+// SAFETY: Vec has exclusive ownership of its single storage.
+unsafe impl FlatbufferStorage for Vec<u8> {}
+
+// SAFETY: We have a shared reference to the slice and u8 does not have interior mutability, so
+// nobody can change the memory as long as the shared reference exists. We're only using the one
+// slice too.
+unsafe impl FlatbufferStorage for &[u8] {}
+
+// SAFETY: The array is its own storage.
+unsafe impl<const N: usize> FlatbufferStorage for [u8; N] {}
+
+/// A type to wrap buffers returned from [`FlatBufferBuilder.collapse`].
+///
+/// See [`Flatbuffer.finish_minimal_and_collapse`] for an easy way to make use of this.
+#[derive(Clone)]
+pub struct CollapsedFlatBufferBuilder(pub Vec<u8>, pub usize);
+
+impl AsRef<[u8]> for CollapsedFlatBufferBuilder {
+ fn as_ref(&self) -> &[u8] {
+ &self.0[self.1..]
+ }
+}
+
+// SAFETY: The Vec has exclusive ownership of its single storage, and we always access the same
+// part of it.
+unsafe impl FlatbufferStorage for CollapsedFlatBufferBuilder {}
+
+/// An internal sealed trait to manage the size-prefixed versions vs non-size-prefixed variants.
+pub trait PrefixedOrNot: private_size_prefixed::Sealed {
+ /// [`FlatBufferBuilder::finish`] or [`FlatBufferBuilder::finish_size_prefixed`].
+ fn finish_builder<T>(
+ fbb: &mut FlatBufferBuilder,
+ root: WIPOffset<T>,
+ file_identifier: Option<&str>,
+ );
+
+ /// [`flatbuffers::root`] or [`flatbuffers::size_prefixed_root`].
+ fn root<'buf, T: 'buf + Follow<'buf> + Verifiable>(
+ data: &'buf [u8],
+ ) -> Result<T::Inner, InvalidFlatbuffer>;
+
+ /// [`flatbuffers::root_unchecked`] or [`flatbuffers::size_prefixed_root_unchecked`].
+ unsafe fn root_unchecked<'buf, T: 'buf + Follow<'buf>>(data: &'buf [u8]) -> T::Inner;
+
+ /// [`ForwardsUOffset::run_verifier`], after skipping the size if present.
+ fn run_verifier<T: Verifiable>(
+ v: &mut Verifier<'_, '_>,
+ pos: usize,
+ ) -> Result<(), InvalidFlatbuffer>;
+}
+
+/// Marker type to implement [`PrefixedOrNot`] on.
+pub struct NotSizePrefixed;
+
+impl PrefixedOrNot for NotSizePrefixed {
+ fn finish_builder<T>(
+ fbb: &mut FlatBufferBuilder,
+ root: WIPOffset<T>,
+ file_identifier: Option<&str>,
+ ) {
+ fbb.finish::<T>(root, file_identifier);
+ }
+
+ fn root<'buf, T: 'buf + Follow<'buf> + Verifiable>(
+ data: &'buf [u8],
+ ) -> Result<T::Inner, InvalidFlatbuffer> {
+ flatbuffers::root::<'buf, T>(data)
+ }
+
+ unsafe fn root_unchecked<'buf, T: 'buf + Follow<'buf>>(data: &'buf [u8]) -> T::Inner {
+ flatbuffers::root_unchecked::<'buf, T>(data)
+ }
+
+ fn run_verifier<T: Verifiable>(
+ v: &mut Verifier<'_, '_>,
+ pos: usize,
+ ) -> Result<(), InvalidFlatbuffer> {
+ <ForwardsUOffset<T>>::run_verifier(v, pos)
+ }
+}
+
+/// Marker type to implement [`PrefixedOrNot`] on.
+pub struct SizePrefixed;
+
+impl PrefixedOrNot for SizePrefixed {
+ fn finish_builder<T>(
+ fbb: &mut FlatBufferBuilder,
+ root: WIPOffset<T>,
+ file_identifier: Option<&str>,
+ ) {
+ fbb.finish_size_prefixed::<T>(root, file_identifier);
+ }
+
+ fn root<'buf, T: 'buf + Follow<'buf> + Verifiable>(
+ data: &'buf [u8],
+ ) -> Result<T::Inner, InvalidFlatbuffer> {
+ flatbuffers::size_prefixed_root::<'buf, T>(data)
+ }
+
+ unsafe fn root_unchecked<'buf, T: 'buf + Follow<'buf>>(data: &'buf [u8]) -> T::Inner {
+ flatbuffers::size_prefixed_root_unchecked::<'buf, T>(data)
+ }
+
+ fn run_verifier<T: Verifiable>(
+ v: &mut Verifier<'_, '_>,
+ pos: usize,
+ ) -> Result<(), InvalidFlatbuffer> {
+ <SkipSizePrefix<ForwardsUOffset<T>>>::run_verifier(v, pos)
+ }
+}
+
+mod private_size_prefixed {
+ pub trait Sealed {}
+
+ impl Sealed for super::NotSizePrefixed {}
+ impl Sealed for super::SizePrefixed {}
+}
+
+/// A flatbuffer along with its backing storage.
+///
+/// `T` should be a generated flatbuffer table type. Its lifetime argument does not matter, using
+/// `'static` is recommended for simplicity. This type uses the [`FollowWith`] trait to access the
+/// equivalents of `T` with appropriate lifetimes as needed.
+///
+/// This is a bit tricky in Rust because we want to avoid internal pointers (requiring the buffer
+/// to be pinned before using it would be hard to use). Instead of actually storing the flatbuffer
+/// with its pointer to the storage, we (optionally) verify it once and then create the flatbuffer
+/// type on demand.
+pub trait Flatbuffer<T>
+where
+ for<'a> T: FollowWith<'a>,
+ for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
+{
+ /// Provides access to the message.
+ ///
+ /// Note that we can't implement `Deref` because we're not returning a reference. We're
+ /// effectively returning a fat pointer which is semantically equivalent to a Rust reference,
+ /// but it's not the same thing to the language. Also implementing `Deref` would allow wrapping
+ /// this with `Pin`, which doesn't do anything because the actual type being dereferenced is
+ /// still `Unpin`, even though other languages commonly store pointers into the flatbuffer.
+ fn message<'this>(&'this self) -> <<T as FollowWith<'this>>::Inner as Follow<'this>>::Inner;
+
+ /// Revalidates the data. If this returns `Err`, then something has violated the safety
+ /// requirements and code responsible is likely unsound.
+ ///
+ /// You should probably not be using this. It's intended for debug assertions when working with
+ /// cross-language boundaries and otherwise doing tricky things with buffers. Once again, you
+ /// probably have unsoundness if you're using this, verify your flatbuffers before wrapping them
+ /// in a [`Flatbuffer`], not after.
+ fn revalidate<'this>(&'this self) -> Result<(), InvalidFlatbuffer>
+ where
+ <T as FollowWith<'this>>::Inner: Verifiable;
+}
+
+/// A generic implementation of [`Flatbuffer`]. `Storage` is the type used to store the buffer.
+/// `MaybePrefixed` controls whether we're using size-prefixed flatbuffers or not.
+///
+/// Flatbuffers provides a parallel set of APIs for handling size-prefixed buffers. This just means
+/// the size is placed at the beginning, so the root table's offset isn't at the very beginning of
+/// the buffer.
+///
+/// [`NonSizePrefixedFlatbuffer`] and [`SizePrefixedFlatbuffer`] provide more convenient names.
+pub struct MaybePrefixedFlatbuffer<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot> {
+ // SAFETY: We have an invariant that this is always a valid flatbuffer.
+ //
+ // All of our constructors verify that, and we never hand out mutable references so the
+ // requirements for implementors of [`FlatbufferStorage`] ensure it stays valid.
+ storage: Storage,
+
+ /// Pretend we store one of these. The compiler needs this information to clarify that two
+ /// instances with different `T` and/or `MaybePrefixed` are not the same type.
+ _marker: PhantomData<(T, MaybePrefixed)>,
+}
+
+impl<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot> Flatbuffer<T>
+ for MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
+where
+ for<'a> T: FollowWith<'a>,
+ for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
+{
+ fn message<'this>(&'this self) -> <<T as FollowWith<'this>>::Inner as Follow<'this>>::Inner {
+ // SAFETY: This being a valid flatbuffer is an invariant.
+ unsafe {
+ MaybePrefixed::root_unchecked::<<T as FollowWith<'this>>::Inner>(self.storage.as_ref())
+ }
+ }
+
+ fn revalidate<'this>(&'this self) -> Result<(), InvalidFlatbuffer>
+ where
+ <T as FollowWith<'this>>::Inner: Verifiable,
+ {
+ MaybePrefixed::root::<<T as FollowWith<'this>>::Inner>(self.storage.as_ref())?;
+ Ok(())
+ }
+}
+
+impl<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot>
+ MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
+where
+ for<'a> T: FollowWith<'a>,
+ for<'a> <T as FollowWith<'a>>::Inner: Follow<'a> + Verifiable,
+{
+ /// Verifies the flatbuffer and then creates an instance.
+ ///
+ /// Note that `storage` must only refer to the flatbuffer. `FlatBufferBuilder::collapse`
+ /// returns a buffer with unused space at the beginning, for example, you will have to either
+ /// use a slice here or remove the unused space first.
+ pub fn new(storage: Storage) -> Result<Self, InvalidFlatbuffer> {
+ MaybePrefixed::root::<<T as FollowWith<'_>>::Inner>(storage.as_ref())?;
+ Ok(Self {
+ storage,
+ _marker: PhantomData,
+ })
+ }
+}
+
+impl<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot>
+ MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
+where
+ for<'a> T: FollowWith<'a>,
+ for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
+{
+ /// Wraps a flatbuffer without verifying it.
+ ///
+ /// Note that `storage` must only refer to the flatbuffer. `FlatBufferBuilder::collapse`
+ /// returns a buffer with unused space at the beginning, for example, so you can not pass that
+ /// here directly. See [`CollapsedFlatBufferBuilder`] for that particular case.
+ ///
+ /// # Safety
+ ///
+ /// `storage` must refer to a valid flatbuffer.
+ pub unsafe fn new_unchecked(storage: Storage) -> Self {
+ Self {
+ storage,
+ _marker: PhantomData,
+ }
+ }
+
+ /// Returns a reference to the underlying storage.
+ pub fn storage_ref(&self) -> &[u8] {
+ self.storage.as_ref()
+ }
+
+ /// Converts back into the underlying storage.
+ pub fn into_storage(self) -> Storage {
+ self.storage
+ }
+}
+
+impl<T, Storage: FlatbufferStorage + Clone, MaybePrefixed: PrefixedOrNot> Clone
+ for MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
+where
+ for<'a> T: FollowWith<'a>,
+ for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
+{
+ fn clone(&self) -> Self {
+ // SAFETY: We already know it's valid because it was used by `self`.
+ unsafe { Self::new_unchecked(self.storage.clone()) }
+ }
+}
+
+impl<T, MaybePrefixed: PrefixedOrNot>
+ MaybePrefixedFlatbuffer<T, CollapsedFlatBufferBuilder, MaybePrefixed>
+where
+ for<'a> T: FollowWith<'a>,
+ for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
+{
+ /// Finishes a [`FlatBufferBuilder`] and then grabs its buffer. This does not require
+ /// verification to be safe.
+ ///
+ /// We have to encapsulate the `finish` call to enforce matching types.
+ pub fn finish_minimal_and_collapse(mut fbb: FlatBufferBuilder<'_>, root: WIPOffset<T>) -> Self {
+ MaybePrefixed::finish_builder(&mut fbb, root, None);
+ let (vec, offset) = fbb.collapse();
+ // SAFETY: We just finished the builder with an offset of the correct type. The builder
+ // maintained the invariant that it was building a valid flatbuffer, which we are directly
+ // using.
+ unsafe { Self::new_unchecked(CollapsedFlatBufferBuilder(vec, offset)) }
+ }
+}
+
+impl<T, F: Flatbuffer<T> + ?Sized> Flatbuffer<T> for Box<F>
+where
+ for<'a> T: FollowWith<'a>,
+ for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
+{
+ fn message<'this>(&'this self) -> <<T as FollowWith<'this>>::Inner as Follow<'this>>::Inner {
+ self.as_ref().message()
+ }
+
+ fn revalidate<'this>(&'this self) -> Result<(), InvalidFlatbuffer>
+ where
+ <T as FollowWith<'this>>::Inner: Verifiable,
+ {
+ self.as_ref().revalidate()
+ }
+}
+
+pub type NonSizePrefixedFlatbuffer<T, Storage> =
+ MaybePrefixedFlatbuffer<T, Storage, NotSizePrefixed>;
+pub type SizePrefixedFlatbuffer<T, Storage> = MaybePrefixedFlatbuffer<T, Storage, SizePrefixed>;
+
+/// This is the alias for a generic "some kind of buffer that contains a flatbuffer". Use this if
+/// you want your code to work with any type of buffer without being generic.
+///
+/// `'buf` is the lifetime of the underlying storage.
+///
+/// If you only want to support flatbuffers that own their underlying buffer, and don't want to
+/// even make your functions lifetime-generic, use `DynFlatbuffer<'static, T>`. However, note
+/// that being lifetime-generic has no cost in code size or vtable lookups, and if your code isn't
+/// storing references to the flatbuffer anywhere it's a strict increase in flexibility.
+pub type DynFlatbuffer<'buf, T> = Box<dyn Flatbuffer<T> + 'buf>;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use aos_json_to_flatbuffer_fbs::aos::testing::{Location, LocationBuilder};
+
+ /// Tests various methods that take flatbuffers as arguments.
+ #[test]
+ fn test_take() {
+ fn take_box_dyn_ref(flatbuffer: &DynFlatbuffer<'_, Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_box_dyn_ref_static(flatbuffer: &DynFlatbuffer<'static, Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_bare_dyn_ref(flatbuffer: &dyn Flatbuffer<Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_impl_ref(flatbuffer: &impl Flatbuffer<Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_impl_ref_static(flatbuffer: &(impl Flatbuffer<Location<'static>> + 'static)) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_box_dyn(flatbuffer: DynFlatbuffer<'_, Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_box_dyn_static(flatbuffer: DynFlatbuffer<'static, Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_impl(flatbuffer: impl Flatbuffer<Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_impl_static(flatbuffer: impl Flatbuffer<Location<'static>> + 'static) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_nondyn_ref(
+ flatbuffer: &NonSizePrefixedFlatbuffer<Location<'static>, CollapsedFlatBufferBuilder>,
+ ) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ fn take_nondyn(
+ flatbuffer: NonSizePrefixedFlatbuffer<Location<'static>, CollapsedFlatBufferBuilder>,
+ ) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ let flatbuffer = {
+ let mut fbb = FlatBufferBuilder::new();
+ let xyz = fbb.create_string("xyz");
+ let mut location = LocationBuilder::new(&mut fbb);
+ location.add_name(xyz);
+ location.add_frequency(971);
+ let location = location.finish();
+ NonSizePrefixedFlatbuffer::finish_minimal_and_collapse(fbb, location)
+ };
+ let make_dyn_flatbuffer = || Box::new(flatbuffer.clone());
+ let dyn_flatbuffer: DynFlatbuffer<_> = make_dyn_flatbuffer();
+
+ take_box_dyn_ref(&dyn_flatbuffer);
+ take_box_dyn_ref_static(&dyn_flatbuffer);
+ take_bare_dyn_ref(&dyn_flatbuffer);
+ take_bare_dyn_ref(&flatbuffer);
+ take_impl_ref(&dyn_flatbuffer);
+ take_impl_ref(&flatbuffer);
+ take_impl_ref_static(&dyn_flatbuffer);
+ take_impl_ref_static(&flatbuffer);
+ take_box_dyn(make_dyn_flatbuffer());
+ take_box_dyn_static(make_dyn_flatbuffer());
+ take_impl(make_dyn_flatbuffer());
+ take_impl(flatbuffer.clone());
+ take_impl_static(make_dyn_flatbuffer());
+ take_impl_static(flatbuffer.clone());
+ take_nondyn_ref(&flatbuffer);
+ take_nondyn(flatbuffer.clone());
+ }
+
+ /// Tests creating a DynFlatbuffer from various types.
+ #[test]
+ fn test_make_dyn() {
+ fn take_ref(flatbuffer: &impl Flatbuffer<Location<'static>>) {
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ }
+
+ let flatbuffer = {
+ let mut fbb = FlatBufferBuilder::new();
+ let xyz = fbb.create_string("xyz");
+ let mut location = LocationBuilder::new(&mut fbb);
+ location.add_name(xyz);
+ location.add_frequency(971);
+ let location = location.finish();
+ NonSizePrefixedFlatbuffer::finish_minimal_and_collapse(fbb, location)
+ };
+ take_ref(&flatbuffer);
+ let slice_flatbuffer =
+ NonSizePrefixedFlatbuffer::<Location<'static>, &[u8]>::new(flatbuffer.storage_ref())
+ .unwrap();
+ take_ref(&slice_flatbuffer);
+
+ {
+ let dyn_flatbuffer: DynFlatbuffer<_> = Box::new(flatbuffer.clone());
+ take_ref(&dyn_flatbuffer);
+ }
+ {
+ let dyn_flatbuffer: DynFlatbuffer<_> = Box::new(slice_flatbuffer.clone());
+ take_ref(&dyn_flatbuffer);
+ }
+ }
+
+ macro_rules! test_variant {
+ ($maybe_prefixed:ident, $modname:ident, $finish:ident) => {
+ mod $modname {
+ use super::*;
+
+ use aos_json_to_flatbuffer_fbs::aos::testing::{Location, LocationBuilder};
+
+ #[test]
+ fn test_basic() {
+ let fbb = {
+ let mut fbb = FlatBufferBuilder::new();
+ let xyz = fbb.create_string("xyz");
+ let mut location = LocationBuilder::new(&mut fbb);
+ location.add_name(xyz);
+ location.add_frequency(971);
+ let location = location.finish();
+ fbb.$finish(location, None);
+ fbb
+ };
+
+ {
+ let flatbuffer = $maybe_prefixed::<Location, _>::new(
+ fbb.finished_data().iter().copied().collect::<Vec<u8>>(),
+ )
+ .unwrap();
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ flatbuffer.revalidate().unwrap();
+ }
+
+ {
+ let flatbuffer =
+ $maybe_prefixed::<Location, _>::new(fbb.finished_data()).unwrap();
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ flatbuffer.revalidate().unwrap();
+ }
+
+ {
+ let mut array_data = [0; 64];
+ let finished_data = fbb.finished_data();
+ array_data[..finished_data.len()].copy_from_slice(finished_data);
+ let flatbuffer = $maybe_prefixed::<Location, _>::new(array_data).unwrap();
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ flatbuffer.revalidate().unwrap();
+ }
+ }
+
+ #[test]
+ fn test_collapse() {
+ let mut fbb = FlatBufferBuilder::new();
+ let xyz = fbb.create_string("xyz");
+ let mut location = LocationBuilder::new(&mut fbb);
+ location.add_name(xyz);
+ location.add_frequency(971);
+ let location = location.finish();
+ let flatbuffer = $maybe_prefixed::finish_minimal_and_collapse(fbb, location);
+
+ assert_eq!(Some("xyz"), flatbuffer.message().name());
+ assert_eq!(971, flatbuffer.message().frequency());
+ flatbuffer.revalidate().unwrap();
+ }
+
+ #[test]
+ fn test_verification_failed() {
+ // Something arbitrary that is not a valid flatbuffer. The test will fail if this does end
+ // up being valid.
+ let data = [5u8; 1];
+ assert!($maybe_prefixed::<Location, _>::new(data).is_err());
+ }
+
+ mod bad {
+ use super::*;
+
+ use std::cell::Cell;
+
+ struct Bad {
+ a: Vec<u8>,
+ b: [u8; 1],
+ first: Cell<bool>,
+ }
+
+ impl AsRef<[u8]> for Bad {
+ fn as_ref(&self) -> &[u8] {
+ if self.first.replace(false) {
+ self.a.as_ref()
+ } else {
+ self.b.as_ref()
+ }
+ }
+ }
+
+ unsafe impl FlatbufferStorage for Bad {}
+
+ /// A demonstration of how using an incorrect `FlatbufferStorage` implementation can go
+ /// wrong. Note that this test is careful to not actually do anything unsound by never
+ /// using the invalid flatbuffer, it only verifies validation fails on it.
+ #[test]
+ fn test_bad() {
+ let fbb = {
+ let mut fbb = FlatBufferBuilder::new();
+ let mut location = LocationBuilder::new(&mut fbb);
+ location.add_frequency(971);
+ let location = location.finish();
+ fbb.$finish(location, None);
+ fbb
+ };
+
+ let data = Bad {
+ a: fbb.finished_data().iter().copied().collect(),
+ // Something arbitrary that is not a valid flatbuffer. The test will fail if this
+ // does end up being valid.
+ b: [5; 1],
+ first: Cell::new(true),
+ };
+
+ let flatbuffer = $maybe_prefixed::<Location, _>::new(data).unwrap();
+ assert!(flatbuffer.revalidate().is_err());
+ }
+ }
+ }
+ };
+ }
+
+ test_variant!(NonSizePrefixedFlatbuffer, non_prefixed, finish);
+ test_variant!(SizePrefixedFlatbuffer, prefixed, finish_size_prefixed);
+}