diff --git a/.gitignore b/.gitignore
index b45308aba..15db59094 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
*.keystore
**/.vs/**
**/bin/**
+!tests/rust_usage_test/bin/**
**/gen/**
**/libs/**
**/obj/**
@@ -56,6 +57,7 @@ tests/monsterdata_java_wire_sp.mon
tests/monsterdata_go_wire.mon
tests/monsterdata_javascript_wire.mon
tests/monsterdata_lobster_wire.mon
+tests/monsterdata_rust_wire.mon
tests/unicode_test.mon
tests/ts/
tests/php/
diff --git a/BUILD b/BUILD
index ba3ca2eb2..1a3ff58b4 100644
--- a/BUILD
+++ b/BUILD
@@ -92,6 +92,7 @@ cc_binary(
"src/idl_gen_lobster.cpp",
"src/idl_gen_php.cpp",
"src/idl_gen_python.cpp",
+ "src/idl_gen_rust.cpp",
"src/idl_gen_text.cpp",
],
includes = [
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dab15cafd..122009b61 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,6 +54,7 @@ set(FlatBuffers_Compiler_SRCS
src/idl_gen_python.cpp
src/idl_gen_lobster.cpp
src/idl_gen_lua.cpp
+ src/idl_gen_rust.cpp
src/idl_gen_fbs.cpp
src/idl_gen_grpc.cpp
src/idl_gen_json_schema.cpp
diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md
index 60494049c..586087ad7 100644
--- a/docs/source/Compiler.md
+++ b/docs/source/Compiler.md
@@ -43,6 +43,8 @@ For any schema input files, one or more generators can be specified:
- `--lobster`: Generate Lobster code.
+- `--rust`, `-r` : Generate Rust code.
+
For any data input files:
- `--binary`, `-b` : If data is contained in this file, generate a
diff --git a/docs/source/FlatBuffers.md b/docs/source/FlatBuffers.md
index 98042214d..7cc93b92c 100644
--- a/docs/source/FlatBuffers.md
+++ b/docs/source/FlatBuffers.md
@@ -4,7 +4,7 @@ FlatBuffers {#flatbuffers_index}
# Overview {#flatbuffers_overview}
[FlatBuffers](@ref flatbuffers_overview) is an efficient cross platform
-serialization library for C++, C#, C, Go, Java, JavaScript, Lobster, Lua, TypeScript, PHP, and Python.
+serialization library for C++, C#, C, Go, Java, JavaScript, Lobster, Lua, TypeScript, PHP, Python, and Rust.
It was originally created at Google for game development and other
performance-critical applications.
@@ -144,6 +144,8 @@ sections provide a more in-depth usage guide.
own programs.
- How to [use the generated Lobster code](@ref flatbuffers_guide_use_lobster) in your
own programs.
+- How to [use the generated Rust code](@ref flatbuffers_guide_use_rust) in your
+ own programs.
- [Support matrix](@ref flatbuffers_support) for platforms/languages/features.
- Some [benchmarks](@ref flatbuffers_benchmarks) showing the advantage of
using FlatBuffers.
diff --git a/docs/source/RustUsage.md b/docs/source/RustUsage.md
new file mode 100644
index 000000000..f4201086f
--- /dev/null
+++ b/docs/source/RustUsage.md
@@ -0,0 +1,166 @@
+Use in Rust {#flatbuffers_guide_use_rust}
+==========
+
+## Before you get started
+
+Before diving into the FlatBuffers usage in Rust, it should be noted that
+the [Tutorial](@ref flatbuffers_guide_tutorial) page has a complete guide
+to general FlatBuffers usage in all of the supported languages (including Rust).
+This page is designed to cover the nuances of FlatBuffers usage, specific to
+Rust.
+
+#### Prerequisites
+
+This page assumes you have written a FlatBuffers schema and compiled it
+with the Schema Compiler. If you have not, please see
+[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler)
+and [Writing a schema](@ref flatbuffers_guide_writing_schema).
+
+Assuming you wrote a schema, say `mygame.fbs` (though the extension doesn't
+matter), you've generated a Rust file called `mygame_generated.rs` using the
+compiler (e.g. `flatc --rust mygame.fbs`), you can now start using this in
+your program by including the file. As noted, this header relies on the crate
+`flatbuffers`, which should be in your include `Cargo.toml`.
+
+## FlatBuffers Rust library code location
+
+The code for the FlatBuffers Rust library can be found at
+`flatbuffers/rust`. You can browse the library code on the
+[FlatBuffers GitHub page](https://github.com/google/flatbuffers/tree/master/rust).
+
+## Testing the FlatBuffers Rust library
+
+The code to test the Rust library can be found at `flatbuffers/tests/rust_usage_test`.
+The test code itself is located in
+[integration_test.rs](https://github.com/google/flatbuffers/blob/master/tests/rust_usage_test/tests/integration_test.rs)
+
+This test file requires `flatc` to be present. To review how to build the project,
+please read the [Building](@ref flatbuffers_guide_building) documenation.
+
+To run the tests, execute `RustTest.sh` from the `flatbuffers/tests` directory.
+For example, on [Linux](https://en.wikipedia.org/wiki/Linux), you would simply
+run: `cd tests && ./RustTest.sh`.
+
+*Note: The shell script requires [Rust](https://www.rust-lang.org) to
+be installed.*
+
+## Using the FlatBuffers Rust library
+
+*Note: See [Tutorial](@ref flatbuffers_guide_tutorial) for a more in-depth
+example of how to use FlatBuffers in Rust.*
+
+FlatBuffers supports both reading and writing FlatBuffers in Rust.
+
+To use FlatBuffers in your code, first generate the Rust modules from your
+schema with the `--rust` option to `flatc`. Then you can import both FlatBuffers
+and the generated code to read or write FlatBuffers.
+
+For example, here is how you would read a FlatBuffer binary file in Rust:
+First, include the library and generated code. Then read the file into
+a `u8` vector, which you pass, as a byte slice, to `get_root_as_monster()`.
+
+This full example program is available in the Rust test suite:
+[monster_example.rs](https://github.com/google/flatbuffers/blob/master/tests/rust_usage_test/bin/monster_example.rs)
+
+It can be run by `cd`ing to the `rust_usage_test` directory and executing: `cargo run monster_example`.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.rs}
+ extern crate flatbuffers;
+
+ #[path = "../../monster_test_generated.rs"]
+ mod monster_test_generated;
+ pub use monster_test_generated::my_game;
+
+ use std::io::Read;
+
+ fn main() {
+ let mut f = std::fs::File::open("../monsterdata_test.mon").unwrap();
+ let mut buf = Vec::new();
+ f.read_to_end(&mut buf).expect("file reading failed");
+
+ let monster = my_game::example::get_root_as_monster(&buf[..]);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`monster` is of type `Monster`, and points to somewhere *inside* your
+buffer (root object pointers are not the same as `buffer_pointer` !).
+If you look in your generated header, you'll see it has
+convenient accessors for all fields, e.g. `hp()`, `mana()`, etc:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.rs}
+ println!("{}", monster.hp()); // `80`
+ println!("{}", monster.mana()); // default value of `150`
+ println!("{:?}", monster.name()); // Some("MyMonster")
+ }
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+*Note: That we never stored a `mana` value, so it will return the default.*
+
+## Direct memory access
+
+As you can see from the above examples, all elements in a buffer are
+accessed through generated accessors. This is because everything is
+stored in little endian format on all platforms (the accessor
+performs a swap operation on big endian machines), and also because
+the layout of things is generally not known to the user.
+
+For structs, layout is deterministic and guaranteed to be the same
+across platforms (scalars are aligned to their
+own size, and structs themselves to their largest member), and you
+are allowed to access this memory directly by using `safe_slice` and
+on the reference to a struct, or even an array of structs.
+
+To compute offsets to sub-elements of a struct, make sure they
+are structs themselves, as then you can use the pointers to
+figure out the offset without having to hardcode it. This is
+handy for use of arrays of structs with calls like `glVertexAttribPointer`
+in OpenGL or similar APIs.
+
+It is important to note is that structs are still little endian on all
+machines, so only use tricks like this if you can guarantee you're not
+shipping on a big endian machine (using an `#[cfg(target_endian = "little")]`
+attribute would be wise).
+
+The special function `safe_slice` is implemented on Vector objects that are
+represented in memory the same way as they are represented on the wire. This
+function is always available on vectors of struct, bool, u8, and i8. It is
+conditionally-compiled on little-endian systems for all the remaining scalar
+types.
+
+The FlatBufferBuilder function `create_vector_direct` is implemented for all
+types that are endian-safe to write with a `memcpy`. It is the write-equivalent
+of `safe_slice`.
+
+## Access of untrusted buffers
+
+The generated accessor functions access fields over offsets, which is
+very quick. These offsets are used to index into Rust slices, so they are
+bounds-checked by the Rust runtime. However, our Rust implementation may
+change: we may convert access functions to use direct pointer dereferencing, to
+improve lookup speed. As a result, users should not rely on the aforementioned
+bounds-checking behavior.
+
+When you're processing large amounts of data from a source you know (e.g.
+your own generated data on disk), this is acceptable, but when reading
+data from the network that can potentially have been modified by an
+attacker, this is undesirable.
+
+The C++ port provides a buffer verifier. At this time, Rust does not. Rust may
+provide a verifier in a future version. In the meantime, Rust users can access
+the buffer verifier generated by the C++ port through a foreign function
+interface (FFI).
+
+## Threading
+
+Reading a FlatBuffer does not touch any memory outside the original buffer,
+and is entirely read-only (all immutable), so is safe to access from multiple
+threads even without synchronisation primitives.
+
+Creating a FlatBuffer is not thread safe. All state related to building
+a FlatBuffer is contained in a FlatBufferBuilder instance, and no memory
+outside of it is touched. To make this thread safe, either do not
+share instances of FlatBufferBuilder between threads (recommended), or
+manually wrap it in synchronisation primitives. There's no automatic way to
+accomplish this, by design, as we feel multithreaded construction
+of a single buffer will be rare, and synchronisation overhead would be costly.
+
+
diff --git a/docs/source/Support.md b/docs/source/Support.md
index e4c66cd60..c8ac7f7e8 100644
--- a/docs/source/Support.md
+++ b/docs/source/Support.md
@@ -18,23 +18,23 @@ In general:
NOTE: this table is a start, it needs to be extended.
-Feature | C++ | Java | C# | Go | Python | JS | TS | C | PHP | Dart | Lobster
------------------------------- | ------ | ------ | ------ | ------ | ------ | --------- | --------- | ------ | --- | ------- | -------
-Codegen for all basic features | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | WiP | Yes | Yes
-JSON parsing | Yes | No | No | No | No | No | No | Yes | No | No | Yes
-Simple mutation | Yes | Yes | Yes | Yes | No | No | No | No | No | No | No
-Reflection | Yes | No | No | No | No | No | No | Basic | No | No | No
-Buffer verifier | Yes | No | No | No | No | No | No | Yes | No | No | No
-Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | Yes | Yes
-Testing: fuzz | Yes | No | No | Yes | Yes | No | No | No | ? | No | No
-Performance: | Superb | Great | Great | Great | Ok | ? | ? | Superb | ? | ? | Great
-Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | Yes | VS2010 | ? | Yes | Yes
-Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | Yes | ? | Yes | Yes
-Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | Yes | ? | Yes | Yes
-Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes
-Platform: iOS | ? | ? | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes
-Engine: Unity | ? | ? | Yes | ? | ? | ? | ? | ? | ? | ? | No
-Primary authors (github) | aard* | aard* | ev*/js*| rw | rw | evanw/ev* | kr* | mik* | ch* | dnfield | aard*
+Feature | C++ | Java | C# | Go | Python | JS | TS | C | PHP | Dart | Lobster | Rust
+------------------------------ | ------ | ------ | ------ | ------ | ------ | --------- | --------- | ------ | --- | ------- | ------- | ----
+Codegen for all basic features | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | WiP | Yes | Yes | Yes
+JSON parsing | Yes | No | No | No | No | No | No | Yes | No | No | Yes | No
+Simple mutation | Yes | Yes | Yes | Yes | No | No | No | No | No | No | No | No
+Reflection | Yes | No | No | No | No | No | No | Basic | No | No | No | No
+Buffer verifier | Yes | No | No | No | No | No | No | Yes | No | No | No | No
+Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | Yes | Yes | Yes
+Testing: fuzz | Yes | No | No | Yes | Yes | No | No | No | ? | No | No | Yes
+Performance: | Superb | Great | Great | Great | Ok | ? | ? | Superb | ? | ? | Great | Superb
+Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | Yes | VS2010 | ? | Yes | Yes | Yes
+Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | Yes | ? | Yes | Yes | Yes
+Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | Yes | ? | Yes | Yes | Yes
+Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes | ?
+Platform: iOS | ? | ? | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes | ?
+Engine: Unity | ? | ? | Yes | ? | ? | ? | ? | ? | ? | ? | No | ?
+Primary authors (github) | aard* | aard* | ev*/js*| rw | rw | evanw/ev* | kr* | mik* | ch* | dnfield | aard* | rw
* aard = aardappel (previously: gwvo)
* ev = evolutional
diff --git a/docs/source/Tutorial.md b/docs/source/Tutorial.md
index 8cb1ab173..f2eaa2ea7 100644
--- a/docs/source/Tutorial.md
+++ b/docs/source/Tutorial.md
@@ -33,6 +33,7 @@ Please select your desired language for our quest:
Dart
Lua
Lobster
+ Rust
\endhtmlonly
@@ -144,6 +145,9 @@ For your chosen language, please cross-reference with:
## Writing the Monsters' FlatBuffer Schema
@@ -343,6 +347,12 @@ Please be aware of the difference between `flatc` and `flatcc` tools.
./../flatc --lobster monster.fbs
~~~
+
+~~~{.sh}
+ cd flatbuffers/sample
+ ./../flatc --rust monster.fbs
+~~~
+
For a more complete guide to using the `flatc` compiler, please read the
[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler)
@@ -479,6 +489,21 @@ The first step is to import/include the library, generated files, etc.
include "monster_generated.lobster"
~~~
+
Now we are ready to start building some buffers. In order to start, we need
to create an instance of the `FlatBufferBuilder`, which will contain the buffer
@@ -570,6 +595,13 @@ which will grow automatically if needed:
let builder = flatbuffers_builder {}
~~~
+
+~~~{.rs}
+ // Build up a serialized buffer algorithmically.
+ // Initialize it with a capacity of 1024 bytes.
+ let mut builder = flatbuffers::FlatBufferBuilder::new_with_capacity(1024);
+~~~
+
After creating the `builder`, we can start serializing our data. Before we make
our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
@@ -788,6 +820,24 @@ our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
builder.MyGame_Sample_WeaponEnd()
~~~
+
+~~~{.rs}
+ // Serialize some weapons for the Monster: A 'sword' and an 'axe'.
+ let weapon_one_name = builder.create_string("Sword");
+ let weapon_two_name = builder.create_string("Axe");
+
+ // Use the `Weapon::create` shortcut to create Weapons with named field
+ // arguments.
+ let sword = Weapon::create(&mut builder, &WeaponArgs{
+ name: Some(weapon_one_name),
+ damage: 3,
+ });
+ let axe = Weapon::create(&mut builder, &WeaponArgs{
+ name: Some(weapon_two_name),
+ damage: 5,
+ });
+~~~
+
Now let's create our monster, the `orc`. For this `orc`, lets make him
`red` with rage, positioned at `(1.0, 2.0, 3.0)`, and give him
@@ -959,6 +1009,15 @@ traversal. This is generally easy to do on any tree structures.
let inv = builder.MyGame_Sample_MonsterCreateInventoryVector(map(10): _)
~~~
+
+~~~{.rs}
+ // Name of the Monster.
+ let name = builder.create_string("Orc");
+
+ // Inventory.
+ let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+~~~
+
We serialized two built-in data types (`string` and `vector`) and captured
their return values. These values are offsets into the serialized data,
@@ -1086,8 +1145,14 @@ offsets.
let weapons = builder.MyGame_Sample_MonsterCreateWeaponsVector(weapon_offsets)
~~~
+
+~~~{.rs}
+ // Create a FlatBuffer `vector` that contains offsets to the sword and axe
+ // we created above.
+ let weapons = builder.create_vector(&[sword, axe]);
+~~~
+
-
Note there's additional convenience overloads of `CreateVector`, allowing you
to work with data that's not in a `std::vector`, or allowing you to generate
@@ -1203,6 +1268,18 @@ for the `path` field above:
let path = builder.EndVector(2)
~~~
+
+~~~{.rs}
+ // Create the path vector of Vec3 objects.
+ let x = Vec3::new(1.0, 2.0, 3.0);
+ let y = Vec3::new(4.0, 5.0, 6.0);
+ let path = builder.create_vector(&[x, y]);
+
+ // Note that, for convenience, it is also valid to create a vector of
+ // references to structs, like this:
+ // let path = builder.create_vector(&[&x, &y]);
+~~~
+
We have now serialized the non-scalar components of the orc, so we
can serialize the monster itself:
@@ -1438,6 +1515,27 @@ can serialize the monster itself:
let orc = builder.MyGame_Sample_MonsterEnd()
~~~
+
+~~~{.rs}
+ // Create the monster using the `Monster::create` helper function. This
+ // function accepts a `MonsterArgs` struct, which supplies all of the data
+ // needed to build a `Monster`. To supply empty/default fields, just use the
+ // Rust built-in `Default::default()` function, as demononstrated below.
+ let orc = Monster::create(&mut builder, &MonsterArgs{
+ pos: Some(&Vec3::new(1.0f32, 2.0f32, 3.0f32)),
+ mana: 150,
+ hp: 80,
+ name: Some(name),
+ inventory: Some(inventory),
+ color: Color::Red,
+ weapons: Some(weapons),
+ equipped_type: Equipment::Weapon,
+ equipped: Some(axe.as_union_value()),
+ path: Some(path),
+ ..Default::default()
+ });
+~~~
+
Note how we create `Vec3` struct in-line in the table. Unlike tables, structs
are simple combinations of scalars that are always stored inline, just like
@@ -1592,6 +1690,14 @@ Here is a repetition these lines, to help highlight them more clearly:
builder.MyGame_Sample_MonsterAddEquipped(axe)
~~~
+
+ ~~~{.rs}
+ // You need to call `as_union_value` to turn an object into a type that
+ // can be used as a union value.
+ monster_builder.add_equipped_type(Equipment::Weapon); // Union type
+ monster_builder.add_equipped(axe.as_union_value()); // Union data
+ ~~~
+
After you have created your buffer, you will have the offset to the root of the
data in the `orc` variable, so you can finish the buffer by calling the
@@ -1675,6 +1781,12 @@ appropriate `finish` method.
builder.Finish(orc)
~~~
+
+~~~{.rs}
+ // Call `finish()` to instruct the builder that this monster is complete.
+ builder.finish(orc, None);
+~~~
+
The buffer is now ready to be stored somewhere, sent over the network, be
compressed, or whatever you'd like to do with it. You can access the buffer
@@ -1784,6 +1896,13 @@ like so:
let buf = builder.SizedCopy() // Of type `string`.
~~~
+
+~~~{.rs}
+ // This must be called after `finish()`.
+ // `finished_data` returns a byte slice.
+ let buf = builder.finished_data(); // Of type `&[u8]`
+~~~
+
Now you can write the bytes to a file, send them over the network..
@@ -1917,6 +2036,21 @@ import './monster_my_game.sample_generated.dart' as myGame;
include "monster_generated.lobster"
~~~
+
Then, assuming you have a buffer of bytes received from disk,
network, etc., you can create start accessing the buffer like so:
@@ -2044,6 +2178,14 @@ myGame.Monster monster = new myGame.Monster(data);
let monster = MyGame_Sample_GetRootAsMonster(buf)
~~~
+
+~~~{.rs}
+ let buf = /* the data you just read, in a &[u8] */
+
+ // Get an accessor to the root object inside the buffer.
+ let monster = get_root_as_monster(buf);
+~~~
+
If you look in the generated files from the schema compiler, you will see it generated
accessors for all non-`deprecated` fields. For example:
@@ -2136,6 +2278,14 @@ accessors for all non-`deprecated` fields. For example:
let name = monster.name
~~~
+
+~~~{.rs}
+ // Get and test some scalar types from the FlatBuffer.
+ let hp = monster.hp();
+ let mana = monster.mana();
+ let name = monster.name();
+~~~
+
These should hold `300`, `150`, and `"Orc"` respectively.
@@ -2245,6 +2395,14 @@ To access sub-objects, in the case of our `pos`, which is a `Vec3`:
let z = pos.z
~~~
+
+~~~{.rs}
+ let pos = monster.pos().unwrap();
+ let x = pos.x();
+ let y = pos.y();
+ let z = pos.z();
+~~~
+
`x`, `y`, and `z` will contain `1.0`, `2.0`, and `3.0`, respectively.
@@ -2329,6 +2487,16 @@ FlatBuffers `vector`.
let third_item = monster.inventory(2)
~~~
+
+~~~{.rs}
+ // Get a test an element from the `inventory` FlatBuffer's `vector`.
+ let inv = monster.inventory().unwrap();
+
+ // Note that this vector is returned as a slice, because direct access for
+ // this type, a `u8` vector, is safe on all platforms:
+ let third_item = inv[2];
+~~~
+
For `vector`s of `table`s, you can access the elements like any other vector,
except your need to handle the result as a FlatBuffer `table`:
@@ -2424,6 +2592,17 @@ except your need to handle the result as a FlatBuffer `table`:
let second_weapon_damage = monster.weapons(1).damage
~~~
+
+~~~{.rs}
+ // Get and test the `weapons` FlatBuffers's `vector`.
+ let weps = monster.weapons().unwrap();
+ let weps_len = weps.len();
+
+ let wep2 = weps.get(1);
+ let second_weapon_name = wep2.name();
+ let second_weapon_damage = wep2.damage();
+~~~
+
Last, we can access our `Equipped` FlatBuffer `union`. Just like when we created
the `union`, we need to get both parts of the `union`: the type and the data.
@@ -2585,6 +2764,18 @@ We can access the type to dynamically cast the data as needed (since the
let weapon_damage = union_weapon.damage // 5
~~~
+
+~~~{.rs}
+ // Get and test the `Equipment` union (`equipped` field).
+ // `equipped_as_weapon` returns a FlatBuffer handle much like normal table
+ // fields, but this will return `None` is the union is not actually of that
+ // type.
+ if monster.equipped_type() == Equipment::Weapon {
+ let equipped = monster.equipped_as_weapon().unwrap();
+ let weapon_name = equipped.name();
+ let weapon_damage = equipped.damage();
+~~~
+
## Mutating FlatBuffers
@@ -2675,6 +2866,11 @@ mutators like so:
~~~
+
+~~~{.rs}
+
+~~~
+
We use the somewhat verbose term `mutate` instead of `set` to indicate that this
is a special use case, not to be confused with the default way of constructing
@@ -2798,5 +2994,8 @@ For your chosen language, see:
[Use in Lobster](@ref flatbuffers_guide_use_lobster)
+
+[Use in Rust](@ref flatbuffers_guide_use_rust)
+
diff --git a/docs/source/doxyfile b/docs/source/doxyfile
index 19a2ec943..6ba3c108c 100644
--- a/docs/source/doxyfile
+++ b/docs/source/doxyfile
@@ -760,6 +760,7 @@ INPUT = "FlatBuffers.md" \
"PythonUsage.md" \
"LuaUsage.md" \
"LobsterUsage.md" \
+ "RustUsage.md" \
"Support.md" \
"Benchmarks.md" \
"WhitePaper.md" \
@@ -778,6 +779,7 @@ INPUT = "FlatBuffers.md" \
"../../net/FlatBuffers/FlatBufferBuilder.cs" \
"../../include/flatbuffers/flatbuffers.h" \
"../../go/builder.go"
+ "../../rust/flatbuffers/src/builder.rs"
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h
index 54ebf764c..75e34c52d 100644
--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
@@ -41,24 +41,24 @@ namespace flatbuffers {
// of type tokens.
// clang-format off
#define FLATBUFFERS_GEN_TYPES_SCALAR(TD) \
- TD(NONE, "", uint8_t, byte, byte, byte, uint8) \
- TD(UTYPE, "", uint8_t, byte, byte, byte, uint8) /* begin scalar/int */ \
- TD(BOOL, "bool", uint8_t, boolean,byte, bool, bool) \
- TD(CHAR, "byte", int8_t, byte, int8, sbyte, int8) \
- TD(UCHAR, "ubyte", uint8_t, byte, byte, byte, uint8) \
- TD(SHORT, "short", int16_t, short, int16, short, int16) \
- TD(USHORT, "ushort", uint16_t, short, uint16, ushort, uint16) \
- TD(INT, "int", int32_t, int, int32, int, int32) \
- TD(UINT, "uint", uint32_t, int, uint32, uint, uint32) \
- TD(LONG, "long", int64_t, long, int64, long, int64) \
- TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64) /* end int */ \
- TD(FLOAT, "float", float, float, float32, float, float32) /* begin float */ \
- TD(DOUBLE, "double", double, double, float64, double, float64) /* end float/scalar */
+ TD(NONE, "", uint8_t, byte, byte, byte, uint8, u8) \
+ TD(UTYPE, "", uint8_t, byte, byte, byte, uint8, u8) /* begin scalar/int */ \
+ TD(BOOL, "bool", uint8_t, boolean,byte, bool, bool, bool) \
+ TD(CHAR, "byte", int8_t, byte, int8, sbyte, int8, i8) \
+ TD(UCHAR, "ubyte", uint8_t, byte, byte, byte, uint8, u8) \
+ TD(SHORT, "short", int16_t, short, int16, short, int16, i16) \
+ TD(USHORT, "ushort", uint16_t, short, uint16, ushort, uint16, u16) \
+ TD(INT, "int", int32_t, int, int32, int, int32, i32) \
+ TD(UINT, "uint", uint32_t, int, uint32, uint, uint32, u32) \
+ TD(LONG, "long", int64_t, long, int64, long, int64, i64) \
+ TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64, u64) /* end int */ \
+ TD(FLOAT, "float", float, float, float32, float, float32, f32) /* begin float */ \
+ TD(DOUBLE, "double", double, double, float64, double, float64, f64) /* end float/scalar */
#define FLATBUFFERS_GEN_TYPES_POINTER(TD) \
- TD(STRING, "string", Offset, int, int, StringOffset, int) \
- TD(VECTOR, "", Offset, int, int, VectorOffset, int) \
- TD(STRUCT, "", Offset, int, int, int, int) \
- TD(UNION, "", Offset, int, int, int, int)
+ TD(STRING, "string", Offset, int, int, StringOffset, int, unused) \
+ TD(VECTOR, "", Offset, int, int, VectorOffset, int, unused) \
+ TD(STRUCT, "", Offset, int, int, int, int, unused) \
+ TD(UNION, "", Offset, int, int, int, int, unused)
// The fields are:
// - enum
@@ -68,12 +68,14 @@ namespace flatbuffers {
// - Go type.
// - C# / .Net type.
// - Python type.
+// - Rust type.
// using these macros, we can now write code dealing with types just once, e.g.
/*
switch (type) {
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE) \
case BASE_TYPE_ ## ENUM: \
// do something specific to CTYPE here
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
@@ -90,13 +92,15 @@ switch (type) {
__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
#endif
enum BaseType {
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE) \
BASE_TYPE_ ## ENUM,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
};
-#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE) \
static_assert(sizeof(CTYPE) <= sizeof(largest_scalar_t), \
"define largest_scalar_t as " #CTYPE);
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
@@ -111,6 +115,8 @@ inline bool IsFloat (BaseType t) { return t == BASE_TYPE_FLOAT ||
inline bool IsLong (BaseType t) { return t == BASE_TYPE_LONG ||
t == BASE_TYPE_ULONG; }
inline bool IsBool (BaseType t) { return t == BASE_TYPE_BOOL; }
+inline bool IsOneByte(BaseType t) { return t >= BASE_TYPE_UTYPE &&
+ t <= BASE_TYPE_UCHAR; }
// clang-format on
extern const char *const kTypeNames[];
@@ -410,6 +416,7 @@ struct IDLOptions {
kDart = 1 << 11,
kLua = 1 << 12,
kLobster = 1 << 13,
+ kRust = 1 << 14,
kMAX
};
@@ -834,6 +841,12 @@ extern bool GenerateLua(const Parser &parser,
const std::string &path,
const std::string &file_name);
+// Generate Rust files from the definitions in the Parser object.
+// See idl_gen_rust.cpp.
+extern bool GenerateRust(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name);
+
// Generate Json schema file
// See idl_gen_json_schema.cpp.
extern bool GenerateJsonSchema(const Parser &parser,
@@ -872,6 +885,12 @@ extern std::string DartMakeRule(const Parser &parser,
const std::string &path,
const std::string &file_name);
+// Generate a make rule for the generated Rust code.
+// See idl_gen_rust.cpp.
+extern std::string RustMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name);
+
// Generate a make rule for the generated Java/C#/... files.
// See idl_gen_general.cpp.
extern std::string GeneralMakeRule(const Parser &parser,
diff --git a/rust/flatbuffers/Cargo.lock b/rust/flatbuffers/Cargo.lock
new file mode 100644
index 000000000..dc2168d08
--- /dev/null
+++ b/rust/flatbuffers/Cargo.lock
@@ -0,0 +1,4 @@
+[[package]]
+name = "flatbuffers"
+version = "0.1.0"
+
diff --git a/rust/flatbuffers/Cargo.toml b/rust/flatbuffers/Cargo.toml
new file mode 100644
index 000000000..f5914e991
--- /dev/null
+++ b/rust/flatbuffers/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "flatbuffers"
+version = "0.1.0"
+authors = ["Robert Winslow ", "FlatBuffers Maintainers"]
+
+[dependencies]
+smallvec = "0.6"
diff --git a/rust/flatbuffers/src/builder.rs b/rust/flatbuffers/src/builder.rs
new file mode 100644
index 000000000..2a808ef77
--- /dev/null
+++ b/rust/flatbuffers/src/builder.rs
@@ -0,0 +1,636 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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 smallvec;
+
+use std::cmp::max;
+use std::marker::PhantomData;
+use std::ptr::write_bytes;
+use std::slice::from_raw_parts;
+
+use endian_scalar::{read_scalar, emplace_scalar};
+use primitives::*;
+use push::{Push, PushAlignment};
+use table::Table;
+use vtable::{VTable, field_index_to_field_offset};
+use vtable_writer::VTableWriter;
+use vector::{SafeSliceAccess, Vector};
+
+#[derive(Clone, Copy, Debug)]
+struct FieldLoc {
+ off: UOffsetT,
+ id: VOffsetT,
+}
+
+/// FlatBufferBuilder builds a FlatBuffer through manipulating its internal
+/// state. It has an owned `Vec` that grows as needed (up to the hardcoded
+/// limit of 2GiB, which is set by the FlatBuffers format).
+pub struct FlatBufferBuilder<'fbb> {
+ owned_buf: Vec,
+ head: usize,
+
+ field_locs: Vec,
+ written_vtable_revpos: Vec,
+
+ nested: bool,
+ finished: bool,
+
+ min_align: usize,
+
+ _phantom: PhantomData<&'fbb ()>,
+}
+
+impl<'fbb> FlatBufferBuilder<'fbb> {
+ /// Create a FlatBufferBuilder that is ready for writing.
+ pub fn new() -> Self {
+ Self::new_with_capacity(0)
+ }
+
+ /// Create a FlatBufferBuilder that is ready for writing, with a
+ /// ready-to-use capacity of the provided size.
+ ///
+ /// The maximum valid value is `FLATBUFFERS_MAX_BUFFER_SIZE`.
+ pub fn new_with_capacity(size: usize) -> Self {
+ // we need to check the size here because we create the backing buffer
+ // directly, bypassing the typical way of using grow_owned_buf:
+ assert!(size <= FLATBUFFERS_MAX_BUFFER_SIZE,
+ "cannot initialize buffer bigger than 2 gigabytes");
+
+ FlatBufferBuilder {
+ owned_buf: vec![0u8; size],
+ head: size,
+
+ field_locs: Vec::new(),
+ written_vtable_revpos: Vec::new(),
+
+ nested: false,
+ finished: false,
+
+ min_align: 0,
+
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Reset the FlatBufferBuilder internal state. Use this method after a
+ /// call to a `finish` function in order to re-use a FlatBufferBuilder.
+ ///
+ /// This function is the only way to reset the `finished` state and start
+ /// again.
+ ///
+ /// If you are using a FlatBufferBuilder repeatedly, make sure to use this
+ /// function, because it re-uses the FlatBufferBuilder's existing
+ /// heap-allocated `Vec` internal buffer. This offers significant speed
+ /// improvements as compared to creating a new FlatBufferBuilder for every
+ /// new object.
+ pub fn reset(&mut self) {
+ // memset only the part of the buffer that could be dirty:
+ {
+ let to_clear = self.owned_buf.len() - self.head;
+ let ptr = (&mut self.owned_buf[self.head..]).as_mut_ptr();
+ unsafe { write_bytes(ptr, 0, to_clear); }
+ }
+
+ self.head = self.owned_buf.len();
+ self.written_vtable_revpos.clear();
+
+ self.nested = false;
+ self.finished = false;
+
+ self.min_align = 0;
+ }
+
+ /// Destroy the FlatBufferBuilder, returning its internal byte vector
+ /// and the index into it that represents the start of valid data.
+ pub fn collapse(self) -> (Vec, usize) {
+ (self.owned_buf, self.head)
+ }
+
+ /// Push a Push'able value onto the front of the in-progress data.
+ ///
+ /// This function uses traits to provide a unified API for writing
+ /// scalars, tables, vectors, and WIPOffsets.
+ #[inline]
+ pub fn push(&mut self, x: P) -> WIPOffset {
+ let sz = P::size();
+ self.align(sz, P::alignment());
+ self.make_space(sz);
+ {
+ let (dst, rest) = (&mut self.owned_buf[self.head..]).split_at_mut(sz);
+ x.push(dst, rest);
+ }
+ WIPOffset::new(self.used_space() as UOffsetT)
+ }
+
+ /// Push a Push'able value onto the front of the in-progress data, and
+ /// store a reference to it in the in-progress vtable. If the value matches
+ /// the default, then this is a no-op.
+ #[inline]
+ pub fn push_slot(&mut self, slotoff: VOffsetT, x: X, default: X) {
+ self.assert_nested("push_slot");
+ if x == default {
+ return;
+ }
+ self.push_slot_always(slotoff, x);
+ }
+
+ /// Push a Push'able value onto the front of the in-progress data, and
+ /// store a reference to it in the in-progress vtable.
+ #[inline]
+ pub fn push_slot_always(&mut self, slotoff: VOffsetT, x: X) {
+ self.assert_nested("push_slot_always");
+ let off = self.push(x);
+ self.track_field(slotoff, off.value());
+ }
+
+ /// Retrieve the number of vtables that have been serialized into the
+ /// FlatBuffer. This is primarily used to check vtable deduplication.
+ #[inline]
+ pub fn num_written_vtables(&self) -> usize {
+ self.written_vtable_revpos.len()
+ }
+
+ /// Start a Table write.
+ ///
+ /// Asserts that the builder is not in a nested state.
+ ///
+ /// Users probably want to use `push_slot` to add values after calling this.
+ #[inline]
+ pub fn start_table(&mut self) -> WIPOffset {
+ self.assert_not_nested("start_table can not be called when a table or vector is under construction");
+ self.nested = true;
+
+ WIPOffset::new(self.used_space() as UOffsetT)
+ }
+
+ /// End a Table write.
+ ///
+ /// Asserts that the builder is in a nested state.
+ #[inline]
+ pub fn end_table(&mut self, off: WIPOffset) -> WIPOffset {
+ self.assert_nested("end_table");
+
+ let o = self.write_vtable(off);
+
+ self.nested = false;
+ self.field_locs.clear();
+
+ WIPOffset::new(o.value())
+ }
+
+ /// Start a Vector write.
+ ///
+ /// Asserts that the builder is not in a nested state.
+ ///
+ /// Most users will prefer to call `create_vector`.
+ /// Speed optimizing users who choose to create vectors manually using this
+ /// function will want to use `push` to add values.
+ #[inline]
+ pub fn start_vector(&mut self, num_items: usize) {
+ self.assert_not_nested("start_vector can not be called when a table or vector is under construction");
+ self.nested = true;
+ self.align(num_items * T::size(), T::alignment().max_of(SIZE_UOFFSET));
+ }
+
+ /// End a Vector write.
+ ///
+ /// Note that the `num_elems` parameter is the number of written items, not
+ /// the byte count.
+ ///
+ /// Asserts that the builder is in a nested state.
+ #[inline]
+ pub fn end_vector(&mut self, num_elems: usize) -> WIPOffset> {
+ self.assert_nested("end_vector");
+ self.nested = false;
+ let o = self.push::(num_elems as UOffsetT);
+ WIPOffset::new(o.value())
+ }
+
+ /// Create a utf8 string.
+ ///
+ /// The wire format represents this as a zero-terminated byte vector.
+ #[inline]
+ pub fn create_string<'a: 'b, 'b>(&'a mut self, s: &'b str) -> WIPOffset<&'fbb str> {
+ self.assert_not_nested("create_string can not be called when a table or vector is under construction");
+ WIPOffset::new(self.create_byte_string(s.as_bytes()).value())
+ }
+
+ /// Create a zero-terminated byte vector.
+ #[inline]
+ pub fn create_byte_string(&mut self, data: &[u8]) -> WIPOffset<&'fbb [u8]> {
+ self.assert_not_nested("create_byte_string can not be called when a table or vector is under construction");
+ self.align(data.len() + 1, PushAlignment::new(SIZE_UOFFSET));
+ self.push(0u8);
+ self.push_bytes_unprefixed(data);
+ self.push(data.len() as UOffsetT);
+ WIPOffset::new(self.used_space() as UOffsetT)
+ }
+
+ /// Create a vector by memcpy'ing. This is much faster than calling
+ /// `create_vector`, but the underlying type must be represented as
+ /// little-endian on the host machine. This property is encoded in the
+ /// type system through the SafeSliceAccess trait. The following types are
+ /// always safe, on any platform: bool, u8, i8, and any
+ /// FlatBuffers-generated struct.
+ #[inline]
+ pub fn create_vector_direct<'a: 'b, 'b, T: SafeSliceAccess + Push + Sized + 'b>(&'a mut self, items: &'b [T]) -> WIPOffset> {
+ self.assert_not_nested("create_vector_direct can not be called when a table or vector is under construction");
+ let elem_size = T::size();
+ self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
+
+ let bytes = {
+ let ptr = items.as_ptr() as *const T as *const u8;
+ unsafe { from_raw_parts(ptr, items.len() * elem_size) }
+ };
+ self.push_bytes_unprefixed(bytes);
+ self.push(items.len() as UOffsetT);
+
+ WIPOffset::new(self.used_space() as UOffsetT)
+ }
+
+ /// Create a vector of strings.
+ ///
+ /// Speed-sensitive users may wish to reduce memory usage by creating the
+ /// vector manually: use `create_vector`, `push`, and `end_vector`.
+ #[inline]
+ pub fn create_vector_of_strings<'a, 'b>(&'a mut self, xs: &'b [&'b str]) -> WIPOffset>> {
+ self.assert_not_nested("create_vector_of_strings can not be called when a table or vector is under construction");
+ // internally, smallvec can be a stack-allocated or heap-allocated vector.
+ // we expect it to usually be stack-allocated.
+ let mut offsets: smallvec::SmallVec<[WIPOffset<&str>; 0]> = smallvec::SmallVec::with_capacity(xs.len());
+ unsafe { offsets.set_len(xs.len()); }
+ for (i, &s) in xs.iter().enumerate().rev() {
+ let o = self.create_string(s);
+ offsets[i] = o;
+ }
+ self.create_vector(&offsets[..])
+ }
+
+ /// Create a vector of Push-able objects.
+ ///
+ /// Speed-sensitive users may wish to reduce memory usage by creating the
+ /// vector manually: use `create_vector`, `push`, and `end_vector`.
+ #[inline]
+ pub fn create_vector<'a: 'b, 'b, T: Push + Copy + 'b>(&'a mut self, items: &'b [T]) -> WIPOffset> {
+ let elem_size = T::size();
+ self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
+ for i in (0..items.len()).rev() {
+ self.push(items[i]);
+ }
+ WIPOffset::new(self.push::(items.len() as UOffsetT).value())
+ }
+
+ /// Get the byte slice for the data that has been written, regardless of
+ /// whether it has been finished.
+ #[inline]
+ pub fn unfinished_data(&self) -> &[u8] {
+ &self.owned_buf[self.head..]
+ }
+ /// Get the byte slice for the data that has been written after a call to
+ /// one of the `finish` functions.
+ #[inline]
+ pub fn finished_data(&self) -> &[u8] {
+ self.assert_finished("finished_bytes cannot be called when the buffer is not yet finished");
+ &self.owned_buf[self.head..]
+ }
+ /// Assert that a field is present in the just-finished Table.
+ ///
+ /// This is somewhat low-level and is mostly used by the generated code.
+ #[inline]
+ pub fn required(&self,
+ tab_revloc: WIPOffset,
+ slot_byte_loc: VOffsetT,
+ assert_msg_name: &'static str) {
+ let idx = self.used_space() - tab_revloc.value() as usize;
+ let tab = Table::new(&self.owned_buf[self.head..], idx);
+ let o = tab.vtable().get(slot_byte_loc) as usize;
+ assert!(o != 0, "missing required field {}", assert_msg_name);
+ }
+
+ /// Finalize the FlatBuffer by: aligning it, pushing an optional file
+ /// identifier on to it, pushing a size prefix on to it, and marking the
+ /// internal state of the FlatBufferBuilder as `finished`. Afterwards,
+ /// users can call `finished_data` to get the resulting data.
+ #[inline]
+ pub fn finish_size_prefixed(&mut self, root: WIPOffset, file_identifier: Option<&str>) {
+ self.finish_with_opts(root, file_identifier, true);
+ }
+
+ /// Finalize the FlatBuffer by: aligning it, pushing an optional file
+ /// identifier on to it, and marking the internal state of the
+ /// FlatBufferBuilder as `finished`. Afterwards, users can call
+ /// `finished_data` to get the resulting data.
+ #[inline]
+ pub fn finish(&mut self, root: WIPOffset, file_identifier: Option<&str>) {
+ self.finish_with_opts(root, file_identifier, false);
+ }
+
+ /// Finalize the FlatBuffer by: aligning it and marking the internal state
+ /// of the FlatBufferBuilder as `finished`. Afterwards, users can call
+ /// `finished_data` to get the resulting data.
+ #[inline]
+ pub fn finish_minimal(&mut self, root: WIPOffset) {
+ self.finish_with_opts(root, None, false);
+ }
+
+ #[inline]
+ fn used_space(&self) -> usize {
+ self.owned_buf.len() - self.head as usize
+ }
+
+ #[inline]
+ fn track_field(&mut self, slot_off: VOffsetT, off: UOffsetT) {
+ let fl = FieldLoc {
+ id: slot_off,
+ off: off,
+ };
+ self.field_locs.push(fl);
+ }
+
+ /// Write the VTable, if it is new.
+ fn write_vtable(&mut self, table_tail_revloc: WIPOffset) -> WIPOffset {
+ self.assert_nested("write_vtable");
+
+ // Write the vtable offset, which is the start of any Table.
+ // We fill its value later.
+ let object_revloc_to_vtable: WIPOffset =
+ WIPOffset::new(self.push::(0xF0F0F0F0 as UOffsetT).value());
+
+ // Layout of the data this function will create when a new vtable is
+ // needed.
+ // --------------------------------------------------------------------
+ // vtable starts here
+ // | x, x -- vtable len (bytes) [u16]
+ // | x, x -- object inline len (bytes) [u16]
+ // | x, x -- zero, or num bytes from start of object to field #0 [u16]
+ // | ...
+ // | x, x -- zero, or num bytes from start of object to field #n-1 [u16]
+ // vtable ends here
+ // table starts here
+ // | x, x, x, x -- offset (negative direction) to the vtable [i32]
+ // | aka "vtableoffset"
+ // | -- table inline data begins here, we don't touch it --
+ // table ends here -- aka "table_start"
+ // --------------------------------------------------------------------
+ //
+ // Layout of the data this function will create when we re-use an
+ // existing vtable.
+ //
+ // We always serialize this particular vtable, then compare it to the
+ // other vtables we know about to see if there is a duplicate. If there
+ // is, then we erase the serialized vtable we just made.
+ // We serialize it first so that we are able to do byte-by-byte
+ // comparisons with already-serialized vtables. This 1) saves
+ // bookkeeping space (we only keep revlocs to existing vtables), 2)
+ // allows us to convert to little-endian once, then do
+ // fast memcmp comparisons, and 3) by ensuring we are comparing real
+ // serialized vtables, we can be more assured that we are doing the
+ // comparisons correctly.
+ //
+ // --------------------------------------------------------------------
+ // table starts here
+ // | x, x, x, x -- offset (negative direction) to an existing vtable [i32]
+ // | aka "vtableoffset"
+ // | -- table inline data begins here, we don't touch it --
+ // table starts here: aka "table_start"
+ // --------------------------------------------------------------------
+
+ // fill the WIP vtable with zeros:
+ let vtable_byte_len = get_vtable_byte_len(&self.field_locs);
+ self.make_space(vtable_byte_len);
+
+ // compute the length of the table (not vtable!) in bytes:
+ let table_object_size = object_revloc_to_vtable.value() - table_tail_revloc.value();
+ debug_assert!(table_object_size < 0x10000); // vTable use 16bit offsets.
+
+ // Write the VTable (we may delete it afterwards, if it is a duplicate):
+ let vt_start_pos = self.head;
+ let vt_end_pos = self.head + vtable_byte_len;
+ {
+ // write the vtable header:
+ let vtfw = &mut VTableWriter::init(&mut self.owned_buf[vt_start_pos..vt_end_pos]);
+ vtfw.write_vtable_byte_length(vtable_byte_len as VOffsetT);
+ vtfw.write_object_inline_size(table_object_size as VOffsetT);
+
+ // serialize every FieldLoc to the vtable:
+ for &fl in self.field_locs.iter() {
+ let pos: VOffsetT = (object_revloc_to_vtable.value() - fl.off) as VOffsetT;
+ debug_assert_eq!(vtfw.get_field_offset(fl.id),
+ 0,
+ "tried to write a vtable field multiple times");
+ vtfw.write_field_offset(fl.id, pos);
+ }
+ }
+ let dup_vt_use = {
+ let this_vt = VTable::init(&self.owned_buf[..], self.head);
+ self.find_duplicate_stored_vtable_revloc(this_vt)
+ };
+
+ let vt_use = match dup_vt_use {
+ Some(n) => {
+ VTableWriter::init(&mut self.owned_buf[vt_start_pos..vt_end_pos]).clear();
+ self.head += vtable_byte_len;
+ n
+ }
+ None => {
+ let new_vt_use = self.used_space() as UOffsetT;
+ self.written_vtable_revpos.push(new_vt_use);
+ new_vt_use
+ }
+ };
+
+ {
+ let n = self.head + self.used_space() - object_revloc_to_vtable.value() as usize;
+ let saw = read_scalar::(&self.owned_buf[n..n + SIZE_SOFFSET]);
+ debug_assert_eq!(saw, 0xF0F0F0F0);
+ emplace_scalar::(&mut self.owned_buf[n..n + SIZE_SOFFSET],
+ vt_use as SOffsetT - object_revloc_to_vtable.value() as SOffsetT);
+ }
+
+ self.field_locs.clear();
+
+ object_revloc_to_vtable
+ }
+
+ #[inline]
+ fn find_duplicate_stored_vtable_revloc(&self, needle: VTable) -> Option {
+ for &revloc in self.written_vtable_revpos.iter().rev() {
+ let o = VTable::init(&self.owned_buf[..], self.head + self.used_space() - revloc as usize);
+ if needle == o {
+ return Some(revloc);
+ }
+ }
+ None
+ }
+
+ // Only call this when you know it is safe to double the size of the buffer.
+ #[inline]
+ fn grow_owned_buf(&mut self) {
+ let old_len = self.owned_buf.len();
+ let new_len = max(1, old_len * 2);
+
+ let starting_active_size = self.used_space();
+
+ let diff = new_len - old_len;
+ self.owned_buf.resize(new_len, 0);
+ self.head += diff;
+
+ let ending_active_size = self.used_space();
+ debug_assert_eq!(starting_active_size, ending_active_size);
+
+ if new_len == 1 {
+ return;
+ }
+
+ // calculate the midpoint, and safely copy the old end data to the new
+ // end position:
+ let middle = new_len / 2;
+ {
+ let (left, right) = &mut self.owned_buf[..].split_at_mut(middle);
+ right.copy_from_slice(left);
+ }
+ // finally, zero out the old end data.
+ {
+ let ptr = (&mut self.owned_buf[..middle]).as_mut_ptr();
+ unsafe { write_bytes(ptr, 0, middle); }
+ }
+ }
+
+ // with or without a size prefix changes how we load the data, so finish*
+ // functions are split along those lines.
+ fn finish_with_opts(&mut self,
+ root: WIPOffset,
+ file_identifier: Option<&str>,
+ size_prefixed: bool) {
+ self.assert_not_finished("buffer cannot be finished when it is already finished");
+ self.assert_not_nested("buffer cannot be finished when a table or vector is under construction");
+ self.written_vtable_revpos.clear();
+
+ let to_align = {
+ // for the root offset:
+ let a = SIZE_UOFFSET;
+ // for the size prefix:
+ let b = if size_prefixed { SIZE_UOFFSET } else { 0 };
+ // for the file identifier (a string that is not zero-terminated):
+ let c = if file_identifier.is_some() {
+ FILE_IDENTIFIER_LENGTH
+ } else {
+ 0
+ };
+ a + b + c
+ };
+
+ {
+ let ma = PushAlignment::new(self.min_align);
+ self.align(to_align, ma);
+ }
+
+ if let Some(ident) = file_identifier {
+ debug_assert_eq!(ident.len(), FILE_IDENTIFIER_LENGTH);
+ self.push_bytes_unprefixed(ident.as_bytes());
+ }
+
+ self.push(root);
+
+ if size_prefixed {
+ let sz = self.used_space() as UOffsetT;
+ self.push::(sz);
+ }
+ self.finished = true;
+ }
+
+ #[inline]
+ fn align(&mut self, len: usize, alignment: PushAlignment) {
+ self.track_min_align(alignment.value());
+ let s = self.used_space() as usize;
+ self.make_space(padding_bytes(s + len, alignment.value()));
+ }
+
+ #[inline]
+ fn track_min_align(&mut self, alignment: usize) {
+ self.min_align = max(self.min_align, alignment);
+ }
+
+ #[inline]
+ fn push_bytes_unprefixed(&mut self, x: &[u8]) -> UOffsetT {
+ let n = self.make_space(x.len());
+ &mut self.owned_buf[n..n + x.len()].copy_from_slice(x);
+
+ n as UOffsetT
+ }
+
+ #[inline]
+ fn make_space(&mut self, want: usize) -> usize {
+ self.ensure_capacity(want);
+ self.head -= want;
+ self.head
+ }
+
+ #[inline]
+ fn ensure_capacity(&mut self, want: usize) -> usize {
+ if self.unused_ready_space() >= want {
+ return want;
+ }
+ assert!(want <= FLATBUFFERS_MAX_BUFFER_SIZE,
+ "cannot grow buffer beyond 2 gigabytes");
+
+ while self.unused_ready_space() < want {
+ self.grow_owned_buf();
+ }
+ want
+ }
+ #[inline]
+ fn unused_ready_space(&self) -> usize {
+ self.head
+ }
+ #[inline]
+ fn assert_nested(&self, fn_name: &'static str) {
+ // we don't assert that self.field_locs.len() >0 because the vtable
+ // could be empty (e.g. for empty tables, or for all-default values).
+ debug_assert!(self.nested, format!("incorrect FlatBufferBuilder usage: {} must be called while in a nested state", fn_name));
+ }
+ #[inline]
+ fn assert_not_nested(&self, msg: &'static str) {
+ debug_assert!(!self.nested, msg);
+ }
+ #[inline]
+ fn assert_finished(&self, msg: &'static str) {
+ debug_assert!(self.finished, msg);
+ }
+ #[inline]
+ fn assert_not_finished(&self, msg: &'static str) {
+ debug_assert!(!self.finished, msg);
+ }
+
+}
+
+/// Compute the length of the vtable needed to represent the provided FieldLocs.
+/// If there are no FieldLocs, then provide the minimum number of bytes
+/// required: enough to write the VTable header.
+#[inline]
+fn get_vtable_byte_len(field_locs: &[FieldLoc]) -> usize {
+ let max_voffset = field_locs.iter().map(|fl| fl.id).max();
+ match max_voffset {
+ None => { field_index_to_field_offset(0) as usize }
+ Some(mv) => { mv as usize + SIZE_VOFFSET }
+ }
+}
+
+#[inline]
+fn padding_bytes(buf_size: usize, scalar_size: usize) -> usize {
+ // ((!buf_size) + 1) & (scalar_size - 1)
+ (!buf_size).wrapping_add(1) & (scalar_size.wrapping_sub(1))
+}
diff --git a/rust/flatbuffers/src/endian_scalar.rs b/rust/flatbuffers/src/endian_scalar.rs
new file mode 100644
index 000000000..00f2ebef4
--- /dev/null
+++ b/rust/flatbuffers/src/endian_scalar.rs
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use std::mem::size_of;
+
+/// Trait for values that must be stored in little-endian byte order, but
+/// might be represented in memory as big-endian. Every type that implements
+/// EndianScalar is a valid FlatBuffers scalar value.
+///
+/// The Rust stdlib does not provide a trait to represent scalars, so this trait
+/// serves that purpose, too.
+///
+/// Note that we do not use the num-traits crate for this, because it provides
+/// "too much". For example, num-traits provides i128 support, but that is an
+/// invalid FlatBuffers type.
+pub trait EndianScalar: Sized + PartialEq + Copy + Clone {
+ fn to_little_endian(self) -> Self;
+ fn from_little_endian(self) -> Self;
+}
+
+/// Macro for implementing a no-op endian conversion. This is used for types
+/// that are one byte wide.
+macro_rules! impl_endian_scalar_noop {
+ ($ty:ident) => (
+ impl EndianScalar for $ty {
+ #[inline]
+ fn to_little_endian(self) -> Self {
+ self
+ }
+ #[inline]
+ fn from_little_endian(self) -> Self {
+ self
+ }
+ }
+ )
+}
+
+/// Macro for implementing an endian conversion using the stdlib `to_le` and
+/// `from_le` functions. This is used for integer types. It is not used for
+/// floats, because the `to_le` and `from_le` are not implemented for them in
+/// the stdlib.
+macro_rules! impl_endian_scalar_stdlib_le_conversion {
+ ($ty:ident) => (
+ impl EndianScalar for $ty {
+ #[inline]
+ fn to_little_endian(self) -> Self {
+ Self::to_le(self)
+ }
+ #[inline]
+ fn from_little_endian(self) -> Self {
+ Self::from_le(self)
+ }
+ }
+ )
+}
+
+impl_endian_scalar_noop!(bool);
+impl_endian_scalar_noop!(u8);
+impl_endian_scalar_noop!(i8);
+
+impl_endian_scalar_stdlib_le_conversion!(u16);
+impl_endian_scalar_stdlib_le_conversion!(u32);
+impl_endian_scalar_stdlib_le_conversion!(u64);
+impl_endian_scalar_stdlib_le_conversion!(i16);
+impl_endian_scalar_stdlib_le_conversion!(i32);
+impl_endian_scalar_stdlib_le_conversion!(i64);
+
+impl EndianScalar for f32 {
+ /// Convert f32 from host endian-ness to little-endian.
+ #[inline]
+ fn to_little_endian(self) -> Self {
+ #[cfg(target_endian = "little")]
+ {
+ self
+ }
+ #[cfg(not(target_endian = "little"))]
+ {
+ byte_swap_f32(&self)
+ }
+ }
+ /// Convert f32 from little-endian to host endian-ness.
+ #[inline]
+ fn from_little_endian(self) -> Self {
+ #[cfg(target_endian = "little")]
+ {
+ self
+ }
+ #[cfg(not(target_endian = "little"))]
+ {
+ byte_swap_f32(&self)
+ }
+ }
+}
+
+impl EndianScalar for f64 {
+ /// Convert f64 from host endian-ness to little-endian.
+ #[inline]
+ fn to_little_endian(self) -> Self {
+ #[cfg(target_endian = "little")]
+ {
+ self
+ }
+ #[cfg(not(target_endian = "little"))]
+ {
+ byte_swap_f64(&self)
+ }
+ }
+ /// Convert f64 from little-endian to host endian-ness.
+ #[inline]
+ fn from_little_endian(self) -> Self {
+ #[cfg(target_endian = "little")]
+ {
+ self
+ }
+ #[cfg(not(target_endian = "little"))]
+ {
+ byte_swap_f64(&self)
+ }
+ }
+}
+
+/// Swaps the bytes of an f32.
+#[allow(dead_code)]
+#[inline]
+pub fn byte_swap_f32(x: f32) -> f32 {
+ f32::from_bits(x.to_bits().swap_bytes())
+}
+
+/// Swaps the bytes of an f64.
+#[allow(dead_code)]
+#[inline]
+pub fn byte_swap_f64(x: f64) -> f64 {
+ f64::from_bits(x.to_bits().swap_bytes())
+}
+
+/// Place an EndianScalar into the provided mutable byte slice. Performs
+/// endian conversion, if necessary.
+#[inline]
+pub fn emplace_scalar(s: &mut [u8], x: T) {
+ let sz = size_of::();
+ let mut_ptr = (&mut s[..sz]).as_mut_ptr() as *mut T;
+ let val = x.to_little_endian();
+ unsafe {
+ *mut_ptr = val;
+ }
+}
+
+/// Read an EndianScalar from the provided byte slice at the specified location.
+/// Performs endian conversion, if necessary.
+#[inline]
+pub fn read_scalar_at(s: &[u8], loc: usize) -> T {
+ let buf = &s[loc..loc + size_of::()];
+ read_scalar(buf)
+}
+
+/// Read an EndianScalar from the provided byte slice. Performs endian
+/// conversion, if necessary.
+#[inline]
+pub fn read_scalar(s: &[u8]) -> T {
+ let sz = size_of::();
+
+ let p = (&s[..sz]).as_ptr() as *const T;
+ let x = unsafe { *p };
+
+ x.from_little_endian()
+}
+
diff --git a/rust/flatbuffers/src/follow.rs b/rust/flatbuffers/src/follow.rs
new file mode 100644
index 000000000..4d3eff776
--- /dev/null
+++ b/rust/flatbuffers/src/follow.rs
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use std::marker::PhantomData;
+
+/// Follow is a trait that allows us to access FlatBuffers in a declarative,
+/// type safe, and fast way. They compile down to almost no code (after
+/// optimizations). Conceptually, Follow lifts the offset-based access
+/// patterns of FlatBuffers data into the type system. This trait is used
+/// pervasively at read time, to access tables, vtables, vectors, strings, and
+/// all other data. At this time, Follow is not utilized much on the write
+/// path.
+///
+/// Writing a new Follow implementation primarily involves deciding whether
+/// you want to return data (of the type Self::Inner) or do you want to
+/// continue traversing the FlatBuffer.
+pub trait Follow<'a> {
+ type Inner;
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner;
+}
+
+/// Execute a follow as a top-level function.
+#[allow(dead_code)]
+#[inline]
+pub fn lifted_follow<'a, T: Follow<'a>>(buf: &'a [u8], loc: usize) -> T::Inner {
+ T::follow(buf, loc)
+}
+
+/// FollowStart wraps a Follow impl in a struct type. This can make certain
+/// programming patterns more ergonomic.
+#[derive(Debug)]
+pub struct FollowStart(PhantomData);
+impl<'a, T: Follow<'a> + 'a> FollowStart {
+ #[inline]
+ pub fn new() -> Self {
+ Self { 0: PhantomData }
+ }
+ #[inline]
+ pub fn self_follow(&'a self, buf: &'a [u8], loc: usize) -> T::Inner {
+ T::follow(buf, loc)
+ }
+}
+impl<'a, T: Follow<'a>> Follow<'a> for FollowStart {
+ type Inner = T::Inner;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ T::follow(buf, loc)
+ }
+}
diff --git a/rust/flatbuffers/src/lib.rs b/rust/flatbuffers/src/lib.rs
new file mode 100644
index 000000000..1783b34c7
--- /dev/null
+++ b/rust/flatbuffers/src/lib.rs
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+mod builder;
+mod endian_scalar;
+mod follow;
+mod primitives;
+mod push;
+mod table;
+mod vector;
+mod vtable;
+mod vtable_writer;
+
+pub use builder::FlatBufferBuilder;
+pub use endian_scalar::{EndianScalar, emplace_scalar, read_scalar, read_scalar_at, byte_swap_f32, byte_swap_f64};
+pub use follow::{Follow, FollowStart};
+pub use primitives::*;
+pub use push::Push;
+pub use table::{Table, buffer_has_identifier, get_root, get_size_prefixed_root};
+pub use vector::{SafeSliceAccess, Vector, follow_cast_ref};
+pub use vtable::field_index_to_field_offset;
+
+// TODO(rw): Unify `create_vector` and `create_vector_direct` by using
+// `Into>`.
+// TODO(rw): Split fill ops in builder into fill_small, fill_big like in C++.
diff --git a/rust/flatbuffers/src/primitives.rs b/rust/flatbuffers/src/primitives.rs
new file mode 100644
index 000000000..59176b8f8
--- /dev/null
+++ b/rust/flatbuffers/src/primitives.rs
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use std::marker::PhantomData;
+use std::mem::size_of;
+use std::ops::Deref;
+
+use endian_scalar::{emplace_scalar, read_scalar, read_scalar_at};
+use follow::Follow;
+use push::Push;
+
+pub const FLATBUFFERS_MAX_BUFFER_SIZE: usize = (1u64 << 31) as usize;
+
+pub const FILE_IDENTIFIER_LENGTH: usize = 4;
+
+pub const VTABLE_METADATA_FIELDS: usize = 2;
+
+pub const SIZE_U8: usize = size_of::();
+pub const SIZE_I8: usize = size_of::();
+
+pub const SIZE_U16: usize = size_of::();
+pub const SIZE_I16: usize = size_of::();
+
+pub const SIZE_U32: usize = size_of::();
+pub const SIZE_I32: usize = size_of::();
+
+pub const SIZE_U64: usize = size_of::();
+pub const SIZE_I64: usize = size_of::();
+
+pub const SIZE_F32: usize = size_of::();
+pub const SIZE_F64: usize = size_of::();
+
+pub const SIZE_SOFFSET: usize = SIZE_I32;
+pub const SIZE_UOFFSET: usize = SIZE_U32;
+pub const SIZE_VOFFSET: usize = SIZE_I16;
+
+pub const SIZE_SIZEPREFIX: usize = SIZE_UOFFSET;
+
+/// SOffsetT is an i32 that is used by tables to reference their vtables.
+pub type SOffsetT = i32;
+
+/// UOffsetT is a u32 that is used by pervasively to represent both pointers
+/// and lengths of vectors.
+pub type UOffsetT = u32;
+
+/// VOffsetT is a i32 that is used by vtables to store field data.
+pub type VOffsetT = i16;
+
+/// TableFinishedWIPOffset marks a WIPOffset as being for a finished table.
+pub struct TableFinishedWIPOffset {}
+
+/// TableUnfinishedWIPOffset marks a WIPOffset as being for an unfinished table.
+pub struct TableUnfinishedWIPOffset {}
+
+/// UnionWIPOffset marks a WIPOffset as being for a union value.
+pub struct UnionWIPOffset {}
+
+/// VTableWIPOffset marks a WIPOffset as being for a vtable.
+pub struct VTableWIPOffset {}
+
+/// WIPOffset contains an UOffsetT with a special meaning: it is the location of
+/// data relative to the *end* of an in-progress FlatBuffer. The
+/// FlatBufferBuilder uses this to track the location of objects in an absolute
+/// way. The impl of Push converts a WIPOffset into a ForwardsUOffset.
+#[derive(Debug)]
+pub struct WIPOffset(UOffsetT, PhantomData);
+
+// TODO(rw): why do we need to reimplement (with a default impl) Copy to
+// avoid ownership errors?
+impl Copy for WIPOffset {}
+impl Clone for WIPOffset {
+ #[inline]
+ fn clone(&self) -> WIPOffset {
+ WIPOffset::new(self.0.clone())
+ }
+}
+impl PartialEq for WIPOffset {
+ fn eq(&self, o: &WIPOffset) -> bool {
+ self.value() == o.value()
+ }
+}
+
+impl Deref for WIPOffset {
+ type Target = UOffsetT;
+ #[inline]
+ fn deref(&self) -> &UOffsetT {
+ &self.0
+ }
+}
+impl<'a, T: 'a> WIPOffset {
+ /// Create a new WIPOffset.
+ #[inline]
+ pub fn new(o: UOffsetT) -> WIPOffset {
+ WIPOffset {
+ 0: o,
+ 1: PhantomData,
+ }
+ }
+
+ /// Return a wrapped value that brings its meaning as a union WIPOffset
+ /// into the type system.
+ #[inline(always)]
+ pub fn as_union_value(&self) -> WIPOffset {
+ WIPOffset::new(self.0)
+ }
+ /// Get the underlying value.
+ #[inline(always)]
+ pub fn value(&self) -> UOffsetT {
+ self.0
+ }
+}
+
+impl Push for WIPOffset {
+ type Output = ForwardsUOffset;
+
+ #[inline(always)]
+ fn push(&self, dst: &mut [u8], rest: &[u8]) {
+ let n = (SIZE_UOFFSET + rest.len() - self.value() as usize) as UOffsetT;
+ emplace_scalar::(dst, n);
+ }
+}
+
+impl Push for ForwardsUOffset {
+ type Output = Self;
+
+ #[inline(always)]
+ fn push(&self, dst: &mut [u8], rest: &[u8]) {
+ self.value().push(dst, rest);
+ }
+}
+
+/// ForwardsUOffset is used by Follow to traverse a FlatBuffer: the pointer
+/// is incremented by the value contained in this type.
+#[derive(Debug)]
+pub struct ForwardsUOffset(UOffsetT, PhantomData);
+impl ForwardsUOffset {
+ #[inline(always)]
+ pub fn value(&self) -> UOffsetT {
+ self.0
+ }
+}
+
+impl<'a, T: Follow<'a>> Follow<'a> for ForwardsUOffset {
+ type Inner = T::Inner;
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ let slice = &buf[loc..loc + SIZE_UOFFSET];
+ let off = read_scalar::(slice) as usize;
+ T::follow(buf, loc + off)
+ }
+}
+
+/// ForwardsVOffset is used by Follow to traverse a FlatBuffer: the pointer
+/// is incremented by the value contained in this type.
+#[derive(Debug)]
+pub struct ForwardsVOffset(VOffsetT, PhantomData);
+impl ForwardsVOffset {
+ #[inline(always)]
+ pub fn value(&self) -> VOffsetT {
+ self.0
+ }
+}
+
+impl<'a, T: Follow<'a>> Follow<'a> for ForwardsVOffset {
+ type Inner = T::Inner;
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ let slice = &buf[loc..loc + SIZE_VOFFSET];
+ let off = read_scalar::(slice) as usize;
+ T::follow(buf, loc + off)
+ }
+}
+
+impl Push for ForwardsVOffset {
+ type Output = Self;
+
+ #[inline]
+ fn push(&self, dst: &mut [u8], rest: &[u8]) {
+ self.value().push(dst, rest);
+ }
+}
+
+/// ForwardsSOffset is used by Follow to traverse a FlatBuffer: the pointer
+/// is incremented by the *negative* of the value contained in this type.
+#[derive(Debug)]
+pub struct BackwardsSOffset(SOffsetT, PhantomData);
+impl BackwardsSOffset {
+ #[inline(always)]
+ pub fn value(&self) -> SOffsetT {
+ self.0
+ }
+}
+
+impl<'a, T: Follow<'a>> Follow<'a> for BackwardsSOffset {
+ type Inner = T::Inner;
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ let slice = &buf[loc..loc + SIZE_SOFFSET];
+ let off = read_scalar::(slice);
+ T::follow(buf, (loc as SOffsetT - off) as usize)
+ }
+}
+
+impl Push for BackwardsSOffset {
+ type Output = Self;
+
+ #[inline]
+ fn push(&self, dst: &mut [u8], rest: &[u8]) {
+ self.value().push(dst, rest);
+ }
+}
+
+/// SkipSizePrefix is used by Follow to traverse a FlatBuffer: the pointer is
+/// incremented by a fixed constant in order to skip over the size prefix value.
+pub struct SkipSizePrefix(PhantomData);
+impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipSizePrefix {
+ type Inner = T::Inner;
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ T::follow(buf, loc + SIZE_SIZEPREFIX)
+ }
+}
+
+/// SkipRootOffset is used by Follow to traverse a FlatBuffer: the pointer is
+/// incremented by a fixed constant in order to skip over the root offset value.
+pub struct SkipRootOffset(PhantomData);
+impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipRootOffset {
+ type Inner = T::Inner;
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ T::follow(buf, loc + SIZE_UOFFSET)
+ }
+}
+
+/// FileIdentifier is used by Follow to traverse a FlatBuffer: the pointer is
+/// dereferenced into a byte slice, whose bytes are the file identifer value.
+pub struct FileIdentifier;
+impl<'a> Follow<'a> for FileIdentifier {
+ type Inner = &'a [u8];
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ &buf[loc..loc + FILE_IDENTIFIER_LENGTH]
+ }
+}
+
+/// SkipFileIdentifier is used by Follow to traverse a FlatBuffer: the pointer
+/// is incremented by a fixed constant in order to skip over the file
+/// identifier value.
+pub struct SkipFileIdentifier(PhantomData);
+impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipFileIdentifier {
+ type Inner = T::Inner;
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ T::follow(buf, loc + FILE_IDENTIFIER_LENGTH)
+ }
+}
+
+/// Follow trait impls for primitive types.
+///
+/// Ideally, these would be implemented as a single impl using trait bounds on
+/// EndianScalar, but implementing Follow that way causes a conflict with
+/// other impls.
+macro_rules! impl_follow_for_endian_scalar {
+ ($ty:ident) => (
+ impl<'a> Follow<'a> for $ty {
+ type Inner = $ty;
+ #[inline(always)]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ read_scalar_at::<$ty>(buf, loc)
+ }
+ }
+ )
+}
+
+impl_follow_for_endian_scalar!(bool);
+impl_follow_for_endian_scalar!(u8);
+impl_follow_for_endian_scalar!(u16);
+impl_follow_for_endian_scalar!(u32);
+impl_follow_for_endian_scalar!(u64);
+impl_follow_for_endian_scalar!(i8);
+impl_follow_for_endian_scalar!(i16);
+impl_follow_for_endian_scalar!(i32);
+impl_follow_for_endian_scalar!(i64);
+impl_follow_for_endian_scalar!(f32);
+impl_follow_for_endian_scalar!(f64);
diff --git a/rust/flatbuffers/src/push.rs b/rust/flatbuffers/src/push.rs
new file mode 100644
index 000000000..2b307a3a7
--- /dev/null
+++ b/rust/flatbuffers/src/push.rs
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use std::cmp::max;
+use std::mem::{align_of, size_of};
+
+use endian_scalar::emplace_scalar;
+
+/// Trait to abstract over functionality needed to write values (either owned
+/// or referenced). Used in FlatBufferBuilder and implemented for generated
+/// types.
+pub trait Push: Sized {
+ type Output;
+ fn push(&self, dst: &mut [u8], _rest: &[u8]);
+ #[inline]
+ fn size() -> usize {
+ size_of::()
+ }
+ #[inline]
+ fn alignment() -> PushAlignment {
+ PushAlignment::new(align_of::())
+ }
+}
+
+/// Ensure Push alignment calculations are typesafe (because this helps reduce
+/// implementation issues when using FlatBufferBuilder::align).
+pub struct PushAlignment(usize);
+impl PushAlignment {
+ #[inline]
+ pub fn new(x: usize) -> Self {
+ PushAlignment { 0: x }
+ }
+ #[inline]
+ pub fn value(&self) -> usize {
+ self.0
+ }
+ #[inline]
+ pub fn max_of(&self, o: usize) -> Self {
+ PushAlignment::new(max(self.0, o))
+ }
+}
+
+/// Macro to implement Push for EndianScalar types.
+macro_rules! impl_push_for_endian_scalar {
+ ($ty:ident) => (
+ impl Push for $ty {
+ type Output = $ty;
+
+ #[inline]
+ fn push(&self, dst: &mut [u8], _rest: &[u8]) {
+ emplace_scalar::<$ty>(dst, *self);
+ }
+
+ }
+ )
+}
+
+impl_push_for_endian_scalar!(bool);
+impl_push_for_endian_scalar!(u8);
+impl_push_for_endian_scalar!(i8);
+impl_push_for_endian_scalar!(u16);
+impl_push_for_endian_scalar!(i16);
+impl_push_for_endian_scalar!(u32);
+impl_push_for_endian_scalar!(i32);
+impl_push_for_endian_scalar!(u64);
+impl_push_for_endian_scalar!(i64);
+impl_push_for_endian_scalar!(f32);
+impl_push_for_endian_scalar!(f64);
diff --git a/rust/flatbuffers/src/table.rs b/rust/flatbuffers/src/table.rs
new file mode 100644
index 000000000..d9e952d01
--- /dev/null
+++ b/rust/flatbuffers/src/table.rs
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use follow::Follow;
+use primitives::*;
+use vtable::VTable;
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Table<'a> {
+ pub buf: &'a [u8],
+ pub loc: usize,
+}
+
+impl<'a> Table<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8], loc: usize) -> Self {
+ Table { buf: buf, loc: loc }
+ }
+ #[inline]
+ pub fn vtable(&'a self) -> VTable<'a> {
+ >>::follow(self.buf, self.loc)
+ }
+ #[inline]
+ pub fn get + 'a>(
+ &'a self,
+ slot_byte_loc: VOffsetT,
+ default: Option,
+ ) -> Option {
+ let o = self.vtable().get(slot_byte_loc) as usize;
+ if o == 0 {
+ return default;
+ }
+ Some(::follow(self.buf, self.loc + o))
+ }
+}
+
+impl<'a> Follow<'a> for Table<'a> {
+ type Inner = Table<'a>;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Table { buf: buf, loc: loc }
+ }
+}
+
+#[inline]
+pub fn get_root<'a, T: Follow<'a> + 'a>(data: &'a [u8]) -> T::Inner {
+ >::follow(data, 0)
+}
+#[inline]
+pub fn get_size_prefixed_root<'a, T: Follow<'a> + 'a>(data: &'a [u8]) -> T::Inner {
+ >>::follow(data, 0)
+}
+#[inline]
+pub fn buffer_has_identifier(data: &[u8], ident: &str, size_prefixed: bool) -> bool {
+ assert_eq!(ident.len(), FILE_IDENTIFIER_LENGTH);
+
+ let got = if size_prefixed {
+ >>::follow(data, 0)
+ } else {
+ >::follow(data, 0)
+ };
+
+ ident.as_bytes() == got
+}
diff --git a/rust/flatbuffers/src/vector.rs b/rust/flatbuffers/src/vector.rs
new file mode 100644
index 000000000..8c2d6d509
--- /dev/null
+++ b/rust/flatbuffers/src/vector.rs
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use std::marker::PhantomData;
+use std::mem::size_of;
+use std::slice::from_raw_parts;
+use std::str::from_utf8_unchecked;
+
+use endian_scalar::{EndianScalar, read_scalar};
+use follow::Follow;
+use primitives::*;
+
+#[derive(Debug)]
+pub struct Vector<'a, T: 'a>(&'a [u8], usize, PhantomData);
+
+impl<'a, T: 'a> Vector<'a, T> {
+ #[inline(always)]
+ pub fn new(buf: &'a [u8], loc: usize) -> Self {
+ Vector {
+ 0: buf,
+ 1: loc,
+ 2: PhantomData,
+ }
+ }
+
+ #[inline(always)]
+ pub fn len(&self) -> usize {
+ read_scalar::(&self.0[self.1 as usize..]) as usize
+ }
+}
+
+impl<'a, T: Follow<'a> + 'a> Vector<'a, T> {
+ #[inline(always)]
+ pub fn get(&self, idx: usize) -> T::Inner {
+ debug_assert!(idx < read_scalar::(&self.0[self.1 as usize..]) as usize);
+ let sz = size_of::();
+ debug_assert!(sz > 0);
+ T::follow(self.0, self.1 as usize + SIZE_UOFFSET + sz * idx)
+ }
+}
+
+pub trait SafeSliceAccess {}
+impl<'a, T: SafeSliceAccess + 'a> Vector<'a, T> {
+ pub fn safe_slice(self) -> &'a [T] {
+ let buf = self.0;
+ let loc = self.1;
+ let sz = size_of::();
+ debug_assert!(sz > 0);
+ let len = read_scalar::(&buf[loc..loc + SIZE_UOFFSET]) as usize;
+ let data_buf = &buf[loc + SIZE_UOFFSET..loc + SIZE_UOFFSET + len * sz];
+ let ptr = data_buf.as_ptr() as *const T;
+ let s: &'a [T] = unsafe { from_raw_parts(ptr, len) };
+ s
+ }
+}
+
+impl SafeSliceAccess for u8 {}
+impl SafeSliceAccess for i8 {}
+impl SafeSliceAccess for bool {}
+
+#[cfg(target_endian = "little")]
+mod le_safe_slice_impls {
+ impl super::SafeSliceAccess for u16 {}
+ impl super::SafeSliceAccess for u32 {}
+ impl super::SafeSliceAccess for u64 {}
+
+ impl super::SafeSliceAccess for i16 {}
+ impl super::SafeSliceAccess for i32 {}
+ impl super::SafeSliceAccess for i64 {}
+
+ impl super::SafeSliceAccess for f32 {}
+ impl super::SafeSliceAccess for f64 {}
+}
+
+pub use self::le_safe_slice_impls::*;
+
+pub fn follow_cast_ref<'a, T: Sized + 'a>(buf: &'a [u8], loc: usize) -> &'a T {
+ let sz = size_of::();
+ let buf = &buf[loc..loc + sz];
+ let ptr = buf.as_ptr() as *const T;
+ unsafe { &*ptr }
+}
+
+impl<'a> Follow<'a> for &'a str {
+ type Inner = &'a str;
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ let len = read_scalar::(&buf[loc..loc + SIZE_UOFFSET]) as usize;
+ let slice = &buf[loc + SIZE_UOFFSET..loc + SIZE_UOFFSET + len];
+ let s = unsafe { from_utf8_unchecked(slice) };
+ s
+ }
+}
+
+fn follow_slice_helper(buf: &[u8], loc: usize) -> &[T] {
+ let sz = size_of::();
+ debug_assert!(sz > 0);
+ let len = read_scalar::(&buf[loc..loc + SIZE_UOFFSET]) as usize;
+ let data_buf = &buf[loc + SIZE_UOFFSET..loc + SIZE_UOFFSET + len * sz];
+ let ptr = data_buf.as_ptr() as *const T;
+ let s: &[T] = unsafe { from_raw_parts(ptr, len) };
+ s
+}
+
+/// Implement direct slice access if the host is little-endian.
+#[cfg(target_endian = "little")]
+impl<'a, T: EndianScalar> Follow<'a> for &'a [T] {
+ type Inner = &'a [T];
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ follow_slice_helper::(buf, loc)
+ }
+}
+
+/// Implement Follow for all possible Vectors that have Follow-able elements.
+impl<'a, T: Follow<'a> + 'a> Follow<'a> for Vector<'a, T> {
+ type Inner = Vector<'a, T>;
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Vector::new(buf, loc)
+ }
+}
+
diff --git a/rust/flatbuffers/src/vtable.rs b/rust/flatbuffers/src/vtable.rs
new file mode 100644
index 000000000..cd7ede6e4
--- /dev/null
+++ b/rust/flatbuffers/src/vtable.rs
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use endian_scalar::read_scalar_at;
+use follow::Follow;
+use primitives::*;
+
+/// VTable encapsulates read-only usage of a vtable. It is only to be used
+/// by generated code.
+#[derive(Debug)]
+pub struct VTable<'a> {
+ buf: &'a [u8],
+ loc: usize,
+}
+
+impl<'a> PartialEq for VTable<'a> {
+ fn eq(&self, other: &VTable) -> bool {
+ self.as_bytes().eq(other.as_bytes())
+ }
+}
+
+impl<'a> VTable<'a> {
+ pub fn init(buf: &'a [u8], loc: usize) -> Self {
+ VTable {
+ buf: buf,
+ loc: loc,
+ }
+ }
+ pub fn num_fields(&self) -> usize {
+ (self.num_bytes() / SIZE_VOFFSET) - 2
+ }
+ pub fn num_bytes(&self) -> usize {
+ read_scalar_at::(self.buf, self.loc) as usize
+ }
+ pub fn object_inline_num_bytes(&self) -> usize {
+ let n = read_scalar_at::(self.buf, self.loc + SIZE_VOFFSET);
+ n as usize
+ }
+ pub fn get_field(&self, idx: usize) -> VOffsetT {
+ // TODO(rw): distinguish between None and 0?
+ if idx > self.num_fields() {
+ return 0;
+ }
+ read_scalar_at::(
+ self.buf,
+ self.loc + SIZE_VOFFSET + SIZE_VOFFSET + SIZE_VOFFSET * idx,
+ )
+ }
+ pub fn get(&self, byte_loc: VOffsetT) -> VOffsetT {
+ // TODO(rw): distinguish between None and 0?
+ if byte_loc as usize >= self.num_bytes() {
+ return 0;
+ }
+ read_scalar_at::(self.buf, self.loc + byte_loc as usize)
+ }
+ pub fn as_bytes(&self) -> &[u8] {
+ let len = self.num_bytes();
+ &self.buf[self.loc..self.loc + len]
+ }
+}
+
+
+#[allow(dead_code)]
+pub fn field_index_to_field_offset(field_id: VOffsetT) -> VOffsetT {
+ // Should correspond to what end_table() below builds up.
+ let fixed_fields = 2; // Vtable size and Object Size.
+ ((field_id + fixed_fields) * (SIZE_VOFFSET as VOffsetT)) as VOffsetT
+}
+
+#[allow(dead_code)]
+pub fn field_offset_to_field_index(field_o: VOffsetT) -> VOffsetT {
+ debug_assert!(field_o >= 2);
+ let fixed_fields = 2; // VTable size and Object Size.
+ (field_o / (SIZE_VOFFSET as VOffsetT)) - fixed_fields
+}
+
+impl<'a> Follow<'a> for VTable<'a> {
+ type Inner = VTable<'a>;
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ VTable::init(buf, loc)
+ }
+}
diff --git a/rust/flatbuffers/src/vtable_writer.rs b/rust/flatbuffers/src/vtable_writer.rs
new file mode 100644
index 000000000..119f794cd
--- /dev/null
+++ b/rust/flatbuffers/src/vtable_writer.rs
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+use std::ptr::write_bytes;
+
+use endian_scalar::{emplace_scalar, read_scalar};
+use primitives::*;
+
+/// VTableWriter compartmentalizes actions needed to create a vtable.
+#[derive(Debug)]
+pub struct VTableWriter<'a> {
+ buf: &'a mut [u8],
+}
+
+impl<'a> VTableWriter<'a> {
+ #[inline(always)]
+ pub fn init(buf: &'a mut [u8]) -> Self {
+ VTableWriter { buf: buf }
+ }
+
+ /// Writes the vtable length (in bytes) into the vtable.
+ ///
+ /// Note that callers already need to have computed this to initialize
+ /// a VTableWriter.
+ ///
+ /// In debug mode, asserts that the length of the underlying data is equal
+ /// to the provided value.
+ #[inline(always)]
+ pub fn write_vtable_byte_length(&mut self, n: VOffsetT) {
+ emplace_scalar::(&mut self.buf[..SIZE_VOFFSET], n);
+ debug_assert_eq!(n as usize, self.buf.len());
+ }
+
+ /// Writes an object length (in bytes) into the vtable.
+ #[inline(always)]
+ pub fn write_object_inline_size(&mut self, n: VOffsetT) {
+ emplace_scalar::(&mut self.buf[SIZE_VOFFSET..2 * SIZE_VOFFSET], n);
+ }
+
+ /// Gets an object field offset from the vtable. Only used for debugging.
+ ///
+ /// Note that this expects field offsets (which are like pointers), not
+ /// field ids (which are like array indices).
+ #[inline(always)]
+ pub fn get_field_offset(&self, vtable_offset: VOffsetT) -> VOffsetT {
+ let idx = vtable_offset as usize;
+ read_scalar::(&self.buf[idx..idx + SIZE_VOFFSET])
+ }
+
+ /// Writes an object field offset into the vtable.
+ ///
+ /// Note that this expects field offsets (which are like pointers), not
+ /// field ids (which are like array indices).
+ #[inline(always)]
+ pub fn write_field_offset(&mut self, vtable_offset: VOffsetT, object_data_offset: VOffsetT) {
+ let idx = vtable_offset as usize;
+ emplace_scalar::(&mut self.buf[idx..idx + SIZE_VOFFSET], object_data_offset);
+ }
+
+ /// Clears all data in this VTableWriter. Used to cleanly undo a
+ /// vtable write.
+ #[inline(always)]
+ pub fn clear(&mut self) {
+ // This is the closest thing to memset in Rust right now.
+ let len = self.buf.len();
+ let p = self.buf.as_mut_ptr() as *mut u8;
+ unsafe {
+ write_bytes(p, 0, len);
+ }
+ }
+}
+
diff --git a/samples/monster_generated.rs b/samples/monster_generated.rs
new file mode 100644
index 000000000..9ec573c7c
--- /dev/null
+++ b/samples/monster_generated.rs
@@ -0,0 +1,507 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+pub mod my_game {
+ #![allow(dead_code)]
+ #![allow(unused_imports)]
+
+ use std::mem;
+ use std::marker::PhantomData;
+ use std::cmp::Ordering;
+
+ extern crate flatbuffers;
+ use self::flatbuffers::EndianScalar;
+pub mod sample {
+ #![allow(dead_code)]
+ #![allow(unused_imports)]
+
+ use std::mem;
+ use std::marker::PhantomData;
+ use std::cmp::Ordering;
+
+ extern crate flatbuffers;
+ use self::flatbuffers::EndianScalar;
+
+#[allow(non_camel_case_types)]
+#[repr(i8)]
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum Color {
+ Red = 0,
+ Green = 1,
+ Blue = 2
+}
+
+const ENUM_MIN_COLOR: i8 = 0;
+const ENUM_MAX_COLOR: i8 = 2;
+
+impl<'a> flatbuffers::Follow<'a> for Color {
+ type Inner = Self;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ flatbuffers::read_scalar_at::(buf, loc)
+ }
+}
+
+impl flatbuffers::EndianScalar for Color {
+ #[inline]
+ fn to_little_endian(self) -> Self {
+ let n = i8::to_le(self as i8);
+ let p = &n as *const i8 as *const Color;
+ unsafe { *p }
+ }
+ #[inline]
+ fn from_little_endian(self) -> Self {
+ let n = i8::from_le(self as i8);
+ let p = &n as *const i8 as *const Color;
+ unsafe { *p }
+ }
+}
+
+impl flatbuffers::Push for Color {
+ type Output = Color;
+ #[inline]
+ fn push(&self, dst: &mut [u8], _rest: &[u8]) {
+ flatbuffers::emplace_scalar::(dst, *self);
+ }
+}
+
+#[allow(non_camel_case_types)]
+const ENUM_VALUES_COLOR:[Color; 3] = [
+ Color::Red,
+ Color::Green,
+ Color::Blue
+];
+
+#[allow(non_camel_case_types)]
+const ENUM_NAMES_COLOR:[&'static str; 3] = [
+ "Red",
+ "Green",
+ "Blue"
+];
+
+pub fn enum_name_color(e: Color) -> &'static str {
+ let index: usize = e as usize;
+ ENUM_NAMES_COLOR[index]
+}
+
+#[allow(non_camel_case_types)]
+#[repr(u8)]
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum Equipment {
+ NONE = 0,
+ Weapon = 1
+}
+
+const ENUM_MIN_EQUIPMENT: u8 = 0;
+const ENUM_MAX_EQUIPMENT: u8 = 1;
+
+impl<'a> flatbuffers::Follow<'a> for Equipment {
+ type Inner = Self;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ flatbuffers::read_scalar_at::(buf, loc)
+ }
+}
+
+impl flatbuffers::EndianScalar for Equipment {
+ #[inline]
+ fn to_little_endian(self) -> Self {
+ let n = u8::to_le(self as u8);
+ let p = &n as *const u8 as *const Equipment;
+ unsafe { *p }
+ }
+ #[inline]
+ fn from_little_endian(self) -> Self {
+ let n = u8::from_le(self as u8);
+ let p = &n as *const u8 as *const Equipment;
+ unsafe { *p }
+ }
+}
+
+impl flatbuffers::Push for Equipment {
+ type Output = Equipment;
+ #[inline]
+ fn push(&self, dst: &mut [u8], _rest: &[u8]) {
+ flatbuffers::emplace_scalar::(dst, *self);
+ }
+}
+
+#[allow(non_camel_case_types)]
+const ENUM_VALUES_EQUIPMENT:[Equipment; 2] = [
+ Equipment::NONE,
+ Equipment::Weapon
+];
+
+#[allow(non_camel_case_types)]
+const ENUM_NAMES_EQUIPMENT:[&'static str; 2] = [
+ "NONE",
+ "Weapon"
+];
+
+pub fn enum_name_equipment(e: Equipment) -> &'static str {
+ let index: usize = e as usize;
+ ENUM_NAMES_EQUIPMENT[index]
+}
+
+pub struct EquipmentUnionTableOffset {}
+// struct Vec3, aligned to 4
+#[repr(C, align(4))]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Vec3 {
+ x_: f32,
+ y_: f32,
+ z_: f32,
+} // pub struct Vec3
+impl flatbuffers::SafeSliceAccess for Vec3 {}
+impl<'a> flatbuffers::Follow<'a> for Vec3 {
+ type Inner = &'a Vec3;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ <&'a Vec3>::follow(buf, loc)
+ //flatbuffers::follow_cast_ref::(buf, loc)
+ }
+}
+impl<'a> flatbuffers::Follow<'a> for &'a Vec3 {
+ type Inner = &'a Vec3;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ flatbuffers::follow_cast_ref::(buf, loc)
+ }
+}
+impl<'b> flatbuffers::Push for Vec3 {
+ type Output = Vec3;
+ #[inline]
+ fn push(&self, dst: &mut [u8], _rest: &[u8]) {
+ let src = unsafe {
+ ::std::slice::from_raw_parts(self as *const Vec3 as *const u8, Self::size())
+ };
+ dst.copy_from_slice(src);
+ }
+}
+impl<'b> flatbuffers::Push for &'b Vec3 {
+ type Output = Vec3;
+
+ #[inline]
+ fn push(&self, dst: &mut [u8], _rest: &[u8]) {
+ let src = unsafe {
+ ::std::slice::from_raw_parts(*self as *const Vec3 as *const u8, Self::size())
+ };
+ dst.copy_from_slice(src);
+ }
+}
+
+
+impl Vec3 {
+ pub fn new<'a>(_x: f32, _y: f32, _z: f32) -> Self {
+ Vec3 {
+ x_: _x.to_little_endian(),
+ y_: _y.to_little_endian(),
+ z_: _z.to_little_endian(),
+
+ }
+ }
+ pub fn x<'a>(&'a self) -> f32 {
+ self.x_.from_little_endian()
+ }
+ pub fn y<'a>(&'a self) -> f32 {
+ self.y_.from_little_endian()
+ }
+ pub fn z<'a>(&'a self) -> f32 {
+ self.z_.from_little_endian()
+ }
+}
+
+pub enum MonsterOffset {}
+#[derive(Copy, Clone, Debug, PartialEq)]
+
+pub struct Monster<'a> {
+ pub _tab: flatbuffers::Table<'a>,
+}
+
+impl<'a> flatbuffers::Follow<'a> for Monster<'a> {
+ type Inner = Monster<'a>;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Self {
+ _tab: flatbuffers::Table { buf: buf, loc: loc },
+ }
+ }
+}
+
+impl<'a> Monster<'a> {
+ #[inline]
+ pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
+ Monster {
+ _tab: table,
+ }
+ }
+ #[allow(unused_mut)]
+ pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
+ _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
+ args: &'args MonsterArgs<'args>) -> flatbuffers::WIPOffset> {
+ let mut builder = MonsterBuilder::new(_fbb);
+ if let Some(x) = args.equipped { builder.add_equipped(x); }
+ if let Some(x) = args.weapons { builder.add_weapons(x); }
+ if let Some(x) = args.inventory { builder.add_inventory(x); }
+ if let Some(x) = args.name { builder.add_name(x); }
+ if let Some(x) = args.pos { builder.add_pos(x); }
+ builder.add_hp(args.hp);
+ builder.add_mana(args.mana);
+ builder.add_equipped_type(args.equipped_type);
+ builder.add_color(args.color);
+ builder.finish()
+ }
+
+ pub const VT_POS: flatbuffers::VOffsetT = 4;
+ pub const VT_MANA: flatbuffers::VOffsetT = 6;
+ pub const VT_HP: flatbuffers::VOffsetT = 8;
+ pub const VT_NAME: flatbuffers::VOffsetT = 10;
+ pub const VT_INVENTORY: flatbuffers::VOffsetT = 14;
+ pub const VT_COLOR: flatbuffers::VOffsetT = 16;
+ pub const VT_WEAPONS: flatbuffers::VOffsetT = 18;
+ pub const VT_EQUIPPED_TYPE: flatbuffers::VOffsetT = 20;
+ pub const VT_EQUIPPED: flatbuffers::VOffsetT = 22;
+
+ #[inline]
+ pub fn pos(&'a self) -> Option<&'a Vec3> {
+ self._tab.get::(Monster::VT_POS, None)
+ }
+ #[inline]
+ pub fn mana(&'a self) -> i16 {
+ self._tab.get::(Monster::VT_MANA, Some(150)).unwrap()
+ }
+ #[inline]
+ pub fn hp(&'a self) -> i16 {
+ self._tab.get::(Monster::VT_HP, Some(100)).unwrap()
+ }
+ #[inline]
+ pub fn name(&'a self) -> Option<&'a str> {
+ self._tab.get::>(Monster::VT_NAME, None)
+ }
+ #[inline]
+ pub fn inventory(&'a self) -> Option<&'a [u8]> {
+ self._tab.get::>>(Monster::VT_INVENTORY, None).map(|v| v.safe_slice())
+ }
+ #[inline]
+ pub fn color(&'a self) -> Color {
+ self._tab.get::(Monster::VT_COLOR, Some(Color::Blue)).unwrap()
+ }
+ #[inline]
+ pub fn weapons(&'a self) -> Option>>> {
+ self._tab.get::>>>>(Monster::VT_WEAPONS, None)
+ }
+ #[inline]
+ pub fn equipped_type(&'a self) -> Equipment {
+ self._tab.get::(Monster::VT_EQUIPPED_TYPE, Some(Equipment::NONE)).unwrap()
+ }
+ #[inline]
+ pub fn equipped(&'a self) -> Option> {
+ self._tab.get::>>(Monster::VT_EQUIPPED, None)
+ }
+ #[inline]
+ #[allow(non_snake_case)]
+ pub fn equipped_as_weapon(&'a self) -> Option {
+ if self.equipped_type() == Equipment::Weapon {
+ self.equipped().map(|u| Weapon::init_from_table(u))
+ } else {
+ None
+ }
+ }
+
+}
+
+pub struct MonsterArgs<'a> {
+ pub pos: Option<&'a Vec3>,
+ pub mana: i16,
+ pub hp: i16,
+ pub name: Option>,
+ pub inventory: Option>>,
+ pub color: Color,
+ pub weapons: Option>>>>,
+ pub equipped_type: Equipment,
+ pub equipped: Option>,
+}
+impl<'a> Default for MonsterArgs<'a> {
+ #[inline]
+ fn default() -> Self {
+ MonsterArgs {
+ pos: None,
+ mana: 150,
+ hp: 100,
+ name: None,
+ inventory: None,
+ color: Color::Blue,
+ weapons: None,
+ equipped_type: Equipment::NONE,
+ equipped: None,
+ }
+ }
+}
+pub struct MonsterBuilder<'a: 'b, 'b> {
+ fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ start_: flatbuffers::WIPOffset,
+}
+impl<'a: 'b, 'b> MonsterBuilder<'a, 'b> {
+ #[inline]
+ pub fn add_pos(&mut self, pos: &'b Vec3) {
+ self.fbb_.push_slot_always::<&Vec3>(Monster::VT_POS, pos);
+ }
+ #[inline]
+ pub fn add_mana(&mut self, mana: i16) {
+ self.fbb_.push_slot::(Monster::VT_MANA, mana, 150);
+ }
+ #[inline]
+ pub fn add_hp(&mut self, hp: i16) {
+ self.fbb_.push_slot::(Monster::VT_HP, hp, 100);
+ }
+ #[inline]
+ pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::>(Monster::VT_NAME, name);
+ }
+ #[inline]
+ pub fn add_inventory(&mut self, inventory: flatbuffers::WIPOffset>) {
+ self.fbb_.push_slot_always::>(Monster::VT_INVENTORY, inventory);
+ }
+ #[inline]
+ pub fn add_color(&mut self, color: Color) {
+ self.fbb_.push_slot::(Monster::VT_COLOR, color, Color::Blue);
+ }
+ #[inline]
+ pub fn add_weapons(&mut self, weapons: flatbuffers::WIPOffset>>>) {
+ self.fbb_.push_slot_always::>(Monster::VT_WEAPONS, weapons);
+ }
+ #[inline]
+ pub fn add_equipped_type(&mut self, equipped_type: Equipment) {
+ self.fbb_.push_slot::(Monster::VT_EQUIPPED_TYPE, equipped_type, Equipment::NONE);
+ }
+ #[inline]
+ pub fn add_equipped(&mut self, equipped: flatbuffers::WIPOffset) {
+ self.fbb_.push_slot_always::>(Monster::VT_EQUIPPED, equipped);
+ }
+ #[inline]
+ pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> MonsterBuilder<'a, 'b> {
+ let start = _fbb.start_table();
+ MonsterBuilder {
+ fbb_: _fbb,
+ start_: start,
+ }
+ }
+ #[inline]
+ pub fn finish(self) -> flatbuffers::WIPOffset> {
+ let o = self.fbb_.end_table(self.start_);
+ flatbuffers::WIPOffset::new(o.value())
+ }
+}
+
+pub enum WeaponOffset {}
+#[derive(Copy, Clone, Debug, PartialEq)]
+
+pub struct Weapon<'a> {
+ pub _tab: flatbuffers::Table<'a>,
+}
+
+impl<'a> flatbuffers::Follow<'a> for Weapon<'a> {
+ type Inner = Weapon<'a>;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Self {
+ _tab: flatbuffers::Table { buf: buf, loc: loc },
+ }
+ }
+}
+
+impl<'a> Weapon<'a> {
+ #[inline]
+ pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
+ Weapon {
+ _tab: table,
+ }
+ }
+ #[allow(unused_mut)]
+ pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
+ _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
+ args: &'args WeaponArgs<'args>) -> flatbuffers::WIPOffset> {
+ let mut builder = WeaponBuilder::new(_fbb);
+ if let Some(x) = args.name { builder.add_name(x); }
+ builder.add_damage(args.damage);
+ builder.finish()
+ }
+
+ pub const VT_NAME: flatbuffers::VOffsetT = 4;
+ pub const VT_DAMAGE: flatbuffers::VOffsetT = 6;
+
+ #[inline]
+ pub fn name(&'a self) -> Option<&'a str> {
+ self._tab.get::>(Weapon::VT_NAME, None)
+ }
+ #[inline]
+ pub fn damage(&'a self) -> i16 {
+ self._tab.get::(Weapon::VT_DAMAGE, Some(0)).unwrap()
+ }
+}
+
+pub struct WeaponArgs<'a> {
+ pub name: Option>,
+ pub damage: i16,
+}
+impl<'a> Default for WeaponArgs<'a> {
+ #[inline]
+ fn default() -> Self {
+ WeaponArgs {
+ name: None,
+ damage: 0,
+ }
+ }
+}
+pub struct WeaponBuilder<'a: 'b, 'b> {
+ fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ start_: flatbuffers::WIPOffset,
+}
+impl<'a: 'b, 'b> WeaponBuilder<'a, 'b> {
+ #[inline]
+ pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::>(Weapon::VT_NAME, name);
+ }
+ #[inline]
+ pub fn add_damage(&mut self, damage: i16) {
+ self.fbb_.push_slot::(Weapon::VT_DAMAGE, damage, 0);
+ }
+ #[inline]
+ pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> WeaponBuilder<'a, 'b> {
+ let start = _fbb.start_table();
+ WeaponBuilder {
+ fbb_: _fbb,
+ start_: start,
+ }
+ }
+ #[inline]
+ pub fn finish(self) -> flatbuffers::WIPOffset> {
+ let o = self.fbb_.end_table(self.start_);
+ flatbuffers::WIPOffset::new(o.value())
+ }
+}
+
+#[inline]
+pub fn get_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> {
+ flatbuffers::get_root::>(buf)
+}
+
+#[inline]
+pub fn get_size_prefixed_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> {
+ flatbuffers::get_size_prefixed_root::>(buf)
+}
+
+#[inline]
+pub fn finish_monster_buffer<'a, 'b>(
+ fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ root: flatbuffers::WIPOffset>) {
+ fbb.finish(root, None);
+}
+
+#[inline]
+pub fn finish_size_prefixed_monster_buffer<'a, 'b>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, root: flatbuffers::WIPOffset>) {
+ fbb.finish_size_prefixed(root, None);
+}
+} // pub mod Sample
+} // pub mod MyGame
+
diff --git a/samples/sample_binary.rs b/samples/sample_binary.rs
new file mode 100644
index 000000000..7a4c2ae0c
--- /dev/null
+++ b/samples/sample_binary.rs
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+// import the flatbuffers runtime library
+extern crate flatbuffers;
+
+// import the generated code
+#[path = "./monster_generated.rs"]
+mod monster_generated;
+pub use monster_generated::my_game::sample::{get_root_as_monster,
+ Color, Equipment,
+ Monster, MonsterArgs,
+ Vec3,
+ Weapon, WeaponArgs};
+
+
+// Example how to use FlatBuffers to create and read binary buffers.
+
+fn main() {
+ // Build up a serialized buffer algorithmically.
+ // Initialize it with a capacity of 1024 bytes.
+ let mut builder = flatbuffers::FlatBufferBuilder::new_with_capacity(1024);
+
+ // Serialize some weapons for the Monster: A 'sword' and an 'axe'.
+ let weapon_one_name = builder.create_string("Sword");
+ let weapon_two_name = builder.create_string("Axe");
+
+ // Use the `Weapon::create` shortcut to create Weapons with named field
+ // arguments.
+ let sword = Weapon::create(&mut builder, &WeaponArgs{
+ name: Some(weapon_one_name),
+ damage: 3,
+ });
+ let axe = Weapon::create(&mut builder, &WeaponArgs{
+ name: Some(weapon_two_name),
+ damage: 5,
+ });
+
+ // Name of the Monster.
+ let name = builder.create_string("Orc");
+
+ // Inventory.
+ let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+
+ // Create a FlatBuffer `vector` that contains offsets to the sword and axe
+ // we created above.
+ let weapons = builder.create_vector(&[sword, axe]);
+
+ // Create the path vector of Vec3 objects:
+ //let x = Vec3::new(1.0, 2.0, 3.0);
+ //let y = Vec3::new(4.0, 5.0, 6.0);
+ //let path = builder.create_vector(&[x, y]);
+
+ // Note that, for convenience, it is also valid to create a vector of
+ // references to structs, like this:
+ // let path = builder.create_vector(&[&x, &y]);
+
+ // Create the monster using the `Monster::create` helper function. This
+ // function accepts a `MonsterArgs` struct, which supplies all of the data
+ // needed to build a `Monster`. To supply empty/default fields, just use the
+ // Rust built-in `Default::default()` function, as demononstrated below.
+ let orc = Monster::create(&mut builder, &MonsterArgs{
+ pos: Some(&Vec3::new(1.0f32, 2.0f32, 3.0f32)),
+ mana: 150,
+ hp: 80,
+ name: Some(name),
+ inventory: Some(inventory),
+ color: Color::Red,
+ weapons: Some(weapons),
+ equipped_type: Equipment::Weapon,
+ equipped: Some(axe.as_union_value()),
+ //path: Some(path),
+ ..Default::default()
+ });
+
+ // Serialize the root of the object, without providing a file identifier.
+ builder.finish(orc, None);
+
+ // We now have a FlatBuffer we can store on disk or send over a network.
+
+ // ** file/network code goes here :) **
+
+ // Instead, we're going to access it right away (as if we just received it).
+ // This must be called after `finish()`.
+ let buf = builder.finished_data(); // Of type `&[u8]`
+
+ // Get access to the root:
+ let monster = get_root_as_monster(buf);
+
+ // Get and test some scalar types from the FlatBuffer.
+ let hp = monster.hp();
+ let mana = monster.mana();
+ let name = monster.name();
+
+ assert_eq!(hp, 80);
+ assert_eq!(mana, 150); // default
+ assert_eq!(name, Some("Orc"));
+
+ // Get and test a field of the FlatBuffer's `struct`.
+ assert!(monster.pos().is_some());
+ let pos = monster.pos().unwrap();
+ let x = pos.x();
+ let y = pos.y();
+ let z = pos.z();
+ assert_eq!(x, 1.0f32);
+ assert_eq!(y, 2.0f32);
+ assert_eq!(z, 3.0f32);
+
+ // Get an element from the `inventory` FlatBuffer's `vector`.
+ assert!(monster.inventory().is_some());
+ let inv = monster.inventory().unwrap();
+
+ // Note that this vector is returned as a slice, because direct access for
+ // this type, a u8 vector, is safe on all platforms:
+ let third_item = inv[2];
+ assert_eq!(third_item, 2);
+
+ // Get and test the `weapons` FlatBuffers's `vector`.
+ assert!(monster.weapons().is_some());
+ let weps = monster.weapons().unwrap();
+ //let weps_len = weps.len();
+ let wep2 = weps.get(1);
+ let second_weapon_name = wep2.name();
+ let second_weapon_damage = wep2.damage();
+ assert_eq!(second_weapon_name, Some("Axe"));
+ assert_eq!(second_weapon_damage, 5);
+
+ // Get and test the `Equipment` union (`equipped` field).
+ assert_eq!(monster.equipped_type(), Equipment::Weapon);
+ let equipped = monster.equipped_as_weapon().unwrap();
+ let weapon_name = equipped.name();
+ let weapon_damage = equipped.damage();
+ assert_eq!(weapon_name, Some("Axe"));
+ assert_eq!(weapon_damage, 5);
+
+ // Get and test the `path` FlatBuffers's `vector`.
+ //assert_eq!(monster.path().unwrap().len(), 2);
+ //assert_eq!(monster.path().unwrap()[0].x(), 1.0);
+ //assert_eq!(monster.path().unwrap()[1].x(), 4.0);
+
+ println!("The FlatBuffer was successfully created and accessed!");
+}
diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp
index 2a3bc9808..c632fe276 100644
--- a/src/flatc_main.cpp
+++ b/src/flatc_main.cpp
@@ -79,6 +79,10 @@ int main(int argc, const char *argv[]) {
flatbuffers::IDLOptions::kLua,
"Generate Lua files for tables/structs",
flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateRust, "-r", "--rust", "Rust", true, nullptr,
+ flatbuffers::IDLOptions::kRust,
+ "Generate Rust files for tables/structs",
+ flatbuffers::RustMakeRule },
{ flatbuffers::GeneratePhp, nullptr, "--php", "PHP", true, nullptr,
flatbuffers::IDLOptions::kPhp, "Generate PHP files for tables/structs",
flatbuffers::GeneralMakeRule },
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index df4c0ad92..b9bfacb02 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -477,7 +477,8 @@ class CppGenerator : public BaseGenerator {
std::string GenTypeBasic(const Type &type, bool user_facing_type) const {
static const char * const ctypename[] = {
// clang-format off
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE) \
#CTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp
index f3ed3e4fc..3245c586f 100644
--- a/src/idl_gen_general.cpp
+++ b/src/idl_gen_general.cpp
@@ -247,7 +247,7 @@ class GeneralGenerator : public BaseGenerator {
// clang-format off
static const char * const java_typename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
#JTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -255,7 +255,7 @@ class GeneralGenerator : public BaseGenerator {
static const char * const csharp_typename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
#NTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp
index c5767b479..4f20719bf 100644
--- a/src/idl_gen_go.cpp
+++ b/src/idl_gen_go.cpp
@@ -690,7 +690,7 @@ static std::string GenTypeBasic(const Type &type) {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
#GTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_lobster.cpp b/src/idl_gen_lobster.cpp
index 0487d17a7..5f199e3a1 100644
--- a/src/idl_gen_lobster.cpp
+++ b/src/idl_gen_lobster.cpp
@@ -81,7 +81,7 @@ class LobsterGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
#PTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_lua.cpp b/src/idl_gen_lua.cpp
index 862649181..34408d316 100644
--- a/src/idl_gen_lua.cpp
+++ b/src/idl_gen_lua.cpp
@@ -604,7 +604,7 @@ namespace lua {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
#PTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp
index 7d98e00de..5563f4bb5 100644
--- a/src/idl_gen_php.cpp
+++ b/src/idl_gen_php.cpp
@@ -864,7 +864,7 @@ class PhpGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
#NTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp
index e000ada9a..109e203d4 100644
--- a/src/idl_gen_python.cpp
+++ b/src/idl_gen_python.cpp
@@ -615,7 +615,7 @@ class PythonGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
#PTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp
new file mode 100644
index 000000000..1441af492
--- /dev/null
+++ b/src/idl_gen_rust.cpp
@@ -0,0 +1,1811 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + "_generated.rs";
+}
+
+// Convert a camelCaseIdentifier or CamelCaseIdentifier to a
+// snake_case_indentifier.
+std::string MakeSnakeCase(const std::string &in) {
+ std::string s;
+ for (size_t i = 0; i < in.length(); i++) {
+ if (islower(in[i])) {
+ s += static_cast(in[i]);
+ } else {
+ if (i > 0) {
+ s += '_';
+ }
+ s += static_cast(tolower(in[i]));
+ }
+ }
+ return s;
+}
+
+// Convert a string to all uppercase.
+std::string MakeUpper(const std::string &in) {
+ std::string s;
+ for (size_t i = 0; i < in.length(); i++) {
+ s += static_cast(toupper(in[i]));
+ }
+ return s;
+}
+
+// Encapsulate all logical field types in this enum. This allows us to write
+// field logic based on type switches, instead of branches on the properties
+// set on the Type.
+// TODO(rw): for backwards compatibility, we can't use a strict `enum class`
+// declaration here. could we use the `-Wswitch-enum` warning to
+// achieve the same effect?
+enum FullType {
+ ftInteger = 0,
+ ftFloat = 1,
+ ftBool = 2,
+
+ ftStruct = 3,
+ ftTable = 4,
+
+ ftEnumKey = 5,
+ ftUnionKey = 6,
+
+ ftUnionValue = 7,
+
+ // TODO(rw): bytestring?
+ ftString = 8,
+
+ ftVectorOfInteger = 9,
+ ftVectorOfFloat = 10,
+ ftVectorOfBool = 11,
+ ftVectorOfEnumKey = 12,
+ ftVectorOfStruct = 13,
+ ftVectorOfTable = 14,
+ ftVectorOfString = 15,
+ ftVectorOfUnionValue = 16,
+};
+
+// Convert a Type to a FullType (exhaustive).
+FullType GetFullType(const Type &type) {
+ // N.B. The order of these conditionals matters for some types.
+
+ if (type.base_type == BASE_TYPE_STRING) {
+ return ftString;
+ } else if (type.base_type == BASE_TYPE_STRUCT) {
+ if (type.struct_def->fixed) {
+ return ftStruct;
+ } else {
+ return ftTable;
+ }
+ } else if (type.base_type == BASE_TYPE_VECTOR) {
+ switch (GetFullType(type.VectorType())) {
+ case ftInteger: {
+ return ftVectorOfInteger;
+ }
+ case ftFloat: {
+ return ftVectorOfFloat;
+ }
+ case ftBool: {
+ return ftVectorOfBool;
+ }
+ case ftStruct: {
+ return ftVectorOfStruct;
+ }
+ case ftTable: {
+ return ftVectorOfTable;
+ }
+ case ftString: {
+ return ftVectorOfString;
+ }
+ case ftEnumKey: {
+ return ftVectorOfEnumKey;
+ }
+ case ftUnionKey:
+ case ftUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are unsupported");
+ }
+ default: {
+ FLATBUFFERS_ASSERT(false && "vector of vectors are unsupported");
+ }
+ }
+ } else if (type.enum_def != nullptr) {
+ if (type.enum_def->is_union) {
+ if (type.base_type == BASE_TYPE_UNION) {
+ return ftUnionValue;
+ } else if (IsInteger(type.base_type)) {
+ return ftUnionKey;
+ } else {
+ FLATBUFFERS_ASSERT(false && "unknown union field type");
+ }
+ } else {
+ return ftEnumKey;
+ }
+ } else if (IsScalar(type.base_type)) {
+ if (IsBool(type.base_type)) {
+ return ftBool;
+ } else if (IsInteger(type.base_type)) {
+ return ftInteger;
+ } else if (IsFloat(type.base_type)) {
+ return ftFloat;
+ } else {
+ FLATBUFFERS_ASSERT(false && "unknown number type");
+ }
+ }
+
+ FLATBUFFERS_ASSERT(false && "completely unknown type");
+
+ // this is only to satisfy the compiler's return analysis.
+ return ftBool;
+}
+
+namespace rust {
+
+class RustGenerator : public BaseGenerator {
+ public:
+ RustGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "::"),
+ cur_name_space_(nullptr) {
+ const char *keywords[] = {
+ // list taken from:
+ // https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html
+ //
+ // we write keywords one per line so that we can easily compare them with
+ // changes to that webpage in the future.
+
+ // currently-used keywords
+ "as",
+ "break",
+ "const",
+ "continue",
+ "crate",
+ "else",
+ "enum",
+ "extern",
+ "false",
+ "fn",
+ "for",
+ "if",
+ "impl",
+ "in",
+ "let",
+ "loop",
+ "match",
+ "mod",
+ "move",
+ "mut",
+ "pub",
+ "ref",
+ "return",
+ "Self",
+ "self",
+ "static",
+ "struct",
+ "super",
+ "trait",
+ "true",
+ "type",
+ "unsafe",
+ "use",
+ "where",
+ "while",
+
+ // future possible keywords
+ "abstract",
+ "alignof",
+ "become",
+ "box",
+ "do",
+ "final",
+ "macro",
+ "offsetof",
+ "override",
+ "priv",
+ "proc",
+ "pure",
+ "sizeof",
+ "typeof",
+ "unsized",
+ "virtual",
+ "yield",
+
+ // other rust terms we should not use
+ "std",
+ "usize",
+ "isize",
+ "u8",
+ "i8",
+ "u16",
+ "i16",
+ "u32",
+ "i32",
+ "u64",
+ "i64",
+ "u128",
+ "i128",
+ "f32",
+ "f64",
+
+ // These are terms the code generator can implement on types.
+ //
+ // In Rust, the trait resolution rules (as described at
+ // https://github.com/rust-lang/rust/issues/26007) mean that, as long
+ // as we impl table accessors as inherent methods, we'll never create
+ // conflicts with these keywords. However, that's a fairly nuanced
+ // implementation detail, and how we implement methods could change in
+ // the future. as a result, we proactively block these out as reserved
+ // words.
+ "follow",
+ "push",
+ "size",
+ "alignment",
+ "to_little_endian",
+ "from_little_endian",
+ nullptr };
+ for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
+ }
+
+ // Iterate through all definitions we haven't generated code for (enums,
+ // structs, and tables) and output them to a single file.
+ bool generate() {
+ code_.Clear();
+ code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+
+ assert(!cur_name_space_);
+
+ // Generate all code in their namespaces, once, because Rust does not
+ // permit re-opening modules.
+ //
+ // TODO(rw): Use a set data structure to reduce namespace evaluations from
+ // O(n**2) to O(n).
+ for (auto ns_it = parser_.namespaces_.begin();
+ ns_it != parser_.namespaces_.end();
+ ++ns_it) {
+ const auto &ns = *ns_it;
+
+ // Generate code for all the enum declarations.
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ const auto &enum_def = **it;
+ if (enum_def.defined_namespace != ns) { continue; }
+ if (!enum_def.generated) {
+ SetNameSpace(enum_def.defined_namespace);
+ GenEnum(enum_def);
+ }
+ }
+
+ // Generate code for all structs.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (struct_def.defined_namespace != ns) { continue; }
+ if (struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenStruct(struct_def);
+ }
+ }
+
+ // Generate code for all tables.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (struct_def.defined_namespace != ns) { continue; }
+ if (!struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenTable(struct_def);
+ }
+ }
+
+ // Generate global helper functions.
+ if (parser_.root_struct_def_) {
+ auto &struct_def = *parser_.root_struct_def_;
+ if (struct_def.defined_namespace != ns) { continue; }
+ SetNameSpace(struct_def.defined_namespace);
+ GenRootTableFuncs(struct_def);
+ }
+ }
+ if (cur_name_space_) SetNameSpace(nullptr);
+
+ const auto file_path = GeneratedFileName(path_, file_name_);
+ const auto final_code = code_.ToString();
+ return SaveFile(file_path.c_str(), final_code, false);
+ }
+
+ private:
+ CodeWriter code_;
+
+ std::set keywords_;
+
+ // This tracks the current namespace so we can insert namespace declarations.
+ const Namespace *cur_name_space_;
+
+ const Namespace *CurrentNameSpace() const { return cur_name_space_; }
+
+ // Determine if a Type needs a lifetime template parameter when used in Rust.
+ bool TypeNeedsLifetimeParameter(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftTable:
+ case ftEnumKey:
+ case ftUnionKey:
+ case ftStruct: { return false; }
+ default: { return true; }
+ }
+ }
+
+ // Determine if a table args rust type needs a lifetime template parameter.
+ bool TableBuilderArgsNeedsLifetime(const StructDef &struct_def) const {
+ FLATBUFFERS_ASSERT(!struct_def.fixed);
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ continue;
+ }
+
+ if (TypeNeedsLifetimeParameter(field.value.type)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Determine if a Type needs to be copied (for endian safety) when used in a
+ // Struct.
+ bool StructMemberAccessNeedsCopy(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger: // requires endian swap
+ case ftFloat: // requires endian swap
+ case ftBool: // no endian-swap, but do the copy for UX consistency
+ case ftEnumKey: { return true; } // requires endian swap
+ case ftStruct: { return false; } // no endian swap
+ default: {
+ // logic error: no other types can be struct members.
+ FLATBUFFERS_ASSERT(false && "invalid struct member type");
+ return false; // only to satisfy compiler's return analysis
+ }
+ }
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : name + "_";
+ }
+
+ std::string Name(const Definition &def) const {
+ return EscapeKeyword(def.name);
+ }
+
+ std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); }
+
+ std::string WrapInNameSpace(const Definition &def) const {
+ return WrapInNameSpace(def.defined_namespace, Name(def));
+ }
+ std::string WrapInNameSpace(const Namespace *ns,
+ const std::string &name) const {
+ if (CurrentNameSpace() == ns) return name;
+ std::string prefix = GetRelativeNamespaceTraversal(CurrentNameSpace(), ns);
+ return prefix + name;
+ }
+
+ // Determine the namespace traversal needed from the Rust crate root.
+ // This may be useful in the future for referring to included files, but is
+ // currently unused.
+ std::string GetAbsoluteNamespaceTraversal(const Namespace *dst) const {
+ std::stringstream stream;
+
+ stream << "::";
+ for (auto d = dst->components.begin(); d != dst->components.end(); d++) {
+ stream << MakeSnakeCase(*d) + "::";
+ }
+ return stream.str();
+ }
+
+ // Determine the relative namespace traversal needed to reference one
+ // namespace from another namespace. This is useful because it does not force
+ // the user to have a particular file layout. (If we output absolute
+ // namespace paths, that may require users to organize their Rust crates in a
+ // particular way.)
+ std::string GetRelativeNamespaceTraversal(const Namespace *src,
+ const Namespace *dst) const {
+ // calculate the path needed to reference dst from src.
+ // example: f(A::B::C, A::B::C) -> (none)
+ // example: f(A::B::C, A::B) -> super::
+ // example: f(A::B::C, A::B::D) -> super::D
+ // example: f(A::B::C, A) -> super::super::
+ // example: f(A::B::C, D) -> super::super::super::D
+ // example: f(A::B::C, D::E) -> super::super::super::D::E
+ // example: f(A, D::E) -> super::D::E
+ // does not include leaf object (typically a struct type).
+
+ size_t i = 0;
+ std::stringstream stream;
+
+ auto s = src->components.begin();
+ auto d = dst->components.begin();
+ for(;;) {
+ if (s == src->components.end()) { break; }
+ if (d == dst->components.end()) { break; }
+ if (*s != *d) { break; }
+ s++;
+ d++;
+ i++;
+ }
+
+ for (; s != src->components.end(); s++) {
+ stream << "super::";
+ }
+ for (; d != dst->components.end(); d++) {
+ stream << MakeSnakeCase(*d) + "::";
+ }
+ return stream.str();
+ }
+
+ // Generate a comment from the schema.
+ void GenComment(const std::vector &dc, const char *prefix = "") {
+ std::string text;
+ ::flatbuffers::GenComment(dc, &text, nullptr, prefix);
+ code_ += text + "\\";
+ }
+
+ // Return a Rust type from the table in idl.h.
+ std::string GetTypeBasic(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey: { break; }
+ default: { FLATBUFFERS_ASSERT(false && "incorrect type given");}
+ }
+
+ // clang-format off
+ static const char * const ctypename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE) \
+ #RTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+
+ if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
+ return ctypename[type.base_type];
+ }
+
+ // Look up the native type for an enum. This will always be an integer like
+ // u8, i32, etc.
+ std::string GetEnumTypeForDecl(const Type &type) {
+ const auto ft = GetFullType(type);
+ if (!(ft == ftEnumKey || ft == ftUnionKey)) {
+ FLATBUFFERS_ASSERT(false && "precondition failed in GetEnumTypeForDecl");
+ }
+
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE) \
+ #RTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+
+ // Enums can be bools, but their Rust representation must be a u8, as used
+ // in the repr attribute (#[repr(bool)] is an invalid attribute).
+ if (type.base_type == BASE_TYPE_BOOL) return "u8";
+ return ctypename[type.base_type];
+ }
+
+ // Return a Rust type for any type (scalar, table, struct) specifically for
+ // using a FlatBuffer.
+ std::string GetTypeGet(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey: {
+ return GetTypeBasic(type); }
+ case ftTable: {
+ return WrapInNameSpace(type.struct_def->defined_namespace,
+ type.struct_def->name) + "<'a>"; }
+ default: {
+ return WrapInNameSpace(type.struct_def->defined_namespace,
+ type.struct_def->name); }
+ }
+ }
+
+ std::string GetEnumValUse(const EnumDef &enum_def,
+ const EnumVal &enum_val) const {
+ return Name(enum_def) + "::" + Name(enum_val);
+ }
+
+ // Generate an enum declaration,
+ // an enum string lookup table,
+ // an enum match function,
+ // and an enum array of values
+ void GenEnum(const EnumDef &enum_def) {
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+ code_.SetValue("BASE_TYPE", GetEnumTypeForDecl(enum_def.underlying_type));
+ code_.SetValue("SEP", "");
+
+ GenComment(enum_def.doc_comment);
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "#[repr({{BASE_TYPE}})]";
+ code_ += "#[derive(Clone, Copy, PartialEq, Debug)]";
+ code_ += "pub enum " + Name(enum_def) + " {";
+
+ int64_t anyv = 0;
+ const EnumVal *minv = nullptr, *maxv = nullptr;
+ for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+ ++it) {
+ const auto &ev = **it;
+
+ GenComment(ev.doc_comment, " ");
+ code_.SetValue("KEY", Name(ev));
+ code_.SetValue("VALUE", NumToString(ev.value));
+ code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
+ code_.SetValue("SEP", ",\n");
+
+ minv = !minv || minv->value > ev.value ? &ev : minv;
+ maxv = !maxv || maxv->value < ev.value ? &ev : maxv;
+ anyv |= ev.value;
+ }
+
+ code_ += "";
+ code_ += "}";
+ code_ += "";
+
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+ code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def)));
+ code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def))));
+ code_.SetValue("ENUM_MIN_BASE_VALUE", NumToString(minv->value));
+ code_.SetValue("ENUM_MAX_BASE_VALUE", NumToString(maxv->value));
+
+ // Generate enum constants, and impls for Follow, EndianScalar, and Push.
+ code_ += "const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
+ code_ += "{{ENUM_MIN_BASE_VALUE}};";
+ code_ += "const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
+ code_ += "{{ENUM_MAX_BASE_VALUE}};";
+ code_ += "";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {";
+ code_ += " type Inner = Self;";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " flatbuffers::read_scalar_at::(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {";
+ code_ += " #[inline]";
+ code_ += " fn to_little_endian(self) -> Self {";
+ code_ += " let n = {{BASE_TYPE}}::to_le(self as {{BASE_TYPE}});";
+ code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
+ code_ += " unsafe { *p }";
+ code_ += " }";
+ code_ += " #[inline]";
+ code_ += " fn from_little_endian(self) -> Self {";
+ code_ += " let n = {{BASE_TYPE}}::from_le(self as {{BASE_TYPE}});";
+ code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
+ code_ += " unsafe { *p }";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {";
+ code_ += " type Output = {{ENUM_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " flatbuffers::emplace_scalar::<{{ENUM_NAME}}>"
+ "(dst, *self);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+
+ // Generate an array of all enumeration values.
+ auto num_fields = NumToString(enum_def.vals.vec.size());
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "const ENUM_VALUES_{{ENUM_NAME_CAPS}}:[{{ENUM_NAME}}; " +
+ num_fields + "] = [";
+ for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+ ++it) {
+ const auto &ev = **it;
+ auto value = GetEnumValUse(enum_def, ev);
+ auto suffix = *it != enum_def.vals.vec.back() ? "," : "";
+ code_ += " " + value + suffix;
+ }
+ code_ += "];";
+ code_ += "";
+
+ // Generate a string table for enum values.
+ // Problem is, if values are very sparse that could generate really big
+ // tables. Ideally in that case we generate a map lookup instead, but for
+ // the moment we simply don't output a table at all.
+ auto range =
+ enum_def.vals.vec.back()->value - enum_def.vals.vec.front()->value + 1;
+ // Average distance between values above which we consider a table
+ // "too sparse". Change at will.
+ static const int kMaxSparseness = 5;
+ if (range / static_cast(enum_def.vals.vec.size()) <
+ kMaxSparseness) {
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "const ENUM_NAMES_{{ENUM_NAME_CAPS}}:[&'static str; " +
+ NumToString(range) + "] = [";
+
+ auto val = enum_def.vals.vec.front()->value;
+ for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+ ++it) {
+ const auto &ev = **it;
+ while (val++ != ev.value) { code_ += " \"\","; }
+ auto suffix = *it != enum_def.vals.vec.back() ? "," : "";
+ code_ += " \"" + Name(ev) + "\"" + suffix;
+ }
+ code_ += "];";
+ code_ += "";
+
+ code_ += "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> "
+ "&'static str {";
+
+ code_ += " let index: usize = e as usize\\";
+ if (enum_def.vals.vec.front()->value) {
+ auto vals = GetEnumValUse(enum_def, *enum_def.vals.vec.front());
+ code_ += " - " + vals + " as usize\\";
+ }
+ code_ += ";";
+
+ code_ += " ENUM_NAMES_{{ENUM_NAME_CAPS}}[index]";
+ code_ += "}";
+ code_ += "";
+ }
+
+ if (enum_def.is_union) {
+ // Generate tyoesafe offset(s) for unions
+ code_.SetValue("NAME", Name(enum_def));
+ code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset");
+ code_ += "pub struct {{UNION_OFFSET_NAME}} {}";
+ }
+ }
+
+ std::string GetFieldOffsetName(const FieldDef &field) {
+ return "VT_" + MakeUpper(Name(field));
+ }
+
+ std::string GetDefaultConstant(const FieldDef &field) {
+ return field.value.type.base_type == BASE_TYPE_FLOAT
+ ? field.value.constant + ""
+ : field.value.constant;
+ }
+
+ std::string GetDefaultScalarValue(const FieldDef &field) {
+ switch (GetFullType(field.value.type)) {
+ case ftInteger: { return GetDefaultConstant(field); }
+ case ftFloat: { return GetDefaultConstant(field); }
+ case ftBool: {
+ return field.value.constant == "0" ? "false" : "true";
+ }
+ case ftUnionKey:
+ case ftEnumKey: {
+ auto ev = field.value.type.enum_def->ReverseLookup(
+ StringToInt(field.value.constant.c_str()), false);
+ assert(ev);
+ return WrapInNameSpace(field.value.type.enum_def->defined_namespace,
+ GetEnumValUse(*field.value.type.enum_def, *ev));
+ }
+
+ // All pointer-ish types have a default value of None, because they are
+ // wrapped in Option.
+ default: { return "None"; }
+ }
+ }
+
+ // Create the return type for fields in the *BuilderArgs structs that are
+ // used to create Tables.
+ //
+ // Note: we could make all inputs to the BuilderArgs be an Option, as well
+ // as all outputs. But, the UX of Flatbuffers is that the user doesn't get to
+ // know if the value is default or not, because there are three ways to
+ // return a default value:
+ // 1) return a stored value that happens to be the default,
+ // 2) return a hardcoded value because the relevant vtable field is not in
+ // the vtable, or
+ // 3) return a hardcoded value because the vtable field value is set to zero.
+ std::string TableBuilderArgsDefnType(const FieldDef &field,
+ const std::string lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<&" + lifetime + " " + typname + ">";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option>>";
+ }
+ case ftString: {
+ return "Option>";
+ }
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftUnionValue: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "Option>";
+ }
+
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ return "Option>>";
+ }
+ case ftVectorOfBool: {
+ return "Option>>";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "Option>>";
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option>>";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option>>>>";
+ }
+ case ftVectorOfString: {
+ return "Option>>>";
+ }
+ case ftVectorOfUnionValue: {
+ const auto typname = WrapInNameSpace(*type.enum_def) + \
+ "UnionTableOffset";
+ return "Option>>>";
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string TableBuilderArgsDefaultValue(const FieldDef &field) {
+ return GetDefaultScalarValue(field);
+ }
+ std::string TableBuilderAddFuncDefaultValue(const FieldDef &field) {
+ switch (GetFullType(field.value.type)) {
+ case ftUnionKey:
+ case ftEnumKey: {
+ const std::string basetype = GetTypeBasic(field.value.type);
+ return GetDefaultScalarValue(field);
+ }
+
+ default: { return GetDefaultScalarValue(field); }
+ }
+ }
+
+ std::string TableBuilderArgsAddFuncType(const FieldDef &field,
+ const std::string lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset>";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset>>>";
+ }
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ return "flatbuffers::WIPOffset>";
+ }
+ case ftVectorOfBool: {
+ return "flatbuffers::WIPOffset>";
+ }
+ case ftVectorOfString: {
+ return "flatbuffers::WIPOffset>>";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "flatbuffers::WIPOffset>";
+ }
+ case ftVectorOfUnionValue: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "flatbuffers::WIPOffset>>";
+ }
+ case ftEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "&" + lifetime + " " + typname + "";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>";
+ }
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftBool: {
+ return "bool";
+ }
+ case ftString: {
+ return "flatbuffers::WIPOffset<&" + lifetime + " str>";
+ }
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftUnionValue: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "flatbuffers::WIPOffset";
+ }
+ }
+
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string TableBuilderArgsAddFuncBody(const FieldDef &field) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(field.value.type);
+ return "self.fbb_.push_slot::<" + typname + ">";
+ }
+ case ftBool: {
+ return "self.fbb_.push_slot::";
+ }
+
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto underlying_typname = GetTypeBasic(type);
+ return "self.fbb_.push_slot::<" + underlying_typname + ">";
+ }
+
+ case ftStruct: {
+ const std::string typname = WrapInNameSpace(*type.struct_def);
+ return "self.fbb_.push_slot_always::<&" + typname + ">";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "self.fbb_.push_slot_always::>";
+ }
+
+ case ftUnionValue:
+ case ftString:
+ case ftVectorOfInteger:
+ case ftVectorOfFloat:
+ case ftVectorOfBool:
+ case ftVectorOfEnumKey:
+ case ftVectorOfStruct:
+ case ftVectorOfTable:
+ case ftVectorOfString:
+ case ftVectorOfUnionValue: {
+ return "self.fbb_.push_slot_always::>";
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string GenTableAccessorFuncReturnType(const FieldDef &field,
+ const std::string lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftBool: {
+ return "bool";
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<&" + lifetime + " " + typname + ">";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<" + typname + "<" + lifetime + ">>";
+ }
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+
+ case ftUnionValue: {
+ return "Option>";
+ }
+ case ftString: {
+ return "Option<&" + lifetime + " str>";
+ }
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ if (IsOneByte(type.VectorType().base_type)) {
+ return "Option<&" + lifetime + " [" + typname + "]>";
+ }
+ return "Option>";
+ }
+ case ftVectorOfBool: {
+ return "Option<&" + lifetime + " [bool]>";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "Option>";
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<&" + lifetime + " [" + typname + "]>";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option>>>";
+ }
+ case ftVectorOfString: {
+ return "Option>>";
+ }
+ case ftVectorOfUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
+ // TODO(rw): when we do support these, we should consider using the
+ // Into trait to convert tables to typesafe union values.
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string GenTableAccessorFuncBody(const FieldDef &field,
+ const std::string lifetime,
+ const std::string offset_prefix) {
+ const std::string offset_name = offset_prefix + "::" + \
+ GetFieldOffsetName(field);
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool: {
+ const auto typname = GetTypeBasic(type);
+ const auto default_value = GetDefaultScalarValue(field);
+ return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + \
+ default_value + ")).unwrap()";
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "self._tab.get::<" + typname + ">(" + offset_name + ", None)";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "self._tab.get::>>(" + offset_name + ", None)";
+ }
+ case ftUnionValue: {
+ return "self._tab.get::>>(" + offset_name + \
+ ", None)";
+ }
+ case ftUnionKey:
+ case ftEnumKey: {
+ const auto underlying_typname = GetTypeBasic(type);
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ const auto default_value = GetDefaultScalarValue(field);
+ return "self._tab.get::<" + typname + ">(" + offset_name + \
+ ", Some(" + default_value + ")).unwrap()";
+ }
+ case ftString: {
+ return "self._tab.get::>(" + \
+ offset_name + ", None)";
+ }
+
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ std::string s = "self._tab.get::>>(" + offset_name + ", None)";
+ // single-byte values are safe to slice
+ if (IsOneByte(type.VectorType().base_type)) {
+ s += ".map(|v| v.safe_slice())";
+ }
+ return s;
+ }
+ case ftVectorOfBool: {
+ return "self._tab.get::>>(" + \
+ offset_name + ", None).map(|v| v.safe_slice())";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "self._tab.get::>>(" + \
+ offset_name + ", None)";
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "self._tab.get::>>(" + \
+ offset_name + ", None).map(|v| v.safe_slice() )";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "self._tab.get::>>>>(" + offset_name + ", None)";
+ }
+ case ftVectorOfString: {
+ return "self._tab.get::>>>(" + offset_name + ", None)";
+ }
+ case ftVectorOfUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ bool TableFieldReturnsOption(const Type& type) {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey:
+ return false;
+ default: return true;
+ }
+ }
+
+ // Generate an accessor struct, builder struct, and create function for a
+ // table.
+ void GenTable(const StructDef &struct_def) {
+ GenComment(struct_def.doc_comment);
+
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+ code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
+ code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(Name(struct_def)));
+
+ // Generate an offset type, the base type, the Follow impl, and the
+ // init_from_table impl.
+ code_ += "pub enum {{OFFSET_TYPELABEL}} {}";
+ code_ += "#[derive(Copy, Clone, Debug, PartialEq)]";
+ code_ += "";
+ code_ += "pub struct {{STRUCT_NAME}}<'a> {";
+ code_ += " pub _tab: flatbuffers::Table<'a>,";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}}<'a> {";
+ code_ += " type Inner = {{STRUCT_NAME}}<'a>;";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " Self {";
+ code_ += " _tab: flatbuffers::Table { buf: buf, loc: loc },";
+ code_ += " }";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl<'a> {{STRUCT_NAME}}<'a> {";
+ code_ += " #[inline]";
+ code_ += " pub fn init_from_table(table: flatbuffers::Table<'a>) -> "
+ "Self {";
+ code_ += " {{STRUCT_NAME}} {";
+ code_ += " _tab: table,";
+ code_ += " }";
+ code_ += " }";
+
+ // Generate a convenient create* function that uses the above builder
+ // to create a table in one function call.
+ code_.SetValue("MAYBE_US",
+ struct_def.fields.vec.size() == 0 ? "_" : "");
+ code_.SetValue("MAYBE_LT",
+ TableBuilderArgsNeedsLifetime(struct_def) ? "<'args>" : "");
+ code_ += " #[allow(unused_mut)]";
+ code_ += " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(";
+ code_ += " _fbb: "
+ "&'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,";
+ code_ += " {{MAYBE_US}}args: &'args {{STRUCT_NAME}}Args{{MAYBE_LT}})"
+ " -> flatbuffers::WIPOffset<{{STRUCT_NAME}}<'bldr>> {";
+
+ code_ += " let mut builder = {{STRUCT_NAME}}Builder::new(_fbb);";
+ for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
+ size; size /= 2) {
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ const auto &field = **it;
+ // TODO(rw): fully understand this sortbysize usage
+ if (!field.deprecated && (!struct_def.sortbysize ||
+ size == SizeOf(field.value.type.base_type))) {
+ code_.SetValue("FIELD_NAME", Name(field));
+ if (TableFieldReturnsOption(field.value.type)) {
+ code_ += " if let Some(x) = args.{{FIELD_NAME}} "
+ "{ builder.add_{{FIELD_NAME}}(x); }";
+ } else {
+ code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});";
+ }
+ }
+ }
+ }
+ code_ += " builder.finish()";
+ code_ += " }";
+ code_ += "";
+
+ // Generate field id constants.
+ if (struct_def.fields.vec.size() > 0) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
+ code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
+ code_ += " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = "
+ "{{OFFSET_VALUE}};";
+ }
+ code_ += "";
+ }
+
+ // Generate the accessors. Each has one of two forms:
+ //
+ // If a value can be None:
+ // pub fn name(&'a self) -> Option {
+ // self._tab.get::(offset, defaultval)
+ // }
+ //
+ // If a value is always Some:
+ // pub fn name(&'a self) -> user_facing_type {
+ // self._tab.get::(offset, defaultval).unwrap()
+ // }
+ const auto offset_prefix = Name(struct_def);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("RETURN_TYPE",
+ GenTableAccessorFuncReturnType(field, "'a"));
+ code_.SetValue("FUNC_BODY",
+ GenTableAccessorFuncBody(field, "'a", offset_prefix));
+
+ GenComment(field.doc_comment, " ");
+ code_ += " #[inline]";
+ code_ += " pub fn {{FIELD_NAME}}(&'a self) -> {{RETURN_TYPE}} {";
+ code_ += " {{FUNC_BODY}}";
+ code_ += " }";
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) {
+ GenKeyFieldMethods(field);
+ }
+
+ // Generate a nested flatbuffer field, if applicable.
+ auto nested = field.attributes.Lookup("nested_flatbuffer");
+ if (nested) {
+ std::string qualified_name = nested->constant;
+ auto nested_root = parser_.LookupStruct(nested->constant);
+ if (nested_root == nullptr) {
+ qualified_name = parser_.current_namespace_->GetFullyQualifiedName(
+ nested->constant);
+ nested_root = parser_.LookupStruct(qualified_name);
+ }
+ FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser.
+ (void)nested_root;
+
+ code_.SetValue("OFFSET_NAME",
+ offset_prefix + "::" + GetFieldOffsetName(field));
+ code_ += " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> "
+ " Option<{{STRUCT_NAME}}<'a>> {";
+ code_ += " match self.{{FIELD_NAME}}() {";
+ code_ += " None => { None }";
+ code_ += " Some(data) => {";
+ code_ += " use self::flatbuffers::Follow;";
+ code_ += " Some(>>::follow(data, 0))";
+ code_ += " },";
+ code_ += " }";
+ code_ += " }";
+ }
+ }
+
+ // Explicit specializations for union accessors
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated || field.value.type.base_type != BASE_TYPE_UNION) {
+ continue;
+ }
+
+ auto u = field.value.type.enum_def;
+ if (u->uses_type_aliases) continue;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+
+ for (auto u_it = u->vals.vec.begin(); u_it != u->vals.vec.end(); ++u_it) {
+ auto &ev = **u_it;
+ if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
+
+ auto table_init_type = WrapInNameSpace(
+ ev.union_type.struct_def->defined_namespace,
+ ev.union_type.struct_def->name);
+
+ code_.SetValue("U_ELEMENT_ENUM_TYPE",
+ WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev)));
+ code_.SetValue("U_ELEMENT_TABLE_TYPE", table_init_type);
+ code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(ev)));
+
+ code_ += " #[inline]";
+ code_ += " #[allow(non_snake_case)]";
+ code_ += " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&'a self) -> "
+ "Option<{{U_ELEMENT_TABLE_TYPE}}> {";
+ code_ += " if self.{{FIELD_NAME}}_type() == {{U_ELEMENT_ENUM_TYPE}} {";
+ code_ += " self.{{FIELD_NAME}}().map(|u| "
+ "{{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))";
+ code_ += " } else {";
+ code_ += " None";
+ code_ += " }";
+ code_ += " }";
+ code_ += "";
+ }
+ }
+
+ code_ += "}"; // End of table impl.
+ code_ += "";
+
+ // Generate an args struct:
+ code_.SetValue("MAYBE_LT",
+ TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : "");
+ code_ += "pub struct {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("PARAM_NAME", Name(field));
+ code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a "));
+ code_ += " pub {{PARAM_NAME}}: {{PARAM_TYPE}},";
+ }
+ }
+ code_ += "}";
+
+ // Generate an impl of Default for the *Args type:
+ code_ += "impl<'a> Default for {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
+ code_ += " #[inline]";
+ code_ += " fn default() -> Self {";
+ code_ += " {{STRUCT_NAME}}Args {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("PARAM_VALUE", TableBuilderArgsDefaultValue(field));
+ code_.SetValue("REQ", field.required ? " // required field" : "");
+ code_.SetValue("PARAM_NAME", Name(field));
+ code_ += " {{PARAM_NAME}}: {{PARAM_VALUE}},{{REQ}}";
+ }
+ }
+ code_ += " }";
+ code_ += " }";
+ code_ += "}";
+
+ // Generate a builder struct:
+ code_ += "pub struct {{STRUCT_NAME}}Builder<'a: 'b, 'b> {";
+ code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ += " start_: flatbuffers::WIPOffset<"
+ "flatbuffers::TableUnfinishedWIPOffset>,";
+ code_ += "}";
+
+ // Generate builder functions:
+ code_ += "impl<'a: 'b, 'b> {{STRUCT_NAME}}Builder<'a, 'b> {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ const bool is_scalar = IsScalar(field.value.type.base_type);
+
+ std::string offset = GetFieldOffsetName(field);
+ std::string name = Name(field);
+ std::string value = GetDefaultScalarValue(field);
+
+ // Generate functions to add data, which take one of two forms.
+ //
+ // If a value has a default:
+ // fn add_x(x_: type) {
+ // fbb_.push_slot::(offset, x_, Some(default));
+ // }
+ //
+ // If a value does not have a default:
+ // fn add_x(x_: type) {
+ // fbb_.push_slot_always::(offset, x_);
+ // }
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_OFFSET", Name(struct_def) + "::" + offset);
+ code_.SetValue("FIELD_TYPE", TableBuilderArgsAddFuncType(field, "'b "));
+ code_.SetValue("FUNC_BODY", TableBuilderArgsAddFuncBody(field));
+ code_ += " #[inline]";
+ code_ += " pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: "
+ "{{FIELD_TYPE}}) {";
+ if (is_scalar) {
+ code_.SetValue("FIELD_DEFAULT_VALUE",
+ TableBuilderAddFuncDefaultValue(field));
+ code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, "
+ "{{FIELD_DEFAULT_VALUE}});";
+ } else {
+ code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});";
+ }
+ code_ += " }";
+ }
+ }
+
+ // Struct initializer (all fields required);
+ code_ += " #[inline]";
+ code_ +=
+ " pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> "
+ "{{STRUCT_NAME}}Builder<'a, 'b> {";
+ code_.SetValue("NUM_FIELDS", NumToString(struct_def.fields.vec.size()));
+ code_ += " let start = _fbb.start_table();";
+ code_ += " {{STRUCT_NAME}}Builder {";
+ code_ += " fbb_: _fbb,";
+ code_ += " start_: start,";
+ code_ += " }";
+ code_ += " }";
+
+ // finish() function.
+ code_ += " #[inline]";
+ code_ += " pub fn finish(self) -> "
+ "flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>> {";
+ code_ += " let o = self.fbb_.end_table(self.start_);";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code_.SetValue("FIELD_NAME", MakeSnakeCase(Name(field)));
+ code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
+ code_ += " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}},"
+ "\"{{FIELD_NAME}}\");";
+ }
+ }
+ code_ += " flatbuffers::WIPOffset::new(o.value())";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ }
+
+ // Generate functions to compare tables and structs by key. This function
+ // must only be called if the field key is defined.
+ void GenKeyFieldMethods(const FieldDef &field) {
+ FLATBUFFERS_ASSERT(field.key);
+ const bool is_string = (field.value.type.base_type == BASE_TYPE_STRING);
+
+ if (is_string) {
+ code_.SetValue("KEY_TYPE", "Option<&str>");
+ } else {
+ FLATBUFFERS_ASSERT(IsScalar(field.value.type.base_type));
+ auto type = GetTypeBasic(field.value.type);
+ if (parser_.opts.scoped_enums && field.value.type.enum_def &&
+ IsScalar(field.value.type.base_type)) {
+ type = GetTypeGet(field.value.type);
+ }
+ code_.SetValue("KEY_TYPE", type);
+ }
+
+ code_ += " #[inline]";
+ code_ += " pub fn key_compare_less_than(&self, o: &{{STRUCT_NAME}}) -> "
+ " bool {";
+ code_ += " self.{{FIELD_NAME}}() < o.{{FIELD_NAME}}()";
+ code_ += " }";
+ code_ += "";
+ code_ += " #[inline]";
+ code_ += " pub fn key_compare_with_value(&self, val: {{KEY_TYPE}}) -> "
+ " ::std::cmp::Ordering {";
+ code_ += " let key = self.{{FIELD_NAME}}();";
+ code_ += " key.cmp(&val)";
+ code_ += " }";
+ }
+
+ // Generate functions for accessing the root table object. This function
+ // must only be called if the root table is defined.
+ void GenRootTableFuncs(const StructDef &struct_def) {
+ FLATBUFFERS_ASSERT(parser_.root_struct_def_ && "root table not defined");
+ auto name = Name(struct_def);
+
+ code_.SetValue("STRUCT_NAME", name);
+ code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(name));
+ code_.SetValue("STRUCT_NAME_CAPS", MakeUpper(MakeSnakeCase(name)));
+
+ // The root datatype accessors:
+ code_ += "#[inline]";
+ code_ +=
+ "pub fn get_root_as_{{STRUCT_NAME_SNAKECASE}}<'a>(buf: &'a [u8])"
+ " -> {{STRUCT_NAME}}<'a> {";
+ code_ += " flatbuffers::get_root::<{{STRUCT_NAME}}<'a>>(buf)";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "#[inline]";
+ code_ += "pub fn get_size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}"
+ "<'a>(buf: &'a [u8]) -> {{STRUCT_NAME}}<'a> {";
+ code_ += " flatbuffers::get_size_prefixed_root::<{{STRUCT_NAME}}<'a>>"
+ "(buf)";
+ code_ += "}";
+ code_ += "";
+
+ if (parser_.file_identifier_.length()) {
+ // Declare the identifier
+ code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &'static str\\";
+ code_ += " = \"" + parser_.file_identifier_ + "\";";
+ code_ += "";
+
+ // Check if a buffer has the identifier.
+ code_ += "#[inline]";
+ code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_buffer_has_identifier\\";
+ code_ += "(buf: &[u8]) -> bool {";
+ code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
+ code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, false);";
+ code_ += "}";
+ code_ += "";
+ code_ += "#[inline]";
+ code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_size_prefixed\\";
+ code_ += "_buffer_has_identifier(buf: &[u8]) -> bool {";
+ code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
+ code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, true);";
+ code_ += "}";
+ code_ += "";
+ }
+
+ if (parser_.file_extension_.length()) {
+ // Return the extension
+ code_ += "pub const {{STRUCT_NAME_CAPS}}_EXTENSION: &'static str = \\";
+ code_ += "\"" + parser_.file_extension_ + "\";";
+ code_ += "";
+ }
+
+ // Finish a buffer with a given root object:
+ code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
+ code_ += "#[inline]";
+ code_ += "pub fn finish_{{STRUCT_NAME_SNAKECASE}}_buffer<'a, 'b>(";
+ code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ += " root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
+ if (parser_.file_identifier_.length()) {
+ code_ += " fbb.finish(root, Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
+ } else {
+ code_ += " fbb.finish(root, None);";
+ }
+ code_ += "}";
+ code_ += "";
+ code_ += "#[inline]";
+ code_ += "pub fn finish_size_prefixed_{{STRUCT_NAME_SNAKECASE}}_buffer"
+ "<'a, 'b>("
+ "fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, "
+ "root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
+ if (parser_.file_identifier_.length()) {
+ code_ += " fbb.finish_size_prefixed(root, "
+ "Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
+ } else {
+ code_ += " fbb.finish_size_prefixed(root, None);";
+ }
+ code_ += "}";
+ }
+
+ static void GenPadding(
+ const FieldDef &field, std::string *code_ptr, int *id,
+ const std::function &f) {
+ if (field.padding) {
+ for (int i = 0; i < 4; i++) {
+ if (static_cast(field.padding) & (1 << i)) {
+ f((1 << i) * 8, code_ptr, id);
+ }
+ }
+ assert(!(field.padding & ~0xF));
+ }
+ }
+
+ static void PaddingDefinition(int bits, std::string *code_ptr, int *id) {
+ *code_ptr += " padding" + NumToString((*id)++) + "__: u" + \
+ NumToString(bits) + ",";
+ }
+
+ static void PaddingInitializer(int bits, std::string *code_ptr, int *id) {
+ (void)bits;
+ *code_ptr += "padding" + NumToString((*id)++) + "__: 0,";
+ }
+
+ // Generate an accessor struct with constructor for a flatbuffers struct.
+ void GenStruct(const StructDef &struct_def) {
+ // Generates manual padding and alignment.
+ // Variables are private because they contain little endian data on all
+ // platforms.
+ GenComment(struct_def.doc_comment);
+ code_.SetValue("ALIGN", NumToString(struct_def.minalign));
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+
+ code_ += "// struct {{STRUCT_NAME}}, aligned to {{ALIGN}}";
+ code_ += "#[repr(C, align({{ALIGN}}))]";
+
+ // PartialEq is useful to derive because we can correctly compare structs
+ // for equality by just comparing their underlying byte data. This doesn't
+ // hold for PartialOrd/Ord.
+ code_ += "#[derive(Clone, Copy, Debug, PartialEq)]";
+ code_ += "pub struct {{STRUCT_NAME}} {";
+
+ int padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ code_.SetValue("FIELD_TYPE", GetTypeGet(field.value.type));
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_ += " {{FIELD_NAME}}_: {{FIELD_TYPE}},";
+
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingDefinition);
+ code_ += padding;
+ }
+ }
+
+ code_ += "} // pub struct {{STRUCT_NAME}}";
+
+ // Generate impls for SafeSliceAccess (because all structs are endian-safe),
+ // Follow for the value type, Follow for the reference type, Push for the
+ // value type, and Push for the reference type.
+ code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_NAME}} {}";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}} {";
+ code_ += " type Inner = &'a {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " <&'a {{STRUCT_NAME}}>::follow(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for &'a {{STRUCT_NAME}} {";
+ code_ += " type Inner = &'a {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " flatbuffers::follow_cast_ref::<{{STRUCT_NAME}}>(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'b> flatbuffers::Push for {{STRUCT_NAME}} {";
+ code_ += " type Output = {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " let src = unsafe {";
+ code_ += " ::std::slice::from_raw_parts("
+ "self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
+ code_ += " };";
+ code_ += " dst.copy_from_slice(src);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'b> flatbuffers::Push for &'b {{STRUCT_NAME}} {";
+ code_ += " type Output = {{STRUCT_NAME}};";
+ code_ += "";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " let src = unsafe {";
+ code_ += " ::std::slice::from_raw_parts("
+ "*self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
+ code_ += " };";
+ code_ += " dst.copy_from_slice(src);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "";
+
+ // Generate a constructor that takes all fields as arguments.
+ code_ += "impl {{STRUCT_NAME}} {";
+ std::string arg_list;
+ std::string init_list;
+ padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ const auto member_name = Name(field) + "_";
+ const auto reference = StructMemberAccessNeedsCopy(field.value.type)
+ ? "" : "&'a ";
+ const auto arg_name = "_" + Name(field);
+ const auto arg_type = reference + GetTypeGet(field.value.type);
+
+ if (it != struct_def.fields.vec.begin()) {
+ arg_list += ", ";
+ }
+ arg_list += arg_name + ": ";
+ arg_list += arg_type;
+ init_list += " " + member_name;
+ if (StructMemberAccessNeedsCopy(field.value.type)) {
+ init_list += ": " + arg_name + ".to_little_endian(),\n";
+ } else {
+ init_list += ": *" + arg_name + ",\n";
+ }
+ }
+
+ code_.SetValue("ARG_LIST", arg_list);
+ code_.SetValue("INIT_LIST", init_list);
+ code_ += " pub fn new<'a>({{ARG_LIST}}) -> Self {";
+ code_ += " {{STRUCT_NAME}} {";
+ code_ += "{{INIT_LIST}}";
+ padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingInitializer);
+ code_ += " " + padding;
+ }
+ }
+ code_ += " }";
+ code_ += " }";
+
+ // Generate accessor methods for the struct.
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+
+ auto field_type = TableBuilderArgsAddFuncType(field, "'a");
+ auto member = "self." + Name(field) + "_";
+ auto value = StructMemberAccessNeedsCopy(field.value.type) ?
+ member + ".from_little_endian()" : member;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_TYPE", field_type);
+ code_.SetValue("FIELD_VALUE", value);
+ code_.SetValue("REF", IsStruct(field.value.type) ? "&" : "");
+
+ GenComment(field.doc_comment, " ");
+ code_ += " pub fn {{FIELD_NAME}}<'a>(&'a self) -> {{FIELD_TYPE}} {";
+ code_ += " {{REF}}{{FIELD_VALUE}}";
+ code_ += " }";
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) {
+ GenKeyFieldMethods(field);
+ }
+ }
+ code_ += "}";
+ code_ += "";
+ }
+
+ // Set up the correct namespace. This opens a namespace if the current
+ // namespace is different from the target namespace. This function
+ // closes and opens the namespaces only as necessary.
+ //
+ // The file must start and end with an empty (or null) namespace so that
+ // namespaces are properly opened and closed.
+ void SetNameSpace(const Namespace *ns) {
+ if (cur_name_space_ == ns) { return; }
+
+ // Compute the size of the longest common namespace prefix.
+ // If cur_name_space is A::B::C::D and ns is A::B::E::F::G,
+ // the common prefix is A::B:: and we have old_size = 4, new_size = 5
+ // and common_prefix_size = 2
+ size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0;
+ size_t new_size = ns ? ns->components.size() : 0;
+
+ size_t common_prefix_size = 0;
+ while (common_prefix_size < old_size && common_prefix_size < new_size &&
+ ns->components[common_prefix_size] ==
+ cur_name_space_->components[common_prefix_size]) {
+ common_prefix_size++;
+ }
+
+ // Close cur_name_space in reverse order to reach the common prefix.
+ // In the previous example, D then C are closed.
+ for (size_t j = old_size; j > common_prefix_size; --j) {
+ code_ += "} // pub mod " + cur_name_space_->components[j - 1];
+ }
+ if (old_size != common_prefix_size) { code_ += ""; }
+
+ // open namespace parts to reach the ns namespace
+ // in the previous example, E, then F, then G are opened
+ for (auto j = common_prefix_size; j != new_size; ++j) {
+ code_ += "pub mod " + MakeSnakeCase(ns->components[j]) + " {";
+ code_ += " #![allow(dead_code)]";
+ code_ += " #![allow(unused_imports)]";
+ code_ += "";
+ code_ += " use std::mem;";
+ code_ += " use std::cmp::Ordering;";
+ code_ += "";
+ code_ += " extern crate flatbuffers;";
+ code_ += " use self::flatbuffers::EndianScalar;";
+ }
+ if (new_size != common_prefix_size) { code_ += ""; }
+
+ cur_name_space_ = ns;
+ }
+};
+
+} // namespace rust
+
+bool GenerateRust(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ rust::RustGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string RustMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ std::string filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ std::string make_rule = GeneratedFileName(path, filebase) + ": ";
+
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ for (auto it = included_files.begin(); it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+} // namespace flatbuffers
+
+// TODO(rw): Generated code should import other generated files.
+// TODO(rw): Generated code should refer to namespaces in included files in a
+// way that makes them referrable.
+// TODO(rw): Generated code should indent according to nesting level.
+// TODO(rw): Generated code should generate endian-safe Debug impls.
+// TODO(rw): Generated code could use a Rust-only enum type to access unions,
+// instead of making the user use _type() to manually switch.
diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp
index 41d19125e..563f69013 100644
--- a/src/idl_gen_text.cpp
+++ b/src/idl_gen_text.cpp
@@ -131,7 +131,7 @@ bool Print(const void *val, Type type, int indent,
switch (type.base_type) {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
if (!PrintVector( \
*reinterpret_cast *>(val), \
@@ -223,7 +223,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table,
switch (fd.value.type.base_type) {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
if (!GenField(fd, table, struct_def.fixed, \
opts, indent + Indent(opts), _text)) { \
@@ -234,7 +234,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table,
#undef FLATBUFFERS_TD
// Generate drop-thru case statements for all pointer types:
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM:
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index 366a77be8..d10a2e7e9 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -30,7 +30,7 @@ const double kPi = 3.14159265358979323846;
const char *const kTypeNames[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
IDLTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -41,7 +41,7 @@ const char *const kTypeNames[] = {
const char kTypeSizes[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
sizeof(CTYPE),
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -217,7 +217,7 @@ static std::string TokenToString(int t) {
FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
#undef FLATBUFFERS_TOKEN
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
IDLTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -1077,7 +1077,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
switch (field_value.type.base_type) {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
builder_.Pad(field->padding); \
if (struct_def.fixed) { \
@@ -1094,7 +1094,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
#undef FLATBUFFERS_TD
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
builder_.Pad(field->padding); \
if (IsStruct(field->value.type)) { \
@@ -1176,7 +1176,7 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) {
switch (val.type.base_type) {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
else { \
diff --git a/tests/RustTest.sh b/tests/RustTest.sh
new file mode 100755
index 000000000..8388701ce
--- /dev/null
+++ b/tests/RustTest.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+set -ex
+#
+# Copyright 2018 Google Inc. All rights reserved.
+#
+# 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
+#
+# http://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.
+
+cd ./rust_usage_test
+cargo test $1
+TEST_RESULT=$?
+if [[ $TEST_RESULT == 0 ]]; then
+ echo "OK: Rust tests passed."
+else
+ echo "KO: Rust tests failed."
+ exit 1
+fi
+
+cargo bench
diff --git a/tests/generate_code.bat b/tests/generate_code.bat
index eaadc5ab9..b233c1447 100644
--- a/tests/generate_code.bat
+++ b/tests/generate_code.bat
@@ -15,8 +15,8 @@
set buildtype=Release
if "%1"=="-b" set buildtype=%2
-..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lobster --lua --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
-..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lobster --lua --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
+..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lobster --lua --js --rust --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
+..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lobster --lua --js --rust --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
..\%buildtype%\flatc.exe --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
..\%buildtype%\flatc.exe -b --schema --bfbs-comments -I include_test monster_test.fbs
..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs
@@ -24,4 +24,4 @@ cd ../samples
..\%buildtype%\flatc.exe --cpp --lobster --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr monster.fbs
cd ../reflection
-cd ../tests
\ No newline at end of file
+cd ../tests
diff --git a/tests/generate_code.sh b/tests/generate_code.sh
index 8e060dbd7..f25366b0e 100755
--- a/tests/generate_code.sh
+++ b/tests/generate_code.sh
@@ -14,8 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-../flatc --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
-../flatc --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
+../flatc --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
+../flatc --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
../flatc --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
../flatc -b --schema --bfbs-comments -I include_test monster_test.fbs
../flatc --jsonschema --schema -I include_test monster_test.fbs
diff --git a/tests/monster_test_generated.rs b/tests/monster_test_generated.rs
new file mode 100644
index 000000000..aa236a20c
--- /dev/null
+++ b/tests/monster_test_generated.rs
@@ -0,0 +1,1644 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+pub mod my_game {
+ #![allow(dead_code)]
+ #![allow(unused_imports)]
+
+ use std::mem;
+ use std::cmp::Ordering;
+
+ extern crate flatbuffers;
+ use self::flatbuffers::EndianScalar;
+
+pub enum InParentNamespaceOffset {}
+#[derive(Copy, Clone, Debug, PartialEq)]
+
+pub struct InParentNamespace<'a> {
+ pub _tab: flatbuffers::Table<'a>,
+}
+
+impl<'a> flatbuffers::Follow<'a> for InParentNamespace<'a> {
+ type Inner = InParentNamespace<'a>;
+ #[inline]
+ fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Self {
+ _tab: flatbuffers::Table { buf: buf, loc: loc },
+ }
+ }
+}
+
+impl<'a> InParentNamespace<'a> {
+ #[inline]
+ pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
+ InParentNamespace {
+ _tab: table,
+ }
+ }
+ #[allow(unused_mut)]
+ pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
+ _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
+ _args: &'args InParentNamespaceArgs) -> flatbuffers::WIPOffset> {
+ let mut builder = InParentNamespaceBuilder::new(_fbb);
+ builder.finish()
+ }
+
+}
+
+pub struct InParentNamespaceArgs {
+}
+impl<'a> Default for InParentNamespaceArgs {
+ #[inline]
+ fn default() -> Self {
+ InParentNamespaceArgs {
+ }
+ }
+}
+pub struct InParentNamespaceBuilder<'a: 'b, 'b> {
+ fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ start_: flatbuffers::WIPOffset