File: flatbuffers_alloc_check.rs

package info (click to toggle)
golang-github-google-flatbuffers 24.3.25-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 17,364 kB
  • sloc: cpp: 49,726; python: 6,901; cs: 5,566; java: 4,370; ansic: 2,512; php: 1,460; javascript: 1,053; xml: 1,016; sh: 870; makefile: 13
file content (188 lines) | stat: -rw-r--r-- 6,474 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// define a passthrough allocator that tracks alloc calls.
// (note that we can't drop this in to the usual test suite, because it's a big
// global variable).
use std::alloc::{GlobalAlloc, Layout, System};


static mut N_ALLOCS: usize = 0;

struct TrackingAllocator;

impl TrackingAllocator {
    fn n_allocs(&self) -> usize {
        unsafe { N_ALLOCS }
    }
}

unsafe impl GlobalAlloc for TrackingAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        N_ALLOCS += 1;
        System.alloc(layout)
    }
    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        System.dealloc(ptr, layout)
    }
}

// use the tracking allocator:
#[global_allocator]
static A: TrackingAllocator = TrackingAllocator;

// import the flatbuffers generated code:
extern crate flatbuffers;

#[allow(dead_code, unused_imports, clippy::all)]
#[path = "../../include_test1/mod.rs"]
pub mod include_test1_generated;

#[allow(dead_code, unused_imports, clippy::all)]
#[path = "../../include_test2/mod.rs"]
pub mod include_test2_generated;

#[allow(dead_code, unused_imports, clippy::all)]
#[path = "../../monster_test/mod.rs"]
mod monster_test_generated;

pub use monster_test_generated::my_game;

// verbatim from the test suite:
fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder) {
    let mon = {
        let strings = [
            builder.create_string("these"),
            builder.create_string("unused"),
            builder.create_string("strings"),
            builder.create_string("check"),
            builder.create_string("the"),
            builder.create_string("create_vector_of_strings"),
            builder.create_string("function")
        ];
        let _ = builder.create_vector(&strings);

        let s0 = builder.create_string("test1");
        let s1 = builder.create_string("test2");
        let fred_name = builder.create_string("Fred");

        // can't inline creation of this Vec3 because we refer to it by reference, so it must live
        // long enough to be used by MonsterArgs.
        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),
        );

        let args = my_game::example::MonsterArgs {
            hp: 80,
            mana: 150,
            name: Some(builder.create_string("MyMonster")),
            pos: Some(&pos),
            test_type: my_game::example::Any::Monster,
            test: Some(
                my_game::example::Monster::create(
                    builder,
                    &my_game::example::MonsterArgs {
                        name: Some(fred_name),
                        ..Default::default()
                    },
                )
                    .as_union_value(),
            ),
            inventory: Some(builder.create_vector(&[0u8, 1, 2, 3, 4])),
            test4: Some(builder.create_vector(&[
                my_game::example::Test::new(10, 20),
                my_game::example::Test::new(30, 40),
            ])),
            testarrayofstring: Some(builder.create_vector(&[s0, s1])),
            ..Default::default()
        };
        my_game::example::Monster::create(builder, &args)
    };
    my_game::example::finish_monster_buffer(builder, mon);
}

#[cfg(not(miri))]  // slow.
fn main() {
    // test the allocation tracking:
    {
        let before = A.n_allocs();
        let _x: Vec<u8> = vec![0u8; 1];
        let after = A.n_allocs();
        assert_eq!(before + 1, after);
    }

    let builder = &mut flatbuffers::FlatBufferBuilder::new();
    {
        // warm up the builder (it can make small allocs internally, such as for storing vtables):
        create_serialized_example_with_generated_code(builder);
    }

    // reset the builder, clearing its heap-allocated memory:
    builder.reset();

    {
        let before = A.n_allocs();
        create_serialized_example_with_generated_code(builder);
        let after = A.n_allocs();
        assert_eq!(before, after, "KO: Heap allocs occurred in Rust write path");
    }

    let buf = builder.finished_data();

    // use the allocation tracking on the read path:
    {
        let before = A.n_allocs();

        // do many reads, forcing them to execute by using assert_eq:
        {
            let m = unsafe { my_game::example::root_as_monster_unchecked(buf) };
            assert_eq!(80, m.hp());
            assert_eq!(150, m.mana());
            assert_eq!("MyMonster", m.name());

            let pos = m.pos().unwrap();
            // We know the bits should be exactly equal here but compilers may
            // optimize floats in subtle ways so we're playing it safe and using
            // epsilon comparison
            assert!((pos.x() - 1.0f32).abs() < std::f32::EPSILON);
            assert!((pos.y() - 2.0f32).abs() < std::f32::EPSILON);
            assert!((pos.z() - 3.0f32).abs() < std::f32::EPSILON);
            assert!((pos.test1() - 3.0f64).abs() < std::f64::EPSILON);
            assert_eq!(pos.test2(), my_game::example::Color::Green);
            let pos_test3 = pos.test3();
            assert_eq!(pos_test3.a(), 5i16);
            assert_eq!(pos_test3.b(), 6i8);
            assert_eq!(m.test_type(), my_game::example::Any::Monster);
            let table2 = m.test().unwrap();
            let m2 = unsafe { my_game::example::Monster::init_from_table(table2) };

            assert_eq!(m2.name(), "Fred");

            let inv = m.inventory().unwrap();
            assert_eq!(inv.len(), 5);
            assert_eq!(inv.iter().sum::<u8>(), 10u8);

            let test4 = m.test4().unwrap();
            assert_eq!(test4.len(), 2);
            assert_eq!(
                i32::from(test4.get(0).a())
                    + i32::from(test4.get(1).a())
                    + i32::from(test4.get(0).b())
                    + i32::from(test4.get(1).b()),
                100
            );

            let testarrayofstring = m.testarrayofstring().unwrap();
            assert_eq!(testarrayofstring.len(), 2);
            assert_eq!(testarrayofstring.get(0), "test1");
            assert_eq!(testarrayofstring.get(1), "test2");
        }

        // assert that no allocs occurred:
        let after = A.n_allocs();
        assert_eq!(before, after, "KO: Heap allocs occurred in Rust read path");
    }
    println!("Rust: Heap alloc checks completed successfully");
}