// 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())); // For very speed sensitive applications, you can directly read the slice if all of the // following are true: // // * The provided data buffer contains a valid flexbuffer. // * You correctly specify the flexbuffer type and width. // * The host machine is little endian. // * The provided data buffer itself is aligned in memory to 8 bytes. // // Vec has alignment 1 so special care is needed to get your buffer's alignment to 8. #[cfg(target_endian = "little")] { if monster_coins.is_aligned() { assert_eq!( monster_coins.get_slice::().unwrap(), &[5, 10, 25, 25, 25, 100] ); } } // 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() }