Rust Flexbuffers (#5669)

* Cargo clippy lints

* more lints

* more lints

* Restored a doc comment

* Comment on float eps-eq and adjusted casting

* Rust Flexbuffers

* more serde tests, removed some unsafe

* Redid serde to be map-like and Reader is Display

* Moved iter from Reader to VectorReader

* Serious quickcheck + bugs

* wvo api

* Made types smaller for a reasonable speedup

* redid reading in a way that's a bit faster.

Profiling shows the rust slowdown as building +10%, reading +20%

* src/bin are developer binaries in rust

* Root and Map width are not packed

* key null check is debug only + doc changes

* BuilderOptions

* Documentation

* Documentation

* Moved tests to rust_usage_test

* Moved rust flexbuffers samples to Flatbuffers/samples

* Fixed RustTest

* Fixed for Rust 1.37.0

* Upgraded to rust 1_40_0

* fixed a little-endian-only feature in a test

* 1.40.0

* fixed some benchmarks for bigendian

* Updated .bat file

* misspelling

* Gold Flexbuffer test.

* Serialize,Deserialize, std::error::Error for Errors.

* Undo rustfmt in integration_test.rs

* from_slice instead of from_vec

* Added comments to unsafe blocks

* expanded on comment

* bump

Co-authored-by: CasperN <cneo@google.com>
This commit is contained in:
Casper
2020-05-07 14:11:26 -07:00
committed by GitHub
parent 870ecbc09a
commit 8be05f6bd4
38 changed files with 5515 additions and 54 deletions

View File

@@ -5,23 +5,31 @@ authors = ["Robert Winslow <hello@rwinslow.com>", "FlatBuffers Maintainers"]
[dependencies]
flatbuffers = { path = "../../rust/flatbuffers" }
flexbuffers = { path = "../../rust/flexbuffers" }
[[bin]]
name = "monster_example"
path = "bin/monster_example.rs"
[[bin]]
name = "alloc_check"
path = "bin/alloc_check.rs"
name = "flatbuffers_alloc_check"
path = "bin/flatbuffers_alloc_check.rs"
[[bin]]
name = "flexbuffers_alloc_check"
path = "bin/flexbuffers_alloc_check.rs"
[dev-dependencies]
quickcheck = "0.6"
# TODO(rw): look into moving to criterion.rs
bencher = "0.1.5"
static_assertions = "1.0.0"
serde_derive = "*"
serde = "*"
rand = "*"
quickcheck_derive = "*"
[[bench]]
# setup for bencher
name = "flatbuffers_benchmarks"
name = "benchmarks"
harness = false

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2020 Google Inc. All rights reserved.
*
* 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
*
* http://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.
*/
#[macro_use]
extern crate bencher;
extern crate flatbuffers;
extern crate flexbuffers;
mod flatbuffers_benchmarks;
mod flexbuffers_benchmarks;
#[allow(dead_code, unused_imports)]
#[path = "../../include_test/include_test1_generated.rs"]
pub mod include_test1_generated;
#[allow(dead_code, unused_imports)]
#[path = "../../include_test/sub/include_test2_generated.rs"]
pub mod include_test2_generated;
#[allow(dead_code, unused_imports)]
#[path = "../../monster_test_generated.rs"]
mod monster_test_generated;
pub use monster_test_generated::my_game;
benchmark_main!(
flatbuffers_benchmarks::benches,
flexbuffers_benchmarks::benches
);

View File

@@ -14,12 +14,8 @@
* limitations under the License.
*/
#[macro_use]
extern crate bencher;
use bencher::Bencher;
extern crate flatbuffers;
#[allow(dead_code, unused_imports)]
#[path = "../../include_test/include_test1_generated.rs"]
pub mod include_test1_generated;
@@ -63,42 +59,69 @@ fn create_canonical_buffer_then_reset(bench: &mut Bencher) {
}
#[inline(always)]
fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder, finish: bool) -> usize{
fn create_serialized_example_with_generated_code(
builder: &mut flatbuffers::FlatBufferBuilder,
finish: bool,
) -> usize {
let s0 = builder.create_string("test1");
let s1 = builder.create_string("test2");
let t0_name = builder.create_string("Barney");
let t1_name = builder.create_string("Fred");
let t2_name = builder.create_string("Wilma");
let t0 = my_game::example::Monster::create(builder, &my_game::example::MonsterArgs{
hp: 1000,
name: Some(t0_name),
..Default::default()
});
let t1 = my_game::example::Monster::create(builder, &my_game::example::MonsterArgs{
name: Some(t1_name),
..Default::default()
});
let t2 = my_game::example::Monster::create(builder, &my_game::example::MonsterArgs{
name: Some(t2_name),
..Default::default()
});
let t0 = my_game::example::Monster::create(
builder,
&my_game::example::MonsterArgs {
hp: 1000,
name: Some(t0_name),
..Default::default()
},
);
let t1 = my_game::example::Monster::create(
builder,
&my_game::example::MonsterArgs {
name: Some(t1_name),
..Default::default()
},
);
let t2 = my_game::example::Monster::create(
builder,
&my_game::example::MonsterArgs {
name: Some(t2_name),
..Default::default()
},
);
let mon = {
let name = builder.create_string("MyMonster");
let fred_name = builder.create_string("Fred");
let inventory = builder.create_vector_direct(&[0u8, 1, 2, 3, 4]);
let test4 = builder.create_vector_direct(&[my_game::example::Test::new(10, 20),
my_game::example::Test::new(30, 40)]);
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{
let test4 = builder.create_vector_direct(&[
my_game::example::Test::new(10, 20),
my_game::example::Test::new(30, 40),
]);
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(name),
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()),
test: Some(
my_game::example::Monster::create(
builder,
&my_game::example::MonsterArgs {
name: Some(fred_name),
..Default::default()
},
)
.as_union_value(),
),
inventory: Some(inventory),
test4: Some(test4),
testarrayofstring: Some(builder.create_vector(&[s0, s1])),
@@ -155,7 +178,7 @@ fn traverse_serialized_example_with_generated_code(bytes: &[u8]) {
}
fn create_string_10(bench: &mut Bencher) {
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1<<20);
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1 << 20);
let mut i = 0;
bench.iter(|| {
builder.create_string("foobarbaz"); // zero-terminated -> 10 bytes
@@ -170,7 +193,7 @@ fn create_string_10(bench: &mut Bencher) {
}
fn create_string_100(bench: &mut Bencher) {
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1<<20);
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1 << 20);
let s_owned = (0..99).map(|_| "x").collect::<String>();
let s: &str = &s_owned;
@@ -188,7 +211,7 @@ fn create_string_100(bench: &mut Bencher) {
}
fn create_byte_vector_100_naive(bench: &mut Bencher) {
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1<<20);
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1 << 20);
let v_owned = (0u8..100).map(|i| i).collect::<Vec<u8>>();
let v: &[u8] = &v_owned;
@@ -206,7 +229,7 @@ fn create_byte_vector_100_naive(bench: &mut Bencher) {
}
fn create_byte_vector_100_optimal(bench: &mut Bencher) {
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1<<20);
let builder = &mut flatbuffers::FlatBufferBuilder::new_with_capacity(1 << 20);
let v_owned = (0u8..100).map(|i| i).collect::<Vec<u8>>();
let v: &[u8] = &v_owned;
@@ -223,5 +246,12 @@ fn create_byte_vector_100_optimal(bench: &mut Bencher) {
bench.bytes = v.len() as u64;
}
benchmark_group!(benches, create_byte_vector_100_naive, create_byte_vector_100_optimal, traverse_canonical_buffer, create_canonical_buffer_then_reset, create_string_10, create_string_100);
benchmark_main!(benches);
benchmark_group!(
benches,
create_byte_vector_100_naive,
create_byte_vector_100_optimal,
traverse_canonical_buffer,
create_canonical_buffer_then_reset,
create_string_10,
create_string_100
);

View File

@@ -0,0 +1,295 @@
// 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.
use bencher::Bencher;
use flexbuffers::*;
fn push_vec_u64_to_map(b: &mut Bencher) {
let va = vec![u64::max_value() - 10; 512];
let vb = vec![u64::max_value() - 20; 512];
let vc = vec![u64::max_value() - 30; 512];
let mut n = 0;
b.iter(|| {
let mut fxb = Builder::default();
let mut m = fxb.start_map();
let mut ma = m.start_vector("a");
for &a in va.iter() {
ma.push(a);
}
ma.end_vector();
let mut mb = m.start_vector("b");
for &b in vb.iter() {
mb.push(b);
}
mb.end_vector();
let mut mc = m.start_vector("c");
for &c in vc.iter() {
mc.push(c);
}
mc.end_vector();
m.end_map();
n = fxb.view().len();
});
b.bytes = n as u64;
}
fn push_vec_u64_to_map_reused(b: &mut Bencher) {
let va = vec![u64::max_value() - 10; 512];
let vb = vec![u64::max_value() - 20; 512];
let vc = vec![u64::max_value() - 30; 512];
let mut fxb = Builder::default();
let mut n = 0;
let mut go = || {
let mut m = fxb.start_map();
let mut ma = m.start_vector("a");
for &a in va.iter() {
ma.push(a);
}
ma.end_vector();
let mut mb = m.start_vector("b");
for &b in vb.iter() {
mb.push(b);
}
mb.end_vector();
let mut mc = m.start_vector("c");
for &c in vc.iter() {
mc.push(c);
}
mc.end_vector();
m.end_map();
n = fxb.view().len();
};
go(); // warm up allocations.
b.iter(go);
b.bytes = n as u64;
}
fn push_vec_u64_to_map_direct(b: &mut Bencher) {
let va = vec![u64::max_value() - 10; 512];
let vb = vec![u64::max_value() - 20; 512];
let vc = vec![u64::max_value() - 30; 512];
let mut n = 0;
b.iter(|| {
let mut fxb = Builder::default();
let mut m = fxb.start_map();
m.push("a", &va);
m.push("b", &vb);
m.push("c", &vc);
m.end_map();
n = fxb.view().len();
});
b.bytes = n as u64;
}
fn push_vec_u64_to_map_direct_reused(b: &mut Bencher) {
let va = vec![u64::max_value() - 10; 512];
let vb = vec![u64::max_value() - 20; 512];
let vc = vec![u64::max_value() - 30; 512];
let mut n = 0;
let mut fxb = Builder::default();
let mut go = || {
let mut m = fxb.start_map();
m.push("a", &va);
m.push("b", &vb);
m.push("c", &vc);
m.end_map();
n = fxb.view().len();
};
go(); // warm up allocations.
b.iter(go);
b.bytes = n as u64;
}
fn push_vec_without_indirect(b: &mut Bencher) {
let mut builder = Builder::default();
let mut n = 0;
let mut go = || {
let mut b = builder.start_vector();
for i in 0..1024u16 {
b.push(i);
}
b.push(i64::max_value());
b.end_vector();
n = builder.view().len();
};
go(); // warm up allocations.
b.iter(go);
b.bytes = n as u64;
}
// This isn't actually faster than the alternative but it is a lot smaller.
// Based on the above benchmarks a lot of time is stuck in the `values` stack.
fn push_vec_with_indirect(b: &mut Bencher) {
let mut builder = Builder::default();
let mut n = 0;
let mut go = || {
let mut b = builder.start_vector();
for i in 0..1024u16 {
b.push(i);
}
b.push(IndirectInt(i64::max_value()));
b.end_vector();
n = builder.view().len();
};
go(); // warm up allocations.
b.iter(go);
b.bytes = n as u64;
}
fn example_map<'a>(m: &mut MapBuilder<'a>) {
m.push("some_ints", &[256; 5]);
m.push("some_uints", &[256u16; 5]);
m.push("some_floats", &[256f32; 5]);
m.push("some_strings", "muahahahahaha");
}
fn hundred_maps(b: &mut Bencher) {
let mut builder = Builder::default();
let mut n = 0;
let mut go = || {
let mut v = builder.start_vector();
for _ in 0..100 {
example_map(&mut v.start_map());
}
v.end_vector();
n = builder.view().len();
};
go(); // Warm up allocations.
b.iter(go);
b.bytes = n as u64;
}
fn hundred_maps_pooled(b: &mut Bencher) {
let mut builder = Builder::default();
let mut n = 0;
let mut go = || {
let mut v = builder.start_vector();
for _ in 0..100 {
example_map(&mut v.start_map());
}
v.end_vector();
n = builder.view().len();
};
go(); // Warm up allocations.
b.iter(go);
b.bytes = n as u64;
}
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");
}
}
fn serialize_monsters(b: &mut Bencher) {
let mut builder = Builder::default();
let mut n = 0;
let mut go = || {
let mut monsters = builder.start_vector();
for _ in 0..100 {
make_monster(monsters.start_map())
}
monsters.end_vector();
n = builder.view().len();
};
go(); // Warm up allocations.
b.iter(go);
b.bytes = n as u64;
}
fn validate_monster(r: MapReader) {
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");
assert!(r
.idx("coins")
.as_vector()
.iter()
.map(|c| c.as_i16())
.eq([1, 25, 50, 100, 250].iter().cloned()));
assert!(r
.idx("color")
.as_vector()
.iter()
.map(|c| c.as_u8())
.eq([255, 0, 0, 0].iter().cloned()));
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);
assert!(r
.idx("sounds")
.as_vector()
.iter()
.map(|s| s.as_str())
.eq(["grr", "rawr", "muahaha"].iter().cloned()));
}
fn read_monsters(b: &mut Bencher) {
let mut builder = Builder::default();
let mut monsters = builder.start_vector();
for _ in 0..100 {
make_monster(monsters.start_map());
}
monsters.end_vector();
b.bytes = builder.view().len() as u64;
let go = || {
let r = Reader::get_root(builder.view()).unwrap().as_vector();
assert_eq!(r.len(), 100);
for i in 0..100 {
validate_monster(r.idx(i).as_map());
}
};
b.iter(go);
}
benchmark_group!(
benches,
push_vec_u64_to_map,
push_vec_u64_to_map_reused,
push_vec_u64_to_map_direct,
push_vec_u64_to_map_direct_reused,
push_vec_without_indirect,
push_vec_with_indirect,
hundred_maps,
hundred_maps_pooled,
serialize_monsters,
read_monsters,
);
benchmark_main!(benches);

