Skip to content

Commit 536321d

Browse files
committed
Union vector support
1 parent 8f3e757 commit 536321d

File tree

2 files changed

+164
-86
lines changed

2 files changed

+164
-86
lines changed

rust/flatbuffers/src/verifier.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> {
345345
if pos % core::mem::align_of::<T>() == 0 {
346346
Ok(())
347347
} else {
348-
Err(InvalidFlatbuffer::Unaligned {
348+
panic!("{}",InvalidFlatbuffer::Unaligned {
349349
unaligned_type: Cow::Borrowed(core::any::type_name::<T>()),
350350
position: pos,
351351
error_trace: Default::default(),

rust/reflection/src/reflection_verifier.rs

Lines changed: 163 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ fn verify_table(
6262

6363
let mut table_verifier = verifier.visit_table(table_pos)?;
6464

65-
for field in &table_object.fields() {
65+
let fields = table_object.fields();
66+
for field in &fields {
6667
let field_name = field.name().to_owned();
6768
table_verifier = match field.type_().base_type() {
6869
BaseType::UType | BaseType::UByte => {
@@ -195,91 +196,30 @@ fn verify_vector<'a, 'b, 'c>(
195196
buf_loc_to_obj_idx: &mut HashMap<usize, i32>,
196197
) -> FlatbufferResult<TableVerifier<'a, 'b, 'c>> {
197198
let field_name = field.name().to_owned();
199+
macro_rules! visit_vec {
200+
($t:ty) => {
201+
table_verifier
202+
.visit_field::<ForwardsUOffset<Vector<$t>>>(
203+
field_name,
204+
field.offset(),
205+
field.required(),
206+
)
207+
.map_err(FlatbufferError::VerificationError)
208+
};
209+
}
198210
match field.type_().element() {
199-
BaseType::UType | BaseType::UByte => table_verifier
200-
.visit_field::<ForwardsUOffset<Vector<u8>>>(
201-
field_name,
202-
field.offset(),
203-
field.required(),
204-
)
205-
.map_err(FlatbufferError::VerificationError),
206-
BaseType::Bool => table_verifier
207-
.visit_field::<ForwardsUOffset<Vector<bool>>>(
208-
field_name,
209-
field.offset(),
210-
field.required(),
211-
)
212-
.map_err(FlatbufferError::VerificationError),
213-
BaseType::Byte => table_verifier
214-
.visit_field::<ForwardsUOffset<Vector<i8>>>(
215-
field_name,
216-
field.offset(),
217-
field.required(),
218-
)
219-
.map_err(FlatbufferError::VerificationError),
220-
BaseType::Short => table_verifier
221-
.visit_field::<ForwardsUOffset<Vector<i16>>>(
222-
field_name,
223-
field.offset(),
224-
field.required(),
225-
)
226-
.map_err(FlatbufferError::VerificationError),
227-
BaseType::UShort => table_verifier
228-
.visit_field::<ForwardsUOffset<Vector<u16>>>(
229-
field_name,
230-
field.offset(),
231-
field.required(),
232-
)
233-
.map_err(FlatbufferError::VerificationError),
234-
BaseType::Int => table_verifier
235-
.visit_field::<ForwardsUOffset<Vector<i32>>>(
236-
field_name,
237-
field.offset(),
238-
field.required(),
239-
)
240-
.map_err(FlatbufferError::VerificationError),
241-
BaseType::UInt => table_verifier
242-
.visit_field::<ForwardsUOffset<Vector<u32>>>(
243-
field_name,
244-
field.offset(),
245-
field.required(),
246-
)
247-
.map_err(FlatbufferError::VerificationError),
248-
BaseType::Long => table_verifier
249-
.visit_field::<ForwardsUOffset<Vector<i64>>>(
250-
field_name,
251-
field.offset(),
252-
field.required(),
253-
)
254-
.map_err(FlatbufferError::VerificationError),
255-
BaseType::ULong => table_verifier
256-
.visit_field::<ForwardsUOffset<Vector<u64>>>(
257-
field_name,
258-
field.offset(),
259-
field.required(),
260-
)
261-
.map_err(FlatbufferError::VerificationError),
262-
BaseType::Float => table_verifier
263-
.visit_field::<ForwardsUOffset<Vector<f32>>>(
264-
field_name,
265-
field.offset(),
266-
field.required(),
267-
)
268-
.map_err(FlatbufferError::VerificationError),
269-
BaseType::Double => table_verifier
270-
.visit_field::<ForwardsUOffset<Vector<f64>>>(
271-
field_name,
272-
field.offset(),
273-
field.required(),
274-
)
275-
.map_err(FlatbufferError::VerificationError),
276-
BaseType::String => table_verifier
277-
.visit_field::<ForwardsUOffset<Vector<ForwardsUOffset<&str>>>>(
278-
field_name,
279-
field.offset(),
280-
field.required(),
281-
)
282-
.map_err(FlatbufferError::VerificationError),
211+
BaseType::UType | BaseType::UByte => visit_vec!(u8),
212+
BaseType::Bool => visit_vec!(bool),
213+
BaseType::Byte => visit_vec!(i8),
214+
BaseType::Short => visit_vec!(i16),
215+
BaseType::UShort => visit_vec!(u16),
216+
BaseType::Int => visit_vec!(i32),
217+
BaseType::UInt => visit_vec!(u32),
218+
BaseType::Long => visit_vec!(i64),
219+
BaseType::ULong => visit_vec!(u64),
220+
BaseType::Float => visit_vec!(f32),
221+
BaseType::Double => visit_vec!(f64),
222+
BaseType::String => visit_vec!(ForwardsUOffset<&str>),
283223
BaseType::Obj => {
284224
if let Some(field_pos) = table_verifier.deref(field.offset())? {
285225
let verifier = table_verifier.verifier();
@@ -333,12 +273,150 @@ fn verify_vector<'a, 'b, 'c>(
333273
}
334274
Ok(table_verifier)
335275
}
276+
BaseType::Union => {
277+
verify_vector_of_unions(table_verifier, field, schema, verified, buf_loc_to_obj_idx)
278+
}
336279
other => {
337280
return Err(FlatbufferError::UnsupportedVectorElementType(other));
338281
}
339282
}
340283
}
341284

285+
fn verify_vector_of_unions<'a, 'b, 'c>(
286+
mut table_verifier: TableVerifier<'a, 'b, 'c>,
287+
field: &Field,
288+
schema: &Schema,
289+
verified: &mut [bool],
290+
buf_loc_to_obj_idx: &mut HashMap<usize, i32>,
291+
) -> FlatbufferResult<TableVerifier<'a, 'b, 'c>> {
292+
let field_type = field.type_();
293+
// If the schema is valid, none of these asserts can fail.
294+
debug_assert_eq!(field_type.base_type(), BaseType::Vector);
295+
debug_assert_eq!(field_type.element(), BaseType::Union);
296+
let child_enum_idx = field_type.index();
297+
let child_enum = schema.enums().get(child_enum_idx.try_into()?);
298+
debug_assert!(!child_enum.values().is_empty());
299+
300+
// Assuming the schema is valid, the previous field must be the enum vector, which consists of
301+
// of 1-byte enums.
302+
let enum_field_offset = field.offset() - u16::try_from(SIZE_VOFFSET)?;
303+
304+
// Either both vectors must be present, or both must be absent.
305+
let (value_field_pos, enum_field_pos) = match (
306+
table_verifier.deref(field.offset())?,
307+
table_verifier.deref(enum_field_offset)?,
308+
) {
309+
(Some(value_field_pos), Some(enum_field_pos)) => (value_field_pos, enum_field_pos),
310+
(None, None) => {
311+
if field.required() {
312+
return InvalidFlatbuffer::new_missing_required(field.name().to_owned())?;
313+
} else {
314+
return Ok(table_verifier);
315+
}
316+
}
317+
_ => {
318+
return InvalidFlatbuffer::new_inconsistent_union(
319+
format!("{}_type", field.name()),
320+
field.name().to_owned(),
321+
)?;
322+
}
323+
};
324+
325+
let verifier = table_verifier.verifier();
326+
let enum_vector_offset = verifier.get_uoffset(enum_field_pos)?;
327+
let enum_vector_pos = enum_field_pos.saturating_add(enum_vector_offset.try_into()?);
328+
let enum_vector_len = verifier.get_uoffset(enum_vector_pos)?;
329+
let enum_vector_start = enum_vector_pos.saturating_add(SIZE_UOFFSET);
330+
331+
let value_vector_offset = verifier.get_uoffset(value_field_pos)?;
332+
let value_vector_pos = value_field_pos.saturating_add(value_vector_offset.try_into()?);
333+
let value_vector_len = verifier.get_uoffset(value_vector_pos)?;
334+
let value_vector_start = value_vector_pos.saturating_add(SIZE_UOFFSET);
335+
336+
// Both vectors should have the same length.
337+
// The C++ verifier instead assumes that the length of the value vector is the length of the enum vector:
338+
// https://github.com/google/flatbuffers/blob/bd1b2d0bafb8be6059a29487db9e5ace5c32914d/src/reflection.cpp#L147-L162
339+
// This has been reported at https://github.com/google/flatbuffers/issues/8567
340+
if enum_vector_len != value_vector_len {
341+
return InvalidFlatbuffer::new_inconsistent_union(
342+
format!("{}_type", field.name()),
343+
field.name().to_owned(),
344+
)?;
345+
}
346+
347+
// Regardless of its contents, the value vector in a vector of unions must be a vector of
348+
// offsets. Source: https://github.com/dvidelabs/flatcc/blob/master/doc/binary-format.md#unions
349+
verifier.is_aligned::<UOffsetT>(value_vector_start)?;
350+
let value_vector_size = value_vector_len.saturating_mul(SIZE_UOFFSET.try_into()?);
351+
verifier.range_in_buffer(value_vector_start, value_vector_size.try_into()?)?;
352+
let value_vector_range = core::ops::Range {
353+
start: value_vector_start,
354+
end: value_vector_start.saturating_add(value_vector_size.try_into()?),
355+
};
356+
357+
// The enums must have a size of 1 byte, so we just use the length of the vector.
358+
let enum_vector_size = enum_vector_len;
359+
verifier.range_in_buffer(enum_vector_start, enum_vector_size.try_into()?)?;
360+
let enum_vector_range = core::ops::Range {
361+
start: enum_vector_start,
362+
end: enum_vector_start.saturating_add(enum_vector_size.try_into()?),
363+
};
364+
365+
let enum_values = child_enum.values();
366+
367+
for (enum_pos, union_offset_pos) in enum_vector_range.zip(value_vector_range.step_by(SIZE_UOFFSET)) {
368+
let enum_value = verifier.get_u8(enum_pos)?;
369+
if enum_value == 0 {
370+
// Discriminator is NONE. This should never happen: the C++ implementation forbids it.
371+
// For example, the C++ JSON parser forbids the type entry to be NONE for a vector of
372+
// unions: in
373+
// https://github.com/google/flatbuffers/blob/bd1b2d0bafb8be6059a29487db9e5ace5c32914d/src/idl_parser.cpp#L1383
374+
// the second argument is 'true', meaning that the NONE entry is skipped for the reverse
375+
// lookup.
376+
//
377+
// This should possibly be an error, but we can be forgiving and just ignore the
378+
// corresponding union entry entirely.
379+
continue;
380+
}
381+
382+
let enum_value: usize = enum_value.into();
383+
let enum_type = if enum_value < enum_values.len() {
384+
enum_values.get(enum_value).union_type().expect(
385+
"Schema verification should have checked that every union enum value has a type",
386+
)
387+
} else {
388+
return Err(FlatbufferError::InvalidUnionEnum);
389+
};
390+
let union_pos = union_offset_pos.saturating_add(verifier.get_uoffset(union_offset_pos)?.try_into()?);
391+
verifier.in_buffer::<u8>(union_pos)?;
392+
393+
match enum_type.base_type() {
394+
BaseType::String => <&str>::run_verifier(verifier, union_pos)?,
395+
BaseType::Obj => {
396+
let child_obj = schema.objects().get(enum_type.index().try_into()?);
397+
buf_loc_to_obj_idx.insert(union_pos, enum_type.index());
398+
if child_obj.is_struct() {
399+
verify_struct(verifier, &child_obj, union_pos, schema, buf_loc_to_obj_idx)?
400+
} else {
401+
verify_table(
402+
verifier,
403+
&child_obj,
404+
union_pos,
405+
schema,
406+
verified,
407+
buf_loc_to_obj_idx,
408+
)?;
409+
}
410+
}
411+
other => {
412+
return Err(FlatbufferError::UnsupportedUnionElementType(other));
413+
}
414+
}
415+
verified[union_pos] = true;
416+
}
417+
Ok(table_verifier)
418+
}
419+
342420
fn verify_union<'a, 'b, 'c>(
343421
mut table_verifier: TableVerifier<'a, 'b, 'c>,
344422
field: &Field,

0 commit comments

Comments
 (0)