1*890232f2SAndroid Build Coastguard Worker // Copyright 2019 Google LLC
2*890232f2SAndroid Build Coastguard Worker //
3*890232f2SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*890232f2SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*890232f2SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*890232f2SAndroid Build Coastguard Worker //
7*890232f2SAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*890232f2SAndroid Build Coastguard Worker //
9*890232f2SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*890232f2SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*890232f2SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*890232f2SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*890232f2SAndroid Build Coastguard Worker // limitations under the License.
14*890232f2SAndroid Build Coastguard Worker
15*890232f2SAndroid Build Coastguard Worker extern crate flexbuffers;
16*890232f2SAndroid Build Coastguard Worker
17*890232f2SAndroid Build Coastguard Worker use flexbuffers::{BitWidth, Builder, Reader, ReaderError};
18*890232f2SAndroid Build Coastguard Worker
19*890232f2SAndroid Build Coastguard Worker // In this Example we're creating a monster that corresponds to the following JSON:
20*890232f2SAndroid Build Coastguard Worker // {
21*890232f2SAndroid Build Coastguard Worker // "coins": [5, 10, 25, 25, 25, 100],
22*890232f2SAndroid Build Coastguard Worker // "color": [255, 0, 0, 255],
23*890232f2SAndroid Build Coastguard Worker // "enraged": true,
24*890232f2SAndroid Build Coastguard Worker // "hp": 80,
25*890232f2SAndroid Build Coastguard Worker // "mana": 200,
26*890232f2SAndroid Build Coastguard Worker // "position": [0, 0, 0],
27*890232f2SAndroid Build Coastguard Worker // "velocity": [1, 0, 0],
28*890232f2SAndroid Build Coastguard Worker // "weapons": [
29*890232f2SAndroid Build Coastguard Worker // "fist",
30*890232f2SAndroid Build Coastguard Worker // {"damage": 15, "name": "great axe"},
31*890232f2SAndroid Build Coastguard Worker // {"damage": 5, "name": "hammer"}]
32*890232f2SAndroid Build Coastguard Worker // }
33*890232f2SAndroid Build Coastguard Worker #[allow(clippy::float_cmp)]
main()34*890232f2SAndroid Build Coastguard Worker fn main() {
35*890232f2SAndroid Build Coastguard Worker // Create a new Flexbuffer builder.
36*890232f2SAndroid Build Coastguard Worker let mut builder = Builder::default();
37*890232f2SAndroid Build Coastguard Worker
38*890232f2SAndroid Build Coastguard Worker // The root of the builder can be a singleton, map or vector.
39*890232f2SAndroid Build Coastguard Worker // Our monster will be represented with a map.
40*890232f2SAndroid Build Coastguard Worker let mut monster = builder.start_map();
41*890232f2SAndroid Build Coastguard Worker
42*890232f2SAndroid Build Coastguard Worker // Use `push` to add elements to a vector or map. Note that it up to the programmer to ensure
43*890232f2SAndroid Build Coastguard Worker // duplicate keys are avoided and the key has no null bytes.
44*890232f2SAndroid Build Coastguard Worker monster.push("hp", 80);
45*890232f2SAndroid Build Coastguard Worker monster.push("mana", 200);
46*890232f2SAndroid Build Coastguard Worker monster.push("enraged", true);
47*890232f2SAndroid Build Coastguard Worker
48*890232f2SAndroid Build Coastguard Worker // Let's give our monster some weapons. Use `start_vector` to store a vector.
49*890232f2SAndroid Build Coastguard Worker let mut weapons = monster.start_vector("weapons");
50*890232f2SAndroid Build Coastguard Worker
51*890232f2SAndroid Build Coastguard Worker // The first weapon is a fist which has no damage so we'll store it as a string.
52*890232f2SAndroid Build Coastguard Worker // Strings in Flexbuffers are utf8 encoded and are distinct from map Keys which are c strings.
53*890232f2SAndroid Build Coastguard Worker weapons.push("fist");
54*890232f2SAndroid Build Coastguard Worker
55*890232f2SAndroid Build Coastguard Worker // The monster also has an axe. We'll store it as a map to make it more interesting.
56*890232f2SAndroid Build Coastguard Worker let mut axe = weapons.start_map();
57*890232f2SAndroid Build Coastguard Worker axe.push("name", "great axe");
58*890232f2SAndroid Build Coastguard Worker axe.push("damage", 15);
59*890232f2SAndroid Build Coastguard Worker // We're done adding to the axe.
60*890232f2SAndroid Build Coastguard Worker axe.end_map();
61*890232f2SAndroid Build Coastguard Worker
62*890232f2SAndroid Build Coastguard Worker // The monster also has a hammer.
63*890232f2SAndroid Build Coastguard Worker {
64*890232f2SAndroid Build Coastguard Worker let mut hammer = weapons.start_map();
65*890232f2SAndroid Build Coastguard Worker hammer.push("name", "hammer");
66*890232f2SAndroid Build Coastguard Worker hammer.push("damage", 5);
67*890232f2SAndroid Build Coastguard Worker // Instead of calling `hammer.end_map()`, we can just drop the `hammer` for the same effect.
68*890232f2SAndroid Build Coastguard Worker // Vectors and maps are completed and serialized when their builders are dropped.
69*890232f2SAndroid Build Coastguard Worker }
70*890232f2SAndroid Build Coastguard Worker
71*890232f2SAndroid Build Coastguard Worker // We're done adding weapons.
72*890232f2SAndroid Build Coastguard Worker weapons.end_vector();
73*890232f2SAndroid Build Coastguard Worker
74*890232f2SAndroid Build Coastguard Worker // Give the monster some money. Flexbuffers has typed vectors which are smaller than
75*890232f2SAndroid Build Coastguard Worker // heterogenous vectors. Elements of typed vectors can be pushed one at a time, as above, or
76*890232f2SAndroid Build Coastguard Worker // they can be passed as a slice. This will be stored as a `FlexBufferType::VectorInt`.
77*890232f2SAndroid Build Coastguard Worker monster.push("coins", &[5, 10, 25, 25, 25, 100]);
78*890232f2SAndroid Build Coastguard Worker
79*890232f2SAndroid Build Coastguard Worker // Flexbuffer has special types for fixed-length-typed-vectors (if the length is 3 or 4 and the
80*890232f2SAndroid Build Coastguard Worker // type is int, uint, or float). They're even more compact than typed vectors.
81*890232f2SAndroid Build Coastguard Worker // The monster's position and Velocity will be stored as `FlexbufferType::VectorFloat3`.
82*890232f2SAndroid Build Coastguard Worker monster.push("position", &[0.0; 3]);
83*890232f2SAndroid Build Coastguard Worker monster.push("velocity", &[1.0, 0.0, 0.0]);
84*890232f2SAndroid Build Coastguard Worker
85*890232f2SAndroid Build Coastguard Worker // Give the monster bright red skin. In rust, numbers are assumed integers until proven
86*890232f2SAndroid Build Coastguard Worker // otherwise. We annotate u8 to tell flexbuffers to store it as a FlexbufferType::VectorUInt4.
87*890232f2SAndroid Build Coastguard Worker monster.push("color", &[255, 0, 0, 255u8]);
88*890232f2SAndroid Build Coastguard Worker
89*890232f2SAndroid Build Coastguard Worker // End the map at the root of the builder. This finishes the Flexbuffer.
90*890232f2SAndroid Build Coastguard Worker monster.end_map();
91*890232f2SAndroid Build Coastguard Worker
92*890232f2SAndroid Build Coastguard Worker // Now the buffer is free to be reused. Let's see the final buffer.
93*890232f2SAndroid Build Coastguard Worker let data = builder.view();
94*890232f2SAndroid Build Coastguard Worker println!("The monster was serialized in {:?} bytes.", data.len());
95*890232f2SAndroid Build Coastguard Worker
96*890232f2SAndroid Build Coastguard Worker // Let's read and verify the data.
97*890232f2SAndroid Build Coastguard Worker let root = Reader::get_root(data).unwrap();
98*890232f2SAndroid Build Coastguard Worker println!("The monster: {}", root);
99*890232f2SAndroid Build Coastguard Worker
100*890232f2SAndroid Build Coastguard Worker let read_monster = root.as_map();
101*890232f2SAndroid Build Coastguard Worker
102*890232f2SAndroid Build Coastguard Worker // What attributes does this monster have?
103*890232f2SAndroid Build Coastguard Worker let attrs: Vec<_> = read_monster.iter_keys().collect();
104*890232f2SAndroid Build Coastguard Worker assert_eq!(
105*890232f2SAndroid Build Coastguard Worker attrs,
106*890232f2SAndroid Build Coastguard Worker vec!["coins", "color", "enraged", "hp", "mana", "position", "velocity", "weapons"]
107*890232f2SAndroid Build Coastguard Worker );
108*890232f2SAndroid Build Coastguard Worker
109*890232f2SAndroid Build Coastguard Worker // index into a vector or map with the `idx` method.
110*890232f2SAndroid Build Coastguard Worker let read_hp = read_monster.idx("hp");
111*890232f2SAndroid Build Coastguard Worker let read_mana = read_monster.idx("mana");
112*890232f2SAndroid Build Coastguard Worker // If `idx` fails it will return a Null flexbuffer Reader
113*890232f2SAndroid Build Coastguard Worker
114*890232f2SAndroid Build Coastguard Worker // Use `as_T` to cast the data to your desired type.
115*890232f2SAndroid Build Coastguard Worker assert_eq!(read_hp.as_u8(), 80);
116*890232f2SAndroid Build Coastguard Worker assert_eq!(read_hp.as_f32(), 80.0);
117*890232f2SAndroid Build Coastguard Worker // If it fails it will return T::default().
118*890232f2SAndroid Build Coastguard Worker assert_eq!(read_hp.as_str(), ""); // Its not a string.
119*890232f2SAndroid Build Coastguard Worker assert_eq!(read_mana.as_i8(), 0); // 200 is not representable in i8.
120*890232f2SAndroid Build Coastguard Worker assert!(read_mana.as_vector().is_empty()); // Its not a vector.
121*890232f2SAndroid Build Coastguard Worker assert_eq!(read_monster.idx("foo").as_i32(), 0); // `foo` is not a monster attribute.
122*890232f2SAndroid Build Coastguard Worker
123*890232f2SAndroid Build Coastguard Worker // To examine how your data is stored, check the flexbuffer type and bitwidth.
124*890232f2SAndroid Build Coastguard Worker assert!(read_hp.flexbuffer_type().is_int());
125*890232f2SAndroid Build Coastguard Worker assert!(read_mana.flexbuffer_type().is_int());
126*890232f2SAndroid Build Coastguard Worker // Note that mana=200 is bigger than the maximum i8 so everything in the top layer of the
127*890232f2SAndroid Build Coastguard Worker // monster map is stored in 16 bits.
128*890232f2SAndroid Build Coastguard Worker assert_eq!(read_hp.bitwidth(), BitWidth::W16);
129*890232f2SAndroid Build Coastguard Worker assert_eq!(read_monster.idx("mana").bitwidth(), BitWidth::W16);
130*890232f2SAndroid Build Coastguard Worker
131*890232f2SAndroid Build Coastguard Worker // Use get_T functions if you want to ensure the flexbuffer type matches what you expect.
132*890232f2SAndroid Build Coastguard Worker assert_eq!(read_hp.get_i64(), Ok(80));
133*890232f2SAndroid Build Coastguard Worker assert!(read_hp.get_u64().is_err());
134*890232f2SAndroid Build Coastguard Worker assert!(read_hp.get_vector().is_err());
135*890232f2SAndroid Build Coastguard Worker
136*890232f2SAndroid Build Coastguard Worker // Analogously, the `index` method is the safe version of `idx`.
137*890232f2SAndroid Build Coastguard Worker assert!(read_monster.index("hp").is_ok());
138*890232f2SAndroid Build Coastguard Worker assert_eq!(
139*890232f2SAndroid Build Coastguard Worker read_monster.index("foo").unwrap_err(),
140*890232f2SAndroid Build Coastguard Worker ReaderError::KeyNotFound
141*890232f2SAndroid Build Coastguard Worker );
142*890232f2SAndroid Build Coastguard Worker
143*890232f2SAndroid Build Coastguard Worker // Maps can also be indexed by usize. They're stored by key so `coins` are the first element.
144*890232f2SAndroid Build Coastguard Worker let monster_coins = read_monster.idx(0);
145*890232f2SAndroid Build Coastguard Worker // Maps and Vectors can be iterated over.
146*890232f2SAndroid Build Coastguard Worker assert!(monster_coins
147*890232f2SAndroid Build Coastguard Worker .as_vector()
148*890232f2SAndroid Build Coastguard Worker .iter()
149*890232f2SAndroid Build Coastguard Worker .map(|r| r.as_u8())
150*890232f2SAndroid Build Coastguard Worker .eq(vec![5, 10, 25, 25, 25, 100].into_iter()));
151*890232f2SAndroid Build Coastguard Worker
152*890232f2SAndroid Build Coastguard Worker // Build the answer to life the universe and everything. Reusing a builder resets it. The
153*890232f2SAndroid Build Coastguard Worker // reused internals won't need to reallocate leading to a potential 2x speedup.
154*890232f2SAndroid Build Coastguard Worker builder.build_singleton(42);
155*890232f2SAndroid Build Coastguard Worker
156*890232f2SAndroid Build Coastguard Worker // The monster is now no more.
157*890232f2SAndroid Build Coastguard Worker assert_eq!(builder.view().len(), 3); // Bytes.
158*890232f2SAndroid Build Coastguard Worker
159*890232f2SAndroid Build Coastguard Worker let the_answer = Reader::get_root(builder.view()).unwrap();
160*890232f2SAndroid Build Coastguard Worker assert_eq!(the_answer.as_i32(), 42);
161*890232f2SAndroid Build Coastguard Worker }
162*890232f2SAndroid Build Coastguard Worker
163*890232f2SAndroid Build Coastguard Worker #[test]
test_main()164*890232f2SAndroid Build Coastguard Worker fn test_main() {
165*890232f2SAndroid Build Coastguard Worker main()
166*890232f2SAndroid Build Coastguard Worker }
167