View File

@@ -44,7 +44,15 @@ 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 _ = builder.create_vector_of_strings(&["these", "unused", "strings", "check", "the", "create_vector_of_strings", "function"]);
let _ = builder.create_vector_of_strings(&[
"these",
"unused",
"strings",
"check",
"the",
"create_vector_of_strings",
"function",
]);
let s0 = builder.create_string("test1");
let s1 = builder.create_string("test2");
@@ -52,21 +60,36 @@ fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::Flat
// 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 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{
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()),
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_direct(&[0u8, 1, 2, 3, 4][..])),
test4: Some(builder.create_vector_direct(&[my_game::example::Test::new(10, 20),
my_game::example::Test::new(30, 40)])),
test4: Some(builder.create_vector_direct(&[
my_game::example::Test::new(10, 20),
my_game::example::Test::new(30, 40),
])),
testarrayofstring: Some(builder.create_vector(&[s0, s1])),
..Default::default()
};
@@ -137,8 +160,13 @@ fn main() {
let test4 = m.test4().unwrap();
assert_eq!(test4.len(), 2);
assert_eq!(i32::from(test4[0].a()) + i32::from(test4[1].a()) +
i32::from(test4[0].b()) + i32::from(test4[1].b()), 100);
assert_eq!(
i32::from(test4[0].a())
+ i32::from(test4[1].a())
+ i32::from(test4[0].b())
+ i32::from(test4[1].b()),
100
);
let testarrayofstring = m.testarrayofstring().unwrap();
assert_eq!(testarrayofstring.len(), 2);

View File

@@ -0,0 +1,138 @@
// 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_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.
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]
fn no_extra_allocations() {
main()
}

