blob: aec61d8691003a27d75eec13c60e69983450ed02 [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
Brian Silverman67cdbd62022-08-15 06:03:55 -0700344/// Transmutes the in-memory flatbuffer data to a type. This is only useful if `T` is a C++-style
345/// flatbuffer generated class, it doesn't match the layout of any type in any language.
346///
347/// It is strongly recommended to explicitly specify `T` using the turbofish operator when using
348/// this function. Mismatches of the type are very bad and leaving it implicit is error prone.
349///
350/// # Safety
351///
352/// This is extremely unsafe. `T` has to be something expecting a flatbuffer table of the correct
353/// type as its address.
354pub unsafe fn transmute_table_to<'a, T>(table: &flatbuffers::Table<'a>) -> &'a T {
355 let address = &table.buf[table.loc];
356 std::mem::transmute(address)
357}
358
Brian Silvermand1805b62022-07-20 20:47:05 -0700359#[cfg(test)]
360mod tests {
361 use super::*;
362
363 use aos_json_to_flatbuffer_fbs::aos::testing::{Location, LocationBuilder};
364
365 /// Tests various methods that take flatbuffers as arguments.
366 #[test]
367 fn test_take() {
368 fn take_box_dyn_ref(flatbuffer: &DynFlatbuffer<'_, Location<'static>>) {
369 assert_eq!(Some("xyz"), flatbuffer.message().name());
370 assert_eq!(971, flatbuffer.message().frequency());
371 }
372
373 fn take_box_dyn_ref_static(flatbuffer: &DynFlatbuffer<'static, Location<'static>>) {
374 assert_eq!(Some("xyz"), flatbuffer.message().name());
375 assert_eq!(971, flatbuffer.message().frequency());
376 }
377
378 fn take_bare_dyn_ref(flatbuffer: &dyn Flatbuffer<Location<'static>>) {
379 assert_eq!(Some("xyz"), flatbuffer.message().name());
380 assert_eq!(971, flatbuffer.message().frequency());
381 }
382
383 fn take_impl_ref(flatbuffer: &impl Flatbuffer<Location<'static>>) {
384 assert_eq!(Some("xyz"), flatbuffer.message().name());
385 assert_eq!(971, flatbuffer.message().frequency());
386 }
387
388 fn take_impl_ref_static(flatbuffer: &(impl Flatbuffer<Location<'static>> + 'static)) {
389 assert_eq!(Some("xyz"), flatbuffer.message().name());
390 assert_eq!(971, flatbuffer.message().frequency());
391 }
392
393 fn take_box_dyn(flatbuffer: DynFlatbuffer<'_, Location<'static>>) {
394 assert_eq!(Some("xyz"), flatbuffer.message().name());
395 assert_eq!(971, flatbuffer.message().frequency());
396 }
397
398 fn take_box_dyn_static(flatbuffer: DynFlatbuffer<'static, Location<'static>>) {
399 assert_eq!(Some("xyz"), flatbuffer.message().name());
400 assert_eq!(971, flatbuffer.message().frequency());
401 }
402
403 fn take_impl(flatbuffer: impl Flatbuffer<Location<'static>>) {
404 assert_eq!(Some("xyz"), flatbuffer.message().name());
405 assert_eq!(971, flatbuffer.message().frequency());
406 }
407
408 fn take_impl_static(flatbuffer: impl Flatbuffer<Location<'static>> + 'static) {
409 assert_eq!(Some("xyz"), flatbuffer.message().name());
410 assert_eq!(971, flatbuffer.message().frequency());
411 }
412
413 fn take_nondyn_ref(
414 flatbuffer: &NonSizePrefixedFlatbuffer<Location<'static>, CollapsedFlatBufferBuilder>,
415 ) {
416 assert_eq!(Some("xyz"), flatbuffer.message().name());
417 assert_eq!(971, flatbuffer.message().frequency());
418 }
419
420 fn take_nondyn(
421 flatbuffer: NonSizePrefixedFlatbuffer<Location<'static>, CollapsedFlatBufferBuilder>,
422 ) {
423 assert_eq!(Some("xyz"), flatbuffer.message().name());
424 assert_eq!(971, flatbuffer.message().frequency());
425 }
426
427 let flatbuffer = {
428 let mut fbb = FlatBufferBuilder::new();
429 let xyz = fbb.create_string("xyz");
430 let mut location = LocationBuilder::new(&mut fbb);
431 location.add_name(xyz);
432 location.add_frequency(971);
433 let location = location.finish();
434 NonSizePrefixedFlatbuffer::finish_minimal_and_collapse(fbb, location)
435 };
436 let make_dyn_flatbuffer = || Box::new(flatbuffer.clone());
437 let dyn_flatbuffer: DynFlatbuffer<_> = make_dyn_flatbuffer();
438
439 take_box_dyn_ref(&dyn_flatbuffer);
440 take_box_dyn_ref_static(&dyn_flatbuffer);
441 take_bare_dyn_ref(&dyn_flatbuffer);
442 take_bare_dyn_ref(&flatbuffer);
443 take_impl_ref(&dyn_flatbuffer);
444 take_impl_ref(&flatbuffer);
445 take_impl_ref_static(&dyn_flatbuffer);
446 take_impl_ref_static(&flatbuffer);
447 take_box_dyn(make_dyn_flatbuffer());
448 take_box_dyn_static(make_dyn_flatbuffer());
449 take_impl(make_dyn_flatbuffer());
450 take_impl(flatbuffer.clone());
451 take_impl_static(make_dyn_flatbuffer());
452 take_impl_static(flatbuffer.clone());
453 take_nondyn_ref(&flatbuffer);
454 take_nondyn(flatbuffer.clone());
455 }
456
457 /// Tests creating a DynFlatbuffer from various types.
458 #[test]
459 fn test_make_dyn() {
460 fn take_ref(flatbuffer: &impl Flatbuffer<Location<'static>>) {
461 assert_eq!(Some("xyz"), flatbuffer.message().name());
462 assert_eq!(971, flatbuffer.message().frequency());
463 }
464
465 let flatbuffer = {
466 let mut fbb = FlatBufferBuilder::new();
467 let xyz = fbb.create_string("xyz");
468 let mut location = LocationBuilder::new(&mut fbb);
469 location.add_name(xyz);
470 location.add_frequency(971);
471 let location = location.finish();
472 NonSizePrefixedFlatbuffer::finish_minimal_and_collapse(fbb, location)
473 };
474 take_ref(&flatbuffer);
475 let slice_flatbuffer =
476 NonSizePrefixedFlatbuffer::<Location<'static>, &[u8]>::new(flatbuffer.storage_ref())
477 .unwrap();
478 take_ref(&slice_flatbuffer);
479
480 {
481 let dyn_flatbuffer: DynFlatbuffer<_> = Box::new(flatbuffer.clone());
482 take_ref(&dyn_flatbuffer);
483 }
484 {
485 let dyn_flatbuffer: DynFlatbuffer<_> = Box::new(slice_flatbuffer.clone());
486 take_ref(&dyn_flatbuffer);
487 }
488 }
489
490 macro_rules! test_variant {
491 ($maybe_prefixed:ident, $modname:ident, $finish:ident) => {
492 mod $modname {
493 use super::*;
494
495 use aos_json_to_flatbuffer_fbs::aos::testing::{Location, LocationBuilder};
496
497 #[test]
498 fn test_basic() {
499 let fbb = {
500 let mut fbb = FlatBufferBuilder::new();
501 let xyz = fbb.create_string("xyz");
502 let mut location = LocationBuilder::new(&mut fbb);
503 location.add_name(xyz);
504 location.add_frequency(971);
505 let location = location.finish();
506 fbb.$finish(location, None);
507 fbb
508 };
509
510 {
511 let flatbuffer = $maybe_prefixed::<Location, _>::new(
512 fbb.finished_data().iter().copied().collect::<Vec<u8>>(),
513 )
514 .unwrap();
515 assert_eq!(Some("xyz"), flatbuffer.message().name());
516 assert_eq!(971, flatbuffer.message().frequency());
517 flatbuffer.revalidate().unwrap();
518 }
519
520 {
521 let flatbuffer =
522 $maybe_prefixed::<Location, _>::new(fbb.finished_data()).unwrap();
523 assert_eq!(Some("xyz"), flatbuffer.message().name());
524 assert_eq!(971, flatbuffer.message().frequency());
525 flatbuffer.revalidate().unwrap();
526 }
527
528 {
529 let mut array_data = [0; 64];
530 let finished_data = fbb.finished_data();
531 array_data[..finished_data.len()].copy_from_slice(finished_data);
532 let flatbuffer = $maybe_prefixed::<Location, _>::new(array_data).unwrap();
533 assert_eq!(Some("xyz"), flatbuffer.message().name());
534 assert_eq!(971, flatbuffer.message().frequency());
535 flatbuffer.revalidate().unwrap();
536 }
537 }
538
539 #[test]
540 fn test_collapse() {
541 let mut fbb = FlatBufferBuilder::new();
542 let xyz = fbb.create_string("xyz");
543 let mut location = LocationBuilder::new(&mut fbb);
544 location.add_name(xyz);
545 location.add_frequency(971);
546 let location = location.finish();
547 let flatbuffer = $maybe_prefixed::finish_minimal_and_collapse(fbb, location);
548
549 assert_eq!(Some("xyz"), flatbuffer.message().name());
550 assert_eq!(971, flatbuffer.message().frequency());
551 flatbuffer.revalidate().unwrap();
552 }
553
554 #[test]
555 fn test_verification_failed() {
556 // Something arbitrary that is not a valid flatbuffer. The test will fail if this does end
557 // up being valid.
558 let data = [5u8; 1];
559 assert!($maybe_prefixed::<Location, _>::new(data).is_err());
560 }
561
562 mod bad {
563 use super::*;
564
565 use std::cell::Cell;
566
567 struct Bad {
568 a: Vec<u8>,
569 b: [u8; 1],
570 first: Cell<bool>,
571 }
572
573 impl AsRef<[u8]> for Bad {
574 fn as_ref(&self) -> &[u8] {
575 if self.first.replace(false) {
576 self.a.as_ref()
577 } else {
578 self.b.as_ref()
579 }
580 }
581 }
582
583 unsafe impl FlatbufferStorage for Bad {}
584
585 /// A demonstration of how using an incorrect `FlatbufferStorage` implementation can go
586 /// wrong. Note that this test is careful to not actually do anything unsound by never
587 /// using the invalid flatbuffer, it only verifies validation fails on it.
588 #[test]
589 fn test_bad() {
590 let fbb = {
591 let mut fbb = FlatBufferBuilder::new();
592 let mut location = LocationBuilder::new(&mut fbb);
593 location.add_frequency(971);
594 let location = location.finish();
595 fbb.$finish(location, None);
596 fbb
597 };
598
599 let data = Bad {
600 a: fbb.finished_data().iter().copied().collect(),
601 // Something arbitrary that is not a valid flatbuffer. The test will fail if this
602 // does end up being valid.
603 b: [5; 1],
604 first: Cell::new(true),
605 };
606
607 let flatbuffer = $maybe_prefixed::<Location, _>::new(data).unwrap();
608 assert!(flatbuffer.revalidate().is_err());
609 }
610 }
611 }
612 };
613 }
614
615 test_variant!(NonSizePrefixedFlatbuffer, non_prefixed, finish);
616 test_variant!(SizePrefixedFlatbuffer, prefixed, finish_size_prefixed);
617}