Flatbuffer multiple roots doesn't work as expected

286 Views Asked by At

I'm evaluating/learning flatbuffers and I've written a schema and some basic code. The schema contains two root tables but when I try to convert a wrong root it doesn't fail. Is this expected behavior?

schema.fbs:

table Weapon {
  name:string;
  damage:short;
  two_handed:bool;
}
root_type Weapon;

table Shield {
  name:string;
  damage:short;
}
root_type Shield;

main.rs:

use flatbuffers;

// import the generated code
#[allow(dead_code, unused_imports)]
#[path = "./schema_generated.rs"]
mod schema;


fn main() {
    let mut sword_builder = flatbuffers::FlatBufferBuilder::new();
    let sword_name = sword_builder.create_string("Sword");
    let sword = schema::Weapon::create(
        &mut sword_builder,
        &schema::WeaponArgs {
            name: Some(sword_name),
            damage: 10,
            two_handed: false,
        },
    );
    sword_builder.finish(sword, None);
    let sword_buffer = sword_builder.finished_data();


    let mut shield_builder = flatbuffers::FlatBufferBuilder::new();
    let shield_name = shield_builder.create_string("Shield");
    let shield = schema::Weapon::create(
        &mut shield_builder,
        &schema::WeaponArgs {
            name: Some(shield_name),
            damage: 2,
            two_handed: true,
        },
    );
    shield_builder.finish(shield, None);
    let shield_buffer = shield_builder.finished_data();


    // Lets decode our buffers
    let sword_decoded = flatbuffers::root::<schema::Weapon>(&sword_buffer).unwrap();
    println!("{:#?}", sword_decoded);

    let shield_decoded = flatbuffers::root::<schema::Shield>(&shield_buffer).unwrap();
    println!("{:#?}", shield_decoded);

    // This should fail:
    let sword_decoded_failure = flatbuffers::root::<schema::Weapon>(&shield_buffer).unwrap();
    println!("{:#?}", sword_decoded_failure);
}

output:

Weapon {
    name: Some(
        "Sword",
    ),
    damage: 10,
    two_handed: false,
}
Shield {
    name: Some(
        "Shield",
    ),
    damage: 2,
}
Weapon {
    name: Some(
        "Shield",
    ),
    damage: 2,
    two_handed: true,
}

github link: https://github.com/ic3man5/fb_test

Documentation for root:

Gets the root of the Flatbuffer, verifying it first with default options. Note that verification is an experimental feature and may not be maximally performant or catch every error (though that is the goal). See the _unchecked variants for previous behavior.

I would expect it to be able to catch a basic error like this? If so I can see two work arounds, one to prepend a header in front of the bytes to identify the table or using a flatbuffer union (I don't want to do this).

1

There are 1 best solutions below

0
Moop On

Flatbuffers only allows one root type per schema and one instance of it per buffer. So your schema needs to change to reflect this.

As for there being no error, a verifier takes a binary buffer of bytes and checks that it can be safely accessed according to the current schema. There is no type information in the binary bytes, so if the bytes happen to be safely accessible by another schema, it may succeed. To force it to not succeed, you could add a file_identifier to your schema, which if the Rust verifier checks it, would cause it to fail for the wrong schema.