| /* | |
| * 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 num_traits::float::Float; | |
| use num_traits::int::PrimInt; | |
| use num_traits::FromPrimitive; | |
| 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<T, E = FlatbufferError> = core::result::Result<T, E>; | |
| /// 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 { | |
| <ForwardsUOffset<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<T: for<'a> Follow<'a, Inner = T> + PrimInt + FromPrimitive>( | |
| table: &Table, | |
| field: &Field, | |
| ) -> FlatbufferResult<Option<T>> { | |
| if size_of::<T>() != get_type_size(field.type_().base_type()) { | |
| return Err(FlatbufferError::FieldTypeMismatch( | |
| std::any::type_name::<T>().to_string(), | |
| field.type_().base_type().variant_name().unwrap_or_default().to_string(), | |
| )); | |
| } | |
| let default = T::from_i64(field.default_integer()); | |
| Ok(table.get::<T>(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<T: for<'a> Follow<'a, Inner = T> + Float>( | |
| table: &Table, | |
| field: &Field, | |
| ) -> FlatbufferResult<Option<T>> { | |
| if size_of::<T>() != get_type_size(field.type_().base_type()) { | |
| return Err(FlatbufferError::FieldTypeMismatch( | |
| std::any::type_name::<T>().to_string(), | |
| field.type_().base_type().variant_name().unwrap_or_default().to_string(), | |
| )); | |
| } | |
| let default = T::from(field.default_real()); | |
| Ok(table.get::<T>(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<Option<&'a str>> { | |
| 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::<ForwardsUOffset<&'a str>>(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<Option<Struct<'a>>> { | |
| // 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::<Struct>(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<Option<Vector<'a, T>>> { | |
| if field.type_().base_type() != BaseType::Vector | |
| || core::mem::size_of::<T>() != get_type_size(field.type_().element()) | |
| { | |
| return Err(FlatbufferError::FieldTypeMismatch( | |
| std::any::type_name::<T>().to_string(), | |
| field.type_().base_type().variant_name().unwrap_or_default().to_string(), | |
| )); | |
| } | |
| Ok(table.get::<ForwardsUOffset<Vector<'a, T>>>(field.offset(), Some(Vector::<T>::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<Option<Table<'a>>> { | |
| 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::<ForwardsUOffset<Table<'a>>>(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<i64> { | |
| 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<f64> { | |
| 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<Struct<'a>> { | |
| // 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::<Struct>(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<i64> { | |
| 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<f64> { | |
| 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::<f64>()?) | |
| } | |
| /// 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<T: EndianScalar>( | |
| 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::<T>() != get_type_size(field_type) { | |
| return Err(FlatbufferError::FieldTypeMismatch( | |
| std::any::type_name::<T>().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::<T>(&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<u8>, | |
| 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. | |
| // stdint crate defines intmax_t as an alias for c_long; use it directly to avoid extra | |
| // dependency. | |
| let mask = (size_of::<core::ffi::c_long>() - 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::<SOffsetT>( | |
| &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<usize> { | |
| 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::<T>()` at all the access layers. | |
| unsafe fn get_any_value_integer( | |
| base_type: BaseType, | |
| buf: &[u8], | |
| loc: usize, | |
| ) -> FlatbufferResult<i64> { | |
| 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::<i64>().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::<T>()` at all the access layers. | |
| unsafe fn get_any_value_float( | |
| base_type: BaseType, | |
| buf: &[u8], | |
| loc: usize, | |
| ) -> FlatbufferResult<f64> { | |
| 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::<f64>().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::<T>()` 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::<Table>::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 { | |
| // Escape the string | |
| field_value = format!("{:?}", field_value.as_str()); | |
| } | |
| 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::<bool>(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::<i64>(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::<f32>(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::<f64>(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::<u8>(buf, value)); | |
| } | |
| } | |
| } | |
| BaseType::Bool => { | |
| // SAFETY: buffer size is verified at the beginning of this function. | |
| unsafe { | |
| return Ok(emplace_scalar::<bool>(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::<i8>(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::<i16>(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::<u16>(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::<i32>(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::<u32>(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::<i64>(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::<u64>(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::<f32>(buf, value)); | |
| } | |
| } | |
| } | |
| } | |
| BaseType::Double => { | |
| // SAFETY: buffer size is verified at the beginning of this function. | |
| unsafe { | |
| return Ok(emplace_scalar::<f64>(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::<SOffsetT>(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::<SOffsetT>(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::<UOffsetT>(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::<UOffsetT>( | |
| 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::<UOffsetT>(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::<UOffsetT>( | |
| 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::<SOffsetT>(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<usize> { | |
| 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::<UOffsetT>(slice) | |
| } |