Port FlatBuffers to Rust (#4898)

This is a port of FlatBuffers to Rust. It provides code generation and a
runtime library derived from the C++ implementation. It utilizes the
Rust type system to provide safe and fast traversal of FlatBuffers data.

There are 188 tests, including many fuzz tests of roundtrips for various
serialization scenarios. Initial benchmarks indicate that the canonical
example payload can be written in ~700ns, and traversed in ~100ns.

Rustaceans may be interested in the Follow, Push, and SafeSliceAccess
traits. These traits lift traversals, reads, writes, and slice accesses
into the type system, providing abstraction with no runtime penalty.
This commit is contained in:
Robert
2018-09-02 17:05:50 -07:00
committed by rw
parent e7578548a5
commit 3c54fd964b
46 changed files with 9998 additions and 61 deletions

View File

@@ -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<void>, int, int, StringOffset, int) \
TD(VECTOR, "", Offset<void>, int, int, VectorOffset, int) \
TD(STRUCT, "", Offset<void>, int, int, int, int) \
TD(UNION, "", Offset<void>, int, int, int, int)
TD(STRING, "string", Offset<void>, int, int, StringOffset, int, unused) \
TD(VECTOR, "", Offset<void>, int, int, VectorOffset, int, unused) \
TD(STRUCT, "", Offset<void>, int, int, int, int, unused) \
TD(UNION, "", Offset<void>, 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,