View File

@@ -13,7 +13,6 @@ pub mod include_test2_generated;
mod monster_test_generated;
pub use monster_test_generated::my_game;
use std::io::Read;
fn main() {
@@ -22,7 +21,7 @@ fn main() {
f.read_to_end(&mut buf).expect("file reading failed");
let monster = my_game::example::get_root_as_monster(&buf[..]);
println!("{}", monster.hp()); // `80`
println!("{}", monster.mana()); // default value of `150`
println!("{}", monster.hp()); // `80`
println!("{}", monster.mana()); // default value of `150`
println!("{:?}", monster.name()); // Some("MyMonster")
}

View File

@@ -0,0 +1,536 @@
// 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.
use flexbuffers::*;
use serde::Serialize;
#[test]
fn store_13() {
let buf = singleton(13i32);
assert_eq!(&buf, &[13, 4, 1]);
}
#[test]
fn store_2pow20() {
let buf = singleton(1_048_576i32);
assert_eq!(
&buf,
&[
0,
0,
16,
0, // 2^20 in LE bytes.
1 << 2 | 2, // Int 32bit
4 // Root width 32 bit
]
);
}
#[test]
fn heterogenous_vector_of_string_because_width() {
// Each string is 32 characters. They are 256 bytes altogether.
// This forces the vector to be W16 because of the large offsets.
let test_data = [
"0aaabbbbccccddddeeeeffffgggghhh",
"1aaabbbbccccddddeeeeffffgggghhh",
"2aaabbbbccccddddeeeeffffgggghhh",
"3aaabbbbccccddddeeeeffffgggghhh",
"4aaabbbbccccddddeeeeffffgggghhh",
"5aaabbbbccccddddeeeeffffgggghhh",
"6aaabbbbccccddddeeeeffffgggghhh",
"7aaabbbbccccddddeeeeffffgggghhh",
];
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
for &s in test_data.iter() {
v.push(s);
}
v.end_vector();
let mut expected = vec![];
for &s in test_data.iter() {
expected.push(s.len() as u8);
expected.extend(s.bytes());
expected.push(b'\0');
}
expected.extend(8u16.to_le_bytes().iter()); // Length.
for i in 0..test_data.len() as u16 {
let offset = 32 * (8 - i) + 9 + i;
expected.extend(offset.to_le_bytes().iter());
}
for _ in 0..test_data.len() {
expected.push(5 << 2 | 0); // String, W8.
}
expected.push(24); // Offset to Vector.
expected.push(10 << 2 | 1); // Vector, W16.
expected.push(1); // Root width W8.
assert_eq!(fxb.view(), expected.as_slice());
}
#[test]
fn store_vec_uint_16() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push(256u16);
v.push(257u16);
v.push(258u16);
v.push(259u16);
v.push(0u8); // This still becomes u16.
v.end_vector();
assert_eq!(
fxb.view(),
&[
5,
0,
0,
1,
1,
1,
2,
1,
3,
1,
0,
0, // Data
10, // Vector offset.
12 << 2 | 1, // (VectorUInt, W16 - referring to data).
1, // Root width W8 - referring to vector.
]
);
}
quickcheck! {
fn qc_f32(x: f32) -> bool {
let fxb = singleton(x);
let mut expected = x.to_le_bytes().to_vec();
expected.push(3 << 2 | 2); // Float W32.
expected.push(4); // Root width W32.
println!("{:?}: {:?} vs {:?} cmp {:?}", x, &fxb, &expected, fxb==expected);
fxb == expected
}
}
#[test]
fn singleton_vector_uint_4_16bit() {
let buf = singleton(&[4u16, 16, 64, 256]);
assert_eq!(
&buf,
&[
4,
0,
16,
0,
64,
0,
0,
1, // Data
8, // Vector offset.
23 << 2 | 1, // (VectorUInt, W16 - referring to data).
1, // Root width W8 - referring to vector.
]
);
}
#[test]
fn store_u64() {
let buf = singleton(u64::max_value() - 10);
assert_eq!(
&buf,
&[
245,
255,
255,
255,
255,
255,
255,
255, // max value - 10.
2 << 2 | 3, // (UInt, W64)
8, // Root width W64.
]
);
}
#[test]
fn vector_uint4() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push(2u8);
v.push(3u8);
v.push(5u8);
v.push(7u8);
v.end_vector();
assert_eq!(
&fxb.view(),
&[
2,
3,
5,
7, // data
4, // Root (offset)
23 << 2 | 0, // Root type VectorUInt4, BitWidth::W8
1, // Root bitwidth W8
]
);
}
#[test]
fn nested_vector() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push(0u8);
{
let mut nested = v.start_vector();
nested.push(1u8);
nested.push(2u8);
nested.push(3u8);
}
v.push(-42i8);
v.end_vector();
assert_eq!(
fxb.view(),
&[
1,
2,
3, // Nested vector
3,
0,
5,
214, // Root Vector: size, v[0], v[1] (offset), v[2] as u8
2 << 2 | 0, // v[0]: (UInt, W8)
20 << 2 | 0, // v[1]: (VectorUInt3, W8)
1 << 2 | 0, // v[2]: (Int, W8)
6, // Root points to Root vector
10 << 2 | 0, // Root type and width (Vector, W8)
1, // Root bytes
]
)
}
#[test]
fn nested_vector_push_direct() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push(0u8);
v.push(&[1u8, 2, 3]);
v.push(-42i8);
v.end_vector();
assert_eq!(
fxb.view(),
&[
1,
2,
3, // Nested VectorUInt3
3,
0,
5,
214, // Root Vector: size, v[0], v[1] (offset), v[2] as u8
2 << 2 | 0, // v[0]: (UInt, W8)
20 << 2 | 0, // v[1]: (VectorUInt3, W8)
1 << 2 | 0, // v[2]: (Int, W8)
6, // Root points to Root vector
10 << 2 | 0, // Root type and width (Vector, W8)
1, // Root bytes
]
)
}
#[test]
fn store_map_index_into_it() {
let mut fxb = Builder::default();
{
let mut m = fxb.start_map();
m.push("foo", 17u8);
m.push("bar", 33u16);
m.push("baz", 41u32);
}
assert_eq!(
fxb.view(),
&[
b'f',
b'o',
b'o',
b'\0',
b'b',
b'a',
b'r',
b'\0',
b'b',
b'a',
b'z',
b'\0',
3,
9,
6,
15, // Keys vector (note "bar" < "baz" < "foo").
3,
1,
3, // map prefix
33,
41,
17, // values
8,
8,
8, // types (UInt, W8) ~ (2 << 2 | 0)
6, // Offset to map (root)
9 << 2 | 0, // Root type (map)
1, // Root bytes
]
);
}
#[test]
fn utf8_snowman() {
let buf = singleton("snowman ☃︎");
assert_eq!(
&buf,
&[
14, // Byte length (besides extra null terminator).
b's',
b'n',
b'o',
b'w',
b'm',
b'a',
b'n',
b' ',
226,
152,
131, // snowman bytes
239,
184,
142, // UTF Variation selector 15
0, // extra null terminator.
15, // Offset to string start.
5 << 2, // String, W8
1, // Root bytes
]
);
let r = Reader::get_root(&buf).unwrap();
assert_eq!(r.get_str(), Ok("snowman ☃︎"));
}
#[test]
fn indirect_numbers() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push(IndirectUInt(u64::max_value()));
v.push(IndirectInt(i64::min_value()));
// TODO(cneo): Something about Float EPSILON and casting leads to a different binary format.
v.push(IndirectFloat(std::f64::consts::PI));
v.push(0u32); // This is stored in 8 bits instead of 64 because of indirection.
v.end_vector();
assert_eq!(
fxb.view(),
vec![
255,
255,
255,
255,
255,
255,
255,
255, // u64 max
0,
0,
0,
0,
0,
0,
0,
128, // i64 min value
24,
45,
68,
84,
251,
33,
9,
64, // f64 PI.
4, // Vector length
25,
18,
11,
0, // offsets to the indirect numbers and zero.
7 << 2 | 3, // IndirectUInt 64 bit
6 << 2 | 3, // IndirectInt 64 bit
8 << 2 | 3, // IndirectFloat 64 bit
2 << 2 | 0, // (inline) UInt 8 bit
8, // Offset to Root.
10 << 2 | 0, // Vector 8 bit
1, // 1 byte root
]
.as_slice()
)
}
#[test]
fn indirect_2p5x_smaller() {
let mut builder = Builder::default();
let mut v = builder.start_vector();
for i in 0..512 {
v.push(i);
}
v.push(i64::max_value());
v.end_vector();
let len_without_indirect = builder.view().len() as f32;
let mut v = builder.start_vector();
for i in 0..512 {
v.push(i);
}
v.push(IndirectInt(i64::max_value()));
v.end_vector();
let len_with_indirect = builder.view().len() as f32;
dbg!(len_with_indirect, len_without_indirect);
assert!(len_with_indirect * 2.5 < len_without_indirect);
}
#[test]
fn key_pool() {
let mut builder = Builder::default();
let mut vector = builder.start_vector();
for _ in 0..2 {
let mut m = vector.start_map();
m.push("a", 42u8);
m.push("b", 42u8);
m.push("c", 42u8);
}
vector.end_vector();
assert_eq!(
builder.view(),
vec![
b'a',
b'\0',
b'b',
b'\0',
b'c',
b'\0',
3,
7,
6,
5, // Key vector 0
3,
1,
3,
42,
42,
42,
2 << 2,
2 << 2,
2 << 2, // Map 0.
3,
20,
19,
18, // Key vector 1 (shares keys with key vector 0).
3,
1,
3,
42,
42,
42,
2 << 2,
2 << 2,
2 << 2, // Map 1.
2,
20,
8,
9 << 2,
9 << 2, // Vector containing the maps.
4,
10 << 2,
1, // Root.
]
.as_slice()
);
}
#[test]
fn serialize_unit() {
#[derive(Serialize)]
struct Foo;
let mut s = FlexbufferSerializer::new();
Foo.serialize(&mut s).unwrap();
assert_eq!(s.view(), &[0, 0, 1]);
}
#[test]
fn serialize_i8() {
let mut s = FlexbufferSerializer::new();
13i8.serialize(&mut s).unwrap();
assert_eq!(s.view(), &[13, 4, 1]);
}
#[test]
fn serialize_tuple_struct_i8() {
#[derive(Serialize)]
struct Foo(i32);
let mut s = FlexbufferSerializer::new();
Foo(13).serialize(&mut s).unwrap();
assert_eq!(s.view(), &[13, 4, 1]);
}
#[test]
fn serialize_tuple_tuple_struct_i8_is_inlined() {
#[derive(Serialize)]
struct Foo(i32);
#[derive(Serialize)]
struct Bar(Foo);
let mut s = FlexbufferSerializer::new();
Bar(Foo(13)).serialize(&mut s).unwrap();
assert_eq!(s.view(), &[13, 4, 1]);
}
#[test]
fn align_8byte() {
let mut b = Builder::default();
let mut v = b.start_vector();
v.push(IndirectUInt(42));
v.push(&[u64::max_value(); 2]);
v.end_vector();
assert_eq!(
b.view()[..16],
[
42, 0, 0, 0, 0, 0, 0, 0, // padding
255, 255, 255, 255, 255, 255, 255, 255, // the first u64 max value.
]
);
}
#[test]
fn align_4byte() {
let mut b = Builder::default();
let mut v = b.start_vector();
v.push(IndirectUInt(42));
v.push(&[u32::max_value(); 2]);
v.end_vector();
assert_eq!(
b.view()[..8],
[
42, 0, 0, 0, // padding
255, 255, 255, 255, // the first u32 max value.
]
);
}
#[test]
fn align_2byte() {
let mut b = Builder::default();
let mut v = b.start_vector();
v.push(IndirectUInt(42));
v.push(&[u16::max_value(); 2]);
v.end_vector();
assert_eq!(
b.view()[..4],
[
42, 0, // padding
255, 255, // the first u16 max value.
]
);
}
#[test]
fn align_1byte() {
let mut b = Builder::default();
let mut v = b.start_vector();
v.push(IndirectUInt(42));
v.push(&[u8::max_value(); 2]);
v.end_vector();
assert_eq!(b.view()[..2], [42, 255]); // No padding.
}

