Rust full reflection (#8102)

* #Rust Create a crate for reflection

* #Rust Add a crate for reflection tests and helper to access schema

* #Rust Get root table of a buffer and access field with schema

* #Rust Add 'Struct' struct and corresponding getter

* #Rust Add functions of getting any table/struct field value as integer/float/string

* #Rust Add setters for scalar fields

* #Rust Add setter for string fields

* #Rust Add getter for Table/Vector fields

* #Rust Add buffer verification

* Add a 'SafeBuffer' struct which provides safe methods for reflection

It verifies buffer against schema during construction and provides all the unsafe getters in lib.rs in a safe way

---------

Co-authored-by: Derek Bailey <derekbailey@google.com>
This commit is contained in:
Chan Wang
2025-01-15 19:03:10 +01:00
committed by GitHub
parent 5414e04b45
commit 733e432bfd
13 changed files with 7181 additions and 29 deletions

1142
rust/reflection/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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<usize, i32>,
) -> 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<usize, i32>,
) -> 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::<u8>(field_name, field.offset(), field.required())?
}
BaseType::Bool => {
table_verifier.visit_field::<bool>(field_name, field.offset(), field.required())?
}
BaseType::Byte => {
table_verifier.visit_field::<i8>(field_name, field.offset(), field.required())?
}
BaseType::Short => {
table_verifier.visit_field::<i16>(field_name, field.offset(), field.required())?
}
BaseType::UShort => {
table_verifier.visit_field::<u16>(field_name, field.offset(), field.required())?
}
BaseType::Int => {
table_verifier.visit_field::<i32>(field_name, field.offset(), field.required())?
}
BaseType::UInt => {
table_verifier.visit_field::<u32>(field_name, field.offset(), field.required())?
}
BaseType::Long => {
table_verifier.visit_field::<i64>(field_name, field.offset(), field.required())?
}
BaseType::ULong => {
table_verifier.visit_field::<u64>(field_name, field.offset(), field.required())?
}
BaseType::Float => {
table_verifier.visit_field::<f32>(field_name, field.offset(), field.required())?
}
BaseType::Double => {
table_verifier.visit_field::<f64>(field_name, field.offset(), field.required())?
}
BaseType::String => table_verifier.visit_field::<ForwardsUOffset<&str>>(
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<usize, i32>,
) -> 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<usize, i32>,
) -> FlatbufferResult<TableVerifier<'a, 'b, 'c>> {
let field_name = field.name().to_owned();
match field.type_().element() {
BaseType::UType | BaseType::UByte => table_verifier
.visit_field::<ForwardsUOffset<Vector<u8>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::Bool => table_verifier
.visit_field::<ForwardsUOffset<Vector<bool>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::Byte => table_verifier
.visit_field::<ForwardsUOffset<Vector<i8>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::Short => table_verifier
.visit_field::<ForwardsUOffset<Vector<i16>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::UShort => table_verifier
.visit_field::<ForwardsUOffset<Vector<u16>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::Int => table_verifier
.visit_field::<ForwardsUOffset<Vector<i32>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::UInt => table_verifier
.visit_field::<ForwardsUOffset<Vector<u32>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::Long => table_verifier
.visit_field::<ForwardsUOffset<Vector<i64>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::ULong => table_verifier
.visit_field::<ForwardsUOffset<Vector<u64>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::Float => table_verifier
.visit_field::<ForwardsUOffset<Vector<f32>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::Double => table_verifier
.visit_field::<ForwardsUOffset<Vector<f64>>>(
field_name,
field.offset(),
field.required(),
)
.map_err(FlatbufferError::VerificationError),
BaseType::String => table_verifier
.visit_field::<ForwardsUOffset<Vector<ForwardsUOffset<&str>>>>(
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::<UOffsetT>(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<usize, i32>,
) -> FlatbufferResult<TableVerifier<'a, 'b, 'c>> {
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)
}

View File

@@ -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<usize, i32>,
}
impl<'a> SafeBuffer<'a> {
pub fn new(buf: &'a [u8], schema: &'a Schema) -> FlatbufferResult<Self> {
Self::new_with_options(buf, schema, &VerifierOptions::default())
}
pub fn new_with_options(
buf: &'a [u8],
schema: &'a Schema,
opts: &VerifierOptions,
) -> FlatbufferResult<Self> {
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<Option<Field>> {
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<Vector<ForwardsUOffset<Field>>> {
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<T: for<'b> Follow<'b, Inner = T> + PrimInt + FromPrimitive>(
&self,
field_name: &str,
) -> FlatbufferResult<Option<T>> {
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::<T>(&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<T: for<'b> Follow<'b, Inner = T> + Float>(
&self,
field_name: &str,
) -> FlatbufferResult<Option<T>> {
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::<T>(&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<Option<&str>> {
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<Option<SafeStruct<'a>>> {
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<T: Follow<'a, Inner = T>>(
&self,
field_name: &str,
) -> FlatbufferResult<Option<Vector<'a, T>>> {
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<Option<SafeTable<'a>>> {
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<i64> {
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<f64> {
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<String> {
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<SafeStruct<'a>> {
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<i64> {
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<f64> {
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<String> {
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)
}
}
}

View File

@@ -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<T: Follow<'a> + 'a>(&self, byte_loc: usize) -> T::Inner {
<T>::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 }
}
}