diff --git a/rust/flatbuffers/src/lib.rs b/rust/flatbuffers/src/lib.rs index 1ded368171c..9ed308024fb 100644 --- a/rust/flatbuffers/src/lib.rs +++ b/rust/flatbuffers/src/lib.rs @@ -56,7 +56,7 @@ pub use crate::push::{Push, PushAlignment}; pub use crate::table::{buffer_has_identifier, Table}; pub use crate::vector::{follow_cast_ref, Vector, VectorIter}; pub use crate::verifier::{ - ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, Verifiable, Verifier, + ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, TableVerifier, Verifiable, Verifier, VerifierOptions, }; pub use crate::vtable::field_index_to_field_offset; diff --git a/rust/flatbuffers/src/vector.rs b/rust/flatbuffers/src/vector.rs index b486ff33c01..e1569510c85 100644 --- a/rust/flatbuffers/src/vector.rs +++ b/rust/flatbuffers/src/vector.rs @@ -14,6 +14,7 @@ * limitations under the License. */ +use core::cmp::Ordering; use core::fmt::{Debug, Formatter, Result}; use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator}; use core::marker::PhantomData; @@ -102,6 +103,37 @@ impl<'a, T: Follow<'a> + 'a> Vector<'a, T> { unsafe { T::follow(self.0, self.1 as usize + SIZE_UOFFSET + sz * idx) } } + #[inline(always)] + pub fn lookup_by_key( + &self, + key: K, + f: fn(&>::Inner, &K) -> Ordering, + ) -> Option { + if self.is_empty() { + return None; + } + + let mut left: usize = 0; + let mut right = self.len() - 1; + + while left <= right { + let mid = (left + right) / 2; + let value = self.get(mid); + match f(&value, &key) { + Ordering::Equal => return Some(value), + Ordering::Less => left = mid + 1, + Ordering::Greater => { + if mid == 0 { + return None; + } + right = mid - 1; + }, + } + } + + None + } + #[inline(always)] pub fn iter(&self) -> VectorIter<'a, T> { VectorIter::from_vector(*self) diff --git a/rust/flatbuffers/src/verifier.rs b/rust/flatbuffers/src/verifier.rs index 047d4f61360..c4c55f587d0 100644 --- a/rust/flatbuffers/src/verifier.rs +++ b/rust/flatbuffers/src/verifier.rs @@ -5,6 +5,11 @@ use alloc::vec::Vec; use core::ops::Range; use core::option::Option; +#[cfg(not(feature = "std"))] +use alloc::borrow::Cow; +#[cfg(feature = "std")] +use std::borrow::Cow; + #[cfg(all(nightly, not(feature = "std")))] use core::error::Error; #[cfg(feature = "std")] @@ -20,11 +25,11 @@ pub enum ErrorTraceDetail { position: usize, }, TableField { - field_name: &'static str, + field_name: Cow<'static, str>, position: usize, }, UnionVariant { - variant: &'static str, + variant: Cow<'static, str>, position: usize, }, } @@ -44,12 +49,12 @@ impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace { #[derive(Clone, Debug, PartialEq, Eq)] pub enum InvalidFlatbuffer { MissingRequiredField { - required: &'static str, + required: Cow<'static, str>, error_trace: ErrorTrace, }, InconsistentUnion { - field: &'static str, - field_type: &'static str, + field: Cow<'static, str>, + field_type: Cow<'static, str>, error_trace: ErrorTrace, }, Utf8Error { @@ -63,7 +68,7 @@ pub enum InvalidFlatbuffer { }, Unaligned { position: usize, - unaligned_type: &'static str, + unaligned_type: Cow<'static, str>, error_trace: ErrorTrace, }, RangeOutOfBounds { @@ -217,16 +222,19 @@ impl InvalidFlatbuffer { error_trace: Default::default(), }) } - fn new_inconsistent_union(field: &'static str, field_type: &'static str) -> Result { + pub fn new_inconsistent_union( + field: impl Into>, + field_type: impl Into>, + ) -> Result { Err(Self::InconsistentUnion { - field, - field_type, + field: field.into(), + field_type: field_type.into(), error_trace: Default::default(), }) } - fn new_missing_required(required: &'static str) -> Result { + pub fn new_missing_required(required: impl Into>) -> Result { Err(Self::MissingRequiredField { - required, + required: required.into(), error_trace: Default::default(), }) } @@ -251,7 +259,7 @@ fn append_trace(mut res: Result, d: ErrorTraceDetail) -> Result { } /// Adds a TableField trace detail if `res` is a data error. -fn trace_field(res: Result, field_name: &'static str, position: usize) -> Result { +fn trace_field(res: Result, field_name: Cow<'static, str>, position: usize) -> Result { append_trace( res, ErrorTraceDetail::TableField { @@ -333,19 +341,19 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> { /// /// Note this does not impact soundness as this crate does not assume alignment of structs #[inline] - fn is_aligned(&self, pos: usize) -> Result<()> { + pub fn is_aligned(&self, pos: usize) -> Result<()> { if pos % core::mem::align_of::() == 0 { Ok(()) } else { Err(InvalidFlatbuffer::Unaligned { - unaligned_type: core::any::type_name::(), + unaligned_type: Cow::Borrowed(core::any::type_name::()), position: pos, error_trace: Default::default(), }) } } #[inline] - fn range_in_buffer(&mut self, pos: usize, size: usize) -> Result<()> { + pub 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); @@ -363,12 +371,17 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> { self.range_in_buffer(pos, core::mem::size_of::()) } #[inline] + pub fn get_u8(&mut self, pos: usize) -> Result { + self.in_buffer::(pos)?; + Ok(u8::from_le_bytes([self.buffer[pos]])) + } + #[inline] fn get_u16(&mut self, pos: usize) -> Result { self.in_buffer::(pos)?; Ok(u16::from_le_bytes([self.buffer[pos], self.buffer[pos + 1]])) } #[inline] - fn get_uoffset(&mut self, pos: usize) -> Result { + pub fn get_uoffset(&mut self, pos: usize) -> Result { self.in_buffer::(pos)?; Ok(u32::from_le_bytes([ self.buffer[pos], @@ -434,11 +447,17 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> { /// tracing the error. pub fn verify_union_variant( &mut self, - variant: &'static str, + variant: impl Into>, position: usize, ) -> Result<()> { let res = T::run_verifier(self, position); - append_trace(res, ErrorTraceDetail::UnionVariant { variant, position }) + append_trace( + res, + ErrorTraceDetail::UnionVariant { + variant: variant.into(), + position, + }, + ) } } @@ -456,7 +475,7 @@ pub struct TableVerifier<'ver, 'opts, 'buf> { } impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> { - fn deref(&mut self, field: VOffsetT) -> Result> { + pub fn deref(&mut self, field: VOffsetT) -> Result> { let field = field as usize; if field < self.vtable_len { let field_offset = self.verifier.get_u16(self.vtable.saturating_add(field))?; @@ -469,23 +488,28 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> { Ok(None) } + #[inline] + pub fn verifier(&mut self) -> &mut Verifier<'opts, 'buf> { + self.verifier + } + #[inline] pub fn visit_field( mut self, - field_name: &'static str, + field_name: impl Into>, field: VOffsetT, required: bool, ) -> Result { if let Some(field_pos) = self.deref(field)? { trace_field( T::run_verifier(self.verifier, field_pos), - field_name, + field_name.into(), field_pos, )?; return Ok(self); } if required { - InvalidFlatbuffer::new_missing_required(field_name) + InvalidFlatbuffer::new_missing_required(field_name.into()) } else { Ok(self) } @@ -496,9 +520,9 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> { /// reads the key, then invokes the callback to perform data-dependent verification. pub fn visit_union( mut self, - key_field_name: &'static str, + key_field_name: impl Into>, key_field_voff: VOffsetT, - val_field_name: &'static str, + val_field_name: impl Into>, val_field_voff: VOffsetT, required: bool, verify_union: UnionVerifier, @@ -515,24 +539,28 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> { match (key_pos, val_pos) { (None, None) => { if required { - InvalidFlatbuffer::new_missing_required(val_field_name) + InvalidFlatbuffer::new_missing_required(val_field_name.into()) } else { Ok(self) } } (Some(k), Some(v)) => { - trace_field(Key::run_verifier(self.verifier, k), key_field_name, k)?; + trace_field( + Key::run_verifier(self.verifier, k), + key_field_name.into(), + k, + )?; // Safety: // Run verifier on `k` above let discriminant = unsafe { Key::follow(self.verifier.buffer, k) }; trace_field( verify_union(discriminant, self.verifier, v), - val_field_name, + val_field_name.into(), v, )?; Ok(self) } - _ => InvalidFlatbuffer::new_inconsistent_union(key_field_name, val_field_name), + _ => InvalidFlatbuffer::new_inconsistent_union(key_field_name.into(), val_field_name.into()), } } pub fn finish(self) -> &'ver mut Verifier<'opts, 'buf> { diff --git a/rust/reflection/.gitignore b/rust/reflection/.gitignore new file mode 100644 index 00000000000..96ef6c0b944 --- /dev/null +++ b/rust/reflection/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/rust/reflection/Cargo.toml b/rust/reflection/Cargo.toml new file mode 100644 index 00000000000..9593d9b2e7b --- /dev/null +++ b/rust/reflection/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "flatbuffers-reflection" +version = "0.1.0" +edition = "2021" + +[dependencies] +flatbuffers = { path = "../flatbuffers"} +escape_string = "0.1.2" +stdint = "0.2.0" +num = "0.4.1" +anyhow = "1.0.75" +thiserror = "1.0" diff --git a/rust/reflection/src/lib.rs b/rust/reflection/src/lib.rs new file mode 100644 index 00000000000..1ca5a5e84e5 --- /dev/null +++ b/rust/reflection/src/lib.rs @@ -0,0 +1,1142 @@ +/* + * Copyright 2018 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. + */ + +mod reflection_generated; +mod reflection_verifier; +mod safe_buffer; +mod r#struct; +pub use crate::r#struct::Struct; +pub use crate::reflection_generated::reflection; +pub use crate::safe_buffer::SafeBuffer; + +use flatbuffers::{ + emplace_scalar, read_scalar, EndianScalar, Follow, ForwardsUOffset, InvalidFlatbuffer, + SOffsetT, Table, UOffsetT, VOffsetT, Vector, SIZE_SOFFSET, SIZE_UOFFSET, +}; +use reflection_generated::reflection::{BaseType, Field, Object, Schema}; + +use core::mem::size_of; +use escape_string::escape; +use num::traits::float::Float; +use num::traits::int::PrimInt; +use num::traits::FromPrimitive; +use stdint::uintmax_t; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum FlatbufferError { + #[error(transparent)] + VerificationError(#[from] flatbuffers::InvalidFlatbuffer), + #[error("Failed to convert between data type {0} and field type {1}")] + FieldTypeMismatch(String, String), + #[error("Set field value not supported for non-populated or non-scalar fields")] + SetValueNotSupported, + #[error(transparent)] + ParseFloatError(#[from] std::num::ParseFloatError), + #[error(transparent)] + TryFromIntError(#[from] std::num::TryFromIntError), + #[error("Couldn't set string because cache vector is polluted")] + SetStringPolluted, + #[error("Invalid schema: Polluted buffer or the schema doesn't match the buffer.")] + InvalidSchema, + #[error("Type not supported: {0}")] + TypeNotSupported(String), + #[error("No type or invalid type found in union enum")] + InvalidUnionEnum, + #[error("Table or Struct doesn't belong to the buffer")] + InvalidTableOrStruct, + #[error("Field not found in the table schema")] + FieldNotFound, +} + +pub type FlatbufferResult = core::result::Result; + +/// Gets the root table from a trusted Flatbuffer. +/// +/// # Safety +/// +/// Flatbuffers accessors do not perform validation checks before accessing. Users +/// must trust [data] contains a valid flatbuffer. Reading unchecked buffers may cause panics or even UB. +pub unsafe fn get_any_root(data: &[u8]) -> Table { + >::follow(data, 0) +} + +/// Gets an integer table field given its exact type. Returns default integer value if the field is not set. Returns [None] if no default value is found. Returns error if the type size doesn't match. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type T +pub unsafe fn get_field_integer Follow<'a, Inner = T> + PrimInt + FromPrimitive>( + table: &Table, + field: &Field, +) -> FlatbufferResult> { + if size_of::() != get_type_size(field.type_().base_type()) { + return Err(FlatbufferError::FieldTypeMismatch( + std::any::type_name::().to_string(), + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + + let default = T::from_i64(field.default_integer()); + Ok(table.get::(field.offset(), default)) +} + +/// Gets a floating point table field given its exact type. Returns default float value if the field is not set. Returns [None] if no default value is found. Returns error if the type doesn't match. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type T +pub unsafe fn get_field_float Follow<'a, Inner = T> + Float>( + table: &Table, + field: &Field, +) -> FlatbufferResult> { + if size_of::() != get_type_size(field.type_().base_type()) { + return Err(FlatbufferError::FieldTypeMismatch( + std::any::type_name::().to_string(), + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + + let default = T::from(field.default_real()); + Ok(table.get::(field.offset(), default)) +} + +/// Gets a String table field given its exact type. Returns empty string if the field is not set. Returns [None] if no default value is found. Returns error if the type size doesn't match. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type String +pub unsafe fn get_field_string<'a>( + table: &Table<'a>, + field: &Field, +) -> FlatbufferResult> { + if field.type_().base_type() != BaseType::String { + return Err(FlatbufferError::FieldTypeMismatch( + String::from("String"), + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + + Ok(table.get::>(field.offset(), Some(""))) +} + +/// Gets a [Struct] table field given its exact type. Returns [None] if the field is not set. Returns error if the type doesn't match. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type Struct +pub unsafe fn get_field_struct<'a>( + table: &Table<'a>, + field: &Field, +) -> FlatbufferResult>> { + // TODO inherited from C++: This does NOT check if the field is a table or struct, but we'd need + // access to the schema to check the is_struct flag. + if field.type_().base_type() != BaseType::Obj { + return Err(FlatbufferError::FieldTypeMismatch( + String::from("Obj"), + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + + Ok(table.get::(field.offset(), None)) +} + +/// Gets a Vector table field given its exact type. Returns empty vector if the field is not set. Returns error if the type doesn't match. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type Vector +pub unsafe fn get_field_vector<'a, T: Follow<'a, Inner = T>>( + table: &Table<'a>, + field: &Field, +) -> FlatbufferResult>> { + if field.type_().base_type() != BaseType::Vector + || core::mem::size_of::() != get_type_size(field.type_().element()) + { + return Err(FlatbufferError::FieldTypeMismatch( + std::any::type_name::().to_string(), + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + + Ok(table.get::>>(field.offset(), Some(Vector::::default()))) +} + +/// Gets a Table table field given its exact type. Returns [None] if the field is not set. Returns error if the type doesn't match. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type Table +pub unsafe fn get_field_table<'a>( + table: &Table<'a>, + field: &Field, +) -> FlatbufferResult>> { + if field.type_().base_type() != BaseType::Obj { + return Err(FlatbufferError::FieldTypeMismatch( + String::from("Obj"), + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + + Ok(table.get::>>(field.offset(), None)) +} + +/// Returns the value of any table field as a 64-bit int, regardless of what type it is. Returns default integer if the field is not set or error if the value cannot be parsed as integer. +/// [num_traits](https://docs.rs/num-traits/latest/num_traits/cast/trait.NumCast.html) is used for number casting. +/// +/// # Safety +/// +/// [table] must contain recursively valid offsets that match the [field]. +pub unsafe fn get_any_field_integer(table: &Table, field: &Field) -> FlatbufferResult { + if let Some(field_loc) = get_field_loc(table, field) { + get_any_value_integer(field.type_().base_type(), table.buf(), field_loc) + } else { + Ok(field.default_integer()) + } +} + +/// Returns the value of any table field as a 64-bit floating point, regardless of what type it is. Returns default float if the field is not set or error if the value cannot be parsed as float. +/// +/// # Safety +/// +/// [table] must contain recursively valid offsets that match the [field]. +pub unsafe fn get_any_field_float(table: &Table, field: &Field) -> FlatbufferResult { + if let Some(field_loc) = get_field_loc(table, field) { + get_any_value_float(field.type_().base_type(), table.buf(), field_loc) + } else { + Ok(field.default_real()) + } +} + +/// Returns the value of any table field as a string, regardless of what type it is. Returns empty string if the field is not set. +/// +/// # Safety +/// +/// [table] must contain recursively valid offsets that match the [field]. +pub unsafe fn get_any_field_string(table: &Table, field: &Field, schema: &Schema) -> String { + if let Some(field_loc) = get_field_loc(table, field) { + get_any_value_string( + field.type_().base_type(), + table.buf(), + field_loc, + schema, + field.type_().index() as usize, + ) + } else { + String::from("") + } +} + +/// Gets a [Struct] struct field given its exact type. Returns error if the type doesn't match. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type Struct. +pub unsafe fn get_field_struct_in_struct<'a>( + st: &Struct<'a>, + field: &Field, +) -> FlatbufferResult> { + // TODO inherited from C++: This does NOT check if the field is a table or struct, but we'd need + // access to the schema to check the is_struct flag. + if field.type_().base_type() != BaseType::Obj { + return Err(FlatbufferError::FieldTypeMismatch( + String::from("Obj"), + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + + Ok(st.get::(field.offset() as usize)) +} + +/// Returns the value of any struct field as a 64-bit int, regardless of what type it is. Returns error if the value cannot be parsed as integer. +/// +/// # Safety +/// +/// [st] must contain valid offsets that match the [field]. +pub unsafe fn get_any_field_integer_in_struct(st: &Struct, field: &Field) -> FlatbufferResult { + let field_loc = st.loc() + field.offset() as usize; + + get_any_value_integer(field.type_().base_type(), st.buf(), field_loc) +} + +/// Returns the value of any struct field as a 64-bit floating point, regardless of what type it is. Returns error if the value cannot be parsed as float. +/// +/// # Safety +/// +/// [st] must contain valid offsets that match the [field]. +pub unsafe fn get_any_field_float_in_struct(st: &Struct, field: &Field) -> FlatbufferResult { + let field_loc = st.loc() + field.offset() as usize; + + get_any_value_float(field.type_().base_type(), st.buf(), field_loc) +} + +/// Returns the value of any struct field as a string, regardless of what type it is. +/// +/// # Safety +/// +/// [st] must contain valid offsets that match the [field]. +pub unsafe fn get_any_field_string_in_struct( + st: &Struct, + field: &Field, + schema: &Schema, +) -> String { + let field_loc = st.loc() + field.offset() as usize; + + get_any_value_string( + field.type_().base_type(), + st.buf(), + field_loc, + schema, + field.type_().index() as usize, + ) +} + +/// Sets any table field with the value of a 64-bit integer. Returns error if the field is not originally set or is with non-scalar value or the provided value cannot be cast into the field type. +/// +/// # Safety +/// +/// [buf] must contain a valid root table and valid offset to it. +pub unsafe fn set_any_field_integer( + buf: &mut [u8], + table_loc: usize, + field: &Field, + v: i64, +) -> FlatbufferResult<()> { + let field_type = field.type_().base_type(); + let table = Table::follow(buf, table_loc); + + let Some(field_loc) = get_field_loc(&table, field) else { + return Err(FlatbufferError::SetValueNotSupported); + }; + + if !is_scalar(field_type) { + return Err(FlatbufferError::SetValueNotSupported); + } + + set_any_value_integer(field_type, buf, field_loc, v) +} + +/// Sets any table field with the value of a 64-bit floating point. Returns error if the field is not originally set or is with non-scalar value or the provided value cannot be cast into the field type. +/// +/// # Safety +/// +/// [buf] must contain a valid root table and valid offset to it. +pub unsafe fn set_any_field_float( + buf: &mut [u8], + table_loc: usize, + field: &Field, + v: f64, +) -> FlatbufferResult<()> { + let field_type = field.type_().base_type(); + let table = Table::follow(buf, table_loc); + + let Some(field_loc) = get_field_loc(&table, field) else { + return Err(FlatbufferError::SetValueNotSupported); + }; + + if !is_scalar(field_type) { + return Err(FlatbufferError::SetValueNotSupported); + } + + set_any_value_float(field_type, buf, field_loc, v) +} + +/// Sets any table field with the value of a string. Returns error if the field is not originally set or is with non-scalar value or the provided value cannot be parsed as the field type. +/// +/// # Safety +/// +/// [buf] must contain a valid root table and valid offset to it. +pub unsafe fn set_any_field_string( + buf: &mut [u8], + table_loc: usize, + field: &Field, + v: &str, +) -> FlatbufferResult<()> { + let field_type = field.type_().base_type(); + let table = Table::follow(buf, table_loc); + + let Some(field_loc) = get_field_loc(&table, field) else { + return Err(FlatbufferError::SetValueNotSupported); + }; + + if !is_scalar(field_type) { + return Err(FlatbufferError::SetValueNotSupported); + } + + set_any_value_float(field_type, buf, field_loc, v.parse::()?) +} + +/// Sets any scalar field given its exact type. Returns error if the field is not originally set or is with non-scalar value. +/// +/// # Safety +/// +/// [buf] must contain a valid root table and valid offset to it. +pub unsafe fn set_field( + buf: &mut [u8], + table_loc: usize, + field: &Field, + v: T, +) -> FlatbufferResult<()> { + let field_type = field.type_().base_type(); + let table = Table::follow(buf, table_loc); + + if !is_scalar(field_type) { + return Err(FlatbufferError::SetValueNotSupported); + } + + if core::mem::size_of::() != get_type_size(field_type) { + return Err(FlatbufferError::FieldTypeMismatch( + std::any::type_name::().to_string(), + field_type.variant_name().unwrap_or_default().to_string(), + )); + } + + let Some(field_loc) = get_field_loc(&table, field) else { + return Err(FlatbufferError::SetValueNotSupported); + }; + + if buf.len() < field_loc.saturating_add(get_type_size(field_type)) { + return Err(FlatbufferError::VerificationError( + InvalidFlatbuffer::RangeOutOfBounds { + range: core::ops::Range { + start: field_loc, + end: field_loc.saturating_add(get_type_size(field_type)), + }, + error_trace: Default::default(), + }, + )); + } + + // SAFETY: the buffer range was verified above. + unsafe { Ok(emplace_scalar::(&mut buf[field_loc..], v)) } +} + +/// Sets a string field to a new value. Returns error if the field is not originally set or is not of string type in which cases the [buf] stays intact. Returns error if the [buf] fails to be updated. +/// +/// # Safety +/// +/// [buf] must contain a valid root table and valid offset to it and conform to the [schema]. +pub unsafe fn set_string( + buf: &mut Vec, + table_loc: usize, + field: &Field, + v: &str, + schema: &Schema, +) -> FlatbufferResult<()> { + if v.is_empty() { + return Ok(()); + } + + let field_type = field.type_().base_type(); + if field_type != BaseType::String { + return Err(FlatbufferError::FieldTypeMismatch( + String::from("String"), + field_type.variant_name().unwrap_or_default().to_string(), + )); + } + + let table = Table::follow(buf, table_loc); + + let Some(field_loc) = get_field_loc(&table, field) else { + return Err(FlatbufferError::SetValueNotSupported); + }; + + if buf.len() < field_loc + get_type_size(field_type) { + return Err(FlatbufferError::VerificationError( + InvalidFlatbuffer::RangeOutOfBounds { + range: core::ops::Range { + start: field_loc, + end: field_loc.saturating_add(get_type_size(field_type)), + }, + error_trace: Default::default(), + }, + )); + } + + // SAFETY: the buffer range was verified above. + let string_loc = unsafe { deref_uoffset(buf, field_loc)? }; + if buf.len() < string_loc.saturating_add(SIZE_UOFFSET) { + return Err(FlatbufferError::VerificationError( + InvalidFlatbuffer::RangeOutOfBounds { + range: core::ops::Range { + start: string_loc, + end: string_loc.saturating_add(SIZE_UOFFSET), + }, + error_trace: Default::default(), + }, + )); + } + + // SAFETY: the buffer range was verified above. + let len_old = unsafe { read_uoffset(buf, string_loc) }; + if buf.len() + < string_loc + .saturating_add(SIZE_UOFFSET) + .saturating_add(len_old.try_into()?) + { + return Err(FlatbufferError::VerificationError( + InvalidFlatbuffer::RangeOutOfBounds { + range: core::ops::Range { + start: string_loc, + end: string_loc + .saturating_add(SIZE_UOFFSET) + .saturating_add(len_old.try_into()?), + }, + error_trace: Default::default(), + }, + )); + } + + let len_new = v.len(); + let delta = len_new as isize - len_old as isize; + let mut bytes_to_insert = v.as_bytes().to_vec(); + + if delta != 0 { + // Rounds the delta up to the nearest multiple of the maximum int size to keep the types after the insersion point aligned. + let mask = (size_of::() - 1) as isize; + let offset = (delta + mask) & !mask; + let mut visited_vec = vec![false; buf.len()]; + + if offset != 0 { + update_offset( + buf, + table_loc, + &mut visited_vec, + &schema.root_table().unwrap(), + schema, + string_loc, + offset, + )?; + + // Sets the new length. + emplace_scalar::( + &mut buf[string_loc..string_loc + SIZE_UOFFSET], + len_new.try_into()?, + ); + } + + // Pads the bytes vector with 0 if `offset` doesn't equal `delta`. + bytes_to_insert.resize(bytes_to_insert.len() + (offset - delta) as usize, 0); + } + + // Replaces the data. + buf.splice( + string_loc + SIZE_SOFFSET..string_loc + SIZE_UOFFSET + usize::try_from(len_old)?, + bytes_to_insert, + ); + Ok(()) +} + +/// Returns the size of a scalar type in the `BaseType` enum. In the case of structs, returns the size of their offset (`UOffsetT`) in the buffer. +fn get_type_size(base_type: BaseType) -> usize { + match base_type { + BaseType::UType | BaseType::Bool | BaseType::Byte | BaseType::UByte => 1, + BaseType::Short | BaseType::UShort => 2, + BaseType::Int + | BaseType::UInt + | BaseType::Float + | BaseType::String + | BaseType::Vector + | BaseType::Obj + | BaseType::Union => 4, + BaseType::Long | BaseType::ULong | BaseType::Double | BaseType::Vector64 => 8, + _ => 0, + } +} + +/// Returns the absolute field location in the buffer and [None] if the field is not populated. +/// +/// # Safety +/// +/// [table] must contain a valid vtable. +unsafe fn get_field_loc(table: &Table, field: &Field) -> Option { + let field_offset = table.vtable().get(field.offset()) as usize; + if field_offset == 0 { + return None; + } + + Some(table.loc() + field_offset) +} + +/// Reads value as a 64-bit int from the provided byte slice at the specified location. Returns error if the value cannot be parsed as integer. +/// +/// # Safety +/// +/// Caller must ensure `buf.len() >= loc + size_of::()` at all the access layers. +unsafe fn get_any_value_integer( + base_type: BaseType, + buf: &[u8], + loc: usize, +) -> FlatbufferResult { + match base_type { + BaseType::UType | BaseType::UByte => i64::from_u8(u8::follow(buf, loc)), + BaseType::Bool => bool::follow(buf, loc).try_into().ok(), + BaseType::Byte => i64::from_i8(i8::follow(buf, loc)), + BaseType::Short => i64::from_i16(i16::follow(buf, loc)), + BaseType::UShort => i64::from_u16(u16::follow(buf, loc)), + BaseType::Int => i64::from_i32(i32::follow(buf, loc)), + BaseType::UInt => i64::from_u32(u32::follow(buf, loc)), + BaseType::Long => Some(i64::follow(buf, loc)), + BaseType::ULong => i64::from_u64(u64::follow(buf, loc)), + BaseType::Float => i64::from_f32(f32::follow(buf, loc)), + BaseType::Double => i64::from_f64(f64::follow(buf, loc)), + BaseType::String => ForwardsUOffset::<&str>::follow(buf, loc) + .parse::() + .ok(), + _ => None, // Tables & vectors do not make sense. + } + .ok_or(FlatbufferError::FieldTypeMismatch( + String::from("i64"), + base_type.variant_name().unwrap_or_default().to_string(), + )) +} + +/// Reads value as a 64-bit floating point from the provided byte slice at the specified location. Returns error if the value cannot be parsed as float. +/// +/// # Safety +/// +/// Caller must ensure `buf.len() >= loc + size_of::()` at all the access layers. +unsafe fn get_any_value_float( + base_type: BaseType, + buf: &[u8], + loc: usize, +) -> FlatbufferResult { + match base_type { + BaseType::UType | BaseType::UByte => f64::from_u8(u8::follow(buf, loc)), + BaseType::Bool => bool::follow(buf, loc).try_into().ok(), + BaseType::Byte => f64::from_i8(i8::follow(buf, loc)), + BaseType::Short => f64::from_i16(i16::follow(buf, loc)), + BaseType::UShort => f64::from_u16(u16::follow(buf, loc)), + BaseType::Int => f64::from_i32(i32::follow(buf, loc)), + BaseType::UInt => f64::from_u32(u32::follow(buf, loc)), + BaseType::Long => f64::from_i64(i64::follow(buf, loc)), + BaseType::ULong => f64::from_u64(u64::follow(buf, loc)), + BaseType::Float => f64::from_f32(f32::follow(buf, loc)), + BaseType::Double => Some(f64::follow(buf, loc)), + BaseType::String => ForwardsUOffset::<&str>::follow(buf, loc) + .parse::() + .ok(), + _ => None, + } + .ok_or(FlatbufferError::FieldTypeMismatch( + String::from("f64"), + base_type.variant_name().unwrap_or_default().to_string(), + )) +} + +/// Reads value as a string from the provided byte slice at the specified location. +/// +/// # Safety +/// +/// Caller must ensure `buf.len() >= loc + size_of::()` at all the access layers. +unsafe fn get_any_value_string( + base_type: BaseType, + buf: &[u8], + loc: usize, + schema: &Schema, + type_index: usize, +) -> String { + match base_type { + BaseType::Float | BaseType::Double => get_any_value_float(base_type, buf, loc) + .unwrap_or_default() + .to_string(), + BaseType::String => { + String::from_utf8_lossy(ForwardsUOffset::<&[u8]>::follow(buf, loc)).to_string() + } + BaseType::Obj => { + // Converts the table to a string. This is mostly for debugging purposes, + // and does NOT promise to be JSON compliant. + // Also prefixes the type. + let object: Object = schema.objects().get(type_index); + let mut s = object.name().to_string(); + s += " { "; + if object.is_struct() { + let st: Struct<'_> = Struct::follow(buf, loc); + for field in object.fields() { + let field_value = get_any_field_string_in_struct(&st, &field, schema); + s += field.name(); + s += ": "; + s += field_value.as_str(); + s += ", "; + } + } else { + let table = ForwardsUOffset::::follow(buf, loc); + for field in object.fields() { + if table.vtable().get(field.offset()) == 0 { + continue; + } + let mut field_value = get_any_field_string(&table, &field, schema); + if field.type_().base_type() == BaseType::String { + field_value = escape(field_value.as_str()).to_string(); + } + s += field.name(); + s += ": "; + s += field_value.as_str(); + s += ", "; + } + } + s + "}" + } + BaseType::Vector => String::from("[(elements)]"), // TODO inherited from C++: implement this as well. + BaseType::Union => String::from("(union)"), // TODO inherited from C++: implement this as well. + _ => get_any_value_integer(base_type, buf, loc) + .unwrap_or_default() + .to_string(), + } +} + +/// Sets any scalar value with a 64-bit integer. Returns error if the value is not successfully replaced. +fn set_any_value_integer( + base_type: BaseType, + buf: &mut [u8], + field_loc: usize, + v: i64, +) -> FlatbufferResult<()> { + if buf.len() < get_type_size(base_type) { + return Err(FlatbufferError::VerificationError( + InvalidFlatbuffer::RangeOutOfBounds { + range: core::ops::Range { + start: field_loc, + end: field_loc.saturating_add(get_type_size(base_type)), + }, + error_trace: Default::default(), + }, + )); + } + let buf = &mut buf[field_loc..]; + let type_name = base_type.variant_name().unwrap_or_default().to_string(); + + macro_rules! try_emplace { + ($ty:ty, $value:expr) => { + if let Ok(v) = TryInto::<$ty>::try_into($value) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { Ok(emplace_scalar::<$ty>(buf, v)) } + } else { + Err(FlatbufferError::FieldTypeMismatch( + String::from("i64"), + type_name, + )) + } + }; + } + + match base_type { + BaseType::UType | BaseType::UByte => { + try_emplace!(u8, v) + } + BaseType::Bool => { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { Ok(emplace_scalar::(buf, v != 0)) } + } + BaseType::Byte => { + try_emplace!(i8, v) + } + BaseType::Short => { + try_emplace!(i16, v) + } + BaseType::UShort => { + try_emplace!(u16, v) + } + BaseType::Int => { + try_emplace!(i32, v) + } + BaseType::UInt => { + try_emplace!(u32, v) + } + BaseType::Long => { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { Ok(emplace_scalar::(buf, v)) } + } + BaseType::ULong => { + try_emplace!(u64, v) + } + BaseType::Float => { + if let Some(value) = f32::from_i64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { Ok(emplace_scalar::(buf, value)) } + } else { + Err(FlatbufferError::FieldTypeMismatch( + String::from("i64"), + type_name, + )) + } + } + BaseType::Double => { + if let Some(value) = f64::from_i64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { Ok(emplace_scalar::(buf, value)) } + } else { + Err(FlatbufferError::FieldTypeMismatch( + String::from("i64"), + type_name, + )) + } + } + _ => Err(FlatbufferError::SetValueNotSupported), + } +} + +/// Sets any scalar value with a 64-bit floating point. Returns error if the value is not successfully replaced. +fn set_any_value_float( + base_type: BaseType, + buf: &mut [u8], + field_loc: usize, + v: f64, +) -> FlatbufferResult<()> { + if buf.len() < get_type_size(base_type) { + return Err(FlatbufferError::VerificationError( + InvalidFlatbuffer::RangeOutOfBounds { + range: core::ops::Range { + start: field_loc, + end: field_loc.saturating_add(get_type_size(base_type)), + }, + error_trace: Default::default(), + }, + )); + } + let buf = &mut buf[field_loc..]; + let type_name = base_type.variant_name().unwrap_or_default().to_string(); + + match base_type { + BaseType::UType | BaseType::UByte => { + if let Some(value) = u8::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::Bool => { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, v != 0f64)); + } + } + BaseType::Byte => { + if let Some(value) = i8::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::Short => { + if let Some(value) = i16::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::UShort => { + if let Some(value) = u16::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::Int => { + if let Some(value) = i32::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::UInt => { + if let Some(value) = u32::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::Long => { + if let Some(value) = i64::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::ULong => { + if let Some(value) = u64::from_f64(v) { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + BaseType::Float => { + if let Some(value) = f32::from_f64(v) { + // Value converted to inf if overflow occurs + if value != f32::INFINITY { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, value)); + } + } + } + } + BaseType::Double => { + // SAFETY: buffer size is verified at the beginning of this function. + unsafe { + return Ok(emplace_scalar::(buf, v)); + } + } + _ => return Err(FlatbufferError::SetValueNotSupported), + } + return Err(FlatbufferError::FieldTypeMismatch( + String::from("f64"), + type_name, + )); +} + +fn is_scalar(base_type: BaseType) -> bool { + return base_type <= BaseType::Double; +} + +/// Iterates through the buffer and updates all the relative offsets affected by the insertion. +/// +/// # Safety +/// +/// Caller must ensure [buf] contains valid data that conforms to [schema]. +unsafe fn update_offset( + buf: &mut [u8], + table_loc: usize, + updated: &mut [bool], + object: &Object, + schema: &Schema, + insertion_loc: usize, + offset: isize, +) -> FlatbufferResult<()> { + if updated.len() != buf.len() { + return Err(FlatbufferError::SetStringPolluted); + } + + if updated[table_loc] { + return Ok(()); + } + + let slice = &mut buf[table_loc..table_loc + SIZE_SOFFSET]; + let vtable_offset = isize::try_from(read_scalar::(slice))?; + let vtable_loc = (isize::try_from(table_loc)? - vtable_offset).try_into()?; + + if insertion_loc <= table_loc { + // Checks if insertion point is between the table and a vtable that + // precedes it. + if (vtable_loc..table_loc).contains(&insertion_loc) { + emplace_scalar::(slice, (vtable_offset + offset).try_into()?); + updated[table_loc] = true; + } + + // Early out: since all fields inside the table must point forwards in + // memory, if the insertion point is before the table we can stop here. + return Ok(()); + } + + for field in object.fields() { + let field_type = field.type_().base_type(); + if is_scalar(field_type) { + continue; + } + + let field_offset = VOffsetT::follow(buf, vtable_loc.saturating_add(field.offset().into())); + if field_offset == 0 { + continue; + } + + let field_loc = table_loc + usize::from(field_offset); + if updated[field_loc] { + continue; + } + + if field_type == BaseType::Obj + && schema + .objects() + .get(field.type_().index().try_into()?) + .is_struct() + { + continue; + } + + // Updates the relative offset from table to actual data if needed + let slice = &mut buf[field_loc..field_loc + SIZE_UOFFSET]; + let field_value_offset = read_scalar::(slice); + let field_value_loc = field_loc.saturating_add(field_value_offset.try_into()?); + if (field_loc..field_value_loc).contains(&insertion_loc) { + emplace_scalar::( + slice, + (isize::try_from(field_value_offset)? + offset).try_into()?, + ); + updated[field_loc] = true; + } + + match field_type { + BaseType::Obj => { + let field_obj = schema.objects().get(field.type_().index().try_into()?); + update_offset( + buf, + field_value_loc, + updated, + &field_obj, + schema, + insertion_loc, + offset, + )?; + } + BaseType::Vector => { + let elem_type = field.type_().element(); + if elem_type != BaseType::Obj || elem_type != BaseType::String { + continue; + } + if elem_type == BaseType::Obj + && schema + .objects() + .get(field.type_().index().try_into()?) + .is_struct() + { + continue; + } + let vec_size = usize::try_from(read_uoffset(buf, field_value_loc))?; + for index in 0..vec_size { + let elem_loc = field_value_loc + SIZE_UOFFSET + index * SIZE_UOFFSET; + if updated[elem_loc] { + continue; + } + let slice = &mut buf[elem_loc..elem_loc + SIZE_UOFFSET]; + let elem_value_offset = read_scalar::(slice); + let elem_value_loc = elem_loc.saturating_add(elem_value_offset.try_into()?); + if (elem_loc..elem_value_loc).contains(&insertion_loc) { + emplace_scalar::( + slice, + (isize::try_from(elem_value_offset)? + offset).try_into()?, + ); + updated[elem_loc] = true; + } + + if elem_type == BaseType::Obj { + let elem_obj = schema.objects().get(field.type_().index().try_into()?); + update_offset( + buf, + elem_value_loc, + updated, + &elem_obj, + schema, + insertion_loc, + offset, + )?; + } + } + } + BaseType::Union => { + let union_enum = schema.enums().get(field.type_().index().try_into()?); + let union_type = object + .fields() + .lookup_by_key(field.name().to_string() + "_type", |field, key| { + field.key_compare_with_value(key) + }) + .unwrap(); + let union_type_loc = vtable_loc.saturating_add(union_type.offset().into()); + let union_type_offset = VOffsetT::follow(buf, union_type_loc); + let union_type_value = + u8::follow(buf, table_loc.saturating_add(union_type_offset.into())); + let union_enum_value = union_enum + .values() + .lookup_by_key(union_type_value.into(), |value, key| { + value.key_compare_with_value(*key) + }) + .unwrap(); + let union_object = schema + .objects() + .get(union_enum_value.union_type().unwrap().index().try_into()?); + update_offset( + buf, + field_value_loc, + updated, + &union_object, + schema, + insertion_loc, + offset, + )?; + } + _ => (), + } + } + + // Checks if the vtable offset points beyond the insertion point. + if (table_loc..vtable_loc).contains(&insertion_loc) { + let slice = &mut buf[table_loc..table_loc + SIZE_SOFFSET]; + emplace_scalar::(slice, (vtable_offset - offset).try_into()?); + updated[table_loc] = true; + } + Ok(()) +} + +/// Returns the absolute location of the data (e.g. string) in the buffer when the field contains relative offset (`UOffsetT`) to the data. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type `UOffsetT`. +unsafe fn deref_uoffset(buf: &[u8], field_loc: usize) -> FlatbufferResult { + Ok(field_loc.saturating_add(read_uoffset(buf, field_loc).try_into()?)) +} + +/// Reads the value of `UOffsetT` at the give location. +/// +/// # Safety +/// +/// The value of the corresponding slot must have type `UOffsetT`. +unsafe fn read_uoffset(buf: &[u8], loc: usize) -> UOffsetT { + let slice = &buf[loc..loc + SIZE_UOFFSET]; + read_scalar::(slice) +} diff --git a/rust/reflection/src/reflection_generated.rs b/rust/reflection/src/reflection_generated.rs new file mode 100644 index 00000000000..adf5c858348 --- /dev/null +++ b/rust/reflection/src/reflection_generated.rs @@ -0,0 +1,2988 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +// @generated + +use core::cmp::Ordering; +use core::mem; + +extern crate flatbuffers; +use self::flatbuffers::{EndianScalar, Follow}; + +#[allow(unused_imports, dead_code)] +pub mod reflection { + + use core::cmp::Ordering; + use core::mem; + + extern crate flatbuffers; + use self::flatbuffers::{EndianScalar, Follow}; + + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + pub const ENUM_MIN_BASE_TYPE: i8 = 0; + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + pub const ENUM_MAX_BASE_TYPE: i8 = 19; + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + #[allow(non_camel_case_types)] + pub const ENUM_VALUES_BASE_TYPE: [BaseType; 20] = [ + BaseType::None, + BaseType::UType, + BaseType::Bool, + BaseType::Byte, + BaseType::UByte, + BaseType::Short, + BaseType::UShort, + BaseType::Int, + BaseType::UInt, + BaseType::Long, + BaseType::ULong, + BaseType::Float, + BaseType::Double, + BaseType::String, + BaseType::Vector, + BaseType::Obj, + BaseType::Union, + BaseType::Array, + BaseType::Vector64, + BaseType::MaxBaseType, + ]; + + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] + #[repr(transparent)] + pub struct BaseType(pub i8); + #[allow(non_upper_case_globals)] + impl BaseType { + pub const None: Self = Self(0); + pub const UType: Self = Self(1); + pub const Bool: Self = Self(2); + pub const Byte: Self = Self(3); + pub const UByte: Self = Self(4); + pub const Short: Self = Self(5); + pub const UShort: Self = Self(6); + pub const Int: Self = Self(7); + pub const UInt: Self = Self(8); + pub const Long: Self = Self(9); + pub const ULong: Self = Self(10); + pub const Float: Self = Self(11); + pub const Double: Self = Self(12); + pub const String: Self = Self(13); + pub const Vector: Self = Self(14); + pub const Obj: Self = Self(15); + pub const Union: Self = Self(16); + pub const Array: Self = Self(17); + pub const Vector64: Self = Self(18); + pub const MaxBaseType: Self = Self(19); + + pub const ENUM_MIN: i8 = 0; + pub const ENUM_MAX: i8 = 19; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::None, + Self::UType, + Self::Bool, + Self::Byte, + Self::UByte, + Self::Short, + Self::UShort, + Self::Int, + Self::UInt, + Self::Long, + Self::ULong, + Self::Float, + Self::Double, + Self::String, + Self::Vector, + Self::Obj, + Self::Union, + Self::Array, + Self::Vector64, + Self::MaxBaseType, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::None => Some("None"), + Self::UType => Some("UType"), + Self::Bool => Some("Bool"), + Self::Byte => Some("Byte"), + Self::UByte => Some("UByte"), + Self::Short => Some("Short"), + Self::UShort => Some("UShort"), + Self::Int => Some("Int"), + Self::UInt => Some("UInt"), + Self::Long => Some("Long"), + Self::ULong => Some("ULong"), + Self::Float => Some("Float"), + Self::Double => Some("Double"), + Self::String => Some("String"), + Self::Vector => Some("Vector"), + Self::Obj => Some("Obj"), + Self::Union => Some("Union"), + Self::Array => Some("Array"), + Self::Vector64 => Some("Vector64"), + Self::MaxBaseType => Some("MaxBaseType"), + _ => None, + } + } + } + impl core::fmt::Debug for BaseType { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } + } + impl<'a> flatbuffers::Follow<'a> for BaseType { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + Self(b) + } + } + + impl flatbuffers::Push for BaseType { + type Output = BaseType; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.0); + } + } + + impl flatbuffers::EndianScalar for BaseType { + type Scalar = i8; + #[inline] + fn to_little_endian(self) -> i8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: i8) -> Self { + let b = i8::from_le(v); + Self(b) + } + } + + impl<'a> flatbuffers::Verifiable for BaseType { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } + } + + impl flatbuffers::SimpleToVerifyInSlice for BaseType {} + #[allow(non_upper_case_globals)] + mod bitflags_advanced_features { + flatbuffers::bitflags::bitflags! { + /// New schema language features that are not supported by old code generators. + #[derive(Default)] + pub struct AdvancedFeatures: u64 { + const AdvancedArrayFeatures = 1; + const AdvancedUnionFeatures = 2; + const OptionalScalars = 4; + const DefaultVectorsAndStrings = 8; + } + } + } + pub use self::bitflags_advanced_features::AdvancedFeatures; + + impl<'a> flatbuffers::Follow<'a> for AdvancedFeatures { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + // Safety: + // This is safe because we know bitflags is implemented with a repr transparent uint of the correct size. + // from_bits_unchecked will be replaced by an equivalent but safe from_bits_retain in bitflags 2.0 + // https://github.com/bitflags/bitflags/issues/262 + Self::from_bits_unchecked(b) + } + } + + impl flatbuffers::Push for AdvancedFeatures { + type Output = AdvancedFeatures; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.bits()); + } + } + + impl flatbuffers::EndianScalar for AdvancedFeatures { + type Scalar = u64; + #[inline] + fn to_little_endian(self) -> u64 { + self.bits().to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u64) -> Self { + let b = u64::from_le(v); + // Safety: + // This is safe because we know bitflags is implemented with a repr transparent uint of the correct size. + // from_bits_unchecked will be replaced by an equivalent but safe from_bits_retain in bitflags 2.0 + // https://github.com/bitflags/bitflags/issues/262 + unsafe { Self::from_bits_unchecked(b) } + } + } + + impl<'a> flatbuffers::Verifiable for AdvancedFeatures { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u64::run_verifier(v, pos) + } + } + + impl flatbuffers::SimpleToVerifyInSlice for AdvancedFeatures {} + pub enum TypeOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Type<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Type<'a> { + type Inner = Type<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Type<'a> { + pub const VT_BASE_TYPE: flatbuffers::VOffsetT = 4; + pub const VT_ELEMENT: flatbuffers::VOffsetT = 6; + pub const VT_INDEX: flatbuffers::VOffsetT = 8; + pub const VT_FIXED_LENGTH: flatbuffers::VOffsetT = 10; + pub const VT_BASE_SIZE: flatbuffers::VOffsetT = 12; + pub const VT_ELEMENT_SIZE: flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Type { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args TypeArgs, + ) -> flatbuffers::WIPOffset> { + let mut builder = TypeBuilder::new(_fbb); + builder.add_element_size(args.element_size); + builder.add_base_size(args.base_size); + builder.add_index(args.index); + builder.add_fixed_length(args.fixed_length); + builder.add_element(args.element); + builder.add_base_type(args.base_type); + builder.finish() + } + + #[inline] + pub fn base_type(&self) -> BaseType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Type::VT_BASE_TYPE, Some(BaseType::None)) + .unwrap() + } + } + #[inline] + pub fn element(&self) -> BaseType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Type::VT_ELEMENT, Some(BaseType::None)) + .unwrap() + } + } + #[inline] + pub fn index(&self) -> i32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Type::VT_INDEX, Some(-1)).unwrap() } + } + #[inline] + pub fn fixed_length(&self) -> u16 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Type::VT_FIXED_LENGTH, Some(0)) + .unwrap() + } + } + /// The size (octets) of the `base_type` field. + #[inline] + pub fn base_size(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Type::VT_BASE_SIZE, Some(4)).unwrap() } + } + /// The size (octets) of the `element` field, if present. + #[inline] + pub fn element_size(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Type::VT_ELEMENT_SIZE, Some(0)) + .unwrap() + } + } + } + + impl flatbuffers::Verifiable for Type<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("base_type", Self::VT_BASE_TYPE, false)? + .visit_field::("element", Self::VT_ELEMENT, false)? + .visit_field::("index", Self::VT_INDEX, false)? + .visit_field::("fixed_length", Self::VT_FIXED_LENGTH, false)? + .visit_field::("base_size", Self::VT_BASE_SIZE, false)? + .visit_field::("element_size", Self::VT_ELEMENT_SIZE, false)? + .finish(); + Ok(()) + } + } + pub struct TypeArgs { + pub base_type: BaseType, + pub element: BaseType, + pub index: i32, + pub fixed_length: u16, + pub base_size: u32, + pub element_size: u32, + } + impl<'a> Default for TypeArgs { + #[inline] + fn default() -> Self { + TypeArgs { + base_type: BaseType::None, + element: BaseType::None, + index: -1, + fixed_length: 0, + base_size: 4, + element_size: 0, + } + } + } + + pub struct TypeBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> TypeBuilder<'a, 'b> { + #[inline] + pub fn add_base_type(&mut self, base_type: BaseType) { + self.fbb_ + .push_slot::(Type::VT_BASE_TYPE, base_type, BaseType::None); + } + #[inline] + pub fn add_element(&mut self, element: BaseType) { + self.fbb_ + .push_slot::(Type::VT_ELEMENT, element, BaseType::None); + } + #[inline] + pub fn add_index(&mut self, index: i32) { + self.fbb_.push_slot::(Type::VT_INDEX, index, -1); + } + #[inline] + pub fn add_fixed_length(&mut self, fixed_length: u16) { + self.fbb_ + .push_slot::(Type::VT_FIXED_LENGTH, fixed_length, 0); + } + #[inline] + pub fn add_base_size(&mut self, base_size: u32) { + self.fbb_.push_slot::(Type::VT_BASE_SIZE, base_size, 4); + } + #[inline] + pub fn add_element_size(&mut self, element_size: u32) { + self.fbb_ + .push_slot::(Type::VT_ELEMENT_SIZE, element_size, 0); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> TypeBuilder<'a, 'b> { + let start = _fbb.start_table(); + TypeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Type<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Type"); + ds.field("base_type", &self.base_type()); + ds.field("element", &self.element()); + ds.field("index", &self.index()); + ds.field("fixed_length", &self.fixed_length()); + ds.field("base_size", &self.base_size()); + ds.field("element_size", &self.element_size()); + ds.finish() + } + } + pub enum KeyValueOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct KeyValue<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for KeyValue<'a> { + type Inner = KeyValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> KeyValue<'a> { + pub const VT_KEY: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + KeyValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args KeyValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = KeyValueBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + if let Some(x) = args.key { + builder.add_key(x); + } + builder.finish() + } + + #[inline] + pub fn key(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(KeyValue::VT_KEY, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &KeyValue) -> bool { + self.key() < o.key() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.key(); + key.cmp(val) + } + #[inline] + pub fn value(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(KeyValue::VT_VALUE, None) + } + } + } + + impl flatbuffers::Verifiable for KeyValue<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("key", Self::VT_KEY, true)? + .visit_field::>("value", Self::VT_VALUE, false)? + .finish(); + Ok(()) + } + } + pub struct KeyValueArgs<'a> { + pub key: Option>, + pub value: Option>, + } + impl<'a> Default for KeyValueArgs<'a> { + #[inline] + fn default() -> Self { + KeyValueArgs { + key: None, // required field + value: None, + } + } + } + + pub struct KeyValueBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> KeyValueBuilder<'a, 'b> { + #[inline] + pub fn add_key(&mut self, key: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(KeyValue::VT_KEY, key); + } + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(KeyValue::VT_VALUE, value); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> KeyValueBuilder<'a, 'b> { + let start = _fbb.start_table(); + KeyValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, KeyValue::VT_KEY, "key"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for KeyValue<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("KeyValue"); + ds.field("key", &self.key()); + ds.field("value", &self.value()); + ds.finish() + } + } + pub enum EnumValOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct EnumVal<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for EnumVal<'a> { + type Inner = EnumVal<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> EnumVal<'a> { + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + pub const VT_UNION_TYPE: flatbuffers::VOffsetT = 10; + pub const VT_DOCUMENTATION: flatbuffers::VOffsetT = 12; + pub const VT_ATTRIBUTES: flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + EnumVal { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args EnumValArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = EnumValBuilder::new(_fbb); + builder.add_value(args.value); + if let Some(x) = args.attributes { + builder.add_attributes(x); + } + if let Some(x) = args.documentation { + builder.add_documentation(x); + } + if let Some(x) = args.union_type { + builder.add_union_type(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(EnumVal::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn value(&self) -> i64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(EnumVal::VT_VALUE, Some(0)).unwrap() } + } + #[inline] + pub fn key_compare_less_than(&self, o: &EnumVal) -> bool { + self.value() < o.value() + } + + #[inline] + pub fn key_compare_with_value(&self, val: i64) -> ::core::cmp::Ordering { + let key = self.value(); + key.cmp(&val) + } + #[inline] + pub fn union_type(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(EnumVal::VT_UNION_TYPE, None) + } + } + #[inline] + pub fn documentation( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(EnumVal::VT_DOCUMENTATION, None) + } + } + #[inline] + pub fn attributes( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(EnumVal::VT_ATTRIBUTES, None) + } + } + } + + impl flatbuffers::Verifiable for EnumVal<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::("value", Self::VT_VALUE, false)? + .visit_field::>( + "union_type", + Self::VT_UNION_TYPE, + false, + )? + .visit_field::>, + >>("documentation", Self::VT_DOCUMENTATION, false)? + .visit_field::>, + >>("attributes", Self::VT_ATTRIBUTES, false)? + .finish(); + Ok(()) + } + } + pub struct EnumValArgs<'a> { + pub name: Option>, + pub value: i64, + pub union_type: Option>>, + pub documentation: Option< + flatbuffers::WIPOffset>>, + >, + pub attributes: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + } + impl<'a> Default for EnumValArgs<'a> { + #[inline] + fn default() -> Self { + EnumValArgs { + name: None, // required field + value: 0, + union_type: None, + documentation: None, + attributes: None, + } + } + } + + pub struct EnumValBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> EnumValBuilder<'a, 'b> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(EnumVal::VT_NAME, name); + } + #[inline] + pub fn add_value(&mut self, value: i64) { + self.fbb_.push_slot::(EnumVal::VT_VALUE, value, 0); + } + #[inline] + pub fn add_union_type(&mut self, union_type: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>( + EnumVal::VT_UNION_TYPE, + union_type, + ); + } + #[inline] + pub fn add_documentation( + &mut self, + documentation: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + EnumVal::VT_DOCUMENTATION, + documentation, + ); + } + #[inline] + pub fn add_attributes( + &mut self, + attributes: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(EnumVal::VT_ATTRIBUTES, attributes); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> EnumValBuilder<'a, 'b> { + let start = _fbb.start_table(); + EnumValBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, EnumVal::VT_NAME, "name"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for EnumVal<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("EnumVal"); + ds.field("name", &self.name()); + ds.field("value", &self.value()); + ds.field("union_type", &self.union_type()); + ds.field("documentation", &self.documentation()); + ds.field("attributes", &self.attributes()); + ds.finish() + } + } + pub enum EnumOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Enum<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Enum<'a> { + type Inner = Enum<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Enum<'a> { + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_VALUES: flatbuffers::VOffsetT = 6; + pub const VT_IS_UNION: flatbuffers::VOffsetT = 8; + pub const VT_UNDERLYING_TYPE: flatbuffers::VOffsetT = 10; + pub const VT_ATTRIBUTES: flatbuffers::VOffsetT = 12; + pub const VT_DOCUMENTATION: flatbuffers::VOffsetT = 14; + pub const VT_DECLARATION_FILE: flatbuffers::VOffsetT = 16; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Enum { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args EnumArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = EnumBuilder::new(_fbb); + if let Some(x) = args.declaration_file { + builder.add_declaration_file(x); + } + if let Some(x) = args.documentation { + builder.add_documentation(x); + } + if let Some(x) = args.attributes { + builder.add_attributes(x); + } + if let Some(x) = args.underlying_type { + builder.add_underlying_type(x); + } + if let Some(x) = args.values { + builder.add_values(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_union(args.is_union); + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Enum::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &Enum) -> bool { + self.name() < o.name() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.name(); + key.cmp(val) + } + #[inline] + pub fn values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Enum::VT_VALUES, None) + .unwrap() + } + } + #[inline] + pub fn is_union(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Enum::VT_IS_UNION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn underlying_type(&self) -> Type<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Enum::VT_UNDERLYING_TYPE, None) + .unwrap() + } + } + #[inline] + pub fn attributes( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Enum::VT_ATTRIBUTES, None) + } + } + #[inline] + pub fn documentation( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Enum::VT_DOCUMENTATION, None) + } + } + /// File that this Enum is declared in. + #[inline] + pub fn declaration_file(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Enum::VT_DECLARATION_FILE, None) + } + } + } + + impl flatbuffers::Verifiable for Enum<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>, + >>("values", Self::VT_VALUES, true)? + .visit_field::("is_union", Self::VT_IS_UNION, false)? + .visit_field::>( + "underlying_type", + Self::VT_UNDERLYING_TYPE, + true, + )? + .visit_field::>, + >>("attributes", Self::VT_ATTRIBUTES, false)? + .visit_field::>, + >>("documentation", Self::VT_DOCUMENTATION, false)? + .visit_field::>( + "declaration_file", + Self::VT_DECLARATION_FILE, + false, + )? + .finish(); + Ok(()) + } + } + pub struct EnumArgs<'a> { + pub name: Option>, + pub values: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub is_union: bool, + pub underlying_type: Option>>, + pub attributes: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub documentation: Option< + flatbuffers::WIPOffset>>, + >, + pub declaration_file: Option>, + } + impl<'a> Default for EnumArgs<'a> { + #[inline] + fn default() -> Self { + EnumArgs { + name: None, // required field + values: None, // required field + is_union: false, + underlying_type: None, // required field + attributes: None, + documentation: None, + declaration_file: None, + } + } + } + + pub struct EnumBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> EnumBuilder<'a, 'b> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Enum::VT_NAME, name); + } + #[inline] + pub fn add_values( + &mut self, + values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Enum::VT_VALUES, values); + } + #[inline] + pub fn add_is_union(&mut self, is_union: bool) { + self.fbb_ + .push_slot::(Enum::VT_IS_UNION, is_union, false); + } + #[inline] + pub fn add_underlying_type(&mut self, underlying_type: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>( + Enum::VT_UNDERLYING_TYPE, + underlying_type, + ); + } + #[inline] + pub fn add_attributes( + &mut self, + attributes: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Enum::VT_ATTRIBUTES, attributes); + } + #[inline] + pub fn add_documentation( + &mut self, + documentation: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + Enum::VT_DOCUMENTATION, + documentation, + ); + } + #[inline] + pub fn add_declaration_file(&mut self, declaration_file: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + Enum::VT_DECLARATION_FILE, + declaration_file, + ); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> EnumBuilder<'a, 'b> { + let start = _fbb.start_table(); + EnumBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Enum::VT_NAME, "name"); + self.fbb_.required(o, Enum::VT_VALUES, "values"); + self.fbb_ + .required(o, Enum::VT_UNDERLYING_TYPE, "underlying_type"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Enum<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Enum"); + ds.field("name", &self.name()); + ds.field("values", &self.values()); + ds.field("is_union", &self.is_union()); + ds.field("underlying_type", &self.underlying_type()); + ds.field("attributes", &self.attributes()); + ds.field("documentation", &self.documentation()); + ds.field("declaration_file", &self.declaration_file()); + ds.finish() + } + } + pub enum FieldOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Field<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Field<'a> { + type Inner = Field<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Field<'a> { + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_TYPE_: flatbuffers::VOffsetT = 6; + pub const VT_ID: flatbuffers::VOffsetT = 8; + pub const VT_OFFSET: flatbuffers::VOffsetT = 10; + pub const VT_DEFAULT_INTEGER: flatbuffers::VOffsetT = 12; + pub const VT_DEFAULT_REAL: flatbuffers::VOffsetT = 14; + pub const VT_DEPRECATED: flatbuffers::VOffsetT = 16; + pub const VT_REQUIRED: flatbuffers::VOffsetT = 18; + pub const VT_KEY: flatbuffers::VOffsetT = 20; + pub const VT_ATTRIBUTES: flatbuffers::VOffsetT = 22; + pub const VT_DOCUMENTATION: flatbuffers::VOffsetT = 24; + pub const VT_OPTIONAL: flatbuffers::VOffsetT = 26; + pub const VT_PADDING: flatbuffers::VOffsetT = 28; + pub const VT_OFFSET64: flatbuffers::VOffsetT = 30; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Field { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args FieldArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = FieldBuilder::new(_fbb); + builder.add_default_real(args.default_real); + builder.add_default_integer(args.default_integer); + if let Some(x) = args.documentation { + builder.add_documentation(x); + } + if let Some(x) = args.attributes { + builder.add_attributes(x); + } + if let Some(x) = args.type_ { + builder.add_type_(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_padding(args.padding); + builder.add_offset(args.offset); + builder.add_id(args.id); + builder.add_offset64(args.offset64); + builder.add_optional(args.optional); + builder.add_key(args.key); + builder.add_required(args.required); + builder.add_deprecated(args.deprecated); + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Field::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &Field) -> bool { + self.name() < o.name() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.name(); + key.cmp(val) + } + #[inline] + pub fn type_(&self) -> Type<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Field::VT_TYPE_, None) + .unwrap() + } + } + #[inline] + pub fn id(&self) -> u16 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Field::VT_ID, Some(0)).unwrap() } + } + #[inline] + pub fn offset(&self) -> u16 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Field::VT_OFFSET, Some(0)).unwrap() } + } + #[inline] + pub fn default_integer(&self) -> i64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Field::VT_DEFAULT_INTEGER, Some(0)) + .unwrap() + } + } + #[inline] + pub fn default_real(&self) -> f64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Field::VT_DEFAULT_REAL, Some(0.0)) + .unwrap() + } + } + #[inline] + pub fn deprecated(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Field::VT_DEPRECATED, Some(false)) + .unwrap() + } + } + #[inline] + pub fn required(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Field::VT_REQUIRED, Some(false)) + .unwrap() + } + } + #[inline] + pub fn key(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Field::VT_KEY, Some(false)).unwrap() } + } + #[inline] + pub fn attributes( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Field::VT_ATTRIBUTES, None) + } + } + #[inline] + pub fn documentation( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Field::VT_DOCUMENTATION, None) + } + } + #[inline] + pub fn optional(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Field::VT_OPTIONAL, Some(false)) + .unwrap() + } + } + /// Number of padding octets to always add after this field. Structs only. + #[inline] + pub fn padding(&self) -> u16 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Field::VT_PADDING, Some(0)).unwrap() } + } + /// If the field uses 64-bit offsets. + #[inline] + pub fn offset64(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Field::VT_OFFSET64, Some(false)) + .unwrap() + } + } + } + + impl flatbuffers::Verifiable for Field<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>("type_", Self::VT_TYPE_, true)? + .visit_field::("id", Self::VT_ID, false)? + .visit_field::("offset", Self::VT_OFFSET, false)? + .visit_field::("default_integer", Self::VT_DEFAULT_INTEGER, false)? + .visit_field::("default_real", Self::VT_DEFAULT_REAL, false)? + .visit_field::("deprecated", Self::VT_DEPRECATED, false)? + .visit_field::("required", Self::VT_REQUIRED, false)? + .visit_field::("key", Self::VT_KEY, false)? + .visit_field::>, + >>("attributes", Self::VT_ATTRIBUTES, false)? + .visit_field::>, + >>("documentation", Self::VT_DOCUMENTATION, false)? + .visit_field::("optional", Self::VT_OPTIONAL, false)? + .visit_field::("padding", Self::VT_PADDING, false)? + .visit_field::("offset64", Self::VT_OFFSET64, false)? + .finish(); + Ok(()) + } + } + pub struct FieldArgs<'a> { + pub name: Option>, + pub type_: Option>>, + pub id: u16, + pub offset: u16, + pub default_integer: i64, + pub default_real: f64, + pub deprecated: bool, + pub required: bool, + pub key: bool, + pub attributes: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub documentation: Option< + flatbuffers::WIPOffset>>, + >, + pub optional: bool, + pub padding: u16, + pub offset64: bool, + } + impl<'a> Default for FieldArgs<'a> { + #[inline] + fn default() -> Self { + FieldArgs { + name: None, // required field + type_: None, // required field + id: 0, + offset: 0, + default_integer: 0, + default_real: 0.0, + deprecated: false, + required: false, + key: false, + attributes: None, + documentation: None, + optional: false, + padding: 0, + offset64: false, + } + } + } + + pub struct FieldBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> FieldBuilder<'a, 'b> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Field::VT_NAME, name); + } + #[inline] + pub fn add_type_(&mut self, type_: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Field::VT_TYPE_, type_); + } + #[inline] + pub fn add_id(&mut self, id: u16) { + self.fbb_.push_slot::(Field::VT_ID, id, 0); + } + #[inline] + pub fn add_offset(&mut self, offset: u16) { + self.fbb_.push_slot::(Field::VT_OFFSET, offset, 0); + } + #[inline] + pub fn add_default_integer(&mut self, default_integer: i64) { + self.fbb_ + .push_slot::(Field::VT_DEFAULT_INTEGER, default_integer, 0); + } + #[inline] + pub fn add_default_real(&mut self, default_real: f64) { + self.fbb_ + .push_slot::(Field::VT_DEFAULT_REAL, default_real, 0.0); + } + #[inline] + pub fn add_deprecated(&mut self, deprecated: bool) { + self.fbb_ + .push_slot::(Field::VT_DEPRECATED, deprecated, false); + } + #[inline] + pub fn add_required(&mut self, required: bool) { + self.fbb_ + .push_slot::(Field::VT_REQUIRED, required, false); + } + #[inline] + pub fn add_key(&mut self, key: bool) { + self.fbb_.push_slot::(Field::VT_KEY, key, false); + } + #[inline] + pub fn add_attributes( + &mut self, + attributes: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Field::VT_ATTRIBUTES, attributes); + } + #[inline] + pub fn add_documentation( + &mut self, + documentation: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + Field::VT_DOCUMENTATION, + documentation, + ); + } + #[inline] + pub fn add_optional(&mut self, optional: bool) { + self.fbb_ + .push_slot::(Field::VT_OPTIONAL, optional, false); + } + #[inline] + pub fn add_padding(&mut self, padding: u16) { + self.fbb_.push_slot::(Field::VT_PADDING, padding, 0); + } + #[inline] + pub fn add_offset64(&mut self, offset64: bool) { + self.fbb_ + .push_slot::(Field::VT_OFFSET64, offset64, false); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> FieldBuilder<'a, 'b> { + let start = _fbb.start_table(); + FieldBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Field::VT_NAME, "name"); + self.fbb_.required(o, Field::VT_TYPE_, "type_"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Field<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Field"); + ds.field("name", &self.name()); + ds.field("type_", &self.type_()); + ds.field("id", &self.id()); + ds.field("offset", &self.offset()); + ds.field("default_integer", &self.default_integer()); + ds.field("default_real", &self.default_real()); + ds.field("deprecated", &self.deprecated()); + ds.field("required", &self.required()); + ds.field("key", &self.key()); + ds.field("attributes", &self.attributes()); + ds.field("documentation", &self.documentation()); + ds.field("optional", &self.optional()); + ds.field("padding", &self.padding()); + ds.field("offset64", &self.offset64()); + ds.finish() + } + } + pub enum ObjectOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Object<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Object<'a> { + type Inner = Object<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Object<'a> { + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_FIELDS: flatbuffers::VOffsetT = 6; + pub const VT_IS_STRUCT: flatbuffers::VOffsetT = 8; + pub const VT_MINALIGN: flatbuffers::VOffsetT = 10; + pub const VT_BYTESIZE: flatbuffers::VOffsetT = 12; + pub const VT_ATTRIBUTES: flatbuffers::VOffsetT = 14; + pub const VT_DOCUMENTATION: flatbuffers::VOffsetT = 16; + pub const VT_DECLARATION_FILE: flatbuffers::VOffsetT = 18; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Object { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args ObjectArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ObjectBuilder::new(_fbb); + if let Some(x) = args.declaration_file { + builder.add_declaration_file(x); + } + if let Some(x) = args.documentation { + builder.add_documentation(x); + } + if let Some(x) = args.attributes { + builder.add_attributes(x); + } + builder.add_bytesize(args.bytesize); + builder.add_minalign(args.minalign); + if let Some(x) = args.fields { + builder.add_fields(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_struct(args.is_struct); + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Object::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &Object) -> bool { + self.name() < o.name() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.name(); + key.cmp(val) + } + #[inline] + pub fn fields(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Object::VT_FIELDS, None) + .unwrap() + } + } + #[inline] + pub fn is_struct(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Object::VT_IS_STRUCT, Some(false)) + .unwrap() + } + } + #[inline] + pub fn minalign(&self) -> i32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Object::VT_MINALIGN, Some(0)).unwrap() } + } + #[inline] + pub fn bytesize(&self) -> i32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Object::VT_BYTESIZE, Some(0)).unwrap() } + } + #[inline] + pub fn attributes( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Object::VT_ATTRIBUTES, None) + } + } + #[inline] + pub fn documentation( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Object::VT_DOCUMENTATION, None) + } + } + /// File that this Object is declared in. + #[inline] + pub fn declaration_file(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Object::VT_DECLARATION_FILE, None) + } + } + } + + impl flatbuffers::Verifiable for Object<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>, + >>("fields", Self::VT_FIELDS, true)? + .visit_field::("is_struct", Self::VT_IS_STRUCT, false)? + .visit_field::("minalign", Self::VT_MINALIGN, false)? + .visit_field::("bytesize", Self::VT_BYTESIZE, false)? + .visit_field::>, + >>("attributes", Self::VT_ATTRIBUTES, false)? + .visit_field::>, + >>("documentation", Self::VT_DOCUMENTATION, false)? + .visit_field::>( + "declaration_file", + Self::VT_DECLARATION_FILE, + false, + )? + .finish(); + Ok(()) + } + } + pub struct ObjectArgs<'a> { + pub name: Option>, + pub fields: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub is_struct: bool, + pub minalign: i32, + pub bytesize: i32, + pub attributes: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub documentation: Option< + flatbuffers::WIPOffset>>, + >, + pub declaration_file: Option>, + } + impl<'a> Default for ObjectArgs<'a> { + #[inline] + fn default() -> Self { + ObjectArgs { + name: None, // required field + fields: None, // required field + is_struct: false, + minalign: 0, + bytesize: 0, + attributes: None, + documentation: None, + declaration_file: None, + } + } + } + + pub struct ObjectBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> ObjectBuilder<'a, 'b> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Object::VT_NAME, name); + } + #[inline] + pub fn add_fields( + &mut self, + fields: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Object::VT_FIELDS, fields); + } + #[inline] + pub fn add_is_struct(&mut self, is_struct: bool) { + self.fbb_ + .push_slot::(Object::VT_IS_STRUCT, is_struct, false); + } + #[inline] + pub fn add_minalign(&mut self, minalign: i32) { + self.fbb_.push_slot::(Object::VT_MINALIGN, minalign, 0); + } + #[inline] + pub fn add_bytesize(&mut self, bytesize: i32) { + self.fbb_.push_slot::(Object::VT_BYTESIZE, bytesize, 0); + } + #[inline] + pub fn add_attributes( + &mut self, + attributes: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Object::VT_ATTRIBUTES, attributes); + } + #[inline] + pub fn add_documentation( + &mut self, + documentation: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + Object::VT_DOCUMENTATION, + documentation, + ); + } + #[inline] + pub fn add_declaration_file(&mut self, declaration_file: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + Object::VT_DECLARATION_FILE, + declaration_file, + ); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ObjectBuilder<'a, 'b> { + let start = _fbb.start_table(); + ObjectBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Object::VT_NAME, "name"); + self.fbb_.required(o, Object::VT_FIELDS, "fields"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Object<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Object"); + ds.field("name", &self.name()); + ds.field("fields", &self.fields()); + ds.field("is_struct", &self.is_struct()); + ds.field("minalign", &self.minalign()); + ds.field("bytesize", &self.bytesize()); + ds.field("attributes", &self.attributes()); + ds.field("documentation", &self.documentation()); + ds.field("declaration_file", &self.declaration_file()); + ds.finish() + } + } + pub enum RPCCallOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct RPCCall<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for RPCCall<'a> { + type Inner = RPCCall<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> RPCCall<'a> { + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_REQUEST: flatbuffers::VOffsetT = 6; + pub const VT_RESPONSE: flatbuffers::VOffsetT = 8; + pub const VT_ATTRIBUTES: flatbuffers::VOffsetT = 10; + pub const VT_DOCUMENTATION: flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + RPCCall { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args RPCCallArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = RPCCallBuilder::new(_fbb); + if let Some(x) = args.documentation { + builder.add_documentation(x); + } + if let Some(x) = args.attributes { + builder.add_attributes(x); + } + if let Some(x) = args.response { + builder.add_response(x); + } + if let Some(x) = args.request { + builder.add_request(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(RPCCall::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &RPCCall) -> bool { + self.name() < o.name() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.name(); + key.cmp(val) + } + #[inline] + pub fn request(&self) -> Object<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(RPCCall::VT_REQUEST, None) + .unwrap() + } + } + #[inline] + pub fn response(&self) -> Object<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(RPCCall::VT_RESPONSE, None) + .unwrap() + } + } + #[inline] + pub fn attributes( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(RPCCall::VT_ATTRIBUTES, None) + } + } + #[inline] + pub fn documentation( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(RPCCall::VT_DOCUMENTATION, None) + } + } + } + + impl flatbuffers::Verifiable for RPCCall<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>( + "request", + Self::VT_REQUEST, + true, + )? + .visit_field::>( + "response", + Self::VT_RESPONSE, + true, + )? + .visit_field::>, + >>("attributes", Self::VT_ATTRIBUTES, false)? + .visit_field::>, + >>("documentation", Self::VT_DOCUMENTATION, false)? + .finish(); + Ok(()) + } + } + pub struct RPCCallArgs<'a> { + pub name: Option>, + pub request: Option>>, + pub response: Option>>, + pub attributes: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub documentation: Option< + flatbuffers::WIPOffset>>, + >, + } + impl<'a> Default for RPCCallArgs<'a> { + #[inline] + fn default() -> Self { + RPCCallArgs { + name: None, // required field + request: None, // required field + response: None, // required field + attributes: None, + documentation: None, + } + } + } + + pub struct RPCCallBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> RPCCallBuilder<'a, 'b> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(RPCCall::VT_NAME, name); + } + #[inline] + pub fn add_request(&mut self, request: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(RPCCall::VT_REQUEST, request); + } + #[inline] + pub fn add_response(&mut self, response: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(RPCCall::VT_RESPONSE, response); + } + #[inline] + pub fn add_attributes( + &mut self, + attributes: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(RPCCall::VT_ATTRIBUTES, attributes); + } + #[inline] + pub fn add_documentation( + &mut self, + documentation: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + RPCCall::VT_DOCUMENTATION, + documentation, + ); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> RPCCallBuilder<'a, 'b> { + let start = _fbb.start_table(); + RPCCallBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, RPCCall::VT_NAME, "name"); + self.fbb_.required(o, RPCCall::VT_REQUEST, "request"); + self.fbb_.required(o, RPCCall::VT_RESPONSE, "response"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for RPCCall<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("RPCCall"); + ds.field("name", &self.name()); + ds.field("request", &self.request()); + ds.field("response", &self.response()); + ds.field("attributes", &self.attributes()); + ds.field("documentation", &self.documentation()); + ds.finish() + } + } + pub enum ServiceOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Service<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Service<'a> { + type Inner = Service<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Service<'a> { + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_CALLS: flatbuffers::VOffsetT = 6; + pub const VT_ATTRIBUTES: flatbuffers::VOffsetT = 8; + pub const VT_DOCUMENTATION: flatbuffers::VOffsetT = 10; + pub const VT_DECLARATION_FILE: flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Service { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args ServiceArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ServiceBuilder::new(_fbb); + if let Some(x) = args.declaration_file { + builder.add_declaration_file(x); + } + if let Some(x) = args.documentation { + builder.add_documentation(x); + } + if let Some(x) = args.attributes { + builder.add_attributes(x); + } + if let Some(x) = args.calls { + builder.add_calls(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Service::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &Service) -> bool { + self.name() < o.name() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.name(); + key.cmp(val) + } + #[inline] + pub fn calls( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Service::VT_CALLS, None) + } + } + #[inline] + pub fn attributes( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Service::VT_ATTRIBUTES, None) + } + } + #[inline] + pub fn documentation( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Service::VT_DOCUMENTATION, None) + } + } + /// File that this Service is declared in. + #[inline] + pub fn declaration_file(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Service::VT_DECLARATION_FILE, None) + } + } + } + + impl flatbuffers::Verifiable for Service<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>, + >>("calls", Self::VT_CALLS, false)? + .visit_field::>, + >>("attributes", Self::VT_ATTRIBUTES, false)? + .visit_field::>, + >>("documentation", Self::VT_DOCUMENTATION, false)? + .visit_field::>( + "declaration_file", + Self::VT_DECLARATION_FILE, + false, + )? + .finish(); + Ok(()) + } + } + pub struct ServiceArgs<'a> { + pub name: Option>, + pub calls: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub attributes: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub documentation: Option< + flatbuffers::WIPOffset>>, + >, + pub declaration_file: Option>, + } + impl<'a> Default for ServiceArgs<'a> { + #[inline] + fn default() -> Self { + ServiceArgs { + name: None, // required field + calls: None, + attributes: None, + documentation: None, + declaration_file: None, + } + } + } + + pub struct ServiceBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> ServiceBuilder<'a, 'b> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Service::VT_NAME, name); + } + #[inline] + pub fn add_calls( + &mut self, + calls: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Service::VT_CALLS, calls); + } + #[inline] + pub fn add_attributes( + &mut self, + attributes: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Service::VT_ATTRIBUTES, attributes); + } + #[inline] + pub fn add_documentation( + &mut self, + documentation: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + Service::VT_DOCUMENTATION, + documentation, + ); + } + #[inline] + pub fn add_declaration_file(&mut self, declaration_file: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + Service::VT_DECLARATION_FILE, + declaration_file, + ); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ServiceBuilder<'a, 'b> { + let start = _fbb.start_table(); + ServiceBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Service::VT_NAME, "name"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Service<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Service"); + ds.field("name", &self.name()); + ds.field("calls", &self.calls()); + ds.field("attributes", &self.attributes()); + ds.field("documentation", &self.documentation()); + ds.field("declaration_file", &self.declaration_file()); + ds.finish() + } + } + pub enum SchemaFileOffset {} + #[derive(Copy, Clone, PartialEq)] + + /// File specific information. + /// Symbols declared within a file may be recovered by iterating over all + /// symbols and examining the `declaration_file` field. + pub struct SchemaFile<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for SchemaFile<'a> { + type Inner = SchemaFile<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> SchemaFile<'a> { + pub const VT_FILENAME: flatbuffers::VOffsetT = 4; + pub const VT_INCLUDED_FILENAMES: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + SchemaFile { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args SchemaFileArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = SchemaFileBuilder::new(_fbb); + if let Some(x) = args.included_filenames { + builder.add_included_filenames(x); + } + if let Some(x) = args.filename { + builder.add_filename(x); + } + builder.finish() + } + + /// Filename, relative to project root. + #[inline] + pub fn filename(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(SchemaFile::VT_FILENAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &SchemaFile) -> bool { + self.filename() < o.filename() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.filename(); + key.cmp(val) + } + /// Names of included files, relative to project root. + #[inline] + pub fn included_filenames( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(SchemaFile::VT_INCLUDED_FILENAMES, None) + } + } + } + + impl flatbuffers::Verifiable for SchemaFile<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>( + "filename", + Self::VT_FILENAME, + true, + )? + .visit_field::>, + >>("included_filenames", Self::VT_INCLUDED_FILENAMES, false)? + .finish(); + Ok(()) + } + } + pub struct SchemaFileArgs<'a> { + pub filename: Option>, + pub included_filenames: Option< + flatbuffers::WIPOffset>>, + >, + } + impl<'a> Default for SchemaFileArgs<'a> { + #[inline] + fn default() -> Self { + SchemaFileArgs { + filename: None, // required field + included_filenames: None, + } + } + } + + pub struct SchemaFileBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> SchemaFileBuilder<'a, 'b> { + #[inline] + pub fn add_filename(&mut self, filename: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(SchemaFile::VT_FILENAME, filename); + } + #[inline] + pub fn add_included_filenames( + &mut self, + included_filenames: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + SchemaFile::VT_INCLUDED_FILENAMES, + included_filenames, + ); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> SchemaFileBuilder<'a, 'b> { + let start = _fbb.start_table(); + SchemaFileBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, SchemaFile::VT_FILENAME, "filename"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for SchemaFile<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("SchemaFile"); + ds.field("filename", &self.filename()); + ds.field("included_filenames", &self.included_filenames()); + ds.finish() + } + } + pub enum SchemaOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Schema<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Schema<'a> { + type Inner = Schema<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Schema<'a> { + pub const VT_OBJECTS: flatbuffers::VOffsetT = 4; + pub const VT_ENUMS: flatbuffers::VOffsetT = 6; + pub const VT_FILE_IDENT: flatbuffers::VOffsetT = 8; + pub const VT_FILE_EXT: flatbuffers::VOffsetT = 10; + pub const VT_ROOT_TABLE: flatbuffers::VOffsetT = 12; + pub const VT_SERVICES: flatbuffers::VOffsetT = 14; + pub const VT_ADVANCED_FEATURES: flatbuffers::VOffsetT = 16; + pub const VT_FBS_FILES: flatbuffers::VOffsetT = 18; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Schema { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args SchemaArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = SchemaBuilder::new(_fbb); + builder.add_advanced_features(args.advanced_features); + if let Some(x) = args.fbs_files { + builder.add_fbs_files(x); + } + if let Some(x) = args.services { + builder.add_services(x); + } + if let Some(x) = args.root_table { + builder.add_root_table(x); + } + if let Some(x) = args.file_ext { + builder.add_file_ext(x); + } + if let Some(x) = args.file_ident { + builder.add_file_ident(x); + } + if let Some(x) = args.enums { + builder.add_enums(x); + } + if let Some(x) = args.objects { + builder.add_objects(x); + } + builder.finish() + } + + #[inline] + pub fn objects(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_OBJECTS, None) + .unwrap() + } + } + #[inline] + pub fn enums(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_ENUMS, None) + .unwrap() + } + } + #[inline] + pub fn file_ident(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Schema::VT_FILE_IDENT, None) + } + } + #[inline] + pub fn file_ext(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Schema::VT_FILE_EXT, None) + } + } + #[inline] + pub fn root_table(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Schema::VT_ROOT_TABLE, None) + } + } + #[inline] + pub fn services( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Schema::VT_SERVICES, None) + } + } + #[inline] + pub fn advanced_features(&self) -> AdvancedFeatures { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Schema::VT_ADVANCED_FEATURES, Some(Default::default())) + .unwrap() + } + } + /// All the files used in this compilation. Files are relative to where + /// flatc was invoked. + #[inline] + pub fn fbs_files( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Schema::VT_FBS_FILES, None) + } + } + } + + impl flatbuffers::Verifiable for Schema<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>, + >>("objects", Self::VT_OBJECTS, true)? + .visit_field::>, + >>("enums", Self::VT_ENUMS, true)? + .visit_field::>( + "file_ident", + Self::VT_FILE_IDENT, + false, + )? + .visit_field::>( + "file_ext", + Self::VT_FILE_EXT, + false, + )? + .visit_field::>( + "root_table", + Self::VT_ROOT_TABLE, + false, + )? + .visit_field::>, + >>("services", Self::VT_SERVICES, false)? + .visit_field::( + "advanced_features", + Self::VT_ADVANCED_FEATURES, + false, + )? + .visit_field::>, + >>("fbs_files", Self::VT_FBS_FILES, false)? + .finish(); + Ok(()) + } + } + pub struct SchemaArgs<'a> { + pub objects: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub enums: Option< + flatbuffers::WIPOffset>>>, + >, + pub file_ident: Option>, + pub file_ext: Option>, + pub root_table: Option>>, + pub services: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub advanced_features: AdvancedFeatures, + pub fbs_files: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + } + impl<'a> Default for SchemaArgs<'a> { + #[inline] + fn default() -> Self { + SchemaArgs { + objects: None, // required field + enums: None, // required field + file_ident: None, + file_ext: None, + root_table: None, + services: None, + advanced_features: Default::default(), + fbs_files: None, + } + } + } + + pub struct SchemaBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> SchemaBuilder<'a, 'b> { + #[inline] + pub fn add_objects( + &mut self, + objects: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_OBJECTS, objects); + } + #[inline] + pub fn add_enums( + &mut self, + enums: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_ENUMS, enums); + } + #[inline] + pub fn add_file_ident(&mut self, file_ident: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Schema::VT_FILE_IDENT, file_ident); + } + #[inline] + pub fn add_file_ext(&mut self, file_ext: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Schema::VT_FILE_EXT, file_ext); + } + #[inline] + pub fn add_root_table(&mut self, root_table: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>( + Schema::VT_ROOT_TABLE, + root_table, + ); + } + #[inline] + pub fn add_services( + &mut self, + services: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_SERVICES, services); + } + #[inline] + pub fn add_advanced_features(&mut self, advanced_features: AdvancedFeatures) { + self.fbb_.push_slot::( + Schema::VT_ADVANCED_FEATURES, + advanced_features, + Default::default(), + ); + } + #[inline] + pub fn add_fbs_files( + &mut self, + fbs_files: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_FBS_FILES, fbs_files); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> SchemaBuilder<'a, 'b> { + let start = _fbb.start_table(); + SchemaBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Schema::VT_OBJECTS, "objects"); + self.fbb_.required(o, Schema::VT_ENUMS, "enums"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Schema<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Schema"); + ds.field("objects", &self.objects()); + ds.field("enums", &self.enums()); + ds.field("file_ident", &self.file_ident()); + ds.field("file_ext", &self.file_ext()); + ds.field("root_table", &self.root_table()); + ds.field("services", &self.services()); + ds.field("advanced_features", &self.advanced_features()); + ds.field("fbs_files", &self.fbs_files()); + ds.finish() + } + } + #[inline] + /// Verifies that a buffer of bytes contains a `Schema` + /// 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_schema_unchecked`. + pub fn root_as_schema(buf: &[u8]) -> Result { + flatbuffers::root::(buf) + } + #[inline] + /// Verifies that a buffer of bytes contains a size prefixed + /// `Schema` 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_schema_unchecked`. + pub fn size_prefixed_root_as_schema( + buf: &[u8], + ) -> Result { + flatbuffers::size_prefixed_root::(buf) + } + #[inline] + /// Verifies, with the given options, that a buffer of bytes + /// contains a `Schema` 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_schema_unchecked`. + pub fn root_as_schema_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], + ) -> Result, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root_with_opts::>(opts, buf) + } + #[inline] + /// Verifies, with the given verifier options, that a buffer of + /// bytes contains a size prefixed `Schema` 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_schema_unchecked`. + pub fn size_prefixed_root_as_schema_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], + ) -> Result, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root_with_opts::>(opts, buf) + } + #[inline] + /// Assumes, without verification, that a buffer of bytes contains a Schema and returns it. + /// # Safety + /// Callers must trust the given bytes do indeed contain a valid `Schema`. + pub unsafe fn root_as_schema_unchecked(buf: &[u8]) -> Schema { + flatbuffers::root_unchecked::(buf) + } + #[inline] + /// Assumes, without verification, that a buffer of bytes contains a size prefixed Schema and returns it. + /// # Safety + /// Callers must trust the given bytes do indeed contain a valid size prefixed `Schema`. + pub unsafe fn size_prefixed_root_as_schema_unchecked(buf: &[u8]) -> Schema { + flatbuffers::size_prefixed_root_unchecked::(buf) + } + pub const SCHEMA_IDENTIFIER: &str = "BFBS"; + + #[inline] + pub fn schema_buffer_has_identifier(buf: &[u8]) -> bool { + flatbuffers::buffer_has_identifier(buf, SCHEMA_IDENTIFIER, false) + } + + #[inline] + pub fn schema_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool { + flatbuffers::buffer_has_identifier(buf, SCHEMA_IDENTIFIER, true) + } + + pub const SCHEMA_EXTENSION: &str = "bfbs"; + + #[inline] + pub fn finish_schema_buffer<'a, 'b>( + fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, + root: flatbuffers::WIPOffset>, + ) { + fbb.finish(root, Some(SCHEMA_IDENTIFIER)); + } + + #[inline] + pub fn finish_size_prefixed_schema_buffer<'a, 'b>( + fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, + root: flatbuffers::WIPOffset>, + ) { + fbb.finish_size_prefixed(root, Some(SCHEMA_IDENTIFIER)); + } +} // pub mod reflection diff --git a/rust/reflection/src/reflection_verifier.rs b/rust/reflection/src/reflection_verifier.rs new file mode 100644 index 00000000000..43739147783 --- /dev/null +++ b/rust/reflection/src/reflection_verifier.rs @@ -0,0 +1,421 @@ +/* + * Copyright 2018 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::reflection_generated::reflection::{BaseType, Field, Object, Schema}; +use crate::{FlatbufferError, FlatbufferResult}; +use flatbuffers::{ + ForwardsUOffset, InvalidFlatbuffer, TableVerifier, UOffsetT, Vector, Verifiable, Verifier, + VerifierOptions, SIZE_UOFFSET, SIZE_VOFFSET, +}; +use std::collections::HashMap; + +/// Verifies a buffer against its schema with custom verification options. +pub fn verify_with_options( + buffer: &[u8], + schema: &Schema, + opts: &VerifierOptions, + buf_loc_to_obj_idx: &mut HashMap, +) -> FlatbufferResult<()> { + let mut verifier = Verifier::new(opts, buffer); + if let Some(table_object) = schema.root_table() { + if let core::result::Result::Ok(table_pos) = verifier.get_uoffset(0) { + // Inserts -1 as object index for root table + buf_loc_to_obj_idx.insert(table_pos.try_into()?, -1); + let mut verified = vec![false; buffer.len()]; + return verify_table( + &mut verifier, + &table_object, + table_pos.try_into()?, + schema, + &mut verified, + buf_loc_to_obj_idx, + ); + } + } + Err(FlatbufferError::InvalidSchema) +} + +fn verify_table( + verifier: &mut Verifier, + table_object: &Object, + table_pos: usize, + schema: &Schema, + verified: &mut [bool], + buf_loc_to_obj_idx: &mut HashMap, +) -> FlatbufferResult<()> { + if table_pos < verified.len() && verified[table_pos] { + return Ok(()); + } + + let mut table_verifier = verifier.visit_table(table_pos)?; + + for field in &table_object.fields() { + let field_name = field.name().to_owned(); + table_verifier = match field.type_().base_type() { + BaseType::UType | BaseType::UByte => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::Bool => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::Byte => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::Short => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::UShort => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::Int => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::UInt => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::Long => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::ULong => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::Float => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::Double => { + table_verifier.visit_field::(field_name, field.offset(), field.required())? + } + BaseType::String => table_verifier.visit_field::>( + field_name, + field.offset(), + field.required(), + )?, + BaseType::Vector => { + verify_vector(table_verifier, &field, schema, verified, buf_loc_to_obj_idx)? + } + BaseType::Obj => { + if let Some(field_pos) = table_verifier.deref(field.offset())? { + let object_index = field.type_().index(); + let child_obj = schema.objects().get(object_index.try_into()?); + if child_obj.is_struct() { + buf_loc_to_obj_idx.insert(field_pos, object_index); + verify_struct( + table_verifier.verifier(), + &child_obj, + field_pos, + schema, + buf_loc_to_obj_idx, + )? + } else { + let field_value = table_verifier.verifier().get_uoffset(field_pos)?; + let table_pos = field_pos.saturating_add(field_value.try_into()?); + buf_loc_to_obj_idx.insert(table_pos, object_index); + verify_table( + table_verifier.verifier(), + &child_obj, + table_pos, + schema, + verified, + buf_loc_to_obj_idx, + )?; + } + } else if field.required() { + return InvalidFlatbuffer::new_missing_required(field.name().to_string())?; + } + table_verifier + } + BaseType::Union => { + if let Some(field_pos) = table_verifier.deref(field.offset())? { + let field_value = table_verifier.verifier().get_uoffset(field_pos)?; + verify_union( + table_verifier, + &field, + field_pos.saturating_add(field_value.try_into()?), + schema, + verified, + buf_loc_to_obj_idx, + )? + } else if field.required() { + return InvalidFlatbuffer::new_missing_required(field.name().to_string())?; + } else { + table_verifier + } + } + _ => { + return Err(FlatbufferError::TypeNotSupported( + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )); + } + }; + } + + table_verifier.finish(); + verified[table_pos] = true; + Ok(()) +} + +fn verify_struct( + verifier: &mut Verifier, + struct_object: &Object, + struct_pos: usize, + schema: &Schema, + buf_loc_to_obj_idx: &mut HashMap, +) -> FlatbufferResult<()> { + verifier.range_in_buffer(struct_pos, struct_object.bytesize().try_into()?)?; + for field in &struct_object.fields() { + if field.type_().base_type() == BaseType::Obj { + let obj_idx = field.type_().index(); + let child_obj = schema.objects().get(obj_idx.try_into()?); + if child_obj.is_struct() { + let field_pos = struct_pos.saturating_add(field.offset().into()); + buf_loc_to_obj_idx.insert(field_pos, obj_idx); + verify_struct(verifier, &child_obj, field_pos, schema, buf_loc_to_obj_idx)?; + } + } + } + Ok(()) +} + +fn verify_vector<'a, 'b, 'c>( + mut table_verifier: TableVerifier<'a, 'b, 'c>, + field: &Field, + schema: &Schema, + verified: &mut [bool], + buf_loc_to_obj_idx: &mut HashMap, +) -> FlatbufferResult> { + let field_name = field.name().to_owned(); + match field.type_().element() { + BaseType::UType | BaseType::UByte => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Bool => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Byte => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Short => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::UShort => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Int => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::UInt => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Long => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::ULong => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Float => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Double => table_verifier + .visit_field::>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::String => table_verifier + .visit_field::>>>( + field_name, + field.offset(), + field.required(), + ) + .map_err(FlatbufferError::VerificationError), + BaseType::Obj => { + if let Some(field_pos) = table_verifier.deref(field.offset())? { + let verifier = table_verifier.verifier(); + let vector_offset = verifier.get_uoffset(field_pos)?; + let vector_pos = field_pos.saturating_add(vector_offset.try_into()?); + let vector_len = verifier.get_uoffset(vector_pos)?; + let vector_start = vector_pos.saturating_add(SIZE_UOFFSET); + let child_obj_idx = field.type_().index(); + let child_obj = schema.objects().get(child_obj_idx.try_into()?); + if child_obj.is_struct() { + let vector_size = vector_len.saturating_mul(child_obj.bytesize().try_into()?); + verifier.range_in_buffer(vector_start, vector_size.try_into()?)?; + let vector_range = core::ops::Range { + start: vector_start, + end: vector_start.saturating_add(vector_size.try_into()?), + }; + for struct_pos in vector_range.step_by(child_obj.bytesize().try_into()?) { + buf_loc_to_obj_idx.insert(struct_pos, child_obj_idx); + verify_struct( + verifier, + &child_obj, + struct_pos, + schema, + buf_loc_to_obj_idx, + )?; + } + } else { + verifier.is_aligned::(vector_start)?; + let vector_size = vector_len.saturating_mul(SIZE_UOFFSET.try_into()?); + verifier.range_in_buffer(vector_start, vector_size.try_into()?)?; + let vector_range = core::ops::Range { + start: vector_start, + end: vector_start.saturating_add(vector_size.try_into()?), + }; + for element_pos in vector_range.step_by(SIZE_UOFFSET) { + let table_pos = element_pos + .saturating_add(verifier.get_uoffset(element_pos)?.try_into()?); + buf_loc_to_obj_idx.insert(table_pos, child_obj_idx); + verify_table( + verifier, + &child_obj, + table_pos, + schema, + verified, + buf_loc_to_obj_idx, + )?; + } + } + } else if field.required() { + return InvalidFlatbuffer::new_missing_required(field.name().to_string())?; + } + Ok(table_verifier) + } + _ => { + return Err(FlatbufferError::TypeNotSupported( + field + .type_() + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )) + } + } +} + +fn verify_union<'a, 'b, 'c>( + mut table_verifier: TableVerifier<'a, 'b, 'c>, + field: &Field, + union_pos: usize, + schema: &Schema, + verified: &mut [bool], + buf_loc_to_obj_idx: &mut HashMap, +) -> FlatbufferResult> { + let union_enum = schema.enums().get(field.type_().index().try_into()?); + if union_enum.values().is_empty() { + return Err(FlatbufferError::InvalidUnionEnum); + } + + let enum_offset = field.offset() - u16::try_from(SIZE_VOFFSET)?; + if let Some(enum_pos) = table_verifier.deref(enum_offset)? { + let enum_value = table_verifier.verifier().get_u8(enum_pos)?; + let enum_type = union_enum + .values() + .get(enum_value.into()) + .union_type() + .ok_or(FlatbufferError::InvalidUnionEnum)?; + + match enum_type.base_type() { + BaseType::String => <&str>::run_verifier(table_verifier.verifier(), union_pos)?, + BaseType::Obj => { + let child_obj = schema.objects().get(enum_type.index().try_into()?); + buf_loc_to_obj_idx.insert(union_pos, enum_type.index()); + if child_obj.is_struct() { + verify_struct( + table_verifier.verifier(), + &child_obj, + union_pos, + schema, + buf_loc_to_obj_idx, + )? + } else { + verify_table( + table_verifier.verifier(), + &child_obj, + union_pos, + schema, + verified, + buf_loc_to_obj_idx, + )?; + } + } + _ => { + return Err(FlatbufferError::TypeNotSupported( + enum_type + .base_type() + .variant_name() + .unwrap_or_default() + .to_string(), + )) + } + } + } else { + return InvalidFlatbuffer::new_inconsistent_union( + format!("{}_type", field.name()), + field.name().to_string(), + )?; + } + + verified[union_pos] = true; + Ok(table_verifier) +} diff --git a/rust/reflection/src/safe_buffer.rs b/rust/reflection/src/safe_buffer.rs new file mode 100644 index 00000000000..5923f0fb7ff --- /dev/null +++ b/rust/reflection/src/safe_buffer.rs @@ -0,0 +1,319 @@ +/* + * Copyright 2025 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::r#struct::Struct; +use crate::reflection_generated::reflection::{Field, Schema}; +use crate::reflection_verifier::verify_with_options; +use crate::{ + get_any_field_float, get_any_field_float_in_struct, get_any_field_integer, + get_any_field_integer_in_struct, get_any_field_string, get_any_field_string_in_struct, + get_any_root, get_field_float, get_field_integer, get_field_string, get_field_struct, + get_field_struct_in_struct, get_field_table, get_field_vector, FlatbufferError, + FlatbufferResult, ForwardsUOffset, +}; +use flatbuffers::{Follow, Table, Vector, VerifierOptions}; +use num::traits::float::Float; +use num::traits::int::PrimInt; +use num::traits::FromPrimitive; +use std::collections::HashMap; + +#[derive(Debug)] +pub struct SafeBuffer<'a> { + buf: &'a [u8], + schema: &'a Schema<'a>, + buf_loc_to_obj_idx: HashMap, +} + +impl<'a> SafeBuffer<'a> { + pub fn new(buf: &'a [u8], schema: &'a Schema) -> FlatbufferResult { + Self::new_with_options(buf, schema, &VerifierOptions::default()) + } + + pub fn new_with_options( + buf: &'a [u8], + schema: &'a Schema, + opts: &VerifierOptions, + ) -> FlatbufferResult { + let mut buf_loc_to_obj_idx = HashMap::new(); + verify_with_options(&buf, schema, opts, &mut buf_loc_to_obj_idx)?; + Ok(SafeBuffer { + buf, + schema, + buf_loc_to_obj_idx, + }) + } + + /// Gets the root table in the buffer. + pub fn get_root(&self) -> SafeTable { + // SAFETY: the buffer was verified during construction. + let table = unsafe { get_any_root(self.buf) }; + + SafeTable { + safe_buf: self, + loc: table.loc(), + } + } + + fn find_field_by_name( + &self, + buf_loc: usize, + field_name: &str, + ) -> FlatbufferResult> { + Ok(self + .get_all_fields(buf_loc)? + .lookup_by_key(field_name, |field: &Field<'_>, key| { + field.key_compare_with_value(key) + })) + } + + fn get_all_fields(&self, buf_loc: usize) -> FlatbufferResult>> { + if let Some(&obj_idx) = self.buf_loc_to_obj_idx.get(&buf_loc) { + let obj = if obj_idx == -1 { + self.schema.root_table().unwrap() + } else { + self.schema.objects().get(obj_idx.try_into()?) + }; + Ok(obj.fields()) + } else { + Err(FlatbufferError::InvalidTableOrStruct) + } + } +} + +#[derive(Debug)] +pub struct SafeTable<'a> { + safe_buf: &'a SafeBuffer<'a>, + loc: usize, +} + +impl<'a> SafeTable<'a> { + /// Gets an integer table field given its exact type. Returns default integer value if the field is not set. Returns [None] if no default value is found. Returns error if + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table or + /// the field type doesn't match. + pub fn get_field_integer Follow<'b, Inner = T> + PrimInt + FromPrimitive>( + &self, + field_name: &str, + ) -> FlatbufferResult> { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { get_field_integer::(&Table::new(&self.safe_buf.buf, self.loc), &field) } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Gets a floating point table field given its exact type. Returns default float value if the field is not set. Returns [None] if no default value is found. Returns error if + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table or + /// the field type doesn't match. + pub fn get_field_float Follow<'b, Inner = T> + Float>( + &self, + field_name: &str, + ) -> FlatbufferResult> { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { get_field_float::(&Table::new(&self.safe_buf.buf, self.loc), &field) } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Gets a String table field given its exact type. Returns empty string if the field is not set. Returns [None] if no default value is found. Returns error if + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table or + /// the field type doesn't match. + pub fn get_field_string(&self, field_name: &str) -> FlatbufferResult> { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { get_field_string(&Table::new(&self.safe_buf.buf, self.loc), &field) } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Gets a [SafeStruct] table field given its exact type. Returns [None] if the field is not set. Returns error if + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table or + /// the field type doesn't match. + pub fn get_field_struct(&self, field_name: &str) -> FlatbufferResult>> { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + let optional_st = + unsafe { get_field_struct(&Table::new(&self.safe_buf.buf, self.loc), &field)? }; + Ok(optional_st.map(|st| SafeStruct { + safe_buf: self.safe_buf, + loc: st.loc(), + })) + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Gets a Vector table field given its exact type. Returns empty vector if the field is not set. Returns error if + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table or + /// the field type doesn't match. + pub fn get_field_vector>( + &self, + field_name: &str, + ) -> FlatbufferResult>> { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { get_field_vector(&Table::new(&self.safe_buf.buf, self.loc), &field) } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Gets a [SafeTable] table field given its exact type. Returns [None] if the field is not set. Returns error if + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table or + /// the field type doesn't match. + pub fn get_field_table(&self, field_name: &str) -> FlatbufferResult>> { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + let optional_table = + unsafe { get_field_table(&Table::new(&self.safe_buf.buf, self.loc), &field)? }; + Ok(optional_table.map(|t| SafeTable { + safe_buf: self.safe_buf, + loc: t.loc(), + })) + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Returns the value of any table field as a 64-bit int, regardless of what type it is. Returns default integer if the field is not set or error if + /// the value cannot be parsed as integer or + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table. + /// [num_traits](https://docs.rs/num-traits/latest/num_traits/cast/trait.NumCast.html) is used for number casting. + pub fn get_any_field_integer(&self, field_name: &str) -> FlatbufferResult { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { get_any_field_integer(&Table::new(&self.safe_buf.buf, self.loc), &field) } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Returns the value of any table field as a 64-bit floating point, regardless of what type it is. Returns default float if the field is not set or error if + /// the value cannot be parsed as float or + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table. + pub fn get_any_field_float(&self, field_name: &str) -> FlatbufferResult { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { get_any_field_float(&Table::new(&self.safe_buf.buf, self.loc), &field) } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Returns the string representation of any table field value (e.g. integer 123 is returned as "123"), regardless of what type it is. Returns empty string if the field is not set. Returns error if + /// the table doesn't match the buffer or + /// the [field_name] doesn't match the table. + pub fn get_any_field_string(&self, field_name: &str) -> FlatbufferResult { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { + Ok(get_any_field_string( + &Table::new(&self.safe_buf.buf, self.loc), + &field, + self.safe_buf.schema, + )) + } + } else { + Err(FlatbufferError::FieldNotFound) + } + } +} + +#[derive(Debug)] +pub struct SafeStruct<'a> { + safe_buf: &'a SafeBuffer<'a>, + loc: usize, +} + +impl<'a> SafeStruct<'a> { + /// Gets a [SafeStruct] struct field given its exact type. Returns error if + /// the struct doesn't match the buffer or + /// the [field_name] doesn't match the struct or + /// the field type doesn't match. + pub fn get_field_struct(&self, field_name: &str) -> FlatbufferResult> { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + let st = unsafe { + get_field_struct_in_struct(&Struct::new(&self.safe_buf.buf, self.loc), &field)? + }; + Ok(SafeStruct { + safe_buf: self.safe_buf, + loc: st.loc(), + }) + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Returns the value of any struct field as a 64-bit int, regardless of what type it is. Returns error if + /// the struct doesn't match the buffer or + /// the [field_name] doesn't match the struct or + /// the value cannot be parsed as integer. + pub fn get_any_field_integer(&self, field_name: &str) -> FlatbufferResult { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { + get_any_field_integer_in_struct(&Struct::new(&self.safe_buf.buf, self.loc), &field) + } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Returns the value of any struct field as a 64-bit floating point, regardless of what type it is. Returns error if + /// the struct doesn't match the buffer or + /// the [field_name] doesn't match the struct or + /// the value cannot be parsed as float. + pub fn get_any_field_float(&self, field_name: &str) -> FlatbufferResult { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { + get_any_field_float_in_struct(&Struct::new(&self.safe_buf.buf, self.loc), &field) + } + } else { + Err(FlatbufferError::FieldNotFound) + } + } + + /// Returns the string representation of any struct field value (e.g. integer 123 is returned as "123"), regardless of what type it is. Returns error if + /// the struct doesn't match the buffer or + /// the [field_name] doesn't match the struct. + pub fn get_any_field_string(&self, field_name: &str) -> FlatbufferResult { + if let Some(field) = self.safe_buf.find_field_by_name(self.loc, field_name)? { + // SAFETY: the buffer was verified during construction. + unsafe { + Ok(get_any_field_string_in_struct( + &Struct::new(&self.safe_buf.buf, self.loc), + &field, + self.safe_buf.schema, + )) + } + } else { + Err(FlatbufferError::FieldNotFound) + } + } +} diff --git a/rust/reflection/src/struct.rs b/rust/reflection/src/struct.rs new file mode 100644 index 00000000000..0952fafd4fa --- /dev/null +++ b/rust/reflection/src/struct.rs @@ -0,0 +1,61 @@ +/* + * Copyright 2018 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 flatbuffers::Follow; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Struct<'a> { + buf: &'a [u8], + loc: usize, +} + +impl<'a> Struct<'a> { + #[inline] + pub fn buf(&self) -> &'a [u8] { + self.buf + } + + #[inline] + pub fn loc(&self) -> usize { + self.loc + } + + /// # Safety + /// + /// [buf] must contain a valid struct at [loc] + #[inline] + pub unsafe fn new(buf: &'a [u8], loc: usize) -> Self { + Struct { buf, loc } + } + + /// Retrieves the value at the provided [byte_loc]. No field in [Struct] is optional. + /// + /// # Safety + /// + /// The value of the corresponding slot must have type T + #[inline] + pub unsafe fn get + 'a>(&self, byte_loc: usize) -> T::Inner { + ::follow(self.buf, self.loc + byte_loc) + } +} + +impl<'a> Follow<'a> for Struct<'a> { + type Inner = Struct<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Struct { buf, loc } + } +} diff --git a/tests/rust_reflection_test/.gitignore b/tests/rust_reflection_test/.gitignore new file mode 100644 index 00000000000..96ef6c0b944 --- /dev/null +++ b/tests/rust_reflection_test/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/tests/rust_reflection_test/Cargo.toml b/tests/rust_reflection_test/Cargo.toml new file mode 100644 index 00000000000..1e5328e126e --- /dev/null +++ b/tests/rust_reflection_test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust_reflection_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +flatbuffers-reflection = { path = "../../rust/reflection" } +flatbuffers = { path = "../../rust/flatbuffers" } +assert_approx_eq = "1.1.0" diff --git a/tests/rust_reflection_test/src/lib.rs b/tests/rust_reflection_test/src/lib.rs new file mode 100644 index 00000000000..f403e0d9808 --- /dev/null +++ b/tests/rust_reflection_test/src/lib.rs @@ -0,0 +1,2136 @@ +use flatbuffers_reflection::reflection::{root_as_schema, BaseType, Field}; +use flatbuffers_reflection::{ + get_any_field_float, get_any_field_float_in_struct, get_any_field_integer, + get_any_field_integer_in_struct, get_any_field_string, get_any_field_string_in_struct, + get_any_root, get_field_float, get_field_integer, get_field_string, get_field_struct, + get_field_struct_in_struct, get_field_table, get_field_vector, set_any_field_float, + set_any_field_integer, set_any_field_string, set_field, set_string, FlatbufferError, +}; +use flatbuffers_reflection::{SafeBuffer, Struct}; + +use flatbuffers::{FlatBufferBuilder, Table, VerifierOptions}; + +use std::error::Error; +use std::fs::File; +use std::io::Read; + +use assert_approx_eq::assert_approx_eq; + +#[allow(dead_code, unused_imports)] +#[path = "../../monster_test/mod.rs"] +mod monster_test_generated; +pub use monster_test_generated::my_game; + +#[test] +fn test_schema_correct_root_table() { + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + + let root_table = schema.root_table().unwrap(); + + assert_eq!(root_table.name(), "MyGame.Example.Monster"); + assert_eq!(root_table.declaration_file().unwrap(), "//monster_test.fbs"); +} + +#[test] +fn test_schema_correct_object() { + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + + assert_eq!( + schema + .objects() + .lookup_by_key("TableA", |object, key| object.key_compare_with_value(key)) + .unwrap() + .declaration_file() + .unwrap(), + "//include_test/include_test1.fbs" + ); + assert_eq!( + schema + .objects() + .lookup_by_key("MyGame.OtherNameSpace.Unused", |object, key| object + .key_compare_with_value(key)) + .unwrap() + .declaration_file() + .unwrap(), + "//include_test/sub/include_test2.fbs" + ); +} + +#[test] +fn test_schema_correct_enum() { + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + + assert_eq!( + schema + .enums() + .lookup_by_key("MyGame.OtherNameSpace.FromInclude", |en, key| en + .key_compare_with_value(key)) + .unwrap() + .declaration_file() + .unwrap(), + "//include_test/sub/include_test2.fbs" + ); +} + +#[test] +fn test_schema_correct_file() { + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + + assert_eq!(schema.fbs_files().unwrap().len(), 3); + + let fbs0 = schema.fbs_files().unwrap().get(0); + assert_eq!(fbs0.filename(), "//include_test/include_test1.fbs"); + let fbs0_includes = fbs0.included_filenames().unwrap(); + assert_eq!(fbs0_includes.len(), 2); + assert_eq!(fbs0_includes.get(0), "//include_test/include_test1.fbs"); + assert_eq!(fbs0_includes.get(1), "//include_test/sub/include_test2.fbs"); + + let fbs1 = schema.fbs_files().unwrap().get(1); + assert_eq!(fbs1.filename(), "//include_test/sub/include_test2.fbs"); + let fbs1_includes = fbs1.included_filenames().unwrap(); + assert_eq!(fbs1_includes.len(), 2); + assert_eq!(fbs1_includes.get(0), "//include_test/include_test1.fbs"); + assert_eq!(fbs1_includes.get(1), "//include_test/sub/include_test2.fbs"); + + let fbs2 = schema.fbs_files().unwrap().get(2); + assert_eq!(fbs2.filename(), "//monster_test.fbs"); + let fbs2_includes = fbs2.included_filenames().unwrap(); + assert_eq!(fbs2_includes.len(), 1); + assert_eq!(fbs2_includes.get(0), "//include_test/include_test1.fbs"); +} + +#[test] +fn test_schema_correct_table_field() { + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let root_table = root_as_schema(schema_buffer.as_slice()) + .unwrap() + .root_table() + .unwrap(); + + let fields = root_table.fields(); + + let hp_field_option = + fields.lookup_by_key("hp", |field, key| field.key_compare_with_value(key)); + assert!(hp_field_option.is_some()); + let hp_field = hp_field_option.unwrap(); + assert_eq!(hp_field.name(), "hp"); + assert_eq!(hp_field.id(), 2); + assert_eq!(hp_field.type_().base_type(), BaseType::Short); + + let friendly_field_option = + fields.lookup_by_key("friendly", |field, key| field.key_compare_with_value(key)); + assert!(friendly_field_option.is_some()); + assert!(friendly_field_option.unwrap().attributes().is_some()); + assert!(friendly_field_option + .unwrap() + .attributes() + .unwrap() + .lookup_by_key("priority", |key_value, key| key_value + .key_compare_with_value(key)) + .is_some()); +} + +#[test] +fn test_schema_correct_table_field_nullability() { + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let root_table = root_as_schema(schema_buffer.as_slice()) + .unwrap() + .root_table() + .unwrap(); + + let fields = root_table.fields(); + + assert_eq!( + fields + .lookup_by_key("hp", |field, key| field.key_compare_with_value(key)) + .unwrap() + .optional(), + false + ); + assert_eq!( + fields + .lookup_by_key("pos", |field, key| field.key_compare_with_value(key)) + .unwrap() + .optional(), + true + ); + assert_eq!( + fields + .lookup_by_key("name", |field, key| field.key_compare_with_value(key)) + .unwrap() + .optional(), + false + ); +} + +#[test] +fn test_schema_correct_child_table_index() { + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let root_table = schema.root_table().unwrap(); + + let fields = root_table.fields(); + + let pos_field_option = + fields.lookup_by_key("pos", |field, key| field.key_compare_with_value(key)); + assert!(pos_field_option.is_some()); + let pos_field = pos_field_option.unwrap(); + assert_eq!(pos_field.type_().base_type(), BaseType::Obj); + let pos_table = schema.objects().get(pos_field.type_().index() as usize); + assert_eq!(pos_table.name(), "MyGame.Example.Vec3"); +} + +#[test] +fn test_buffer_integer_same_type_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let value = unsafe { get_field_integer::(&root_table, &i16_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), Some(32767)); +} + +#[test] +fn test_buffer_integer_diff_type_same_size_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let value = unsafe { get_field_integer::(&root_table, &i16_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), Some(32767)); +} + +#[test] +fn test_buffer_integer_diff_size_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let value = unsafe { get_field_integer::(&root_table, &i16_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("Short")) + ); +} + +#[test] +fn test_buffer_float_same_type_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let value = unsafe { get_field_float::(&root_table, &f32_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), Some(3.14)); +} + +#[test] +fn test_buffer_float_diff_type_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let value = unsafe { get_field_float::(&root_table, &f32_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_string_same_type_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let value = unsafe { get_field_string(&root_table, &string_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), Some("MyMonster")); +} + +#[test] +fn test_buffer_string_diff_type_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let value = unsafe { get_field_string(&root_table, &f32_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("String"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_struct_same_type_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + + let res = unsafe { get_field_struct(&root_table, &struct_field) }; + + assert!(res.is_ok()); + let optional_value = res.unwrap(); + assert!(optional_value.is_some()); + assert_eq!(optional_value.unwrap().buf(), &buffer); + assert!(optional_value.unwrap().loc() > root_table.loc()); +} + +#[test] +fn test_buffer_struct_diff_type_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { get_field_struct(&root_table, &f32_field) }; + + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("Obj"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_vector_same_type_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let vector_field = get_schema_field(&schema, "inventory"); + + let value = unsafe { get_field_vector::(&root_table, &vector_field) }; + + assert!(value.is_ok()); + let optional_vector = value.unwrap(); + assert!(optional_vector.is_some()); + let vector = optional_vector.unwrap(); + assert_eq!(vector.len(), 5); + assert_eq!(vector.get(0), 0); + assert_eq!(vector.get(1), 1); + assert_eq!(vector.get(2), 2); + assert_eq!(vector.get(3), 3); + assert_eq!(vector.get(4), 4); +} + +#[test] +fn test_buffer_vector_diff_type_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let value = unsafe { get_field_vector::(&root_table, &f32_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("u8"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_table_same_type_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let table_field = get_schema_field(&schema, "testempty"); + + let value = unsafe { get_field_table(&root_table, &table_field) }; + + assert!(value.is_ok()); + let optional_nested_table = value.unwrap(); + assert!(optional_nested_table.is_some()); + let nested_table = optional_nested_table.unwrap(); + let nested_table_fields = &root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(table_field.type_().index() as usize) + .fields(); + let nested_table_field = nested_table_fields + .lookup_by_key("id", |field: &Field<'_>, key| { + field.key_compare_with_value(key) + }) + .unwrap(); + let nested_table_id = unsafe { get_field_string(&nested_table, &nested_table_field) }; + assert!(nested_table_id.is_ok()); + assert_eq!(nested_table_id.unwrap(), Some("Fred")); +} + +#[test] +fn test_buffer_table_diff_type_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let value = unsafe { get_field_table(&root_table, &f32_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("Obj"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_nested_struct_same_type_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let nested_struct_field = struct_schema + .fields() + .lookup_by_key("test3", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let res = unsafe { get_field_struct_in_struct(&struct_value, &nested_struct_field) }; + + assert!(res.is_ok()); + let value = res.unwrap(); + assert_eq!(value.buf(), &buffer); + assert!(value.loc() > struct_value.loc()); +} + +#[test] +fn test_buffer_nested_struct_diff_type_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let nested_float_field = struct_schema + .fields() + .lookup_by_key("x", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let res = unsafe { get_field_struct_in_struct(&struct_value, &nested_float_field) }; + + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("Obj"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_i16_as_integer_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let value = unsafe { get_any_field_integer(&root_table, &i16_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 32767); +} + +#[test] +fn test_buffer_f32_as_integer_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let value = unsafe { get_any_field_integer(&root_table, &f32_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 3); +} + +#[test] +fn test_buffer_inf_as_integer_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let inf_field = get_schema_field(&schema, "inf_default"); + + let value = unsafe { get_any_field_integer(&root_table, &inf_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 0); +} + +#[test] +fn test_buffer_bool_as_integer_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let bool_field = get_schema_field(&schema, "testbool"); + + let value = unsafe { get_any_field_integer(&root_table, &bool_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 0); +} + +#[test] +fn test_buffer_nan_as_integer_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let nan_field = get_schema_field(&schema, "nan_default"); + + let value = unsafe { get_any_field_integer(&root_table, &nan_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_string_as_integer_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let value = unsafe { get_any_field_integer(&root_table, &string_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("String")) + ); +} + +#[test] +fn test_buffer_i16_as_float_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let value = unsafe { get_any_field_float(&root_table, &i16_field) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 32767f64); +} + +#[test] +fn test_buffer_f32_as_float_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let value = unsafe { get_any_field_float(&root_table, &f32_field) }; + + assert!(value.is_ok()); + assert_approx_eq!(value.unwrap(), 3.14); +} + +#[test] +fn test_buffer_string_as_float_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let value = unsafe { get_any_field_float(&root_table, &string_field) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("String")) + ); +} + +#[test] +fn test_buffer_i16_as_string_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let value = unsafe { + get_any_field_string( + &root_table, + &i16_field, + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert_eq!(value, String::from("32767")); +} + +#[test] +fn test_buffer_f32_as_string_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let mut value = unsafe { + get_any_field_string( + &root_table, + &f32_field, + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + value.truncate(4); + assert_eq!(value, String::from("3.14")); +} + +#[test] +fn test_buffer_string_as_string_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let value = unsafe { + get_any_field_string( + &root_table, + &string_field, + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert_eq!(value, "MyMonster"); +} + +#[test] +fn test_buffer_i16_in_struct_as_integer_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let nested_struct_field = struct_schema + .fields() + .lookup_by_key("test3", |field, key| field.key_compare_with_value(key)) + .unwrap(); + let nested_struct_value = + unsafe { get_field_struct_in_struct(&struct_value, &nested_struct_field).unwrap() }; + let nested_struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(nested_struct_field.type_().index() as usize); + let i16_in_struct = nested_struct_schema + .fields() + .lookup_by_key("a", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_integer_in_struct(&nested_struct_value, &i16_in_struct) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 5); +} + +#[test] +fn test_buffer_f32_in_struct_as_integer_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let f32_in_struct = struct_schema + .fields() + .lookup_by_key("x", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_integer_in_struct(&struct_value, &f32_in_struct) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 1); +} + +#[test] +fn test_buffer_enum_in_struct_as_integer_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let enum_in_struct = struct_schema + .fields() + .lookup_by_key("test2", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_integer_in_struct(&struct_value, &enum_in_struct) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 2); +} + +#[test] +fn test_buffer_struct_in_struct_as_integer_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let struct_in_struct = struct_schema + .fields() + .lookup_by_key("test3", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_integer_in_struct(&struct_value, &struct_in_struct) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("Obj")) + ); +} + +#[test] +fn test_buffer_i16_in_struct_as_float_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let nested_struct_field = struct_schema + .fields() + .lookup_by_key("test3", |field, key| field.key_compare_with_value(key)) + .unwrap(); + let nested_struct_value = + unsafe { get_field_struct_in_struct(&struct_value, &nested_struct_field).unwrap() }; + let nested_struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(nested_struct_field.type_().index() as usize); + let i16_in_struct = nested_struct_schema + .fields() + .lookup_by_key("a", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_float_in_struct(&nested_struct_value, &i16_in_struct) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 5f64); +} + +#[test] +fn test_buffer_f32_in_struct_as_float_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let f32_in_struct = struct_schema + .fields() + .lookup_by_key("x", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_float_in_struct(&struct_value, &f32_in_struct) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 1f64); +} + +#[test] +fn test_buffer_enum_in_struct_as_float_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let enum_in_struct = struct_schema + .fields() + .lookup_by_key("test2", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_float_in_struct(&struct_value, &enum_in_struct) }; + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 2f64); +} + +#[test] +fn test_buffer_struct_in_struct_as_float_fails() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let struct_in_struct = struct_schema + .fields() + .lookup_by_key("test3", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { get_any_field_float_in_struct(&struct_value, &struct_in_struct) }; + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("Obj")) + ); +} + +#[test] +fn test_buffer_i16_in_struct_as_string_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let nested_struct_field = struct_schema + .fields() + .lookup_by_key("test3", |field, key| field.key_compare_with_value(key)) + .unwrap(); + let nested_struct_value = + unsafe { get_field_struct_in_struct(&struct_value, &nested_struct_field).unwrap() }; + let nested_struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(nested_struct_field.type_().index() as usize); + let i16_in_struct = nested_struct_schema + .fields() + .lookup_by_key("a", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { + get_any_field_string_in_struct( + &nested_struct_value, + &i16_in_struct, + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert_eq!(value, String::from("5")); +} + +#[test] +fn test_buffer_f32_in_struct_as_string_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let f32_in_struct = struct_schema + .fields() + .lookup_by_key("x", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { + get_any_field_string_in_struct( + &struct_value, + &f32_in_struct, + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert_eq!(value, String::from("1")); +} + +#[test] +fn test_buffer_enum_in_struct_as_string_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let enum_in_struct = struct_schema + .fields() + .lookup_by_key("test2", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { + get_any_field_string_in_struct( + &struct_value, + &enum_in_struct, + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert_eq!(value, String::from("2")); +} + +#[test] +fn test_buffer_struct_in_struct_as_string_succeeds() { + let buffer = create_test_buffer(); + let root_table = unsafe { get_any_root(&buffer) }; + let schema = load_file_as_buffer("../monster_test.bfbs"); + let struct_field = get_schema_field(&schema, "pos"); + let struct_value = unsafe { + get_field_struct(&root_table, &struct_field) + .unwrap() + .unwrap() + }; + let struct_schema = root_as_schema(schema.as_slice()) + .unwrap() + .objects() + .get(struct_field.type_().index() as usize); + let struct_in_struct = struct_schema + .fields() + .lookup_by_key("test3", |field, key| field.key_compare_with_value(key)) + .unwrap(); + + let value = unsafe { + get_any_field_string_in_struct( + &struct_value, + &struct_in_struct, + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert_eq!(value, String::from("MyGame.Example.Test { a: 5, b: 6, }")); +} + +#[test] +fn test_buffer_set_valid_int_to_i16_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let res = unsafe { set_any_field_integer(&mut buffer, table_loc, &i16_field, 111) }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_integer::(&updated_table, &i16_field) }; + assert!(updated_value.is_ok()); + assert_eq!(updated_value.unwrap(), Some(111)); +} + +#[test] +fn test_buffer_set_integer_to_f32_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { set_any_field_integer(&mut buffer, table_loc, &f32_field, 111) }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_float::(&updated_table, &f32_field) }; + assert!(updated_value.is_ok()); + assert_approx_eq!(updated_value.unwrap().unwrap(), 111f32); +} + +#[test] +fn test_buffer_set_overflow_to_i16_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let res = unsafe { set_any_field_integer(&mut buffer, table_loc, &i16_field, 32768) }; + + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("Short")) + ); +} + +#[test] +fn test_buffer_set_integer_to_string_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let res = unsafe { set_any_field_integer(&mut buffer, table_loc, &string_field, 1) }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), FlatbufferError::SetValueNotSupported); +} + +#[test] +fn test_buffer_set_integer_to_unset_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let unset_field = get_schema_field(&schema, "testf3"); + + let res = unsafe { set_any_field_integer(&mut buffer, table_loc, &unset_field, 1) }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), FlatbufferError::SetValueNotSupported); +} + +#[test] +fn test_buffer_set_valid_float_to_f32_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { set_any_field_float(&mut buffer, table_loc, &f32_field, 111.11) }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_float::(&updated_table, &f32_field) }; + assert!(updated_value.is_ok()); + assert_approx_eq!(updated_value.unwrap().unwrap(), 111.11); +} + +#[test] +fn test_buffer_set_float_to_i16_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let res = unsafe { set_any_field_float(&mut buffer, table_loc, &i16_field, 111.11) }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_integer::(&updated_table, &i16_field) }; + assert!(updated_value.is_ok()); + assert_eq!(updated_value.unwrap(), Some(111)); +} + +#[test] +fn test_buffer_set_overflow_to_f32_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { set_any_field_float(&mut buffer, table_loc, &f32_field, f64::MAX) }; + + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_set_float_to_string_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let res = unsafe { set_any_field_float(&mut buffer, table_loc, &string_field, 1.1) }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), FlatbufferError::SetValueNotSupported); +} + +#[test] +fn test_buffer_set_float_to_unset_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let unset_field = get_schema_field(&schema, "testf3"); + + let res = unsafe { set_any_field_float(&mut buffer, table_loc, &unset_field, 1.1) }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), FlatbufferError::SetValueNotSupported); +} + +#[test] +fn test_buffer_set_float_str_to_f32_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { set_any_field_string(&mut buffer, table_loc, &f32_field, "111.11") }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_float::(&updated_table, &f32_field) }; + assert!(updated_value.is_ok()); + assert_approx_eq!(updated_value.unwrap().unwrap(), 111.11); +} + +#[test] +fn test_buffer_set_int_str_to_i16_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let res = unsafe { set_any_field_string(&mut buffer, table_loc, &i16_field, "111") }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_integer::(&updated_table, &i16_field) }; + assert!(updated_value.is_ok()); + assert_eq!(updated_value.unwrap(), Some(111)); +} + +#[test] +fn test_buffer_set_non_num_str_to_f32_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { set_any_field_string(&mut buffer, table_loc, &f32_field, "any") }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err().to_string(), "invalid float literal"); +} + +#[test] +fn test_buffer_set_int_str_to_string_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let res = unsafe { set_any_field_string(&mut buffer, table_loc, &string_field, "1") }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), FlatbufferError::SetValueNotSupported); +} + +#[test] +fn test_buffer_set_int_str_to_unset_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let unset_field = get_schema_field(&schema, "testf3"); + + let res = unsafe { set_any_field_string(&mut buffer, table_loc, &unset_field, "1") }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), FlatbufferError::SetValueNotSupported); +} + +#[test] +fn test_buffer_set_i16_to_i16_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let res = unsafe { set_field::(&mut buffer, table_loc, &i16_field, 111) }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_integer::(&updated_table, &i16_field) }; + assert!(updated_value.is_ok()); + assert_eq!(updated_value.unwrap(), Some(111)); +} + +#[test] +fn test_buffer_set_i32_to_i16_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let i16_field = get_schema_field(&schema, "hp"); + + let res = unsafe { set_field::(&mut buffer, table_loc, &i16_field, 111) }; + + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i32"), String::from("Short")) + ); +} + +#[test] +fn test_buffer_set_f32_to_f32_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { set_field::(&mut buffer, table_loc, &f32_field, 111.11) }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_float::(&updated_table, &f32_field) }; + assert!(updated_value.is_ok()); + assert_approx_eq!(updated_value.unwrap().unwrap(), 111.11); +} + +#[test] +fn test_buffer_set_f64_to_f32_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { set_field::(&mut buffer, table_loc, &f32_field, 111.11) }; + + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("Float")) + ); +} + +#[test] +fn test_buffer_set_f32_to_f32_unset_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let unset_field = get_schema_field(&schema, "testf3"); + + let res = unsafe { set_field::(&mut buffer, table_loc, &unset_field, 111.11) }; + + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), FlatbufferError::SetValueNotSupported); +} + +#[test] +fn test_buffer_set_string_same_str_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let res = unsafe { + set_string( + &mut buffer, + table_loc, + &string_field, + "MyMonster", + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_string(&updated_table, &string_field) }; + assert!(updated_value.is_ok()); + assert_eq!(updated_value.unwrap(), Some("MyMonster")); +} + +#[test] +fn test_buffer_set_string_same_size_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let res = unsafe { + set_string( + &mut buffer, + table_loc, + &string_field, + "YoMonster", + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_string(&updated_table, &string_field) }; + assert!(updated_value.is_ok()); + assert_eq!(updated_value.unwrap(), Some("YoMonster")); +} + +#[test] +fn test_buffer_set_string_bigger_size_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let res = unsafe { + set_string( + &mut buffer, + table_loc, + &string_field, + "AStringWithSlightlyBiggerSize", + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_string(&updated_table, &string_field) }; + assert!(updated_value.is_ok()); + assert_eq!( + updated_value.unwrap(), + Some("AStringWithSlightlyBiggerSize") + ); +} + +#[test] +fn test_buffer_set_string_smaller_size_succeeds() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let string_field = get_schema_field(&schema, "name"); + + let res = unsafe { + set_string( + &mut buffer, + table_loc, + &string_field, + "s", + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert!(res.is_ok()); + let updated_table = unsafe { get_any_root(&buffer) }; + let updated_value = unsafe { get_field_string(&updated_table, &string_field) }; + assert!(updated_value.is_ok()); + assert_eq!(updated_value.unwrap(), Some("s")); +} + +#[test] +fn test_buffer_set_string_diff_type_fails() { + let mut buffer = create_test_buffer(); + let table_loc = unsafe { get_any_root(&buffer) }.loc(); + let schema = load_file_as_buffer("../monster_test.bfbs"); + let f32_field = get_schema_field(&schema, "testf"); + + let res = unsafe { + set_string( + &mut buffer, + table_loc, + &f32_field, + "any", + &root_as_schema(schema.as_slice()).unwrap(), + ) + }; + + assert!(res.is_err()); + assert_eq!( + res.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("String"), String::from("Float")) + ); +} + +#[test] +fn test_create_safe_buffer_default_options_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + + let safe_buffer = SafeBuffer::new(&buffer, &schema); + + assert!(safe_buffer.is_ok()); +} + +#[test] +fn test_create_safe_buffer_limit_max_depth_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let verify_options = VerifierOptions { + max_depth: 1, + ..Default::default() + }; + + let safe_buffer = SafeBuffer::new_with_options(&buffer, &schema, &verify_options); + + assert!(safe_buffer.is_err()); + assert!(format!("{:#?}", safe_buffer.err().unwrap()).contains("DepthLimitReached")); +} + +#[test] +fn test_create_safe_buffer_limit_max_table_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let verify_options = VerifierOptions { + max_tables: 1, + ..Default::default() + }; + + let safe_buffer = SafeBuffer::new_with_options(&buffer, &schema, &verify_options); + + assert!(safe_buffer.is_err()); + assert!(format!("{:#?}", safe_buffer.err().unwrap()).contains("TooManyTables")); +} + +#[test] +fn test_create_safe_buffer_limit_max_size_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let verify_options = VerifierOptions { + max_apparent_size: 1 << 6, + ..Default::default() + }; + + let safe_buffer = SafeBuffer::new_with_options(&buffer, &schema, &verify_options); + + assert!(safe_buffer.is_err()); + assert!(format!("{:#?}", safe_buffer.err().unwrap()).contains("ApparentSizeTooLarge")); +} + +#[test] +fn test_safe_buffer_integer_same_type_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_integer::("hp"); + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), Some(32767)); +} + +#[test] +fn test_safe_buffer_integer_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_integer::("nonexistent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_integer_diff_size_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_integer::("hp"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("Short")) + ); +} + +#[test] +fn test_safe_buffer_float_same_type_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_float::("testf"); + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), Some(3.14)); +} + +#[test] +fn test_safe_buffer_float_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_float::("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_float_diff_type_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_float::("testf"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("Float")) + ); +} + +#[test] +fn test_safe_buffer_string_same_type_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_string("name"); + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), Some("MyMonster")); +} + +#[test] +fn test_safe_buffer_string_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_string("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_string_diff_type_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_string("testf"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("String"), String::from("Float")) + ); +} + +#[test] +fn test_safe_buffer_struct_same_type_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_struct("pos"); + + assert!(value.is_ok()); + let optional_value = value.unwrap(); + assert!(optional_value.is_some()); + let safe_struct = optional_value.unwrap(); + assert!(safe_struct.get_field_struct("test3").is_ok()); +} + +#[test] +fn test_safe_buffer_struct_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_struct("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_struct_diff_type_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_struct("testf"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("Obj"), String::from("Float")) + ); +} + +#[test] +fn test_safe_buffer_vector_same_type_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_vector::("inventory"); + + assert!(value.is_ok()); + let optional_vector = value.unwrap(); + assert!(optional_vector.is_some()); + let vector = optional_vector.unwrap(); + assert_eq!(vector.len(), 5); + assert_eq!(vector.get(0), 0); + assert_eq!(vector.get(1), 1); + assert_eq!(vector.get(2), 2); + assert_eq!(vector.get(3), 3); + assert_eq!(vector.get(4), 4); +} + +#[test] +fn test_safe_buffer_vector_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_vector::("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_vector_diff_type_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_vector::("testf"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("u8"), String::from("Float")) + ); +} + +#[test] +fn test_safe_buffer_table_same_type_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_table("testempty"); + + assert!(value.is_ok()); + let optional_nested_table = value.unwrap(); + assert!(optional_nested_table.is_some()); + let nested_table = optional_nested_table.unwrap(); + let nested_field_value = nested_table.get_field_string("id"); + + assert!(nested_field_value.is_ok()); + assert_eq!(nested_field_value.unwrap(), Some("Fred")); +} + +#[test] +fn test_safe_buffer_table_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_table("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_table_diff_type_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_field_table("testf"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("Obj"), String::from("Float")) + ); +} + +#[test] +fn test_safe_buffer_i16_as_integer_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_integer("hp"); + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 32767); +} + +#[test] +fn test_safe_buffer_i16_as_integer_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_integer("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_string_as_integer_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_integer("name"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("String")) + ); +} + +#[test] +fn test_safe_buffer_i16_as_float_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_float("hp"); + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), 32767f64); +} + +#[test] +fn test_safe_buffer_i16_as_float_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_float("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_string_as_float_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_float("name"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("String")) + ); +} + +#[test] +fn test_safe_buffer_string_as_string_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_string("name"); + + assert!(value.is_ok()); + assert_eq!(value.unwrap(), "MyMonster"); +} + +#[test] +fn test_safe_buffer_i16_as_string_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + + let value = root_table.get_any_field_string("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_nested_struct_same_type_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let nested_struct_value = struct_value.get_field_struct("test3"); + + assert!(nested_struct_value.is_ok()); + let value = nested_struct_value.unwrap(); + assert!(value.get_any_field_integer("a").is_ok()); +} + +#[test] +fn test_safe_buffer_nested_struct_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let value = struct_value.get_field_struct("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_nested_struct_diff_type_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let nested_struct_value = struct_value.get_field_struct("x"); + + assert!(nested_struct_value.is_err()); + assert_eq!( + nested_struct_value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("Obj"), String::from("Float")) + ); +} + +#[test] +fn test_safe_buffer_enum_in_struct_as_integer_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let nested_enum_value = struct_value.get_any_field_integer("test2"); + + assert!(nested_enum_value.is_ok()); + assert_eq!(nested_enum_value.unwrap(), 2); +} + +#[test] +fn test_safe_buffer_enum_in_struct_as_integer_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let value = struct_value.get_any_field_integer("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_struct_in_struct_as_integer_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let nested_struct_value = struct_value.get_any_field_integer("test3"); + + assert!(nested_struct_value.is_err()); + assert_eq!( + nested_struct_value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("i64"), String::from("Obj")) + ); +} + +#[test] +fn test_safe_buffer_i16_in_struct_as_float_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + let nested_struct_value = struct_value.get_field_struct("test3").unwrap(); + + let nested_i16_value = nested_struct_value.get_any_field_float("a"); + + assert!(nested_i16_value.is_ok()); + assert_eq!(nested_i16_value.unwrap(), 5f64); +} + +#[test] +fn test_safe_buffer_child_in_struct_as_float_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let value = struct_value.get_any_field_float("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +#[test] +fn test_safe_buffer_struct_in_struct_as_float_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let value = struct_value.get_any_field_float("test3"); + + assert!(value.is_err()); + assert_eq!( + value.unwrap_err(), + FlatbufferError::FieldTypeMismatch(String::from("f64"), String::from("Obj")) + ); +} + +#[test] +fn test_safe_buffer_f32_in_struct_as_string_succeeds() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let nested_f32_value = struct_value.get_any_field_string("x"); + + assert!(nested_f32_value.is_ok()); + assert_eq!(nested_f32_value.unwrap(), String::from("1")); +} + +#[test] +fn test_safe_buffer_child_in_struct_as_string_invalid_field_fails() { + let buffer = create_test_buffer(); + let schema_buffer = load_file_as_buffer("../monster_test.bfbs"); + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let safe_buffer = SafeBuffer::new(&buffer, &schema).unwrap(); + let root_table = safe_buffer.get_root(); + let struct_value = root_table.get_field_struct("pos").unwrap().unwrap(); + + let value = struct_value.get_any_field_string("non_existent"); + + assert!(value.is_err()); + assert_eq!(value.unwrap_err(), FlatbufferError::FieldNotFound); +} + +fn load_file_as_buffer(path: &str) -> Vec { + std::fs::read(path).unwrap() +} + +fn get_schema_field<'a>(schema_buffer: &'a Vec, field_name: &'a str) -> Field<'a> { + let schema = root_as_schema(schema_buffer.as_slice()).unwrap(); + let root_table = schema.root_table().unwrap(); + let fields = root_table.fields(); + fields + .lookup_by_key(field_name, |field, key| field.key_compare_with_value(key)) + .unwrap() +} + +fn create_test_buffer() -> Vec { + let mut builder = FlatBufferBuilder::new(); + let mon = { + let s0 = builder.create_string("test1"); + let s1 = builder.create_string("test2"); + let fred_name = builder.create_string("Fred"); + + // can't inline creation of this Vec3 because we refer to it by reference, so it must live + // long enough to be used by MonsterArgs. + let pos = my_game::example::Vec3::new( + 1.0, + 2.0, + 3.0, + 3.0, + my_game::example::Color::Green, + &my_game::example::Test::new(5i16, 6i8), + ); + + let args = my_game::example::MonsterArgs { + hp: 32767, + testf: 3.14, + mana: 150, + name: Some(builder.create_string("MyMonster")), + pos: Some(&pos), + test_type: my_game::example::Any::Monster, + testempty: Some(my_game::example::Stat::create( + &mut builder, + &my_game::example::StatArgs { + id: Some(fred_name), + ..Default::default() + }, + )), + test: Some( + my_game::example::Monster::create( + &mut builder, + &my_game::example::MonsterArgs { + name: Some(fred_name), + ..Default::default() + }, + ) + .as_union_value(), + ), + inventory: Some(builder.create_vector(&[0u8, 1, 2, 3, 4])), + test4: Some(builder.create_vector(&[ + my_game::example::Test::new(10, 20), + my_game::example::Test::new(30, 40), + ])), + testarrayofstring: Some(builder.create_vector(&[s0, s1])), + ..Default::default() + }; + my_game::example::Monster::create(&mut builder, &args) + }; + my_game::example::finish_monster_buffer(&mut builder, mon); + let (flatbuf, start_loc) = builder.mut_finished_buffer(); + flatbuf[start_loc..].to_vec() +}