Skip to content

Commit c1cb450

Browse files
authored
Merge pull request #1 from Muon/master
This PR fixes several bugs and adds support for missing features to the flatbuffers and flatbuffers_reflection crates. * fixes #8550 * fixes #8548 * adds reflection support for vectors of values of unknown type * adds verification support for vectors of unions * makes the unsafe get_field_* functions infallible as their success is implied by the unsafe precondition * adds the unsafe function Vector::cast() for casting between vector types * exposes get_type_size() * adds Struct::bytes() to get a slice to a byte buffer which starts with the struct value
2 parents bd1b2d0 + 4bfe9e2 commit c1cb450

File tree

8 files changed

+407
-254
lines changed

8 files changed

+407
-254
lines changed

rust/flatbuffers/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ pub use crate::push::{Push, PushAlignment};
5656
pub use crate::table::{buffer_has_identifier, Table};
5757
pub use crate::vector::{follow_cast_ref, Vector, VectorIter};
5858
pub use crate::verifier::{
59-
ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, TableVerifier, Verifiable, Verifier,
60-
VerifierOptions,
59+
ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, TableVerifier, Verifiable,
60+
Verifier, VerifierOptions,
6161
};
6262
pub use crate::vtable::field_index_to_field_offset;
6363
pub use bitflags;

rust/flatbuffers/src/vector.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ impl<'a, T: 'a> Vector<'a, T> {
9090
let len = self.len();
9191
&self.0[self.1 + SIZE_UOFFSET..self.1 + SIZE_UOFFSET + sz * len]
9292
}
93+
94+
/// # Safety
95+
///
96+
/// The underlying bytes must be interpretable as a vector of the *same number* of `U`'s.
97+
#[inline(always)]
98+
pub unsafe fn cast<U: 'a>(&self) -> Vector<'a, U> {
99+
Vector::new(self.0, self.1)
100+
}
93101
}
94102

95103
impl<'a, T: Follow<'a> + 'a> Vector<'a, T> {
@@ -123,11 +131,11 @@ impl<'a, T: Follow<'a> + 'a> Vector<'a, T> {
123131
Ordering::Equal => return Some(value),
124132
Ordering::Less => left = mid + 1,
125133
Ordering::Greater => {
126-
if mid == 0 {
127-
return None;
128-
}
129-
right = mid - 1;
130-
},
134+
if mid == 0 {
135+
return None;
136+
}
137+
right = mid - 1;
138+
}
131139
}
132140
}
133141

rust/flatbuffers/src/verifier.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,10 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
560560
)?;
561561
Ok(self)
562562
}
563-
_ => InvalidFlatbuffer::new_inconsistent_union(key_field_name.into(), val_field_name.into()),
563+
_ => InvalidFlatbuffer::new_inconsistent_union(
564+
key_field_name.into(),
565+
val_field_name.into(),
566+
),
564567
}
565568
}
566569
pub fn finish(self) -> &'ver mut Verifier<'opts, 'buf> {

rust/reflection/src/lib.rs

