1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
// 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, clippy::all)]
#[path = "../../include_test1/mod.rs"]
pub mod include_test1_generated;
#[allow(dead_code, unused_imports, clippy::all)]
#[path = "../../include_test2/mod.rs"]
pub mod include_test2_generated;
#[allow(dead_code, unused_imports, clippy::all)]
#[path = "../../monster_test/mod.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 strings = [
builder.create_string("these"),
builder.create_string("unused"),
builder.create_string("strings"),
builder.create_string("check"),
builder.create_string("the"),
builder.create_string("create_vector_of_strings"),
builder.create_string("function")
];
let _ = builder.create_vector(&strings);
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(&[0u8, 1, 2, 3, 4])),
test4: Some(builder.create_vector(&[
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);
}
#[cfg(not(miri))] // slow.
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 = unsafe { my_game::example::root_as_monster_unchecked(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 = unsafe { 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.get(0).a())
+ i32::from(test4.get(1).a())
+ i32::from(test4.get(0).b())
+ i32::from(test4.get(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");
}
|