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 = "../../monster_test_generated.rs"]
33 mod monster_test_generated;
34 pub use monster_test_generated::my_game;
35
36 // verbatim from the test suite:
create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder)37 fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder) {
38 let mon = {
39 let _ = builder.create_vector_of_strings(&["these", "unused", "strings", "check", "the", "create_vector_of_strings", "function"]);
40
41 let s0 = builder.create_string("test1");
42 let s1 = builder.create_string("test2");
43 let fred_name = builder.create_string("Fred");
44
45 // can't inline creation of this Vec3 because we refer to it by reference, so it must live
46 // long enough to be used by MonsterArgs.
47 let pos = my_game::example::Vec3::new(1.0, 2.0, 3.0, 3.0, my_game::example::Color::Green, &my_game::example::Test::new(5i16, 6i8));
48
49 let args = my_game::example::MonsterArgs{
50 hp: 80,
51 mana: 150,
52 name: Some(builder.create_string("MyMonster")),
53 pos: Some(&pos),
54 test_type: my_game::example::Any::Monster,
55 test: Some(my_game::example::Monster::create(builder, &my_game::example::MonsterArgs{
56 name: Some(fred_name),
57 ..Default::default()
58 }).as_union_value()),
59 inventory: Some(builder.create_vector_direct(&[0u8, 1, 2, 3, 4][..])),
60 test4: Some(builder.create_vector_direct(&[my_game::example::Test::new(10, 20),
61 my_game::example::Test::new(30, 40)])),
62 testarrayofstring: Some(builder.create_vector(&[s0, s1])),
63 ..Default::default()
64 };
65 my_game::example::Monster::create(builder, &args)
66 };
67 my_game::example::finish_monster_buffer(builder, mon);
68 }
69
main()70 fn main() {
71 // test the allocation tracking:
72 {
73 let before = A.n_allocs();
74 let _x: Vec<u8> = vec![0u8; 1];
75 let after = A.n_allocs();
76 assert_eq!(before + 1, after);
77 }
78
79 let builder = &mut flatbuffers::FlatBufferBuilder::new();
80 {
81 // warm up the builder (it can make small allocs internally, such as for storing vtables):
82 create_serialized_example_with_generated_code(builder);
83 }
84
85 // reset the builder, clearing its heap-allocated memory:
86 builder.reset();
87
88 {
89 let before = A.n_allocs();
90 create_serialized_example_with_generated_code(builder);
91 let after = A.n_allocs();
92 assert_eq!(before, after, "KO: Heap allocs occurred in Rust write path");
93 }
94
95 let buf = builder.finished_data();
96
97 // use the allocation tracking on the read path:
98 {
99 let before = A.n_allocs();
100
101 // do many reads, forcing them to execute by using assert_eq:
102 {
103 let m = my_game::example::get_root_as_monster(buf);
104 assert_eq!(80, m.hp());
105 assert_eq!(150, m.mana());
106 assert_eq!("MyMonster", m.name());
107
108 let pos = m.pos().unwrap();
109 assert_eq!(pos.x(), 1.0f32);
110 assert_eq!(pos.y(), 2.0f32);
111 assert_eq!(pos.z(), 3.0f32);
112 assert_eq!(pos.test1(), 3.0f64);
113 assert_eq!(pos.test2(), my_game::example::Color::Green);
114 let pos_test3 = pos.test3();
115 assert_eq!(pos_test3.a(), 5i16);
116 assert_eq!(pos_test3.b(), 6i8);
117 assert_eq!(m.test_type(), my_game::example::Any::Monster);
118 let table2 = m.test().unwrap();
119 let m2 = my_game::example::Monster::init_from_table(table2);
120
121 assert_eq!(m2.name(), "Fred");
122
123 let inv = m.inventory().unwrap();
124 assert_eq!(inv.len(), 5);
125 assert_eq!(inv.iter().sum::<u8>(), 10u8);
126
127 let test4 = m.test4().unwrap();
128 assert_eq!(test4.len(), 2);
129 assert_eq!(test4[0].a() as i32 + test4[0].b() as i32 +
130 test4[1].a() as i32 + test4[1].b() as i32, 100);
131
132 let testarrayofstring = m.testarrayofstring().unwrap();
133 assert_eq!(testarrayofstring.len(), 2);
134 assert_eq!(testarrayofstring.get(0), "test1");
135 assert_eq!(testarrayofstring.get(1), "test2");
136 }
137
138 // assert that no allocs occurred:
139 let after = A.n_allocs();
140 assert_eq!(before, after, "KO: Heap allocs occurred in Rust read path");
141 }
142 println!("Rust: Heap alloc checks completed successfully");
143 }
144