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