View File

@@ -0,0 +1,50 @@
// 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.
use flexbuffers::*;
#[test]
fn read_golden_flexbuffer() {
let s =
std::fs::read("../gold_flexbuffer_example.bin").expect("Unable to read golden flexbuffer.");
let r = Reader::get_root(&s).unwrap();
let m = r.as_map();
let vec = m.idx("vec").as_vector();
assert_eq!(vec.idx(0).as_i8(), -100);
assert_eq!(vec.idx(1).as_str(), "Fred");
assert_eq!(vec.idx(2).as_f32(), 4.0);
assert_eq!(vec.idx(3).as_blob(), Blob(&[77]));
assert_eq!(vec.idx(4).flexbuffer_type(), FlexBufferType::Bool);
assert_eq!(vec.idx(4).as_bool(), false);
assert_eq!(vec.idx(5).as_f64(), 4.0);
let bar = m.idx("bar").as_vector();
for (i, &x) in [1, 2, 3].iter().enumerate() {
assert_eq!(bar.idx(i).as_i8(), x);
}
let bar3 = m.idx("bar3").as_vector();
for (i, &x) in [1, 2, 3].iter().enumerate() {
assert_eq!(bar3.idx(i).as_i8(), x);
}
let bools = m.idx("bools").as_vector();
for (i, &b) in [true, false, true, false].iter().enumerate() {
assert_eq!(bools.idx(i).as_bool(), b)
}
assert_eq!(m.idx("bool").as_bool(), true);
assert_eq!(m.idx("foo").as_f64(), 100.0);
let mymap = m.idx("mymap").as_map();
assert_eq!(mymap.idx("foo").as_str(), "Fred");
}

