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