167 lines
6.5 KiB
Rust
167 lines
6.5 KiB
Rust
// 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::{BitWidth, Builder, Reader, ReaderError};
|
|
|
|
// In this Example we're creating a monster that corresponds to the following JSON:
|
|
// {
|
|
// "coins": [5, 10, 25, 25, 25, 100],
|
|
// "color": [255, 0, 0, 255],
|
|
// "enraged": true,
|
|
// "hp": 80,
|
|
// "mana": 200,
|
|
// "position": [0, 0, 0],
|
|
// "velocity": [1, 0, 0],
|
|
// "weapons": [
|
|
// "fist",
|
|
// {"damage": 15, "name": "great axe"},
|
|
// {"damage": 5, "name": "hammer"}]
|
|
// }
|
|
#[allow(clippy::float_cmp)]
|
|
fn main() {
|
|
// Create a new Flexbuffer builder.
|
|
let mut builder = Builder::default();
|
|
|
|
// The root of the builder can be a singleton, map or vector.
|
|
// Our monster will be represented with a map.
|
|
let mut monster = builder.start_map();
|
|
|
|
// Use `push` to add elements to a vector or map. Note that it up to the programmer to ensure
|
|
// duplicate keys are avoided and the key has no null bytes.
|
|
monster.push("hp", 80);
|
|
monster.push("mana", 200);
|
|
monster.push("enraged", true);
|
|
|
|
// Let's give our monster some weapons. Use `start_vector` to store a vector.
|
|
let mut weapons = monster.start_vector("weapons");
|
|
|
|
// The first weapon is a fist which has no damage so we'll store it as a string.
|
|
// Strings in Flexbuffers are utf8 encoded and are distinct from map Keys which are c strings.
|
|
weapons.push("fist");
|
|
|
|
// The monster also has an axe. We'll store it as a map to make it more interesting.
|
|
let mut axe = weapons.start_map();
|
|
axe.push("name", "great axe");
|
|
axe.push("damage", 15);
|
|
// We're done adding to the axe.
|
|
axe.end_map();
|
|
|
|
// The monster also has a hammer.
|
|
{
|
|
let mut hammer = weapons.start_map();
|
|
hammer.push("name", "hammer");
|
|
hammer.push("damage", 5);
|
|
// Instead of calling `hammer.end_map()`, we can just drop the `hammer` for the same effect.
|
|
// Vectors and maps are completed and serialized when their builders are dropped.
|
|
}
|
|
|
|
// We're done adding weapons.
|
|
weapons.end_vector();
|
|
|
|
// Give the monster some money. Flexbuffers has typed vectors which are smaller than
|
|
// heterogenous vectors. Elements of typed vectors can be pushed one at a time, as above, or
|
|
// they can be passed as a slice. This will be stored as a `FlexBufferType::VectorInt`.
|
|
monster.push("coins", &[5, 10, 25, 25, 25, 100]);
|
|
|
|
// Flexbuffer has special types for fixed-length-typed-vectors (if the length is 3 or 4 and the
|
|
// type is int, uint, or float). They're even more compact than typed vectors.
|
|
// The monster's position and Velocity will be stored as `FlexbufferType::VectorFloat3`.
|
|
monster.push("position", &[0.0; 3]);
|
|
monster.push("velocity", &[1.0, 0.0, 0.0]);
|
|
|
|
// Give the monster bright red skin. In rust, numbers are assumed integers until proven
|
|
// otherwise. We annotate u8 to tell flexbuffers to store it as a FlexbufferType::VectorUInt4.
|
|
monster.push("color", &[255, 0, 0, 255u8]);
|
|
|
|
// End the map at the root of the builder. This finishes the Flexbuffer.
|
|
monster.end_map();
|
|
|
|
// Now the buffer is free to be reused. Let's see the final buffer.
|
|
let data = builder.view();
|
|
println!("The monster was serialized in {:?} bytes.", data.len());
|
|
|
|
// Let's read and verify the data.
|
|
let root = Reader::get_root(data).unwrap();
|
|
println!("The monster: {}", root);
|
|
|
|
let read_monster = root.as_map();
|
|
|
|
// What attributes does this monster have?
|
|
let attrs: Vec<_> = read_monster.iter_keys().collect();
|
|
assert_eq!(
|
|
attrs,
|
|
vec!["coins", "color", "enraged", "hp", "mana", "position", "velocity", "weapons"]
|
|
);
|
|
|
|
// index into a vector or map with the `idx` method.
|
|
let read_hp = read_monster.idx("hp");
|
|
let read_mana = read_monster.idx("mana");
|
|
// If `idx` fails it will return a Null flexbuffer Reader
|
|
|
|
// Use `as_T` to cast the data to your desired type.
|
|
assert_eq!(read_hp.as_u8(), 80);
|
|
assert_eq!(read_hp.as_f32(), 80.0);
|
|
// If it fails it will return T::default().
|
|
assert_eq!(read_hp.as_str(), ""); // Its not a string.
|
|
assert_eq!(read_mana.as_i8(), 0); // 200 is not representable in i8.
|
|
assert!(read_mana.as_vector().is_empty()); // Its not a vector.
|
|
assert_eq!(read_monster.idx("foo").as_i32(), 0); // `foo` is not a monster attribute.
|
|
|
|
// To examine how your data is stored, check the flexbuffer type and bitwidth.
|
|
assert!(read_hp.flexbuffer_type().is_int());
|
|
assert!(read_mana.flexbuffer_type().is_int());
|
|
// Note that mana=200 is bigger than the maximum i8 so everything in the top layer of the
|
|
// monster map is stored in 16 bits.
|
|
assert_eq!(read_hp.bitwidth(), BitWidth::W16);
|
|
assert_eq!(read_monster.idx("mana").bitwidth(), BitWidth::W16);
|
|
|
|
// Use get_T functions if you want to ensure the flexbuffer type matches what you expect.
|
|
assert_eq!(read_hp.get_i64(), Ok(80));
|
|
assert!(read_hp.get_u64().is_err());
|
|
assert!(read_hp.get_vector().is_err());
|
|
|
|
// Analogously, the `index` method is the safe version of `idx`.
|
|
assert!(read_monster.index("hp").is_ok());
|
|
assert_eq!(
|
|
read_monster.index("foo").unwrap_err(),
|
|
ReaderError::KeyNotFound
|
|
);
|
|
|
|
// Maps can also be indexed by usize. They're stored by key so `coins` are the first element.
|
|
let monster_coins = read_monster.idx(0);
|
|
// Maps and Vectors can be iterated over.
|
|
assert!(monster_coins
|
|
.as_vector()
|
|
.iter()
|
|
.map(|r| r.as_u8())
|
|
.eq(vec![5, 10, 25, 25, 25, 100].into_iter()));
|
|
|
|
// Build the answer to life the universe and everything. Reusing a builder resets it. The
|
|
// reused internals won't need to reallocate leading to a potential 2x speedup.
|
|
builder.build_singleton(42);
|
|
|
|
// The monster is now no more.
|
|
assert_eq!(builder.view().len(), 3); // Bytes.
|
|
|
|
let the_answer = Reader::get_root(builder.view()).unwrap();
|
|
assert_eq!(the_answer.as_i32(), 42);
|
|
}
|
|
|
|
#[test]
|
|
fn test_main() {
|
|
main()
|
|
}
|