View File

@@ -0,0 +1,19 @@
// Copyright 2020 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.
mod binary_format;
mod interop;
mod other_api;
mod qc_serious;
mod rwyw;

View File

@@ -0,0 +1,190 @@
// Copyright 2020 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.
use flexbuffers::*;
use quickcheck::QuickCheck;
#[test]
fn qc_reader_no_crash() {
fn no_crash(xs: Vec<u8>) -> bool {
let r = Reader::get_root(&xs);
r.is_err() || r.is_ok()
}
QuickCheck::new()
.min_tests_passed(10_000_000)
.quicktest(no_crash as fn(Vec<u8>) -> bool)
.unwrap();
no_crash(vec![0, 10 << 2 | 2, 0]);
}
#[test]
fn as_num() {
let mut fxb = Builder::default();
let mut m = fxb.start_map();
m.push("a", &[-1i8, -2, -3, -4]);
m.push("b", 250i64);
m.push("c", 5000u16);
m.end_map();
let r = Reader::get_root(fxb.view()).unwrap();
assert_eq!(r.as_i8(), 3); // length.
assert_eq!(r.as_i16(), 3);
assert_eq!(r.as_i32(), 3);
assert_eq!(r.as_i64(), 3);
assert_eq!(r.as_u8(), 3);
assert_eq!(r.as_u16(), 3);
assert_eq!(r.as_u32(), 3);
assert_eq!(r.as_u64(), 3);
assert_eq!(r.as_f32(), 3.0);
assert_eq!(r.as_f64(), 3.0);
let m = r.as_map();
let a = m.index("a").unwrap();
assert_eq!(a.as_f32(), 4.0); // length.
assert_eq!(a.as_f64(), 4.0); // length.
assert_eq!(a.as_vector().idx(0).as_i8(), -1);
assert_eq!(a.as_vector().idx(1).as_i16(), -2);
assert_eq!(a.as_vector().idx(2).as_i32(), -3);
assert_eq!(a.as_vector().idx(3).as_i64(), -4);
let b = m.index("b").unwrap();
assert_eq!(b.as_u8(), 250);
assert_eq!(b.as_u16(), 250);
assert_eq!(b.as_u32(), 250);
assert_eq!(b.as_u64(), 250);
assert_eq!(b.as_i8(), 0); // overflow
assert_eq!(b.as_i16(), 250);
assert_eq!(b.as_i32(), 250);
assert_eq!(b.as_i64(), 250);
let c = m.index("c").unwrap();
assert_eq!(c.as_i64(), 5000);
assert_eq!(c.as_u64(), 5000);
assert_eq!(c.as_f32(), 5000.0);
assert_eq!(c.as_u8(), 0); // overflow
assert_eq!(c.as_u16(), 5000);
assert_eq!(c.as_u32(), 5000);
assert_eq!(c.as_u64(), 5000);
assert_eq!(c.as_i8(), 0); // overflow
assert_eq!(c.as_i16(), 5000);
assert_eq!(c.as_i32(), 5000);
assert_eq!(c.as_i64(), 5000);
}
#[test]
fn string_as_num() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push("3.1415");
v.push("9.001e3");
v.push("42");
v.end_vector();
let r = Reader::get_root(fxb.view()).unwrap();
let v0 = r.as_vector().idx(0);
assert_eq!(v0.as_f64(), 3.1415);
assert_eq!(v0.as_f32(), 3.1415);
assert_eq!(v0.as_u8(), 0);
assert_eq!(v0.as_u16(), 0);
assert_eq!(v0.as_u32(), 0);
assert_eq!(v0.as_u64(), 0);
assert_eq!(v0.as_i8(), 0);
assert_eq!(v0.as_i16(), 0);
assert_eq!(v0.as_i32(), 0);
assert_eq!(v0.as_i64(), 0);
let v1 = r.as_vector().idx(1);
assert_eq!(v1.as_f64(), 9001.0);
assert_eq!(v1.as_f32(), 9001.0);
assert_eq!(v1.as_u8(), 0);
assert_eq!(v1.as_u16(), 0);
assert_eq!(v1.as_u32(), 0);
assert_eq!(v1.as_u64(), 0);
assert_eq!(v1.as_i8(), 0);
assert_eq!(v1.as_i16(), 0);
assert_eq!(v1.as_i32(), 0);
assert_eq!(v1.as_i64(), 0);
assert_eq!(v1.as_i32(), 0);
let v2 = r.as_vector().idx(2);
assert_eq!(v2.as_f64(), 42.0);
assert_eq!(v2.as_f32(), 42.0);
assert_eq!(v2.as_u8(), 42);
assert_eq!(v2.as_u16(), 42);
assert_eq!(v2.as_u32(), 42);
assert_eq!(v2.as_u64(), 42);
assert_eq!(v2.as_i8(), 42);
assert_eq!(v2.as_i16(), 42);
assert_eq!(v2.as_i32(), 42);
assert_eq!(v2.as_i64(), 42);
assert_eq!(v2.as_i32(), 42);
}
#[test]
fn null_reader() {
let n = Reader::default();
assert_eq!(n.as_i8(), 0);
assert_eq!(n.as_i16(), 0);
assert_eq!(n.as_i32(), 0);
assert_eq!(n.as_i64(), 0);
assert_eq!(n.as_u8(), 0);
assert_eq!(n.as_u16(), 0);
assert_eq!(n.as_u32(), 0);
assert_eq!(n.as_u64(), 0);
assert_eq!(n.as_f32(), 0.0);
assert_eq!(n.as_f64(), 0.0);
assert!(n.get_i64().is_err());
assert!(n.get_u64().is_err());
assert!(n.get_f64().is_err());
assert!(n.as_vector().is_empty());
assert!(n.as_map().is_empty());
assert_eq!(n.as_vector().idx(1).flexbuffer_type(), FlexBufferType::Null);
assert_eq!(n.as_map().idx("1").flexbuffer_type(), FlexBufferType::Null);
}
#[test]
fn get_root_deref_oob() {
let s = &[
4, // Deref out of bounds
(FlexBufferType::Vector as u8) << 2 | BitWidth::W8 as u8,
1,
];
assert!(Reader::get_root(s).is_err());
}
#[test]
fn get_root_deref_u64() {
let s = &[
0,
0,
(FlexBufferType::IndirectUInt as u8) << 2 | BitWidth::W64 as u8,
1,
];
// The risk of crashing is reading 8 bytes from index 0.
assert_eq!(Reader::get_root(s).unwrap().as_u64(), 0);
}
#[test]
#[should_panic]
fn build_map_panic_on_repeated_key() {
let mut b = Builder::default();
let mut m = b.start_map();
m.push("foo", 5u8);
m.push("foo", 6u8);
m.end_map();
}
#[test]
#[should_panic]
fn build_map_panic_on_internal_null() {
let mut b = Builder::default();
let mut m = b.start_map();
m.push("foo\0", 5u8);
m.end_map();
}

