Make flatc generate Rust files not requiring std (#7273)

* Set an explicit 2018 edition for Rust tests

* Replace all `std` usage with `core` and `alloc` in Rust code generator

* Update the generated files

* Make Rust tests actually use no_std when the corresponding feature is enabled
This commit is contained in:
Bogdan Opanchuk
2022-04-26 18:40:03 -07:00
committed by GitHub
parent 9917a168cd
commit 750dde7669
88 changed files with 799 additions and 430 deletions

View File

@@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Robert Winslow <hello@rwinslow.com>",
"Casper Neo <cneo@google.com>",
"FlatBuffers Maintainers"]
edition = "2018"
[dependencies]
flatbuffers = { path = "../../rust/flatbuffers", default-features = false }

View File

@@ -14,9 +14,8 @@
* limitations under the License.
*/
use bencher::{benchmark_group, Bencher};
use flatbuffers;
use bencher::Bencher;
#[allow(dead_code, unused_imports)]
#[path = "../../monster_test/mod.rs"]

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use bencher::Bencher;
use bencher::{benchmark_group, benchmark_main, Bencher};
use flexbuffers::*;
fn push_vec_u64_to_map(b: &mut Bencher) {

View File

@@ -1,16 +1,23 @@
#![no_std]
#[cfg(not(feature = "no_std"))]
extern crate std;
extern crate alloc;
extern crate array_init;
#[allow(dead_code, unused_imports)]
#[path = "../../arrays_test/mod.rs"]
mod arrays_test_generated;
use std::fmt::Debug;
use alloc::format;
use core::fmt::Debug;
use crate::arrays_test_generated::my_game::example::*;
extern crate quickcheck;
use array_init::array_init;
use std::mem::size_of;
use core::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],
@@ -111,12 +118,12 @@ fn generated_code_creates_correct_example() {
#[test]
fn struct_netsted_struct_is_32_bytes() {
assert_eq!(32, ::std::mem::size_of::<NestedStruct>());
assert_eq!(32, ::core::mem::size_of::<NestedStruct>());
}
#[test]
fn struct_array_struct_is_160_bytes() {
assert_eq!(160, ::std::mem::size_of::<ArrayStruct>());
assert_eq!(160, ::core::mem::size_of::<ArrayStruct>());
}
#[test]
@@ -228,7 +235,7 @@ fn verify_struct_array_alignment() {
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>();
let b_aln = ::core::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);

View File

@@ -115,7 +115,6 @@ quickcheck! {
let mut expected = x.to_le_bytes().to_vec();
expected.push(3 << 2 | 2); // Float W32.
expected.push(4); // Root width W32.
println!("{:?}: {:?} vs {:?} cmp {:?}", x, &fxb, &expected, fxb==expected);
fxb == expected
}
}
@@ -321,7 +320,7 @@ fn indirect_numbers() {
v.push(IndirectUInt(u64::max_value()));
v.push(IndirectInt(i64::min_value()));
// TODO(cneo): Something about Float EPSILON and casting leads to a different binary format.
v.push(IndirectFloat(std::f64::consts::PI));
v.push(IndirectFloat(core::f64::consts::PI));
v.push(0u32); // This is stored in 8 bits instead of 64 because of indirection.
v.end_vector();
assert_eq!(
@@ -385,7 +384,6 @@ fn indirect_2p5x_smaller() {
v.push(IndirectInt(i64::max_value()));
v.end_vector();
let len_with_indirect = builder.view().len() as f32;
dbg!(len_with_indirect, len_without_indirect);
assert!(len_with_indirect * 2.5 < len_without_indirect);
}
#[test]

View File

@@ -13,6 +13,7 @@
// limitations under the License.
mod binary_format;
#[cfg(not(feature = "no_std"))] // uses file I/O
mod interop;
mod other_api;
#[cfg(not(miri))] // slow.

View File

@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use alloc::vec::Vec;
use flexbuffers::*;
#[cfg(not(miri))] // slow.
use quickcheck::QuickCheck;

View File

@@ -1,7 +1,10 @@
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use super::rwyw::NonNullString;
use flexbuffers::*;
use quickcheck::{Arbitrary, Gen};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum Enum {

View File

@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use alloc::string::{String, ToString};
use alloc::vec::Vec;
// Read what you wrote.
use flexbuffers::*;
#[cfg(not(miri))] // slow.
@@ -23,7 +26,7 @@ use serde::{Deserialize, Serialize};
pub struct NonNullString(String);
impl quickcheck::Arbitrary for NonNullString {
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
let size = std::cmp::min(1, usize::arbitrary(g));
let size = core::cmp::min(1, usize::arbitrary(g));
NonNullString(
(0..)
.map(|_| <char>::arbitrary(g))
@@ -74,7 +77,7 @@ quickcheck! {
}
v.end_vector();
let r = Reader::get_root(builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(|(i, &x)| (r.idx(i).as_f64() - x).abs() < std::f64::EPSILON)
xs.iter().enumerate().all(|(i, &x)| (r.idx(i).as_f64() - x).abs() < core::f64::EPSILON)
}
fn qc_vec_string(xs: Vec<String>) -> bool {
let mut builder = Builder::default();
@@ -86,6 +89,7 @@ quickcheck! {
let r = Reader::get_root(builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(|(i, x)| (r.idx(i).as_str() == x))
}
#[cfg(not(feature = "no_std"))]
fn qc_map_int(xs: std::collections::BTreeMap<NonNullString, i64>) -> bool {
let mut builder = Builder::default();
let mut m = builder.start_map();
@@ -98,6 +102,7 @@ quickcheck! {
r.idx(i).as_i64() == v && r.idx(k.0.as_str()).as_i64() == v
})
}
#[cfg(not(feature = "no_std"))]
fn qc_map_string(xs: std::collections::BTreeMap<NonNullString, String>) -> bool {
let mut builder = Builder::default();
let mut m = builder.start_map();
@@ -195,7 +200,6 @@ fn empty_vectors() {
let foo1 = Foo::default();
let mut s = FlexbufferSerializer::new();
foo1.serialize(&mut s).unwrap();
dbg!(s.view());
let r = Reader::get_root(s.view()).unwrap();
let foo2 = Foo::deserialize(r).unwrap();
assert_eq!(foo1, foo2);

View File

@@ -15,6 +15,18 @@
* limitations under the License.
*/
#![no_std]
#[cfg(not(feature = "no_std"))]
extern crate std;
#[cfg(not(feature = "no_std"))]
use alloc::vec::Vec;
#[macro_use]
extern crate alloc;
use alloc::string::String;
#[cfg(feature = "no_std")]
#[global_allocator]
static ALLOCATOR: libc_alloc::LibcAlloc = libc_alloc::LibcAlloc;
@@ -392,9 +404,9 @@ fn verifier_one_byte_errors_do_not_crash() {
// If the verifier says a buffer is okay then using it won't cause a crash.
// We use write_fmt since Debug visits all the fields - but there's no need to store anything.
struct ForgetfulWriter;
use std::fmt::Write;
use core::fmt::Write;
impl Write for ForgetfulWriter {
fn write_str(&mut self, _: &str) -> Result<(), std::fmt::Error> {
fn write_str(&mut self, _: &str) -> Result<(), core::fmt::Error> {
Ok(())
}
}
@@ -442,7 +454,7 @@ fn verifier_apparent_size_too_large() {
let b = &mut flatbuffers::FlatBufferBuilder::new();
let name = Some(b.create_string("foo"));
// String amplification attack.
let s = b.create_string(&(std::iter::repeat("X").take(1000).collect::<String>()));
let s = b.create_string(&(core::iter::repeat("X").take(1000).collect::<String>()));
let testarrayofstring = Some(b.create_vector(&vec![s; 1000]));
let m = Monster::create(b, &MonsterArgs {
testarrayofstring,
@@ -541,11 +553,12 @@ mod generated_constants {
}
}
#[cfg(not(feature = "no_std"))]
#[cfg(test)]
mod lifetime_correctness {
extern crate flatbuffers;
use std::mem;
use core::mem;
use super::my_game;
use super::load_file;
@@ -593,6 +606,8 @@ mod lifetime_correctness {
mod roundtrip_generated_code {
extern crate flatbuffers;
use alloc::vec::Vec;
use super::my_game;
fn build_mon<'a, 'b>(builder: &'a mut flatbuffers::FlatBufferBuilder, args: &'b my_game::example::MonsterArgs) -> my_game::example::Monster<'a> {
@@ -988,25 +1003,25 @@ mod generated_code_alignment_and_padding {
#[test]
fn enum_color_is_1_byte() {
assert_eq!(1, ::std::mem::size_of::<my_game::example::Color>());
assert_eq!(1, ::core::mem::size_of::<my_game::example::Color>());
}
#[test]
fn union_any_is_1_byte() {
assert_eq!(1, ::std::mem::size_of::<my_game::example::Any>());
assert_eq!(1, ::core::mem::size_of::<my_game::example::Any>());
}
#[test]
fn union_any_is_aligned_to_1() {
assert_eq!(1, ::std::mem::align_of::<my_game::example::Any>());
assert_eq!(1, ::core::mem::align_of::<my_game::example::Any>());
}
#[test]
fn struct_test_is_4_bytes() {
assert_eq!(4, ::std::mem::size_of::<my_game::example::Test>());
assert_eq!(4, ::core::mem::size_of::<my_game::example::Test>());
}
#[test]
fn struct_vec3_is_32_bytes() {
assert_eq!(32, ::std::mem::size_of::<my_game::example::Vec3>());
assert_eq!(32, ::core::mem::size_of::<my_game::example::Vec3>());
}
#[test]
@@ -1036,7 +1051,7 @@ mod generated_code_alignment_and_padding {
#[test]
fn struct_ability_is_8_bytes() {
assert_eq!(8, ::std::mem::size_of::<my_game::example::Ability>());
assert_eq!(8, ::core::mem::size_of::<my_game::example::Ability>());
}
#[test]
@@ -1061,7 +1076,7 @@ mod generated_code_alignment_and_padding {
for a in abilities.iter() {
let a_ptr = a as *const my_game::example::Ability as usize;
assert!(a_ptr > start_ptr);
let aln = ::std::mem::align_of::<my_game::example::Ability>();
let aln = ::core::mem::align_of::<my_game::example::Ability>();
assert_eq!((a_ptr - start_ptr) % aln, 0);
}
for a in abilities.iter().rev() {
@@ -1166,6 +1181,8 @@ mod roundtrip_vectors {
extern crate quickcheck;
extern crate flatbuffers;
use alloc::vec::Vec;
const N: u64 = 20;
fn prop<T>(xs: Vec<T>)
@@ -1173,7 +1190,7 @@ mod roundtrip_vectors {
T: for<'a> flatbuffers::Follow<'a, Inner = T>
+ flatbuffers::EndianScalar
+ flatbuffers::Push
+ ::std::fmt::Debug,
+ ::core::fmt::Debug,
{
use flatbuffers::Follow;
@@ -1247,7 +1264,7 @@ mod roundtrip_vectors {
// complicated.
macro_rules! impl_prop {
($test_name:ident, $fn_name:ident, $ty:ident) => (
fn $fn_name(xs: Vec<$ty>) {
fn $fn_name(xs: alloc::vec::Vec<$ty>) {
use flatbuffers::Follow;
let mut b = flatbuffers::FlatBufferBuilder::new();
@@ -1258,7 +1275,7 @@ mod roundtrip_vectors {
assert_eq!(got, &xs[..]);
}
#[test]
fn $test_name() { quickcheck::QuickCheck::new().max_tests(N).quickcheck($fn_name as fn(Vec<_>)); }
fn $test_name() { quickcheck::QuickCheck::new().max_tests(N).quickcheck($fn_name as fn(alloc::vec::Vec<_>)); }
)
}
@@ -1289,6 +1306,9 @@ mod roundtrip_vectors {
extern crate quickcheck;
extern crate flatbuffers;
use alloc::string::String;
use alloc::vec::Vec;
fn prop(xs: Vec<String>) {
use flatbuffers::Follow;
@@ -1327,6 +1347,9 @@ mod roundtrip_vectors {
extern crate quickcheck;
extern crate flatbuffers;
use alloc::string::String;
use alloc::vec::Vec;
fn prop(input: Vec<String>) {
let xs: Vec<&str> = input.iter().map(|s: &String| &s[..]).collect();
@@ -1358,6 +1381,8 @@ mod roundtrip_vectors {
extern crate quickcheck;
extern crate flatbuffers;
use alloc::vec::Vec;
#[cfg(not(miri))] // slow.
#[test]
fn fuzz_manual_build() {
@@ -1409,8 +1434,11 @@ mod framing_format {
}
}
#[cfg(not(feature = "no_std"))]
#[cfg(test)]
mod roundtrip_table {
use alloc::string::String;
use alloc::vec::Vec;
use std::collections::HashMap;
extern crate flatbuffers;
@@ -1429,9 +1457,9 @@ mod roundtrip_table {
let uchar_val: u8 = 0xFF;
let short_val: i16 = -32222; // 0x8222;
let ushort_val: u16 = 0xFEEE;
let int_val: i32 = unsafe { ::std::mem::transmute(0x83333333u32) };
let int_val: i32 = unsafe { ::core::mem::transmute(0x83333333u32) };
let uint_val: u32 = 0xFDDDDDDD;
let long_val: i64 = unsafe { ::std::mem::transmute(0x8444444444444444u64) }; // TODO: byte literal?
let long_val: i64 = unsafe { ::core::mem::transmute(0x8444444444444444u64) }; // TODO: byte literal?
let ulong_val: u64 = 0xFCCCCCCCCCCCCCCCu64;
let float_val: f32 = 3.14159;
let double_val: f64 = 3.14159265359;
@@ -1598,6 +1626,9 @@ mod roundtrip_table {
#[cfg(not(miri))] // slow.
mod table_of_vectors_of_scalars {
use alloc::vec::Vec;
extern crate flatbuffers;
#[cfg(not(miri))] // slow.
extern crate quickcheck;
@@ -1609,7 +1640,7 @@ mod roundtrip_table {
T: for<'a> flatbuffers::Follow<'a, Inner = T>
+ flatbuffers::EndianScalar
+ flatbuffers::Push
+ ::std::fmt::Debug,
+ ::core::fmt::Debug,
{
use flatbuffers::field_index_to_field_offset as fi2fo;
use flatbuffers::Follow;
@@ -1689,8 +1720,8 @@ mod roundtrip_scalars {
const N: u64 = 1000;
fn prop<T: PartialEq + ::std::fmt::Debug + Copy + flatbuffers::EndianScalar>(x: T) {
let mut buf = vec![0u8; ::std::mem::size_of::<T>()];
fn prop<T: PartialEq + ::core::fmt::Debug + Copy + flatbuffers::EndianScalar>(x: T) {
let mut buf = vec![0u8; ::core::mem::size_of::<T>()];
let y = unsafe {
flatbuffers::emplace_scalar(&mut buf[..], x);
flatbuffers::read_scalar(&buf[..])
@@ -1742,7 +1773,7 @@ mod roundtrip_push_follow_scalars {
macro_rules! impl_prop {
($fn_name:ident, $ty:ident) => (
fn $fn_name(x: $ty) {
let mut buf = vec![0u8; ::std::mem::size_of::<$ty>()];
let mut buf = vec![0u8; ::core::mem::size_of::<$ty>()];
x.push(&mut buf[..], &[][..]);
let fs: flatbuffers::FollowStart<$ty> = flatbuffers::FollowStart::new();
assert_eq!(fs.self_follow(&buf[..], 0), x);
@@ -1896,10 +1927,13 @@ mod write_and_read_examples {
}
}
#[cfg(not(feature = "no_std"))]
#[cfg(test)]
mod read_examples_from_other_language_ports {
extern crate flatbuffers;
use std::println;
use super::load_file;
use super::serialized_example_is_accessible_and_correct;
@@ -1974,9 +2008,9 @@ mod generated_key_comparisons {
fn struct_key_compare_with_value() {
let a = my_game::example::Ability::new(1, 2);
assert_eq!(a.key_compare_with_value(0), ::std::cmp::Ordering::Greater);
assert_eq!(a.key_compare_with_value(1), ::std::cmp::Ordering::Equal);
assert_eq!(a.key_compare_with_value(2), ::std::cmp::Ordering::Less);
assert_eq!(a.key_compare_with_value(0), ::core::cmp::Ordering::Greater);
assert_eq!(a.key_compare_with_value(1), ::core::cmp::Ordering::Equal);
assert_eq!(a.key_compare_with_value(2), ::core::cmp::Ordering::Less);
}
#[test]
@@ -2010,9 +2044,9 @@ mod generated_key_comparisons {
// preconditions
assert_eq!(a.name(), "MyMonster");
assert_eq!(a.key_compare_with_value("AAA"), ::std::cmp::Ordering::Greater);
assert_eq!(a.key_compare_with_value("MyMonster"), ::std::cmp::Ordering::Equal);
assert_eq!(a.key_compare_with_value("ZZZ"), ::std::cmp::Ordering::Less);
assert_eq!(a.key_compare_with_value("AAA"), ::core::cmp::Ordering::Greater);
assert_eq!(a.key_compare_with_value("MyMonster"), ::core::cmp::Ordering::Equal);
assert_eq!(a.key_compare_with_value("ZZZ"), ::core::cmp::Ordering::Less);
}
#[test]
@@ -2116,6 +2150,8 @@ mod follow_impls {
use flatbuffers::Follow;
use flatbuffers::field_index_to_field_offset as fi2fo;
use alloc::vec::Vec;
// Define a test struct to use in a few tests. This replicates the work that the code generator
// would normally do when defining a FlatBuffer struct. For reference, compare the following
// `FooStruct` code with the code generated for the `Vec3` struct in
@@ -2884,12 +2920,12 @@ mod byte_layouts {
c: i8,
_pad2: [u8; 4],
}
assert_eq!(::std::mem::size_of::<foo>(), 16);
assert_eq!(::core::mem::size_of::<foo>(), 16);
impl<'b> flatbuffers::Push for &'b foo {
type Output = foo;
fn push<'a>(&'a self, dst: &'a mut [u8], _rest: &'a [u8]) {
let src = unsafe {
::std::slice::from_raw_parts(*self as *const foo as *const u8, ::std::mem::size_of::<foo>())
::core::slice::from_raw_parts(*self as *const foo as *const u8, ::core::mem::size_of::<foo>())
};
dst.copy_from_slice(src);
}
@@ -3170,6 +3206,9 @@ mod byte_layouts {
#[cfg(test)]
mod copy_clone_traits {
use alloc::vec::Vec;
#[test]
fn follow_types_implement_copy_and_clone() {
static_assertions::assert_impl_all!(flatbuffers::WIPOffset<u32>: Copy, Clone);
@@ -3187,17 +3226,18 @@ mod copy_clone_traits {
mod fully_qualified_name {
#[test]
fn fully_qualified_name_generated() {
assert!(check_eq!(::my_game::example::Monster::get_fully_qualified_name(), "MyGame.Example.Monster").is_ok());
assert!(check_eq!(::my_game::example_2::Monster::get_fully_qualified_name(), "MyGame.Example2.Monster").is_ok());
assert!(check_eq!(super::my_game::example::Monster::get_fully_qualified_name(), "MyGame.Example.Monster").is_ok());
assert!(check_eq!(super::my_game::example_2::Monster::get_fully_qualified_name(), "MyGame.Example2.Monster").is_ok());
assert!(check_eq!(::my_game::example::Vec3::get_fully_qualified_name(), "MyGame.Example.Vec3").is_ok());
assert!(check_eq!(::my_game::example::Ability::get_fully_qualified_name(), "MyGame.Example.Ability").is_ok());
assert!(check_eq!(super::my_game::example::Vec3::get_fully_qualified_name(), "MyGame.Example.Vec3").is_ok());
assert!(check_eq!(super::my_game::example::Ability::get_fully_qualified_name(), "MyGame.Example.Ability").is_ok());
}
}
// this is not technically a test, but we want to always keep this generated
// file up-to-date, and the simplest way to do that is to make sure that when
// tests are run, the file is generated.
#[cfg(not(feature = "no_std"))]
#[test]
fn write_example_wire_data_to_file() {
let b = &mut flatbuffers::FlatBufferBuilder::new();
@@ -3208,6 +3248,7 @@ fn write_example_wire_data_to_file() {
f.write_all(b.finished_data()).unwrap();
}
#[cfg(not(feature = "no_std"))]
fn load_file(filename: &str) -> Result<Vec<u8>, std::io::Error> {
use std::io::Read;
let mut f = std::fs::File::open(filename)?;

View File

@@ -1,3 +1,8 @@
extern crate alloc;
use alloc::string::ToString;
use alloc::vec::Vec;
#[allow(dead_code, unused_imports)]
#[path = "../../more_defaults/mod.rs"]
mod more_defaults_generated;