Merge commit '0f6aab9da6fe982218a01f4a5b896e65fcced437' as 'third_party/flatbuffers'
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
[package]
|
||||
name = "rust_usage_test"
|
||||
version = "0.1.0"
|
||||
authors = ["Robert Winslow <hello@rwinslow.com>",
|
||||
"Casper Neo <cneo@google.com>",
|
||||
"FlatBuffers Maintainers"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
flatbuffers = { path = "../../rust/flatbuffers", default-features = false }
|
||||
flexbuffers = { path = "../../rust/flexbuffers" }
|
||||
serde_derive = "1.0"
|
||||
serde = "1.0"
|
||||
serde_bytes = "0.11"
|
||||
libc_alloc = { version = "1.0.3", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["flatbuffers/default"]
|
||||
no_std = ["libc_alloc"]
|
||||
|
||||
[[bin]]
|
||||
name = "monster_example"
|
||||
path = "bin/monster_example.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "flatbuffers_alloc_check"
|
||||
path = "bin/flatbuffers_alloc_check.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "flexbuffers_alloc_check"
|
||||
path = "bin/flexbuffers_alloc_check.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "sample_flexbuffers"
|
||||
path = "../../samples/sample_flexbuffers.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "sample_flexbuffers_serde"
|
||||
path = "../../samples/sample_flexbuffers_serde.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "sample_flatbuffers"
|
||||
path = "../../samples/sample_binary.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.6"
|
||||
# TODO(rw): look into moving to criterion.rs
|
||||
serde = "1.0"
|
||||
bencher = "0.1.5"
|
||||
static_assertions = "1.0.0"
|
||||
rand = "*"
|
||||
quickcheck_derive = "*"
|
||||
array-init = "2.0"
|
||||
|
||||
[[bench]]
|
||||
# setup for bencher
|
||||
name = "benchmarks"
|
||||
harness = false
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 = "../../monster_test/mod.rs"]
|
||||
mod monster_test_generated;
|
||||
pub use monster_test_generated::my_game;
|
||||
|
||||
benchmark_main!(
|
||||
flatbuffers_benchmarks::benches,
|
||||
flexbuffers_benchmarks::benches
|
||||
);
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
use bencher::{benchmark_group, Bencher};
|
||||
use flatbuffers;
|
||||
|
||||
#[allow(dead_code, unused_imports)]
|
||||
#[path = "../../monster_test/mod.rs"]
|
||||
mod monster_test_generated;
|
||||
pub use monster_test_generated::my_game;
|
||||
|
||||
fn traverse_canonical_buffer(bench: &mut Bencher) {
|
||||
let owned_data = {
|
||||
let mut builder = &mut flatbuffers::FlatBufferBuilder::new();
|
||||
create_serialized_example_with_generated_code(&mut builder, true);
|
||||
builder.finished_data().to_vec()
|
||||
};
|
||||
let data = &owned_data[..];
|
||||
let n = data.len() as u64;
|
||||
bench.iter(|| {
|
||||
traverse_serialized_example_with_generated_code(data);
|
||||
});
|
||||
bench.bytes = n;
|
||||
}
|
||||
|
||||
fn create_canonical_buffer_then_reset(bench: &mut Bencher) {
|
||||
let mut builder = &mut flatbuffers::FlatBufferBuilder::new();
|
||||
// warmup
|
||||
create_serialized_example_with_generated_code(&mut builder, true);
|
||||
let n = builder.finished_data().len() as u64;
|
||||
builder.reset();
|
||||
|
||||
bench.iter(|| {
|
||||
let _ = create_serialized_example_with_generated_code(&mut builder, true);
|
||||
builder.reset();
|
||||
});
|
||||
|
||||
bench.bytes = n;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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 mon = {
|
||||
let name = builder.create_string("MyMonster");
|
||||
let fred_name = builder.create_string("Fred");
|
||||
let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4]);
|
||||
let test4 = builder.create_vector(&[
|
||||
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(),
|
||||
),
|
||||
inventory: Some(inventory),
|
||||
test4: Some(test4),
|
||||
testarrayofstring: Some(builder.create_vector(&[s0, s1])),
|
||||
testarrayoftables: Some(builder.create_vector(&[t0, t1, t2])),
|
||||
..Default::default()
|
||||
};
|
||||
my_game::example::Monster::create(builder, &args)
|
||||
};
|
||||
if finish {
|
||||
my_game::example::finish_monster_buffer(builder, mon);
|
||||
}
|
||||
|
||||
builder.finished_data().len()
|
||||
|
||||
// make it do some work
|
||||
// if builder.finished_data().len() == 0 { panic!("bad benchmark"); }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn blackbox<T>(t: T) -> T {
|
||||
// encapsulate this in case we need to turn it into a noop
|
||||
bencher::black_box(t)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn traverse_serialized_example_with_generated_code(bytes: &[u8]) {
|
||||
let m = unsafe { my_game::example::root_as_monster_unchecked(bytes) };
|
||||
blackbox(m.hp());
|
||||
blackbox(m.mana());
|
||||
blackbox(m.name());
|
||||
let pos = m.pos().unwrap();
|
||||
blackbox(pos.x());
|
||||
blackbox(pos.y());
|
||||
blackbox(pos.z());
|
||||
blackbox(pos.test1());
|
||||
blackbox(pos.test2());
|
||||
let pos_test3 = pos.test3();
|
||||
blackbox(pos_test3.a());
|
||||
blackbox(pos_test3.b());
|
||||
blackbox(m.test_type());
|
||||
let table2 = m.test().unwrap();
|
||||
let monster2 = unsafe { my_game::example::Monster::init_from_table(table2) };
|
||||
blackbox(monster2.name());
|
||||
blackbox(m.inventory());
|
||||
blackbox(m.test4());
|
||||
let testarrayoftables = m.testarrayoftables().unwrap();
|
||||
blackbox(testarrayoftables.get(0).hp());
|
||||
blackbox(testarrayoftables.get(0).name());
|
||||
blackbox(testarrayoftables.get(1).name());
|
||||
blackbox(testarrayoftables.get(2).name());
|
||||
let testarrayofstring = m.testarrayofstring().unwrap();
|
||||
blackbox(testarrayofstring.get(0));
|
||||
blackbox(testarrayofstring.get(1));
|
||||
}
|
||||
|
||||
fn create_string_10(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_string("foobarbaz"); // zero-terminated -> 10 bytes
|
||||
i += 1;
|
||||
if i == 10000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = 10;
|
||||
}
|
||||
|
||||
fn create_string_100(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let s_owned = (0..99).map(|_| "x").collect::<String>();
|
||||
let s: &str = &s_owned;
|
||||
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_string(s); // zero-terminated -> 100 bytes
|
||||
i += 1;
|
||||
if i == 1000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = s.len() as u64;
|
||||
}
|
||||
|
||||
fn create_byte_vector_100_naive(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let v_owned = (0u8..100).map(|i| i).collect::<Vec<u8>>();
|
||||
let v: &[u8] = &v_owned;
|
||||
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_vector(v); // zero-terminated -> 100 bytes
|
||||
i += 1;
|
||||
if i == 10000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = v.len() as u64;
|
||||
}
|
||||
|
||||
fn create_byte_vector_100_optimal(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let v_owned = (0u8..100).map(|i| i).collect::<Vec<u8>>();
|
||||
let v: &[u8] = &v_owned;
|
||||
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_vector(v);
|
||||
i += 1;
|
||||
if i == 10000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = v.len() as u64;
|
||||
}
|
||||
|
||||
fn create_many_tables(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
// We test vtable overhead by making many unique tables of up to 16 fields of u8s.
|
||||
bench.iter(|| {
|
||||
for i in 0..(1u16 << 10) {
|
||||
let t = builder.start_table();
|
||||
for j in 0..15 {
|
||||
if i & (1 << j) == 1 {
|
||||
builder.push_slot_always(i * 2, 42u8);
|
||||
}
|
||||
}
|
||||
builder.end_table(t);
|
||||
}
|
||||
builder.reset();
|
||||
});
|
||||
bench.bytes = 1 << 15;
|
||||
}
|
||||
|
||||
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,
|
||||
create_many_tables,
|
||||
);
|
||||
+295
@@ -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::{benchmark_group, benchmark_main, 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<&[u8]>) {
|
||||
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);
|
||||
+188
@@ -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");
|
||||
}
|
||||
+144
@@ -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")
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
src/generated
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "outdir"
|
||||
version = "0.1.0"
|
||||
authors = ["Casper Neo <cneo@google.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
flatbuffers = { path = "../../../rust/flatbuffers" }
|
||||
serde = "1.0"
|
||||
@@ -0,0 +1,57 @@
|
||||
fn main() {
|
||||
use std::process::Command;
|
||||
let project_root = std::env::current_dir()
|
||||
.unwrap()
|
||||
.parent() // flatbuffers/tests/rust_usage test
|
||||
.unwrap()
|
||||
.parent() // flatbuffers/tests
|
||||
.unwrap()
|
||||
.parent() // flatbuffers/
|
||||
.unwrap()
|
||||
.to_path_buf();
|
||||
let sample_schema = {
|
||||
let mut s = project_root.to_path_buf();
|
||||
s.push("samples");
|
||||
s.push("monster.fbs");
|
||||
s
|
||||
};
|
||||
|
||||
let flatc = {
|
||||
let mut f = project_root.to_path_buf();
|
||||
f.push("flatc");
|
||||
f
|
||||
};
|
||||
|
||||
let out_dir = {
|
||||
let mut d = std::path::Path::new(&std::env::var("OUT_DIR").unwrap()).to_path_buf();
|
||||
d.push("flatbuffers");
|
||||
d
|
||||
};
|
||||
|
||||
Command::new(&flatc)
|
||||
.arg("-o")
|
||||
.arg(&out_dir)
|
||||
.arg("--rust")
|
||||
.arg("--rust-module-root-file")
|
||||
.arg(&sample_schema)
|
||||
.output()
|
||||
.expect("Failed to generate file");
|
||||
|
||||
assert!(out_dir.exists());
|
||||
|
||||
let generated = std::path::Path::new("src/generated");
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if generated.exists() {
|
||||
std::fs::remove_dir(generated).unwrap();
|
||||
}
|
||||
std::os::windows::fs::symlink_dir(out_dir, generated).unwrap();
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
if generated.exists() {
|
||||
std::fs::remove_file(generated).unwrap();
|
||||
}
|
||||
std::os::unix::fs::symlink(out_dir, generated).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// In this example, a build.rs file generates the code and then copies it into generated/
|
||||
extern crate flatbuffers;
|
||||
#[allow(unused_imports, dead_code)]
|
||||
mod generated;
|
||||
use generated::my_game::sample::{Monster, MonsterArgs};
|
||||
|
||||
fn main() {
|
||||
let mut fbb = flatbuffers::FlatBufferBuilder::new();
|
||||
let name = Some(fbb.create_string("bob"));
|
||||
let m = Monster::create(
|
||||
&mut fbb,
|
||||
&MonsterArgs {
|
||||
hp: 1,
|
||||
mana: 2,
|
||||
name,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
fbb.finish(m, None);
|
||||
let mon = flatbuffers::root::<Monster>(fbb.finished_data()).unwrap();
|
||||
assert_eq!(mon.hp(), 1);
|
||||
assert_eq!(mon.mana(), 2);
|
||||
assert_eq!(mon.name().unwrap(), "bob");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_main() {
|
||||
main()
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
extern crate std;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
extern crate array_init;
|
||||
|
||||
#[allow(dead_code, unused_imports)]
|
||||
#[path = "../../arrays_test/mod.rs"]
|
||||
mod arrays_test_generated;
|
||||
|
||||
use alloc::format;
|
||||
use core::fmt::Debug;
|
||||
|
||||
use crate::arrays_test_generated::my_game::example::*;
|
||||
|
||||
extern crate quickcheck;
|
||||
|
||||
use array_init::array_init;
|
||||
use core::mem::size_of;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
|
||||
fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder) {
|
||||
let nested_struct1 = NestedStruct::new(
|
||||
&[-1, 2],
|
||||
TestEnum::A,
|
||||
&[TestEnum::C, TestEnum::B],
|
||||
&[0x1122334455667788, -0x1122334455667788],
|
||||
);
|
||||
let nested_struct2 = NestedStruct::new(
|
||||
&[3, -4],
|
||||
TestEnum::B,
|
||||
&[TestEnum::B, TestEnum::A],
|
||||
&[-0x1122334455667788, 0x1122334455667788],
|
||||
);
|
||||
let array_struct = ArrayStruct::new(
|
||||
12.34,
|
||||
&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF],
|
||||
-127,
|
||||
&[nested_struct1, nested_struct2],
|
||||
1,
|
||||
&[-0x8000000000000000, 0x7FFFFFFFFFFFFFFF],
|
||||
);
|
||||
// Test five makes sense when specified.
|
||||
let ss = ArrayTable::create(
|
||||
builder,
|
||||
&ArrayTableArgs {
|
||||
a: Some(&array_struct),
|
||||
},
|
||||
);
|
||||
finish_array_table_buffer(builder, ss);
|
||||
}
|
||||
|
||||
fn serialized_example_is_accessible_and_correct(
|
||||
bytes: &[u8],
|
||||
identifier_required: bool,
|
||||
size_prefixed: bool,
|
||||
) {
|
||||
if identifier_required {
|
||||
let correct = if size_prefixed {
|
||||
array_table_size_prefixed_buffer_has_identifier(bytes)
|
||||
} else {
|
||||
array_table_buffer_has_identifier(bytes)
|
||||
};
|
||||
|
||||
assert_eq!(correct, true);
|
||||
}
|
||||
|
||||
let array_table = if size_prefixed {
|
||||
size_prefixed_root_as_array_table(bytes).unwrap()
|
||||
} else {
|
||||
root_as_array_table(bytes).unwrap()
|
||||
};
|
||||
|
||||
let array_struct = array_table.a().unwrap();
|
||||
assert_eq!(array_struct.a(), 12.34);
|
||||
assert_eq!(array_struct.b().len(), 0xF);
|
||||
assert_eq!(array_struct.b().iter().sum::<i32>(), 120);
|
||||
assert_eq!(array_struct.c(), -127);
|
||||
|
||||
assert_eq!(array_struct.d().len(), 2);
|
||||
let nested_struct1 = array_struct.d().get(0);
|
||||
assert_eq!(nested_struct1.a().len(), 2);
|
||||
assert_eq!(nested_struct1.a().iter().sum::<i32>(), 1);
|
||||
assert_eq!(nested_struct1.b(), TestEnum::A);
|
||||
assert_eq!(nested_struct1.c().len(), 2);
|
||||
assert_eq!(nested_struct1.c().get(0), TestEnum::C);
|
||||
assert_eq!(nested_struct1.c().get(1), TestEnum::B);
|
||||
assert_eq!(nested_struct1.d().len(), 2);
|
||||
assert_eq!(
|
||||
[nested_struct1.d().get(0), nested_struct1.d().get(1)],
|
||||
[0x1122334455667788, -0x1122334455667788]
|
||||
);
|
||||
let nested_struct2 = array_struct.d().get(1);
|
||||
assert_eq!(nested_struct2.a().len(), 2);
|
||||
assert_eq!(nested_struct2.a().iter().sum::<i32>(), -1);
|
||||
assert_eq!(nested_struct2.b(), TestEnum::B);
|
||||
assert_eq!(nested_struct2.c().len(), 2);
|
||||
assert_eq!(nested_struct2.c().get(0), TestEnum::B);
|
||||
assert_eq!(nested_struct2.c().get(1), TestEnum::A);
|
||||
assert_eq!(nested_struct2.d().len(), 2);
|
||||
let arr: [i64; 2] = nested_struct2.d().into();
|
||||
assert_eq!(
|
||||
arr,
|
||||
[-0x1122334455667788, 0x1122334455667788]
|
||||
);
|
||||
|
||||
assert_eq!(array_struct.e(), 1);
|
||||
assert_eq!(array_struct.f().len(), 2);
|
||||
assert_eq!(array_struct.f().get(0), -0x8000000000000000);
|
||||
assert_eq!(array_struct.f().get(1), 0x7FFFFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_code_creates_correct_example() {
|
||||
let mut b = flatbuffers::FlatBufferBuilder::new();
|
||||
create_serialized_example_with_generated_code(&mut b);
|
||||
let buf = b.finished_data();
|
||||
serialized_example_is_accessible_and_correct(&buf[..], true, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_netsted_struct_is_32_bytes() {
|
||||
assert_eq!(32, ::core::mem::size_of::<NestedStruct>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_array_struct_is_160_bytes() {
|
||||
assert_eq!(160, ::core::mem::size_of::<ArrayStruct>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_api_reads_correctly() {
|
||||
let mut b = flatbuffers::FlatBufferBuilder::new();
|
||||
create_serialized_example_with_generated_code(&mut b);
|
||||
|
||||
let array_table = root_as_array_table(b.finished_data()).unwrap().unpack();
|
||||
|
||||
let array_struct = array_table.a.unwrap();
|
||||
assert_eq!(array_struct.a, 12.34);
|
||||
assert_eq!(array_struct.b.len(), 0xF);
|
||||
assert_eq!(array_struct.b.iter().sum::<i32>(), 120);
|
||||
assert_eq!(array_struct.c, -127);
|
||||
|
||||
assert_eq!(array_struct.d.len(), 2);
|
||||
let nested_struct1 = &array_struct.d[0];
|
||||
assert_eq!(nested_struct1.a.len(), 2);
|
||||
assert_eq!(nested_struct1.a.iter().sum::<i32>(), 1);
|
||||
assert_eq!(nested_struct1.b, TestEnum::A);
|
||||
assert_eq!(nested_struct1.c.len(), 2);
|
||||
assert_eq!(nested_struct1.c[0], TestEnum::C);
|
||||
assert_eq!(nested_struct1.c[1], TestEnum::B);
|
||||
assert_eq!(nested_struct1.d.len(), 2);
|
||||
assert_eq!(nested_struct1.d, [0x1122334455667788, -0x1122334455667788]);
|
||||
let nested_struct2 = &array_struct.d[1];
|
||||
assert_eq!(nested_struct2.a.len(), 2);
|
||||
assert_eq!(nested_struct2.a.iter().sum::<i32>(), -1);
|
||||
assert_eq!(nested_struct2.b, TestEnum::B);
|
||||
assert_eq!(nested_struct2.c.len(), 2);
|
||||
assert_eq!(nested_struct2.c[0], TestEnum::B);
|
||||
assert_eq!(nested_struct2.c[1], TestEnum::A);
|
||||
assert_eq!(nested_struct2.d.len(), 2);
|
||||
assert_eq!(nested_struct2.d, [-0x1122334455667788, 0x1122334455667788]);
|
||||
|
||||
assert_eq!(array_struct.e, 1);
|
||||
assert_eq!(array_struct.f.len(), 2);
|
||||
assert_eq!(array_struct.f[0], -0x8000000000000000);
|
||||
assert_eq!(array_struct.f[1], 0x7FFFFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_api_defaults() {
|
||||
use arrays_test_generated::my_game::example::*;
|
||||
|
||||
assert_eq!(
|
||||
NestedStructT::default(),
|
||||
NestedStructT {
|
||||
a: [0, 0],
|
||||
b: TestEnum::default(),
|
||||
c: [TestEnum::default(), TestEnum::default()],
|
||||
d: [0, 0],
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ArrayStructT::default(),
|
||||
ArrayStructT {
|
||||
a: 0.0,
|
||||
b: [0; 0xF],
|
||||
c: 0,
|
||||
d: [NestedStructT::default(), NestedStructT::default()],
|
||||
e: 0,
|
||||
f: [0, 0],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_code_debug_prints_correctly() {
|
||||
let b = &mut flatbuffers::FlatBufferBuilder::new();
|
||||
create_serialized_example_with_generated_code(b);
|
||||
let buf = b.finished_data();
|
||||
let array_table = root_as_array_table(buf).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:.5?}", &array_table),
|
||||
"ArrayTable { a: Some(ArrayStruct { a: 12.34000, \
|
||||
b: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], \
|
||||
c: -127, d: [NestedStruct { a: [-1, 2], b: A, c: [C, B], \
|
||||
d: [1234605616436508552, -1234605616436508552] }, \
|
||||
NestedStruct { a: [3, -4], b: B, c: [B, A], d: [-1234605616436508552, 1234605616436508552] }], \
|
||||
e: 1, f: [-9223372036854775808, 9223372036854775807] }) }"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert_on_too_small_array_buf() {
|
||||
let a = [0u8; 19];
|
||||
unsafe { flatbuffers::Array::<i32, 5>::new(&a) };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert_on_too_big_array_buf() {
|
||||
let a = [0u8; 21];
|
||||
unsafe { flatbuffers::Array::<i32, 5>::new(&a) };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_endian = "little")]
|
||||
fn verify_struct_array_alignment() {
|
||||
let mut b = flatbuffers::FlatBufferBuilder::new();
|
||||
create_serialized_example_with_generated_code(&mut b);
|
||||
let buf = b.finished_data();
|
||||
let array_table = root_as_array_table(buf).unwrap();
|
||||
let array_struct = array_table.a().unwrap();
|
||||
let struct_start_ptr = array_struct.0.as_ptr() as usize;
|
||||
let b_start_ptr = array_struct.b().as_ptr() as usize;
|
||||
let d_start_ptr = array_struct.d().as_ptr() as usize;
|
||||
// The T type of b
|
||||
let b_aln = ::core::mem::align_of::<i32>();
|
||||
assert_eq!((b_start_ptr - struct_start_ptr) % b_aln, 0);
|
||||
assert_eq!((d_start_ptr - b_start_ptr) % b_aln, 0);
|
||||
assert_eq!((d_start_ptr - struct_start_ptr) % 8, 0);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct FakeArray<T, const N: usize>([T; N]);
|
||||
|
||||
impl<T: Arbitrary + Debug + PartialEq, const N: usize> Arbitrary for FakeArray<T, N> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> FakeArray<T, N> {
|
||||
let x: [T; N] = array_init(|_| {
|
||||
loop {
|
||||
let generated_scalar = T::arbitrary(g);
|
||||
// Verify that generated scalar is not Nan, which is not equals to itself,
|
||||
// therefore we can't use it to validate input == output
|
||||
if generated_scalar == generated_scalar { return generated_scalar; }
|
||||
}
|
||||
});
|
||||
FakeArray { 0: x }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod array_fuzz {
|
||||
#[cfg(not(miri))] // slow.
|
||||
extern crate quickcheck;
|
||||
extern crate flatbuffers;
|
||||
|
||||
use self::flatbuffers::{Follow, Push};
|
||||
use super::*;
|
||||
|
||||
const MAX_TESTS: u64 = 20;
|
||||
const ARRAY_SIZE: usize = 29;
|
||||
|
||||
// This uses a macro because lifetimes for the trait-bounded function get too
|
||||
// complicated.
|
||||
macro_rules! impl_prop {
|
||||
($test_name:ident, $fn_name:ident, $ty:ident) => (
|
||||
fn $fn_name(xs: FakeArray<$ty, ARRAY_SIZE>) {
|
||||
let mut test_buf = [0 as u8; 1024];
|
||||
let arr: flatbuffers::Array<$ty, ARRAY_SIZE> = unsafe {
|
||||
flatbuffers::emplace_scalar_array(&mut test_buf, 0, &xs.0);
|
||||
flatbuffers::Array::follow(&test_buf, 0)
|
||||
};
|
||||
let got: [$ty; ARRAY_SIZE] = arr.into();
|
||||
assert_eq!(got, xs.0);
|
||||
}
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
quickcheck::QuickCheck::new().max_tests(MAX_TESTS).quickcheck($fn_name as fn(FakeArray<$ty, ARRAY_SIZE>));
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl_prop!(test_bool, prop_bool, bool);
|
||||
impl_prop!(test_u8, prop_u8, u8);
|
||||
impl_prop!(test_i8, prop_i8, i8);
|
||||
impl_prop!(test_u16, prop_u16, u16);
|
||||
impl_prop!(test_u32, prop_u32, u32);
|
||||
impl_prop!(test_u64, prop_u64, u64);
|
||||
impl_prop!(test_i16, prop_i16, i16);
|
||||
impl_prop!(test_i32, prop_i32, i32);
|
||||
impl_prop!(test_i64, prop_i64, i64);
|
||||
impl_prop!(test_f32, prop_f32, f32);
|
||||
impl_prop!(test_f64, prop_f64, f64);
|
||||
|
||||
const NESTED_STRUCT_SIZE: usize = size_of::<NestedStruct>();
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct NestedStructWrapper(NestedStruct);
|
||||
|
||||
impl Arbitrary for NestedStructWrapper {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> NestedStructWrapper {
|
||||
let mut x = NestedStruct::default();
|
||||
x.0 = FakeArray::<u8, NESTED_STRUCT_SIZE>::arbitrary(g).0;
|
||||
NestedStructWrapper { 0: x }
|
||||
}
|
||||
}
|
||||
|
||||
fn prop_struct(xs: FakeArray<NestedStructWrapper, ARRAY_SIZE>) {
|
||||
let mut test_buf = [0 as u8; 1024];
|
||||
let native_struct_array: [&NestedStruct; ARRAY_SIZE] = array_init::from_iter(xs.0.iter().map(|x| &x.0)).unwrap();
|
||||
for i in 0..ARRAY_SIZE {
|
||||
let offset = i * NESTED_STRUCT_SIZE;
|
||||
unsafe { native_struct_array[i].push(&mut test_buf[offset..offset + NESTED_STRUCT_SIZE], 0) };
|
||||
}
|
||||
let arr: flatbuffers::Array<NestedStruct, ARRAY_SIZE> = unsafe { flatbuffers::Array::follow(&test_buf, 0) };
|
||||
let got: [&NestedStruct; ARRAY_SIZE] = arr.into();
|
||||
assert_eq!(got, native_struct_array);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(miri))] // slow.
|
||||
fn test_struct() {
|
||||
quickcheck::QuickCheck::new().max_tests(MAX_TESTS).quickcheck(prop_struct as fn(FakeArray<NestedStructWrapper, ARRAY_SIZE>));
|
||||
}
|
||||
}
|
||||
+535
@@ -0,0 +1,535 @@
|
||||
// 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.
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // slow.
|
||||
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.
|
||||
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.as_ref()).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(core::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;
|
||||
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.
|
||||
}
|
||||
+50
@@ -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.as_ref()).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].as_ref()));
|
||||
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");
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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;
|
||||
#[cfg(not(feature = "no_std"))] // uses file I/O
|
||||
mod interop;
|
||||
mod other_api;
|
||||
#[cfg(not(miri))] // slow.
|
||||
mod qc_serious;
|
||||
mod rwyw;
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
// 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 alloc::vec::Vec;
|
||||
|
||||
use flexbuffers::*;
|
||||
#[cfg(not(miri))] // slow.
|
||||
use quickcheck::QuickCheck;
|
||||
|
||||
#[test]
|
||||
#[cfg(not(miri))] // slow.
|
||||
fn qc_reader_no_crash() {
|
||||
fn no_crash(xs: Vec<u8>) -> bool {
|
||||
let r = Reader::get_root(xs.as_ref());
|
||||
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::<&[u8]>::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.as_ref()).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.as_ref()).unwrap().as_u64(), 0);
|
||||
}
|
||||
|
||||
/// Verifies that the clone operation is shallow / zero copy.
|
||||
#[test]
|
||||
fn clone_is_shallow() {
|
||||
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();
|
||||
|
||||
let r2 = r.clone();
|
||||
|
||||
assert_eq!(r.buffer().as_ptr(), r2.buffer().as_ptr());
|
||||
}
|
||||
|
||||
#[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();
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use super::rwyw::NonNullString;
|
||||
use flexbuffers::*;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
|
||||
#[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>,
|
||||
},
|
||||
Blobs(#[serde(with = "serde_bytes")] Vec<u8>),
|
||||
}
|
||||
|
||||
// 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(original: Struct) -> bool {
|
||||
let struct_buf = flexbuffers::to_vec(&original).unwrap();
|
||||
let root = Reader::get_root(&*struct_buf).unwrap();
|
||||
let reader_buf = flexbuffers::to_vec(&root).unwrap();
|
||||
let deserialized: Struct = flexbuffers::from_slice(&struct_buf).unwrap();
|
||||
let reserialized: Struct = flexbuffers::from_slice(&reader_buf).unwrap();
|
||||
|
||||
original == deserialized && original == reserialized
|
||||
}
|
||||
}
|
||||
+517
@@ -0,0 +1,517 @@
|
||||
// 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 alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
// Read what you wrote.
|
||||
use flexbuffers::*;
|
||||
#[cfg(not(miri))] // slow.
|
||||
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 = core::cmp::min(1, usize::arbitrary(g));
|
||||
NonNullString(
|
||||
(0..)
|
||||
.map(|_| <char>::arbitrary(g))
|
||||
.filter(|&b| b != '\0')
|
||||
.take(size)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // slow.
|
||||
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() < core::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))
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
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
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
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.as_ref()));
|
||||
}
|
||||
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();
|
||||
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.as_ref()).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());
|
||||
#[allow(deprecated)]
|
||||
#[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].as_ref()));
|
||||
v.push(Blob([5, 6, 7].as_ref()));
|
||||
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].as_ref())));
|
||||
assert_eq!(r.idx(1).get_blob(), Ok(Blob([5, 6, 7].as_ref())));
|
||||
}
|
||||
#[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.as_ref()).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,
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // slow.
|
||||
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 serialize_serde_with_bytes_as_blob() {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Foo(#[serde(with = "serde_bytes")] Vec<u8>);
|
||||
let mut s = FlexbufferSerializer::new();
|
||||
Foo(vec![5, 6, 7, 8]).serialize(&mut s).unwrap();
|
||||
let reader = Reader::get_root(s.view()).unwrap();
|
||||
assert_eq!(reader.flexbuffer_type(), FlexBufferType::Blob);
|
||||
assert_eq!(reader.as_blob(), Blob([5, 6, 7, 8].as_ref()));
|
||||
}
|
||||
#[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.as_ref()).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.as_ref()).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.as_ref()).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.as_ref()).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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[allow(dead_code, unused_imports)]
|
||||
#[path = "../../more_defaults/mod.rs"]
|
||||
mod more_defaults_generated;
|
||||
use self::more_defaults_generated::*;
|
||||
|
||||
#[test]
|
||||
fn object_defaults() {
|
||||
assert_eq!(
|
||||
MoreDefaultsT::default(),
|
||||
MoreDefaultsT {
|
||||
ints: Vec::new(),
|
||||
floats: Vec::new(),
|
||||
empty_string: "".to_string(),
|
||||
some_string: "some".to_string(),
|
||||
abcs: Vec::new(),
|
||||
bools: Vec::new(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonpresent_values() {
|
||||
let m = flatbuffers::root::<MoreDefaults>(&[0; 4]).unwrap();
|
||||
assert_eq!(m.ints().len(), 0);
|
||||
assert_eq!(m.floats().len(), 0);
|
||||
assert_eq!(m.abcs().len(), 0);
|
||||
assert_eq!(m.bools().len(), 0);
|
||||
assert_eq!(m.empty_string(), "");
|
||||
assert_eq!(m.some_string(), "some");
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
#[allow(dead_code, unused_imports)]
|
||||
#[path = "../../optional_scalars/mod.rs"]
|
||||
mod optional_scalars_generated;
|
||||
use crate::optional_scalars_generated::optional_scalars::*;
|
||||
|
||||
// There are 3 variants of scalars in tables - those specified with default=42,
|
||||
// optional scalars, and those with nothing specified (implicitly default=0).
|
||||
// This tests that you can read what you write.
|
||||
macro_rules! make_test {
|
||||
(
|
||||
$test_name: ident,
|
||||
$just: ident, $default: ident, $maybe: ident,
|
||||
$five: expr, $zero: expr, $fortytwo: expr
|
||||
) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||
// Test five makes sense when specified.
|
||||
let ss = ScalarStuff::create(
|
||||
&mut builder,
|
||||
&ScalarStuffArgs {
|
||||
$just: $five,
|
||||
$default: $five,
|
||||
$maybe: Some($five),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
builder.finish(ss, None);
|
||||
|
||||
let s = flatbuffers::root::<ScalarStuff>(builder.finished_data()).unwrap();
|
||||
assert_eq!(s.$just(), $five);
|
||||
assert_eq!(s.$default(), $five);
|
||||
assert_eq!(s.$maybe(), Some($five));
|
||||
|
||||
// Test defaults are used when not specified.
|
||||
let s = flatbuffers::root::<ScalarStuff>(&[0; 8]).unwrap();
|
||||
assert_eq!(s.$just(), $zero);
|
||||
assert_eq!(s.$default(), $fortytwo);
|
||||
assert_eq!(s.$maybe(), None);
|
||||
|
||||
// Same for object API
|
||||
let s = flatbuffers::root::<ScalarStuff>(builder.finished_data()).unwrap().unpack();
|
||||
assert_eq!(s.$just, $five);
|
||||
assert_eq!(s.$default, $five);
|
||||
assert_eq!(s.$maybe, Some($five));
|
||||
let s = flatbuffers::root::<ScalarStuff>(&[0; 8]).unwrap().unpack();
|
||||
assert_eq!(s.$just, $zero);
|
||||
assert_eq!(s.$default, $fortytwo);
|
||||
assert_eq!(s.$maybe, None);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_test!(optional_i8, just_i8, default_i8, maybe_i8, 5, 0, 42);
|
||||
make_test!(optional_u8, just_u8, default_u8, maybe_u8, 5, 0, 42);
|
||||
make_test!(optional_i16, just_i16, default_i16, maybe_i16, 5, 0, 42);
|
||||
make_test!(optional_u16, just_u16, default_u16, maybe_u16, 5, 0, 42);
|
||||
make_test!(optional_i32, just_i32, default_i32, maybe_i32, 5, 0, 42);
|
||||
make_test!(optional_u32, just_u32, default_u32, maybe_u32, 5, 0, 42);
|
||||
make_test!(optional_i64, just_i64, default_i64, maybe_i64, 5, 0, 42);
|
||||
make_test!(optional_u64, just_u64, default_u64, maybe_u64, 5, 0, 42);
|
||||
make_test!(
|
||||
optional_f32,
|
||||
just_f32,
|
||||
default_f32,
|
||||
maybe_f32,
|
||||
5.0,
|
||||
0.0,
|
||||
42.0
|
||||
);
|
||||
make_test!(
|
||||
optional_f64,
|
||||
just_f64,
|
||||
default_f64,
|
||||
maybe_f64,
|
||||
5.0,
|
||||
0.0,
|
||||
42.0
|
||||
);
|
||||
make_test!(
|
||||
optional_bool,
|
||||
just_bool,
|
||||
default_bool,
|
||||
maybe_bool,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
);
|
||||
make_test!(
|
||||
optional_enum,
|
||||
just_enum,
|
||||
default_enum,
|
||||
maybe_enum,
|
||||
OptionalByte::Two,
|
||||
OptionalByte::None,
|
||||
OptionalByte::One
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn object_api_defaults() {
|
||||
assert_eq!(
|
||||
ScalarStuffT::default(),
|
||||
ScalarStuffT {
|
||||
just_i8: 0,
|
||||
maybe_i8: None,
|
||||
default_i8: 42,
|
||||
just_u8: 0,
|
||||
maybe_u8: None,
|
||||
default_u8: 42,
|
||||
|
||||
just_i16: 0,
|
||||
maybe_i16: None,
|
||||
default_i16: 42,
|
||||
just_u16: 0,
|
||||
maybe_u16: None,
|
||||
default_u16: 42,
|
||||
|
||||
just_i32: 0,
|
||||
maybe_i32: None,
|
||||
default_i32: 42,
|
||||
just_u32: 0,
|
||||
maybe_u32: None,
|
||||
default_u32: 42,
|
||||
|
||||
just_i64: 0,
|
||||
maybe_i64: None,
|
||||
default_i64: 42,
|
||||
just_u64: 0,
|
||||
maybe_u64: None,
|
||||
default_u64: 42,
|
||||
|
||||
just_f32: 0.0,
|
||||
maybe_f32: None,
|
||||
default_f32: 42.0,
|
||||
just_f64: 0.0,
|
||||
maybe_f64: None,
|
||||
default_f64: 42.0,
|
||||
|
||||
just_bool: false,
|
||||
maybe_bool: None,
|
||||
default_bool: true,
|
||||
|
||||
just_enum: OptionalByte::None,
|
||||
maybe_enum: None,
|
||||
default_enum: OptionalByte::One,
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user