View File

@@ -0,0 +1,146 @@
#![allow(unused_imports)]
use super::rwyw::NonNullString;
use flexbuffers::*;
use quickcheck::{Arbitrary, Gen};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum Enum {
Unit,
U8(u8),
U16(u16),
U32(u32),
U64(u64),
Us(u8, u16, u32, u64),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
Is(i8, i16, i32, i64),
F32(f32),
F64(f64),
Fs(f32, f64),
String(String),
Strings(String, String),
Everything(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64, String),
Arrays {
a: Array3<u16>,
b: Array4<i32>,
c: Array2<f64>,
},
}
// There is some upstream bug in deriving Arbitrary for Enum so we manually implement it here.
impl Arbitrary for Enum {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
match g.gen_range(0, 18) {
0 => Enum::Unit,
1 => Enum::U8(<u8>::arbitrary(g)),
2 => Enum::U16(<u16>::arbitrary(g)),
3 => Enum::U32(<u32>::arbitrary(g)),
4 => Enum::U64(<u64>::arbitrary(g)),
5 => {
let (a, b, c, d) = <(u8, u16, u32, u64)>::arbitrary(g);
Enum::Us(a, b, c, d)
}
6 => Enum::I8(<i8>::arbitrary(g)),
7 => Enum::I16(<i16>::arbitrary(g)),
8 => Enum::I32(<i32>::arbitrary(g)),
9 => Enum::I64(<i64>::arbitrary(g)),
10 => {
let (a, b, c, d) = <(i8, i16, i32, i64)>::arbitrary(g);
Enum::Is(a, b, c, d)
}
11 => Enum::F32(<f32>::arbitrary(g)),
12 => Enum::F64(<f64>::arbitrary(g)),
13 => {
let (a, b) = <(f32, f64)>::arbitrary(g);
Enum::Fs(a, b)
}
14 => Enum::String(String::arbitrary(g)),
15 => {
let (a, b) = <(String, String)>::arbitrary(g);
Enum::Strings(a, b)
}
16 => Enum::Everything(
<u8>::arbitrary(g),
<u16>::arbitrary(g),
<u32>::arbitrary(g),
<u64>::arbitrary(g),
<i8>::arbitrary(g),
<i16>::arbitrary(g),
<i32>::arbitrary(g),
<i64>::arbitrary(g),
<f32>::arbitrary(g),
<f64>::arbitrary(g),
<String>::arbitrary(g),
),
17 => {
let a = Array3::arbitrary(g);
let b = Array4::arbitrary(g);
let c = Array2::arbitrary(g);
Enum::Arrays { a, b, c }
}
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Arbitrary, PartialEq, Serialize, Deserialize)]
struct Unit;
#[derive(Debug, Clone, Arbitrary, PartialEq, Serialize, Deserialize)]
struct NewType(bool);
#[derive(Debug, Clone, Arbitrary, PartialEq, Serialize, Deserialize)]
struct Tuple(bool, u8, i16, f32, String);
#[derive(Debug, Clone, Arbitrary, PartialEq, Serialize, Deserialize)]
struct Struct {
a: Vec<Enum>,
b: BTreeMap<NonNullString, Enum>,
c: Tuple,
d: (Unit, Unit),
e: Array4<NewType>,
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
struct Array2<A: Arbitrary>([A; 2]);
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
struct Array3<A: Arbitrary>([A; 3]);
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
struct Array4<A: Arbitrary>([A; 4]);
impl<A: Arbitrary> Arbitrary for Array2<A> {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Array2([A::arbitrary(g), A::arbitrary(g)])
}
}
impl<A: Arbitrary> Arbitrary for Array3<A> {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Array3([A::arbitrary(g), A::arbitrary(g), A::arbitrary(g)])
}
}
impl<A: Arbitrary> Arbitrary for Array4<A> {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Array4([
A::arbitrary(g),
A::arbitrary(g),
A::arbitrary(g),
A::arbitrary(g),
])
}
}
quickcheck! {
fn qc_serious(x: Struct) -> bool {
let mut s = FlexbufferSerializer::new();
x.serialize(&mut s).unwrap();
let r = Reader::get_root(s.view()).unwrap();
println!("{}", r);
let x2 = Struct::deserialize(r).unwrap();
x == x2
}
}

View File

