Merge commit '0f6aab9da6fe982218a01f4a5b896e65fcced437' as 'third_party/flatbuffers'

This commit is contained in:
Siarhei Fedartsou
2024-06-22 13:33:34 +02:00
1814 changed files with 326902 additions and 0 deletions
@@ -0,0 +1,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");
}
@@ -0,0 +1,144 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
extern crate flexbuffers;
use flexbuffers::*;
use std::alloc::{GlobalAlloc, Layout, System};
/// We take over the Rust allocator to count allocations. This is super not thread safe.
static mut NUM_ALLOCS: usize = 0;
fn current_allocs() -> usize {
unsafe { NUM_ALLOCS }
}
struct TrackingAllocator;
unsafe impl GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
NUM_ALLOCS += 1;
System.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout)
}
}
#[global_allocator]
static T: TrackingAllocator = TrackingAllocator;
/// Make some example data
fn make_monster(mut monster: MapBuilder) {
monster.push("type", "great orc");
monster.push("age", 100u8);
monster.push("name", "Mr. Orc");
monster.push("coins", &[1, 25, 50, 100, 250]);
monster.push("color", &[255u8, 0, 0, 0]);
{
let mut weapons = monster.start_vector("weapons");
{
let mut hammer = weapons.start_map();
hammer.push("name", "hammer");
hammer.push("damage type", "crush");
hammer.push("damage", 20);
}
{
let mut axe = weapons.start_map();
axe.push("name", "Great Axe");
axe.push("damage type", "slash");
axe.push("damage", 30);
}
}
{
let mut sounds = monster.start_vector("sounds");
sounds.push("grr");
sounds.push("rawr");
sounds.push("muahaha");
}
// TODO(cneo): Directly pushing string slices has alloc.
}
// Read back the data from make_monster.
fn validate_monster(flexbuffer: &[u8]) {
let r = Reader::get_root(flexbuffer).unwrap().as_map();
assert!(!r.is_empty());
assert!(r.index_key("not_a_field").is_none());
assert_eq!(r.len(), 7);
assert_eq!(r.idx("type").as_str(), "great orc");
assert_eq!(r.idx("age").as_u8(), 100);
assert_eq!(r.idx("name").as_str(), "Mr. Orc");
let coins = r.idx("coins").as_vector();
for (i, &c) in [1, 25, 50, 100, 250].iter().enumerate() {
assert_eq!(coins.idx(i).as_u16(), c);
}
let color = r.idx("color").as_vector();
for (i, &c) in [255, 0, 0, 0].iter().enumerate() {
assert_eq!(color.idx(i).as_i32(), c);
}
let weapons = r.idx("weapons").as_vector();
assert_eq!(weapons.len(), 2);
let hammer = weapons.idx(0).as_map();
assert_eq!(hammer.idx("name").as_str(), "hammer");
assert_eq!(hammer.idx("damage type").as_str(), "crush");
assert_eq!(hammer.idx("damage").as_u64(), 20);
let axe = weapons.idx(1).as_map();
assert_eq!(axe.idx("name").as_str(), "Great Axe");
assert_eq!(axe.idx("damage type").as_str(), "slash");
assert_eq!(axe.idx("damage").as_u64(), 30);
let sounds = r.idx("sounds").as_vector();
for (i, &s) in ["grr", "rawr", "muahaha"].iter().enumerate() {
assert_eq!(sounds.idx(i).as_str(), s);
}
}
// This is in a separate binary than tests because taking over the global allocator is not
// hermetic and not thread safe.
#[cfg(not(miri))] // slow.
fn main() {
let start_up = current_allocs();
// Let's build a flexbuffer from a new (cold) flexbuffer builder.
let mut builder = Builder::default();
make_monster(builder.start_map());
let after_warmup = current_allocs();
// The builder makes some allocations while warming up.
assert!(after_warmup > start_up);
assert!(after_warmup < start_up + 20);
// A warm builder should make no allocations.
make_monster(builder.start_map());
assert_eq!(after_warmup, current_allocs());
// Nor should a reader.
validate_monster(builder.view());
assert_eq!(after_warmup, current_allocs());
// Do it again just for kicks.
make_monster(builder.start_map());
validate_monster(builder.view());
assert_eq!(after_warmup, current_allocs());
let final_allocs = current_allocs(); // dbg! does allocate.
dbg!(start_up, after_warmup, final_allocs);
}
#[test]
#[cfg(not(miri))] // slow.
fn no_extra_allocations() {
main()
}
@@ -0,0 +1,29 @@
#![allow(clippy::derivable_impls, clippy::all)]
extern crate flatbuffers;
#[allow(dead_code, unused_imports)]
#[path = "../../include_test1/mod.rs"]
pub mod include_test1_generated;
#[allow(dead_code, unused_imports)]
#[path = "../../include_test2/mod.rs"]
pub mod include_test2_generated;
#[allow(dead_code, unused_imports, clippy::approx_constant)]
#[path = "../../monster_test/mod.rs"]
mod monster_test_generated;
pub use monster_test_generated::my_game;
use std::io::Read;
fn main() {
let mut f = std::fs::File::open("../monsterdata_test.mon").unwrap();
let mut buf = Vec::new();
f.read_to_end(&mut buf).expect("file reading failed");
let monster = my_game::example::root_as_monster(&buf[..]).unwrap();
println!("{}", monster.hp()); // `80`
println!("{}", monster.mana()); // default value of `150`
println!("{:?}", monster.name()); // Some("MyMonster")
}