mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 12:05:50 +00:00
* Bugfix for Rust generation of union fields named with language keywords
Looking at ParseField, it appears that in the case of unions, an extra field with a `UnionTypeFieldSuffix` is added to the type definition, however, if the name of this field is a keyword in the target language, it isn't escaped.
For example, if generating code for rust for a union field named `type`, flatc will generate a (non-keyword escaped) field named `type_type` for this hidden union field, and one (keyword escaped) called `type_` for the actual union contents.
When the union accessors are generated, they refer to this `type_type` field, but they will escape it mistakenly, generating code like this:
```
#[inline]
#[allow(non_snake_case)]
pub fn type__as_int(&self) -> Option<Int<'a>> {
if self.type__type() == Type::Int {
self.type_().map(|u| Int::init_from_table(u))
} else {
None
}
}
```
Which will fail to build because the field is called `self.type_type()`, not `self.type__type()`.
* [Rust] Add crate-relative use statements for FBS includes.
At present if a flatbuffer description includes a reference to a type in
another file, the generated Rust code needs to be hand-modified to add
the appropriate `use` statements.
This assumes that the dependencies are built into the same crate, which
I think is a reasonable assumption?
* Revert "[Rust] Add crate-relative use statements for FBS includes."
This reverts commit d554d79fec.
* Add updated generated test files.
* Fixing Rust test harness to handle new includes.
Test binaries need to add references to generated code that's
transitively included.
This also has the knock-on in that this code (which is referenced by
include directives directly in the flatbuffer schema files) also needs
to be generated, hence the changes to generate_code.sh.
* Test harnesses expect test data to be checked in.
Put include_test2 files into the same directory as the include_test2
schema definition.
Update all code generation scripts (forgot the batch file from last
time).
Path updates in Rust test.
* Include updated generated code
* Address comments raised in PR
* Fix failing Rust tests.
* Previous merge clobbered this branch change.
* Add updated imports to benchmarks.
* Clarifying comment per PR request
* Update documentation comments per feedback
* Remove non-Rust generated files for include tests, per feedback from @rw/@aardappel
* Broken code generation batch file
* Fix typo
* Add TODO for tidying up use declaration traversal sometime in the future
* Update test files.
155 lines
5.7 KiB
Rust
155 lines
5.7 KiB
Rust
// define a passthrough allocator that tracks alloc calls.
|
|
// (note that we can't drop this in to the usual test suite, because it's a big
|
|
// global variable).
|
|
use std::alloc::{GlobalAlloc, Layout, System};
|
|
|
|
static mut N_ALLOCS: usize = 0;
|
|
|
|
struct TrackingAllocator;
|
|
|
|
impl TrackingAllocator {
|
|
fn n_allocs(&self) -> usize {
|
|
unsafe { N_ALLOCS }
|
|
}
|
|
}
|
|
unsafe impl GlobalAlloc for TrackingAllocator {
|
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
N_ALLOCS += 1;
|
|
System.alloc(layout)
|
|
}
|
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
System.dealloc(ptr, layout)
|
|
}
|
|
}
|
|
|
|
// use the tracking allocator:
|
|
#[global_allocator]
|
|
static A: TrackingAllocator = TrackingAllocator;
|
|
|
|
// import the flatbuffers generated code:
|
|
extern crate flatbuffers;
|
|
#[allow(dead_code, unused_imports)]
|
|
#[path = "../../include_test/include_test1_generated.rs"]
|
|
pub mod include_test1_generated;
|
|
|
|
#[allow(dead_code, unused_imports)]
|
|
#[path = "../../include_test/sub/include_test2_generated.rs"]
|
|
pub mod include_test2_generated;
|
|
|
|
#[allow(dead_code, unused_imports)]
|
|
#[path = "../../monster_test_generated.rs"]
|
|
mod monster_test_generated;
|
|
pub use monster_test_generated::my_game;
|
|
|
|
// verbatim from the test suite:
|
|
fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder) {
|
|
let mon = {
|
|
let _ = builder.create_vector_of_strings(&["these", "unused", "strings", "check", "the", "create_vector_of_strings", "function"]);
|
|
|
|
let s0 = builder.create_string("test1");
|
|
let s1 = builder.create_string("test2");
|
|
let fred_name = builder.create_string("Fred");
|
|
|
|
// can't inline creation of this Vec3 because we refer to it by reference, so it must live
|
|
// long enough to be used by MonsterArgs.
|
|
let pos = my_game::example::Vec3::new(1.0, 2.0, 3.0, 3.0, my_game::example::Color::Green, &my_game::example::Test::new(5i16, 6i8));
|
|
|
|
let args = my_game::example::MonsterArgs{
|
|
hp: 80,
|
|
mana: 150,
|
|
name: Some(builder.create_string("MyMonster")),
|
|
pos: Some(&pos),
|
|
test_type: my_game::example::Any::Monster,
|
|
test: Some(my_game::example::Monster::create(builder, &my_game::example::MonsterArgs{
|
|
name: Some(fred_name),
|
|
..Default::default()
|
|
}).as_union_value()),
|
|
inventory: Some(builder.create_vector_direct(&[0u8, 1, 2, 3, 4][..])),
|
|
test4: Some(builder.create_vector_direct(&[my_game::example::Test::new(10, 20),
|
|
my_game::example::Test::new(30, 40)])),
|
|
testarrayofstring: Some(builder.create_vector(&[s0, s1])),
|
|
..Default::default()
|
|
};
|
|
my_game::example::Monster::create(builder, &args)
|
|
};
|
|
my_game::example::finish_monster_buffer(builder, mon);
|
|
}
|
|
|
|
fn main() {
|
|
// test the allocation tracking:
|
|
{
|
|
let before = A.n_allocs();
|
|
let _x: Vec<u8> = vec![0u8; 1];
|
|
let after = A.n_allocs();
|
|
assert_eq!(before + 1, after);
|
|
}
|
|
|
|
let builder = &mut flatbuffers::FlatBufferBuilder::new();
|
|
{
|
|
// warm up the builder (it can make small allocs internally, such as for storing vtables):
|
|
create_serialized_example_with_generated_code(builder);
|
|
}
|
|
|
|
// reset the builder, clearing its heap-allocated memory:
|
|
builder.reset();
|
|
|
|
{
|
|
let before = A.n_allocs();
|
|
create_serialized_example_with_generated_code(builder);
|
|
let after = A.n_allocs();
|
|
assert_eq!(before, after, "KO: Heap allocs occurred in Rust write path");
|
|
}
|
|
|
|
let buf = builder.finished_data();
|
|
|
|
// use the allocation tracking on the read path:
|
|
{
|
|
let before = A.n_allocs();
|
|
|
|
// do many reads, forcing them to execute by using assert_eq:
|
|
{
|
|
let m = my_game::example::get_root_as_monster(buf);
|
|
assert_eq!(80, m.hp());
|
|
assert_eq!(150, m.mana());
|
|
assert_eq!("MyMonster", m.name());
|
|
|
|
let pos = m.pos().unwrap();
|
|
// We know the bits should be exactly equal here but compilers may
|
|
// optimize floats in subtle ways so we're playing it safe and using
|
|
// epsilon comparison
|
|
assert!((pos.x() - 1.0f32).abs() < std::f32::EPSILON);
|
|
assert!((pos.y() - 2.0f32).abs() < std::f32::EPSILON);
|
|
assert!((pos.z() - 3.0f32).abs() < std::f32::EPSILON);
|
|
assert!((pos.test1() - 3.0f64).abs() < std::f64::EPSILON);
|
|
assert_eq!(pos.test2(), my_game::example::Color::Green);
|
|
let pos_test3 = pos.test3();
|
|
assert_eq!(pos_test3.a(), 5i16);
|
|
assert_eq!(pos_test3.b(), 6i8);
|
|
assert_eq!(m.test_type(), my_game::example::Any::Monster);
|
|
let table2 = m.test().unwrap();
|
|
let m2 = my_game::example::Monster::init_from_table(table2);
|
|
|
|
assert_eq!(m2.name(), "Fred");
|
|
|
|
let inv = m.inventory().unwrap();
|
|
assert_eq!(inv.len(), 5);
|
|
assert_eq!(inv.iter().sum::<u8>(), 10u8);
|
|
|
|
let test4 = m.test4().unwrap();
|
|
assert_eq!(test4.len(), 2);
|
|
assert_eq!(i32::from(test4[0].a()) + i32::from(test4[1].a()) +
|
|
i32::from(test4[0].b()) + i32::from(test4[1].b()), 100);
|
|
|
|
let testarrayofstring = m.testarrayofstring().unwrap();
|
|
assert_eq!(testarrayofstring.len(), 2);
|
|
assert_eq!(testarrayofstring.get(0), "test1");
|
|
assert_eq!(testarrayofstring.get(1), "test2");
|
|
}
|
|
|
|
// assert that no allocs occurred:
|
|
let after = A.n_allocs();
|
|
assert_eq!(before, after, "KO: Heap allocs occurred in Rust read path");
|
|
}
|
|
println!("Rust: Heap alloc checks completed successfully");
|
|
}
|