Lines changed: 80 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
mod reflection_generated;
1818
mod reflection_verifier;
19-
mod safe_buffer;
19+
pub mod safe_buffer;
20+
mod vector_of_any;
21+
pub use vector_of_any::VectorOfAny;
2022
mod r#struct;
2123
pub use crate::r#struct::Struct;
2224
pub use crate::reflection_generated::reflection;
@@ -50,8 +52,12 @@ pub enum FlatbufferError {
5052
SetStringPolluted,
5153
#[error("Invalid schema: Polluted buffer or the schema doesn't match the buffer.")]
5254
InvalidSchema,
53-
#[error("Type not supported: {0}")]
54-
TypeNotSupported(String),
55+
#[error("Unsupported table field type: {0:?}")]
56+
UnsupportedTableFieldType(BaseType),
57+
#[error("Unsupported vector element type: {0:?}")]
58+
UnsupportedVectorElementType(BaseType),
59+
#[error("Unsupported union element type: {0:?}")]
60+
UnsupportedUnionElementType(BaseType),
5561
#[error("No type or invalid type found in union enum")]
5662
InvalidUnionEnum,
5763
#[error("Table or Struct doesn't belong to the buffer")]
@@ -80,21 +86,17 @@ pub unsafe fn get_any_root(data: &[u8]) -> Table {
8086
pub unsafe fn get_field_integer<T: for<'a> Follow<'a, Inner = T> + PrimInt + FromPrimitive>(
8187
table: &Table,
8288
field: &Field,
83-
) -> FlatbufferResult<Option<T>> {
84-
if size_of::<T>() != get_type_size(field.type_().base_type()) {
85-
return Err(FlatbufferError::FieldTypeMismatch(
86-
std::any::type_name::<T>().to_string(),
87-
field
88-
.type_()
89-
.base_type()
90-
.variant_name()
91-
.unwrap_or_default()
92-
.to_string(),
93-
));
94-
}
89+
) -> Option<T> {
90+
debug_assert_eq!(
91+
size_of::<T>(),
92+
get_type_size(field.type_().base_type()),
93+
"Type size mismatch: {} vs {}",
94+
std::any::type_name::<T>(),
95+
field.type_().base_type().variant_name().unwrap_or_default()
96+
);
9597

9698
let default = T::from_i64(field.default_integer());
97-
Ok(table.get::<T>(field.offset(), default))
99+
table.get::<T>(field.offset(), default)
98100
}
99101

100102
/// 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.
@@ -105,121 +107,98 @@ pub unsafe fn get_field_integer<T: for<'a> Follow<'a, Inner = T> + PrimInt + Fro
105107
pub unsafe fn get_field_float<T: for<'a> Follow<'a, Inner = T> + Float>(
106108
table: &Table,
107109
field: &Field,
108-
) -> FlatbufferResult<Option<T>> {
109-
if size_of::<T>() != get_type_size(field.type_().base_type()) {
110-
return Err(FlatbufferError::FieldTypeMismatch(
111-
std::any::type_name::<T>().to_string(),
112-
field
113-
.type_()
114-
.base_type()
115-
.variant_name()
116-
.unwrap_or_default()
117-
.to_string(),
118-
));
119-
}
110+
) -> Option<T> {
111+
debug_assert_eq!(
112+
size_of::<T>(),
113+
get_type_size(field.type_().base_type()),
114+
"Type size mismatch: {} vs {}",
115+
std::any::type_name::<T>(),
116+
field.type_().base_type().variant_name().unwrap_or_default()
117+
);
120118

121119
let default = T::from(field.default_real());
122-
Ok(table.get::<T>(field.offset(), default))
120+
table.get::<T>(field.offset(), default)
123121
}
124122

125123
/// 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.
126124
///
127125
/// # Safety
128126
///
129127
/// The value of the corresponding slot must have type String
130-
pub unsafe fn get_field_string<'a>(
131-
table: &Table<'a>,
132-
field: &Field,
133-
) -> FlatbufferResult<Option<&'a str>> {
134-
if field.type_().base_type() != BaseType::String {
135-
return Err(FlatbufferError::FieldTypeMismatch(
136-
String::from("String"),
137-
field
138-
.type_()
139-
.base_type()
140-
.variant_name()
141-
.unwrap_or_default()
142-
.to_string(),
143-
));
144-
}
145-
146-
Ok(table.get::<ForwardsUOffset<&'a str>>(field.offset(), Some("")))
128+
pub unsafe fn get_field_string<'a>(table: &Table<'a>, field: &Field) -> &'a str {
129+
debug_assert_eq!(field.type_().base_type(), BaseType::String);
130+
table
131+
.get::<ForwardsUOffset<&'a str>>(field.offset(), Some(""))
132+
.unwrap()
147133
}
148134

149-
/// 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.
135+
/// Gets a [Struct] table field given its exact type. Returns [None] if the field is not set.
150136
///
151137
/// # Safety
152138
///
153139
/// The value of the corresponding slot must have type Struct
154-
pub unsafe fn get_field_struct<'a>(
155-
table: &Table<'a>,
156-
field: &Field,
157-
) -> FlatbufferResult<Option<Struct<'a>>> {
140+
pub unsafe fn get_field_struct<'a>(table: &Table<'a>, field: &Field) -> Option<Struct<'a>> {
158141
// TODO inherited from C++: This does NOT check if the field is a table or struct, but we'd need
159142
// access to the schema to check the is_struct flag.
160-
if field.type_().base_type() != BaseType::Obj {
161-
return Err(FlatbufferError::FieldTypeMismatch(
162-
String::from("Obj"),
163-
field
164-
.type_()
165-
.base_type()
166-
.variant_name()
167-
.unwrap_or_default()
168-
.to_string(),
169-
));
170-
}
143+
debug_assert_eq!(field.type_().base_type(), BaseType::Obj);
171144

172-
Ok(table.get::<Struct>(field.offset(), None))
145+
table.get::<Struct>(field.offset(), None)
173146
}
174147

