1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
// 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()
}
|