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