blob: 073d2065e2cbeccc68ee44c1867d415c948fed7b [file] [log] [blame]
Brian Silvermand1805b62022-07-20 20:47:05 -07001//! Utilities for working with FlatBuffers types.
2//!
3//! These address similar use cases as the C++ library with the same name, but unlike most of these
4//! wrappers the APIs look very different in most cases to be ergnomic in Rust.
5
6use std::marker::PhantomData;
7
8use flatbuffers::{
9 FlatBufferBuilder, Follow, FollowWith, ForwardsUOffset, InvalidFlatbuffer, SkipSizePrefix,
10 Verifiable, Verifier, WIPOffset,
11};
12
13/// Represents the various forms of buffers we can store tables in.
14///
15/// We need this trait to mark types which won't change the data in a flatbuffer after we verify
16/// it. Rust's type system cannot express this constraint directly, so we use an unsafe trait to
17/// call attention to what implementing types are promising.
18///
19/// # Safety
20///
21/// Implementors of this trait must return a slice with the same contents every time
22/// [`AsRef.as_ref`] is called without any intervening mutable access to the object. In other words,
23/// types implementing this trait must have exclusive ownership of the underlying storage, and not
24/// do anything unusal like returning different pieces of storage each time.
25///
26/// Note that the slice is not required to have the same address each time [`AsRef.as_ref`] is
27/// called, just the same contents. This means types using inline storage can implement this trait
28/// without needing pinning.
29pub unsafe trait FlatbufferStorage: AsRef<[u8]> {}
30
31// SAFETY: Vec has exclusive ownership of its single storage.
32unsafe impl FlatbufferStorage for Vec<u8> {}
33
34// SAFETY: We have a shared reference to the slice and u8 does not have interior mutability, so
35// nobody can change the memory as long as the shared reference exists. We're only using the one
36// slice too.
37unsafe impl FlatbufferStorage for &[u8] {}
38
39// SAFETY: The array is its own storage.
40unsafe impl<const N: usize> FlatbufferStorage for [u8; N] {}
41
42/// A type to wrap buffers returned from [`FlatBufferBuilder.collapse`].
43///
44/// See [`Flatbuffer.finish_minimal_and_collapse`] for an easy way to make use of this.
45#[derive(Clone)]
46pub struct CollapsedFlatBufferBuilder(pub Vec<u8>, pub usize);
47
48impl AsRef<[u8]> for CollapsedFlatBufferBuilder {
49 fn as_ref(&self) -> &[u8] {
50 &self.0[self.1..]
51 }
52}
53
54// SAFETY: The Vec has exclusive ownership of its single storage, and we always access the same
55// part of it.
56unsafe impl FlatbufferStorage for CollapsedFlatBufferBuilder {}
57
58/// An internal sealed trait to manage the size-prefixed versions vs non-size-prefixed variants.
59pub trait PrefixedOrNot: private_size_prefixed::Sealed {
60 /// [`FlatBufferBuilder::finish`] or [`FlatBufferBuilder::finish_size_prefixed`].
61 fn finish_builder<T>(
62 fbb: &mut FlatBufferBuilder,
63 root: WIPOffset<T>,
64 file_identifier: Option<&str>,
65 );
66
67 /// [`flatbuffers::root`] or [`flatbuffers::size_prefixed_root`].
68 fn root<'buf, T: 'buf + Follow<'buf> + Verifiable>(
69 data: &'buf [u8],
70 ) -> Result<T::Inner, InvalidFlatbuffer>;
71
72 /// [`flatbuffers::root_unchecked`] or [`flatbuffers::size_prefixed_root_unchecked`].
73 unsafe fn root_unchecked<'buf, T: 'buf + Follow<'buf>>(data: &'buf [u8]) -> T::Inner;
74
75 /// [`ForwardsUOffset::run_verifier`], after skipping the size if present.
76 fn run_verifier<T: Verifiable>(
77 v: &mut Verifier<'_, '_>,
78 pos: usize,
79 ) -> Result<(), InvalidFlatbuffer>;
80}
81
82/// Marker type to implement [`PrefixedOrNot`] on.
83pub struct NotSizePrefixed;
84
85impl PrefixedOrNot for NotSizePrefixed {
86 fn finish_builder<T>(
87 fbb: &mut FlatBufferBuilder,
88 root: WIPOffset<T>,
89 file_identifier: Option<&str>,
90 ) {
91 fbb.finish::<T>(root, file_identifier);
92 }
93
94 fn root<'buf, T: 'buf + Follow<'buf> + Verifiable>(
95 data: &'buf [u8],
96 ) -> Result<T::Inner, InvalidFlatbuffer> {
97 flatbuffers::root::<'buf, T>(data)
98 }
99
100 unsafe fn root_unchecked<'buf, T: 'buf + Follow<'buf>>(data: &'buf [u8]) -> T::Inner {
101 flatbuffers::root_unchecked::<'buf, T>(data)
102 }
103
104 fn run_verifier<T: Verifiable>(
105 v: &mut Verifier<'_, '_>,
106 pos: usize,
107 ) -> Result<(), InvalidFlatbuffer> {
108 <ForwardsUOffset<T>>::run_verifier(v, pos)
109 }
110}
111
112/// Marker type to implement [`PrefixedOrNot`] on.
113pub struct SizePrefixed;
114
115impl PrefixedOrNot for SizePrefixed {
116 fn finish_builder<T>(
117 fbb: &mut FlatBufferBuilder,
118 root: WIPOffset<T>,
119 file_identifier: Option<&str>,
120 ) {
121 fbb.finish_size_prefixed::<T>(root, file_identifier);
122 }
123
124 fn root<'buf, T: 'buf + Follow<'buf> + Verifiable>(
125 data: &'buf [u8],
126 ) -> Result<T::Inner, InvalidFlatbuffer> {
127 flatbuffers::size_prefixed_root::<'buf, T>(data)
128 }
129
130 unsafe fn root_unchecked<'buf, T: 'buf + Follow<'buf>>(data: &'buf [u8]) -> T::Inner {
131 flatbuffers::size_prefixed_root_unchecked::<'buf, T>(data)
132 }
133
134 fn run_verifier<T: Verifiable>(
135 v: &mut Verifier<'_, '_>,
136 pos: usize,
137 ) -> Result<(), InvalidFlatbuffer> {
138 <SkipSizePrefix<ForwardsUOffset<T>>>::run_verifier(v, pos)
139 }
140}
141
142mod private_size_prefixed {
143 pub trait Sealed {}
144
145 impl Sealed for super::NotSizePrefixed {}
146 impl Sealed for super::SizePrefixed {}
147}
148
149/// A flatbuffer along with its backing storage.
150///
151/// `T` should be a generated flatbuffer table type. Its lifetime argument does not matter, using
152/// `'static` is recommended for simplicity. This type uses the [`FollowWith`] trait to access the
153/// equivalents of `T` with appropriate lifetimes as needed.
154///
155/// This is a bit tricky in Rust because we want to avoid internal pointers (requiring the buffer
156/// to be pinned before using it would be hard to use). Instead of actually storing the flatbuffer
157/// with its pointer to the storage, we (optionally) verify it once and then create the flatbuffer
158/// type on demand.
159pub trait Flatbuffer<T>
160where
161 for<'a> T: FollowWith<'a>,
162 for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
163{
164 /// Provides access to the message.
165 ///
166 /// Note that we can't implement `Deref` because we're not returning a reference. We're
167 /// effectively returning a fat pointer which is semantically equivalent to a Rust reference,
168 /// but it's not the same thing to the language. Also implementing `Deref` would allow wrapping
169 /// this with `Pin`, which doesn't do anything because the actual type being dereferenced is
170 /// still `Unpin`, even though other languages commonly store pointers into the flatbuffer.
171 fn message<'this>(&'this self) -> <<T as FollowWith<'this>>::Inner as Follow<'this>>::Inner;
172
173 /// Revalidates the data. If this returns `Err`, then something has violated the safety
174 /// requirements and code responsible is likely unsound.
175 ///
176 /// You should probably not be using this. It's intended for debug assertions when working with
177 /// cross-language boundaries and otherwise doing tricky things with buffers. Once again, you
178 /// probably have unsoundness if you're using this, verify your flatbuffers before wrapping them
179 /// in a [`Flatbuffer`], not after.
180 fn revalidate<'this>(&'this self) -> Result<(), InvalidFlatbuffer>
181 where
182 <T as FollowWith<'this>>::Inner: Verifiable;
183}
184
185/// A generic implementation of [`Flatbuffer`]. `Storage` is the type used to store the buffer.
186/// `MaybePrefixed` controls whether we're using size-prefixed flatbuffers or not.
187///
188/// Flatbuffers provides a parallel set of APIs for handling size-prefixed buffers. This just means
189/// the size is placed at the beginning, so the root table's offset isn't at the very beginning of
190/// the buffer.
191///
192/// [`NonSizePrefixedFlatbuffer`] and [`SizePrefixedFlatbuffer`] provide more convenient names.
193pub struct MaybePrefixedFlatbuffer<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot> {
194 // SAFETY: We have an invariant that this is always a valid flatbuffer.
195 //
196 // All of our constructors verify that, and we never hand out mutable references so the
197 // requirements for implementors of [`FlatbufferStorage`] ensure it stays valid.
198 storage: Storage,
199
200 /// Pretend we store one of these. The compiler needs this information to clarify that two
201 /// instances with different `T` and/or `MaybePrefixed` are not the same type.
202 _marker: PhantomData<(T, MaybePrefixed)>,
203}
204
205impl<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot> Flatbuffer<T>
206 for MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
207where
208 for<'a> T: FollowWith<'a>,
209 for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
210{
211 fn message<'this>(&'this self) -> <<T as FollowWith<'this>>::Inner as Follow<'this>>::Inner {
212 // SAFETY: This being a valid flatbuffer is an invariant.
213 unsafe {
214 MaybePrefixed::root_unchecked::<<T as FollowWith<'this>>::Inner>(self.storage.as_ref())
215 }
216 }
217
218 fn revalidate<'this>(&'this self) -> Result<(), InvalidFlatbuffer>
219 where
220 <T as FollowWith<'this>>::Inner: Verifiable,
221 {
222 MaybePrefixed::root::<<T as FollowWith<'this>>::Inner>(self.storage.as_ref())?;
223 Ok(())
224 }
225}
226
227impl<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot>
228 MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
229where
230 for<'a> T: FollowWith<'a>,
231 for<'a> <T as FollowWith<'a>>::Inner: Follow<'a> + Verifiable,
232{
233 /// Verifies the flatbuffer and then creates an instance.
234 ///
235 /// Note that `storage` must only refer to the flatbuffer. `FlatBufferBuilder::collapse`
236 /// returns a buffer with unused space at the beginning, for example, you will have to either
237 /// use a slice here or remove the unused space first.
238 pub fn new(storage: Storage) -> Result<Self, InvalidFlatbuffer> {
239 MaybePrefixed::root::<<T as FollowWith<'_>>::Inner>(storage.as_ref())?;
240 Ok(Self {
241 storage,
242 _marker: PhantomData,
243 })
244 }
245}
246
247impl<T, Storage: FlatbufferStorage, MaybePrefixed: PrefixedOrNot>
248 MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
249where
250 for<'a> T: FollowWith<'a>,
251 for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
252{
253 /// Wraps a flatbuffer without verifying it.
254 ///
255 /// Note that `storage` must only refer to the flatbuffer. `FlatBufferBuilder::collapse`
256 /// returns a buffer with unused space at the beginning, for example, so you can not pass that
257 /// here directly. See [`CollapsedFlatBufferBuilder`] for that particular case.
258 ///
259 /// # Safety
260 ///
261 /// `storage` must refer to a valid flatbuffer.
262 pub unsafe fn new_unchecked(storage: Storage) -> Self {
263 Self {
264 storage,
265 _marker: PhantomData,
266 }
267 }
268
269 /// Returns a reference to the underlying storage.
270 pub fn storage_ref(&self) -> &[u8] {
271 self.storage.as_ref()
272 }
273
274 /// Converts back into the underlying storage.
275 pub fn into_storage(self) -> Storage {
276 self.storage
277 }
278}
279
280impl<T, Storage: FlatbufferStorage + Clone, MaybePrefixed: PrefixedOrNot> Clone
281 for MaybePrefixedFlatbuffer<T, Storage, MaybePrefixed>
282where
283 for<'a> T: FollowWith<'a>,
284 for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
285{
286 fn clone(&self) -> Self {
287 // SAFETY: We already know it's valid because it was used by `self`.
288 unsafe { Self::new_unchecked(self.storage.clone()) }
289 }
290}
291
292impl<T, MaybePrefixed: PrefixedOrNot>
293 MaybePrefixedFlatbuffer<T, CollapsedFlatBufferBuilder, MaybePrefixed>
294where
295 for<'a> T: FollowWith<'a>,
296 for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
297{
298 /// Finishes a [`FlatBufferBuilder`] and then grabs its buffer. This does not require
299 /// verification to be safe.
300 ///
301 /// We have to encapsulate the `finish` call to enforce matching types.
302 pub fn finish_minimal_and_collapse(mut fbb: FlatBufferBuilder<'_>, root: WIPOffset<T>) -> Self {
303 MaybePrefixed::finish_builder(&mut fbb, root, None);
304 let (vec, offset) = fbb.collapse();
305 // SAFETY: We just finished the builder with an offset of the correct type. The builder
306 // maintained the invariant that it was building a valid flatbuffer, which we are directly
307 // using.
308 unsafe { Self::new_unchecked(CollapsedFlatBufferBuilder(vec, offset)) }
309 }
310}
311
312impl<T, F: Flatbuffer<T> + ?Sized> Flatbuffer<T> for Box<F>
313where
314 for<'a> T: FollowWith<'a>,
315 for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
316{
317 fn message<'this>(&'this self) -> <<T as FollowWith<'this>>::Inner as Follow<'this>>::Inner {
318 self.as_ref().message()
319 }
320
321 fn revalidate<'this>(&'this self) -> Result<(), InvalidFlatbuffer>
322 where
323 <T as FollowWith<'this>>::Inner: Verifiable,
324 {
325 self.as_ref().revalidate()
326 }
327}
328
329pub type NonSizePrefixedFlatbuffer<T, Storage> =
330 MaybePrefixedFlatbuffer<T, Storage, NotSizePrefixed>;
331pub type SizePrefixedFlatbuffer<T, Storage> = MaybePrefixedFlatbuffer<T, Storage, SizePrefixed>;
332
333/// This is the alias for a generic "some kind of buffer that contains a flatbuffer". Use this if
334/// you want your code to work with any type of buffer without being generic.
335///
336/// `'buf` is the lifetime of the underlying storage.
337///
338/// If you only want to support flatbuffers that own their underlying buffer, and don't want to
339/// even make your functions lifetime-generic, use `DynFlatbuffer<'static, T>`. However, note
340/// that being lifetime-generic has no cost in code size or vtable lookups, and if your code isn't
341/// storing references to the flatbuffer anywhere it's a strict increase in flexibility.
342pub type DynFlatbuffer<'buf, T> = Box<dyn Flatbuffer<T> + 'buf>;
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 use aos_json_to_flatbuffer_fbs::aos::testing::{Location, LocationBuilder};
349
350 /// Tests various methods that take flatbuffers as arguments.
351 #[test]
352 fn test_take() {
353 fn take_box_dyn_ref(flatbuffer: &DynFlatbuffer<'_, Location<'static>>) {
354 assert_eq!(Some("xyz"), flatbuffer.message().name());
355 assert_eq!(971, flatbuffer.message().frequency());
356 }
357
358 fn take_box_dyn_ref_static(flatbuffer: &DynFlatbuffer<'static, Location<'static>>) {
359 assert_eq!(Some("xyz"), flatbuffer.message().name());
360 assert_eq!(971, flatbuffer.message().frequency());
361 }
362
363 fn take_bare_dyn_ref(flatbuffer: &dyn Flatbuffer<Location<'static>>) {
364 assert_eq!(Some("xyz"), flatbuffer.message().name());
365 assert_eq!(971, flatbuffer.message().frequency());
366 }
367
368 fn take_impl_ref(flatbuffer: &impl Flatbuffer<Location<'static>>) {
369 assert_eq!(Some("xyz"), flatbuffer.message().name());
370 assert_eq!(971, flatbuffer.message().frequency());
371 }
372
373 fn take_impl_ref_static(flatbuffer: &(impl Flatbuffer<Location<'static>> + 'static)) {
374 assert_eq!(Some("xyz"), flatbuffer.message().name());
375 assert_eq!(971, flatbuffer.message().frequency());
376 }
377
378 fn take_box_dyn(flatbuffer: DynFlatbuffer<'_, Location<'static>>) {
379 assert_eq!(Some("xyz"), flatbuffer.message().name());
380 assert_eq!(971, flatbuffer.message().frequency());
381 }
382
383 fn take_box_dyn_static(flatbuffer: DynFlatbuffer<'static, Location<'static>>) {
384 assert_eq!(Some("xyz"), flatbuffer.message().name());
385 assert_eq!(971, flatbuffer.message().frequency());
386 }
387
388 fn take_impl(flatbuffer: impl Flatbuffer<Location<'static>>) {
389 assert_eq!(Some("xyz"), flatbuffer.message().name());
390 assert_eq!(971, flatbuffer.message().frequency());
391 }
392
393 fn take_impl_static(flatbuffer: impl Flatbuffer<Location<'static>> + 'static) {
394 assert_eq!(Some("xyz"), flatbuffer.message().name());
395 assert_eq!(971, flatbuffer.message().frequency());
396 }
397
398 fn take_nondyn_ref(
399 flatbuffer: &NonSizePrefixedFlatbuffer<Location<'static>, CollapsedFlatBufferBuilder>,
400 ) {
401 assert_eq!(Some("xyz"), flatbuffer.message().name());
402 assert_eq!(971, flatbuffer.message().frequency());
403 }
404
405 fn take_nondyn(
406 flatbuffer: NonSizePrefixedFlatbuffer<Location<'static>, CollapsedFlatBufferBuilder>,
407 ) {
408 assert_eq!(Some("xyz"), flatbuffer.message().name());
409 assert_eq!(971, flatbuffer.message().frequency());
410 }
411
412 let flatbuffer = {
413 let mut fbb = FlatBufferBuilder::new();
414 let xyz = fbb.create_string("xyz");
415 let mut location = LocationBuilder::new(&mut fbb);
416 location.add_name(xyz);
417 location.add_frequency(971);
418 let location = location.finish();
419 NonSizePrefixedFlatbuffer::finish_minimal_and_collapse(fbb, location)
420 };
421 let make_dyn_flatbuffer = || Box::new(flatbuffer.clone());
422 let dyn_flatbuffer: DynFlatbuffer<_> = make_dyn_flatbuffer();
423
424 take_box_dyn_ref(&dyn_flatbuffer);
425 take_box_dyn_ref_static(&dyn_flatbuffer);
426 take_bare_dyn_ref(&dyn_flatbuffer);
427 take_bare_dyn_ref(&flatbuffer);
428 take_impl_ref(&dyn_flatbuffer);
429 take_impl_ref(&flatbuffer);
430 take_impl_ref_static(&dyn_flatbuffer);
431 take_impl_ref_static(&flatbuffer);
432 take_box_dyn(make_dyn_flatbuffer());
433 take_box_dyn_static(make_dyn_flatbuffer());
434 take_impl(make_dyn_flatbuffer());
435 take_impl(flatbuffer.clone());
436 take_impl_static(make_dyn_flatbuffer());
437 take_impl_static(flatbuffer.clone());
438 take_nondyn_ref(&flatbuffer);
439 take_nondyn(flatbuffer.clone());
440 }
441
442 /// Tests creating a DynFlatbuffer from various types.
443 #[test]
444 fn test_make_dyn() {
445 fn take_ref(flatbuffer: &impl Flatbuffer<Location<'static>>) {
446 assert_eq!(Some("xyz"), flatbuffer.message().name());
447 assert_eq!(971, flatbuffer.message().frequency());
448 }
449
450 let flatbuffer = {
451 let mut fbb = FlatBufferBuilder::new();
452 let xyz = fbb.create_string("xyz");
453 let mut location = LocationBuilder::new(&mut fbb);
454 location.add_name(xyz);
455 location.add_frequency(971);
456 let location = location.finish();
457 NonSizePrefixedFlatbuffer::finish_minimal_and_collapse(fbb, location)
458 };
459 take_ref(&flatbuffer);
460 let slice_flatbuffer =
461 NonSizePrefixedFlatbuffer::<Location<'static>, &[u8]>::new(flatbuffer.storage_ref())
462 .unwrap();
463 take_ref(&slice_flatbuffer);
464
465 {
466 let dyn_flatbuffer: DynFlatbuffer<_> = Box::new(flatbuffer.clone());
467 take_ref(&dyn_flatbuffer);
468 }
469 {
470 let dyn_flatbuffer: DynFlatbuffer<_> = Box::new(slice_flatbuffer.clone());
471 take_ref(&dyn_flatbuffer);
472 }
473 }
474
475 macro_rules! test_variant {
476 ($maybe_prefixed:ident, $modname:ident, $finish:ident) => {
477 mod $modname {
478 use super::*;
479
480 use aos_json_to_flatbuffer_fbs::aos::testing::{Location, LocationBuilder};
481
482 #[test]
483 fn test_basic() {
484 let fbb = {
485 let mut fbb = FlatBufferBuilder::new();
486 let xyz = fbb.create_string("xyz");
487 let mut location = LocationBuilder::new(&mut fbb);
488 location.add_name(xyz);
489 location.add_frequency(971);
490 let location = location.finish();
491 fbb.$finish(location, None);
492 fbb
493 };
494
495 {
496 let flatbuffer = $maybe_prefixed::<Location, _>::new(
497 fbb.finished_data().iter().copied().collect::<Vec<u8>>(),
498 )
499 .unwrap();
500 assert_eq!(Some("xyz"), flatbuffer.message().name());
501 assert_eq!(971, flatbuffer.message().frequency());
502 flatbuffer.revalidate().unwrap();
503 }
504
505 {
506 let flatbuffer =
507 $maybe_prefixed::<Location, _>::new(fbb.finished_data()).unwrap();
508 assert_eq!(Some("xyz"), flatbuffer.message().name());
509 assert_eq!(971, flatbuffer.message().frequency());
510 flatbuffer.revalidate().unwrap();
511 }
512
513 {
514 let mut array_data = [0; 64];
515 let finished_data = fbb.finished_data();
516 array_data[..finished_data.len()].copy_from_slice(finished_data);
517 let flatbuffer = $maybe_prefixed::<Location, _>::new(array_data).unwrap();
518 assert_eq!(Some("xyz"), flatbuffer.message().name());
519 assert_eq!(971, flatbuffer.message().frequency());
520 flatbuffer.revalidate().unwrap();
521 }
522 }
523
524 #[test]
525 fn test_collapse() {
526 let mut fbb = FlatBufferBuilder::new();
527 let xyz = fbb.create_string("xyz");
528 let mut location = LocationBuilder::new(&mut fbb);
529 location.add_name(xyz);
530 location.add_frequency(971);
531 let location = location.finish();
532 let flatbuffer = $maybe_prefixed::finish_minimal_and_collapse(fbb, location);
533
534 assert_eq!(Some("xyz"), flatbuffer.message().name());
535 assert_eq!(971, flatbuffer.message().frequency());
536 flatbuffer.revalidate().unwrap();
537 }
538
539 #[test]
540 fn test_verification_failed() {
541 // Something arbitrary that is not a valid flatbuffer. The test will fail if this does end
542 // up being valid.
543 let data = [5u8; 1];
544 assert!($maybe_prefixed::<Location, _>::new(data).is_err());
545 }
546
547 mod bad {
548 use super::*;
549
550 use std::cell::Cell;
551
552 struct Bad {
553 a: Vec<u8>,
554 b: [u8; 1],
555 first: Cell<bool>,
556 }
557
558 impl AsRef<[u8]> for Bad {
559 fn as_ref(&self) -> &[u8] {
560 if self.first.replace(false) {
561 self.a.as_ref()
562 } else {
563 self.b.as_ref()
564 }
565 }
566 }
567
568 unsafe impl FlatbufferStorage for Bad {}
569
570 /// A demonstration of how using an incorrect `FlatbufferStorage` implementation can go
571 /// wrong. Note that this test is careful to not actually do anything unsound by never
572 /// using the invalid flatbuffer, it only verifies validation fails on it.
573 #[test]
574 fn test_bad() {
575 let fbb = {
576 let mut fbb = FlatBufferBuilder::new();
577 let mut location = LocationBuilder::new(&mut fbb);
578 location.add_frequency(971);
579 let location = location.finish();
580 fbb.$finish(location, None);
581 fbb
582 };
583
584 let data = Bad {
585 a: fbb.finished_data().iter().copied().collect(),
586 // Something arbitrary that is not a valid flatbuffer. The test will fail if this
587 // does end up being valid.
588 b: [5; 1],
589 first: Cell::new(true),
590 };
591
592 let flatbuffer = $maybe_prefixed::<Location, _>::new(data).unwrap();
593 assert!(flatbuffer.revalidate().is_err());
594 }
595 }
596 }
597 };
598 }
599
600 test_variant!(NonSizePrefixedFlatbuffer, non_prefixed, finish);
601 test_variant!(SizePrefixedFlatbuffer, prefixed, finish_size_prefixed);
602}