summaryrefslogtreecommitdiff
path: root/samples/sample_flexbuffers.rs
blob: efe02c3426fa33ef1723e6d1174b1e42b08d6bd4 (plain)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// 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"}]
// }
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<u8> 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::<i8>().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()
}