@@ -0,0 +1,499 @@
// 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.
// Read what you wrote.
use flexbuffers::*;
use quickcheck;
use serde::{Deserialize, Serialize};
// TODO(cneo): Upstream this to the quickcheck crate. Also, write a macro to derive Arbitrary.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct NonNullString(String);
impl quickcheck::Arbitrary for NonNullString {
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
let size = std::cmp::min(1, usize::arbitrary(g));
NonNullString(
(0..)
.map(|_| <char>::arbitrary(g))
.filter(|&b| b != '\0')
.take(size)
.collect(),
)
}
}
quickcheck! {
fn qc_vec_bool(xs: Vec<bool>) -> bool {
let mut builder = Builder::default();
let mut v = builder.start_vector();
for &x in &xs {
v.push(x);
}
v.end_vector();
let r = Reader::get_root(&builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(|(i, &x)| r.index(i).unwrap().get_bool().unwrap() == x)
}
fn qc_vec_uint(xs: Vec<u64>) -> bool {
let mut builder = Builder::default();
let mut v = builder.start_vector();
for &x in &xs {
v.push(x);
}
v.end_vector();
let r = Reader::get_root(&builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(|(i, &x)| r.idx(i).as_u64() == x)
}
fn qc_vec_int(xs: Vec<i64>) -> bool {
let mut builder = Builder::default();
let mut v = builder.start_vector();
for &x in &xs {
v.push(x);
}
v.end_vector();
let r = Reader::get_root(&builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(|(i, &x)| r.idx(i).as_i64() == x)
}
fn qc_vec_float(xs: Vec<f64>) -> bool {
let mut builder = Builder::default();
let mut v = builder.start_vector();
for &x in &xs {
v.push(x);
}
v.end_vector();
let r = Reader::get_root(&builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(|(i, &x)| (r.idx(i).as_f64() - x).abs() < std::f64::EPSILON)
}
fn qc_vec_string(xs: Vec<String>) -> bool {
let mut builder = Builder::default();
let mut v = builder.start_vector();
for x in &xs {
v.push(x as &str);
}
v.end_vector();
let r = Reader::get_root(&builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(|(i, x)| (r.idx(i).as_str() == x))
}
fn qc_map_int(xs: std::collections::BTreeMap<NonNullString, i64>) -> bool {
let mut builder = Builder::default();
let mut m = builder.start_map();
for (k, &v) in &xs {
m.push(&k.0, v);
}
m.end_map();
let r = Reader::get_root(&builder.view()).unwrap().as_map();
xs.iter().enumerate().all(|(i, (k, &v))| {
r.idx(i).as_i64() == v && r.idx(k.0.as_str()).as_i64() == v
})
}
fn qc_map_string(xs: std::collections::BTreeMap<NonNullString, String>) -> bool {
let mut builder = Builder::default();
let mut m = builder.start_map();
for (k, v) in &xs {
m.push(&k.0, v as &str);
}
m.end_map();
let r = Reader::get_root(&builder.view()).unwrap().as_map();
xs.iter().enumerate().all(|(i, (k, v))| {
r.idx(i).as_str() == v && r.idx(k.0.as_str()).as_str() == v
})
}
fn qc_blob(xs: Vec<Vec<u8>>) -> bool {
let mut builder = Builder::default();
let mut v = builder.start_vector();
for x in &xs {
v.push(Blob(&x));
}
v.end_vector();
let r = Reader::get_root(&builder.view()).unwrap().as_vector();
xs.iter().enumerate().all(
|(i, x)| r.idx(i).get_blob().unwrap().0.iter().eq(x.iter())
)
}
fn qc_serde_ints(
u8s: Vec<u8>,
u16s: Vec<u16>,
u32s: Vec<u32>,
u64s: Vec<u64>,
i8s: Vec<i8>,
i16s: Vec<i16>,
i32s: Vec<i32>,
i64s: Vec<i64>
) -> bool {
#[derive(Serialize, Deserialize, PartialEq)]
struct Foo {
u8s: Vec<u8>,
u16s: Vec<u16>,
u32s: Vec<u32>,
u64s: Vec<u64>,
i8s: Vec<i8>,
i16s: Vec<i16>,
i32s: Vec<i32>,
i64s: Vec<i64>,
}
let mut ser = FlexbufferSerializer::new();
let foo1 = Foo { u8s, u16s, u32s, u64s, i8s, i16s, i32s, i64s };
foo1.serialize(&mut ser).unwrap();
let r = Reader::get_root(ser.view()).unwrap();
let foo2 = Foo::deserialize(r).unwrap();
foo1 == foo2
}
fn qc_serde_others(
bools: Vec<bool>,
strings: Vec<String>,
f32s: Vec<f32>,
f64s: Vec<f64>
) -> bool {
#[derive(Serialize, Deserialize, PartialEq)]
struct Foo {
bools: Vec<bool>,
strings: Vec<String>,
f32s: Vec<f32>,
f64s: Vec<f64>,
}
let mut ser = FlexbufferSerializer::new();
let foo1 = Foo { bools, strings, f32s, f64s };
foo1.serialize(&mut ser).unwrap();
let r = Reader::get_root(ser.view()).unwrap();
let foo2 = Foo::deserialize(r).unwrap();
foo1 == foo2
}
fn qc_serde_others2(
bools: Vec<bool>,
strings: Vec<String>,
f32s: Vec<f32>,
f64s: Vec<f64>
) -> bool {
#[derive(Serialize, Deserialize, PartialEq)]
struct Foo (Vec<bool>, Vec<String>, Vec<f32>, Vec<f64>);
let mut ser = FlexbufferSerializer::new();
let foo1 = Foo(bools, strings, f32s, f64s);
foo1.serialize(&mut ser).unwrap();
let r = Reader::get_root(ser.view()).unwrap();
let foo2 = Foo::deserialize(r).unwrap();
foo1 == foo2
}
}
#[test]
fn empty_vectors() {
#[derive(PartialEq, Serialize, Deserialize, Default, Debug)]
struct Foo(Vec<u8>, Vec<i8>);
let foo1 = Foo::default();
let mut s = FlexbufferSerializer::new();
foo1.serialize(&mut s).unwrap();
dbg!(s.view());
let r = Reader::get_root(s.view()).unwrap();
let foo2 = Foo::deserialize(r).unwrap();
assert_eq!(foo1, foo2);
}
#[test]
fn string() {
let mut builder = Builder::default();
let mut v = builder.start_vector();
v.push("foo");
v.push("barrr");
v.push("bazzzzzz");
v.end_vector();
let r = Reader::get_root(&builder.view()).unwrap().as_vector();
assert_eq!(r.idx(0).as_str(), "foo");
assert_eq!(r.idx(1).as_str(), "barrr");
assert_eq!(r.idx(2).as_str(), "bazzzzzz");
}
#[test]
fn store_13() {
let finished = singleton::<i32>(13);
let r = Reader::get_root(&finished).unwrap();
assert_eq!(r.as_i32(), 13);
}
#[test]
fn singleton_vector_uint_4_16bit() {
let mut builder = Builder::default();
let mut v = builder.start_vector();
v.push(2u8);
v.push(3u8);
v.push(5u8);
v.end_vector();
let buf1 = builder.view();
let buf2 = singleton(&[2u8, 3, 5]);
assert_eq!(buf1, buf2.as_slice());
let r = Reader::get_root(&buf1).unwrap().as_vector();
assert_eq!(r.idx(0).get_u64(), Ok(2));
assert_eq!(r.idx(1).get_u64(), Ok(3));
assert_eq!(r.idx(2).get_u64(), Ok(5));
assert_eq!(r.index(3).unwrap_err(), ReaderError::IndexOutOfBounds);
}
#[test]
fn vector_uint4() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push(2u8);
v.push(3u8);
v.push(5u8);
v.push(7u8);
v.end_vector();
let r = Reader::get_root(&fxb.view()).unwrap();
let v = r.as_vector();
assert_eq!(v.idx(0).get_u64(), Ok(2));
assert_eq!(v.idx(1).get_u64(), Ok(3));
assert_eq!(v.idx(2).get_u64(), Ok(5));
assert_eq!(v.idx(3).get_u64(), Ok(7));
assert!(v.index(4).is_err());
#[cfg(target_endian = "little")]
{
assert_eq!(r.get_slice::<u8>().unwrap(), [2, 3, 5, 7]);
}
}
#[test]
fn store_and_read_blob() {
let mut fxb = Builder::default();
let mut v = fxb.start_vector();
v.push(Blob(&[1, 2, 3, 4]));
v.push(Blob(&[5, 6, 7]));
v.end_vector();
let r = Reader::get_root(&fxb.view()).unwrap().as_vector();
assert_eq!(r.idx(0).get_blob(), Ok(Blob(&[1, 2, 3, 4])));
assert_eq!(r.idx(1).get_blob(), Ok(Blob(&[5, 6, 7])));
}
#[test]
fn map_64bit() {
let mut fxb = Builder::default();
let mut m = fxb.start_map();
m.push("a", 257u16);
m.push("b", u64::max_value() - 3);
m.end_map();
let r = Reader::get_root(&fxb.view()).unwrap().as_map();
assert_eq!(r.idx("a").as_u16(), 257);
assert_eq!(r.idx("b").as_u64(), u64::max_value() - 3);
}
#[test]
fn index_map() {
let mut fxb = Builder::default();
let mut m = fxb.start_map();
m.push("foo", 17u8);
m.push("bar", 33u16);
m.push("baz", 41u32);
m.end_map();
let r = Reader::get_root(fxb.view()).unwrap().as_map();
assert_eq!(r.idx(0).get_u64(), Ok(33));
assert_eq!(r.idx(1).get_u64(), Ok(41));
assert_eq!(r.idx(2).as_u8(), 17);
assert_eq!(r.index(3).unwrap_err(), ReaderError::IndexOutOfBounds);
assert_eq!(r.idx("bar").as_u64(), 33);
assert_eq!(r.idx("baz").as_u32(), 41);
assert_eq!(r.idx("foo").as_u16(), 17);
assert_eq!(r.index("???").unwrap_err(), ReaderError::KeyNotFound);
}
#[test]
fn map_strings() {
let mut fxb = Builder::default();
{
let mut m = fxb.start_map();
let mut a = m.start_vector("a");
for &s in ["b", "c", "d", "e"].iter() {
a.push(s);
}
a.end_vector();
let mut f = m.start_vector("f");
for &s in ["gh", "ij"].iter() {
f.push(s);
}
}
let r = Reader::get_root(fxb.view()).unwrap().as_map();
let a = r.idx("a").as_vector();
assert_eq!(a.idx(0).as_str(), "b");
assert_eq!(a.idx(1).as_str(), "c");
assert_eq!(a.idx(2).as_str(), "d");
assert_eq!(a.idx(3).as_str(), "e");
let f = r.idx("f").as_vector();
assert_eq!(f.idx(0).as_str(), "gh");
assert_eq!(f.idx(1).as_str(), "ij");
// Defaults to empty string for index errors.
assert_eq!(r.idx("a").as_vector().idx(4).as_str(), "");
assert_eq!(r.idx("b").as_vector().idx(2).as_str(), "");
assert_eq!(r.idx("c").as_str(), "");
}
#[test]
fn store_u64() {
let finished = singleton(u64::max_value() - 10);
let r = Reader::get_root(&finished).unwrap();
assert_eq!(r.get_u64(), Ok(u64::max_value() - 10));
}
#[test]
fn store_indirects() {
let mut b = Builder::default();
let mut v = b.start_vector();
v.push(IndirectInt(-42));
v.push(IndirectUInt(9000));
v.push(IndirectFloat(3.14));
v.end_vector();
let r = Reader::get_root(b.view()).unwrap().as_vector();
assert_eq!(r.idx(0).get_i64().unwrap(), -42);
assert_eq!(r.idx(1).get_u64().unwrap(), 9000);
assert_eq!(r.idx(2).get_f64().unwrap(), 3.14);
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Foo {
a: i8,
b: f64,
c: Vec<u32>,
d: String,
}
quickcheck! {
fn serde_foo(a: i8,
b: f64,
c: Vec<u32>,
d: String) -> bool {
let mut s = FlexbufferSerializer::new();
let data = Foo { a, b, c, d };
data.serialize(&mut s).unwrap();
let read = Foo::deserialize(Reader::get_root(s.view()).unwrap()).unwrap();
data == read
}
}
#[test]
fn serde_serious() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum MyEnum {
Unit,
NewType([i32; 3]),
Tuple(f32, f64),
Struct { a: u8, b: u16, c: u32 },
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct MyNewType;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct MyStruct {
a: u8,
b: u16,
c: u32,
d: u64,
};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct MyUnitStruct(Vec<String>);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct MyTupleStruct(MyNewType, MyUnitStruct, MyStruct, Vec<MyEnum>);
let data = MyTupleStruct(
MyNewType,
MyUnitStruct(vec!["Hello".to_string(), "World".to_string()]),
MyStruct {
a: 2,
b: 4,
c: 8,
d: 16,
},
vec![
MyEnum::Unit,
MyEnum::NewType([-1, 0, 1]),
MyEnum::Unit,
MyEnum::Tuple(3.14, 2.71),
MyEnum::Struct {
a: 32,
b: 64,
c: 128,
},
],
);
let mut s = FlexbufferSerializer::new();
data.serialize(&mut s).unwrap();
let reader = Reader::get_root(s.view()).unwrap();
let read = MyTupleStruct::deserialize(reader).unwrap();
assert_eq!(data, read);
}
#[test]
fn iter() {
let mut fxb = Builder::default();
{
let mut m = fxb.start_map();
m.push("a", "42");
m.push("b", 250i64);
m.push("c", 5000u16);
}
let r = Reader::get_root(fxb.view()).unwrap();
let v: Vec<u32> = r.as_vector().iter().map(|x| x.as_u32()).collect();
assert_eq!(&v, &[42, 250, 5000]);
}
#[test]
fn deserialize_newtype_i8() {
#[derive(Deserialize)]
struct Foo(u8);
let data = [13, 4, 1];
let r = Reader::get_root(&data).unwrap();
let foo = Foo::deserialize(r).unwrap();
assert_eq!(foo.0, 13);
}
#[test]
fn deserialize_newtype_str() {
#[derive(Deserialize)]
struct Foo<'a>(&'a str);
let data = [5, b'h', b'e', b'l', b'l', b'o', b'\0', 6, 5 << 2, 1];
let r = Reader::get_root(&data).unwrap();
let foo = Foo::deserialize(r).unwrap();
assert_eq!(foo.0, "hello");
}
#[test]
#[rustfmt::skip]
fn deserialize_tuple_struct_to_vec_uint4() {
#[derive(Deserialize)]
struct Foo(u8, u16, u32, u64);
let data = [
4, 0, 16, 0, 64, 0, 0, 1, // Data
8, // Vector offset.
23 << 2 | 1, // (VectorUInt4, W16 - referring to data).
1, // Root width W8 - referring to vector.
];
let r = Reader::get_root(&data).unwrap();
let foo = Foo::deserialize(r).unwrap();
assert_eq!(foo.0, 4);
assert_eq!(foo.1, 16);
assert_eq!(foo.2, 64);
assert_eq!(foo.3, 256);
let data = [
1, 2, 3, 4, // The vector.
4, // Root data (offset).
23 << 2, // Root type: VectorUInt4, W8.
1, // Root width: W8.
];
let r = Reader::get_root(&data).unwrap();
let foo = Foo::deserialize(r).unwrap();
assert_eq!(foo.0, 1);
assert_eq!(foo.1, 2);
assert_eq!(foo.2, 3);
assert_eq!(foo.3, 4);
}

View File

@@ -15,9 +15,18 @@
* limitations under the License.
*/
#[macro_use]
extern crate quickcheck;
extern crate flatbuffers;
extern crate flexbuffers;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate quickcheck_derive;
mod flexbuffers_tests;
#[allow(dead_code, unused_imports)]
#[path = "../../include_test/include_test1_generated.rs"]
@@ -32,6 +41,11 @@ pub mod include_test2_generated;
mod monster_test_generated;
pub use monster_test_generated::my_game;
#[rustfmt::skip] // TODO: Use standard rust formatting and remove dead code.
#[allow(dead_code)]
mod flatbuffers_tests {
use super::*;
// Include simple random number generator to ensure results will be the
// same across platforms.
// http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
@@ -2907,3 +2921,4 @@ fn load_file(filename: &str) -> Result<Vec<u8>, std::io::Error> {
f.read_to_end(&mut buf)?;
Ok(buf)
}
}