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