175-
/// 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.
148+
/// Get a vector table field, whose elements have type `T`.
149+
///
150+
/// Returns an empty vector if the field is not set.
151+
///
152+
/// Non-scalar values such as tables or strings are not stored inline. In such cases, you should use
153+
/// `ForwardsUOffset`. So for example, use `T = ForwardsUOffset<Table<'a>>` for a vector of tables,
154+
/// or `T = ForwardsUOffset<&'a str>` for a vector of strings.
176155
///
177156
/// # Safety
178157
///
179-
/// The value of the corresponding slot must have type Vector
180-
pub unsafe fn get_field_vector<'a, T: Follow<'a, Inner = T>>(
158+
/// The value of the corresponding slot must be a vector of elements of type `T` which are stored
159+
/// inline.
160+
pub unsafe fn get_field_vector<'a, T: Follow<'a>>(
181161
table: &Table<'a>,
182162
field: &Field,
183-
) -> FlatbufferResult<Option<Vector<'a, T>>> {
184-
if field.type_().base_type() != BaseType::Vector
185-
|| core::mem::size_of::<T>() != get_type_size(field.type_().element())
186-
{
187-
return Err(FlatbufferError::FieldTypeMismatch(
188-
std::any::type_name::<T>().to_string(),
189-
field
190-
.type_()
191-
.base_type()
192-
.variant_name()
193-
.unwrap_or_default()
194-
.to_string(),
195-
));
163+
) -> Vector<'a, T> {
164+
debug_assert_eq!(field.type_().base_type(), BaseType::Vector);
165+
if field.type_().element() != BaseType::Obj {
166+
// Skip this check in the case that it is a vector of structs, because the struct element's
167+
// size cannot be checked on older schema versions without access to the schema.
168+
debug_assert_eq!(
169+
core::mem::size_of::<T>(),
170+
get_type_size(field.type_().element())
171+
);
196172
}
197173

198-
Ok(table.get::<ForwardsUOffset<Vector<'a, T>>>(field.offset(), Some(Vector::<T>::default())))
174+
table
175+
.get::<ForwardsUOffset<Vector<'a, T>>>(field.offset(), Some(Vector::<T>::default()))
176+
.unwrap()
199177
}
200178

201-
/// 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.
179+
/// Get a vector table field, whose elements have unknown type.
180+
///
181+
/// Returns an empty vector if the field is not set.
182+
///
183+
/// # Safety
184+
///
185+
/// The value of the corresponding slot must be a vector of elements of type `T`.
186+
pub unsafe fn get_field_vector_of_any<'a>(table: &Table<'a>, field: &Field) -> VectorOfAny<'a> {
187+
debug_assert_eq!(field.type_().base_type(), BaseType::Vector);
188+
table
189+
.get::<ForwardsUOffset<VectorOfAny<'a>>>(field.offset(), Some(VectorOfAny::default()))
190+
.unwrap()
191+
}
192+
193+
/// Gets a Table table field given its exact type. Returns [None] if the field is not set.
202194
///
203195
/// # Safety
204196
///
205197
/// The value of the corresponding slot must have type Table
206-
pub unsafe fn get_field_table<'a>(
207-
table: &Table<'a>,
208-
field: &Field,
209-
) -> FlatbufferResult<Option<Table<'a>>> {
210-
if field.type_().base_type() != BaseType::Obj {
211-
return Err(FlatbufferError::FieldTypeMismatch(
212-
String::from("Obj"),
213-
field
214-
.type_()
215-
.base_type()
216-
.variant_name()
217-
.unwrap_or_default()
218-
.to_string(),
219-
));
220-
}
198+
pub unsafe fn get_field_table<'a>(table: &Table<'a>, field: &Field) -> Option<Table<'a>> {
199+
debug_assert_eq!(field.type_().base_type(), BaseType::Obj);
221200

222-
Ok(table.get::<ForwardsUOffset<Table<'a>>>(field.offset(), None))
201+
table.get::<ForwardsUOffset<Table<'a>>>(field.offset(), None)
223202
}
224203

225204
/// 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.
@@ -273,25 +252,12 @@ pub unsafe fn get_any_field_string(table: &Table, field: &Field, schema: &Schema
273252
/// # Safety
274253
///
275254
/// The value of the corresponding slot must have type Struct.
276-
pub unsafe fn get_field_struct_in_struct<'a>(
277-
st: &Struct<'a>,
278-
field: &Field,
279-
) -> FlatbufferResult<Struct<'a>> {
255+
pub unsafe fn get_field_struct_in_struct<'a>(st: &Struct<'a>, field: &Field) -> Struct<'a> {
280256
// TODO inherited from C++: This does NOT check if the field is a table or struct, but we'd need
281257
// access to the schema to check the is_struct flag.
282-
if field.type_().base_type() != BaseType::Obj {
283-
return Err(FlatbufferError::FieldTypeMismatch(
284-
String::from("Obj"),
285-
field
286-
.type_()
287-
.base_type()
288-
.variant_name()
289-
.unwrap_or_default()
290-
.to_string(),
291-
));
292-
}
258+
debug_assert_eq!(field.type_().base_type(), BaseType::Obj);
293259

294-
Ok(st.get::<Struct>(field.offset() as usize))
260+
st.get::<Struct>(field.offset() as usize)
295261
}
296262

297263
/// 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.
@@ -576,7 +542,7 @@ pub unsafe fn set_string(
576542
}
577543

578544
/// 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.
579-
fn get_type_size(base_type: BaseType) -> usize {
545+
pub fn get_type_size(base_type: BaseType) -> usize {
580546
match base_type {
581547
BaseType::UType | BaseType::Bool | BaseType::Byte | BaseType::UByte => 1,
582548
BaseType::Short | BaseType::UShort => 2,

0 commit comments

Comments
 (0)