diff --git a/.gitignore b/.gitignore index 07531061d89..06ab8c60580 100755 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ *.suo *.keystore **/bin/** +!rust/src/bin/** **/gen/** **/libs/** **/obj/** @@ -44,6 +45,7 @@ grpctest grpctest.exe snapshot.sh tests/go_gen +tests/rust_gen tests/monsterdata_java_wire.mon tests/monsterdata_go_wire.mon tests/monsterdata_javascript_wire.mon @@ -79,3 +81,4 @@ android/build/ samples/android/.externalNativeBuild/ samples/android/.gradle/ samples/android/build/ +.editorconfig diff --git a/CMakeLists.txt b/CMakeLists.txt index e8353252784..cfe66327cf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_fbs.cpp src/idl_gen_grpc.cpp src/idl_gen_json_schema.cpp + src/idl_gen_rust.cpp src/flatc.cpp src/flatc_main.cpp grpc/src/compiler/schema_interface.h diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 4d158d12903..15309664687 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -40,24 +40,24 @@ namespace flatbuffers { // Additionally, Parser::ParseType assumes bool..string is a contiguous range // of type tokens. #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", int8_t, byte, int8, sbyte, int8) \ - TD(UCHAR, "ubyte", "uint8", uint8_t, byte, byte, byte, uint8) \ - TD(SHORT, "short", "int16", int16_t, short, int16, short, int16) \ - TD(USHORT, "ushort", "uint16", uint16_t, short, uint16, ushort, uint16) \ - TD(INT, "int", "int32", int32_t, int, int32, int, int32) \ - TD(UINT, "uint", "uint32", uint32_t, int, uint32, uint, uint32) \ - TD(LONG, "long", "int64", int64_t, long, int64, long, int64) \ - TD(ULONG, "ulong", "uint64", uint64_t, long, uint64, ulong, uint64) /* end int */ \ - TD(FLOAT, "float", "float32", float, float, float32, float, float32) /* begin float */ \ - TD(DOUBLE, "double", "float64", 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", int8_t, byte, int8, sbyte, int8, i8) \ + TD(UCHAR, "ubyte", "uint8", uint8_t, byte, byte, byte, uint8, u8) \ + TD(SHORT, "short", "int16", int16_t, short, int16, short, int16, i16) \ + TD(USHORT, "ushort", "uint16", uint16_t, short, uint16, ushort, uint16, u16) \ + TD(INT, "int", "int32", int32_t, int, int32, int, int32, i32) \ + TD(UINT, "uint", "uint32", uint32_t, int, uint32, uint, uint32, u32) \ + TD(LONG, "long", "int64", int64_t, long, int64, long, int64, i64) \ + TD(ULONG, "ulong", "uint64", uint64_t, long, uint64, ulong, uint64, u64) /* end int */ \ + TD(FLOAT, "float", "float32", float, float, float32, float, float32, f32) /* begin float */ \ + TD(DOUBLE, "double", "float64", 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, i32) \ + TD(VECTOR, "", "", Offset, int, int, VectorOffset, int, i32) \ + TD(STRUCT, "", "", Offset, int, int, int, int, i32) \ + TD(UNION, "", "", Offset, int, int, int, int, i32) // The fields are: // - enum @@ -68,12 +68,13 @@ 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, ALIASTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ case BASE_TYPE_ ## ENUM: \ // do something specific to CTYPE here FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) @@ -90,13 +91,13 @@ switch (type) { __extension__ // Stop GCC complaining about trailing comma with -Wpendantic. #endif enum BaseType { - #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ BASE_TYPE_ ## ENUM, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD }; -#define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ +#define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, 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) @@ -207,7 +208,8 @@ struct Namespace { // With max_components you can request less than the number of components // the current namespace has. std::string GetFullyQualifiedName(const std::string &name, - size_t max_components = 1000) const; + size_t max_components = 1000, + const std::string &sep = ".") const; }; // Base class for all definition types (fields, structs_, enums_). @@ -375,6 +377,7 @@ struct IDLOptions { std::string go_namespace; bool reexport_ts_modules; bool protobuf_ascii_alike; + bool strict_rust; // Possible options for the more general generator below. enum Language { @@ -389,6 +392,7 @@ struct IDLOptions { kBinary = 1 << 8, kTs = 1 << 9, kJsonSchema = 1 << 10, + kRust = 1 << 11, kMAX }; @@ -422,6 +426,7 @@ struct IDLOptions { skip_flatbuffers_import(false), reexport_ts_modules(true), protobuf_ascii_alike(false), + strict_rust(false), lang(IDLOptions::kJava), lang_to_generate(0) {} }; @@ -765,6 +770,13 @@ extern bool GenerateGeneral(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 a schema file from the internal representation, useful after // parsing a .proto schema. extern std::string GenerateFBS(const Parser &parser, diff --git a/readme.md b/readme.md index a7c0e93fd35..a86b5f1713a 100755 --- a/readme.md +++ b/readme.md @@ -25,7 +25,7 @@ unpacking/parsing it first, while still having great forwards/backwards compatib * JavaScript * PHP * Python - +* Rust *and many more in progress...* ## Contribution diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 00000000000..49b38e5a134 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,12 @@ +[root] +name = "flatbuffers" +version = "0.3.0" +dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 00000000000..50901d28236 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "flatbuffers" +version = "0.3.0" +authors = ["Joseph Dunne "] +description = "Memory Efficient Serialization Library. Flatbuffers runtime for Rust." +keywords = ["flatbuffers", "serialization", "deserialization"] +readme = "../readme.md" +license = "Apache-2.0" +homepage = "http://google.github.io/flatbuffers/index.html" +repository = "https://github.com/josephDunne/flatbuffers" + +[lib] +name = "flatbuffers" + +[[bin]] +name = "test" + +[dependencies] +byteorder = "0.5.*" + +[features] +default = [] +# Test the idl_gen_rust cpp code. +test_idl_gen = [] + diff --git a/rust/macros/Cargo.toml b/rust/macros/Cargo.toml new file mode 100644 index 00000000000..986c6a52df8 --- /dev/null +++ b/rust/macros/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "flatbuffers_macros" +version = "0.1.0" +authors = ["Joseph Dunne "] +license = "Apache-2.0" +description = "Macros for generating flatbuffer objects" +keywords = ["flatbuffers"] + + +[lib] +plugin = true + +[dependencies] +regex = "*" + +[dev-dependencies] +compiletest_rs = "*" + +[dev-dependencies.flatbuffers] +path = "../" +version = ">=0.0" + diff --git a/rust/macros/src/lib.rs b/rust/macros/src/lib.rs new file mode 100644 index 00000000000..2709791fb73 --- /dev/null +++ b/rust/macros/src/lib.rs @@ -0,0 +1,385 @@ +#![crate_type = "dylib"] +#![feature(quote, plugin_registrar, rustc_private)] +extern crate rustc_plugin; +extern crate syntax; +extern crate regex; + +use syntax::ast; +use syntax::codemap; +use syntax::print; +use syntax::ext::base::{ExtCtxt, MacResult, DummyResult}; +use syntax::parse::token; +use std::ops::Deref; +use rustc_plugin::Registry; + +mod table; +mod utils; +mod union; + +use self::utils::*; + +const EXPECTED_IDENT_TABLE_ETC: &'static str = "Expected one of 'Table', 'Struct', 'Enum' or 'Union'"; +const EXPECTED_FAT_ARROW: &'static str = "Expected '=>'"; +const EXPECTED_COLON: &'static str = "Expected ':'"; +const EXPECTED_TABLE_FIELDS: &'static str = "Expected a list of fields"; +const EXPECTED_FIELD_DEF: &'static str = "Expected field"; +const INVALID_FIELD_DEF: &'static str = "Invalid field definition"; +const EXPECTED_SLOT_INT: &'static str = "Slot must be an integer"; +const UNKNOWN_ATTRIBUTE: &'static str = "Unknown attribute"; +const EXPECTED_SIZE_INT: &'static str = "Size must be an integer"; +const EXPECTED_ENUM_ITEMS: &'static str = "Expected Enum items"; +const EXPECTED_UNION_ITEMS: &'static str = "Expected Union items"; +const EXPECTED_ENUM_TYPE: &'static str = "Expected one of ubyte, byte, ushort, short, uint, int, ulong, or long"; + + +#[plugin_registrar] +pub fn plugin_registrar(registry: &mut Registry) { + registry.register_macro("flatbuffers_object", expand) +} + +fn expand(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree]) -> Box { + if let Ok(res) = expand_impl(cx, sp, ast) { + res + } else { + DummyResult::any(sp) + } +} + +fn expand_impl(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree]) -> Result, ()> { + if ast.len() == 0 { + cx.span_err(sp, EXPECTED_IDENT_TABLE_ETC); + return Err(()); + } + let ident = try!(expect_ident(cx, sp, &ast[0], &["Table", "Struct", "Enum", "Union"], EXPECTED_IDENT_TABLE_ETC)); + if ast.len() < 2 { + cx.span_err(sp, EXPECTED_FAT_ARROW); + return Err(()); + } + try!(consume_fat_arrow(cx, sp, &ast[1], EXPECTED_FAT_ARROW)); + let ast = &ast[2..]; + match try!(object_type(ident)) { + ObjectType::Table => expand_table_object(cx, sp, ast), + ObjectType::Struct => expand_struct_object(cx, sp, ast), + ObjectType::Enum => expand_enum_object(cx, sp, ast), + ObjectType::Union => expand_union_object(cx, sp, ast), + } +} + + +fn object_type(ident: ast::Ident) -> Result { + match ident.name.as_str().deref() { + "Table" => Ok(ObjectType::Table), + "Struct" => Ok(ObjectType::Struct), + "Enum" => Ok(ObjectType::Enum), + "Union" => Ok(ObjectType::Union), + _ => Err(()) + } +} + +fn expand_table_object(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree]) -> Result, ()> { + let name = try!(get_name(cx, sp, ast, &format!("Expected a name for the {}", "Table"))); + let attributes = try!(get_attributes(cx, sp, &ast[1..])); + let ast = if attributes.len() > 0 { + &ast[2..] + } else { + &ast[1..] + }; + let fields = try!(get_obj_fields(cx, sp, ast, EXPECTED_TABLE_FIELDS, ObjectType::Table)); + table::build_table_items(cx, name, fields, attributes) +} + +fn expand_struct_object(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree]) -> Result, ()> { + let name = try!(get_name(cx, sp, ast, &format!("Expected a name for the {}", "Struct"))); + let attributes = try!(get_attributes(cx, sp, &ast[1..])); + let ast = if attributes.len() > 0 { + &ast[2..] + } else { + &ast[1..] + }; + let fields = try!(get_obj_fields(cx, sp, ast, EXPECTED_TABLE_FIELDS, ObjectType::Struct)); + table::build_struct_items(cx, name, fields, attributes) +} + +fn expand_enum_object(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree]) -> Result, ()> { + let name = try!(get_name(cx, sp, ast, &format!("Expected a name for the {}", "Enum"))); + let items = try!(get_enum_items(cx, sp, &ast[1], EXPECTED_ENUM_ITEMS)); + let ty = try!(get_enum_repr(cx, &ast[3], EXPECTED_ENUM_TYPE)); + union::build_simple_enum(cx, name, ty, items) +} + +fn expand_union_object(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree]) -> Result, ()> { + let name = try!(get_name(cx, sp, ast, &format!("Expected a name for the {}", "Union"))); + let items = try!(get_enum_items(cx, sp, &ast[1], EXPECTED_UNION_ITEMS)); + union::build_union(cx, name, items) +} + +fn get_name(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree], msg: &str) -> Result { + if ast.len() > 0 { + match ast[0] { + ast::TokenTree::Token(_, token::Token::Ident(ident)) => { + return Ok(ident.name.as_str()) + } + _ => {} + } + } + cx.span_err(sp, msg); + Err(()) +} + +fn get_attributes(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree]) -> Result, ()> { + if ast.len() == 0 { + return Ok(vec![]) + } + match ast[0] { + ast::TokenTree::Delimited(_, ref delemented) => { + if delemented.delim == token::DelimToken::Paren { + return get_obj_attributes(cx, sp, delemented.as_ref()) + } + } + _ => {} + } + return Ok(vec![]) +} + +fn get_obj_attributes(cx: &mut ExtCtxt, sp: codemap::Span, ast: &ast::Delimited) -> Result, ()> { + let tts = &ast.tts; + let mut res = Vec::new(); + if tts.len() == 0 { + return Ok(res); + } + let mut i = 0; + loop { + if tts.len() < i + 3 { + cx.span_err(ast.open_span, INVALID_FIELD_DEF); + return Err(()) + } + let attribute = try!(expect_ident(cx, tts[i].get_span(), &tts[i], &["size"], EXPECTED_FIELD_DEF)); + let attribute = attribute.name.as_str().deref().to_string(); + try!(consume_colon(cx, sp, &tts[i+1], EXPECTED_COLON)); + match &*attribute { + "size" => { + let lit = try!(get_lit(cx, tts[i+2].get_span(), &tts[i+2], EXPECTED_SIZE_INT)); + if let token::Lit::Integer(size) = lit { + res.push( ObjAttribute{ + name: attribute, + value: size.as_str().deref().to_string() + } ) + } else { + cx.span_err(tts[i+2].get_span(), EXPECTED_SIZE_INT); + return Err(()) + } + } + _ => { + cx.span_warn(ast.open_span, UNKNOWN_ATTRIBUTE); + } + } + if tts.len() >= i + 4 && maybe_comma(&tts[i+3]) { + i += 4; + } else { + i += 3; + } + if tts.len() <= i { + break; + } + } + Ok(res) +} + +fn get_obj_fields(cx: &mut ExtCtxt, sp: codemap::Span, ast: &[ast::TokenTree], msg: &str, objty: ObjectType) -> Result, ()> { + if ast.len() == 0 { + cx.span_warn(sp, msg); + return Err(()) + } + match ast[0] { + ast::TokenTree::Delimited(_, ref delemented) => { + return get_obj_fields_impl(cx, sp, delemented.as_ref(), objty); + } + _ => {} + } + cx.span_err(sp, msg); + Err(()) +} + +fn get_obj_fields_impl(cx: &mut ExtCtxt, sp: codemap::Span, ast: &ast::Delimited, objty: ObjectType) -> Result, ()> { + let tts = &ast.tts; + let mut res = Vec::new(); + if tts.len() == 0 { + return Ok(res); + } + let mut i = 0; + loop { + if tts.len() < i + 3 { + cx.span_err(ast.open_span, INVALID_FIELD_DEF); + return Err(()) + } + try!(expect_ident(cx, tts[i].get_span(), &tts[i], &["field"], EXPECTED_FIELD_DEF)); + try!(consume_fat_arrow(cx, sp, &tts[i+1], EXPECTED_FAT_ARROW)); + let def = try!(get_field_def(cx, &tts[i+2], &objty)); + res.push(def); + if tts.len() >= i + 4 && maybe_comma(&tts[i+3]) { + i += 4; + } else { + i += 3; + } + if tts.len() <= i { + break; + } + } + Ok(res) +} + +fn get_field_def(cx: &mut ExtCtxt, ast: &ast::TokenTree, objty: &ObjectType) -> Result { + if let &ast::TokenTree::Delimited(_, ref delemented) = ast { + let fieldef_str = print::pprust::tts_to_string(&delemented.tts); + return parse_field_names(cx, ast.get_span(), fieldef_str, objty) + } + cx.span_err(ast.get_span(), INVALID_FIELD_DEF); + Err(()) +} + +fn parse_field_names(cx: &mut ExtCtxt, sp: codemap::Span, ast: String, objty: &ObjectType) -> Result { + if ast.len() == 0 { + cx.span_err(sp, INVALID_FIELD_DEF); + return Err(()) + } + let def = FieldDef { + name: "".to_string(), + ty: Default::default(), + slot: "".to_string(), + default: "".to_string(), + comments: Vec::new(), + padding: None + }; + let mut required = if *objty == ObjectType::Table { + vec!["default", "name", "slot", "typeOf"] + } else { + vec!["name", "slot", "typeOf"] + }; + let list = required.clone(); + let iter = ast.split(','); + let mut error = false; + let def = iter.fold(def, |mut acc, attr| { + if error { return acc } + let mut iter = attr.split('='); + match iter.next().unwrap().trim() { + "name" => { + acc.name = iter.next().unwrap().trim().to_string(); + remove_required(&mut required, "name"); + } + "typeOf" => { + let ty = map_ty(iter.next().unwrap().trim().to_string()); + if ty.is_none() { + cx.span_err(sp, INVALID_FIELD_DEF); + error = true; + return acc; + } + remove_required(&mut required, "typeOf"); + acc.ty = ty.unwrap(); + } + "default" => { + acc.default = iter.next().unwrap().trim().to_string(); + remove_required(&mut required, "default"); + } + "slot" => { + let slot = iter.next().unwrap().trim().to_string(); + if slot.parse::().is_err() { + cx.span_err(sp, EXPECTED_SLOT_INT); + error = true; + return acc + } + acc.slot = slot; + remove_required(&mut required, "slot"); + } + "comment" => { + let comment = iter.next().unwrap().trim().to_string(); + acc.comments.push(comment); + } + "padding" => { + let padding = iter.next().unwrap().trim().to_string(); + acc.padding = Some(padding); + } + a => { + error = true; + cx.span_err(sp, &format!("{}: {}", UNKNOWN_ATTRIBUTE, a)); + } + } + acc + }); + if error { + return Err(()) + } + if required.len() > 0 { + fn tokens_to_string(tokens: &[&str]) -> String { + let mut i = tokens.iter(); + let b = i.next() + .map_or("".to_string(), |t| t.to_string()); + i.enumerate().fold(b, |mut b, (i, ref a)| { + if tokens.len() > 2 && i == tokens.len() - 2 { + b.push_str(", and "); + } else if tokens.len() == 2 && i == tokens.len() - 2 { + b.push_str(" and "); + } else { + b.push_str(", "); + } + b.push_str(&a.to_string()); + b + }) + } + let msg = format!("Expected {}, missing {}", + tokens_to_string(&list), + tokens_to_string(&required)); + cx.span_err(sp, &msg); + return Err(()) + } + Ok(def) +} + + +fn remove_required(required: &mut Vec<&str>, found: &str) { + if let Ok(i) = required.binary_search(&&found) { + required.remove(i); + } +} + +fn get_enum_items(cx: &mut ExtCtxt, sp: codemap::Span, ast: &ast::TokenTree, msg: &str) -> Result,()> { + if let &ast::TokenTree::Delimited(_, ref delemented) = ast { + let fieldef_str = print::pprust::tts_to_string(&delemented.tts); + return get_enum_items_impl(cx, ast.get_span(), fieldef_str, msg) + } + cx.span_err(sp, INVALID_FIELD_DEF); + Err(()) +} + +fn get_enum_items_impl(cx: &mut ExtCtxt, sp: codemap::Span, ast: String, msg: &str) -> Result ,()> { + if ast.len() == 0 { + cx.span_err(sp, msg); + return Err(()) + } + let items = Vec::new(); + let iter = ast.split(','); + let items = iter.fold(items, |mut acc, attr| { + let mut iter = attr.split('='); + let enum_name = iter.next().unwrap().trim(); + let enum_value = iter.next().unwrap().trim(); + let item = EnumItem { + name: enum_name.to_string(), + value: enum_value.to_string() + }; + acc.push(item); + acc + }); + Ok(items) +} + +fn get_enum_repr(cx: &mut ExtCtxt, ast: &ast::TokenTree, msg: &str) -> Result { + if let &ast::TokenTree::Token(_, token::Token::Ident(ident))= ast { + let ty = ident.name.as_str(); + if let Some(ty) = map_ty(ty.to_string()) { + if ty.is_scalar() { + return Ok(ty) + } + } + } + cx.span_err(ast.get_span(), msg); + Err(()) +} diff --git a/rust/macros/src/table.rs b/rust/macros/src/table.rs new file mode 100644 index 00000000000..cd4793d83c9 --- /dev/null +++ b/rust/macros/src/table.rs @@ -0,0 +1,85 @@ +use syntax::ext::base::{ExtCtxt, MacResult, MacEager}; +use syntax::parse::token; +use syntax::util::small_vector::SmallVector; +use syntax::ext::quote::rt::ExtParseUtils; + +use utils::*; + +pub fn build_table_items(cx: &mut ExtCtxt, + name: token::InternedString, + fields: Vec, + attributes: Vec) + -> Result, ()> { + let struct_def = cx.parse_item(build_struct_def(&name)); + let build_impl = cx.parse_item(build_impl(&name, &fields)); + let from_impl = cx.parse_item(build_from(&name)); + let vtype = cx.parse_item(build_vector_type(&name, &attributes)); + Ok(MacEager::items(SmallVector::many(vec![struct_def, build_impl, from_impl, vtype]))) +} + +pub fn build_struct_items(cx: &mut ExtCtxt, + name: token::InternedString, + fields: Vec, + attributes: Vec) + -> Result, ()> { + let struct_def = cx.parse_item(build_struct_def(&name)); + let build_impl = cx.parse_item(build_struct_impl(&name, &fields)); + let from_impl = cx.parse_item(build_from(&name)); + let vtype = cx.parse_item(build_vector_type(&name, &attributes)); + Ok(MacEager::items(SmallVector::many(vec![struct_def, build_impl, from_impl, vtype]))) +} + +fn build_struct_def(name: &token::InternedString) -> String { + format!("#[derive(Debug, Clone, PartialEq, Eq)] pub struct {}>(flatbuffers::Table);", + name) +} + +fn build_impl(name: &token::InternedString, fields: &Vec) -> String { + let mut str1 = format!("impl> {} {{\n", name); + for field in fields { + str1 = format!("{} fn {}(&self) -> {} {{ {} }}", + str1, + name, + field.ty.base_type(), + field.ty.get_table_accessor(&field.slot, &field.default)); + } + format!("{} }}", str1) +} + +fn build_struct_impl(name: &token::InternedString, fields: &Vec) -> String { + let mut str1 = format!("impl> {} {{ \n", name); + for field in fields { + str1 = format!("{} fn {}(&self) -> {} {{ {} }}", + str1, + field.name.to_lowercase(), + field.ty.base_type(), + field.ty.get_struct_accessor(&field.slot)); + } + format!("{} }}", str1) +} + +fn build_from(name: &token::InternedString) -> String { + format!("impl> From> for {} {{ fn from(table: \ + flatbuffers::Table) -> {} {{ {}(table) }}}}", + name, + name, + name) + +} + +fn build_vector_type(name: &token::InternedString, attributes: &[ObjAttribute]) -> String { + let size = find_attribute("size", attributes).unwrap_or("4".to_string()); + let size = size.parse::().unwrap(); + format!("impl<'a, T: AsRef<[u8]>> flatbuffers::VectorType<'a> for {} {{ \ + type Item = {}<&'a [u8]>; + fn inline_size() -> usize {{ \ + {} \ + }} \ + fn read_next(buffer:&'a [u8]) -> Self::Item {{ \ + let table = flatbuffers::Table::get_indirect_root(buffer, 0); \ + table.into() \ + }} }}", + name, + name, + size) +} diff --git a/rust/macros/src/union.rs b/rust/macros/src/union.rs new file mode 100644 index 00000000000..2cf0024867b --- /dev/null +++ b/rust/macros/src/union.rs @@ -0,0 +1,101 @@ +use syntax::ext::base::{ExtCtxt, MacResult, MacEager}; +use syntax::parse::token; +use syntax::util::small_vector::SmallVector; +use syntax::ext::quote::rt::ExtParseUtils; + +use utils::*; + +pub fn build_simple_enum(cx: &mut ExtCtxt, + name: token::InternedString, + ty: FieldType, + items: Vec) + -> Result, ()> { + let enum_def = cx.parse_item(build_enum_def(&name, &ty.base_type(), &items)); + let from_def = cx.parse_item(build_from_def(&name, &ty.base_type(), &items)); + let vtype = try!(build_enum_vector_type(&name, &ty)); + let vtype_def = cx.parse_item(vtype); + Ok(MacEager::items(SmallVector::many(vec![enum_def, from_def, vtype_def]))) +} + +pub fn build_union(cx: &mut ExtCtxt, + name: token::InternedString, + items: Vec) + -> Result, ()> { + let union_def = cx.parse_item(build_union_enum_def(&name, &items)); + let type_name = format!("{}Type", &name); + let union_new_def = cx.parse_item(build_union_new_def(&name, &type_name, &items)); + let enum_def = cx.parse_item(build_enum_def(&type_name, "u8", &items)); + let from_def = cx.parse_item(build_from_def(&type_name, "u8", &items)); + Ok(MacEager::items(SmallVector::many(vec![union_def, union_new_def, enum_def, from_def]))) +} + +fn build_union_enum_def(name: &str, items: &[EnumItem]) -> String { + let mut str1 = format!("#[derive(Debug)] \ + pub enum {}> {{\n", + name); + for item in items { + str1 = format!("{} {}({}),\n", str1, item.name, item.name); + } + format!("{} }}", str1) +} + +fn build_union_new_def(name: &str, type_name: &str, items: &[EnumItem]) -> String { + let mut str1 = format!("impl> {} {{ \ + pub fn new(table: flatbuffers::Table, utype: Option<{}>) -> Option<{}> {{ + match utype {{", name, type_name, name); + for item in items { + str1 = format!("{} Some({}::{}) => Some( {}::{}(table.into() ) ), ", str1, type_name, + item.name, name, item.name); + } + format!("{} _ => None }} }} }}", str1) +} + + +fn build_enum_def(name: &str, ty: &str, items: &[EnumItem]) -> String { + let mut str1 = format!("#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]\n \ + #[repr({})]\n \ + pub enum {} {{\n", + ty, + name); + for item in items { + str1 = format!("{} {} = {},\n", str1, item.name, item.value); + } + format!("{} }}", str1) +} + +fn build_from_def(name: &str, ty: &str, items: &[EnumItem]) -> String { + let mut str1 = + format!("impl {} {{ pub fn from(value: {}) -> Option<{}> {{ match value {{ ", + name, + ty, + name); + for item in items { + str1 = format!("{} {} => Some({}::{}),\n", + str1, + item.value, + name, + item.name); + } + format!("{} _ => None }}}}}}", str1) +} + +fn build_enum_vector_type(name: &token::InternedString, ty: &FieldType) -> Result { + let (base_ty, size) = match *ty { + FieldType::Scalar(ref base_ty, size) => (base_ty.clone(), size), + _ => return Err(()), + }; + let str1 = + format!("impl<'a> flatbuffers::VectorType<'a> for {} {{ type Item = \ + Option<{}>; + fn inline_size() -> usize {{ {} }} fn \ + read_next(buffer:&'a [u8]) -> Self::Item {{ \ + let i = {}::read_next(buffer); + {}::from(i) \ + }} }}", + name, + name, + size, + base_ty, + name); + Ok(str1) +} diff --git a/rust/macros/src/utils.rs b/rust/macros/src/utils.rs new file mode 100644 index 00000000000..e157e3c3a2e --- /dev/null +++ b/rust/macros/src/utils.rs @@ -0,0 +1,287 @@ +use syntax::ast; +use syntax::codemap; +use syntax::ext::base::ExtCtxt; +use syntax::parse::token; + +#[derive(Debug, PartialEq, Eq)] +pub enum ObjectType { + Table, + Struct, + Enum, + Union, +} + +#[derive(Debug)] +pub struct ObjAttribute { + pub name: String, + pub value: String, +} + +#[derive(Debug)] +pub struct FieldDef { + pub name: String, + pub ty: FieldType, + pub slot: String, + pub default: String, + pub comments: Vec, + pub padding: Option +} + +#[derive(Debug)] +pub enum FieldType { + Scalar(String, u8), + Enum(String, Box), + Union(String), + Table(String), + Vector(Box), +} + +#[derive(Debug)] +pub struct EnumItem { + pub name: String, + pub value: String, +} + +pub fn map_ty(other: String) -> Option { + let ft = match &*other { + "byte" => FieldType::Scalar("i8".to_string(), 1), + "ubyte" => FieldType::Scalar("u8".to_string(), 1), + "short" => FieldType::Scalar("i16".to_string(), 2), + "ushort" => FieldType::Scalar("u16".to_string(), 2), + "int" => FieldType::Scalar("i32".to_string(), 4), + "uint" => FieldType::Scalar("u32".to_string(), 4), + "long" => FieldType::Scalar("i64".to_string(), 8), + "ulong" => FieldType::Scalar("u64".to_string(), 8), + "float" => FieldType::Scalar("f32".to_string(), 4), + "double" => FieldType::Scalar("f64".to_string(), 8), + "bool" => FieldType::Scalar("bool".to_string(), 1), + "string" => FieldType::Scalar("&str".to_string(), 4), + _ => { + let otherc = other.clone(); + let mut chars = otherc.chars(); + if chars.next() == Some('[') && chars.next_back() == Some(']') { + // vector + let ty = chars.as_str() + .to_string(); + let ty = map_ty(ty); + if ty.is_none() { + return None; + } + FieldType::Vector(Box::new(ty.unwrap())) + } else if other.starts_with("union ") { + let mut iter = other.split_whitespace(); + let md = iter.nth(1); + if md.is_none() { + return None; + } + FieldType::Union(md.unwrap().to_string()) + } else if other.starts_with("enum ") { + let mut iter = other.split_whitespace(); + let _ = iter.next(); + let md = iter.next(); + let ty = iter.next(); + if md.is_none() || ty.is_none() { + return None; + } + let ty = map_ty(ty.unwrap().to_string()); + if ty.is_none() { + return None; + } + FieldType::Enum(md.unwrap().to_string(), Box::new(ty.unwrap())) + } else { + FieldType::Table(other) + } + } + }; + Some(ft) +} + +impl Default for FieldType { + fn default() -> FieldType { + FieldType::Scalar("I8".to_string(), 1) + } +} + +impl FieldType { + pub fn get_table_accessor(&self, slot: &str, default: &str) -> String { + match *self { + FieldType::Scalar(ref ty, _) => scalar_accessor(ty, slot, default), + FieldType::Enum(ref md, ref ty) => enum_accessor(md, &ty, slot, default), + FieldType::Union(ref md) => union_accessor(md, slot), + FieldType::Table(_) => table_accessor(slot), + FieldType::Vector(ref ty) => vector_accessor(&ty, slot), + } + } + + pub fn get_struct_accessor(&self, slot: &str) -> String { + match *self { + FieldType::Scalar(ref ty, _) => struct_scalar_accessor(ty, slot), + FieldType::Enum(ref md, ref ty) => struct_enum_accessor(md, &ty, slot), + FieldType::Table(_) => struct_table_accessor(slot), + _ => panic!("Structs do not have Union or Vector fields"), + } + } + + + pub fn base_type(&self) -> String { + match *self { + FieldType::Scalar(ref ty, _) => ty.to_string(), + FieldType::Table(ref ty) => format!("{}<&[u8]>", ty.to_string()), + FieldType::Vector(ref ty) => format!("Iter<'a,{}>", ty.base_type()), + FieldType::Union(ref ty) => ty.to_string(), + FieldType::Enum(ref md, _) => format!("Option<{}>", md.to_string()), + } + } + + pub fn is_scalar(&self) -> bool { + if let FieldType::Scalar(_,_) = *self { + true + } else { + false + } + } +} + +fn enum_accessor(md: &str, ty: &FieldType, slot: &str, default: &str) -> String { + let fun = ty.get_table_accessor(slot, default); + format!("let v = {}; {}::from(v)", fun, md) +} + +fn struct_enum_accessor(md: &str, ty: &FieldType, slot: &str) -> String { + let fun = ty.get_struct_accessor(slot); + format!("let v = {}; {}::from(v)", fun, md) +} + +fn union_accessor(md: &str, slot: &str) -> String { + let fun = format!("self.{}_type();", md.to_lowercase()); + let table = format!("let table = ($i.0).get_slot_table({});", slot); + format!("{} {} {}::new(table, ty)", fun, table, md.to_lowercase()) +} + +fn table_accessor(slot: &str) -> String { + let fun = format!("let t = (self.0).get_slot_table({})", slot); + format!("{} if t.is_some() {{ return t.unwrap().into(); }} None", + fun) +} + +fn struct_table_accessor(slot: &str) -> String { + format!("(self.0).get_struct({})", slot) +} + +fn vector_accessor(ty: &FieldType, slot: &str) -> String { + let ty = ty.base_type(); + format!("(self.0).get_slot_vector::<{}>({})", ty, slot) +} + +fn scalar_accessor(ty: &str, slot: &str, default: &str) -> String { + match &*ty { + "i8" => format!("(self.0).get_slot_i8({},{})", slot, default), + "u8" => format!("(self.0).get_slot_u8({},{})", slot, default), + "i16" => format!("(self.0).get_slot_i16({},{})", slot, default), + "u16" => format!("(self.0).get_slot_u16({},{})", slot, default), + "i32" => format!("(self.0).get_slot_i32({},{})", slot, default), + "u32" => format!("(self.0).get_slot_u32({},{})", slot, default), + "i64" => format!("(self.0).get_slot_i64({},{})", slot, default), + "u64" => format!("(self.0).get_slot_u64({},{})", slot, default), + "f32" => format!("(self.0).get_slot_f32({},{})", slot, default), + "f64" => format!("(self.0).get_slot_f64({},{})", slot, default), + "bool" => format!("(self.0).get_slot_bool({},{})", slot, default), + "&str" => format!("(self.0).get_slot_str({},{})", slot, default), + otherwise => panic!("Unknow scalar type {}", otherwise), + } +} + +fn struct_scalar_accessor(ty: &str, slot: &str) -> String { + match &*ty { + "i8" => format!("(self.0).get_i8({})", slot), + "u8" => format!("(self.0).get_u8({})", slot), + "i16" => format!("(self.0).get_i16({})", slot), + "u16" => format!("(self.0).get_u16({})", slot), + "i32" => format!("(self.0).get_i32({})", slot), + "u32" => format!("(self.0).get_u32({})", slot), + "i64" => format!("(self.0).get_i64({})", slot), + "u64" => format!("(self.0).get_u64({})", slot), + "f32" => format!("(self.0).get_f32({})", slot), + "f64" => format!("(self.0).get_f64({})", slot), + "bool" => format!("(self.0).get_bool({})", slot), + "&str" => format!("(self.0).get_str({})", slot), + otherwise => panic!("Unknow scalar type {}", otherwise), + } +} + +pub fn find_attribute(name: &str, attributes: &[ObjAttribute]) -> Option { + for attr in attributes { + if attr.name == name { + return Some(attr.value.clone()); + } + } + None +} + +pub fn consume_fat_arrow(cx: &mut ExtCtxt, + sp: codemap::Span, + ast: &ast::TokenTree, + msg: &str) + -> Result<(), ()> { + match *ast { + ast::TokenTree::Token(_, token::Token::FatArrow) => return Ok(()), + _ => {} + } + cx.span_err(sp, msg); + Err(()) +} + +pub fn consume_colon(cx: &mut ExtCtxt, + sp: codemap::Span, + ast: &ast::TokenTree, + msg: &str) + -> Result<(), ()> { + match *ast { + ast::TokenTree::Token(_, token::Token::Colon) => return Ok(()), + _ => {} + } + cx.span_err(sp, msg); + Err(()) +} + + +pub fn maybe_comma(ast: &ast::TokenTree) -> bool { + match *ast { + ast::TokenTree::Token(_, token::Token::Comma) => return true, + _ => {} + } + false +} + +pub fn get_lit(cx: &mut ExtCtxt, + sp: codemap::Span, + ast: &ast::TokenTree, + msg: &str) + -> Result { + match ast { + &ast::TokenTree::Token(_, token::Token::Literal(lit, _)) => return Ok(lit), + _ => { + cx.span_err(sp, msg); + return Err(()); + } + } +} + +pub fn expect_ident(cx: &mut ExtCtxt, + sp: codemap::Span, + ast: &ast::TokenTree, + name: &[&str], + msg: &str) + -> Result { + match ast { + &ast::TokenTree::Token(_, token::Token::Ident(ident)) => { + let res = name.iter().any(|x| *x == ident.name.as_str()); + if res { + return Ok(ident); + } + } + _ => {} + } + cx.span_err(sp, msg); + Err(()) +} diff --git a/rust/macros/tests/compile-fail/empty_field.rs b/rust/macros/tests/compile-fail/empty_field.rs new file mode 100644 index 00000000000..c760ea45055 --- /dev/null +++ b/rust/macros/tests/compile-fail/empty_field.rs @@ -0,0 +1,11 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster [ + field => {} + //~^ Error: Invalid field definition +]} + +fn main() {} diff --git a/rust/macros/tests/compile-fail/expected_field.rs b/rust/macros/tests/compile-fail/expected_field.rs new file mode 100644 index 00000000000..730f8e1e202 --- /dev/null +++ b/rust/macros/tests/compile-fail/expected_field.rs @@ -0,0 +1,11 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster [//~ Error: Invalid field def + pos +]} + + +fn main() {} diff --git a/rust/macros/tests/compile-fail/fat_arrow.rs b/rust/macros/tests/compile-fail/fat_arrow.rs new file mode 100644 index 00000000000..c63dbc585a8 --- /dev/null +++ b/rust/macros/tests/compile-fail/fat_arrow.rs @@ -0,0 +1,9 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table} +//~^ ERROR: Expected '=>' + +fn main() {} diff --git a/rust/macros/tests/compile-fail/fat_arrow_name.rs b/rust/macros/tests/compile-fail/fat_arrow_name.rs new file mode 100644 index 00000000000..8b0673b5c49 --- /dev/null +++ b/rust/macros/tests/compile-fail/fat_arrow_name.rs @@ -0,0 +1,8 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] +extern crate flatbuffers_macros; + +flatbuffers_object!{Table =>} +//~^ ERROR: Expected a name for the Table + +fn main() {} diff --git a/rust/macros/tests/compile-fail/first_ident.rs b/rust/macros/tests/compile-fail/first_ident.rs new file mode 100644 index 00000000000..273cbaa9c09 --- /dev/null +++ b/rust/macros/tests/compile-fail/first_ident.rs @@ -0,0 +1,9 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Monster, []} +//~^ ERROR: Expected one of 'Table', 'Struct', 'Enum' or 'Union' + +fn main() {} diff --git a/rust/macros/tests/compile-fail/invalid_field.rs b/rust/macros/tests/compile-fail/invalid_field.rs new file mode 100644 index 00000000000..5fce66b07ff --- /dev/null +++ b/rust/macros/tests/compile-fail/invalid_field.rs @@ -0,0 +1,11 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster [//~ Error: Invalid field definition + field +]} + + +fn main() {} diff --git a/rust/macros/tests/compile-fail/invalid_field_attr.rs b/rust/macros/tests/compile-fail/invalid_field_attr.rs new file mode 100644 index 00000000000..d531420354b --- /dev/null +++ b/rust/macros/tests/compile-fail/invalid_field_attr.rs @@ -0,0 +1,14 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster [ + field => { name = pos, + //~^ Error: Unknown attribute: wierd + typeOf = Vec3, + wierd = true, + slot = 4 } +]} + +fn main() {} diff --git a/rust/macros/tests/compile-fail/invalid_other_field.rs b/rust/macros/tests/compile-fail/invalid_other_field.rs new file mode 100644 index 00000000000..e7af80eb381 --- /dev/null +++ b/rust/macros/tests/compile-fail/invalid_other_field.rs @@ -0,0 +1,15 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster [ + field => { name = pos, + typeOf = Vec3, + slot = 4, + default = true }, + field => { typeOf = pos } + //~^ Error: Expected default, name, slot, and typeOf, missing default, name, and slot +]} + +fn main() {} diff --git a/rust/macros/tests/compile-fail/missing_name.rs b/rust/macros/tests/compile-fail/missing_name.rs new file mode 100644 index 00000000000..3146330f818 --- /dev/null +++ b/rust/macros/tests/compile-fail/missing_name.rs @@ -0,0 +1,11 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster [ + field => { typeOf = pos } + //~^ Error: Expected default, name, slot, and typeOf, missing default, name, and slot +]} + +fn main() {} diff --git a/rust/macros/tests/compile-fail/name_no_fields.rs b/rust/macros/tests/compile-fail/name_no_fields.rs new file mode 100644 index 00000000000..92dfdd6d461 --- /dev/null +++ b/rust/macros/tests/compile-fail/name_no_fields.rs @@ -0,0 +1,9 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster,} +//~^ Error: Expected a list of fields + +fn main() {} diff --git a/rust/macros/tests/compile-fail/slot_int.rs b/rust/macros/tests/compile-fail/slot_int.rs new file mode 100644 index 00000000000..1d2677c5e6a --- /dev/null +++ b/rust/macros/tests/compile-fail/slot_int.rs @@ -0,0 +1,11 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; + +flatbuffers_object!{Table => Monster [ + field => { slot = me } + //~^ Error: Slot must be an integer +]} + +fn main() {} diff --git a/rust/macros/tests/run-pass/enum.rs b/rust/macros/tests/run-pass/enum.rs new file mode 100644 index 00000000000..b85447eb163 --- /dev/null +++ b/rust/macros/tests/run-pass/enum.rs @@ -0,0 +1,12 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; +extern crate flatbuffers; + +flatbuffers_object!{Enum => Color {Red = 1, Blue = 2} as byte} + +fn main() { + let x = Color::Red; + let y = Color::from(2).unwrap(); +} diff --git a/rust/macros/tests/run-pass/no_fields.rs b/rust/macros/tests/run-pass/no_fields.rs new file mode 100644 index 00000000000..e610d31cdf3 --- /dev/null +++ b/rust/macros/tests/run-pass/no_fields.rs @@ -0,0 +1,14 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; +extern crate flatbuffers; + +flatbuffers_object!{Table => Vec3 []} +//~^ WARNING: Expected a list of fields + + +flatbuffers_object!{Table => Monster} +//~^ WARNING: Expected a list of fields + +fn main() {} diff --git a/rust/macros/tests/run-pass/struct.rs b/rust/macros/tests/run-pass/struct.rs new file mode 100644 index 00000000000..62d04b62927 --- /dev/null +++ b/rust/macros/tests/run-pass/struct.rs @@ -0,0 +1,34 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; +extern crate flatbuffers; + +flatbuffers_object!{Enum => Color {Red = 1, Blue = 2} as ubyte} + +flatbuffers_object!{Struct => Vec3 ( size:32 ) [ + field => { name = x, + typeOf = float, + slot = 0, + default = 0.0 }, + field => { name = y, + typeOf = float, + slot = 4, + default = 0.0 }, + field => { name = z, + typeOf = float, + slot = 8, + default = 0.0 }, + field => { name = test1, + typeOf = double, + slot = 16, + default = 0.0 }, + field => { name = test2, + typeOf = enum Color ubyte, + slot = 24, + default = 0 }, + field => { name = test3, + typeOf = Vec3, + slot = 26 }]} + +fn main() {} diff --git a/rust/macros/tests/run-pass/table.rs b/rust/macros/tests/run-pass/table.rs new file mode 100644 index 00000000000..de28c5c9041 --- /dev/null +++ b/rust/macros/tests/run-pass/table.rs @@ -0,0 +1,37 @@ +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; +extern crate flatbuffers; + +use flatbuffers::VectorType; + +flatbuffers_object!{Table => Monster [ + field => { name = pos, + typeOf = bool, + slot = 4, + comment = "test", + default = true } +]} + +fn main() { + let buf = + vec![72, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 60, 0, 128, 0, 8, 0, 0, 0, 6, 0, 52, 0, 0, + 0, 56, 0, 0, 0, 5, 0, 60, 0, 64, 0, 68, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 76, 0, 80, + 0, 96, 0, 104, 0, 84, 0, 88, 0, 112, 0, 120, 0, 92, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, + 0, 1, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 0, 0, 0, 228, + 0, 0, 0, 204, 0, 0, 0, 128, 0, 0, 0, 88, 0, 0, 0, 64, 0, 0, 0, 65, 201, 121, 221, 65, + 201, 121, 221, 113, 164, 129, 142, 113, 164, 129, 142, 36, 0, 0, 0, 129, 145, 123, + 242, 205, 128, 15, 110, 129, 145, 123, 242, 205, 128, 15, 110, 241, 221, 103, 199, + 220, 72, 249, 67, 241, 221, 103, 199, 220, 72, 249, 67, 3, 0, 0, 0, 1, 0, 1, 0, 188, + 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 2, 0, 0, 0, 20, + 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, + 101, 115, 116, 49, 0, 0, 0, 2, 0, 0, 0, 10, 0, 20, 0, 30, 0, 40, 0, 60, 0, 8, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, + 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, + 3, 4, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0]; + let table = flatbuffers::Table::get_root(buf, 0); + let mon: Monster<_> = table.into(); +} diff --git a/rust/macros/tests/run-pass/union.rs b/rust/macros/tests/run-pass/union.rs new file mode 100644 index 00000000000..e7b6b44e345 --- /dev/null +++ b/rust/macros/tests/run-pass/union.rs @@ -0,0 +1,15 @@ + +#![feature(plugin)] +#![plugin(flatbuffers_macros)] + +extern crate flatbuffers_macros; +extern crate flatbuffers; + +flatbuffers_object!{Struct => Vec3 ( size:32 ) []} + +flatbuffers_object!{Table => Monster []} + +flatbuffers_object!{Union => Any { Monster = 1, Vec3 = 2 }} + +fn main() { +} diff --git a/rust/macros/tests/tests.rs b/rust/macros/tests/tests.rs new file mode 100644 index 00000000000..333c8ce7a75 --- /dev/null +++ b/rust/macros/tests/tests.rs @@ -0,0 +1,21 @@ +extern crate compiletest_rs as compiletest; + +use std::path::PathBuf; + +fn run_mode(mode: &'static str) { + + let mut config = compiletest::default_config(); + let cfg_mode = mode.parse().ok().expect("Invalid mode"); + + config.mode = cfg_mode; + config.src_base = PathBuf::from(format!("tests/{}", mode)); + config.target_rustcflags = Some("-L target/debug -L target/debug/deps".to_string()); + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail"); + run_mode("run-pass"); +} diff --git a/rust/src/bin/test.rs b/rust/src/bin/test.rs new file mode 100644 index 00000000000..8687693c864 --- /dev/null +++ b/rust/src/bin/test.rs @@ -0,0 +1,247 @@ +// #![feature(trace_macros)] +// #[macro_use] +// extern crate flatbuffers; + +// fn main() { + +// } + +// #[cfg(test)] +// #[cfg(feature = "test_idl_gen")] +// mod test { +// use std::rc::Rc; + +// #[allow(non_snake_case,dead_code,unused_imports, non_camel_case_types)] +// mod MyGame; + +// use flatbuffers::*; +// use self::MyGame::*; + +// #[test] +// fn manual_monster_build() { +// let mut b = Builder::with_capacity(0); +// let str = b.create_string("MyMonster"); + +// b.start_vector(1, 5, 1); +// b.add_u8(4); +// b.add_u8(3); +// b.add_u8(2); +// b.add_u8(1); +// b.add_u8(0); +// let inv = b.end_vector(); + +// b.start_object(13); +// b.add_slot_i16(2, 20, 100); +// let mon2 = b.end_object(); + +// // Test4Vector +// b.start_vector(4, 2, 1); + +// // Test 0 +// b.prep(2, 4); +// b.pad(1); +// b.add_i8(20); +// b.add_i16(10); + +// // Test 1 +// b.prep(2, 4); +// b.pad(1); +// b.add_i8(40); +// b.add_i16(30); + +// // end testvector +// let test4 = b.end_vector(); + +// b.start_object(13); + +// // a vec3 +// b.prep(16, 32); +// b.pad(2); +// b.prep(2, 4); +// b.pad(1); +// b.add_u8(6); +// b.add_i16(5); +// b.pad(1); +// b.add_u8(4); +// b.add_f64(3.0); +// b.pad(4); +// b.add_f32(3.0); +// b.add_f32(2.0); +// b.add_f32(1.0); +// let vec3_loc = b.offset(); +// // end vec3 + +// b.add_slot_struct(0, vec3_loc as u32, 0); // vec3. noop +// b.add_slot_i16(2, 80, 100); // hp +// b.add_slot_uoffset(3, str as u32, 0); +// b.add_slot_uoffset(5, inv as u32, 0); // inventory +// b.add_slot_u8(7, 1, 0); +// b.add_slot_uoffset(8, mon2 as u32, 0); +// b.add_slot_uoffset(9, test4 as u32, 0); +// let mon = b.end_object(); + +// b.finish_table(mon); +// } + +// fn build_monster() { +// use self::MyGame::example::monster::MonsterBuilder; + +// let mut b = Builder::with_capacity(0); +// let name = b.create_string("MyMonster"); +// let test1 = b.create_string("test1"); +// let test2 = b.create_string("test2"); +// let fred = b.create_string("Fred"); +// b.start_inventory_vector(5); +// b.add_u8(4); +// b.add_u8(3); +// b.add_u8(2); +// b.add_u8(1); +// b.add_u8(0); +// let inv = b.end_vector(); + +// b.start_monster(); +// b.add_name(fred); +// let mon2 = b.end_object(); + + +// use self::MyGame::example::test::TestBuilder; +// b.start_test4_vector(2); +// b.build_test(10, 20); +// b.build_test(30, 40); +// let test4 = b.end_vector(); + +// b.start_testarrayofstring_vector(2); +// b.add_uoffset(test2); +// b.add_uoffset(test1); +// let test_array = b.end_vector(); +// use self::MyGame::example::stat::StatBuilder; +// let mut stat = b; +// let stat_id = stat.create_string("way out..statistical"); +// stat.start_stat(); +// stat.add_id(stat_id); +// stat.add_val(20); +// stat.add_count(0); +// let stat_offset = stat.end_object(); +// b = stat; + + +// use self::MyGame::example::vec3::Vec3Builder; +// b.start_monster(); +// let pos = b.build_vec3(1.0, 2.0, 3.0, 3.0, 2, 5, 6); +// b.add_pos(pos); + +// b.add_hp(80); +// b.add_name(name); +// b.add_inventory(inv); +// b.add_test_type(1); +// b.add_test(mon2); +// b.add_test4(test4); +// b.add_testempty(stat_offset); +// b.add_testarrayofstring(test_array); +// let mon = b.end_object(); + +// b.finish_table(mon); +// } + +// #[test] +// fn build_monster_with_generated() { +// build_monster(); +// } + +// #[test] +// fn can_read_monster() { +// use std::env; +// use std::path::Path; +// use std::io::{Read, BufReader}; +// use std::fs::File; +// let mut data_path = env::current_exe().unwrap(); +// data_path.pop(); +// data_path.pop(); +// data_path.pop(); //create root +// data_path.push(Path::new("monsterdata_test.mon")); +// let f1 = File::open(data_path).unwrap(); +// let mut reader = BufReader::new(f1); +// let mut buf: Vec = Vec::new(); +// reader.read_to_end(&mut buf).unwrap(); +// let rc = Rc::new(buf); +// let table = Table::from_offset(rc.clone(), 0); +// let monster = example::monster::Monster::new(table); +// let got = monster.hp(); +// assert!(got == 80, "bad {}: want {:?} got {:?}", "HP", 80, got); +// let got = monster.mana(); +// assert!(got == 150, "bad {}: want {:?} got {:?}", "Mana", 150, got); +// let got = monster.name(); +// assert!(got == "MyMonster", "bad {}: want {:?} got {:?}", "Name", "MyMonster", got); +// let vec3 = monster.pos(); +// assert!(vec3.is_some(), "bad {}: want {:?} got {:?}", "Pos", "Vec3", "None"); +// // verify the properties of the Vec3 +// let vec3 = vec3.unwrap(); +// let got = vec3.x(); +// assert!(got == 1.0, "bad {}: want {:?} got {:?}", "Vec3.x", "1.0", got); +// let got = vec3.y(); +// assert!(got == 2.0, "bad {}: want {:?} got {:?}", "Vec3.x", "2.0", got); +// let got = vec3.z(); +// assert!(got == 3.0, "bad {}: want {:?} got {:?}", "Vec3.x", "3.0", got); +// let got = vec3.test1(); +// assert!(got == 3.0, "bad {}: want {:?} got {:?}", "Vec3.test1", "3.0", got); +// let got = vec3.test2(); +// assert!(got == Some(example::Color::Green), "bad {}: want {:?} got {:?}", +// "Vec3.test2", "Color::Green", got); +// // Verify properties of test3 +// let test3 = vec3.test3(); +// let got = test3.a(); +// assert!(got == 5, "bad {}: want {:?} got {:?}", "Test.a", "5", got); +// let got = test3.b(); +// assert!(got == 6, "bad {}: want {:?} got {:?}", "Test.b", "6", got); +// // Verify test type +// let got = monster.test_type(); +// assert!(got == Some(example::AnyType::Monster), "bad {}: want {:?} got {:?}", "TestType", "Monster", got); +// // initialize a Table from a union field +// let got = monster.test(); +// if let Some(example::Any::Monster(monster2)) = got { +// let got = monster2.name(); +// assert!(got == "Fred", "bad {}: want {:?} got {:?}", "Name", "Fred", got); +// } else { +// panic!( "bad {}: want {:?} got {:?}", "Test", "Monster Union", got) +// } +// let got = monster.inventory(); + +// assert!(got.size_hint() == (5, Some(5)), "bad {}: want {:?} got {:?}", +// "Inventory", "Byte Vector of length 5", got); +// use std::ops::Add; +// let got: u8 = got.fold(0, Add::add); +// assert!(got == 10, "bad {}: want {:?} got {:?}", "Inventory Sum", "10", got); +// // Test4 +// let mut got = monster.test4(); +// assert!(got.size_hint() == (2, Some(2)), "bad {}: want {:?} got {:?}", "Test4 vector length", "4", got.size_hint()); +// let test1 = got.next().unwrap(); +// let test2 = got.next().unwrap(); +// assert!(test1.a() == 10, "bad {}: want {:?} got {:?}", "Test4 array test1.a", "10", test1.a()); +// assert!(test1.b() == 20, "bad {}: want {:?} got {:?}", "Test4 array test1.b", "20", test1.b()); +// assert!(test2.a() == 30, "bad {}: want {:?} got {:?}", "Test4 array test2.a", "30", test2.a()); +// assert!(test2.b() == 40, "bad {}: want {:?} got {:?}", "Test4 array test2.b", "40", test2.b()); +// // Test Array of string +// let mut got = monster.testarrayofstring(); +// assert!(got.size_hint() == (2, Some(2)), "bad {}: want {:?} got {:?}", "Test4 string vector length", "2", got.size_hint()); +// let str1 = got.next().unwrap(); +// let str2 = got.next().unwrap(); +// assert!(str1 == "test1", "bad {}: want {:?} got {:?}", "Test string array 1", "test1", str1); +// assert!(str2 == "test2", "bad {}: want {:?} got {:?}", "Test string array 2", "test2", str2); +// // array of tables +// let got = monster.testarrayoftables(); +// assert_eq!(got.size_hint(),(0, Some(0))); +// } + + + +// // until benchmark tests are stablized in Rust... +// #[test] +// fn bench_build() { +// use std::time::Instant; +// let now = Instant::now(); +// for _ in 1..10000 { +// build_monster() +// } +// println!("Building 1000 took {:?}", now.elapsed()); +// } +// } diff --git a/rust/src/builder.rs b/rust/src/builder.rs new file mode 100644 index 00000000000..dd24beba2bf --- /dev/null +++ b/rust/src/builder.rs @@ -0,0 +1,1258 @@ +//! Flatbuffer Builder. +//! +//! Builder is a state machine for creating FlatBuffer objects. +//! Use a Builder to construct object(s) starting from leaf nodes. +use byteorder::{ByteOrder, LittleEndian}; +use std::{io, ops, slice, ptr}; +use std::io::Write; + +use types::*; + +/// Builder provides functions to build Flatbuffer data. +/// +/// A Builder constructs byte buffers in a last-first manner for simplicity and +/// performance. +#[derive(Debug, Clone)] +pub struct Builder { + // Where the FlatBuffer is constructed. + bytes: Vec, + // Minimum alignment encountered so far. + min_align: usize, + // Starting offset of the current struct/table. + object_start: UOffsetT, + // Whether we are currently serializing a table. + nested: bool, + // Whether the buffer is finished. + finished: bool, + // The vtable for the current table. + vtable: Vec, + // The number of vtable slots required by the current object. + vtable_in_use: usize, + // Offsets of other Vtables in the buffer. + vtables: Vec, + // Unused space in the buffer. Size from beggining of buffer to first vtable + space: usize, + // Number of elements in the current vector + vector_len: usize, + // If false builder will ignore default values and always create a field + // and non-zero vslot. Default is true and will only create an entry + // if value != default. + force_defaults: bool, + // A flag to control the vtable deduplication process. see `write_vtable` + vtable_dedup: bool, +} + +impl Builder { + /// Start a new FlatBuffer `Builder` backed by a buffer with an + /// initial capacity of `size`. + pub fn with_capacity(size: usize) -> Self { + let mut bytes = Vec::with_capacity(size); + bytes.resize(size, 0); + Builder { + bytes: bytes, + min_align: 1, + object_start: 0, + nested: false, + finished: false, + vtable_in_use: 0, + vtable: Vec::with_capacity(16), + vtables: Vec::with_capacity(16), + space: size, + vector_len: 0, + force_defaults: true, + vtable_dedup: true, + } + } + + /// Start encoding a new object in the buffer. + pub fn start_object(&mut self, num_fields: usize) { + self.assert_not_nested(); + self.nested = true; + self.finished = false; + self.vtable.clear(); + self.vtable.resize(num_fields, 0); + self.vtable_in_use = num_fields; + self.object_start = self.offset(); + self.min_align = 1; + } + + /// finish off writing the object that is under construction. + /// + /// Returns the offset of the object in the inside the buffer. + pub fn end_object(&mut self) -> UOffsetT { + self.assert_nested(); + self.nested = false; + self.write_vtable() + } + + /// Initializes bookkeeping for writing a new vector. + pub fn start_vector(&mut self, elem_size: usize, num_elems: usize, alignment: usize) { + self.assert_not_nested(); + self.nested = true; + self.finished = false; + self.vector_len = num_elems; + self.prep(UOFFSETT_SIZE, elem_size * num_elems); + self.prep(alignment, elem_size * num_elems); + } + + /// finish off writing the current vector. + pub fn end_vector(&mut self) -> UOffsetT { + let len = self.vector_len; + self.end_vector_with(len as u32) + } + + /// Finish off writing the current vector of length `len`. + /// + /// Useful if you dont know the exact size of the vector when you start it. + pub fn end_vector_with(&mut self, len: u32) -> UOffsetT { + self.assert_nested(); + self.put_u32(len); + self.nested = false; + self.offset() as UOffsetT + } + + /// Create a string in the buffer from an already encoded UTF-8 `String`. + pub fn create_string(&mut self, value: &str) -> UOffsetT { + // null terminated + self.add_u8(0); + self.start_vector(1, value.len(), 1); + self.write(value.as_bytes()).unwrap(); + self.end_vector() + } + + /// Create a vector in the buffer from an already encoded + /// byte vector. + pub fn create_vector(&mut self, value: &[u8]) -> UOffsetT { + self.start_vector(1, value.len(), 1); + self.write(value).unwrap(); + self.end_vector() + } + + /// Create a vector of `UOffsetT` in the buffer. + /// This function will encode the values to `LittleEndian`. + #[cfg(target_endian = "big")] + pub fn create_uoffset_vector(&mut self, value: &[UOffsetT]) -> UOffsetT { + let len = value.len(); + self.start_vector(UOFFSETT_SIZE, len, 0); + self.space -= len; + for (i, c) in value.iter().enumerate() { + let pos = self.space + i; + self.place_u32(pos, *c); + } + self.end_vector() + } + + /// Create a vector of `UOffsetT` in the buffer. + /// This function will encode the values to `LittleEndian`. + #[cfg(target_endian = "little")] + pub fn create_uoffset_vector(&mut self, value: &[UOffsetT]) -> UOffsetT { + self.start_vector(1, value.len(), 1); + unsafe { + let ptr = value.as_ptr(); + let ptr = ptr as *const u8; + self.write(slice::from_raw_parts(ptr, value.len() * UOFFSETT_SIZE)) + .unwrap(); + } + self.end_vector() + } + + + /// Finalize a table, pointing to the given `root_table`. + pub fn finish_table(&mut self, root_table: UOffsetT) -> UOffsetT { + self.assert_not_nested(); + let align = self.min_align; + self.prep(align, UOFFSETT_SIZE); + self.add_uoffset(root_table); + self.finished = true; + self.offset() + } + + /// Get a reference to the underlying buffer. + /// Buffer starts from the first byte of useful data + /// i.e. `&[pos..]`. + pub fn get_bytes(&self) -> &[u8] { + let pos = self.pos(); + &self.bytes[pos..] + } + + /// Returns the current buffer and replaces it with + /// a `new_buffer`. + /// + /// Thie function facilitates some reuse of the `Builder` object. + /// Use `into()` if the `Builder` is no longer required. + pub fn swap_out(&mut self, mut new_buffer: Vec) -> Vec { + use std::mem; + mem::swap(&mut self.bytes, &mut new_buffer); + self.reset(); + new_buffer + } + + /// Clears the builder state. + /// + /// Clears the buffer without resizing. This allows for reuse + /// without allocating new memeory. + pub fn reset(&mut self) { + self.min_align = 1; + self.object_start = 0; + self.nested = false; + self.finished = false; + self.vtable_in_use = 0; + self.vtables.truncate(0); + self.vtable.truncate(0); + self.space = self.bytes.capacity(); + self.vector_len = 0; + } + + /// Returns the length of the buffer. + pub fn len(&self) -> usize { + self.bytes.len() + } + + /// Offset relative to the end of the buffer. + #[inline(always)] + pub fn offset(&self) -> UOffsetT { + (self.bytes.len() - self.space) as UOffsetT + } + + /// Returns the offset relative to the beggining + /// of the buffer. + pub fn pos(&self) -> usize { + self.space + } + + /// Set the vtable default field value check. + /// + /// If true `Builder` will only write values to the table if + /// the value is not equal to the value. If false `Builder` + /// will always write the value to the table. + /// Defaults to true. + pub fn force_defaults(&mut self, defaults: bool) { + self.force_defaults = defaults; + } + + /// Set the vtable deduplication process flag. + /// + /// If `true` the `Builder` will check previously written vtables + /// and reuse identical vtables to safe space at the expense of + /// CPU. If `false` this process is skipped. + pub fn vtable_dedup(&mut self, dedup: bool) { + self.vtable_dedup = dedup; + } +} + +impl Builder { + /// Add a `bool` to the buffer, backwards from the current location. + /// Doesn't align nor check for space. + pub fn put_bool(&mut self, boolean: bool) { + self.space -= 1; + let head = self.space; + self.bytes[head] = if boolean { + 1 + } else { + 0 + }; + } + + /// Add a `byte` to the buffer, backwards from the current location. + /// Doesn't align nor check for space. + pub fn put_u8(&mut self, byte: u8) { + self.space -= 1; + let head = self.space; + self.bytes[head] = byte; + } + + /// Add a `value` of type `i8` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_i8(&mut self, value: i8) { + self.put_u8(value as u8) + } + + /// Add a `value` of type `u16` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_u16(&mut self, value: u16) { + self.space -= 2; + let head = self.space; + LittleEndian::write_u16(&mut self.bytes[head..head + 2], value); + } + + /// Add a `value` of type `i16` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_i16(&mut self, value: i16) { + self.put_u16(value as u16) + } + + /// Add a `value` of type `u32` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_u32(&mut self, value: u32) { + self.space -= 4; + let head = self.space; + LittleEndian::write_u32(&mut self.bytes[head..head + 4], value); + } + + /// Add a `value` of type `i32` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_i32(&mut self, value: i32) { + self.put_u32(value as u32) + } + + /// Add a `value` of type `u64` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_u64(&mut self, value: u64) { + self.space -= 8; + let head = self.space; + LittleEndian::write_u64(&mut self.bytes[head..head + 8], value); + + } + + /// Add a `value` of type `i64` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_i64(&mut self, value: i64) { + self.put_u64(value as u64) + } + + /// Add a `value` of type `f32` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_f32(&mut self, value: f32) { + self.space -= 4; + let head = self.space; + LittleEndian::write_f32(&mut self.bytes[head..head + 4], value); + } + + /// Add a `value` of type `f64` to the buffer, backwards from the current + /// location. Doesn't align nor check for space. + pub fn put_f64(&mut self, value: f64) { + self.space -= 8; + let head = self.space; + LittleEndian::write_f64(&mut self.bytes[head..head + 8], value); + } + + /// Add a `value` of type `UOffsetT` to the buffer, backwards from the + /// current location. Doesn't align nor check for space. + pub fn put_uoffset(&mut self, value: UOffsetT) { + self.space -= 4; + let head = self.space; + LittleEndian::write_u32(&mut self.bytes[head..head + 4], value); + } + + /// Add a `bool` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_bool(&mut self, value: bool) { + self.prep(1, 0); + self.put_bool(value); + } + + /// Add a value of type `u8` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_u8(&mut self, value: u8) { + self.prep(1, 0); + self.put_u8(value); + } + + /// Add a value of type `i8` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_i8(&mut self, value: i8) { + self.add_u8(value as u8); + } + + /// Add a value of type `u16` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_u16(&mut self, value: u16) { + self.prep(2, 0); + self.put_u16(value); + } + + /// Add a value of type `i16` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_i16(&mut self, value: i16) { + self.add_u16(value as u16); + } + + /// Add a value of type `u32` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_u32(&mut self, value: u32) { + self.prep(4, 0); + self.put_u32(value); + } + + /// Add a value of type `i32` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_i32(&mut self, value: i32) { + self.add_u32(value as u32); + } + + /// Add a value of type `u64` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_u64(&mut self, value: u64) { + self.prep(8, 0); + self.put_u64(value); + } + + /// Add a value of type `i64` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_i64(&mut self, value: i64) { + self.add_u64(value as u64); + } + + /// Add a value of type `f32` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_f32(&mut self, value: f32) { + self.prep(4, 0); + self.put_f32(value); + } + + /// Add a value of type `f64` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + pub fn add_f64(&mut self, value: f64) { + self.prep(8, 0); + self.put_f64(value); + } + + /// Add a value of type `f64` to the buffer, properly aligned, and grows the + /// buffer (if necessary). + /// prepends an UOffsetT, relative to where it will be written. + pub fn add_uoffset(&mut self, value: UOffsetT) { + self.prep(UOFFSETT_SIZE, 0); + assert!(value <= self.offset() as UOffsetT, + "unreachable: off <= boffset();"); + let relative = self.offset() as u32 - value as u32 + UOFFSETT_SIZE as u32; + self.put_uoffset(relative); + } + + /// Slot sets the vtable key `voffset` to the current location in the buffer. + pub fn slot(&mut self, slot: usize) { + self.vtable[slot] = self.offset() as UOffsetT; + } + + /// Add a `bool` onto the object at vtable slot `o`. If value `x` equals + /// default `d`, then the slot will be set to zero and no other data + /// will be written. + pub fn add_slot_bool(&mut self, o: usize, value: bool, d: bool) { + if value != d || !self.force_defaults { + self.add_bool(value); + self.slot(o) + } + } + + /// Add a value of type `u8` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_u8(&mut self, o: usize, value: u8, d: u8) { + if value != d || !self.force_defaults { + self.add_u8(value); + self.slot(o) + } + } + + /// Add a value of type `i8` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_i8(&mut self, o: usize, value: i8, d: i8) { + self.add_slot_u8(o, value as u8, d as u8); + } + + /// Add a value of type `u16` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_u16(&mut self, o: usize, value: u16, d: u16) { + if value != d || !self.force_defaults { + self.add_u16(value); + self.slot(o) + } + } + + /// Add a value of type `i16` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_i16(&mut self, o: usize, value: i16, d: i16) { + self.add_slot_u16(o, value as u16, d as u16); + } + + /// Add a value of type `u32` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_u32(&mut self, o: usize, value: u32, d: u32) { + if value != d || !self.force_defaults { + self.add_u32(value); + self.slot(o) + } + } + + /// Add a value of type `i32` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_i32(&mut self, o: usize, value: i32, d: i32) { + self.add_slot_u32(o, value as u32, d as u32); + } + + /// Add a value of type `u64` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_u64(&mut self, o: usize, value: u64, d: u64) { + if value != d || !self.force_defaults { + self.add_u64(value); + self.slot(o) + } + } + + /// Add a value of type `i64` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_i64(&mut self, o: usize, value: i64, d: i64) { + self.add_slot_u64(o, value as u64, d as u64); + } + + /// Add a value of type `f32` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_f32(&mut self, o: usize, value: f32, d: f32) { + if value != d || !self.force_defaults { + self.add_f32(value); + self.slot(o) + } + } + + /// Add a value of type `f64` onto the object at vtable slot `o`. If value + /// `x` equals default `d`, then the slot will be set to zero and no other + /// data will be written. + pub fn add_slot_f64(&mut self, o: usize, value: f64, d: f64) { + if value != d || !self.force_defaults { + self.add_f64(value); + self.slot(o) + } + } + + /// Add a value of type `UOffsetT` onto the object at vtable slot `o`. If + /// value `x` equals default `d`, then the slot will be set to zero and no + /// other data will be written. + /// + /// prepends an UOffsetT, relative to where it will be written. + pub fn add_slot_uoffset(&mut self, o: usize, value: UOffsetT, d: UOffsetT) { + if value != d || !self.force_defaults { + self.add_uoffset(value); + self.slot(o) + } + } + + /// PrependStructSlot prepends a struct onto the object at vtable slot `o`. + /// Structs are stored inline, so nothing additional is being added. + /// In generated code, `d` is always 0. + pub fn add_slot_struct(&mut self, o: usize, value: UOffsetT, d: UOffsetT) { + if value != d || !self.force_defaults { + self.assert_nested(); + assert!(value == self.offset() as u32, + "Inline data write outside of object.Write the slot_struct immediatly after \ + writing the struct slot."); + self.slot(o) + } + } + + /// Place a `u8` at `pos` relative to the beginning + /// of the underlaying buffer. + pub fn place_u8(&mut self, pos: usize, value: u8) { + use std::ptr; + unsafe { + let ptr = self.bytes.as_mut_ptr().offset(pos as isize); + ptr::write(ptr, value) + } + } + + /// Place a `u32` with `LittleEndian` encoding at `pos` + /// relative to the beginning of the underlaying buffer. + pub fn place_u32(&mut self, pos: usize, value: u32) { + LittleEndian::write_u32(&mut self.bytes[pos..pos + 4], value); + } + + /// Place a `i32` with `LittleEndian` encoding at `pos` + /// relative to the beginning of the underlaying buffer. + pub fn place_i32(&mut self, pos: usize, value: i32) { + LittleEndian::write_i32(&mut self.bytes[pos..pos + 4], value); + } + + /// Place a `u64` with `LittleEndian` encoding at `pos` + /// relative to the beginning of the underlaying buffer. + pub fn place_u64(&mut self, pos: usize, value: u64) { + LittleEndian::write_u64(&mut self.bytes[pos..pos + 8], value); + } + + /// Check to assert `finish` has not been called. + pub fn assert_not_finished(&self) { + assert!(!self.finished, "FlatBuffers: root table is not finished."); + } + + /// Check to assert `start_object` has not been called. + pub fn assert_not_nested(&self) { + assert!(!self.nested, + "FlatBuffers: object serialization must not be nested."); + } + + /// Check to assert `start_object` has been called. + pub fn assert_nested(&self) { + assert!(self.nested, + "FlatBuffers: struct must be serialized inline."); + } + + /// Helper method to read a VOffsetT at `pos` relative to the + /// beginning of the underlying buffer - this may not be the start + /// of any useful data. Use with `offset()` to get the `UOffsetT` + /// of useful data. + pub fn get_u16(&self, pos: usize) -> VOffsetT { + LittleEndian::read_u16(&self.bytes[pos..pos + 2]) + } + + /// Helper method to read a UOffsetT at `pos` relative to the + /// beginning of the underlying buffer - this may not be the start + /// of any useful data. Use with `offset()` to get the `UOffsetT` + /// of useful data. + pub fn get_32(&self, pos: usize) -> UOffsetT { + LittleEndian::read_u32(&self.bytes[pos..pos + 4]) + } + + /// Doubles the size of the buffer. + /// + /// Copies the old data towards the end of the new buffer (since we build + /// the buffer backwards). + /// Max buffer size is 2 Gigabytes - otherwise u16 index values might + /// be invalid. + pub fn grow(&mut self) { + let old_capacity = if self.bytes.capacity() == 0 { + 1 + } else { + self.bytes.capacity() + }; + let size_test = old_capacity & 0xC0000000; + assert!(size_test == 0, + "FlatBuffers: cannot grow buffer beyond 2 gigabytes."); + let new_capacity = old_capacity << 1; + let mut nbytes = Vec::with_capacity(new_capacity); + // fill front of buffer with zeroes + if self.bytes.capacity() == 0 { + nbytes.resize(new_capacity, 0); + } else { + nbytes.resize(old_capacity, 0); + // extend the buffer with the old buffer + nbytes.extend_from_slice(&self.bytes); + } + self.space = new_capacity - self.offset() as usize; + self.bytes = nbytes; + } + + /// prepare to write an element of `size` after `additional_bytes` + /// have been written. + pub fn prep(&mut self, size: usize, additional_bytes: usize) { + if size > self.min_align { + self.min_align = size + } + let align_size = self.offset() as usize + additional_bytes + 1; + let align_size = ((align_size ^ 1)) & (size - 1); + while self.space <= align_size + size + additional_bytes { + self.grow(); + } + self.pad(align_size) + } + + /// pad places zeros at the current offset. + pub fn pad(&mut self, n: usize) { + for _ in 0..n { + self.space -= 1; + unsafe { + ptr::write(self.bytes.get_unchecked_mut(self.space), 0); + } + } + } + + // WriteVtable serializes the vtable for the current object. + // + // Before writing out the vtable, this checks pre-existing vtables for + // equality to this one. If an equal vtable is found, point the object to + // the existing vtable and return. + // + // Because vtable values are sensitive to alignment of object data, not all + // logically-equal vtables will be deduplicated. + fn write_vtable(&mut self) -> UOffsetT { + self.add_uoffset(0); + let vtableloc = self.offset() as usize; + // Write out the current vtable. + for i in (0..self.vtable_in_use).rev() { + // Offset relative to the start of the table. + let offset = if self.vtable[i] != 0 { + vtableloc - self.vtable[i] as usize + } else { + 0 + }; + self.add_u16(offset as u16); + } + // write the metadata + let total_obj_size = vtableloc - self.object_start as usize; + let vtable_size = (self.vtable_in_use + VTABLE_METADATA_FIEDS) * VOFFSETT_SIZE; + self.add_u16(total_obj_size as u16); + self.add_u16(vtable_size as u16); + // check for identical vtable + let mut existing_vt = 0; + let mut this_vt = self.space; + if self.vtable_dedup { + 'outer: for (i, vt_offset) in self.vtables.iter().rev().enumerate() { + let vt_start = self.bytes.len() - *vt_offset as usize; + let vt_len = self.get_u16(vt_start); + if vt_len != self.get_u16(this_vt) { + continue; + } + let vt_end = vt_start + vt_len as usize; + let vt_bytes = &self.bytes[vt_start + VTABLE_METADATA_SIZE..vt_end]; + for (j, chunk) in vt_bytes.chunks(VOFFSETT_SIZE).enumerate() { + let this_start = this_vt + VTABLE_METADATA_SIZE + (j * VOFFSETT_SIZE) as usize; + let this_end = this_start + VOFFSETT_SIZE; + let this_chunk = &self.bytes[this_start..this_end]; + if chunk != this_chunk { + continue 'outer; + } + } + existing_vt = self.vtables[i] as usize; + break 'outer; + } + } + + if existing_vt != 0 { + // found a match + self.space = self.bytes.capacity() - vtableloc; + let pos = self.space; + self.place_i32(pos, (existing_vt as i32 - vtableloc as i32)); + } else { + // no match + this_vt = self.offset() as usize; + self.vtables.push(this_vt as u32); + let capacity = self.bytes.capacity(); + self.place_i32(capacity - vtableloc, (this_vt - vtableloc) as i32); + } + self.vtable.clear(); + vtableloc as UOffsetT + } +} + +impl Default for Builder { + fn default() -> Builder { + Builder::with_capacity(1024) + } +} + +impl Into> for Builder { + fn into(self) -> Vec { + self.bytes + } +} + +impl ops::Deref for Builder { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + &self.bytes + } +} + +impl Write for Builder { + fn write(&mut self, bytes: &[u8]) -> io::Result { + let len = bytes.len(); + self.space -= len; + + for i in 0..bytes.len() { + let pos = self.space + i; + unsafe { + ptr::write(self.bytes.get_unchecked_mut(pos), bytes.get_unchecked(i).clone()); + } + } + Ok(len) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::Builder; + use types::*; + use table::Table; + + #[test] + fn grow_buffer() { + let mut b = Builder::with_capacity(0); + b.grow(); + assert_eq!(b.space, 2); + assert_eq!(b.offset(), 0); + assert_eq!(b.len(), 2); + + let mut b = Builder::with_capacity(1); + b.grow(); + assert_eq!(b.space, 2); + assert_eq!(b.offset(), 0); + assert_eq!(b.len(), 2); + + let mut b = Builder::with_capacity(2); + b.grow(); + assert_eq!(b.space, 4); + assert_eq!(b.offset(), 0); + assert_eq!(b.len(), 4); + } + + #[test] + fn grow_buffer2() { + let mut builder = Builder::with_capacity(2); + builder.add_u8(4); + builder.add_u8(2); + let old_capacity = builder.bytes.capacity(); + builder.grow(); + // capacity doubled + assert_eq!(builder.bytes.capacity(), old_capacity * 2); + assert_eq!(builder.bytes.len(), old_capacity * 2); + // bytes moved to end of new buffer + assert_eq!(builder.bytes[(old_capacity * 2) - 2], 2u8); + assert_eq!(builder.bytes[(old_capacity * 2) - 1], 4u8); + } + + #[test] + fn check_bytes() { + fn check(b: &Builder, i: u8, want: &[u8]) { + let got = &b.bytes; + assert!(got.ends_with(want), + "case {}: want\n{:?}\nbut got\n{:?}\n", + i, + want, + got) + }; + let mut b = Builder::with_capacity(0); + check(&b, 1, &[]); + + b.add_bool(true); + check(&b, 2, &[1]); + b.add_i8(-127); + check(&b, 3, &[129, 1]); + b.add_u8(255); + check(&b, 4, &[255, 129, 1]); + b.add_i16(-32222); + check(&b, 5, &[0x22, 0x82, 0, 255, 129, 1]); // first pad + b.add_u16(0xFEEE); + check(&b, 6, &[0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1]); // no pad this time + b.add_i32(-53687092); + check(&b, + 7, + &[204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1]); + b.add_u32(0x98765432); + check(&b, + 8, + &[0x32, 0x54, 0x76, 0x98, 204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, + 1]); + + b = Builder::with_capacity(0); + b.add_u64(0x1122334455667788); + check(&b, 9, &[0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); + + // test 2: 1xbyte vector + b = Builder::with_capacity(0); + check(&b, 10, &[]); + b.start_vector(1, 1, 1); + check(&b, 11, &[0, 0, 0]); // align to 4bytes + b.add_u8(1); + check(&b, 12, &[1, 0, 0, 0]); + b.end_vector(); + check(&b, 13, &[1, 0, 0, 0, 1, 0, 0, 0]); // padding + + // test 3: 2xbyte vector + b = Builder::with_capacity(0); + b.start_vector(1, 2, 1); + check(&b, 14, &[0, 0]); // align to 4bytes + b.add_u8(1); + check(&b, 15, &[1, 0, 0]); + b.add_u8(2); + check(&b, 16, &[2, 1, 0, 0]); + b.end_vector(); + check(&b, 17, &[2, 0, 0, 0, 2, 1, 0, 0]); // padding + + // test 3b: 11xbyte vector matches builder size + b = Builder::with_capacity(12); + b.start_vector(1, 8, 1); + check(&b, 18, &[]); + for i in 0..11 { + b.add_u8(i); + } + b.end_vector(); + // says size is 8 not 11 + check(&b, 19, &[8, 0, 0, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]); + + // test 4: 1xuint16 vector + b = Builder::with_capacity(0); + b.start_vector(2, 1, 1); + check(&b, 20, &[0, 0]); // align to 4bytes + b.add_u16(1); + check(&b, 21, &[1, 0, 0, 0]); + b.end_vector(); + check(&b, 22, &[1, 0, 0, 0, 1, 0, 0, 0]); // padding + + // test 5: 2xuint16 vector + b = Builder::with_capacity(0); + b.start_vector(2, 2, 1); + check(&b, 23, &[]); // align to 4bytes + b.add_u16(0xABCD); + check(&b, 24, &[0xCD, 0xAB]); + b.add_u16(0xDCBA); + check(&b, 25, &[0xBA, 0xDC, 0xCD, 0xAB]); + b.end_vector(); + check(&b, 25, &[2, 0, 0, 0, 0xBA, 0xDC, 0xCD, 0xAB]); + + // create string + b = Builder::with_capacity(0); + b.create_string("foo"); + check(&b, 26, &[3, 0, 0, 0, 102, 111, 111, 0]); // 0-terminated, no pad + b.create_string("moop"); + check(&b, + 27, + &[4, 0, 0, 0, 109, 111, 111, 112, 0, 0, 0, 0 /* 0-terminated, 3-byte pad */, + 3, 0, 0, 0, 102, 111, 111, 0]); + + // test 7: empty vtable + b = Builder::with_capacity(0); + b.start_object(0); + check(&b, 28, &[]); + b.end_object(); + check(&b, 29, &[4, 0, 4, 0, 4, 0, 0, 0]); + + // test 8: vtable with one true bool + b = Builder::with_capacity(0); + b.start_object(1); + check(&b, 30, &[]); + b.add_slot_bool(0, true, false); + b.end_object(); + check(&b, + 31, + &[6, 0 /* vtable bytes */, 8, + 0 /* length of object including vtable offset */, 7, + 0 /* start of bool value */, 6, 0, 0, + 0 /* offset for start of vtable (int32) */, 0, 0, + 0 /* padded to 4 bytes */, 1 /* bool value */]); + + + // test 9: vtable with one default bool + b = Builder::with_capacity(0); + b.start_object(1); + check(&b, 32, &[]); + b.add_slot_bool(0, false, false); + b.end_object(); + check(&b, + 33, + &[6, 0 /* vtable bytes */, 4, 0 /* end of object from here */, 0, + 0 /* entry 1 is zero */, 6, 0, 0, + 0 /* offset for start of vtable (int32) */]); + + // test 10: vtable with one int16 + b = Builder::with_capacity(0); + b.start_object(1); + b.add_slot_i16(0, 0x789A, 0); + b.end_object(); + check(&b, + 34, + &[6, 0 /* vtable bytes */, 8, 0 /* end of object from here */, 6, + 0 /* offset to value */, 6, 0, 0, + 0 /* offset for start of vtable (int32) */, 0, + 0 /* padding to 4 bytes */, 0x9A, 0x78]); + + // test 11: vtable with two int16 + b = Builder::with_capacity(0); + b.start_object(2); + b.add_slot_i16(0, 0x3456, 0); + b.add_slot_i16(1, 0x789A, 0); + b.end_object(); + check(&b, + 35, + &[8, 0 /* vtable bytes */, 8, 0 /* end of object from here */, 6, + 0 /* offset to value 0 */, 4, 0 /* offset to value 1 */, 8, 0, 0, + 0 /* offset for start of vtable (int32) */, 0x9A, 0x78 /* value 1 */, + 0x56, 0x34 /* value 0 */]); + + // test 12a: vtable with int16 and bool + b = Builder::with_capacity(0); + b.start_object(2); + b.add_slot_i16(0, 0x3456, 0); + b.add_slot_bool(1, true, false); + b.end_object(); + check(&b, + 36, + &[8, 0 /* vtable bytes */, 8, 0 /* end of object from here */, 6, + 0 /* offset to value 0 */, 5, 0 /* offset to value 1 */, 8, 0, 0, + 0 /* offset for start of vtable (int32) */, 0 /* padding */, + 1 /* value 1 */, 0x56, 0x34 /* value 0 */]); + + // test 12b: vtable with empty vector + b = Builder::with_capacity(0); + b.start_vector(1, 0, 1); + let vecend = b.end_vector(); + b.start_object(1); + b.add_slot_uoffset(0, vecend, 0); + b.end_object(); + check(&b, + 37, + &[6, 0 /* vtable bytes */, 8, 0, 4, 0 /* offset to vector offset */, 6, + 0, 0, 0 /* offset for start of vtable (int32) */, 4, 0, 0, 0, 0, 0, 0, + 0 /* length of vector (not in struct) */]); + + // test 12c: vtable with empty vector of byte and some scalars + b = Builder::with_capacity(0); + b.start_vector(1, 0, 1); + let vecend = b.end_vector(); + b.start_object(2); + b.add_slot_i16(0, 55, 0); + b.add_slot_uoffset(1, vecend, 0); + b.end_object(); + check(&b, + 38, + &[8, 0 /* vtable bytes */, 12, 0, 10, 0 /* offset to value 0 */, 4, + 0 /* offset to vector offset */, 8, 0, 0, 0 /* vtable loc */, 8, 0, 0, + 0 /* value 1 */, 0, 0, 55, 0 /* value 0 */, 0, 0, 0, + 0 /* length of vector (not in struct) */]); + { + let buffer = b.get_bytes(); + assert_eq!(buffer, + &[8, 0 /* vtable bytes */, 12, 0, 10, 0 /* offset to value 0 */, + 4, 0 /* offset to vector offset */, 8, 0, 0, + 0 /* vtable loc */, 8, 0, 0, 0 /* value 1 */, 0, 0, 55, + 0 /* value 0 */, 0, 0, 0, + 0 /* length of vector (not in struct) */]); + } + + // test 13: vtable with 1 int16 and 2-vector of int16 + b = Builder::with_capacity(0); + b.start_vector(2, 2, 1); + b.add_i16(0x1234); + b.add_i16(0x5678); + let vecend = b.end_vector(); + b.start_object(2); + b.add_slot_uoffset(1, vecend, 0); + b.add_slot_i16(0, 55, 0); + b.end_object(); + check(&b, + 39, + &[8, 0 /* vtable bytes */, 12, 0 /* length of object */, 6, + 0 /* start of value 0 from end of vtable */, 8, + 0 /* start of value 1 from end of buffer */, 8, 0, 0, + 0 /* offset for start of vtable (int32) */, 0, 0 /* padding */, 55, + 0 /* value 0 */, 4, 0, 0, 0 /* vector position from here */, 2, 0, 0, + 0 /* length of vector (uint32) */, 0x78, 0x56 /* vector value 1 */, + 0x34, 0x12 /* vector value 0 */]); + + // test 14: vtable with 1 struct of 1 int8, 1 int16, 1 int32 + b = Builder::with_capacity(0); + b.start_object(1); + b.prep(4 + 4 + 4, 0); + b.add_i8(55); + b.pad(3); + b.add_i16(0x1234); + b.pad(2); + b.add_i32(0x12345678); + let struct_start = b.offset(); + b.add_slot_struct(0, struct_start as u32, 0); + b.end_object(); + check(&b, + 40, + &[6, 0 /* vtable bytes */, 16, 0 /* end of object from here */, 4, + 0 /* start of struct from here */, 6, 0, 0, + 0 /* offset for start of vtable (int32) */, 0x78, 0x56, 0x34, + 0x12 /* value 2 */, 0, 0 /* padding */, 0x34, 0x12 /* value 1 */, + 0, 0, 0 /* padding */, 55 /* value 0 */]); + + // test 15: vtable with 1 vector of 2 struct of 2 int8 + b = Builder::with_capacity(0); + b.start_vector(1 * 2, 2, 1); + b.add_i8(33); + b.add_i8(44); + b.add_i8(55); + b.add_i8(66); + let vecend = b.end_vector(); + b.start_object(1); + b.add_slot_uoffset(0, vecend, 0); + b.end_object(); + check(&b, + 41, + &[6, 0 /* vtable bytes */, 8, 0, 4, 0 /* offset of vector offset */, 6, + 0, 0, 0 /* offset for start of vtable (int32) */, 4, 0, 0, + 0 /* vector start offset */, 2, 0, 0, 0 /* vector length */, + 66 /* vector value 1,1 */, 55 /* vector value 1,0 */, + 44 /* vector value 0,1 */, 33 /* vector value 0,0 */]); + + // test 16: table with some elements + b = Builder::with_capacity(0); + b.start_object(2); + b.add_slot_i8(0, 33, 0); + b.add_slot_i16(1, 66, 0); + let off = b.end_object(); + b.finish_table(off); + + check(&b, + 42, + &[12, 0, 0, 0 /* root of table: points to vtable offset */, 8, + 0 /* vtable bytes */, 8, 0 /* end of object from here */, 7, + 0 /* start of value 0 */, 4, 0 /* start of value 1 */, 8, 0, 0, + 0 /* offset for start of vtable (int32) */, 66, 0 /* value 1 */, + 0 /* padding */, 33 /* value 0 */]); + + // test 17: one unfinished table and one finished table + b = Builder::with_capacity(0); + b.start_object(2); + b.add_slot_i8(0, 33, 0); + b.add_slot_i8(1, 44, 0); + let obj1 = b.end_object(); + b.finish_table(obj1); + + b.start_object(3); + b.add_slot_i8(0, 55, 0); + b.add_slot_i8(1, 66, 0); + b.add_slot_i8(2, 77, 0); + let obj2 = b.end_object(); + b.finish_table(obj2); + + check(&b, + 43, + &[16, 0, 0, 0 /* root of table: points to object */, 0, 0 /* padding */, + 10, 0 /* vtable bytes */, 8, 0 /* size of object */, 7, + 0 /* start of value 0 */, 6, 0 /* start of value 1 */, 5, + 0 /* start of value 2 */, 10, 0, 0, + 0 /* offset for start of vtable (int32) */, 0 /* padding */, + 77 /* value 2 */, 66 /* value 1 */, 55 /* value 0 */, 12, 0, 0, + 0 /* root of table: points to object */, 8, 0 /* vtable bytes */, 8, + 0 /* size of object */, 7, 0 /* start of value 0 */, 6, + 0 /* start of value 1 */, 8, 0, 0, + 0 /* offset for start of vtable (int32) */, 0, 0 /* padding */, + 44 /* value 1 */, 33 /* value 0 */]); + assert!(b.offset() as usize + b.pos() == b.len()); + + // test 18: a bunch of bools + b = Builder::with_capacity(0); + b.start_object(8); + b.add_slot_bool(0, true, false); + b.add_slot_bool(1, true, false); + b.add_slot_bool(2, true, false); + b.add_slot_bool(3, true, false); + b.add_slot_bool(4, true, false); + b.add_slot_bool(5, true, false); + b.add_slot_bool(6, true, false); + b.add_slot_bool(7, true, false); + let off = b.end_object(); + b.finish_table(off); + + check(&b, + 44, + &[24, 0, 0, 0 /* root of table: points to vtable offset */, 20, + 0 /* vtable bytes */, 12, 0 /* size of object */, 11, + 0 /* start of value 0 */, 10, 0 /* start of value 1 */, 9, + 0 /* start of value 2 */, 8, 0 /* start of value 3 */, 7, + 0 /* start of value 4 */, 6, 0 /* start of value 5 */, 5, + 0 /* start of value 6 */, 4, 0 /* start of value 7 */, 20, 0, 0, + 0 /* vtable offset */, 1 /* value 7 */, 1 /* value 6 */, + 1 /* value 5 */, 1 /* value 4 */, 1 /* value 3 */, + 1 /* value 2 */, 1 /* value 1 */, 1 /* value 0 */]); + + + // test 19: three bools + b = Builder::with_capacity(0); + b.start_object(3); + b.add_slot_bool(0, true, false); + b.add_slot_bool(1, true, false); + b.add_slot_bool(2, true, false); + let off = b.end_object(); + b.finish_table(off); + + check(&b, + 45, + &[16, 0, 0, 0 /* root of table: points to vtable offset */, 0, + 0 /* padding */, 10, 0 /* vtable bytes */, 8, + 0 /* size of object */, 7, 0 /* start of value 0 */, 6, + 0 /* start of value 1 */, 5, 0 /* start of value 2 */, 10, 0, 0, + 0 /* vtable offset from here */, 0 /* padding */, 1 /* value 2 */, + 1 /* value 1 */, 1 /* value 0 */]); + + // test 20: some floats + b = Builder::with_capacity(0); + b.start_object(1); + b.add_slot_f32(0, 1.0, 0.0); + b.end_object(); + + check(&b, + 46, + &[6, 0 /* vtable bytes */, 8, 0 /* size of object */, 4, + 0 /* start of value 0 */, 6, 0, 0, 0 /* vtable offset */, 0, 0, 128, + 63 /* value 0 */]); + } + + #[test] + fn duplicate_vtable() { + let mut b = Builder::with_capacity(0); + + b.start_object(4); + b.add_slot_u8(0, 0, 0); + b.add_slot_u8(1, 11, 0); + b.add_slot_u8(2, 22, 0); + b.add_slot_i16(3, 33, 0); + let obj0 = b.end_object(); + + b.start_object(4); + b.add_slot_u8(0, 0, 0); + b.add_slot_u8(1, 44, 0); + b.add_slot_u8(2, 55, 0); + b.add_slot_i16(3, 66, 0); + let obj1 = b.end_object(); + + b.start_object(4); + b.add_slot_u8(0, 0, 0); + b.add_slot_u8(1, 77, 0); + b.add_slot_u8(2, 88, 0); + b.add_slot_i16(3, 99, 0); + let obj2 = b.end_object(); + + let got = b.get_bytes(); + + let want: &[u8] = &[240, 255, 255, 255 /* == -12. offset to dedupped vtable. */, 99, + 0, 88, 77, 248, 255, 255, + 255 /* == -8. offset to dedupped vtable. */, 66, 0, 55, 44, 12, + 0, 8, 0, 0, 0, 7, 0, 6, 0, 4, 0, 12, 0, 0, 0, 33, 0, 22, 11]; + assert!(got == want, + "testVtableDeduplication want:\n{} {:?}\nbut got:\n{} {:?}\n", + want.len(), + want, + got.len(), + got); + let len = got.len(); + let table0 = Table::get_root(got, len - obj0 as usize); + let table1 = Table::get_root(got, len - obj1 as usize); + let table2 = Table::get_root(got, len - obj2 as usize); + fn test_table(tab: &Table<&[u8]>, a: VOffsetT, b: u8, c: u8, d: u8) { + let got = tab.field_offset(0); + if 12 != got { + panic!("failed 0, 0: {}", got) + } + // object size + let got = tab.field_offset(2); + if 8 != got { + panic!("failed 2, 0: {}", got) + } + // default value + let got = tab.field_offset(4); + if a != got { + panic!("failed 4, 0: {}", got) + } + let got = tab.get_slot_u8(6, 0); + if b != got as u8 { + panic!("failed 6, 0: {}", got) + } + let val = tab.get_slot_u8(8, 0); + if c != val as u8 { + panic!("failed 8, 0: {}", got) + } + let got = tab.get_slot_u8(10, 0); + if d != got as u8 { + panic!("failed 10, 0: {}", got) + } + + } + test_table(&table0, 0, 11, 22, 33); + test_table(&table1, 0, 44, 55, 66); + test_table(&table2, 0, 77, 88, 99); + + } +} diff --git a/rust/src/iter.rs b/rust/src/iter.rs new file mode 100644 index 00000000000..7573bc37f6c --- /dev/null +++ b/rust/src/iter.rs @@ -0,0 +1,162 @@ +use std::marker::PhantomData; +use byteorder::{ByteOrder, LittleEndian}; + +use types::*; +use table::Table; + +/// An iterator over vectors of flatbuffer objects. +#[derive(Debug)] +pub struct Iter<'a, T: 'a> { + buffer: &'a [u8], + start: usize, + end: usize, + _marker: PhantomData<&'a [T]>, +} + +impl<'a, T: VectorType<'a>> Iter<'a, T> { + /// Create a new Iterator for type `T` starting at `index` with + /// a length of `len` items of size `T`. + pub fn new<'b>(buffer: &'b [u8], index: usize, len: usize) -> Iter<'b, T> { + Iter { + buffer: buffer, + start: index, + end: len * T::inline_size(), + _marker: PhantomData, + } + } +} + +// Its akward to have a default empty iterator but +// it means that the Table module can return +// `Iter` rather than `Option` for +// table vector slots (with defaults). +impl<'a, T> Default for Iter<'a, T> { + fn default() -> Iter<'a, T> { + Iter { + buffer: &[], + start: 0, + end: 0, + _marker: PhantomData, + } + } +} + +impl<'a> Iterator for Iter<'a, &'a str> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + if self.start == self.end { + return None; + } + let ret = Table::<&[u8]>::read_str(&self.buffer[self.start..]); + self.start += UOFFSETT_SIZE; + Some(ret) + } + + fn size_hint(&self) -> (usize, Option) { + let len = (self.end - self.start) / 4; + (len, Some(len)) + } +} + +impl<'a> DoubleEndedIterator for Iter<'a, &'a str> { + fn next_back(&mut self) -> Option<&'a str> { + if self.start == self.end { + return None; + } + self.end -= UOFFSETT_SIZE; + let ret = Table::<&[u8]>::read_str(&self.buffer[self.end..]); + Some(ret) + } +} + +impl<'a> ExactSizeIterator for Iter<'a, &'a str> {} + +impl<'a, T: VectorType<'a>> Iterator for Iter<'a, T> { + type Item = T::Item; + + fn next(&mut self) -> Option { + if self.start == self.end { + return None; + } + let ret = T::read_next(&self.buffer[self.start..]); + self.start += T::inline_size(); + Some(ret) + } + + fn size_hint(&self) -> (usize, Option) { + let len = (self.end - self.start) / T::inline_size(); + (len, Some(len)) + } +} + +impl<'a, T: VectorType<'a>> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option { + if self.start == self.end { + return None; + } + self.end -= T::inline_size(); + let ret = T::read_next(&self.buffer[self.end..]); + Some(ret) + } +} + +impl<'a, T: VectorType<'a>> ExactSizeIterator for Iter<'a, T> {} + +/// A trait for any Flatbuffer type that can occur in a vector. +/// Flatbuffer tables and structs wil need to implemente this trait. +pub trait VectorType<'a> { + /// The type returned by the itertator over this `ScalarVectorType` + type Item; + /// Return the linline length in bytes of the `ScalarVectorType`. + fn inline_size() -> usize; + /// Read one value of `ScalarVectorType` from the front of the + /// buffer. + fn read_next(buffer: &'a [u8]) -> Self::Item; +} + +macro_rules! vector_type { + ($ty:ty, $size:expr, $fun:ident) => { + impl<'a> VectorType<'a> for $ty { + type Item = $ty; + fn inline_size() -> usize { $size } + fn read_next(buffer: &[u8]) -> $ty { + $fun(buffer) + } + } + }; + ($ty:ty, $size:expr, $md:ident::$fun:ident) => { + impl<'a> VectorType<'a> for $ty { + type Item = $ty; + fn inline_size() -> usize { $size } + fn read_next(buffer: &[u8]) -> $ty { + $md::$fun(buffer) + } + } + } +} + +vector_type!(bool, 1, read_bool); +vector_type!(u8, 1, read_u8); +vector_type!(i8, 1, read_i8); +vector_type!(u16, 2, LittleEndian::read_u16); +vector_type!(i16, 2, LittleEndian::read_i16); +vector_type!(u32, 4, LittleEndian::read_u32); +vector_type!(i32, 4, LittleEndian::read_i32); +vector_type!(u64, 8, LittleEndian::read_u64); +vector_type!(i64, 8, LittleEndian::read_i64); +vector_type!(f32, 4, LittleEndian::read_f32); +vector_type!(f64, 8, LittleEndian::read_f64); + +fn read_bool(buffer: &[u8]) -> bool { + buffer[0] == 1 +} + +fn read_u8(buffer: &[u8]) -> u8 { + buffer[0] +} + + +fn read_i8(buffer: &[u8]) -> i8 { + buffer[0] as i8 +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 00000000000..149c1d36a54 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,25 @@ +//! Rust support for Flatbuffers. +//! +//! FlatBuffers is an efficient cross platform serialization library for games +//! and other memory constrained apps. It allows you to directly access +//! serialized data without unpacking/parsing it first, while still having great +//! forwards/backwards compatibility. +//! +#![doc(html_logo_url="http://google.github.io/flatbuffers/fpl_logo_small.png")] + +#![deny(unused_qualifications, missing_debug_implementations, trivial_casts, + missing_copy_implementations, unused_import_braces, missing_docs)] +#![feature(trace_macros)] +extern crate byteorder; + +mod builder; +mod types; +mod table; +mod iter; + +pub use self::builder::Builder; +pub use self::types::*; +pub use self::table::*; +pub use self::iter::*; + + diff --git a/rust/src/macros.rs b/rust/src/macros.rs new file mode 100644 index 00000000000..f05460d5cf1 --- /dev/null +++ b/rust/src/macros.rs @@ -0,0 +1,199 @@ +//! Macros to generate code for Flatbuffer objects. + +#[macro_export] +macro_rules! table_fn { + (get_u8, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_u8($slot, $default) }; + (get_i8, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_i8($slot, $default) }; + (get_u16, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_u16($slot, $default) }; + (get_i16, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_i16($slot, $default) }; + (get_u32, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_u32($slot, $default) }; + (get_i32, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_i32($slot, $default) }; + (get_u64, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_u64($slot, $default) }; + (get_i64, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_i64($slot, $default) }; + (get_f32, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_f32($slot, $default) }; + (get_f64, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_f64($slot, $default) }; + (get_bool, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_bool($slot, $default) }; + (get_str, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_slot_str($slot) }; + (get_struct, $s:ident, $slot:expr, $ty:ty) => { ($s.0).get_slot_struct::<$ty>($slot) }; + (vector, $s:ident, $slot:expr, $ty:ty) => { ($s.0).get_slot_vector::<$ty>($slot) }; +} + +#[macro_export] +macro_rules! struct_fn { + (get_u8, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_u8($slot) }; + (get_i8, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_i8($slot) }; + (get_u16, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_u16($slot) }; + (get_i16, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_i16($slot) }; + (get_u32, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_u32($slot) }; + (get_i32, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_i32($slot) }; + (get_u64, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_u64($slot) }; + (get_i64, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_i64($slot) }; + (get_f32, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_f32($slot) }; + (get_f64, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_f64($slot) }; + (get_bool, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_bool($slot) }; + (get_str, $s:ident, $slot:expr, $default:expr) => { ($s.0).get_str($slot) }; + (get_struct, $s:ident, $slot:expr, $ty:ty) => { ($s.0).get_struct::<$ty>($slot) }; +} + + +#[macro_export] +macro_rules! table_get_fn { + (($name:ident, get_struct, $ty:ty, $slot:expr)) => { + pub fn $name(&self) -> Option<$ty> { + table_fn!(get_struct, self, $slot, $ty) + } + }; + (($name:ident, vector, $ty:ty, $slot:expr)) => { + pub fn $name<'a>(&self) -> flatbuffers::Iter<'a, $ty> { + table_fn!(vector, self, $slot, $ty) + } + }; + (($name:ident, simple_enum, $n:ident, $ty:ty, $enum_mod:ident, $slot:expr, $default:expr)) => { + pub fn $name(&self) -> Option<$enum_mod> { + let v = table_fn!($n, self, $slot, $default); + $enum_mod::from(v) + } + }; + (($name:ident, union, $ty_fn:ident, $ty:ty, $enum_mod:ident, $slot:expr, $default:expr)) => { + pub fn $name(&self) -> Option<$ty> { + let offset = self.0.field_offset($slot); + if offset != 0 { + let ty = self.$ty_fn(); + let table = self.0.get_root(offset); + return $enum_mod::new(table, ty); + } + None + } + }; + (($name:ident, $n:ident, $ty:ty, $slot:expr, $default:expr)) => { + pub fn $name(&self) -> $ty { + table_fn!($n, self, $slot, $default) + } + } +} + +#[macro_export] +macro_rules! struct_get_fn { + (($name:ident, get_struct, $ty:ty, $slot:expr)) => { + pub fn $name(&self) -> $ty { + struct_fn!(get_struct, self, $slot, $ty) + } + }; + (($name:ident, simple_enum, $n:ident, $ty:ty, $enum_mod:ident, $slot:expr, $default:expr)) => { + pub fn $name(&self) -> Option<$enum_mod> { + let v = struct_fn!($n, self, $slot, $default); + $enum_mod::from(v) + } + }; + (($name:ident, $n:ident, $ty:ty, $slot:expr, $default:expr)) => { + pub fn $name(&self) -> $ty { + struct_fn!($n, self, $slot, $default) + } + } +} + +#[macro_export] +macro_rules! basic_struct_def { + ($name:ident) => { + #[derive(Debug)] + pub struct $name>($crate::Table); + + impl> $name { + pub fn new(table: $crate::Table) -> $name { + $name ( table ) + } + } + + impl> From<$crate::Table> for $name { + fn from(table: $crate::Table) -> $name { + $name(table) + } + } + } +} + +#[macro_export] +macro_rules! table_object_trait { + ($name:ident, $indirect:expr, $inline_size:expr) => { + impl> $crate::VectorType for $name { + fn inline_size() -> usize { + $inline_size + } + fn read_next(buffer: &[u8]) -> $name { + $crate::Table::get_indirect_root(buffer, 0).into(); + } + } + } +} + +#[macro_export] +macro_rules! table_object { + ($name:ident, $inline_size:expr, [ $( $f:tt ),* ]) => { + + basic_struct_def!{$name} + impl $name { + $( table_get_fn!{$f} )* + } + table_object_trait!{ $name, false, $inline_size } + } +} + +#[macro_export] +macro_rules! struct_object { + ($name:ident, $inline_size:expr, [ $( $f:tt ),* ]) => { + + basic_struct_def!{$name} + impl $name { + $( struct_get_fn!{$f} )* + } + table_object_trait!{ $name, true, $inline_size } + } +} + +#[macro_export] +macro_rules! simple_enum { + ($type_name:ident, $repr:ident, + [ $( ($e_name:ident, $value:expr) ),+ ]) => { + + #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] + #[repr($repr)] + pub enum $type_name { + $( $e_name = $value ),+ + } + + impl $type_name { + pub fn from(value: $repr) -> Option<$type_name> { + match value { + $( $value => Some($type_name::$e_name) ),+, + _ => None + } + } + } + } +} + +#[macro_export] +macro_rules! union { + ($name:ident, + $type_name:ident, + $repr:ident, + [ $( ($e_name:ident, $value:expr, $ty:ty) ),+ ]) => { + + #[derive(Debug)] + pub enum $name { + None, + $( $e_name( $ty ) ),+ + } + + impl $name { + pub fn new(table: $crate::Table, utype: Option<$type_name>) -> Option<$name> { + match utype { + $( Some($type_name::$e_name) => Some( $name::$e_name( table.into() ) ), )* + _ => None + } + } + } + + simple_enum!($type_name, $repr, [ $( ($e_name, $value) ),+ ]); + }; +} diff --git a/rust/src/new_macros.rs b/rust/src/new_macros.rs new file mode 100644 index 00000000000..ad621604b35 --- /dev/null +++ b/rust/src/new_macros.rs @@ -0,0 +1,285 @@ +//! Macro for defining flatbuffer Tables, Structs, Enums and Unions. +//! +//! The `flatbuffers_object!` macro is used to create the necessary +//! biolerplate for using flatbuffers. The recommended use is to +//! generate these macros using the `flatc --rust` command. + +//! # Example Enums +//! +//! +use types::*; + +#[macro_export] +macro_rules! flatbuffers_object { + // Table slot accessor functions + (@table_accessor $i:ident [$ty:ty] $slot:expr,) => { + ($i.0).get_slot_vector::<$ty>($slot) + }; + (@table_accessor $i:ident &str $slot:expr,) => { + ($i.0).get_slot_str($slot) + }; + // enum + (@table_accessor $i:ident $ty:ident($si:ident) $slot:expr, $default:expr) => { + let v = flatbuffers_object!(@table_accessor $i $si $slot, $default); + $ty::from(v) + }; + // Union + (@table_accessor $i:ident Union($ty:ident = $fi:ident) $slot:expr, $default:expr) => { + let ty = $i.$fi(); + let table = ($i.0).get_slot_table($slot) + $ty::new(table, ty) + }; + (@table_accessor $i:ident u8 $slot:expr, $default:expr) => { + ($i.0).get_slot_u8($slot, $default) + }; + (@table_accessor $i:ident i8 $slot:expr, $default:expr) => { + ($i.0).get_slot_i8($slot, $default) + }; + (@table_accessor $i:ident u16 $slot:expr, $default:expr) => { + ($i.0).get_slot_u16($slot, $default) + }; + (@table_accessor $i:ident i16 $slot:expr, $default:expr) => { + ($i.0).get_slot_i16($slot, $default) + }; + (@table_accessor $i:ident u32 $slot:expr, $default:expr) => { + ($i.0).get_slot_u32($slot, $default) + }; + (@table_accessor $i:ident i32 $slot:expr, $default:expr) => { + ($i.0).get_slot_i32($slot, $default) + }; + (@table_accessor $i:ident u64 $slot:expr, $default:expr) => { + ($i.0).get_slot_u64($slot, $default) + }; + (@table_accessor $i:ident i64 $slot:expr, $default:expr) => { + ($i.0).get_slot_i64($slot, $default) + }; + (@table_accessor $i:ident f32 $slot:expr, $default:expr) => { + ($i.0).get_slot_f32($slot, $default) + }; + (@table_accessor $i:ident f64 $slot:expr, $default:expr) => { + ($i.0).get_slot_f64($slot, $default) + }; + (@table_accessor $i:ident bool $slot:expr, $default:expr) => { + ($i.0).get_slot_bool($slot, $default) + }; + (@table_accessor $i:ident $ty:ident $slot:expr,) => { + ($i.0).get_slot_struct($slot) + }; + // Struct slot accessor functions + (@table_accessor $i:ident &str $slot:expr,) => { + ($i.0).get_str($slot) + }; + (@struct_accessor $i:ident u8 $slot:expr) => { + ($i.0).get_u8($slot) + }; + (@struct_accessor $i:ident i8) $slot:expr => { + ($i.0).get_i8($slot) + }; + (@struct_accessor $i:ident u16 $slot:expr) => { + ($i.0).get_u16($slot) + }; + (@struct_accessor $i:ident i16 $slot:expr) => { + ($i.0).get_i16($slot) + }; + (@struct_accessor $i:ident u32 $slot:expr) => { + ($i.0).get_u32($slot) + }; + (@struct_accessor $i:ident i32 $slot:expr) => { + ($i.0).get_i32($slot) + }; + (@struct_accessor $i:ident u64 $slot:expr) => { + ($i.0).get_u64($slot) + }; + (@struct_accessor $i:ident i64 $slot:expr) => { + ($i.0).get_i64($slot) + }; + (@struct_accessor $i:ident f32 $slot:expr) => { + ($i.0).get_f32($slot) + }; + (@struct_accessor $i:ident f64 $slot:expr) => { + ($i.0).get_f64($slot) + }; + (@struct_accessor $i:ident bool $slot:expr) => { + ($i.0).get_bool($slot) + }; + (@struct_accessor $i:ident $ty:tt $slot:expr,) => { + ($i.0).get_struct($slot) + }; + // enum + (@struct_accessor $i:ident $ty:ident($si:ident) $slot:expr, $default:expr) => { + let v = flatbuffers_object!(@table_accessor $i $si $slot, $default); + $ty::from(v) + }; + // For each @field build + // 1. table accessor functions, + // 2. builder trait definition, + // 3. builder functions + // and then recurse until no more fields. + // (@field { field { } }, ($($table:tt)*), ($($traits:tt)*), ($($builder:tt)*)) + // => { + // flatbuffers_object!{@field ($($table:tt)*), ($($traits:tt)*), ($($builder:tt)*)} + // }; + (@table_fun { }) => {}; + (@table_fun { field => }) => {}; + (@table_fun { field => { name = $name:ident, + typeOf = $ret_ty:ty, + slot = $slot:expr + $(, default = $default:expr)* + $(, comment = $comment:expr)*} + $(, field => $body:tt)* $(,)*}) => { + $( #[doc = $comment] )* + fn $name(&self) -> $ret_ty { + flatbuffers_object!(@table_accessor self $ret_ty $slot, $($default)*) + } + flatbuffers_object!(@table_fun { field => $($body)* }); + }; + (@struct_fun { }) => {}; + (@struct_fun { field => }) => {}; + (@struct_fun { field => { name = $name:ident, + typeOf = $ret_ty:ty, + slot = $slot:expr + $(, default = $default:expr)* + $(, comment = $comment:expr)*} + $(, field => $body:tt)* $(,)*}) => { + $( #[doc = $comment] )* + fn $name(&self) -> $ret_ty { + flatbuffers_object!(@struct_accessor self $ret_ty $slot) + } + flatbuffers_object!(@struct_fun { field => $($body)* }); + }; + (@from_table $name:ident) => { + impl> From<$crate::Table> for $name { + fn from(table: $crate::Table) -> $name { + $name(table) + } + } + } + (@vector_type $name:ident $size:expr) => { + impl> VectorType for $name { + fn inline_size() -> usize { + $size + } + + fn read_next(buffer: &[u8]) -> $name { + Table::get_indirect_root(buffer, 0).into(); + } + } + }; + // Table + ($name:ident => + $body:tt ) => { + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct $name>($crate::Table); + + impl> $name { + flatbuffers_object!(@table_fun $body); + } + + flatbuffers_object!(@from_table $name); + + flatbuffers_object!(@vector_type $name UOFFSETT_SIZE); + + }; + // Struct + ($name:ident => + fixed_size = $inline_size:expr, + $body:tt ) => { + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct $name>($crate::Table); + + impl> $name { + flatbuffers_object!(@struct_fun $body); + } + + flatbuffers_object!(@from_table $name); + + flatbuffers_object!(@vector_type $name $inline_size); + }; + // Simple Enum + ($name:ident => Enum {$( ($e_name:ident = $value:expr) ),+ } + as $repr:ty) => { + #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] + #[repr($repr)] + pub enum $name { + $( $e_name = $value ),+ + } + + impl $name { + pub fn from(value: $repr) -> Option<$name> { + match value { + $( $value => Some($name::$e_name) ),+, + _ => None + } + } + } + }; + // Union + ($name:ident => $ty:ident {$( ($e_name:ident = $value:expr) ),+ } + as $repr:ty) => { + #[derive(Debug)] + pub enum $name { + None, + $( $e_name( $ty ) ),+ + } + + impl $name { + pub fn new(table: $crate::Table, utype: Option<$type_name>) -> Option<$name> { + match utype { + $( Some($type_name::$e_name) => Some( $name::$e_name( table.into() ) ), )* + _ => None + } + } + } + + flatbuffers_object!($ty => Enum {$( ($e_name = $value) ),+ } as $repr); + } +} + +// use table::Table; + +// pub struct Vec3; + +// impl<'a> From> for Vec3 { +// fn from(table: Table) -> Vec3 { +// Vec3 +// } +// } + +// /// an example documentation comment: monster object +// flatbuffers_object!{Monster => +// { field => { name = pos, +// typeOf = Vec3, +// slot = 4, +// comment = "///Testing all my ways are dead\ntestomg"}, +// field => { name = mana, +// typeOf = i16, +// slot = 6, +// default = 150, +// comment = "///Testing all my ways are dead\ntestomg", +// comment = "///Testing all my ways are dead\ntestomg"}, +// field => { name = color, +// typeOf = Color(i8), +// slot = 6, +// default = 8, +// comment = "///Testing all my ways are dead\ntestomg", +// comment = "///Testing all my ways are dead\ntestomg"}, +// field => { name = test_type, +// typeOf = AnyType(u8), +// slot = 8, +// default = 0, +// comment = "///Testing all my ways are dead\ntestomg", +// comment = "///Testing all my ways are dead\ntestomg"}, +// field => { name = test, +// typeOf = Union(Any = test_type), +// slot = 10, +// default = 0, +// comment = "///Testing all my ways are dead\ntestomg", +// comment = "///Testing all my ways are dead\ntestomg"}, +// } +// } + +// flatbuffers_object!{Color => Enum{ Red = 1, Blue = 2} as i8} + +// flatbuffers_object!{Any => AnyType{ } as u8} diff --git a/rust/src/table.rs b/rust/src/table.rs new file mode 100644 index 00000000000..298cd496e82 --- /dev/null +++ b/rust/src/table.rs @@ -0,0 +1,424 @@ +use std::{ops, str}; +use byteorder::{ByteOrder, LittleEndian}; + +use types::*; +use iter::{Iter, VectorType}; + +/// Table provides functions to read Flatbuffer data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Table> { + inner: T, + pos: usize, +} + +impl> Table { + /// Create a table using from a slice using data starting at `pos`. + /// First `UOffsetT` should indicate the offset from the root + /// of the table to the begining of the object. + /// + /// In the following example pos should be 0: + /// ``` + /// 16, 0, 0, 0, // root of table: points to object <--------- + /// 0, 0, // padding + /// 10, 0, // vtable bytes + /// 8, 0, // size of object + /// 7, 0, // start of value 0 + /// 6, 0, // start of value 1 + /// 5, 0, // start of value 2 + /// 10, 0, 0, 0, // start of object..ie. vtable offset + /// 0, // padding + /// 77, // value 2 + /// 66, // value 1 + /// 55, // value 0 + /// ``` + pub fn get_indirect_root(buffer: T, pos: usize) -> Table { + let offset = Table::::read_uoffset(&buffer.as_ref()[pos..]) as usize; + Table { + inner: buffer, + pos: offset + pos, + } + } + + /// Create a table for an object. + /// `pos` should be the offset to the first byte of + /// useable data. + /// + /// In the following example pos should be 16: + /// ``` + /// 16, 0, 0, 0, // root of table: points to object + /// 0, 0, // padding + /// 10, 0, // vtable bytes + /// 8, 0, // size of object + /// 7, 0, // start of value 0 + /// 6, 0, // start of value 1 + /// 5, 0, // start of value 2 + /// 10, 0, 0, 0, // start of object..ie. vtable offset <--------- + /// 0, // padding + /// 77, // value 2 + /// 66, // value 1 + /// 55, // value 0 + /// ``` + pub fn get_root(buffer: T, pos: usize) -> Table { + Table { + inner: buffer, + pos: pos, + } + } + + // Returns a slice of table including the vtable. + // + // First element of root table is the vtable offset + // and is by default a negative direction. + #[inline(always)] + fn vtable(&self) -> &[u8] { + let vt_offset = Table::::read_soffset(&self[self.pos..]); + let vtable = (self.pos as i32 - vt_offset) as usize; + &self.as_bytes()[vtable..] + } + + /// Returns the field offset or 0 if the field was not present. + pub fn field_offset(&self, field: VOffsetT) -> VOffsetT { + let vtable = self.vtable(); + let vt_size = LittleEndian::read_u16(vtable); + if field < vt_size { + return LittleEndian::read_u16(&vtable[field as usize..]); + } + 0 + } + + /// Returns the `bool` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_bool(&self, slot: VOffsetT, default: bool) -> bool { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_bool(offset); + } + default + } + + /// Returns the `u8` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_u8(&self, slot: VOffsetT, default: u8) -> u8 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_u8(offset); + } + default + } + + /// Returns the `i8` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_i8(&self, slot: VOffsetT, default: i8) -> i8 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_i8(offset); + } + default + } + + /// Returns the `u16` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_u16(&self, slot: VOffsetT, default: u16) -> u16 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_u16(offset); + } + default + } + + /// Returns the `i16` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_i16(&self, slot: VOffsetT, default: i16) -> i16 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_i16(offset); + } + default + } + + /// Returns the `u32` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_u32(&self, slot: VOffsetT, default: u32) -> u32 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_u32(offset); + } + default + } + + /// Returns the `i32` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_i32(&self, slot: VOffsetT, default: i32) -> i32 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_i32(offset); + } + default + } + + /// Returns the `u64` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_u64(&self, slot: VOffsetT, default: u64) -> u64 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_u64(offset); + } + default + } + + /// Returns the `i64` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_i64(&self, slot: VOffsetT, default: i64) -> i64 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_i64(offset); + } + default + } + + /// Returns the `f32` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_f32(&self, slot: VOffsetT, default: f32) -> f32 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_f32(offset); + } + default + } + + /// Returns the `f64` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_f64(&self, slot: VOffsetT, default: f64) -> f64 { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_f64(offset); + } + default + } + + /// Returns the `&str` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_str(&self, slot: VOffsetT) -> &str { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_str(offset); + } + "" + } + + /// Returns the struct `T` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_struct<'a, O>(&'a self, slot: VOffsetT) -> Option + where O: From> + AsRef<[u8]> + { + let offset = self.field_offset(slot); + if offset != 0 { + return Some(self.get_struct(offset)); + } + None + } + + /// Returns the a `Table` value of the field at the offset written in the + /// vtable `slot`. + pub fn get_slot_table(&self, slot: VOffsetT) -> Option> { + let offset = self.field_offset(slot); + if offset != 0 { + return Some(self.get_table(offset)); + } + None + } + + + /// Returns the vector value of the field at the offset + /// written in the vtable `slot`. + pub fn get_slot_vector<'a, V>(&'a self, slot: VOffsetT) -> Iter + where V: VectorType<'a> + { + let offset = self.field_offset(slot); + if offset != 0 { + return self.get_vector::(offset); + } + Default::default() + } + + /// Returns byte slice from data stored inside + /// the flatbuffer. + pub fn get_vector<'a, V>(&'a self, offset: VOffsetT) -> Iter + where V: VectorType<'a> + { + let mut offset = offset as usize + self.pos; + let length = Table::::read_uoffset(&self[offset..]) as usize; + offset += length; + let start = offset + UOFFSETT_SIZE; + Iter::new(&self.as_bytes(), start, length) + } + + /// Returns a value of `bool` at `offset`. + pub fn get_bool(&self, offset: VOffsetT) -> bool { + let pos = self.pos as usize; + self.as_bytes()[pos + offset as usize] == 1 + } + + /// Returns a value of `u8` at `offset`. + pub fn get_u8(&self, offset: VOffsetT) -> u8 { + let pos = self.pos as usize; + self.as_bytes()[pos + offset as usize] + } + + /// Returns a value of `i8` at `offset`. + pub fn get_i8(&self, offset: VOffsetT) -> i8 { + let pos = self.pos as usize; + self.as_bytes()[pos + offset as usize] as i8 + } + + /// Returns a value of `u16` at `offset`. + pub fn get_u16(&self, offset: VOffsetT) -> u16 { + let pos = self.pos as usize; + LittleEndian::read_u16(&self[pos + offset as usize..]) + } + + /// Returns a value of `i16` at `offset`. + pub fn get_i16(&self, offset: VOffsetT) -> i16 { + let pos = self.pos as usize; + LittleEndian::read_i16(&self[pos + offset as usize..]) + } + + /// Returns a value of `u32` at `offset`. + pub fn get_u32(&self, offset: VOffsetT) -> u32 { + let pos = self.pos as usize; + LittleEndian::read_u32(&self[pos + offset as usize..]) + } + + /// Returns a value of `i32` at `offset`. + pub fn get_i32(&self, offset: VOffsetT) -> i32 { + let pos = self.pos as usize; + LittleEndian::read_i32(&self[pos + offset as usize..]) + } + + /// Returns a value of `u64` at `offset`. + pub fn get_u64(&self, offset: VOffsetT) -> u64 { + let pos = self.pos as usize; + LittleEndian::read_u64(&self[pos + offset as usize..]) + } + + /// Returns a value of `i64` at `offset`. + pub fn get_i64(&self, offset: VOffsetT) -> i64 { + let pos = self.pos as usize; + LittleEndian::read_i64(&self[pos + offset as usize..]) + } + + /// Returns a value of `f32` at `offset`. + pub fn get_f32(&self, offset: VOffsetT) -> f32 { + let pos = self.pos as usize; + LittleEndian::read_f32(&self[pos + offset as usize..]) + } + + /// Returns a value of `f64` at `offset`. + pub fn get_f64(&self, offset: VOffsetT) -> f64 { + let pos = self.pos as usize; + LittleEndian::read_f64(&self[pos + offset as usize..]) + } + + /// Returns a value of `&str` at `offset`. + pub fn get_str(&self, offset: VOffsetT) -> &str { + let offset = offset as usize + self.pos; + Table::::read_str(&self[offset..]) + } + + /// Retrieve a struct table from offset. Offset should point to the + /// first usable byte of data i.e. the Vtable offset or start + /// of struct + pub fn get_struct<'a, O>(&'a self, offset: VOffsetT) -> O + where O: From> + { + let table = self.get_table(offset); + table.into() + } + + /// Retrieve a `Table` from offset. Offset should point to the + /// first usable byte of data i.e. the Vtable offset or start + /// of struct + pub fn get_table(&self, offset: VOffsetT) -> Table<&[u8]> { + let pos = self.pos as usize + offset as usize; + Table::get_root(self.inner.as_ref(), pos as usize) + } + + /// Accesor function for the tables position in the buffer. + #[inline] + pub fn get_pos(&self) -> UOffsetT { + self.pos as UOffsetT + } + + /// Reads an `UOffsetT` at exact position. + pub fn get_uoffset(&self, pos: usize) -> UOffsetT { + Table::::read_uoffset(&self[pos..]) + } + + /// Reads an `VOffsetT` at exact position. + pub fn get_voffset(&self, pos: usize) -> VOffsetT { + Table::::read_voffset(&self[pos..]) + } + + /// Read a Signed offset value at given pos. + pub fn get_soffset(&self, pos: usize) -> SOffsetT { + Table::::read_soffset(&self[pos..]) + } + + /// Get a reference to the raw buffer. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.inner.as_ref() + } + + /// Return the backing buffer. + pub fn into_inner(self) -> T { + self.inner + } + + /// Read a string starting at the beginning of the buffer. + pub fn read_str(buffer: &[u8]) -> &str { + let offset = Table::::read_uoffset(buffer) as usize; + let start = offset + UOFFSETT_SIZE; + let length = Table::::read_uoffset(&buffer[offset..]) as usize; + let s = &buffer[start..start + length]; + unsafe { str::from_utf8_unchecked(s) } + } + + /// Read a `UOffsetT` starting at the beginning of the buffer. + pub fn read_uoffset(buffer: &[u8]) -> UOffsetT { + LittleEndian::read_u32(buffer) + } + + /// Read a `VOffsetT` starting at the beginning of the buffer. + pub fn read_voffset(buffer: &[u8]) -> VOffsetT { + LittleEndian::read_u16(buffer) + } + + /// Read a `SOffsetT` starting at the beginning of the buffer. + pub fn read_soffset(buffer: &[u8]) -> SOffsetT { + LittleEndian::read_i32(buffer) + } +} + +impl From> for Table> { + fn from(buf: Vec) -> Table> { + Table { + inner: buf, + pos: 0, + } + } +} + +impl> ops::Deref for Table { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.inner.as_ref() + } +} + +impl ops::DerefMut for Table> { + fn deref_mut<'a>(&'a mut self) -> &'a mut [u8] { + &mut self.inner + } +} diff --git a/rust/src/types.rs b/rust/src/types.rs new file mode 100644 index 00000000000..5f903d25993 --- /dev/null +++ b/rust/src/types.rs @@ -0,0 +1,20 @@ + +/// A SOffsetT stores a signed offset into arbitrary data. +pub type SOffsetT = i32; + +/// A UOffsetT stores an unsigned offset into vector data. +pub type UOffsetT = u32; + +/// A VOffsetT stores an unsigned offset in a vtable. +pub type VOffsetT = u16; + +/// Byte size of a `VOffsetT`. +pub const VOFFSETT_SIZE: usize = 2; +/// Byte size of a `UOffsetT`. +pub const UOFFSETT_SIZE: usize = 4; + +/// Number of metadata fields in a `VTable`. +/// Vtable Len and Object Size. +pub const VTABLE_METADATA_FIEDS: usize = 2; +/// Byte size of `Vtable` metadata. +pub const VTABLE_METADATA_SIZE: usize = VTABLE_METADATA_FIEDS * VOFFSETT_SIZE; diff --git a/samples/rust/Cargo.toml b/samples/rust/Cargo.toml new file mode 100644 index 00000000000..0a88c980c85 --- /dev/null +++ b/samples/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rust" +version = "0.1.0" +authors = ["Joseph Dunne "] + +[dependencies] +flatbuffers = "*" diff --git a/samples/rust/src/main.rs b/samples/rust/src/main.rs new file mode 100644 index 00000000000..e7a11a969c0 --- /dev/null +++ b/samples/rust/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/flatc.cpp b/src/flatc.cpp index da0085f9989..c57dd948b05 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -108,6 +108,7 @@ std::string FlatCompiler::GetUsageString(const char* program_name) const { " --keep-prefix Keep original prefix of schema include statement.\n" " --no-fb-import Don't include flatbuffers import statement for TypeScript.\n" " --no-ts-reexport Don't re-export imported dependencies for TypeScript.\n" + " --strict-rust Follow naming conventions for Rust.\n" "FILEs may be schemas (must end in .fbs), or JSON files (conforming to preceding\n" "schema). FILEs after the -- must be binary flatbuffer format files.\n" "Output files are named using the base file name of the input,\n" @@ -234,6 +235,8 @@ int FlatCompiler::Compile(int argc, const char** argv) { opts.skip_flatbuffers_import = true; } else if(arg == "--no-ts-reexport") { opts.reexport_ts_modules = false; + } else if(arg == "--strict-rust") { + opts.strict_rust = true; } else { for (size_t i = 0; i < params_.num_generators; ++i) { if (arg == params_.generators[i].generator_opt_long || diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp index 02d01c032cd..0231312adc5 100644 --- a/src/flatc_main.cpp +++ b/src/flatc_main.cpp @@ -101,6 +101,11 @@ int main(int argc, const char *argv[]) { flatbuffers::IDLOptions::kJsonSchema, "Generate Json schema", flatbuffers::GeneralMakeRule }, + { flatbuffers::GenerateRust, "-r", "--rust", "Rust", true, + nullptr, + flatbuffers::IDLOptions::kRust, + "Generate Rust files for tables/structs", + flatbuffers::GeneralMakeRule }, }; flatbuffers::FlatCompiler::InitParams params; diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index fb57dc9d326..d20eaae8458 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -302,7 +302,7 @@ class CppGenerator : public BaseGenerator { // Return a C++ type from the table in idl.h std::string GenTypeBasic(const Type &type, bool user_facing_type) const { static const char *ctypename[] = { - #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, 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 30a097d71da..7f652e75951 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -242,7 +242,7 @@ static bool IsEnum(const Type& type) { std::string GenTypeBasic(const Type &type, bool enableLangOverrides) { static const char *java_typename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ #JTYPE, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD @@ -250,7 +250,7 @@ std::string GenTypeBasic(const Type &type, bool enableLangOverrides) { static const char *csharp_typename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - 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 8d4a7a595ac..43057b58866 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -695,7 +695,7 @@ static std::string GenMethod(const FieldDef &field) { static std::string GenTypeBasic(const Type &type) { static const char *ctypename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - 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_php.cpp b/src/idl_gen_php.cpp index 893f16f2963..713e47d7410 100644 --- a/src/idl_gen_php.cpp +++ b/src/idl_gen_php.cpp @@ -910,7 +910,7 @@ namespace php { static std::string GenTypeBasic(const Type &type) { static const char *ctypename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - 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 acfcf0be28b..45b85e1a4d6 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -581,7 +581,7 @@ static std::string GenMethod(const FieldDef &field) { static std::string GenTypeBasic(const Type &type) { static const char *ctypename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - 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 00000000000..81c965a41f3 --- /dev/null +++ b/src/idl_gen_rust.cpp @@ -0,0 +1,713 @@ +/* + * Copyright 2014 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 +#include + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" +#include "flatbuffers/code_generators.h" + +namespace flatbuffers { +namespace rust { + + //static std::string GenGetter(const Type &type); +static std::string GenMethod(const FieldDef &field); +static void GenStructBuilder(const StructDef &struct_def, + std::string *code_ptr); +static std::string GenTypeBasic(const Type &type); +static std::string GenTypeGet(const Type &type); +static std::string TypeName(const FieldDef &field); + +// Hardcode spaces per indentation. +const std::string Indent = " "; +const std::string TwoIndent = Indent + Indent; +const std::string ThreeIndent = Indent + Indent + Indent; + + +// Format a module name from struct/enum definitions. +static std::string ModName(std::string &def_name) { + std::string name = def_name; + std::transform(name.begin(), name.end(), + name.begin(), ::tolower); + return name; +} + +// Begin by declaring namespace and imports. +static void BeginFile(std::string *code_ptr) { + std::string &code = *code_ptr; + code += "//! Automatically generated, do not modify.\n\n"; + // the flatbuffers runtime lib + code += "use flatbuffers;\n"; + // definitions in the same namepsace + code += "use super::*;\n\n"; +} + + static std::string MapConstant(const FieldDef &field) { + if ( (IsScalar(field.value.type.base_type)) + && (TypeName(field).compare("bool") == 0) ) { + if (field.value.constant == "0") { + return "false"; + } + return "true"; + } + return field.value.constant; + } + +// Begin a table struct declaration. +static void TableStructDefinition(const StructDef &struct_def, + std::string *code_ptr) { + std::string &code = *code_ptr; + if (!struct_def.fixed) { + code += "flatbuffers_object!{Table => " + struct_def.name; + } else { + code += "flatbuffers_object!{Struct => " + struct_def.name; + code += " ( size:"; + code += NumToString(struct_def.bytesize) + ","; + code += " align: " + NumToString(struct_def.minalign) + ")"; + } + code += " ["; + bool first = true; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); + ++it) { + auto &field = **it; + if (field.deprecated) continue; + if (first) { + code += "\n"; + first = false; + } else { + code += ", \n"; + } + code += " field => { "; + //GenComment(field.doc_comment, code_ptr, nullptr, ""); + if ( (IsScalar(field.value.type.base_type)) + && !(field.value.type.enum_def)) { + code += "name = "; + code += field.name + ",\n"; + code += ThreeIndent +"typeOf = " + TypeName(field); + code += ",\n"; + code += ThreeIndent + "slot = " + NumToString(field.value.offset); + code += ",\n"; + code += ThreeIndent + "default = " + MapConstant(field); + if (field.padding) { + code += ",\n"; + code += ThreeIndent + "padding = " + NumToString(field.padding); + } + code += " }"; + continue; + } + if ( (IsScalar(field.value.type.base_type)) + && (field.value.type.enum_def) ) { + code += "name = " + field.name; + code += ",\n"; + code += ThreeIndent +"typeOf = enum"; + if (field.value.type.enum_def->is_union) { + code += " " + field.value.type.enum_def->name; + code += "Type"; + } else { + code += " " + field.value.type.enum_def->name; + } + code += " " + TypeName(field); + code += ",\n"; + code += ThreeIndent + "slot = " + NumToString(field.value.offset); + code += ",\n"; + code += ThreeIndent + "default = " + MapConstant(field); + if (field.padding) { + code += ",\n"; + code += ThreeIndent + "padding = " + NumToString(field.padding); + } + code += " }"; + continue; + } + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: { + code += "name = "; + code += field.name + ",\n"; + code += ThreeIndent +"typeOf = "; + code += TypeName(field); + code += ",\n"; + code += ThreeIndent + "slot = " + NumToString(field.value.offset); + if (field.padding) { + code += ",\n"; + code += ThreeIndent + "padding = " + NumToString(field.padding); + } + code += " }"; + break; + } + case BASE_TYPE_STRING: { + code += "name = "; + code += field.name + ",\n"; + code += ThreeIndent + "typeOf = string"; + code += ",\n"; + code += ThreeIndent + "slot = " + NumToString(field.value.offset); + code += ",\n"; + code += ThreeIndent + "default = " + MapConstant(field); + if (field.padding) { + code += ",\n"; + code += ThreeIndent + "padding = " + NumToString(field.padding); + } + code += " }"; + break; + } + case BASE_TYPE_VECTOR: { + code += "name = "; + code += field.name + ",\n"; + code += ThreeIndent +"typeOf = "; + code += TypeName(field); + code += ",\n"; + code += ThreeIndent + "slot = "; + code += NumToString(field.value.offset); + if (field.padding) { + code += ",\n"; + code += ThreeIndent + "padding = " + NumToString(field.padding); + } + code += " }"; + break; + } + case BASE_TYPE_UNION: + code += "name = "; + code += field.name + ",\n"; + code += ThreeIndent +"typeOf = union "; + code += TypeName(field); + code += ",\n"; + code += ThreeIndent + "slot = " + NumToString(field.value.offset); + code += ",\n"; + code += ThreeIndent + "default = " + MapConstant(field); + if (field.padding) { + code += ",\n"; + code += ThreeIndent + "padding = " + NumToString(field.padding); + } + code += " }"; + break; + default: + assert(0); + } + } + code += "]}\n\n"; + +} + +std::string GenFieldOffsetName(const FieldDef &field, + bool qualified) { + std::string uname = field.name; + std::transform(uname.begin(), uname.end(), uname.begin(), ::toupper); + if (qualified) { + return "VT::" + uname; + } else { + return uname; + } +} + +// Most field accessors need to retrieve and test the field offset first, +// this is the prefix code for that. +std::string OffsetPrefix(const FieldDef &field) { + return Indent + Indent + + "let offset = self.table.field_offset(" + + GenFieldOffsetName(field, true) + + " as u16);\n" + Indent + Indent + "if offset != 0 {\n"; +} + +// Begin the creator function signature. +static void BeginBuilderArgs(const StructDef &struct_def, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string lname = struct_def.name; + std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower); + code += Indent + "fn build_" + lname; + code += "(&mut self"; +} + +static void BeginBuilderTraitArgs(const StructDef &struct_def, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string lname = struct_def.name; + std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower); + code += "pub trait " + struct_def.name + "Builder {\n"; + code += Indent + "fn build_" + lname; + code += "(&mut self"; +} + +// Recursively generate arguments for a constructor, to deal with nested +// structs. +static void StructBuilderArgs(const StructDef &struct_def, + const char *nameprefix, + std::string *code_ptr, + bool name_only = false) { + std::string &code = *code_ptr; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); + ++it) { + auto &field = **it; + if (IsStruct(field.value.type)) { + // Generate arguments for a struct inside a struct. To ensure names + // don't clash, and to make it obvious these arguments are constructing + // a nested struct, prefix the name with the field name. + StructBuilderArgs(*field.value.type.struct_def, + (nameprefix + (field.name + "_")).c_str(), + code_ptr, name_only); + } else { + code += (std::string)", " + nameprefix; + code += field.name; + if (!name_only) { + code += ": " + GenTypeBasic(field.value.type); + } + } + } +} + +// End the creator function signature. +static void EndBuilderArgs(std::string *code_ptr) { + std::string &code = *code_ptr; + code += ") -> flatbuffers::UOffsetT {\n"; +} + +// End the creator function signature. +static void EndBuilderTraitArgs(const StructDef &struct_def, + std::string *code_ptr) { + std::string &code = *code_ptr; + code += ") -> flatbuffers::UOffsetT;\n"; + code += "}\n\n"; + code += "impl " + struct_def.name + "Builder for flatbuffers::Builder {\n"; +} + +// Recursively generate struct construction statements and instert manual +// padding. +static void StructBuilderBody(const StructDef &struct_def, + const char *nameprefix, + std::string *code_ptr) { + std::string &code = *code_ptr; + code += Indent + Indent + "self.prep(" + NumToString(struct_def.minalign) + ", "; + code += NumToString(struct_def.bytesize) + ");\n"; + for (auto it = struct_def.fields.vec.rbegin(); + it != struct_def.fields.vec.rend(); + ++it) { + auto &field = **it; + if (field.padding) + code += Indent + Indent + "self.pad(" + NumToString(field.padding) + ");\n"; + if (IsStruct(field.value.type)) { + StructBuilderBody(*field.value.type.struct_def, + (nameprefix + (field.name + "_")).c_str(), + code_ptr); + } else { + code += Indent + Indent + "self.add_" + GenMethod(field) + "("; + code += nameprefix + field.name + ");\n"; + } + } +} + +// End the builder function +static void EndBuilderBody(std::string *code_ptr) { + std::string &code = *code_ptr; + code += Indent + Indent +"self.offset() as flatbuffers::UOffsetT \n"; + code += Indent + "}\n"; + code += "}\n\n"; +} + +// Get the value of a table's starting offset. +static void GetStartOfTable(const StructDef &struct_def, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string lname = struct_def.name; + std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower); + code += Indent; + code += "fn start_" + lname + "(&mut self) {\n"; + code += Indent + Indent; + code += "self.start_object("; + code += NumToString(struct_def.fields.vec.size()); + code += ");\n"; + code += Indent + "}\n\n"; +} + +static void BuildFieldOfTableDef(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string lname = struct_def.name; + std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower); + code += Indent; + code += "/// Set the value for field `" + field.name + "`.\n"; + code += Indent; + code += "fn add_" + field.name; + code += "(&mut self, "; + code += field.name + ": "; + if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { + code += "flatbuffers::UOffsetT"; + } else { + code += GenTypeBasic(field.value.type); + } + code += ");\n"; +} + +// Set the value of a table's field. +static void BuildFieldOfTable(const StructDef &struct_def, + const FieldDef &field, + const size_t offset, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string lname = struct_def.name; + std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower); + code += Indent; + code += "fn add_" + field.name; + code += "(&mut self, "; + code += field.name + ": "; + if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { + code += "flatbuffers::UOffsetT"; + } else { + code += GenTypeBasic(field.value.type); + } + code += ") {\n"; + code += Indent + Indent; + code += "self.add_slot_"; + code += GenMethod(field) + "("; + code += NumToString(offset) + ", "; + code += field.name; + code += ", " + MapConstant(field); + code += ")\n"; + code += Indent + "}\n\n"; +} + +// Set the value of one of the members of a table's vector. +static void BuildVectorOfTableDef(const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + code += Indent; + code += "/// Initializes bookkeeping for writing a new `"; + code += field.name + "` vector.\n"; + code += Indent; + code += "fn start_"; + code += field.name; + code += "_vector(&mut self, numElems: usize);\n"; +} + + + +// Set the value of one of the members of a table's vector. +static void BuildVectorOfTable(const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + code += Indent; + code += "/// Initializes bookkeeping for writing a new `"; + code += field.name + "` vector.\n"; + code += Indent; + code += "fn start_"; + code += field.name; + code += "_vector(&mut self, numElems: usize) {\n"; + code += Indent + Indent; + code += "self.start_vector("; + auto vector_type = field.value.type.VectorType(); + auto alignment = InlineAlignment(vector_type); + auto elem_size = InlineSize(vector_type); + code += NumToString(elem_size); + code += ", numElems, " + NumToString(alignment); + code += ")\n"; + code += Indent + "}\n\n"; +} + +// Generate table constructors, conditioned on its members' types. +static void GenTableBuilderStructImpl(const StructDef &struct_def, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string lname = struct_def.name; + std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower); + code += "/// Builder Trait for `"+ struct_def.name + "` tables.\n"; + code += "pub trait " + struct_def.name + "Builder {\n"; + code += Indent + "fn start_"+lname+ "(&mut self);\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); + ++it) { + auto &field = **it; + if (field.deprecated) continue; + BuildFieldOfTableDef(struct_def, field, code_ptr); + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + BuildVectorOfTableDef(field, code_ptr); + } + } + code += "}\n\n"; + + code += "impl " + struct_def.name + "Builder for flatbuffers::Builder {\n"; +} + +// Generate table constructors, conditioned on its members' types. +static void GenEndTableBuilderStructImpl(std::string *code_ptr) { + std::string &code = *code_ptr; + code += "}\n\n";} + +// Generate table constructors, conditioned on its members' types. +static void GenTableBuilders(const StructDef &struct_def, + std::string *code_ptr) { + GenTableBuilderStructImpl(struct_def, code_ptr); + GetStartOfTable(struct_def, code_ptr); + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); + ++it) { + auto &field = **it; + if (field.deprecated) continue; + + auto offset = it - struct_def.fields.vec.begin(); + BuildFieldOfTable(struct_def, field, offset, code_ptr); + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + BuildVectorOfTable(field, code_ptr); + } + } + //GenTableBuilderFxedFns(struct_def, code_ptr); + GenEndTableBuilderStructImpl(code_ptr); +} + +// Generate struct or table methods. +static void GenStruct(const StructDef &struct_def, + std::string *code_ptr) { + if (struct_def.generated) return; + GenComment(struct_def.doc_comment, code_ptr, nullptr); + TableStructDefinition(struct_def, code_ptr); + + if (struct_def.fixed) { + // create a struct constructor function + GenStructBuilder(struct_def, code_ptr); + } else { + // Create a set of functions that allow table construction. + GenTableBuilders(struct_def, code_ptr); + } +} + +// Generate enum declarations. +static void GenEnum(const EnumDef &enum_def, std::string *code_ptr) { + if (enum_def.generated) return; + std::string &code = *code_ptr; + GenComment(enum_def.doc_comment, code_ptr, nullptr); + if (enum_def.is_union) { + code += "flatbuffers_object!{Union =>" + enum_def.name; + } else { + code += "flatbuffers_object!{Enum =>" + enum_def.name; + } + code += "{ "; + bool first = true; + for (auto it = enum_def.vals.vec.begin(); + it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + //GenComment(ev.doc_comment, code_ptr, nullptr, Indent.c_str()); + // if (ev.name.compare("NONE") == 0) { + // continue; + // } + if (first) { + first = false; + } else { + code += ", "; + } + code += ev.name + " = "+ NumToString(ev.value); + } + code += "} as "; + code += GenTypeGet(enum_def.underlying_type); + code += "}\n\n"; + +} + +// Returns the method name for use with add/put calls. +static std::string GenMethod(const FieldDef &field) { + return IsScalar(field.value.type.base_type) + ? GenTypeBasic(field.value.type) + : (IsStruct(field.value.type) ? "struct" : "uoffset"); +} + + +// Generate the mod.rs export. +static std::string GenNameSpaceExports(const Parser &parser_, + std::string &namespace_name) { + std::string code = ""; + std::string re_exports = ""; + code += "//! Automatically generated, do not modify\n"; + code += "//!\n"; + code += "//! Flatbuffer definitions for the " + namespace_name; + code += " namespace.\n"; + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + auto &enum_def = **it; + std::string mod_name = ModName(enum_def.name); + std::string qname = enum_def.defined_namespace->components.back(); + if (namespace_name.compare(qname) == 0) { + code += "mod " + mod_name + ";\n"; + re_exports += "pub use self::" + mod_name + "::*;\n"; + } + } + + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + std::string mod_name = ModName(struct_def.name); + std::string qname = struct_def.defined_namespace->components.back(); + if (namespace_name.compare(qname) == 0) { + code += "pub mod " + mod_name + ";\n"; + re_exports += "pub use self::" + mod_name + "::{"; + re_exports += struct_def.name +", "+ struct_def.name +"Builder};\n"; + } + } + std::vector components = parser_.namespaces_.back()->components; + int pos = find(components.begin(), + components.end(), + namespace_name) + - components.begin(); + unsigned long next_pos = pos + 1; + if(next_pos < components.size()) { + std::string mod_name = ModName(components[next_pos]); + re_exports += "pub mod " + mod_name; + re_exports += ";\n"; + } + code += "\n" + re_exports; + return code; +} + +// Save out the generated code for a Rust Table type. +static bool SaveType(const Parser &parser, const Definition &def, + const std::string &classcode, const std::string &path + ) { + if (!classcode.length()) return true; + + std::string namespace_name; + std::string namespace_dir = path; + auto &namespaces = parser.namespaces_.back()->components; + for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { + if (namespace_name.length()) { + namespace_name += "."; + namespace_dir += kPathSeparator; + } + namespace_name = *it; + namespace_dir += *it; + if (parser.opts.strict_rust) { + std::transform(namespace_dir.begin(), namespace_dir.end(), + namespace_dir.begin(), ::tolower); + } + EnsureDirExists(namespace_dir.c_str()); + + std::string mod_filename = namespace_dir + "/mod.rs"; + std::string mod_exports = GenNameSpaceExports(parser, namespace_name); + SaveFile(mod_filename.c_str(), mod_exports, false); + } + + + std::string code = ""; + BeginFile(&code); + code += classcode; + std::string filename = namespace_dir + kPathSeparator + def.name + ".rs"; + if (parser.opts.strict_rust) { + std::transform(filename.begin(), filename.end(), + filename.begin(), ::tolower); + } + return SaveFile(filename.c_str(), code, false); +} + +static std::string GenTypeBasic(const Type &type) { + static const char *ctypename[] = { + #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ + #RTYPE, + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD + }; + return ctypename[type.base_type]; +} + +static std::string GenTypePointer(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_VECTOR: + return "[" + GenTypeGet(type.VectorType()) + "]"; + case BASE_TYPE_STRUCT: + return type.struct_def->name; + case BASE_TYPE_STRING: + return "string"; + case BASE_TYPE_UNION: + // fall through + default: + return type.enum_def->name; + } +} + +static std::string GenTypeGet(const Type &type) { + return IsScalar(type.base_type) + ? GenTypeBasic(type) + : GenTypePointer(type); +} + +static std::string TypeName(const FieldDef &field) { + std::string ty = GenTypeGet(field.value.type); + ty.erase(remove( ty.begin(), ty.end(), '\"' ), ty.end()); + return ty; +} + +// Create a struct with a builder and the struct's arguments. +static void GenStructBuilder(const StructDef &struct_def, + std::string *code_ptr) { + BeginBuilderTraitArgs(struct_def, code_ptr); + StructBuilderArgs(struct_def, "", code_ptr); + EndBuilderTraitArgs(struct_def, code_ptr); + + + BeginBuilderArgs(struct_def, code_ptr); + StructBuilderArgs(struct_def, "", code_ptr); + EndBuilderArgs(code_ptr); + + StructBuilderBody(struct_def, "", code_ptr); + EndBuilderBody(code_ptr); +} + +class RustGenerator : public BaseGenerator { + public: + RustGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "" /* not used */, + "" /* not used */){}; + bool generate() { + if (!generateEnums()) return false; + if (!generateStructs()) return false; + return true; + } + + private: + bool generateEnums() { + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + auto &enum_def = **it; + std::string enumcode; + GenEnum(enum_def, &enumcode); + if (!SaveType(parser_, enum_def, enumcode, path_)) return false; + } + return true; + } + + bool generateStructs() { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + std::string declcode; + GenStruct(struct_def, &declcode); + if (!SaveType(parser_, struct_def, declcode, path_)) return false; + } + return true; + } +}; + +} // 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(); +} + +} // namespace flatbuffers diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index b7c58756c9e..ff5efed4133 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -136,7 +136,7 @@ template<> bool Print(const void *val, // Call PrintVector above specifically for each element type: switch (type.base_type) { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ case BASE_TYPE_ ## ENUM: \ if (!PrintVector( \ *reinterpret_cast *>(val), \ @@ -227,7 +227,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table, if (is_present) { switch (fd.value.type.base_type) { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - 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)) { \ @@ -238,7 +238,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, ALIASTYPE, \ - 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 4d9e23052d2..7e929b79189 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -33,7 +33,7 @@ namespace flatbuffers { const char *const kTypeNames[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ IDLTYPE, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD @@ -42,7 +42,7 @@ const char *const kTypeNames[] = { const char kTypeSizes[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ sizeof(CTYPE), FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD @@ -148,7 +148,8 @@ template<> inline CheckedError atot>(const char *s, Parser &parser, } std::string Namespace::GetFullyQualifiedName(const std::string &name, - size_t max_components) const { + size_t max_components, + const std::string &sep) const { // Early exit if we don't have a defined namespace. if (components.size() == 0 || !max_components) { return name; @@ -157,7 +158,7 @@ std::string Namespace::GetFullyQualifiedName(const std::string &name, for (size_t i = 0; i < std::min(components.size(), max_components); i++) { if (i) { - stream << "."; + stream << sep; } stream << components[i]; } @@ -197,7 +198,7 @@ enum { FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN) #undef FLATBUFFERS_TOKEN #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ kToken ## ENUM, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD @@ -209,7 +210,7 @@ static std::string TokenToString(int t) { FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN) #undef FLATBUFFERS_TOKEN #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ IDLTYPE, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD @@ -385,7 +386,7 @@ CheckedError Parser::Next() { attribute_.append(start, cursor_); // First, see if it is a type keyword from the table of types: #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ if (attribute_ == IDLTYPE || attribute_ == ALIASTYPE) { \ token_ = kToken ## ENUM; \ return NoError(); \ @@ -1023,7 +1024,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, size == SizeOf(field_value.type.base_type)) { switch (field_value.type.base_type) { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ case BASE_TYPE_ ## ENUM: \ builder_.Pad(field->padding); \ if (struct_def.fixed) { \ @@ -1040,7 +1041,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, ALIASTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ case BASE_TYPE_ ## ENUM: \ builder_.Pad(field->padding); \ if (IsStruct(field->value.type)) { \ @@ -1117,7 +1118,7 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) { auto &val = field_stack_.back().first; switch (val.type.base_type) { #define FLATBUFFERS_TD(ENUM, IDLTYPE, ALIASTYPE, \ - 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/MyGame/Example/Any.rs b/tests/MyGame/Example/Any.rs new file mode 100644 index 00000000000..12d5020382e --- /dev/null +++ b/tests/MyGame/Example/Any.rs @@ -0,0 +1,7 @@ +//! Automatically generated, do not modify. + +use flatbuffers; +use super::*; + +flatbuffers_object!{Union =>Any{ NONE = 0, Monster = 1, TestSimpleTableWithEnum = 2 as u8} + diff --git a/tests/MyGame/Example/Color.rs b/tests/MyGame/Example/Color.rs new file mode 100644 index 00000000000..803e1f29394 --- /dev/null +++ b/tests/MyGame/Example/Color.rs @@ -0,0 +1,7 @@ +//! Automatically generated, do not modify. + +use flatbuffers; +use super::*; + +flatbuffers_object!{Enum =>Color{ Red = 1, Green = 2, Blue = 8 as i8} + diff --git a/tests/MyGame/Example/Monster.rs b/tests/MyGame/Example/Monster.rs new file mode 100644 index 00000000000..3d77c5c9379 --- /dev/null +++ b/tests/MyGame/Example/Monster.rs @@ -0,0 +1,323 @@ +//! Automatically generated, do not modify. + +use flatbuffers; +use super::*; + +/// an example documentation comment: monster object +flatbuffers_object!{Table => Monster [ + field => { name = pos, + typeOf = Vec3, + slot = 4 }, + field => { name = mana, + typeOf = i16, + slot = 6, + default = 150 }, + field => { name = hp, + typeOf = i16, + slot = 8, + default = 100 }, + field => { name = name, + typeOf = string, + slot = 10, + default = 0 }, + field => { name = inventory, + typeOf = [u8], + slot = 14 }, + field => { name = color, + typeOf = enum Color i8, + slot = 16, + default = 8 }, + field => { name = test_type, + typeOf = enum AnyType u8, + slot = 18, + default = 0 }, + field => { name = test, + typeOf = union Any, + slot = 20, + default = 0 }, + field => { name = test4, + typeOf = [Test], + slot = 22 }, + field => { name = testarrayofstring, + typeOf = [string], + slot = 24 }, + field => { name = testarrayoftables, + typeOf = [Monster], + slot = 26 }, + field => { name = enemy, + typeOf = Monster, + slot = 28 }, + field => { name = testnestedflatbuffer, + typeOf = [u8], + slot = 30 }, + field => { name = testempty, + typeOf = Stat, + slot = 32 }, + field => { name = testbool, + typeOf = bool, + slot = 34, + default = false }, + field => { name = testhashs32_fnv1, + typeOf = i32, + slot = 36, + default = 0 }, + field => { name = testhashu32_fnv1, + typeOf = u32, + slot = 38, + default = 0 }, + field => { name = testhashs64_fnv1, + typeOf = i64, + slot = 40, + default = 0 }, + field => { name = testhashu64_fnv1, + typeOf = u64, + slot = 42, + default = 0 }, + field => { name = testhashs32_fnv1a, + typeOf = i32, + slot = 44, + default = 0 }, + field => { name = testhashu32_fnv1a, + typeOf = u32, + slot = 46, + default = 0 }, + field => { name = testhashs64_fnv1a, + typeOf = i64, + slot = 48, + default = 0 }, + field => { name = testhashu64_fnv1a, + typeOf = u64, + slot = 50, + default = 0 }, + field => { name = testarrayofbools, + typeOf = [bool], + slot = 52 }, + field => { name = testf, + typeOf = f32, + slot = 54, + default = 3.14159 }, + field => { name = testf2, + typeOf = f32, + slot = 56, + default = 3.0 }, + field => { name = testf3, + typeOf = f32, + slot = 58, + default = 0.0 }]} + +/// Builder Trait for `Monster` tables. +pub trait MonsterBuilder { + fn start_monster(&mut self); + /// Set the value for field `pos`. + fn add_pos(&mut self, pos: flatbuffers::UOffsetT); + /// Set the value for field `mana`. + fn add_mana(&mut self, mana: i16); + /// Set the value for field `hp`. + fn add_hp(&mut self, hp: i16); + /// Set the value for field `name`. + fn add_name(&mut self, name: flatbuffers::UOffsetT); + /// Set the value for field `inventory`. + fn add_inventory(&mut self, inventory: flatbuffers::UOffsetT); + /// Initializes bookkeeping for writing a new `inventory` vector. + fn start_inventory_vector(&mut self, numElems: usize); + /// Set the value for field `color`. + fn add_color(&mut self, color: i8); + /// Set the value for field `test_type`. + fn add_test_type(&mut self, test_type: u8); + /// Set the value for field `test`. + fn add_test(&mut self, test: flatbuffers::UOffsetT); + /// Set the value for field `test4`. + fn add_test4(&mut self, test4: flatbuffers::UOffsetT); + /// Initializes bookkeeping for writing a new `test4` vector. + fn start_test4_vector(&mut self, numElems: usize); + /// Set the value for field `testarrayofstring`. + fn add_testarrayofstring(&mut self, testarrayofstring: flatbuffers::UOffsetT); + /// Initializes bookkeeping for writing a new `testarrayofstring` vector. + fn start_testarrayofstring_vector(&mut self, numElems: usize); + /// Set the value for field `testarrayoftables`. + fn add_testarrayoftables(&mut self, testarrayoftables: flatbuffers::UOffsetT); + /// Initializes bookkeeping for writing a new `testarrayoftables` vector. + fn start_testarrayoftables_vector(&mut self, numElems: usize); + /// Set the value for field `enemy`. + fn add_enemy(&mut self, enemy: flatbuffers::UOffsetT); + /// Set the value for field `testnestedflatbuffer`. + fn add_testnestedflatbuffer(&mut self, testnestedflatbuffer: flatbuffers::UOffsetT); + /// Initializes bookkeeping for writing a new `testnestedflatbuffer` vector. + fn start_testnestedflatbuffer_vector(&mut self, numElems: usize); + /// Set the value for field `testempty`. + fn add_testempty(&mut self, testempty: flatbuffers::UOffsetT); + /// Set the value for field `testbool`. + fn add_testbool(&mut self, testbool: bool); + /// Set the value for field `testhashs32_fnv1`. + fn add_testhashs32_fnv1(&mut self, testhashs32_fnv1: i32); + /// Set the value for field `testhashu32_fnv1`. + fn add_testhashu32_fnv1(&mut self, testhashu32_fnv1: u32); + /// Set the value for field `testhashs64_fnv1`. + fn add_testhashs64_fnv1(&mut self, testhashs64_fnv1: i64); + /// Set the value for field `testhashu64_fnv1`. + fn add_testhashu64_fnv1(&mut self, testhashu64_fnv1: u64); + /// Set the value for field `testhashs32_fnv1a`. + fn add_testhashs32_fnv1a(&mut self, testhashs32_fnv1a: i32); + /// Set the value for field `testhashu32_fnv1a`. + fn add_testhashu32_fnv1a(&mut self, testhashu32_fnv1a: u32); + /// Set the value for field `testhashs64_fnv1a`. + fn add_testhashs64_fnv1a(&mut self, testhashs64_fnv1a: i64); + /// Set the value for field `testhashu64_fnv1a`. + fn add_testhashu64_fnv1a(&mut self, testhashu64_fnv1a: u64); + /// Set the value for field `testarrayofbools`. + fn add_testarrayofbools(&mut self, testarrayofbools: flatbuffers::UOffsetT); + /// Initializes bookkeeping for writing a new `testarrayofbools` vector. + fn start_testarrayofbools_vector(&mut self, numElems: usize); + /// Set the value for field `testf`. + fn add_testf(&mut self, testf: f32); + /// Set the value for field `testf2`. + fn add_testf2(&mut self, testf2: f32); + /// Set the value for field `testf3`. + fn add_testf3(&mut self, testf3: f32); +} + +impl MonsterBuilder for flatbuffers::Builder { + fn start_monster(&mut self) { + self.start_object(28); + } + + fn add_pos(&mut self, pos: flatbuffers::UOffsetT) { + self.add_slot_struct(0, pos, 0) + } + + fn add_mana(&mut self, mana: i16) { + self.add_slot_i16(1, mana, 150) + } + + fn add_hp(&mut self, hp: i16) { + self.add_slot_i16(2, hp, 100) + } + + fn add_name(&mut self, name: flatbuffers::UOffsetT) { + self.add_slot_uoffset(3, name, 0) + } + + fn add_inventory(&mut self, inventory: flatbuffers::UOffsetT) { + self.add_slot_uoffset(5, inventory, 0) + } + + /// Initializes bookkeeping for writing a new `inventory` vector. + fn start_inventory_vector(&mut self, numElems: usize) { + self.start_vector(1, numElems, 1) + } + + fn add_color(&mut self, color: i8) { + self.add_slot_i8(6, color, 8) + } + + fn add_test_type(&mut self, test_type: u8) { + self.add_slot_u8(7, test_type, 0) + } + + fn add_test(&mut self, test: flatbuffers::UOffsetT) { + self.add_slot_uoffset(8, test, 0) + } + + fn add_test4(&mut self, test4: flatbuffers::UOffsetT) { + self.add_slot_uoffset(9, test4, 0) + } + + /// Initializes bookkeeping for writing a new `test4` vector. + fn start_test4_vector(&mut self, numElems: usize) { + self.start_vector(4, numElems, 2) + } + + fn add_testarrayofstring(&mut self, testarrayofstring: flatbuffers::UOffsetT) { + self.add_slot_uoffset(10, testarrayofstring, 0) + } + + /// Initializes bookkeeping for writing a new `testarrayofstring` vector. + fn start_testarrayofstring_vector(&mut self, numElems: usize) { + self.start_vector(4, numElems, 4) + } + + fn add_testarrayoftables(&mut self, testarrayoftables: flatbuffers::UOffsetT) { + self.add_slot_uoffset(11, testarrayoftables, 0) + } + + /// Initializes bookkeeping for writing a new `testarrayoftables` vector. + fn start_testarrayoftables_vector(&mut self, numElems: usize) { + self.start_vector(4, numElems, 4) + } + + fn add_enemy(&mut self, enemy: flatbuffers::UOffsetT) { + self.add_slot_uoffset(12, enemy, 0) + } + + fn add_testnestedflatbuffer(&mut self, testnestedflatbuffer: flatbuffers::UOffsetT) { + self.add_slot_uoffset(13, testnestedflatbuffer, 0) + } + + /// Initializes bookkeeping for writing a new `testnestedflatbuffer` vector. + fn start_testnestedflatbuffer_vector(&mut self, numElems: usize) { + self.start_vector(1, numElems, 1) + } + + fn add_testempty(&mut self, testempty: flatbuffers::UOffsetT) { + self.add_slot_uoffset(14, testempty, 0) + } + + fn add_testbool(&mut self, testbool: bool) { + self.add_slot_bool(15, testbool, false) + } + + fn add_testhashs32_fnv1(&mut self, testhashs32_fnv1: i32) { + self.add_slot_i32(16, testhashs32_fnv1, 0) + } + + fn add_testhashu32_fnv1(&mut self, testhashu32_fnv1: u32) { + self.add_slot_u32(17, testhashu32_fnv1, 0) + } + + fn add_testhashs64_fnv1(&mut self, testhashs64_fnv1: i64) { + self.add_slot_i64(18, testhashs64_fnv1, 0) + } + + fn add_testhashu64_fnv1(&mut self, testhashu64_fnv1: u64) { + self.add_slot_u64(19, testhashu64_fnv1, 0) + } + + fn add_testhashs32_fnv1a(&mut self, testhashs32_fnv1a: i32) { + self.add_slot_i32(20, testhashs32_fnv1a, 0) + } + + fn add_testhashu32_fnv1a(&mut self, testhashu32_fnv1a: u32) { + self.add_slot_u32(21, testhashu32_fnv1a, 0) + } + + fn add_testhashs64_fnv1a(&mut self, testhashs64_fnv1a: i64) { + self.add_slot_i64(22, testhashs64_fnv1a, 0) + } + + fn add_testhashu64_fnv1a(&mut self, testhashu64_fnv1a: u64) { + self.add_slot_u64(23, testhashu64_fnv1a, 0) + } + + fn add_testarrayofbools(&mut self, testarrayofbools: flatbuffers::UOffsetT) { + self.add_slot_uoffset(24, testarrayofbools, 0) + } + + /// Initializes bookkeeping for writing a new `testarrayofbools` vector. + fn start_testarrayofbools_vector(&mut self, numElems: usize) { + self.start_vector(1, numElems, 1) + } + + fn add_testf(&mut self, testf: f32) { + self.add_slot_f32(25, testf, 3.14159) + } + + fn add_testf2(&mut self, testf2: f32) { + self.add_slot_f32(26, testf2, 3.0) + } + + fn add_testf3(&mut self, testf3: f32) { + self.add_slot_f32(27, testf3, 0.0) + } + +} + diff --git a/tests/MyGame/Example/Stat.rs b/tests/MyGame/Example/Stat.rs new file mode 100644 index 00000000000..a03e80aaaca --- /dev/null +++ b/tests/MyGame/Example/Stat.rs @@ -0,0 +1,49 @@ +//! Automatically generated, do not modify. + +use flatbuffers; +use super::*; + +flatbuffers_object!{Table => Stat [ + field => { name = id, + typeOf = string, + slot = 4, + default = 0 }, + field => { name = val, + typeOf = i64, + slot = 6, + default = 0 }, + field => { name = count, + typeOf = u16, + slot = 8, + default = 0 }]} + +/// Builder Trait for `Stat` tables. +pub trait StatBuilder { + fn start_stat(&mut self); + /// Set the value for field `id`. + fn add_id(&mut self, id: flatbuffers::UOffsetT); + /// Set the value for field `val`. + fn add_val(&mut self, val: i64); + /// Set the value for field `count`. + fn add_count(&mut self, count: u16); +} + +impl StatBuilder for flatbuffers::Builder { + fn start_stat(&mut self) { + self.start_object(3); + } + + fn add_id(&mut self, id: flatbuffers::UOffsetT) { + self.add_slot_uoffset(0, id, 0) + } + + fn add_val(&mut self, val: i64) { + self.add_slot_i64(1, val, 0) + } + + fn add_count(&mut self, count: u16) { + self.add_slot_u16(2, count, 0) + } + +} + diff --git a/tests/MyGame/Example/Test.rs b/tests/MyGame/Example/Test.rs new file mode 100644 index 00000000000..e4130cb1fce --- /dev/null +++ b/tests/MyGame/Example/Test.rs @@ -0,0 +1,30 @@ +//! Automatically generated, do not modify. + +use flatbuffers; +use super::*; + +flatbuffers_object!{Struct => Test ( size:4, align: 2) [ + field => { name = a, + typeOf = i16, + slot = 0, + default = 0 }, + field => { name = b, + typeOf = i8, + slot = 2, + default = 0, + padding = 1 }]} + +pub trait TestBuilder { + fn build_test(&mut self, a: i16, b: i8) -> flatbuffers::UOffsetT; +} + +impl TestBuilder for flatbuffers::Builder { + fn build_test(&mut self, a: i16, b: i8) -> flatbuffers::UOffsetT { + self.prep(2, 4); + self.pad(1); + self.add_i8(b); + self.add_i16(a); + self.offset() as flatbuffers::UOffsetT + } +} + diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.rs b/tests/MyGame/Example/TestSimpleTableWithEnum.rs new file mode 100644 index 00000000000..0e1ea8bfa89 --- /dev/null +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.rs @@ -0,0 +1,29 @@ +//! Automatically generated, do not modify. + +use flatbuffers; +use super::*; + +flatbuffers_object!{Table => TestSimpleTableWithEnum [ + field => { name = color, + typeOf = enum Color i8, + slot = 4, + default = 2 }]} + +/// Builder Trait for `TestSimpleTableWithEnum` tables. +pub trait TestSimpleTableWithEnumBuilder { + fn start_testsimpletablewithenum(&mut self); + /// Set the value for field `color`. + fn add_color(&mut self, color: i8); +} + +impl TestSimpleTableWithEnumBuilder for flatbuffers::Builder { + fn start_testsimpletablewithenum(&mut self) { + self.start_object(1); + } + + fn add_color(&mut self, color: i8) { + self.add_slot_i8(0, color, 2) + } + +} + diff --git a/tests/MyGame/Example/Vec3.rs b/tests/MyGame/Example/Vec3.rs new file mode 100644 index 00000000000..ea595584d58 --- /dev/null +++ b/tests/MyGame/Example/Vec3.rs @@ -0,0 +1,56 @@ +//! Automatically generated, do not modify. + +use flatbuffers; +use super::*; + +flatbuffers_object!{Struct => Vec3 ( size:32, align: 16) [ + field => { name = x, + typeOf = f32, + slot = 0, + default = 0.0 }, + field => { name = y, + typeOf = f32, + slot = 4, + default = 0.0 }, + field => { name = z, + typeOf = f32, + slot = 8, + default = 0.0, + padding = 4 }, + field => { name = test1, + typeOf = f64, + slot = 16, + default = 0.0 }, + field => { name = test2, + typeOf = enum Color i8, + slot = 24, + default = 0, + padding = 1 }, + field => { name = test3, + typeOf = Test, + slot = 26, + padding = 2 }]} + +pub trait Vec3Builder { + fn build_vec3(&mut self, x: f32, y: f32, z: f32, test1: f64, test2: i8, test3_a: i16, test3_b: i8) -> flatbuffers::UOffsetT; +} + +impl Vec3Builder for flatbuffers::Builder { + fn build_vec3(&mut self, x: f32, y: f32, z: f32, test1: f64, test2: i8, test3_a: i16, test3_b: i8) -> flatbuffers::UOffsetT { + self.prep(16, 32); + self.pad(2); + self.prep(2, 4); + self.pad(1); + self.add_i8(test3_b); + self.add_i16(test3_a); + self.pad(1); + self.add_i8(test2); + self.add_f64(test1); + self.pad(4); + self.add_f32(z); + self.add_f32(y); + self.add_f32(x); + self.offset() as flatbuffers::UOffsetT + } +} + diff --git a/tests/MyGame/Example/__init__.py b/tests/MyGame/Example/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/MyGame/Example/mod.rs b/tests/MyGame/Example/mod.rs new file mode 100644 index 00000000000..f117a8e5e07 --- /dev/null +++ b/tests/MyGame/Example/mod.rs @@ -0,0 +1,18 @@ +//! Automatically generated, do not modify +//! +//! Flatbuffer definitions for the Example namespace. +mod color; +mod any; +pub mod test; +pub mod testsimpletablewithenum; +pub mod vec3; +pub mod stat; +pub mod monster; + +pub use self::color::*; +pub use self::any::*; +pub use self::test::{Test, TestBuilder}; +pub use self::testsimpletablewithenum::{TestSimpleTableWithEnum, TestSimpleTableWithEnumBuilder}; +pub use self::vec3::{Vec3, Vec3Builder}; +pub use self::stat::{Stat, StatBuilder}; +pub use self::monster::{Monster, MonsterBuilder}; diff --git a/tests/MyGame/__init__.py b/tests/MyGame/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/MyGame/mod.rs b/tests/MyGame/mod.rs new file mode 100644 index 00000000000..b305cc9d96b --- /dev/null +++ b/tests/MyGame/mod.rs @@ -0,0 +1,5 @@ +//! Automatically generated, do not modify +//! +//! Flatbuffer definitions for the MyGame namespace. + +pub mod example; diff --git a/tests/RustTest.sh b/tests/RustTest.sh new file mode 100755 index 00000000000..84430efb229 --- /dev/null +++ b/tests/RustTest.sh @@ -0,0 +1,43 @@ +# 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. + +pushd "$(dirname $0)" >/dev/null +test_dir="$(pwd)" +rust_path=${test_dir}/rust_gen +rust_src=${rust_path}/src + +# Emit Rust code for the example schema in the test dir: +../flatc -r monster_test.fbs + +mkdir -p ${rust_src}/bin/test/MyGame/Example + +sync_cmd='cp -u' +unamestr=`uname` +if [[ "$unamestr" == 'Darwin' ]]; then + sync_cmd='rsync -u' +fi + +${sync_cmd} MyGame/Example/*.rs ${rust_src}/bin/test/MyGame/Example/ +${sync_cmd} MyGame/*.rs ${rust_src}/bin/test/MyGame/ +${sync_cmd} ../rust/* ${rust_path} +${sync_cmd} ../rust/macros/* ${rust_path}/macros +#{sync_cmd} ../rust/macros/src/* ${rust_path}/macros/src +${sync_cmd} ../rust/src/* ${rust_src} +${sync_cmd} ../rust/src/bin/* ${rust_src}/bin/ +cp *.mon ${rust_path}/ + +#run tests +( cd $rust_path ; cargo test --features test_idl_gen -- --nocapture) +#run bench +# cant bench on stable rust as of 1.9.0 + +echo "Ok: Rust tests passed."