1// define a passthrough allocator that tracks alloc calls.
2// (note that we can't drop this in to the usual test suite, because it's a big
3// global variable).
4use std::alloc::{GlobalAlloc, Layout, System};
5
6
7static mut N_ALLOCS: usize = 0;
8
9struct TrackingAllocator;
10
11impl TrackingAllocator {
12 fn n_allocs(&self) -> usize {
13 unsafe { N_ALLOCS }
14 }
15}
16
17unsafe impl GlobalAlloc for TrackingAllocator {
18 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
19 N_ALLOCS += 1;
20 System.alloc(layout)
21 }
22 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
23 System.dealloc(ptr, layout)
24 }
25}
26
27// use the tracking allocator:
28#[global_allocator]
29static A: TrackingAllocator = TrackingAllocator;
30
31// import the flatbuffers generated code:
32extern crate flatbuffers;
33
34#[allow(dead_code, unused_imports, clippy::all)]
35#[path = "../../include_test1/mod.rs"]
36pub mod include_test1_generated;
37
38#[allow(dead_code, unused_imports, clippy::all)]
39#[path = "../../include_test2/mod.rs"]
40pub mod include_test2_generated;
41
42#[allow(dead_code, unused_imports, clippy::all)]
43#[path = "../../monster_test/mod.rs"]
44mod monster_test_generated;
45
46pub use monster_test_generated::my_game;
47
48// verbatim from the test suite:
49fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder) {
50 let mon = {
51 let strings = [
52 builder.create_string("these"),
53 builder.create_string("unused"),
54 builder.create_string("strings"),
55 builder.create_string("check"),
56 builder.create_string("the"),
57 builder.create_string("create_vector_of_strings"),
58 builder.create_string("function")
59 ];
60 let _ = builder.create_vector(&strings);
61
62 let s0 = builder.create_string("test1");
63 let s1 = builder.create_string("test2");
64 let fred_name = builder.create_string("Fred");
65
66 // can't inline creation of this Vec3 because we refer to it by reference, so it must live
67 // long enough to be used by MonsterArgs.
68 let pos = my_game::example::Vec3::new(
69 1.0,
70 2.0,
71 3.0,
72 3.0,
73 my_game::example::Color::Green,
74 &my_game::example::Test::new(5i16, 6i8),
75 );
76
77 let args = my_game::example::MonsterArgs {
78 hp: 80,
79 mana: 150,
80 name: Some(builder.create_string("MyMonster")),
81 pos: Some(&pos),
82 test_type: my_game::example::Any::Monster,
83 test: Some(
84 my_game::example::Monster::create(
85 builder,
86 &my_game::example::MonsterArgs {
87 name: Some(fred_name),
88 ..Default::default()
89 },
90 )
91 .as_union_value(),
92 ),
93 inventory: Some(builder.create_vector(&[0u8, 1, 2, 3, 4])),
94 test4: Some(builder.create_vector(&[
95 my_game::example::Test::new(10, 20),
96 my_game::example::Test::new(30, 40),
97 ])),
98 testarrayofstring: Some(builder.create_vector(&[s0, s1])),
99 ..Default::default()
100 };
101 my_game::example::Monster::create(builder, &args)
102 };
103 my_game::example::finish_monster_buffer(builder, mon);
104}
105
106#[cfg(not(miri))] // slow.
107fn main() {
108 // test the allocation tracking:
109 {
110 let before = A.n_allocs();
111 let _x: Vec<u8> = vec![0u8; 1];
112 let after = A.n_allocs();
113 assert_eq!(before + 1, after);
114 }
115
116 let builder = &mut flatbuffers::FlatBufferBuilder::new();
117 {
118 // warm up the builder (it can make small allocs internally, such as for storing vtables):
119 create_serialized_example_with_generated_code(builder);
120 }
121
122 // reset the builder, clearing its heap-allocated memory:
123 builder.reset();
124
125 {
126 let before = A.n_allocs();
127 create_serialized_example_with_generated_code(builder);
128 let after = A.n_allocs();
129 assert_eq!(before, after, "KO: Heap allocs occurred in Rust write path");
130 }
131
132 let buf = builder.finished_data();
133
134 // use the allocation tracking on the read path:
135 {
136 let before = A.n_allocs();
137
138 // do many reads, forcing them to execute by using assert_eq:
139 {
140 let m = unsafe { my_game::example::root_as_monster_unchecked(buf) };
141 assert_eq!(80, m.hp());
142 assert_eq!(150, m.mana());
143 assert_eq!("MyMonster", m.name());
144
145 let pos = m.pos().unwrap();
146 // We know the bits should be exactly equal here but compilers may
147 // optimize floats in subtle ways so we're playing it safe and using
148 // epsilon comparison
149 assert!((pos.x() - 1.0f32).abs() < std::f32::EPSILON);
150 assert!((pos.y() - 2.0f32).abs() < std::f32::EPSILON);
151 assert!((pos.z() - 3.0f32).abs() < std::f32::EPSILON);
152 assert!((pos.test1() - 3.0f64).abs() < std::f64::EPSILON);
153 assert_eq!(pos.test2(), my_game::example::Color::Green);
154 let pos_test3 = pos.test3();
155 assert_eq!(pos_test3.a(), 5i16);
156 assert_eq!(pos_test3.b(), 6i8);
157 assert_eq!(m.test_type(), my_game::example::Any::Monster);
158 let table2 = m.test().unwrap();
159 let m2 = unsafe { my_game::example::Monster::init_from_table(table2) };
160
161 assert_eq!(m2.name(), "Fred");
162
163 let inv = m.inventory().unwrap();
164 assert_eq!(inv.len(), 5);
165 assert_eq!(inv.iter().sum::<u8>(), 10u8);
166
167 let test4 = m.test4().unwrap();
168 assert_eq!(test4.len(), 2);
169 assert_eq!(
170 i32::from(test4.get(0).a())
171 + i32::from(test4.get(1).a())
172 + i32::from(test4.get(0).b())
173 + i32::from(test4.get(1).b()),
174 100
175 );
176
177 let testarrayofstring = m.testarrayofstring().unwrap();
178 assert_eq!(testarrayofstring.len(), 2);
179 assert_eq!(testarrayofstring.get(0), "test1");
180 assert_eq!(testarrayofstring.get(1), "test2");
181 }
182
183 // assert that no allocs occurred:
184 let after = A.n_allocs();
185 assert_eq!(before, after, "KO: Heap allocs occurred in Rust read path");
186 }
187 println!("Rust: Heap alloc checks completed successfully");
188}
View as plain text