• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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