forked from BigfootDev/flatbuffers
* Fix Miri flag passing and bump Rust version. * Fix Miri problems from Arrays PR. SafeSliceAccess was removed for Arrays. It's kind of unsound. It has two properties: 1. EndianSafe 2. Alignment 1 We only need 1. in create_vector_direct to memcpy data. We both 1. and 2. for accessing things with slices as buffers are built on &[u8] which is unaligned. Conditional compilation implements SafeSliceAccess for >1byte scalars (like f32) on LittleEndian machines which is wrong since they don't satisfy 2. This UB is still accessible for Vectors (though not exercised our tests) as it implements SafeSliceAccess. I'll fix this later by splitting SafeSliceAccess into its 2 properties. Co-authored-by: Casper Neo <cneo@google.com>
326 lines
11 KiB
Rust
326 lines
11 KiB
Rust
extern crate array_init;
|
|
#[allow(dead_code, unused_imports)]
|
|
#[path = "../../arrays_test_generated.rs"]
|
|
mod arrays_test_generated;
|
|
use std::fmt::Debug;
|
|
|
|
use crate::arrays_test_generated::my_game::example::*;
|
|
extern crate quickcheck;
|
|
use array_init::array_init;
|
|
use std::mem::size_of;
|
|
use quickcheck::{Arbitrary, Gen};
|
|
|
|
|
|
fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder) {
|
|
let nested_struct1 = NestedStruct::new(
|
|
&[-1, 2],
|
|
TestEnum::A,
|
|
&[TestEnum::C, TestEnum::B],
|
|
&[0x1122334455667788, -0x1122334455667788],
|
|
);
|
|
let nested_struct2 = NestedStruct::new(
|
|
&[3, -4],
|
|
TestEnum::B,
|
|
&[TestEnum::B, TestEnum::A],
|
|
&[-0x1122334455667788, 0x1122334455667788],
|
|
);
|
|
let array_struct = ArrayStruct::new(
|
|
12.34,
|
|
&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF],
|
|
-127,
|
|
&[nested_struct1, nested_struct2],
|
|
1,
|
|
&[-0x8000000000000000, 0x7FFFFFFFFFFFFFFF],
|
|
);
|
|
// Test five makes sense when specified.
|
|
let ss = ArrayTable::create(
|
|
builder,
|
|
&ArrayTableArgs {
|
|
a: Some(&array_struct),
|
|
},
|
|
);
|
|
finish_array_table_buffer(builder, ss);
|
|
}
|
|
|
|
fn serialized_example_is_accessible_and_correct(
|
|
bytes: &[u8],
|
|
identifier_required: bool,
|
|
size_prefixed: bool,
|
|
) {
|
|
if identifier_required {
|
|
let correct = if size_prefixed {
|
|
array_table_size_prefixed_buffer_has_identifier(bytes)
|
|
} else {
|
|
array_table_buffer_has_identifier(bytes)
|
|
};
|
|
assert_eq!(correct, true);
|
|
}
|
|
|
|
let array_table = if size_prefixed {
|
|
size_prefixed_root_as_array_table(bytes).unwrap()
|
|
} else {
|
|
root_as_array_table(bytes).unwrap()
|
|
};
|
|
|
|
let array_struct = array_table.a().unwrap();
|
|
assert_eq!(array_struct.a(), 12.34);
|
|
assert_eq!(array_struct.b().len(), 0xF);
|
|
assert_eq!(array_struct.b().iter().sum::<i32>(), 120);
|
|
assert_eq!(array_struct.c(), -127);
|
|
|
|
assert_eq!(array_struct.d().len(), 2);
|
|
let nested_struct1 = array_struct.d().get(0);
|
|
assert_eq!(nested_struct1.a().len(), 2);
|
|
assert_eq!(nested_struct1.a().iter().sum::<i32>(), 1);
|
|
assert_eq!(nested_struct1.b(), TestEnum::A);
|
|
assert_eq!(nested_struct1.c().len(), 2);
|
|
assert_eq!(nested_struct1.c().get(0), TestEnum::C);
|
|
assert_eq!(nested_struct1.c().get(1), TestEnum::B);
|
|
assert_eq!(nested_struct1.d().len(), 2);
|
|
assert_eq!(
|
|
[nested_struct1.d().get(0), nested_struct1.d().get(1)],
|
|
[0x1122334455667788, -0x1122334455667788]
|
|
);
|
|
let nested_struct2 = array_struct.d().get(1);
|
|
assert_eq!(nested_struct2.a().len(), 2);
|
|
assert_eq!(nested_struct2.a().iter().sum::<i32>(), -1);
|
|
assert_eq!(nested_struct2.b(), TestEnum::B);
|
|
assert_eq!(nested_struct2.c().len(), 2);
|
|
assert_eq!(nested_struct2.c().get(0), TestEnum::B);
|
|
assert_eq!(nested_struct2.c().get(1), TestEnum::A);
|
|
assert_eq!(nested_struct2.d().len(), 2);
|
|
let arr: [i64; 2] = nested_struct2.d().into();
|
|
assert_eq!(
|
|
arr,
|
|
[-0x1122334455667788, 0x1122334455667788]
|
|
);
|
|
|
|
assert_eq!(array_struct.e(), 1);
|
|
assert_eq!(array_struct.f().len(), 2);
|
|
assert_eq!(array_struct.f().get(0), -0x8000000000000000);
|
|
assert_eq!(array_struct.f().get(1), 0x7FFFFFFFFFFFFFFF);
|
|
}
|
|
|
|
#[test]
|
|
fn generated_code_creates_correct_example() {
|
|
let mut b = flatbuffers::FlatBufferBuilder::new();
|
|
create_serialized_example_with_generated_code(&mut b);
|
|
let buf = b.finished_data();
|
|
serialized_example_is_accessible_and_correct(&buf[..], true, false);
|
|
}
|
|
|
|
#[test]
|
|
fn struct_netsted_struct_is_32_bytes() {
|
|
assert_eq!(32, ::std::mem::size_of::<NestedStruct>());
|
|
}
|
|
|
|
#[test]
|
|
fn struct_array_struct_is_160_bytes() {
|
|
assert_eq!(160, ::std::mem::size_of::<ArrayStruct>());
|
|
}
|
|
|
|
#[test]
|
|
fn test_object_api_reads_correctly() {
|
|
let mut b = flatbuffers::FlatBufferBuilder::new();
|
|
create_serialized_example_with_generated_code(&mut b);
|
|
|
|
let array_table = root_as_array_table(b.finished_data()).unwrap().unpack();
|
|
|
|
let array_struct = array_table.a.unwrap();
|
|
assert_eq!(array_struct.a, 12.34);
|
|
assert_eq!(array_struct.b.len(), 0xF);
|
|
assert_eq!(array_struct.b.iter().sum::<i32>(), 120);
|
|
assert_eq!(array_struct.c, -127);
|
|
|
|
assert_eq!(array_struct.d.len(), 2);
|
|
let nested_struct1 = &array_struct.d[0];
|
|
assert_eq!(nested_struct1.a.len(), 2);
|
|
assert_eq!(nested_struct1.a.iter().sum::<i32>(), 1);
|
|
assert_eq!(nested_struct1.b, TestEnum::A);
|
|
assert_eq!(nested_struct1.c.len(), 2);
|
|
assert_eq!(nested_struct1.c[0], TestEnum::C);
|
|
assert_eq!(nested_struct1.c[1], TestEnum::B);
|
|
assert_eq!(nested_struct1.d.len(), 2);
|
|
assert_eq!(nested_struct1.d, [0x1122334455667788, -0x1122334455667788]);
|
|
let nested_struct2 = &array_struct.d[1];
|
|
assert_eq!(nested_struct2.a.len(), 2);
|
|
assert_eq!(nested_struct2.a.iter().sum::<i32>(), -1);
|
|
assert_eq!(nested_struct2.b, TestEnum::B);
|
|
assert_eq!(nested_struct2.c.len(), 2);
|
|
assert_eq!(nested_struct2.c[0], TestEnum::B);
|
|
assert_eq!(nested_struct2.c[1], TestEnum::A);
|
|
assert_eq!(nested_struct2.d.len(), 2);
|
|
assert_eq!(nested_struct2.d, [-0x1122334455667788, 0x1122334455667788]);
|
|
|
|
assert_eq!(array_struct.e, 1);
|
|
assert_eq!(array_struct.f.len(), 2);
|
|
assert_eq!(array_struct.f[0], -0x8000000000000000);
|
|
assert_eq!(array_struct.f[1], 0x7FFFFFFFFFFFFFFF);
|
|
}
|
|
|
|
#[test]
|
|
fn object_api_defaults() {
|
|
use arrays_test_generated::my_game::example::*;
|
|
|
|
assert_eq!(
|
|
NestedStructT::default(),
|
|
NestedStructT {
|
|
a: [0, 0],
|
|
b: TestEnum::default(),
|
|
c: [TestEnum::default(), TestEnum::default()],
|
|
d: [0, 0]
|
|
}
|
|
);
|
|
|
|
assert_eq!(
|
|
ArrayStructT::default(),
|
|
ArrayStructT {
|
|
a: 0.0,
|
|
b: [0; 0xF],
|
|
c: 0,
|
|
d: [NestedStructT::default(), NestedStructT::default()],
|
|
e: 0,
|
|
f: [0, 0]
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn generated_code_debug_prints_correctly() {
|
|
let b = &mut flatbuffers::FlatBufferBuilder::new();
|
|
create_serialized_example_with_generated_code(b);
|
|
let buf = b.finished_data();
|
|
let array_table = root_as_array_table(buf).unwrap();
|
|
assert_eq!(
|
|
format!("{:.5?}", &array_table),
|
|
"ArrayTable { a: Some(ArrayStruct { a: 12.34000, \
|
|
b: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], \
|
|
c: -127, d: [NestedStruct { a: [-1, 2], b: A, c: [C, B], \
|
|
d: [1234605616436508552, -1234605616436508552] }, \
|
|
NestedStruct { a: [3, -4], b: B, c: [B, A], d: [-1234605616436508552, 1234605616436508552] }], \
|
|
e: 1, f: [-9223372036854775808, 9223372036854775807] }) }"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn assert_on_too_small_array_buf() {
|
|
let a = [0u8; 19];
|
|
flatbuffers::Array::<i32, 5>::new(&a);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn assert_on_too_big_array_buf() {
|
|
let a = [0u8; 21];
|
|
flatbuffers::Array::<i32, 5>::new(&a);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_endian = "little")]
|
|
fn verify_struct_array_alignment() {
|
|
let mut b = flatbuffers::FlatBufferBuilder::new();
|
|
create_serialized_example_with_generated_code(&mut b);
|
|
let buf = b.finished_data();
|
|
let array_table = root_as_array_table(buf).unwrap();
|
|
let array_struct = array_table.a().unwrap();
|
|
let struct_start_ptr = array_struct.0.as_ptr() as usize;
|
|
let b_start_ptr = array_struct.b().as_ptr() as usize;
|
|
let d_start_ptr = array_struct.d().as_ptr() as usize;
|
|
// The T type of b
|
|
let b_aln = ::std::mem::align_of::<i32>();
|
|
assert_eq!((b_start_ptr - struct_start_ptr) % b_aln, 0);
|
|
assert_eq!((d_start_ptr - b_start_ptr) % b_aln, 0);
|
|
assert_eq!((d_start_ptr - struct_start_ptr) % 8, 0);
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct FakeArray<T, const N: usize>([T; N]);
|
|
|
|
impl<T: Arbitrary + Debug + PartialEq, const N: usize> Arbitrary for FakeArray<T, N> {
|
|
fn arbitrary<G: Gen>(g: &mut G) -> FakeArray<T, N> {
|
|
let x: [T; N] = array_init(|_| {
|
|
loop {
|
|
let generated_scalar = T::arbitrary(g);
|
|
// Verify that generated scalar is not Nan, which is not equals to itself,
|
|
// therefore we can't use it to validate input == output
|
|
if generated_scalar == generated_scalar { return generated_scalar }
|
|
}
|
|
});
|
|
FakeArray{0: x}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod array_fuzz {
|
|
#[cfg(not(miri))] // slow.
|
|
extern crate quickcheck;
|
|
extern crate flatbuffers;
|
|
use self::flatbuffers::{Follow, Push};
|
|
use super::*;
|
|
|
|
const MAX_TESTS: u64 = 20;
|
|
const ARRAY_SIZE: usize = 29;
|
|
|
|
// This uses a macro because lifetimes for the trait-bounded function get too
|
|
// complicated.
|
|
macro_rules! impl_prop {
|
|
($test_name:ident, $fn_name:ident, $ty:ident) => (
|
|
fn $fn_name(xs: FakeArray<$ty, ARRAY_SIZE>) {
|
|
let mut test_buf = [0 as u8; 1024];
|
|
flatbuffers::emplace_scalar_array(&mut test_buf, 0, &xs.0);
|
|
let arr: flatbuffers::Array<$ty, ARRAY_SIZE> = flatbuffers::Array::follow(&test_buf, 0);
|
|
let got: [$ty; ARRAY_SIZE] = arr.into();
|
|
assert_eq!(got, xs.0);
|
|
}
|
|
#[test]
|
|
fn $test_name() {
|
|
quickcheck::QuickCheck::new().max_tests(MAX_TESTS).quickcheck($fn_name as fn(FakeArray<$ty, ARRAY_SIZE>));
|
|
}
|
|
)
|
|
}
|
|
|
|
impl_prop!(test_bool, prop_bool, bool);
|
|
impl_prop!(test_u8, prop_u8, u8);
|
|
impl_prop!(test_i8, prop_i8, i8);
|
|
impl_prop!(test_u16, prop_u16, u16);
|
|
impl_prop!(test_u32, prop_u32, u32);
|
|
impl_prop!(test_u64, prop_u64, u64);
|
|
impl_prop!(test_i16, prop_i16, i16);
|
|
impl_prop!(test_i32, prop_i32, i32);
|
|
impl_prop!(test_i64, prop_i64, i64);
|
|
impl_prop!(test_f32, prop_f32, f32);
|
|
impl_prop!(test_f64, prop_f64, f64);
|
|
|
|
const NESTED_STRUCT_SIZE: usize = size_of::<NestedStruct>();
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
struct NestedStructWrapper(NestedStruct);
|
|
|
|
impl Arbitrary for NestedStructWrapper {
|
|
fn arbitrary<G: Gen>(g: &mut G) -> NestedStructWrapper {
|
|
let mut x = NestedStruct::default();
|
|
x.0 = FakeArray::<u8, NESTED_STRUCT_SIZE>::arbitrary(g).0;
|
|
NestedStructWrapper{0: x}
|
|
}
|
|
}
|
|
|
|
fn prop_struct(xs: FakeArray<NestedStructWrapper, ARRAY_SIZE>) {
|
|
let mut test_buf = [0 as u8; 1024];
|
|
let native_struct_array: [&NestedStruct; ARRAY_SIZE] = array_init::from_iter(xs.0.iter().map(|x| &x.0)).unwrap();
|
|
for i in 0..ARRAY_SIZE {
|
|
let offset = i * NESTED_STRUCT_SIZE;
|
|
native_struct_array[i].push(&mut test_buf[offset..offset + NESTED_STRUCT_SIZE], &[]);
|
|
}
|
|
let arr: flatbuffers::Array<NestedStruct, ARRAY_SIZE> = flatbuffers::Array::follow(&test_buf, 0);
|
|
let got: [&NestedStruct; ARRAY_SIZE] = arr.into();
|
|
assert_eq!(got, native_struct_array);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(miri))] // slow.
|
|
fn test_struct() {
|
|
quickcheck::QuickCheck::new().max_tests(MAX_TESTS).quickcheck(prop_struct as fn(FakeArray<NestedStructWrapper, ARRAY_SIZE>));
|
|
}
|
|
}
|