diff options
21 files changed, 1857 insertions, 301 deletions
diff --git a/rust/flatbuffers/Cargo.toml b/rust/flatbuffers/Cargo.toml index 460c5522..ad95e673 100644 --- a/rust/flatbuffers/Cargo.toml +++ b/rust/flatbuffers/Cargo.toml @@ -13,3 +13,4 @@ categories = ["encoding", "data-structures", "memory-management"] [dependencies] smallvec = "1.0" bitflags = "1.2" +thiserror = "1.0" diff --git a/rust/flatbuffers/src/builder.rs b/rust/flatbuffers/src/builder.rs index a3c15f26..f3bfcf2d 100644 --- a/rust/flatbuffers/src/builder.rs +++ b/rust/flatbuffers/src/builder.rs @@ -435,7 +435,7 @@ impl<'fbb> FlatBufferBuilder<'fbb> { // Write the vtable offset, which is the start of any Table. // We fill its value later. let object_revloc_to_vtable: WIPOffset<VTableWIPOffset> = - WIPOffset::new(self.push::<UOffsetT>(0xF0F0_F0F0 as UOffsetT).value()); + WIPOffset::new(self.push::<UOffsetT>(0xF0F0_F0F0).value()); // Layout of the data this function will create when a new vtable is // needed. diff --git a/rust/flatbuffers/src/follow.rs b/rust/flatbuffers/src/follow.rs index 8dd70da0..a09003d7 100644 --- a/rust/flatbuffers/src/follow.rs +++ b/rust/flatbuffers/src/follow.rs @@ -27,16 +27,9 @@ use std::marker::PhantomData; /// Writing a new Follow implementation primarily involves deciding whether /// you want to return data (of the type Self::Inner) or do you want to /// continue traversing the FlatBuffer. -pub trait Follow<'a> { +pub trait Follow<'buf> { type Inner; - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner; -} - -/// Execute a follow as a top-level function. -#[allow(dead_code)] -#[inline] -pub fn lifted_follow<'a, T: Follow<'a>>(buf: &'a [u8], loc: usize) -> T::Inner { - T::follow(buf, loc) + fn follow(buf: &'buf [u8], loc: usize) -> Self::Inner; } /// FollowStart wraps a Follow impl in a struct type. This can make certain diff --git a/rust/flatbuffers/src/get_root.rs b/rust/flatbuffers/src/get_root.rs new file mode 100644 index 00000000..2a01cf87 --- /dev/null +++ b/rust/flatbuffers/src/get_root.rs @@ -0,0 +1,107 @@ +/* + * Copyright 2020 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{ + Follow, ForwardsUOffset, InvalidFlatbuffer, SkipSizePrefix, Verifiable, Verifier, + VerifierOptions, +}; + +/// Gets the root of the Flatbuffer, verifying it first with default options. +/// Note that verification is an experimental feature and may not be maximally performant or +/// catch every error (though that is the goal). See the `_unchecked` variants for previous +/// behavior. +pub fn root<'buf, T>(data: &'buf [u8]) -> Result<T::Inner, InvalidFlatbuffer> +where + T: 'buf + Follow<'buf> + Verifiable, +{ + let opts = VerifierOptions::default(); + root_with_opts::<T>(&opts, data) +} + +#[inline] +/// Gets the root of the Flatbuffer, verifying it first with given options. +/// Note that verification is an experimental feature and may not be maximally performant or +/// catch every error (though that is the goal). See the `_unchecked` variants for previous +/// behavior. +pub fn root_with_opts<'opts, 'buf, T>( + opts: &'opts VerifierOptions, + data: &'buf [u8], +) -> Result<T::Inner, InvalidFlatbuffer> +where + T: 'buf + Follow<'buf> + Verifiable, +{ + let mut v = Verifier::new(&opts, data); + <ForwardsUOffset<T>>::run_verifier(&mut v, 0)?; + Ok(unsafe { root_unchecked::<T>(data) }) +} + +#[inline] +/// Gets the root of a size prefixed Flatbuffer, verifying it first with default options. +/// Note that verification is an experimental feature and may not be maximally performant or +/// catch every error (though that is the goal). See the `_unchecked` variants for previous +/// behavior. +pub fn size_prefixed_root<'buf, T>(data: &'buf [u8]) -> Result<T::Inner, InvalidFlatbuffer> +where + T: 'buf + Follow<'buf> + Verifiable, +{ + let opts = VerifierOptions::default(); + size_prefixed_root_with_opts::<T>(&opts, data) +} + +#[inline] +/// Gets the root of a size prefixed Flatbuffer, verifying it first with given options. +/// Note that verification is an experimental feature and may not be maximally performant or +/// catch every error (though that is the goal). See the `_unchecked` variants for previous +/// behavior. +pub fn size_prefixed_root_with_opts<'opts, 'buf, T>( + opts: &'opts VerifierOptions, + data: &'buf [u8], +) -> Result<T::Inner, InvalidFlatbuffer> +where + T: 'buf + Follow<'buf> + Verifiable, +{ + let mut v = Verifier::new(&opts, data); + <SkipSizePrefix<ForwardsUOffset<T>>>::run_verifier(&mut v, 0)?; + Ok(unsafe { size_prefixed_root_unchecked::<T>(data) }) +} + +#[inline] +/// Gets root for a trusted Flatbuffer. +/// # Safety +/// Flatbuffers accessors do not perform validation checks before accessing. Unlike the other +/// `root` functions, this does not validate the flatbuffer before returning the accessor. Users +/// must trust `data` contains a valid flatbuffer (e.g. b/c it was built by your software). Reading +/// unchecked buffers may cause panics or even UB. +pub unsafe fn root_unchecked<'buf, T>(data: &'buf [u8]) -> T::Inner +where + T: Follow<'buf> + 'buf, +{ + <ForwardsUOffset<T>>::follow(data, 0) +} + +#[inline] +/// Gets root for a trusted, size prefixed, Flatbuffer. +/// # Safety +/// Flatbuffers accessors do not perform validation checks before accessing. Unlike the other +/// `root` functions, this does not validate the flatbuffer before returning the accessor. Users +/// must trust `data` contains a valid flatbuffer (e.g. b/c it was built by your software). Reading +/// unchecked buffers may cause panics or even UB. +pub unsafe fn size_prefixed_root_unchecked<'buf, T>(data: &'buf [u8]) -> T::Inner +where + T: Follow<'buf> + 'buf, +{ + <SkipSizePrefix<ForwardsUOffset<T>>>::follow(data, 0) +} diff --git a/rust/flatbuffers/src/lib.rs b/rust/flatbuffers/src/lib.rs index 3abd33b4..77d51f82 100644 --- a/rust/flatbuffers/src/lib.rs +++ b/rust/flatbuffers/src/lib.rs @@ -19,26 +19,27 @@ //! A library for memory-efficient serialization of data. //! //! This crate provides runtime support for the FlatBuffers format in the Rust programming language. -//! To use this crate, first generate code with the `flatc` compiler, as described here: https://google.github.io/flatbuffers/ +//! To use this crate, first generate code with the `flatc` compiler, as described here: <https://google.github.io/flatbuffers/> //! Then, include that code into your project. //! Finally, add this crate to your `Cargo.toml`. //! //! At this time, Rust support is experimental, and APIs may change between minor versions. //! -//! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: https://github.com/google/flatbuffers +//! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: <https://github.com/google/flatbuffers> //! (On OSX, you can install FlatBuffers from `HEAD` with the Homebrew package manager.) mod builder; mod endian_scalar; mod follow; +mod get_root; mod primitives; mod push; mod table; mod vector; +mod verifier; mod vtable; mod vtable_writer; -pub use bitflags; pub use crate::builder::FlatBufferBuilder; pub use crate::endian_scalar::{ byte_swap_f32, byte_swap_f64, emplace_scalar, read_scalar, read_scalar_at, EndianScalar, @@ -46,9 +47,15 @@ pub use crate::endian_scalar::{ pub use crate::follow::{Follow, FollowStart}; pub use crate::primitives::*; pub use crate::push::Push; -pub use crate::table::{buffer_has_identifier, get_root, get_size_prefixed_root, Table}; +pub use crate::table::{buffer_has_identifier, Table}; pub use crate::vector::{follow_cast_ref, SafeSliceAccess, Vector, VectorIter}; +pub use crate::verifier::{ + ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, Verifiable, Verifier, + VerifierOptions, +}; pub use crate::vtable::field_index_to_field_offset; +pub use bitflags; +pub use get_root::*; // TODO(rw): Unify `create_vector` and `create_vector_direct` by using // `Into<Vector<...>>`. diff --git a/rust/flatbuffers/src/table.rs b/rust/flatbuffers/src/table.rs index 46728cda..cfb85590 100644 --- a/rust/flatbuffers/src/table.rs +++ b/rust/flatbuffers/src/table.rs @@ -56,14 +56,6 @@ impl<'a> Follow<'a> for Table<'a> { } #[inline] -pub fn get_root<'a, T: Follow<'a> + 'a>(data: &'a [u8]) -> T::Inner { - <ForwardsUOffset<T>>::follow(data, 0) -} -#[inline] -pub fn get_size_prefixed_root<'a, T: Follow<'a> + 'a>(data: &'a [u8]) -> T::Inner { - <SkipSizePrefix<ForwardsUOffset<T>>>::follow(data, 0) -} -#[inline] pub fn buffer_has_identifier(data: &[u8], ident: &str, size_prefixed: bool) -> bool { assert_eq!(ident.len(), FILE_IDENTIFIER_LENGTH); diff --git a/rust/flatbuffers/src/vector.rs b/rust/flatbuffers/src/vector.rs index 5236ea16..c53a878c 100644 --- a/rust/flatbuffers/src/vector.rs +++ b/rust/flatbuffers/src/vector.rs @@ -14,12 +14,12 @@ * limitations under the License. */ +use std::fmt::{Debug, Formatter, Result}; use std::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator}; use std::marker::PhantomData; use std::mem::size_of; use std::slice::from_raw_parts; use std::str::from_utf8_unchecked; -use std::fmt::{Debug, Result, Formatter}; use crate::endian_scalar::read_scalar_at; #[cfg(target_endian = "little")] @@ -32,14 +32,13 @@ pub struct Vector<'a, T: 'a>(&'a [u8], usize, PhantomData<T>); impl<'a, T> Debug for Vector<'a, T> where T: 'a + Follow<'a>, - <T as Follow<'a>>::Inner : Debug + <T as Follow<'a>>::Inner: Debug, { fn fmt(&self, f: &mut Formatter) -> Result { f.debug_list().entries(self.iter()).finish() } } - // We cannot use derive for these two impls, as it would only implement Copy // and Clone for `T: Copy` and `T: Clone` respectively. However `Vector<'a, T>` // can always be copied, no matter that `T` you have. @@ -104,6 +103,8 @@ impl SafeSliceAccess for u8 {} impl SafeSliceAccess for i8 {} impl SafeSliceAccess for bool {} +// TODO(caspern): Get rid of this. Conditional compliation is unnecessary complexity. +// Vectors of primitives just don't work on big endian machines!!! #[cfg(target_endian = "little")] mod le_safe_slice_impls { impl super::SafeSliceAccess for u16 {} diff --git a/rust/flatbuffers/src/verifier.rs b/rust/flatbuffers/src/verifier.rs new file mode 100644 index 00000000..f33bf922 --- /dev/null +++ b/rust/flatbuffers/src/verifier.rs @@ -0,0 +1,559 @@ +use crate::follow::Follow; +use crate::{ForwardsUOffset, SOffsetT, SkipSizePrefix, UOffsetT, VOffsetT, Vector, SIZE_UOFFSET}; +use std::ops::Range; +use thiserror::Error; + +/// Traces the location of data errors. Not populated for Dos detecting errors. +/// Useful for MissingRequiredField and Utf8Error in particular, though +/// the other errors should not be producible by correct flatbuffers implementations. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ErrorTraceDetail { + VectorElement { + index: usize, + position: usize, + }, + TableField { + field_name: &'static str, + position: usize, + }, + UnionVariant { + variant: &'static str, + position: usize, + }, +} +#[derive(PartialEq, Eq, Default, Debug, Clone)] +pub struct ErrorTrace(Vec<ErrorTraceDetail>); +impl std::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace { + #[inline] + fn as_ref(&self) -> &[ErrorTraceDetail] { + &self.0 + } +} + +/// Describes how a flatuffer is invalid and, for data errors, roughly where. No extra tracing +/// information is given for DoS detecting errors since it will probably be a lot. +#[derive(Clone, Error, Debug, PartialEq, Eq)] +pub enum InvalidFlatbuffer { + #[error("Missing required field `{required}`.\n{error_trace}")] + MissingRequiredField { + required: &'static str, + error_trace: ErrorTrace, + }, + #[error("Union exactly one of union discriminant (`{field_type}`) and value \ + (`{field}`) are present.\n{error_trace}")] + InconsistentUnion { + field: &'static str, + field_type: &'static str, + error_trace: ErrorTrace, + }, + #[error("Utf8 error for string in {range:?}: {error}\n{error_trace}")] + Utf8Error { + #[source] + error: std::str::Utf8Error, + range: Range<usize>, + error_trace: ErrorTrace, + }, + #[error("String in range [{}, {}) is missing its null terminator.\n{error_trace}", + range.start, range.end)] + MissingNullTerminator { + range: Range<usize>, + error_trace: ErrorTrace, + }, + #[error("Type `{unaligned_type}` at position {position} is unaligned.\n{error_trace}")] + Unaligned { + position: usize, + unaligned_type: &'static str, + error_trace: ErrorTrace, + }, + #[error("Range [{}, {}) is out of bounds.\n{error_trace}", range.start, range.end)] + RangeOutOfBounds { + range: Range<usize>, + error_trace: ErrorTrace, + }, + #[error("Signed offset at position {position} has value {soffset} which points out of bounds.\ + \n{error_trace}")] + SignedOffsetOutOfBounds { + soffset: SOffsetT, + position: usize, + error_trace: ErrorTrace, + }, + // Dos detecting errors. These do not get error traces since it will probably be very large. + #[error("Too many tables.")] + TooManyTables, + #[error("Apparent size too large.")] + ApparentSizeTooLarge, + #[error("Nested table depth limit reached.")] + DepthLimitReached, +} + +impl std::fmt::Display for ErrorTrace { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use ErrorTraceDetail::*; + for e in self.0.iter() { + match e { + VectorElement { index, position } => { + writeln!( + f, + "\twhile verifying vector element {:?} at position {:?}", + index, position + )?; + } + TableField { + field_name, + position, + } => { + writeln!( + f, + "\twhile verifying table field `{}` at position {:?}", + field_name, position + )?; + } + UnionVariant { variant, position } => { + writeln!( + f, + "\t while verifying union variant `{}` at position {:?}", + variant, position + )?; + } + } + } + Ok(()) + } +} + +pub type Result<T> = std::prelude::v1::Result<T, InvalidFlatbuffer>; + +impl InvalidFlatbuffer { + fn new_range_oob<T>(start: usize, end: usize) -> Result<T> { + Err(Self::RangeOutOfBounds { + range: Range { start, end }, + error_trace: Default::default(), + }) + } + fn new_inconsistent_union<T>(field: &'static str, field_type: &'static str) -> Result<T> { + Err(Self::InconsistentUnion { + field, + field_type, + error_trace: Default::default(), + }) + } + fn new_missing_required<T>(required: &'static str) -> Result<T> { + Err(Self::MissingRequiredField { + required, + error_trace: Default::default(), + }) + } +} + +/// Records the path to the verifier detail if the error is a data error and not a DoS error. +fn append_trace<T>(mut res: Result<T>, d: ErrorTraceDetail) -> Result<T> { + if let Err(e) = res.as_mut() { + use InvalidFlatbuffer::*; + if let MissingRequiredField { error_trace, .. } + | Unaligned { error_trace, .. } + | RangeOutOfBounds { error_trace, .. } + | InconsistentUnion { error_trace, .. } + | Utf8Error { error_trace, .. } + | MissingNullTerminator { error_trace, .. } + | SignedOffsetOutOfBounds { error_trace, .. } = e + { + error_trace.0.push(d) + } + } + res +} + +/// Adds a TableField trace detail if `res` is a data error. +fn trace_field<T>(res: Result<T>, field_name: &'static str, position: usize) -> Result<T> { + append_trace( + res, + ErrorTraceDetail::TableField { + field_name, + position, + }, + ) +} +/// Adds a TableField trace detail if `res` is a data error. +fn trace_elem<T>(res: Result<T>, index: usize, position: usize) -> Result<T> { + append_trace(res, ErrorTraceDetail::VectorElement { index, position }) +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VerifierOptions { + /// Maximum depth of nested tables allowed in a valid flatbuffer. + pub max_depth: usize, + /// Maximum number of tables allowed in a valid flatbuffer. + pub max_tables: usize, + /// Maximum "apparent" size of the message if the Flatbuffer object DAG is expanded into a + /// tree. + pub max_apparent_size: usize, + /// Ignore errors where a string is missing its null terminator. + /// This is mostly a problem if the message will be sent to a client using old c-strings. + pub ignore_missing_null_terminator: bool, + // probably want an option to ignore utf8 errors since strings come from c++ + // options to error un-recognized enums and unions? possible footgun. + // Ignore nested flatbuffers, etc? +} +impl Default for VerifierOptions { + fn default() -> Self { + Self { + max_depth: 64, + max_tables: 1_000_000, + // size_ might do something different. + max_apparent_size: 1 << 31, + ignore_missing_null_terminator: false, + } + } +} + +/// Carries the verification state. Should not be reused between tables. +#[derive(Debug)] +pub struct Verifier<'opts, 'buf> { + buffer: &'buf [u8], + opts: &'opts VerifierOptions, + depth: usize, + num_tables: usize, + apparent_size: usize, +} +impl<'opts, 'buf> Verifier<'opts, 'buf> { + pub fn new(opts: &'opts VerifierOptions, buffer: &'buf [u8]) -> Self { + Self { + opts, + buffer, + depth: 0, + num_tables: 0, + apparent_size: 0, + } + } + /// Resets verifier internal state. + #[inline] + pub fn reset(&mut self) { + self.depth = 0; + self.num_tables = 0; + self.num_tables = 0; + } + /// Check that there really is a T in there. + #[inline] + fn is_aligned<T>(&self, pos: usize) -> Result<()> { + // Safe because we're not dereferencing. + let p = unsafe { self.buffer.as_ptr().add(pos) }; + if (p as usize) % std::mem::align_of::<T>() == 0 { + Ok(()) + } else { + Err(InvalidFlatbuffer::Unaligned { + unaligned_type: std::any::type_name::<T>(), + position: pos, + error_trace: Default::default(), + }) + } + } + #[inline] + fn range_in_buffer(&mut self, pos: usize, size: usize) -> Result<()> { + let end = pos.saturating_add(size); + if end > self.buffer.len() { + return InvalidFlatbuffer::new_range_oob(pos, end); + } + self.apparent_size += size; + if self.apparent_size > self.opts.max_apparent_size { + return Err(InvalidFlatbuffer::ApparentSizeTooLarge); + } + Ok(()) + } + #[inline] + pub fn in_buffer<T>(&mut self, pos: usize) -> Result<()> { + self.is_aligned::<T>(pos)?; + self.range_in_buffer(pos, std::mem::size_of::<T>()) + } + #[inline] + fn get_u16(&mut self, pos: usize) -> Result<u16> { + self.in_buffer::<u16>(pos)?; + Ok(u16::from_le_bytes([self.buffer[pos], self.buffer[pos + 1]])) + } + #[inline] + fn get_uoffset(&mut self, pos: usize) -> Result<UOffsetT> { + self.in_buffer::<u32>(pos)?; + Ok(u32::from_le_bytes([ + self.buffer[pos], + self.buffer[pos + 1], + self.buffer[pos + 2], + self.buffer[pos + 3], + ])) + } + #[inline] + fn deref_soffset(&mut self, pos: usize) -> Result<usize> { + self.in_buffer::<SOffsetT>(pos)?; + let offset = SOffsetT::from_le_bytes([ + self.buffer[pos], + self.buffer[pos + 1], + self.buffer[pos + 2], + self.buffer[pos + 3], + ]); + + // signed offsets are subtracted. + let derefed = if offset > 0 { + pos.checked_sub(offset.abs() as usize) + } else { + pos.checked_add(offset.abs() as usize) + }; + if let Some(x) = derefed { + if x < self.buffer.len() { + return Ok(x); + } + } + Err(InvalidFlatbuffer::SignedOffsetOutOfBounds { + soffset: offset, + position: pos, + error_trace: Default::default(), + }) + } + #[inline] + pub fn visit_table<'ver>( + &'ver mut self, + table_pos: usize, + ) -> Result<TableVerifier<'ver, 'opts, 'buf>> { + let vtable_pos = self.deref_soffset(table_pos)?; + let vtable_len = self.get_u16(vtable_pos)? as usize; + self.is_aligned::<VOffsetT>(vtable_pos.saturating_add(vtable_len))?; // i.e. vtable_len is even. + self.range_in_buffer(vtable_pos, vtable_len)?; + // Check bounds. + self.num_tables += 1; + if self.num_tables > self.opts.max_tables { + return Err(InvalidFlatbuffer::TooManyTables); + } + self.depth += 1; + if self.depth > self.opts.max_depth { + return Err(InvalidFlatbuffer::DepthLimitReached); + } + Ok(TableVerifier { + pos: table_pos, + vtable: vtable_pos, + vtable_len, + verifier: self, + }) + } + + /// Runs the union variant's type's verifier assuming the variant is at the given position, + /// tracing the error. + pub fn verify_union_variant<T: Verifiable>( + &mut self, + variant: &'static str, + position: usize, + ) -> Result<()> { + let res = T::run_verifier(self, position); + append_trace(res, ErrorTraceDetail::UnionVariant { variant, position }) + } +} + +// Cache table metadata in usize so we don't have to cast types or jump around so much. +// We will visit every field anyway. +pub struct TableVerifier<'ver, 'opts, 'buf> { + // Absolute position of table in buffer + pos: usize, + // Absolute position of vtable in buffer. + vtable: usize, + // Length of vtable. + vtable_len: usize, + // Verifier struct which holds the surrounding state and options. + verifier: &'ver mut Verifier<'opts, 'buf>, +} +impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> { + fn deref(&mut self, field: VOffsetT) -> Result<Option<usize>> { + let field = field as usize; + if field < self.vtable_len { + let field_offset = self.verifier.get_u16(self.vtable.saturating_add(field))?; + if field_offset > 0 { + // Field is present. + let field_pos = self.pos.saturating_add(field_offset as usize); + return Ok(Some(field_pos)); + } + } + Ok(None) + } + + #[inline] + pub fn visit_field<T: Verifiable>( + mut self, + field_name: &'static str, + field: VOffsetT, + required: bool, + ) -> Result<Self> { + if let Some(field_pos) = self.deref(field)? { + trace_field( + T::run_verifier(self.verifier, field_pos), + field_name, + field_pos, + )?; + return Ok(self); + } + if required { + InvalidFlatbuffer::new_missing_required(field_name) + } else { + Ok(self) + } + } + #[inline] + /// Union verification is complicated. The schemas passes this function the metadata of the + /// union's key (discriminant) and value fields, and a callback. The function verifies and + /// reads the key, then invokes the callback to perform data-dependent verification. + pub fn visit_union<Key, UnionVerifier>( + mut self, + key_field_name: &'static str, + key_field_voff: VOffsetT, + val_field_name: &'static str, + val_field_voff: VOffsetT, + required: bool, + verify_union: UnionVerifier, + ) -> Result<Self> + where + Key: Follow<'buf> + Verifiable, + UnionVerifier: + (std::ops::FnOnce(<Key as Follow<'buf>>::Inner, &mut Verifier, usize) -> Result<()>), + // NOTE: <Key as Follow<'buf>>::Inner == Key + { + // TODO(caspern): how to trace vtable errors? + let val_pos = self.deref(val_field_voff)?; + let key_pos = self.deref(key_field_voff)?; + match (key_pos, val_pos) { + (None, None) => { + if required { + InvalidFlatbuffer::new_missing_required(val_field_name) + } else { + Ok(self) + } + } + (Some(k), Some(v)) => { + trace_field(Key::run_verifier(self.verifier, k), key_field_name, k)?; + let discriminant = Key::follow(self.verifier.buffer, k); + trace_field( + verify_union(discriminant, self.verifier, v), + val_field_name, + v, + )?; + Ok(self) + } + _ => InvalidFlatbuffer::new_inconsistent_union(key_field_name, val_field_name), + } + } + pub fn finish(self) -> &'ver mut Verifier<'opts, 'buf> { + self.verifier.depth -= 1; + self.verifier + } +} + +// Needs to be implemented for Tables and maybe structs. +// Unions need some special treatment. +pub trait Verifiable { + /// Runs the verifier for this type, assuming its at position `pos` in the verifier's buffer. + /// Should not need to be called directly. + fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()>; +} + +// Verify the uoffset and then pass verifier to the type being pointed to. +impl<T: Verifiable> Verifiable for ForwardsUOffset<T> { + #[inline] + fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> { + let offset = v.get_uoffset(pos)? as usize; + let next_pos = offset.saturating_add(pos); + T::run_verifier(v, next_pos) + } +} + +/// Checks and returns the range containing the flatbuffers vector. +fn verify_vector_range<T>(v: &mut Verifier, pos: usize) -> Result<std::ops::Range<usize>> { + let len = v.get_uoffset(pos)? as usize; + let start = pos.saturating_add(SIZE_UOFFSET); + v.is_aligned::<T>(start)?; + let size = len.saturating_mul(std::mem::size_of::<T>()); + let end = start.saturating_add(size); + v.range_in_buffer(start, size)?; + Ok(std::ops::Range { start, end }) +} + +pub trait SimpleToVerifyInSlice {} +impl SimpleToVerifyInSlice for bool {} +impl SimpleToVerifyInSlice for i8 {} +impl SimpleToVerifyInSlice for u8 {} +impl SimpleToVerifyInSlice for i16 {} +impl SimpleToVerifyInSlice for u16 {} +impl SimpleToVerifyInSlice for i32 {} +impl SimpleToVerifyInSlice for u32 {} +impl SimpleToVerifyInSlice for f32 {} +impl SimpleToVerifyInSlice for i64 {} +impl SimpleToVerifyInSlice for u64 {} +impl SimpleToVerifyInSlice for f64 {} + +impl<T: SimpleToVerifyInSlice> Verifiable for Vector<'_, T> { + fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> { + verify_vector_range::<T>(v, pos)?; + Ok(()) + } +} + +impl<T: Verifiable> Verifiable for SkipSizePrefix<T> { + #[inline] + fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> { + T::run_verifier(v, pos.saturating_add(crate::SIZE_SIZEPREFIX)) + } +} + +impl<T: Verifiable> Verifiable for Vector<'_, ForwardsUOffset<T>> { + #[inline] + fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> { + let range = verify_vector_range::<ForwardsUOffset<T>>(v, pos)?; + let size = std::mem::size_of::<ForwardsUOffset<T>>(); + for (i, element_pos) in range.step_by(size).enumerate() { + trace_elem( + <ForwardsUOffset<T>>::run_verifier(v, element_pos), + i, + element_pos, + )?; + } + Ok(()) + } +} + +impl<'a> Verifiable for &'a str { + #[inline] + fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> { + let range = verify_vector_range::<u8>(v, pos)?; + let has_null_terminator = v.buffer.get(range.end).map(|&b| b == 0).unwrap_or(false); + let s = std::str::from_utf8(&v.buffer[range.clone()]); + if let Err(error) = s { + return Err(InvalidFlatbuffer::Utf8Error { + error, + range, + error_trace: Default::default(), + }); + } + if !v.opts.ignore_missing_null_terminator && !has_null_terminator { + return Err(InvalidFlatbuffer::MissingNullTerminator { + range, + error_trace: Default::default(), + }); + } + Ok(()) + } +} + +// Verify VectorOfTables, Unions, Arrays, Structs... +macro_rules! impl_verifiable_for { + ($T: ty) => { + impl Verifiable for $T { + #[inline] + fn run_verifier<'opts, 'buf>(v: &mut Verifier<'opts, 'buf>, pos: usize) -> Result<()> { + v.in_buffer::<$T>(pos) + } + } + }; +} +impl_verifiable_for!(bool); +impl_verifiable_for!(u8); +impl_verifiable_for!(i8); +impl_verifiable_for!(u16); +impl_verifiable_for!(i16); +impl_verifiable_for!(u32); +impl_verifiable_for!(i32); +impl_verifiable_for!(f32); +impl_verifiable_for!(u64); +impl_verifiable_for!(i64); +impl_verifiable_for!(f64); diff --git a/samples/monster_generated.rs b/samples/monster_generated.rs index e352b758..53070c0d 100644 --- a/samples/monster_generated.rs +++ b/samples/monster_generated.rs @@ -77,7 +77,8 @@ impl<'a> flatbuffers::Follow<'a> for Color { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<i8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<i8>(buf, loc); + Self(b) } } @@ -92,14 +93,27 @@ impl flatbuffers::Push for Color { impl flatbuffers::EndianScalar for Color { #[inline] fn to_little_endian(self) -> Self { - Self(i8::to_le(self.0)) + let b = i8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(i8::from_le(self.0)) + let b = i8::from_le(self.0); + Self(b) } } +impl<'a> flatbuffers::Verifiable for Color { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for Color {} #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] pub const ENUM_MIN_EQUIPMENT: u8 = 0; #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] @@ -143,11 +157,13 @@ impl std::fmt::Debug for Equipment { } } } +pub struct EquipmentUnionTableOffset {} impl<'a> flatbuffers::Follow<'a> for Equipment { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<u8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<u8>(buf, loc); + Self(b) } } @@ -162,15 +178,27 @@ impl flatbuffers::Push for Equipment { impl flatbuffers::EndianScalar for Equipment { #[inline] fn to_little_endian(self) -> Self { - Self(u8::to_le(self.0)) + let b = u8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(u8::from_le(self.0)) + let b = u8::from_le(self.0); + Self(b) } } -pub struct EquipmentUnionTableOffset {} +impl<'a> flatbuffers::Verifiable for Equipment { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for Equipment {} // struct Vec3, aligned to 4 #[repr(C, align(4))] #[derive(Clone, Copy, PartialEq)] @@ -189,6 +217,7 @@ impl std::fmt::Debug for Vec3 { } } +impl flatbuffers::SimpleToVerifyInSlice for Vec3 {} impl flatbuffers::SafeSliceAccess for Vec3 {} impl<'a> flatbuffers::Follow<'a> for Vec3 { type Inner = &'a Vec3; @@ -226,7 +255,15 @@ impl<'b> flatbuffers::Push for &'b Vec3 { } } - +impl<'a> flatbuffers::Verifiable for Vec3 { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.in_buffer::<Self>(pos) + } +} impl Vec3 { pub fn new(_x: f32, _y: f32, _z: f32) -> Self { Vec3 { @@ -324,7 +361,7 @@ impl<'a> Monster<'a> { } #[inline] pub fn weapons(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Weapon<'a>>>> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<flatbuffers::ForwardsUOffset<Weapon<'a>>>>>(Monster::VT_WEAPONS, None) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Weapon>>>>(Monster::VT_WEAPONS, None) } #[inline] pub fn equipped_type(&self) -> Equipment { @@ -336,7 +373,7 @@ impl<'a> Monster<'a> { } #[inline] pub fn path(&self) -> Option<&'a [Vec3]> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<Vec3>>>(Monster::VT_PATH, None).map(|v| v.safe_slice() ) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, Vec3>>>(Monster::VT_PATH, None).map(|v| v.safe_slice()) } #[inline] #[allow(non_snake_case)] @@ -350,6 +387,31 @@ impl<'a> Monster<'a> { } +impl flatbuffers::Verifiable for Monster<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<Vec3>(&"pos", Self::VT_POS, false)? + .visit_field::<i16>(&"mana", Self::VT_MANA, false)? + .visit_field::<i16>(&"hp", Self::VT_HP, false)? + .visit_field::<flatbuffers::ForwardsUOffset<&str>>(&"name", Self::VT_NAME, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u8>>>(&"inventory", Self::VT_INVENTORY, false)? + .visit_field::<Color>(&"color", Self::VT_COLOR, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Weapon>>>>(&"weapons", Self::VT_WEAPONS, false)? + .visit_union::<Equipment, _>(&"equipped_type", Self::VT_EQUIPPED_TYPE, &"equipped", Self::VT_EQUIPPED, false, |key, v, pos| { + match key { + Equipment::Weapon => v.verify_union_variant::<flatbuffers::ForwardsUOffset<Weapon>>("Equipment::Weapon", pos), + _ => Ok(()), + } + })? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, Vec3>>>(&"path", Self::VT_PATH, false)? + .finish(); + Ok(()) + } +} pub struct MonsterArgs<'a> { pub pos: Option<&'a Vec3>, pub mana: i16, @@ -512,6 +574,19 @@ impl<'a> Weapon<'a> { } } +impl flatbuffers::Verifiable for Weapon<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<flatbuffers::ForwardsUOffset<&str>>(&"name", Self::VT_NAME, false)? + .visit_field::<i16>(&"damage", Self::VT_DAMAGE, false)? + .finish(); + Ok(()) + } +} pub struct WeaponArgs<'a> { pub name: Option<flatbuffers::WIPOffset<&'a str>>, pub damage: i16, @@ -562,16 +637,78 @@ impl std::fmt::Debug for Weapon<'_> { } } #[inline] +#[deprecated(since="1.13", note="Deprecated in favor of `root_as...` methods.")] pub fn get_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> { - flatbuffers::get_root::<Monster<'a>>(buf) + unsafe { flatbuffers::root_unchecked::<Monster<'a>>(buf) } } #[inline] +#[deprecated(since="1.13", note="Deprecated in favor of `root_as...` methods.")] pub fn get_size_prefixed_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> { - flatbuffers::get_size_prefixed_root::<Monster<'a>>(buf) + unsafe { flatbuffers::size_prefixed_root_unchecked::<Monster<'a>>(buf) } } #[inline] +/// Verifies that a buffer of bytes contains a `Monster` +/// and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_monster_unchecked`. +pub fn root_as_monster(buf: &[u8]) -> Result<Monster, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root::<Monster>(buf) +} +#[inline] +/// Verifies that a buffer of bytes contains a size prefixed +/// `Monster` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `size_prefixed_root_as_monster_unchecked`. +pub fn size_prefixed_root_as_monster(buf: &[u8]) -> Result<Monster, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root::<Monster>(buf) +} +#[inline] +/// Verifies, with the given options, that a buffer of bytes +/// contains a `Monster` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_monster_unchecked`. +pub fn root_as_monster_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result<Monster<'b>, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root_with_opts::<Monster<'b>>(opts, buf) +} +#[inline] +/// Verifies, with the given verifier options, that a buffer of +/// bytes contains a size prefixed `Monster` and returns +/// it. Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_monster_unchecked`. +pub fn size_prefixed_root_as_monster_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result<Monster<'b>, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root_with_opts::<Monster<'b>>(opts, buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a Monster and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid `Monster`. +pub unsafe fn root_as_monster_unchecked(buf: &[u8]) -> Monster { + flatbuffers::root_unchecked::<Monster>(buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a size prefixed Monster and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid size prefixed `Monster`. +pub unsafe fn size_prefixed_root_as_monster_unchecked(buf: &[u8]) -> Monster { + flatbuffers::size_prefixed_root_unchecked::<Monster>(buf) +} +#[inline] pub fn finish_monster_buffer<'a, 'b>( fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, root: flatbuffers::WIPOffset<Monster<'a>>) { diff --git a/samples/sample_binary.rs b/samples/sample_binary.rs index 6fb987eb..649babf3 100644 --- a/samples/sample_binary.rs +++ b/samples/sample_binary.rs @@ -20,8 +20,7 @@ extern crate flatbuffers; // import the generated code #[path = "./monster_generated.rs"] mod monster_generated; -pub use monster_generated::my_game::sample::{get_root_as_monster, - Color, Equipment, +pub use monster_generated::my_game::sample::{Color, Equipment, Monster, MonsterArgs, Vec3, Weapon, WeaponArgs}; @@ -98,7 +97,7 @@ fn main() { let buf = builder.finished_data(); // Of type `&[u8]` // Get access to the root: - let monster = get_root_as_monster(buf); + let monster = flatbuffers::root::<Monster>(buf).unwrap(); // Get and test some scalar types from the FlatBuffer. let hp = monster.hp(); diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp index 3995a7f2..3c17b5cc 100644 --- a/src/idl_gen_rust.cpp +++ b/src/idl_gen_rust.cpp @@ -555,111 +555,93 @@ class RustGenerator : public BaseGenerator { code_ += "pub use self::bitflags_{{ENUM_NAME_SNAKE}}::{{ENUM_NAME}};"; code_ += ""; - // Generate Follow and Push so we can serialize and stuff. - code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {"; - code_ += " type Inner = Self;"; - code_ += " #[inline]"; - code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {"; - code_ += " let bits = flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf, loc);"; - code_ += " unsafe { Self::from_bits_unchecked(bits) }"; - code_ += " }"; - code_ += "}"; + code_.SetValue("FROM_BASE", "unsafe { Self::from_bits_unchecked(b) }"); + code_.SetValue("INTO_BASE", "self.bits()"); + } else { + // Normal, c-modelled enums. + // Deprecated associated constants; + const std::string deprecation_warning = + "#[deprecated(since = \"1.13\", note = \"Use associated constants" + " instead. This will no longer be generated in 2021.\")]"; + code_ += deprecation_warning; + code_ += "pub const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" + " = {{ENUM_MIN_BASE_VALUE}};"; + code_ += deprecation_warning; + code_ += "pub const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" + " = {{ENUM_MAX_BASE_VALUE}};"; + auto num_fields = NumToString(enum_def.size()); + code_ += deprecation_warning; + code_ += "#[allow(non_camel_case_types)]"; + code_ += "pub const ENUM_VALUES_{{ENUM_NAME_CAPS}}: [{{ENUM_NAME}}; " + + num_fields + "] = ["; + ForAllEnumValues1(enum_def, [&](const EnumVal &ev){ + code_ += " " + GetEnumValue(enum_def, ev) + ","; + }); + code_ += "];"; code_ += ""; - code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {"; - code_ += " type Output = {{ENUM_NAME}};"; - code_ += " #[inline]"; - code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {"; - code_ += " flatbuffers::emplace_scalar::<{{BASE_TYPE}}>" - "(dst, self.bits());"; - code_ += " }"; - code_ += "}"; + + GenComment(enum_def.doc_comment); + code_ += + "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]"; + code_ += "#[repr(transparent)]"; + code_ += "pub struct {{ENUM_NAME}}(pub {{BASE_TYPE}});"; + code_ += "#[allow(non_upper_case_globals)]"; + code_ += "impl {{ENUM_NAME}} {"; + ForAllEnumValues1(enum_def, [&](const EnumVal &ev){ + this->GenComment(ev.doc_comment, " "); + code_ += " pub const {{VARIANT}}: Self = Self({{VALUE}});"; + }); code_ += ""; - code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {"; - code_ += " #[inline]"; - code_ += " fn to_little_endian(self) -> Self {"; - code_ += " let bits = {{BASE_TYPE}}::to_le(self.bits());"; - code_ += " unsafe { Self::from_bits_unchecked(bits) }"; - code_ += " }"; - code_ += " #[inline]"; - code_ += " fn from_little_endian(self) -> Self {"; - code_ += " let bits = {{BASE_TYPE}}::from_le(self.bits());"; - code_ += " unsafe { Self::from_bits_unchecked(bits) }"; + // Generate Associated constants + code_ += " pub const ENUM_MIN: {{BASE_TYPE}} = {{ENUM_MIN_BASE_VALUE}};"; + code_ += " pub const ENUM_MAX: {{BASE_TYPE}} = {{ENUM_MAX_BASE_VALUE}};"; + code_ += " pub const ENUM_VALUES: &'static [Self] = &["; + ForAllEnumValues(enum_def, [&](){ + code_ += " Self::{{VARIANT}},"; + }); + code_ += " ];"; + code_ += " /// Returns the variant's name or \"\" if unknown."; + code_ += " pub fn variant_name(self) -> Option<&'static str> {"; + code_ += " match self {"; + ForAllEnumValues(enum_def, [&](){ + code_ += " Self::{{VARIANT}} => Some(\"{{VARIANT}}\"),"; + }); + code_ += " _ => None,"; + code_ += " }"; code_ += " }"; code_ += "}"; - code_ += ""; - return; - } - // Deprecated associated constants; - code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants" - " instead. This will no longer be generated in 2021.\")]"; - code_ += "pub const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" - " = {{ENUM_MIN_BASE_VALUE}};"; - code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants" - " instead. This will no longer be generated in 2021.\")]"; - code_ += "pub const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" - " = {{ENUM_MAX_BASE_VALUE}};"; - auto num_fields = NumToString(enum_def.size()); - code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants" - " instead. This will no longer be generated in 2021.\")]"; - code_ += "#[allow(non_camel_case_types)]"; - code_ += "pub const ENUM_VALUES_{{ENUM_NAME_CAPS}}: [{{ENUM_NAME}}; " + - num_fields + "] = ["; - ForAllEnumValues1(enum_def, [&](const EnumVal &ev){ - code_ += " " + GetEnumValue(enum_def, ev) + ","; - }); - code_ += "];"; - code_ += ""; + // Generate Debug. Unknown variants are printed like "<UNKNOWN 42>". + code_ += "impl std::fmt::Debug for {{ENUM_NAME}} {"; + code_ += " fn fmt(&self, f: &mut std::fmt::Formatter) ->" + " std::fmt::Result {"; + code_ += " if let Some(name) = self.variant_name() {"; + code_ += " f.write_str(name)"; + code_ += " } else {"; + code_ += " f.write_fmt(format_args!(\"<UNKNOWN {:?}>\", self.0))"; + code_ += " }"; + code_ += " }"; + code_ += "}"; - GenComment(enum_def.doc_comment); - code_ += - "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]"; - code_ += "#[repr(transparent)]"; - code_ += "pub struct {{ENUM_NAME}}(pub {{BASE_TYPE}});"; - code_ += "#[allow(non_upper_case_globals)]"; - code_ += "impl {{ENUM_NAME}} {"; - ForAllEnumValues1(enum_def, [&](const EnumVal &ev){ - this->GenComment(ev.doc_comment, " "); - code_ += " pub const {{VARIANT}}: Self = Self({{VALUE}});"; - }); - code_ += ""; - // Generate Associated constants - code_ += " pub const ENUM_MIN: {{BASE_TYPE}} = {{ENUM_MIN_BASE_VALUE}};"; - code_ += " pub const ENUM_MAX: {{BASE_TYPE}} = {{ENUM_MAX_BASE_VALUE}};"; - code_ += " pub const ENUM_VALUES: &'static [Self] = &["; - ForAllEnumValues(enum_def, [&](){ - code_ += " Self::{{VARIANT}},"; - }); - code_ += " ];"; - code_ += " /// Returns the variant's name or \"\" if unknown."; - code_ += " pub fn variant_name(self) -> Option<&'static str> {"; - code_ += " match self {"; - ForAllEnumValues(enum_def, [&](){ - code_ += " Self::{{VARIANT}} => Some(\"{{VARIANT}}\"),"; - }); - code_ += " _ => None,"; - code_ += " }"; - code_ += " }"; - code_ += "}"; + if (enum_def.is_union) { + // Generate tyoesafe offset(s) for unions + code_.SetValue("NAME", Name(enum_def)); + code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset"); + code_ += "pub struct {{UNION_OFFSET_NAME}} {}"; + } - // Generate Debug. Unknown variants are printed like "<UNKNOWN 42>". - code_ += "impl std::fmt::Debug for {{ENUM_NAME}} {"; - code_ += " fn fmt(&self, f: &mut std::fmt::Formatter) ->" - " std::fmt::Result {"; - code_ += " if let Some(name) = self.variant_name() {"; - code_ += " f.write_str(name)"; - code_ += " } else {"; - code_ += " f.write_fmt(format_args!(\"<UNKNOWN {:?}>\", self.0))"; - code_ += " }"; - code_ += " }"; - code_ += "}"; + code_.SetValue("FROM_BASE", "Self(b)"); + code_.SetValue("INTO_BASE", "self.0"); + } // Generate Follow and Push so we can serialize and stuff. code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {"; code_ += " type Inner = Self;"; code_ += " #[inline]"; code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {"; - code_ += " Self(flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf, loc))"; + code_ += " let b = flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf," + " loc);"; + code_ += " {{FROM_BASE}}"; code_ += " }"; code_ += "}"; code_ += ""; @@ -668,28 +650,36 @@ class RustGenerator : public BaseGenerator { code_ += " #[inline]"; code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {"; code_ += " flatbuffers::emplace_scalar::<{{BASE_TYPE}}>" - "(dst, self.0);"; + "(dst, {{INTO_BASE}});"; code_ += " }"; code_ += "}"; code_ += ""; code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {"; code_ += " #[inline]"; code_ += " fn to_little_endian(self) -> Self {"; - code_ += " Self({{BASE_TYPE}}::to_le(self.0))"; + code_ += " let b = {{BASE_TYPE}}::to_le({{INTO_BASE}});"; + code_ += " {{FROM_BASE}}"; code_ += " }"; code_ += " #[inline]"; code_ += " fn from_little_endian(self) -> Self {"; - code_ += " Self({{BASE_TYPE}}::from_le(self.0))"; + code_ += " let b = {{BASE_TYPE}}::from_le({{INTO_BASE}});"; + code_ += " {{FROM_BASE}}"; code_ += " }"; code_ += "}"; code_ += ""; - - if (enum_def.is_union) { - // Generate tyoesafe offset(s) for unions - code_.SetValue("NAME", Name(enum_def)); - code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset"); - code_ += "pub struct {{UNION_OFFSET_NAME}} {}"; - } + // Generate verifier - deferring to the base type. + code_ += "impl<'a> flatbuffers::Verifiable for {{ENUM_NAME}} {"; + code_ += " #[inline]"; + code_ += " fn run_verifier<'o, 'b>("; + code_ += " v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize"; + code_ += " ) -> Result<(), flatbuffers::InvalidFlatbuffer> {"; + code_ += " use self::flatbuffers::Verifiable;"; + code_ += " {{BASE_TYPE}}::run_verifier(v, pos)"; + code_ += " }"; + code_ += "}"; + code_ += ""; + // Enums are basically integers. + code_ += "impl flatbuffers::SimpleToVerifyInSlice for {{ENUM_NAME}} {}"; } std::string GetFieldOffsetName(const FieldDef &field) { @@ -1003,109 +993,60 @@ class RustGenerator : public BaseGenerator { return "INVALID_CODE_GENERATION"; // for return analysis } - std::string GenTableAccessorFuncBody(const FieldDef &field, - const std::string &lifetime, - const std::string &offset_prefix) { - const std::string offset_name = - offset_prefix + "::" + GetFieldOffsetName(field); - const Type &type = field.value.type; + std::string FollowType(const Type &type, const std::string &lifetime) { + // IsVector... This can be made iterative? - switch (GetFullType(field.value.type)) { + const auto WrapForwardsUOffset = [](std::string ty) -> std::string { + return "flatbuffers::ForwardsUOffset<" + ty + ">"; + }; + const auto WrapVector = [&](std::string ty) -> std::string { + return "flatbuffers::Vector<" + lifetime + ", " + ty + ">"; + }; + switch (GetFullType(type)) { case ftInteger: case ftFloat: case ftBool: { - const auto typname = GetTypeBasic(type); - if (field.optional) { - return "self._tab.get::<" + typname + ">(" + offset_name + ", None)"; - } else { - const auto default_value = GetDefaultScalarValue(field); - return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + - default_value + ")).unwrap()"; - } + return GetTypeBasic(type); } case ftStruct: { - const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::<" + typname + ">(" + offset_name + ", None)", - field.required); + return WrapInNameSpace(*type.struct_def); + } + case ftUnionKey: + case ftEnumKey: { + return WrapInNameSpace(*type.enum_def); } case ftTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::<flatbuffers::ForwardsUOffset<" + typname + "<" + - lifetime + ">>>(" + offset_name + ", None)", - field.required); + return WrapForwardsUOffset(typname); } case ftUnionValue: { - return AddUnwrapIfRequired( - "self._tab.get::<flatbuffers::ForwardsUOffset<" - "flatbuffers::Table<" + - lifetime + ">>>(" + offset_name + ", None)", - field.required); - } - case ftUnionKey: - case ftEnumKey: { - const std::string typname = WrapInNameSpace(*type.enum_def); - const std::string default_value = GetDefaultScalarValue(field); - if (field.optional) { - return "self._tab.get::<" + typname + ">(" + offset_name + ", None)"; - } else { - return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + - default_value + ")).unwrap()"; - } + return WrapForwardsUOffset("flatbuffers::Table<" + lifetime + ">"); } case ftString: { - return AddUnwrapIfRequired( - "self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(" + - offset_name + ", None)", - field.required); + return WrapForwardsUOffset("&str"); } - case ftVectorOfInteger: case ftVectorOfBool: case ftVectorOfFloat: { const auto typname = GetTypeBasic(type.VectorType()); - std::string s = - "self._tab.get::<flatbuffers::ForwardsUOffset<" - "flatbuffers::Vector<" + - lifetime + ", " + typname + ">>>(" + offset_name + ", None)"; - // single-byte values are safe to slice - if (IsOneByte(type.VectorType().base_type)) { - s += ".map(|v| v.safe_slice())"; - } - return AddUnwrapIfRequired(s, field.required); + return WrapForwardsUOffset(WrapVector(typname)); } case ftVectorOfEnumKey: { - const auto typname = WrapInNameSpace(*type.enum_def); - return AddUnwrapIfRequired( - "self._tab.get::<flatbuffers::ForwardsUOffset<" - "flatbuffers::Vector<" + - lifetime + ", " + typname + ">>>(" + offset_name + ", None)", - field.required); + const auto typname = WrapInNameSpace(*type.VectorType().enum_def); + return WrapForwardsUOffset(WrapVector(typname)); + } case ftVectorOfStruct: { const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::<flatbuffers::ForwardsUOffset<" - "flatbuffers::Vector<" + - typname + ">>>(" + offset_name + - ", None).map(|v| v.safe_slice() )", - field.required); + return WrapForwardsUOffset(WrapVector(typname)); } case ftVectorOfTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::<flatbuffers::ForwardsUOffset<" - "flatbuffers::Vector<flatbuffers::ForwardsUOffset<" + - typname + "<" + lifetime + ">>>>>(" + offset_name + ", None)", - field.required); + return WrapForwardsUOffset(WrapVector(WrapForwardsUOffset(typname))); } case ftVectorOfString: { - return AddUnwrapIfRequired( - "self._tab.get::<flatbuffers::ForwardsUOffset<" - "flatbuffers::Vector<flatbuffers::ForwardsUOffset<&" + - lifetime + " str>>>>(" + offset_name + ", None)", - field.required); + return WrapForwardsUOffset(WrapVector(WrapForwardsUOffset( + "&" + lifetime + " str"))); } case ftVectorOfUnionValue: { FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported"); @@ -1115,6 +1056,28 @@ class RustGenerator : public BaseGenerator { return "INVALID_CODE_GENERATION"; // for return analysis } + std::string GenTableAccessorFuncBody(const FieldDef &field, + const std::string &lifetime) { + const std::string vt_offset = GetFieldOffsetName(field); + const std::string typname = FollowType(field.value.type, lifetime); + // Default-y fields (scalars so far) are neither optional nor required. + const std::string default_value = !(field.optional || field.required) ? + "Some(" + GetDefaultScalarValue(field) + ")" : "None"; + const std::string unwrap = field.optional ? "" : ".unwrap()"; + + const auto t = GetFullType(field.value.type); + + // TODO(caspern): Shouldn't 1byte VectorOfEnumKey be slice too? + const std::string safe_slice = ( + t == ftVectorOfStruct || + ((t == ftVectorOfBool || t == ftVectorOfFloat || t == ftVectorOfInteger) + && IsOneByte(field.value.type.VectorType().base_type)) + ) ? ".map(|v| v.safe_slice())" : ""; + + return "self._tab.get::<" + typname + ">({{STRUCT_NAME}}::" + + vt_offset + ", " + default_value + ")" + safe_slice + unwrap; + } + bool TableFieldReturnsOption(const FieldDef &field) { if (field.optional) return true; switch (GetFullType(field.value.type)) { @@ -1272,17 +1235,14 @@ class RustGenerator : public BaseGenerator { // pub fn name(&'a self) -> user_facing_type { // self._tab.get::<internal_type>(offset, defaultval).unwrap() // } - const auto offset_prefix = Name(struct_def); ForAllTableFields(struct_def, [&](const FieldDef &field) { code_.SetValue("RETURN_TYPE", GenTableAccessorFuncReturnType(field, "'a")); - code_.SetValue("FUNC_BODY", - GenTableAccessorFuncBody(field, "'a", offset_prefix)); this->GenComment(field.doc_comment, " "); code_ += " #[inline]"; code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {"; - code_ += " {{FUNC_BODY}}"; + code_ += " " + GenTableAccessorFuncBody(field, "'a"); code_ += " }"; // Generate a comparison function for this field if it is a key. @@ -1369,6 +1329,50 @@ class RustGenerator : public BaseGenerator { code_ += "}"; // End of table impl. code_ += ""; + // Generate Verifier; + code_ += "impl flatbuffers::Verifiable for {{STRUCT_NAME}}<'_> {"; + code_ += " #[inline]"; + code_ += " fn run_verifier<'o, 'b>("; + code_ += " v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize"; + code_ += " ) -> Result<(), flatbuffers::InvalidFlatbuffer> {"; + code_ += " use self::flatbuffers::Verifiable;"; + code_ += " v.visit_table(pos)?\\"; + // Escape newline and insert it onthe next line so we can end the builder + // with a nice semicolon. + ForAllTableFields(struct_def, [&](const FieldDef &field) { + if (GetFullType(field.value.type) == ftUnionKey) return; + + code_.SetValue("IS_REQ", field.required ? "true" : "false"); + if (GetFullType(field.value.type) != ftUnionValue) { + // All types besides unions. + code_.SetValue("TY", FollowType(field.value.type, "'_")); + code_ += "\n .visit_field::<{{TY}}>(&\"{{FIELD_NAME}}\", " + "Self::{{OFFSET_NAME}}, {{IS_REQ}})?\\"; + return; + } + // Unions. + EnumDef &union_def = *field.value.type.enum_def; + code_.SetValue("UNION_TYPE", Name(union_def)); + code_ += "\n .visit_union::<{{UNION_TYPE}}, _>(" + "&\"{{FIELD_NAME}}_type\", Self::{{OFFSET_NAME}}_TYPE, " + "&\"{{FIELD_NAME}}\", Self::{{OFFSET_NAME}}, {{IS_REQ}}, " + "|key, v, pos| {"; + code_ += " match key {"; + ForAllUnionVariantsBesidesNone(union_def, [&](const EnumVal &unused) { + (void) unused; + code_ += " {{U_ELEMENT_ENUM_TYPE}} => v.verify_union_variant::" + "<flatbuffers::ForwardsUOffset<{{U_ELEMENT_TABLE_TYPE}}>>(" + "\"{{U_ELEMENT_ENUM_TYPE}}\", pos),"; + }); + code_ += " _ => Ok(()),"; + code_ += " }"; + code_ += " })?\\"; + }); + code_ += "\n .finish();"; + code_ += " Ok(())"; + code_ += " }"; + code_ += "}"; + // Generate an args struct: code_.SetValue("MAYBE_LT", TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : ""); @@ -1538,22 +1542,105 @@ class RustGenerator : public BaseGenerator { // The root datatype accessors: code_ += "#[inline]"; + code_ += "#[deprecated(since=\"1.13\", " + "note=\"Deprecated in favor of `root_as...` methods.\")]"; code_ += "pub fn get_root_as_{{STRUCT_NAME_SNAKECASE}}<'a>(buf: &'a [u8])" " -> {{STRUCT_NAME}}<'a> {"; - code_ += " flatbuffers::get_root::<{{STRUCT_NAME}}<'a>>(buf)"; + code_ += " unsafe { flatbuffers::root_unchecked::<{{STRUCT_NAME}}" + "<'a>>(buf) }"; code_ += "}"; code_ += ""; code_ += "#[inline]"; + code_ += "#[deprecated(since=\"1.13\", " + "note=\"Deprecated in favor of `root_as...` methods.\")]"; code_ += "pub fn get_size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}" "<'a>(buf: &'a [u8]) -> {{STRUCT_NAME}}<'a> {"; code_ += - " flatbuffers::get_size_prefixed_root::<{{STRUCT_NAME}}<'a>>" - "(buf)"; + " unsafe { flatbuffers::size_prefixed_root_unchecked::<{{STRUCT_NAME}}" + "<'a>>(buf) }"; code_ += "}"; code_ += ""; + // Default verifier root fns. + code_ += "#[inline]"; + code_ += "/// Verifies that a buffer of bytes contains a `{{STRUCT_NAME}}`"; + code_ += "/// and returns it."; + code_ += "/// Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += "pub fn root_as_{{STRUCT_NAME_SNAKECASE}}(buf: &[u8]) " + "-> Result<{{STRUCT_NAME}}, flatbuffers::InvalidFlatbuffer> {"; + code_ += " flatbuffers::root::<{{STRUCT_NAME}}>(buf)"; + code_ += "}"; + code_ += "#[inline]"; + code_ += "/// Verifies that a buffer of bytes contains a size prefixed"; + code_ += "/// `{{STRUCT_NAME}}` and returns it."; + code_ += "/// Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += "pub fn size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}" + "(buf: &[u8]) -> Result<{{STRUCT_NAME}}, " + "flatbuffers::InvalidFlatbuffer> {"; + code_ += " flatbuffers::size_prefixed_root::<{{STRUCT_NAME}}>(buf)"; + code_ += "}"; + // Verifier with options root fns. + code_ += "#[inline]"; + code_ += "/// Verifies, with the given options, that a buffer of bytes"; + code_ += "/// contains a `{{STRUCT_NAME}}` and returns it."; + code_ += "/// Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += "pub fn root_as_{{STRUCT_NAME_SNAKECASE}}_with_opts<'b, 'o>("; + code_ += " opts: &'o flatbuffers::VerifierOptions,"; + code_ += " buf: &'b [u8],"; + code_ += ") -> Result<{{STRUCT_NAME}}<'b>, flatbuffers::InvalidFlatbuffer>" + " {"; + code_ += " flatbuffers::root_with_opts::<{{STRUCT_NAME}}<'b>>(opts, buf)"; + code_ += "}"; + code_ += "#[inline]"; + code_ += "/// Verifies, with the given verifier options, that a buffer of"; + code_ += "/// bytes contains a size prefixed `{{STRUCT_NAME}}` and returns"; + code_ += "/// it. Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += "pub fn size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}_with_opts" + "<'b, 'o>("; + code_ += " opts: &'o flatbuffers::VerifierOptions,"; + code_ += " buf: &'b [u8],"; + code_ += ") -> Result<{{STRUCT_NAME}}<'b>, flatbuffers::InvalidFlatbuffer>" + " {"; + code_ += " flatbuffers::size_prefixed_root_with_opts::<{{STRUCT_NAME}}" + "<'b>>(opts, buf)"; + code_ += "}"; + // Unchecked root fns. + code_ += "#[inline]"; + code_ += "/// Assumes, without verification, that a buffer of bytes " + "contains a {{STRUCT_NAME}} and returns it."; + code_ += "/// # Safety"; + code_ += "/// Callers must trust the given bytes do indeed contain a valid" + " `{{STRUCT_NAME}}`."; + code_ += "pub unsafe fn root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked" + "(buf: &[u8]) -> {{STRUCT_NAME}} {"; + code_ += " flatbuffers::root_unchecked::<{{STRUCT_NAME}}>(buf)"; + code_ += "}"; + code_ += "#[inline]"; + code_ += "/// Assumes, without verification, that a buffer of bytes " + "contains a size prefixed {{STRUCT_NAME}} and returns it."; + code_ += "/// # Safety"; + code_ += "/// Callers must trust the given bytes do indeed contain a valid" + " size prefixed `{{STRUCT_NAME}}`."; + code_ += "pub unsafe fn size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}" + "_unchecked(buf: &[u8]) -> {{STRUCT_NAME}} {"; + code_ += " flatbuffers::size_prefixed_root_unchecked::<{{STRUCT_NAME}}>" + "(buf)"; + code_ += "}"; + if (parser_.file_identifier_.length()) { // Declare the identifier @@ -1697,6 +1784,7 @@ class RustGenerator : public BaseGenerator { // Generate impls for SafeSliceAccess (because all structs are endian-safe), // Follow for the value type, Follow for the reference type, Push for the // value type, and Push for the reference type. + code_ += "impl flatbuffers::SimpleToVerifyInSlice for {{STRUCT_NAME}} {}"; code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_NAME}} {}"; code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}} {"; code_ += " type Inner = &'a {{STRUCT_NAME}};"; @@ -1738,7 +1826,18 @@ class RustGenerator : public BaseGenerator { code_ += " }"; code_ += "}"; code_ += ""; - code_ += ""; + + // Generate verifier: Structs are simple so presence and alignment are + // all that need to be checked. + code_ += "impl<'a> flatbuffers::Verifiable for {{STRUCT_NAME}} {"; + code_ += " #[inline]"; + code_ += " fn run_verifier<'o, 'b>("; + code_ += " v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize"; + code_ += " ) -> Result<(), flatbuffers::InvalidFlatbuffer> {"; + code_ += " use self::flatbuffers::Verifiable;"; + code_ += " v.in_buffer::<Self>(pos)"; + code_ += " }"; + code_ += "}"; // Generate a constructor that takes all fields as arguments. code_ += "impl {{STRUCT_NAME}} {"; diff --git a/tests/include_test/include_test1_generated.rs b/tests/include_test/include_test1_generated.rs index 3c7b9bd8..cf63626e 100644 --- a/tests/include_test/include_test1_generated.rs +++ b/tests/include_test/include_test1_generated.rs @@ -45,10 +45,22 @@ impl<'a> TableA<'a> { #[inline] pub fn b(&self) -> Option<my_game::other_name_space::TableB<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<my_game::other_name_space::TableB<'a>>>(TableA::VT_B, None) + self._tab.get::<flatbuffers::ForwardsUOffset<my_game::other_name_space::TableB>>(TableA::VT_B, None) } } +impl flatbuffers::Verifiable for TableA<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<flatbuffers::ForwardsUOffset<my_game::other_name_space::TableB>>(&"b", Self::VT_B, false)? + .finish(); + Ok(()) + } +} pub struct TableAArgs<'a> { pub b: Option<flatbuffers::WIPOffset<my_game::other_name_space::TableB<'a>>>, } diff --git a/tests/include_test/sub/include_test2_generated.rs b/tests/include_test/sub/include_test2_generated.rs index 892c652d..a461b82a 100644 --- a/tests/include_test/sub/include_test2_generated.rs +++ b/tests/include_test/sub/include_test2_generated.rs @@ -72,7 +72,8 @@ impl<'a> flatbuffers::Follow<'a> for FromInclude { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<i64>(buf, loc)) + let b = flatbuffers::read_scalar_at::<i64>(buf, loc); + Self(b) } } @@ -87,14 +88,27 @@ impl flatbuffers::Push for FromInclude { impl flatbuffers::EndianScalar for FromInclude { #[inline] fn to_little_endian(self) -> Self { - Self(i64::to_le(self.0)) + let b = i64::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(i64::from_le(self.0)) + let b = i64::from_le(self.0); + Self(b) } } +impl<'a> flatbuffers::Verifiable for FromInclude { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i64::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for FromInclude {} // struct Unused, aligned to 4 #[repr(C, align(4))] #[derive(Clone, Copy, PartialEq)] @@ -109,6 +123,7 @@ impl std::fmt::Debug for Unused { } } +impl flatbuffers::SimpleToVerifyInSlice for Unused {} impl flatbuffers::SafeSliceAccess for Unused {} impl<'a> flatbuffers::Follow<'a> for Unused { type Inner = &'a Unused; @@ -146,7 +161,15 @@ impl<'b> flatbuffers::Push for &'b Unused { } } - +impl<'a> flatbuffers::Verifiable for Unused { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.in_buffer::<Self>(pos) + } +} impl Unused { pub fn new(_a: i32) -> Self { Unused { @@ -194,10 +217,22 @@ impl<'a> TableB<'a> { #[inline] pub fn a(&self) -> Option<super::super::TableA<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<super::super::TableA<'a>>>(TableB::VT_A, None) + self._tab.get::<flatbuffers::ForwardsUOffset<super::super::TableA>>(TableB::VT_A, None) } } +impl flatbuffers::Verifiable for TableB<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<flatbuffers::ForwardsUOffset<super::super::TableA>>(&"a", Self::VT_A, false)? + .finish(); + Ok(()) + } +} pub struct TableBArgs<'a> { pub a: Option<flatbuffers::WIPOffset<super::super::TableA<'a>>>, } diff --git a/tests/monster_test_generated.rs b/tests/monster_test_generated.rs index e6539335..f38ce6ad 100644 --- a/tests/monster_test_generated.rs +++ b/tests/monster_test_generated.rs @@ -58,6 +58,17 @@ impl<'a> InParentNamespace<'a> { } +impl flatbuffers::Verifiable for InParentNamespace<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .finish(); + Ok(()) + } +} pub struct InParentNamespaceArgs { } impl<'a> Default for InParentNamespaceArgs { @@ -140,6 +151,17 @@ impl<'a> Monster<'a> { } +impl flatbuffers::Verifiable for Monster<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .finish(); + Ok(()) + } +} pub struct MonsterArgs { } impl<'a> Default for MonsterArgs { @@ -208,8 +230,8 @@ impl<'a> flatbuffers::Follow<'a> for Color { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - let bits = flatbuffers::read_scalar_at::<u8>(buf, loc); - unsafe { Self::from_bits_unchecked(bits) } + let b = flatbuffers::read_scalar_at::<u8>(buf, loc); + unsafe { Self::from_bits_unchecked(b) } } } @@ -224,16 +246,27 @@ impl flatbuffers::Push for Color { impl flatbuffers::EndianScalar for Color { #[inline] fn to_little_endian(self) -> Self { - let bits = u8::to_le(self.bits()); - unsafe { Self::from_bits_unchecked(bits) } + let b = u8::to_le(self.bits()); + unsafe { Self::from_bits_unchecked(b) } } #[inline] fn from_little_endian(self) -> Self { - let bits = u8::from_le(self.bits()); - unsafe { Self::from_bits_unchecked(bits) } + let b = u8::from_le(self.bits()); + unsafe { Self::from_bits_unchecked(b) } + } +} + +impl<'a> flatbuffers::Verifiable for Color { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) } } +impl flatbuffers::SimpleToVerifyInSlice for Color {} #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] pub const ENUM_MIN_RACE: i8 = -1; #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] @@ -289,7 +322,8 @@ impl<'a> flatbuffers::Follow<'a> for Race { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<i8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<i8>(buf, loc); + Self(b) } } @@ -304,14 +338,27 @@ impl flatbuffers::Push for Race { impl flatbuffers::EndianScalar for Race { #[inline] fn to_little_endian(self) -> Self { - Self(i8::to_le(self.0)) + let b = i8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(i8::from_le(self.0)) + let b = i8::from_le(self.0); + Self(b) } } +impl<'a> flatbuffers::Verifiable for Race { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for Race {} #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] pub const ENUM_MIN_ANY: u8 = 0; #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] @@ -363,11 +410,13 @@ impl std::fmt::Debug for Any { } } } +pub struct AnyUnionTableOffset {} impl<'a> flatbuffers::Follow<'a> for Any { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<u8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<u8>(buf, loc); + Self(b) } } @@ -382,15 +431,27 @@ impl flatbuffers::Push for Any { impl flatbuffers::EndianScalar for Any { #[inline] fn to_little_endian(self) -> Self { - Self(u8::to_le(self.0)) + let b = u8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(u8::from_le(self.0)) + let b = u8::from_le(self.0); + Self(b) } } -pub struct AnyUnionTableOffset {} +impl<'a> flatbuffers::Verifiable for Any { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for Any {} #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] pub const ENUM_MIN_ANY_UNIQUE_ALIASES: u8 = 0; #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] @@ -442,11 +503,13 @@ impl std::fmt::Debug for AnyUniqueAliases { } } } +pub struct AnyUniqueAliasesUnionTableOffset {} impl<'a> flatbuffers::Follow<'a> for AnyUniqueAliases { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<u8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<u8>(buf, loc); + Self(b) } } @@ -461,15 +524,27 @@ impl flatbuffers::Push for AnyUniqueAliases { impl flatbuffers::EndianScalar for AnyUniqueAliases { #[inline] fn to_little_endian(self) -> Self { - Self(u8::to_le(self.0)) + let b = u8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(u8::from_le(self.0)) + let b = u8::from_le(self.0); + Self(b) } } -pub struct AnyUniqueAliasesUnionTableOffset {} +impl<'a> flatbuffers::Verifiable for AnyUniqueAliases { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for AnyUniqueAliases {} #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] pub const ENUM_MIN_ANY_AMBIGUOUS_ALIASES: u8 = 0; #[deprecated(since = "1.13", note = "Use associated constants instead. This will no longer be generated in 2021.")] @@ -521,11 +596,13 @@ impl std::fmt::Debug for AnyAmbiguousAliases { } } } +pub struct AnyAmbiguousAliasesUnionTableOffset {} impl<'a> flatbuffers::Follow<'a> for AnyAmbiguousAliases { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<u8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<u8>(buf, loc); + Self(b) } } @@ -540,15 +617,27 @@ impl flatbuffers::Push for AnyAmbiguousAliases { impl flatbuffers::EndianScalar for AnyAmbiguousAliases { #[inline] fn to_little_endian(self) -> Self { - Self(u8::to_le(self.0)) + let b = u8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(u8::from_le(self.0)) + let b = u8::from_le(self.0); + Self(b) } } -pub struct AnyAmbiguousAliasesUnionTableOffset {} +impl<'a> flatbuffers::Verifiable for AnyAmbiguousAliases { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for AnyAmbiguousAliases {} // struct Test, aligned to 2 #[repr(C, align(2))] #[derive(Clone, Copy, PartialEq)] @@ -566,6 +655,7 @@ impl std::fmt::Debug for Test { } } +impl flatbuffers::SimpleToVerifyInSlice for Test {} impl flatbuffers::SafeSliceAccess for Test {} impl<'a> flatbuffers::Follow<'a> for Test { type Inner = &'a Test; @@ -603,7 +693,15 @@ impl<'b> flatbuffers::Push for &'b Test { } } - +impl<'a> flatbuffers::Verifiable for Test { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.in_buffer::<Self>(pos) + } +} impl Test { pub fn new(_a: i16, _b: i8) -> Self { Test { @@ -652,6 +750,7 @@ impl std::fmt::Debug for Vec3 { } } +impl flatbuffers::SimpleToVerifyInSlice for Vec3 {} impl flatbuffers::SafeSliceAccess for Vec3 {} impl<'a> flatbuffers::Follow<'a> for Vec3 { type Inner = &'a Vec3; @@ -689,7 +788,15 @@ impl<'b> flatbuffers::Push for &'b Vec3 { } } - +impl<'a> flatbuffers::Verifiable for Vec3 { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.in_buffer::<Self>(pos) + } +} impl Vec3 { pub fn new(_x: f32, _y: f32, _z: f32, _test1: f64, _test2: Color, _test3: &Test) -> Self { Vec3 { @@ -745,6 +852,7 @@ impl std::fmt::Debug for Ability { } } +impl flatbuffers::SimpleToVerifyInSlice for Ability {} impl flatbuffers::SafeSliceAccess for Ability {} impl<'a> flatbuffers::Follow<'a> for Ability { type Inner = &'a Ability; @@ -782,7 +890,15 @@ impl<'b> flatbuffers::Push for &'b Ability { } } - +impl<'a> flatbuffers::Verifiable for Ability { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.in_buffer::<Self>(pos) + } +} impl Ability { pub fn new(_id: u32, _distance: u32) -> Self { Ability { @@ -856,6 +972,18 @@ impl<'a> TestSimpleTableWithEnum<'a> { } } +impl flatbuffers::Verifiable for TestSimpleTableWithEnum<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<Color>(&"color", Self::VT_COLOR, false)? + .finish(); + Ok(()) + } +} pub struct TestSimpleTableWithEnumArgs { pub color: Color, } @@ -953,6 +1081,20 @@ impl<'a> Stat<'a> { } } +impl flatbuffers::Verifiable for Stat<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<flatbuffers::ForwardsUOffset<&str>>(&"id", Self::VT_ID, false)? + .visit_field::<i64>(&"val", Self::VT_VAL, false)? + .visit_field::<u16>(&"count", Self::VT_COUNT, false)? + .finish(); + Ok(()) + } +} pub struct StatArgs<'a> { pub id: Option<flatbuffers::WIPOffset<&'a str>>, pub val: i64, @@ -1062,6 +1204,18 @@ impl<'a> Referrable<'a> { } } +impl flatbuffers::Verifiable for Referrable<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<u64>(&"id", Self::VT_ID, false)? + .finish(); + Ok(()) + } +} pub struct ReferrableArgs { pub id: u64, } @@ -1282,21 +1436,21 @@ impl<'a> Monster<'a> { } #[inline] pub fn test4(&self) -> Option<&'a [Test]> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<Test>>>(Monster::VT_TEST4, None).map(|v| v.safe_slice() ) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, Test>>>(Monster::VT_TEST4, None).map(|v| v.safe_slice()) } #[inline] pub fn testarrayofstring(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>>> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<flatbuffers::ForwardsUOffset<&'a str>>>>(Monster::VT_TESTARRAYOFSTRING, None) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>>>>(Monster::VT_TESTARRAYOFSTRING, None) } /// an example documentation comment: this will end up in the generated code /// multiline too #[inline] pub fn testarrayoftables(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Monster<'a>>>> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<flatbuffers::ForwardsUOffset<Monster<'a>>>>>(Monster::VT_TESTARRAYOFTABLES, None) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Monster>>>>(Monster::VT_TESTARRAYOFTABLES, None) } #[inline] pub fn enemy(&self) -> Option<Monster<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<Monster<'a>>>(Monster::VT_ENEMY, None) + self._tab.get::<flatbuffers::ForwardsUOffset<Monster>>(Monster::VT_ENEMY, None) } #[inline] pub fn testnestedflatbuffer(&self) -> Option<&'a [u8]> { @@ -1310,7 +1464,7 @@ impl<'a> Monster<'a> { } #[inline] pub fn testempty(&self) -> Option<Stat<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<Stat<'a>>>(Monster::VT_TESTEMPTY, None) + self._tab.get::<flatbuffers::ForwardsUOffset<Stat>>(Monster::VT_TESTEMPTY, None) } #[inline] pub fn testbool(&self) -> bool { @@ -1366,11 +1520,11 @@ impl<'a> Monster<'a> { } #[inline] pub fn testarrayofstring2(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>>> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<flatbuffers::ForwardsUOffset<&'a str>>>>(Monster::VT_TESTARRAYOFSTRING2, None) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>>>>(Monster::VT_TESTARRAYOFSTRING2, None) } #[inline] pub fn testarrayofsortedstruct(&self) -> Option<&'a [Ability]> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<Ability>>>(Monster::VT_TESTARRAYOFSORTEDSTRUCT, None).map(|v| v.safe_slice() ) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, Ability>>>(Monster::VT_TESTARRAYOFSORTEDSTRUCT, None).map(|v| v.safe_slice()) } #[inline] pub fn flex(&self) -> Option<&'a [u8]> { @@ -1378,7 +1532,7 @@ impl<'a> Monster<'a> { } #[inline] pub fn test5(&self) -> Option<&'a [Test]> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<Test>>>(Monster::VT_TEST5, None).map(|v| v.safe_slice() ) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, Test>>>(Monster::VT_TEST5, None).map(|v| v.safe_slice()) } #[inline] pub fn vector_of_longs(&self) -> Option<flatbuffers::Vector<'a, i64>> { @@ -1390,11 +1544,11 @@ impl<'a> Monster<'a> { } #[inline] pub fn parent_namespace_test(&self) -> Option<super::InParentNamespace<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<super::InParentNamespace<'a>>>(Monster::VT_PARENT_NAMESPACE_TEST, None) + self._tab.get::<flatbuffers::ForwardsUOffset<super::InParentNamespace>>(Monster::VT_PARENT_NAMESPACE_TEST, None) } #[inline] pub fn vector_of_referrables(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Referrable<'a>>>> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<flatbuffers::ForwardsUOffset<Referrable<'a>>>>>(Monster::VT_VECTOR_OF_REFERRABLES, None) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Referrable>>>>(Monster::VT_VECTOR_OF_REFERRABLES, None) } #[inline] pub fn single_weak_reference(&self) -> u64 { @@ -1406,7 +1560,7 @@ impl<'a> Monster<'a> { } #[inline] pub fn vector_of_strong_referrables(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Referrable<'a>>>> { - self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<flatbuffers::ForwardsUOffset<Referrable<'a>>>>>(Monster::VT_VECTOR_OF_STRONG_REFERRABLES, None) + self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Referrable>>>>(Monster::VT_VECTOR_OF_STRONG_REFERRABLES, None) } #[inline] pub fn co_owning_reference(&self) -> u64 { @@ -1550,6 +1704,84 @@ impl<'a> Monster<'a> { } +impl flatbuffers::Verifiable for Monster<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<Vec3>(&"pos", Self::VT_POS, false)? + .visit_field::<i16>(&"mana", Self::VT_MANA, false)? + .visit_field::<i16>(&"hp", Self::VT_HP, false)? + .visit_field::<flatbuffers::ForwardsUOffset<&str>>(&"name", Self::VT_NAME, true)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u8>>>(&"inventory", Self::VT_INVENTORY, false)? + .visit_field::<Color>(&"color", Self::VT_COLOR, false)? + .visit_union::<Any, _>(&"test_type", Self::VT_TEST_TYPE, &"test", Self::VT_TEST, false, |key, v, pos| { + match key { + Any::Monster => v.verify_union_variant::<flatbuffers::ForwardsUOffset<Monster>>("Any::Monster", pos), + Any::TestSimpleTableWithEnum => v.verify_union_variant::<flatbuffers::ForwardsUOffset<TestSimpleTableWithEnum>>("Any::TestSimpleTableWithEnum", pos), + Any::MyGame_Example2_Monster => v.verify_union_variant::<flatbuffers::ForwardsUOffset<super::example_2::Monster>>("Any::MyGame_Example2_Monster", pos), + _ => Ok(()), + } + })? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, Test>>>(&"test4", Self::VT_TEST4, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<&'_ str>>>>(&"testarrayofstring", Self::VT_TESTARRAYOFSTRING, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Monster>>>>(&"testarrayoftables", Self::VT_TESTARRAYOFTABLES, false)? + .visit_field::<flatbuffers::ForwardsUOffset<Monster>>(&"enemy", Self::VT_ENEMY, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u8>>>(&"testnestedflatbuffer", Self::VT_TESTNESTEDFLATBUFFER, false)? + .visit_field::<flatbuffers::ForwardsUOffset<Stat>>(&"testempty", Self::VT_TESTEMPTY, false)? + .visit_field::<bool>(&"testbool", Self::VT_TESTBOOL, false)? + .visit_field::<i32>(&"testhashs32_fnv1", Self::VT_TESTHASHS32_FNV1, false)? + .visit_field::<u32>(&"testhashu32_fnv1", Self::VT_TESTHASHU32_FNV1, false)? + .visit_field::<i64>(&"testhashs64_fnv1", Self::VT_TESTHASHS64_FNV1, false)? + .visit_field::<u64>(&"testhashu64_fnv1", Self::VT_TESTHASHU64_FNV1, false)? + .visit_field::<i32>(&"testhashs32_fnv1a", Self::VT_TESTHASHS32_FNV1A, false)? + .visit_field::<u32>(&"testhashu32_fnv1a", Self::VT_TESTHASHU32_FNV1A, false)? + .visit_field::<i64>(&"testhashs64_fnv1a", Self::VT_TESTHASHS64_FNV1A, false)? + .visit_field::<u64>(&"testhashu64_fnv1a", Self::VT_TESTHASHU64_FNV1A, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, bool>>>(&"testarrayofbools", Self::VT_TESTARRAYOFBOOLS, false)? + .visit_field::<f32>(&"testf", Self::VT_TESTF, false)? + .visit_field::<f32>(&"testf2", Self::VT_TESTF2, false)? + .visit_field::<f32>(&"testf3", Self::VT_TESTF3, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<&'_ str>>>>(&"testarrayofstring2", Self::VT_TESTARRAYOFSTRING2, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, Ability>>>(&"testarrayofsortedstruct", Self::VT_TESTARRAYOFSORTEDSTRUCT, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u8>>>(&"flex", Self::VT_FLEX, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, Test>>>(&"test5", Self::VT_TEST5, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, i64>>>(&"vector_of_longs", Self::VT_VECTOR_OF_LONGS, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, f64>>>(&"vector_of_doubles", Self::VT_VECTOR_OF_DOUBLES, false)? + .visit_field::<flatbuffers::ForwardsUOffset<super::InParentNamespace>>(&"parent_namespace_test", Self::VT_PARENT_NAMESPACE_TEST, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Referrable>>>>(&"vector_of_referrables", Self::VT_VECTOR_OF_REFERRABLES, false)? + .visit_field::<u64>(&"single_weak_reference", Self::VT_SINGLE_WEAK_REFERENCE, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u64>>>(&"vector_of_weak_references", Self::VT_VECTOR_OF_WEAK_REFERENCES, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Referrable>>>>(&"vector_of_strong_referrables", Self::VT_VECTOR_OF_STRONG_REFERRABLES, false)? + .visit_field::<u64>(&"co_owning_reference", Self::VT_CO_OWNING_REFERENCE, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u64>>>(&"vector_of_co_owning_references", Self::VT_VECTOR_OF_CO_OWNING_REFERENCES, false)? + .visit_field::<u64>(&"non_owning_reference", Self::VT_NON_OWNING_REFERENCE, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u64>>>(&"vector_of_non_owning_references", Self::VT_VECTOR_OF_NON_OWNING_REFERENCES, false)? + .visit_union::<AnyUniqueAliases, _>(&"any_unique_type", Self::VT_ANY_UNIQUE_TYPE, &"any_unique", Self::VT_ANY_UNIQUE, false, |key, v, pos| { + match key { + AnyUniqueAliases::M => v.verify_union_variant::<flatbuffers::ForwardsUOffset<Monster>>("AnyUniqueAliases::M", pos), + AnyUniqueAliases::TS => v.verify_union_variant::<flatbuffers::ForwardsUOffset<TestSimpleTableWithEnum>>("AnyUniqueAliases::TS", pos), + AnyUniqueAliases::M2 => v.verify_union_variant::<flatbuffers::ForwardsUOffset<super::example_2::Monster>>("AnyUniqueAliases::M2", pos), + _ => Ok(()), + } + })? + .visit_union::<AnyAmbiguousAliases, _>(&"any_ambiguous_type", Self::VT_ANY_AMBIGUOUS_TYPE, &"any_ambiguous", Self::VT_ANY_AMBIGUOUS, false, |key, v, pos| { + match key { + AnyAmbiguousAliases::M1 => v.verify_union_variant::<flatbuffers::ForwardsUOffset<Monster>>("AnyAmbiguousAliases::M1", pos), + AnyAmbiguousAliases::M2 => v.verify_union_variant::<flatbuffers::ForwardsUOffset<Monster>>("AnyAmbiguousAliases::M2", pos), + AnyAmbiguousAliases::M3 => v.verify_union_variant::<flatbuffers::ForwardsUOffset<Monster>>("AnyAmbiguousAliases::M3", pos), + _ => Ok(()), + } + })? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, Color>>>(&"vector_of_enums", Self::VT_VECTOR_OF_ENUMS, false)? + .visit_field::<Race>(&"signed_enum", Self::VT_SIGNED_ENUM, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u8>>>(&"testrequirednestedflatbuffer", Self::VT_TESTREQUIREDNESTEDFLATBUFFER, false)? + .finish(); + Ok(()) + } +} pub struct MonsterArgs<'a> { pub pos: Option<&'a Vec3>, pub mana: i16, @@ -2116,6 +2348,29 @@ impl<'a> TypeAliases<'a> { } } +impl flatbuffers::Verifiable for TypeAliases<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<i8>(&"i8_", Self::VT_I8_, false)? + .visit_field::<u8>(&"u8_", Self::VT_U8_, false)? + .visit_field::<i16>(&"i16_", Self::VT_I16_, false)? + .visit_field::<u16>(&"u16_", Self::VT_U16_, false)? + .visit_field::<i32>(&"i32_", Self::VT_I32_, false)? + .visit_field::<u32>(&"u32_", Self::VT_U32_, false)? + .visit_field::<i64>(&"i64_", Self::VT_I64_, false)? + .visit_field::<u64>(&"u64_", Self::VT_U64_, false)? + .visit_field::<f32>(&"f32_", Self::VT_F32_, false)? + .visit_field::<f64>(&"f64_", Self::VT_F64_, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, i8>>>(&"v8", Self::VT_V8, false)? + .visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, f64>>>(&"vf64", Self::VT_VF64, false)? + .finish(); + Ok(()) + } +} pub struct TypeAliasesArgs<'a> { pub i8_: i8, pub u8_: u8, @@ -2236,15 +2491,77 @@ impl std::fmt::Debug for TypeAliases<'_> { } } #[inline] +#[deprecated(since="1.13", note="Deprecated in favor of `root_as...` methods.")] pub fn get_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> { - flatbuffers::get_root::<Monster<'a>>(buf) + unsafe { flatbuffers::root_unchecked::<Monster<'a>>(buf) } } #[inline] +#[deprecated(since="1.13", note="Deprecated in favor of `root_as...` methods.")] pub fn get_size_prefixed_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> { - flatbuffers::get_size_prefixed_root::<Monster<'a>>(buf) + unsafe { flatbuffers::size_prefixed_root_unchecked::<Monster<'a>>(buf) } } +#[inline] +/// Verifies that a buffer of bytes contains a `Monster` +/// and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_monster_unchecked`. +pub fn root_as_monster(buf: &[u8]) -> Result<Monster, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root::<Monster>(buf) +} +#[inline] +/// Verifies that a buffer of bytes contains a size prefixed +/// `Monster` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `size_prefixed_root_as_monster_unchecked`. +pub fn size_prefixed_root_as_monster(buf: &[u8]) -> Result<Monster, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root::<Monster>(buf) +} +#[inline] +/// Verifies, with the given options, that a buffer of bytes +/// contains a `Monster` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_monster_unchecked`. +pub fn root_as_monster_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result<Monster<'b>, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root_with_opts::<Monster<'b>>(opts, buf) +} +#[inline] +/// Verifies, with the given verifier options, that a buffer of +/// bytes contains a size prefixed `Monster` and returns +/// it. Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_monster_unchecked`. +pub fn size_prefixed_root_as_monster_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result<Monster<'b>, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root_with_opts::<Monster<'b>>(opts, buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a Monster and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid `Monster`. +pub unsafe fn root_as_monster_unchecked(buf: &[u8]) -> Monster { + flatbuffers::root_unchecked::<Monster>(buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a size prefixed Monster and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid size prefixed `Monster`. +pub unsafe fn size_prefixed_root_as_monster_unchecked(buf: &[u8]) -> Monster { + flatbuffers::size_prefixed_root_unchecked::<Monster>(buf) +} pub const MONSTER_IDENTIFIER: &str = "MONS"; #[inline] diff --git a/tests/namespace_test/namespace_test1_generated.rs b/tests/namespace_test/namespace_test1_generated.rs index dd735a60..b5f062b1 100644 --- a/tests/namespace_test/namespace_test1_generated.rs +++ b/tests/namespace_test/namespace_test1_generated.rs @@ -77,7 +77,8 @@ impl<'a> flatbuffers::Follow<'a> for EnumInNestedNS { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<i8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<i8>(buf, loc); + Self(b) } } @@ -92,14 +93,27 @@ impl flatbuffers::Push for EnumInNestedNS { impl flatbuffers::EndianScalar for EnumInNestedNS { #[inline] fn to_little_endian(self) -> Self { - Self(i8::to_le(self.0)) + let b = i8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(i8::from_le(self.0)) + let b = i8::from_le(self.0); + Self(b) } } +impl<'a> flatbuffers::Verifiable for EnumInNestedNS { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for EnumInNestedNS {} // struct StructInNestedNS, aligned to 4 #[repr(C, align(4))] #[derive(Clone, Copy, PartialEq)] @@ -116,6 +130,7 @@ impl std::fmt::Debug for StructInNestedNS { } } +impl flatbuffers::SimpleToVerifyInSlice for StructInNestedNS {} impl flatbuffers::SafeSliceAccess for StructInNestedNS {} impl<'a> flatbuffers::Follow<'a> for StructInNestedNS { type Inner = &'a StructInNestedNS; @@ -153,7 +168,15 @@ impl<'b> flatbuffers::Push for &'b StructInNestedNS { } } - +impl<'a> flatbuffers::Verifiable for StructInNestedNS { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.in_buffer::<Self>(pos) + } +} impl StructInNestedNS { pub fn new(_a: i32, _b: i32) -> Self { StructInNestedNS { @@ -217,6 +240,18 @@ impl<'a> TableInNestedNS<'a> { } } +impl flatbuffers::Verifiable for TableInNestedNS<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<i32>(&"foo", Self::VT_FOO, false)? + .finish(); + Ok(()) + } +} pub struct TableInNestedNSArgs { pub foo: i32, } diff --git a/tests/namespace_test/namespace_test2_generated.rs b/tests/namespace_test/namespace_test2_generated.rs index b1d84cf0..64120050 100644 --- a/tests/namespace_test/namespace_test2_generated.rs +++ b/tests/namespace_test/namespace_test2_generated.rs @@ -63,7 +63,7 @@ impl<'a> TableInFirstNS<'a> { #[inline] pub fn foo_table(&self) -> Option<namespace_b::TableInNestedNS<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<namespace_b::TableInNestedNS<'a>>>(TableInFirstNS::VT_FOO_TABLE, None) + self._tab.get::<flatbuffers::ForwardsUOffset<namespace_b::TableInNestedNS>>(TableInFirstNS::VT_FOO_TABLE, None) } #[inline] pub fn foo_enum(&self) -> namespace_b::EnumInNestedNS { @@ -75,6 +75,20 @@ impl<'a> TableInFirstNS<'a> { } } +impl flatbuffers::Verifiable for TableInFirstNS<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<flatbuffers::ForwardsUOffset<namespace_b::TableInNestedNS>>(&"foo_table", Self::VT_FOO_TABLE, false)? + .visit_field::<namespace_b::EnumInNestedNS>(&"foo_enum", Self::VT_FOO_ENUM, false)? + .visit_field::<namespace_b::StructInNestedNS>(&"foo_struct", Self::VT_FOO_STRUCT, false)? + .finish(); + Ok(()) + } +} pub struct TableInFirstNSArgs<'a> { pub foo_table: Option<flatbuffers::WIPOffset<namespace_b::TableInNestedNS<'a>>>, pub foo_enum: namespace_b::EnumInNestedNS, @@ -170,10 +184,22 @@ impl<'a> SecondTableInA<'a> { #[inline] pub fn refer_to_c(&self) -> Option<super::namespace_c::TableInC<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<super::namespace_c::TableInC<'a>>>(SecondTableInA::VT_REFER_TO_C, None) + self._tab.get::<flatbuffers::ForwardsUOffset<super::namespace_c::TableInC>>(SecondTableInA::VT_REFER_TO_C, None) } } +impl flatbuffers::Verifiable for SecondTableInA<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<flatbuffers::ForwardsUOffset<super::namespace_c::TableInC>>(&"refer_to_c", Self::VT_REFER_TO_C, false)? + .finish(); + Ok(()) + } +} pub struct SecondTableInAArgs<'a> { pub refer_to_c: Option<flatbuffers::WIPOffset<super::namespace_c::TableInC<'a>>>, } @@ -269,14 +295,27 @@ impl<'a> TableInC<'a> { #[inline] pub fn refer_to_a1(&self) -> Option<super::namespace_a::TableInFirstNS<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<super::namespace_a::TableInFirstNS<'a>>>(TableInC::VT_REFER_TO_A1, None) + self._tab.get::<flatbuffers::ForwardsUOffset<super::namespace_a::TableInFirstNS>>(TableInC::VT_REFER_TO_A1, None) } #[inline] pub fn refer_to_a2(&self) -> Option<super::namespace_a::SecondTableInA<'a>> { - self._tab.get::<flatbuffers::ForwardsUOffset<super::namespace_a::SecondTableInA<'a>>>(TableInC::VT_REFER_TO_A2, None) + self._tab.get::<flatbuffers::ForwardsUOffset<super::namespace_a::SecondTableInA>>(TableInC::VT_REFER_TO_A2, None) } } +impl flatbuffers::Verifiable for TableInC<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<flatbuffers::ForwardsUOffset<super::namespace_a::TableInFirstNS>>(&"refer_to_a1", Self::VT_REFER_TO_A1, false)? + .visit_field::<flatbuffers::ForwardsUOffset<super::namespace_a::SecondTableInA>>(&"refer_to_a2", Self::VT_REFER_TO_A2, false)? + .finish(); + Ok(()) + } +} pub struct TableInCArgs<'a> { pub refer_to_a1: Option<flatbuffers::WIPOffset<super::namespace_a::TableInFirstNS<'a>>>, pub refer_to_a2: Option<flatbuffers::WIPOffset<super::namespace_a::SecondTableInA<'a>>>, diff --git a/tests/optional_scalars_generated.rs b/tests/optional_scalars_generated.rs index 793a8ac4..08468d85 100644 --- a/tests/optional_scalars_generated.rs +++ b/tests/optional_scalars_generated.rs @@ -69,7 +69,8 @@ impl<'a> flatbuffers::Follow<'a> for OptionalByte { type Inner = Self; #[inline] fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self(flatbuffers::read_scalar_at::<i8>(buf, loc)) + let b = flatbuffers::read_scalar_at::<i8>(buf, loc); + Self(b) } } @@ -84,14 +85,27 @@ impl flatbuffers::Push for OptionalByte { impl flatbuffers::EndianScalar for OptionalByte { #[inline] fn to_little_endian(self) -> Self { - Self(i8::to_le(self.0)) + let b = i8::to_le(self.0); + Self(b) } #[inline] fn from_little_endian(self) -> Self { - Self(i8::from_le(self.0)) + let b = i8::from_le(self.0); + Self(b) } } +impl<'a> flatbuffers::Verifiable for OptionalByte { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for OptionalByte {} pub enum ScalarStuffOffset {} #[derive(Copy, Clone, PartialEq)] @@ -341,6 +355,53 @@ impl<'a> ScalarStuff<'a> { } } +impl flatbuffers::Verifiable for ScalarStuff<'_> { + #[inline] + fn run_verifier<'o, 'b>( + v: &mut flatbuffers::Verifier<'o, 'b>, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::<i8>(&"just_i8", Self::VT_JUST_I8, false)? + .visit_field::<i8>(&"maybe_i8", Self::VT_MAYBE_I8, false)? + .visit_field::<i8>(&"default_i8", Self::VT_DEFAULT_I8, false)? + .visit_field::<u8>(&"just_u8", Self::VT_JUST_U8, false)? + .visit_field::<u8>(&"maybe_u8", Self::VT_MAYBE_U8, false)? + .visit_field::<u8>(&"default_u8", Self::VT_DEFAULT_U8, false)? + .visit_field::<i16>(&"just_i16", Self::VT_JUST_I16, false)? + .visit_field::<i16>(&"maybe_i16", Self::VT_MAYBE_I16, false)? + .visit_field::<i16>(&"default_i16", Self::VT_DEFAULT_I16, false)? + .visit_field::<u16>(&"just_u16", Self::VT_JUST_U16, false)? + .visit_field::<u16>(&"maybe_u16", Self::VT_MAYBE_U16, false)? + .visit_field::<u16>(&"default_u16", Self::VT_DEFAULT_U16, false)? + .visit_field::<i32>(&"just_i32", Self::VT_JUST_I32, false)? + .visit_field::<i32>(&"maybe_i32", Self::VT_MAYBE_I32, false)? + .visit_field::<i32>(&"default_i32", Self::VT_DEFAULT_I32, false)? + .visit_field::<u32>(&"just_u32", Self::VT_JUST_U32, false)? + .visit_field::<u32>(&"maybe_u32", Self::VT_MAYBE_U32, false)? + .visit_field::<u32>(&"default_u32", Self::VT_DEFAULT_U32, false)? + .visit_field::<i64>(&"just_i64", Self::VT_JUST_I64, false)? + .visit_field::<i64>(&"maybe_i64", Self::VT_MAYBE_I64, false)? + .visit_field::<i64>(&"default_i64", Self::VT_DEFAULT_I64, false)? + .visit_field::<u64>(&"just_u64", Self::VT_JUST_U64, false)? + .visit_field::<u64>(&"maybe_u64", Self::VT_MAYBE_U64, false)? + .visit_field::<u64>(&"default_u64", Self::VT_DEFAULT_U64, false)? + .visit_field::<f32>(&"just_f32", Self::VT_JUST_F32, false)? + .visit_field::<f32>(&"maybe_f32", Self::VT_MAYBE_F32, false)? + .visit_field::<f32>(&"default_f32", Self::VT_DEFAULT_F32, false)? + .visit_field::<f64>(&"just_f64", Self::VT_JUST_F64, false)? + .visit_field::<f64>(&"maybe_f64", Self::VT_MAYBE_F64, false)? + .visit_field::<f64>(&"default_f64", Self::VT_DEFAULT_F64, false)? + .visit_field::<bool>(&"just_bool", Self::VT_JUST_BOOL, false)? + .visit_field::<bool>(&"maybe_bool", Self::VT_MAYBE_BOOL, false)? + .visit_field::<bool>(&"default_bool", Self::VT_DEFAULT_BOOL, false)? + .visit_field::<OptionalByte>(&"just_enum", Self::VT_JUST_ENUM, false)? + .visit_field::<OptionalByte>(&"maybe_enum", Self::VT_MAYBE_ENUM, false)? + .visit_field::<OptionalByte>(&"default_enum", Self::VT_DEFAULT_ENUM, false)? + .finish(); + Ok(()) + } +} pub struct ScalarStuffArgs { pub just_i8: i8, pub maybe_i8: Option<i8>, @@ -629,15 +690,77 @@ impl std::fmt::Debug for ScalarStuff<'_> { } } #[inline] +#[deprecated(since="1.13", note="Deprecated in favor of `root_as...` methods.")] pub fn get_root_as_scalar_stuff<'a>(buf: &'a [u8]) -> ScalarStuff<'a> { - flatbuffers::get_root::<ScalarStuff<'a>>(buf) + unsafe { flatbuffers::root_unchecked::<ScalarStuff<'a>>(buf) } } #[inline] +#[deprecated(since="1.13", note="Deprecated in favor of `root_as...` methods.")] pub fn get_size_prefixed_root_as_scalar_stuff<'a>(buf: &'a [u8]) -> ScalarStuff<'a> { - flatbuffers::get_size_prefixed_root::<ScalarStuff<'a>>(buf) + unsafe { flatbuffers::size_prefixed_root_unchecked::<ScalarStuff<'a>>(buf) } } +#[inline] +/// Verifies that a buffer of bytes contains a `ScalarStuff` +/// and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_scalar_stuff_unchecked`. +pub fn root_as_scalar_stuff(buf: &[u8]) -> Result<ScalarStuff, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root::<ScalarStuff>(buf) +} +#[inline] +/// Verifies that a buffer of bytes contains a size prefixed +/// `ScalarStuff` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `size_prefixed_root_as_scalar_stuff_unchecked`. +pub fn size_prefixed_root_as_scalar_stuff(buf: &[u8]) -> Result<ScalarStuff, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root::<ScalarStuff>(buf) +} +#[inline] +/// Verifies, with the given options, that a buffer of bytes +/// contains a `ScalarStuff` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_scalar_stuff_unchecked`. +pub fn root_as_scalar_stuff_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result<ScalarStuff<'b>, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root_with_opts::<ScalarStuff<'b>>(opts, buf) +} +#[inline] +/// Verifies, with the given verifier options, that a buffer of +/// bytes contains a size prefixed `ScalarStuff` and returns +/// it. Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_scalar_stuff_unchecked`. +pub fn size_prefixed_root_as_scalar_stuff_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result<ScalarStuff<'b>, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root_with_opts::<ScalarStuff<'b>>(opts, buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a ScalarStuff and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid `ScalarStuff`. +pub unsafe fn root_as_scalar_stuff_unchecked(buf: &[u8]) -> ScalarStuff { + flatbuffers::root_unchecked::<ScalarStuff>(buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a size prefixed ScalarStuff and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid size prefixed `ScalarStuff`. +pub unsafe fn size_prefixed_root_as_scalar_stuff_unchecked(buf: &[u8]) -> ScalarStuff { + flatbuffers::size_prefixed_root_unchecked::<ScalarStuff>(buf) +} pub const SCALAR_STUFF_IDENTIFIER: &str = "NULL"; #[inline] diff --git a/tests/rust_usage_test/bin/flatbuffers_alloc_check.rs b/tests/rust_usage_test/bin/flatbuffers_alloc_check.rs index c47e86e1..a02609ab 100644 --- a/tests/rust_usage_test/bin/flatbuffers_alloc_check.rs +++ b/tests/rust_usage_test/bin/flatbuffers_alloc_check.rs @@ -131,7 +131,7 @@ fn main() { // do many reads, forcing them to execute by using assert_eq: { - let m = my_game::example::get_root_as_monster(buf); + let m = unsafe { my_game::example::root_as_monster_unchecked(buf) }; assert_eq!(80, m.hp()); assert_eq!(150, m.mana()); assert_eq!("MyMonster", m.name()); diff --git a/tests/rust_usage_test/bin/monster_example.rs b/tests/rust_usage_test/bin/monster_example.rs index d0b75d72..a3c4300d 100644 --- a/tests/rust_usage_test/bin/monster_example.rs +++ b/tests/rust_usage_test/bin/monster_example.rs @@ -20,7 +20,7 @@ fn main() { let mut buf = Vec::new(); f.read_to_end(&mut buf).expect("file reading failed"); - let monster = my_game::example::get_root_as_monster(&buf[..]); + let monster = my_game::example::root_as_monster(&buf[..]).unwrap(); println!("{}", monster.hp()); // `80` println!("{}", monster.mana()); // default value of `150` println!("{:?}", monster.name()); // Some("MyMonster") diff --git a/tests/rust_usage_test/tests/integration_test.rs b/tests/rust_usage_test/tests/integration_test.rs index 5957e2c1..4f047604 100644 --- a/tests/rust_usage_test/tests/integration_test.rs +++ b/tests/rust_usage_test/tests/integration_test.rs @@ -177,9 +177,9 @@ fn serialized_example_is_accessible_and_correct(bytes: &[u8], identifier_require } let m = if size_prefixed { - my_game::example::get_size_prefixed_root_as_monster(bytes) + my_game::example::size_prefixed_root_as_monster(bytes).unwrap() } else { - my_game::example::get_root_as_monster(bytes) + my_game::example::root_as_monster(bytes).unwrap() }; check_eq!(m.hp(), 80)?; @@ -245,6 +245,106 @@ fn builder_collapses_into_vec() { serialized_example_is_accessible_and_correct(&backing_buf[head..], true, false).unwrap(); } +#[test] +fn verifier_one_byte_errors_do_not_crash() { + let mut b = flatbuffers::FlatBufferBuilder::new(); + create_serialized_example_with_library_code(&mut b); + let mut badbuf = b.finished_data().to_vec(); + // If the verifier says a buffer is okay then using it won't cause a crash. + // We use write_fmt since Debug visits all the fields - but there's no need to store anything. + struct ForgetfulWriter; + use std::fmt::Write; + impl Write for ForgetfulWriter { + fn write_str(&mut self, _: &str) -> Result<(), std::fmt::Error> { + Ok(()) + } + } + let mut w = ForgetfulWriter; + for d in 1..=255u8 { + for i in 0..badbuf.len() { + let orig = badbuf[i]; + badbuf[i] = badbuf[i].wrapping_add(d); + if let Ok(m) = flatbuffers::root::<my_game::example::Monster>(&badbuf) { + w.write_fmt(format_args!("{:?}", m)).unwrap() + } + badbuf[i] = orig; + } + } +} +#[test] +fn verifier_too_many_tables() { + use my_game::example::*; + let b = &mut flatbuffers::FlatBufferBuilder::new(); + let r = Referrable::create(b, &ReferrableArgs { id: 42 }); + let rs = b.create_vector(&vec![r; 500]); + let name = Some(b.create_string("foo")); + let m = Monster::create(b, &MonsterArgs { + vector_of_referrables: Some(rs), + name, // required field. + ..Default::default() + }); + b.finish(m, None); + + let data = b.finished_data(); + let mut opts = flatbuffers::VerifierOptions::default(); + + opts.max_tables = 500; + let res = flatbuffers::root_with_opts::<Monster>(&opts, data); + assert_eq!(res.unwrap_err(), flatbuffers::InvalidFlatbuffer::TooManyTables); + + opts.max_tables += 2; + assert!(flatbuffers::root_with_opts::<Monster>(&opts, data).is_ok()); +} +#[test] +fn verifier_apparent_size_too_large() { + use my_game::example::*; + let b = &mut flatbuffers::FlatBufferBuilder::new(); + let name = Some(b.create_string("foo")); + // String amplification attack. + let s = b.create_string(&(std::iter::repeat("X").take(1000).collect::<String>())); + let testarrayofstring = Some(b.create_vector(&vec![s; 1000])); + let m = Monster::create(b, &MonsterArgs { + testarrayofstring, + name, // required field. + ..Default::default() + }); + b.finish(m, None); + let data = b.finished_data(); + assert!(data.len() < 5100); // est 4000 for the vector + 1000 for the string + 100 overhead. + let mut opts = flatbuffers::VerifierOptions::default(); + opts.max_apparent_size = 1_000_000; + + let res = flatbuffers::root_with_opts::<Monster>(&opts, data); + assert_eq!(res.unwrap_err(), flatbuffers::InvalidFlatbuffer::ApparentSizeTooLarge); + + opts.max_apparent_size += 20_000; + assert!(flatbuffers::root_with_opts::<Monster>(&opts, data).is_ok()); +} +#[test] +fn verifier_in_too_deep() { + use my_game::example::*; + let b = &mut flatbuffers::FlatBufferBuilder::new(); + let name = Some(b.create_string("foo")); + let mut prev_monster = None; + for _ in 0..11 { + prev_monster = Some(Monster::create(b, &MonsterArgs { + enemy: prev_monster, + name, // required field. + ..Default::default() + })); + }; + b.finish(prev_monster.unwrap(), None); + let mut opts = flatbuffers::VerifierOptions::default(); + opts.max_depth = 10; + + let data = b.finished_data(); + let res = flatbuffers::root_with_opts::<Monster>(&opts, data); + assert_eq!(res.unwrap_err(), flatbuffers::InvalidFlatbuffer::DepthLimitReached); + + opts.max_depth += 1; + assert!(flatbuffers::root_with_opts::<Monster>(&opts, data).is_ok()); +} + #[cfg(test)] mod generated_constants { extern crate flatbuffers; @@ -316,7 +416,7 @@ mod lifetime_correctness { let slice: &[u8] = &buf; let slice: &'static [u8] = unsafe { mem::transmute(slice) }; // make sure values retrieved from the 'static buffer are themselves 'static - let monster: my_game::example::Monster<'static> = my_game::example::get_root_as_monster(slice); + let monster: my_game::example::Monster<'static> = my_game::example::root_as_monster(slice).unwrap(); // this line should compile: let name: Option<&'static str> = monster._tab.get::<flatbuffers::ForwardsUOffset<&str>>(my_game::example::Monster::VT_NAME, None); assert_eq!(name, Some("MyMonster")); @@ -334,7 +434,7 @@ mod lifetime_correctness { fn table_object_self_lifetime_in_closure() { // This test is designed to ensure that lifetimes for temporary intermediate tables aren't inflated beyond where the need to be. let buf = load_file("../monsterdata_test.mon").expect("missing monsterdata_test.mon"); - let monster = my_game::example::get_root_as_monster(&buf); + let monster = my_game::example::root_as_monster(&buf).unwrap(); let enemy: Option<my_game::example::Monster> = monster.enemy(); // This line won't compile if "self" is required to live for the lifetime of buf above as the borrow disappears at the end of the closure. let enemy_of_my_enemy = enemy.map(|e| { @@ -357,7 +457,7 @@ mod roundtrip_generated_code { fn build_mon<'a, 'b>(builder: &'a mut flatbuffers::FlatBufferBuilder, args: &'b my_game::example::MonsterArgs) -> my_game::example::Monster<'a> { let mon = my_game::example::Monster::create(builder, &args); my_game::example::finish_monster_buffer(builder, mon); - my_game::example::get_root_as_monster(builder.finished_data()) + my_game::example::root_as_monster(builder.finished_data()).unwrap() } #[test] @@ -437,7 +537,7 @@ mod roundtrip_generated_code { my_game::example::finish_monster_buffer(b, outer); } - let mon = my_game::example::get_root_as_monster(b.finished_data()); + let mon = my_game::example::root_as_monster(b.finished_data()).unwrap(); assert_eq!(mon.name(), "bar"); assert_eq!(mon.test_type(), my_game::example::Any::Monster); assert_eq!(my_game::example::Monster::init_from_table(mon.test().unwrap()).name(), @@ -473,7 +573,7 @@ mod roundtrip_generated_code { my_game::example::finish_monster_buffer(b, outer); } - let mon = my_game::example::get_root_as_monster(b.finished_data()); + let mon = my_game::example::root_as_monster(b.finished_data()).unwrap(); assert_eq!(mon.name(), "bar"); assert_eq!(mon.enemy().unwrap().name(), "foo"); } @@ -503,7 +603,7 @@ mod roundtrip_generated_code { my_game::example::finish_monster_buffer(b, outer); } - let mon = my_game::example::get_root_as_monster(b.finished_data()); + let mon = my_game::example::root_as_monster(b.finished_data()).unwrap(); assert_eq!(mon.name(), "bar"); assert_eq!(mon.testempty().unwrap().id(), Some("foo")); } @@ -540,12 +640,12 @@ mod roundtrip_generated_code { b1 }; - let m = my_game::example::get_root_as_monster(b1.finished_data()); + let m = my_game::example::root_as_monster(b1.finished_data()).unwrap(); assert!(m.testnestedflatbuffer().is_some()); assert_eq!(m.testnestedflatbuffer().unwrap(), b0.finished_data()); - let m2_a = my_game::example::get_root_as_monster(m.testnestedflatbuffer().unwrap()); + let m2_a = my_game::example::root_as_monster(m.testnestedflatbuffer().unwrap()).unwrap(); assert_eq!(m2_a.hp(), 123); assert_eq!(m2_a.name(), "foobar"); @@ -799,7 +899,7 @@ mod generated_code_alignment_and_padding { my_game::example::finish_monster_buffer(b, mon); } let buf = b.finished_data(); - let mon = my_game::example::get_root_as_monster(buf); + let mon = my_game::example::root_as_monster(buf).unwrap(); let vec3 = mon.pos().unwrap(); let start_ptr = buf.as_ptr() as usize; @@ -835,7 +935,7 @@ mod generated_code_alignment_and_padding { my_game::example::finish_monster_buffer(b, mon); } let buf = b.finished_data(); - let mon = my_game::example::get_root_as_monster(buf); + let mon = my_game::example::root_as_monster(buf).unwrap(); let abilities = mon.testarrayofsortedstruct().unwrap(); let start_ptr = buf.as_ptr() as usize; @@ -1142,7 +1242,7 @@ mod framing_format { // Access it. let buf = b.finished_data(); - let m = flatbuffers::get_size_prefixed_root::<my_game::example::Monster>(buf); + let m = flatbuffers::size_prefixed_root::<my_game::example::Monster>(buf).unwrap(); assert_eq!(m.mana(), 200); assert_eq!(m.hp(), 300); assert_eq!(m.name(), "bob"); @@ -1538,7 +1638,7 @@ mod write_and_read_examples { create_serialized_example_with_generated_code(b); let buf = b.finished_data(); serialized_example_is_accessible_and_correct(&buf, true, false).unwrap(); - let m = super::my_game::example::get_root_as_monster(buf); + let m = super::my_game::example::root_as_monster(buf).unwrap(); assert_eq!( format!("{:.5?}", &m), "Monster { pos: Some(Vec3 { x: 1.00000, y: 2.00000, z: 3.00000, \ @@ -1728,7 +1828,7 @@ mod generated_key_comparisons { let builder = &mut flatbuffers::FlatBufferBuilder::new(); super::create_serialized_example_with_library_code(builder); let buf = builder.finished_data(); - let a = my_game::example::get_root_as_monster(buf); + let a = my_game::example::root_as_monster(buf).unwrap(); // preconditions assert_eq!(a.name(), "MyMonster"); @@ -1744,7 +1844,7 @@ mod generated_key_comparisons { let builder = &mut flatbuffers::FlatBufferBuilder::new(); super::create_serialized_example_with_library_code(builder); let buf = builder.finished_data(); - let a = my_game::example::get_root_as_monster(buf); + let a = my_game::example::root_as_monster(buf).unwrap(); let b = a.test_as_monster().unwrap(); // preconditions diff --git a/tests/rust_usage_test/tests/optional_scalars_test.rs b/tests/rust_usage_test/tests/optional_scalars_test.rs index f029d032..5a662414 100644 --- a/tests/rust_usage_test/tests/optional_scalars_test.rs +++ b/tests/rust_usage_test/tests/optional_scalars_test.rs @@ -27,13 +27,13 @@ macro_rules! make_test { ); builder.finish(ss, None); - let s = flatbuffers::get_root::<ScalarStuff>(builder.finished_data()); + let s = flatbuffers::root::<ScalarStuff>(builder.finished_data()).unwrap(); assert_eq!(s.$just(), $five); assert_eq!(s.$default(), $five); assert_eq!(s.$maybe(), Some($five)); // Test defaults are used when not specified. - let s = flatbuffers::get_root::<ScalarStuff>(&[0; 8]); + let s = flatbuffers::root::<ScalarStuff>(&[0; 8]).unwrap(); assert_eq!(s.$just(), $zero); assert_eq!(s.$default(), $fortytwo); assert_eq!(s.$maybe(), None); |