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).
4 use std::alloc::{GlobalAlloc, Layout, System};
5
6
7 static mut N_ALLOCS: usize = 0;
8
9 struct TrackingAllocator;
10
11 impl TrackingAllocator {
n_allocs(&self) -> usize12 fn n_allocs(&self) -> usize {
13 unsafe { N_ALLOCS }
14 }
15 }
16
17 unsafe impl GlobalAlloc for TrackingAllocator {
alloc(&self, layout: Layout) -> *mut u818 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
19 N_ALLOCS += 1;
20 System.alloc(layout)
21 }
dealloc(&self, ptr: *mut u8, layout: Layout)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]
29 static A: TrackingAllocator = TrackingAllocator;
30
31 // import the flatbuffers generated code:
32 extern crate flatbuffers;
33
34 #[allow(dead_code, unused_imports, clippy::all)]
35 #[path = "../../include_test1/mod.rs"]
36 pub mod include_test1_generated;
37
38 #[allow(dead_code, unused_imports, clippy::all)]
39 #[path = "../../include_test2/mod.rs"]
40 pub mod include_test2_generated;
41
42 #[allow(dead_code, unused_imports, clippy::all)]
43 #[path = "../../monster_test/mod.rs"]
44 mod monster_test_generated;
45
46 pub use monster_test_generated::my_game;
47
48 // verbatim from the test suite:
create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder)49 fn 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.
main()107 fn 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 }
189