From 46bb05d95226c72cc85242eee386465860786333 Mon Sep 17 00:00:00 2001 From: Kamil Rojewski Date: Fri, 11 Aug 2017 18:24:36 +0200 Subject: [PATCH] Vector of unions for TS/JS and PHP (#4404) * Eclipse ignore * TypeScript support * Prefixing enums * Test results * Merged JS and TS generators * Fixed AppVeyor build problems * Fixed more AppVeyor build problems * Fixed more AppVeyor build problems * Changed TS flag to options struct * Storing options by value * Removed unneeded const * Re-export support for unions * Uint support * Casting bools to numbers for mutation * TS shell tests * Reverted generates js test file to original version * Backing up js tests and properly generating test data * Not importing flatbuffers for TS test generation * Not overwriting generated js for tests * AppVeyor test fixes * Generating the most strict TS code possible * Not returning null when creating vectors * Not returning null from struct contructors * Vector of unions for ts/js * Sanity check for languages * Indentation fix + output test files * Vectors of unions for php * Fixes to union vector handling + tests --- .gitignore | 1 + include/flatbuffers/idl.h | 2 + php/Struct.php | 10 + php/Table.php | 14 +- src/idl_gen_js.cpp | 63 ++- src/idl_gen_php.cpp | 34 +- src/idl_parser.cpp | 11 +- tests/JavaScriptUnionVectorTest.js | 57 +++ tests/TestAll.sh | 2 + tests/TypeScriptTest.sh | 14 +- tests/generate_code.sh | 2 +- tests/phpUnionVectorTest.php | 109 +++++ tests/phpUnionVectorTest.sh | 8 + tests/union_vector/Attacker.php | 94 ++++ tests/union_vector/BookReader.php | 43 ++ tests/union_vector/Character.php | 33 ++ tests/union_vector/Movie.php | 222 ++++++++++ tests/union_vector/Rapunzel.php | 43 ++ tests/union_vector/union_vector_generated.js | 437 +++++++++++++++++++ tests/union_vector/union_vector_generated.ts | 436 ++++++++++++++++++ 20 files changed, 1608 insertions(+), 27 deletions(-) create mode 100644 tests/JavaScriptUnionVectorTest.js create mode 100644 tests/phpUnionVectorTest.php create mode 100755 tests/phpUnionVectorTest.sh create mode 100644 tests/union_vector/Attacker.php create mode 100644 tests/union_vector/BookReader.php create mode 100644 tests/union_vector/Character.php create mode 100644 tests/union_vector/Movie.php create mode 100644 tests/union_vector/Rapunzel.php create mode 100644 tests/union_vector/union_vector_generated.js create mode 100644 tests/union_vector/union_vector_generated.ts diff --git a/.gitignore b/.gitignore index bf6d665be..07531061d 100755 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ tests/monsterdata_go_wire.mon tests/monsterdata_javascript_wire.mon tests/unicode_test.mon tests/ts/ +tests/php/ CMakeLists.txt.user CMakeScripts/** CTestTestfile.cmake diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 9c7459c08..2b4335bf0 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -647,6 +647,8 @@ private: const char *suffix, BaseType baseType); + bool SupportsVectorOfUnions() const; + public: SymbolTable types_; SymbolTable structs_; diff --git a/php/Struct.php b/php/Struct.php index 94e712b25..cd7652e7d 100644 --- a/php/Struct.php +++ b/php/Struct.php @@ -28,4 +28,14 @@ abstract class Struct * @var ByteBuffer $bb */ protected $bb; + + public function setByteBufferPos($pos) + { + $this->bb_pos = $pos; + } + + public function setByteBuffer($bb) + { + $this->bb = $bb; + } } diff --git a/php/Table.php b/php/Table.php index 6f917c1af..bf6fe21d7 100644 --- a/php/Table.php +++ b/php/Table.php @@ -32,6 +32,16 @@ abstract class Table { } + public function setByteBufferPos($pos) + { + $this->bb_pos = $pos; + } + + public function setByteBuffer($bb) + { + $this->bb = $bb; + } + /** * returns actual vtable offset * @@ -107,8 +117,8 @@ abstract class Table protected function __union($table, $offset) { $offset += $this->bb_pos; - $table->bb_pos = $offset + $this->bb->getInt($offset); - $table->bb = $this->bb; + $table->setByteBufferPos($offset + $this->bb->getInt($offset)); + $table->setByteBuffer($this->bb); return $table; } diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp index 9fac55038..c43598649 100644 --- a/src/idl_gen_js.cpp +++ b/src/idl_gen_js.cpp @@ -289,9 +289,12 @@ void GenEnum(EnumDef &enum_def, std::string *code_ptr, std::string &code = *code_ptr; std::string &exports = *exports_ptr; GenDocComment(enum_def.doc_comment, code_ptr, "@enum"); + std::string ns = GetNameSpace(enum_def); if (lang_.language == IDLOptions::kTs) { - code += "export namespace " + GetNameSpace(enum_def) + "{\n" + - "export enum " + enum_def.name + "{\n"; + if (!ns.empty()) { + code += "export namespace " + ns + "{\n"; + } + code += "export enum " + enum_def.name + "{\n"; } else { if (enum_def.defined_namespace->components.empty()) { code += "var "; @@ -329,11 +332,10 @@ void GenEnum(EnumDef &enum_def, std::string *code_ptr, } } - if (lang_.language == IDLOptions::kTs) { - code += "}};\n\n"; - } else { - code += "};\n\n"; + if (lang_.language == IDLOptions::kTs && !ns.empty()) { + code += "}"; } + code += "};\n\n"; } static std::string GenType(const Type &type) { @@ -554,13 +556,15 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string &exports = *exports_ptr; std::string object_name; + std::string object_namespace = GetNameSpace(struct_def); // Emit constructor if (lang_.language == IDLOptions::kTs) { object_name = struct_def.name; - std::string object_namespace = GetNameSpace(struct_def); GenDocComment(struct_def.doc_comment, code_ptr, "@constructor"); - code += "export namespace " + object_namespace + "{\n"; + if (!object_namespace.empty()) { + code += "export namespace " + object_namespace + "{\n"; + } code += "export class " + struct_def.name; code += " {\n"; code += " /**\n"; @@ -754,17 +758,37 @@ void GenStruct(const Parser &parser, StructDef &struct_def, auto index = "this.bb.__vector(this.bb_pos + offset) + index" + MaybeScale(inline_size); std::string args = "@param {number} index\n"; - if (vectortype.base_type == BASE_TYPE_STRUCT) { - args += "@param {" + vectortypename + "=} obj\n"; - } else if (vectortype.base_type == BASE_TYPE_STRING) { - args += "@param {flatbuffers.Encoding=} optionalEncoding\n"; + std::string ret_type; + bool is_union = false; + switch (vectortype.base_type) { + case BASE_TYPE_STRUCT: + args += "@param {" + vectortypename + "=} obj\n"; + ret_type = vectortypename; + break; + case BASE_TYPE_STRING: + args += "@param {flatbuffers.Encoding=} optionalEncoding\n"; + ret_type = vectortypename; + break; + case BASE_TYPE_UNION: + args += "@param {flatbuffers.Table=} obj\n"; + ret_type = "?flatbuffers.Table"; + is_union = true; + break; + default: + ret_type = vectortypename; } GenDocComment(field.doc_comment, code_ptr, args + - "@returns {" + vectortypename + "}"); + "@returns {" + ret_type + "}"); if (lang_.language == IDLOptions::kTs) { std::string prefix = MakeCamel(field.name, false); + if (is_union) { + prefix += ""; + } prefix += "(index: number"; - if (vectortype.base_type == BASE_TYPE_STRUCT) { + if (is_union) { + vectortypename = "T"; + code += prefix + ", obj:T"; + } else if (vectortype.base_type == BASE_TYPE_STRUCT) { vectortypename = GenPrefixedTypeName(vectortypename, vectortype.struct_def->file); code += prefix + ", obj?:" + vectortypename; @@ -781,7 +805,7 @@ void GenStruct(const Parser &parser, StructDef &struct_def, } else { code += object_name + ".prototype." + MakeCamel(field.name, false); code += " = function(index"; - if (vectortype.base_type == BASE_TYPE_STRUCT) { + if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) { code += ", obj"; } else if (vectortype.base_type == BASE_TYPE_STRING) { code += ", optionalEncoding"; @@ -797,7 +821,9 @@ void GenStruct(const Parser &parser, StructDef &struct_def, : "this.bb.__indirect(" + index + ")"; code += ", this.bb)"; } else { - if (vectortype.base_type == BASE_TYPE_STRING) { + if (is_union) { + index = "obj, " + index; + } else if (vectortype.base_type == BASE_TYPE_STRING) { index += ", optionalEncoding"; } code += offset_prefix + GenGetter(vectortype, "(" + index + ")"); @@ -1137,7 +1163,10 @@ void GenStruct(const Parser &parser, StructDef &struct_def, } if (lang_.language == IDLOptions::kTs) { - code += "}\n}\n"; + if (!object_namespace.empty()) { + code += "}\n"; + } + code += "}\n"; } } }; diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp index 8021f92fe..893f16f29 100644 --- a/src/idl_gen_php.cpp +++ b/src/idl_gen_php.cpp @@ -67,7 +67,10 @@ namespace php { std::string &code = *code_ptr; code += "__offset(" + + NumToString(field.value.offset) + + ");\n"; + code += Indent + Indent + "return $o != 0 ? "; + code += "$this->__union($obj, $this->__vector($o) + $j * "; + code += NumToString(InlineSize(vectortype)) + " - $this->bb_pos) : null;\n"; + code += Indent + "}\n\n"; + } + // Recursively generate arguments for a constructor, to deal with nested // structs. static void StructBuilderArgs(const StructDef &struct_def, @@ -703,7 +731,9 @@ namespace php { break; case BASE_TYPE_VECTOR: { auto vectortype = field.value.type.VectorType(); - if (vectortype.base_type == BASE_TYPE_STRUCT) { + if (vectortype.base_type == BASE_TYPE_UNION) { + GetMemberOfVectorOfUnion(field, code_ptr); + } else if (vectortype.base_type == BASE_TYPE_STRUCT) { GetMemberOfVectorOfStruct(struct_def, field, code_ptr); } else { GetMemberOfVectorOfNonStruct(field, code_ptr); diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index ade20413d..4fb29eac8 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -628,8 +628,8 @@ CheckedError Parser::ParseField(StructDef &struct_def) { type.enum_def->underlying_type, &typefield)); } else if (type.base_type == BASE_TYPE_VECTOR && type.element == BASE_TYPE_UNION) { - // Only cpp supports the union vector feature so far. - if (opts.lang_to_generate != IDLOptions::kCpp) { + // Only cpp, js and ts supports the union vector feature so far. + if (!SupportsVectorOfUnions()) { return Error("Vectors of unions are not yet supported in all " "the specified programming languages."); } @@ -1571,6 +1571,11 @@ CheckedError Parser::CheckClash(std::vector &fields, return NoError(); } +bool Parser::SupportsVectorOfUnions() const { + return opts.lang_to_generate != 0 && (opts.lang_to_generate & + ~(IDLOptions::kCpp | IDLOptions::kJs | IDLOptions::kTs | IDLOptions::kPhp)) == 0; +} + static bool compareFieldDefs(const FieldDef *a, const FieldDef *b) { auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str()); auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str()); @@ -2141,7 +2146,7 @@ CheckedError Parser::ParseRoot(const char *source, const char **include_paths, val_it != enum_def.vals.vec.end(); ++val_it) { auto &val = **val_it; - if (opts.lang_to_generate != IDLOptions::kCpp && + if (!SupportsVectorOfUnions() && val.union_type.struct_def && val.union_type.struct_def->fixed) return Error( "only tables can be union elements in the generated language: " diff --git a/tests/JavaScriptUnionVectorTest.js b/tests/JavaScriptUnionVectorTest.js new file mode 100644 index 000000000..d79669fab --- /dev/null +++ b/tests/JavaScriptUnionVectorTest.js @@ -0,0 +1,57 @@ +var assert = require('assert'); + +var flatbuffers = require('../js/flatbuffers').flatbuffers; +var Test = require(process.argv[2]); + +function main() { + var fbb = new flatbuffers.Builder(); + + var charTypes = [ + Test.Character.Belle, + Test.Character.MuLan, + Test.Character.BookFan, + ]; + + Test.Attacker.startAttacker(fbb); + Test.Attacker.addSwordAttackDamage(fbb, 5); + var attackerOffset = Test.Attacker.endAttacker(fbb); + + var charTypesOffset = Test.Movie.createCharactersTypeVector(fbb, charTypes); + var charsOffset = Test.Movie.createCharactersVector( + fbb, + [ + Test.BookReader.createBookReader(fbb, 7), + attackerOffset, + Test.BookReader.createBookReader(fbb, 2), + ] + ); + + Test.Movie.startMovie(fbb); + Test.Movie.addCharactersType(fbb, charTypesOffset); + Test.Movie.addCharacters(fbb, charsOffset); + Test.Movie.finishMovieBuffer(fbb, Test.Movie.endMovie(fbb)); + + var buf = new flatbuffers.ByteBuffer(fbb.asUint8Array()); + + var movie = Test.Movie.getRootAsMovie(buf); + + assert.strictEqual(movie.charactersTypeLength(), charTypes.length); + assert.strictEqual(movie.charactersLength(), movie.charactersTypeLength()); + + for (var i = 0; i < charTypes.length; ++i) { + assert.strictEqual(movie.charactersType(i), charTypes[i]); + } + + var bookReader7 = movie.characters(0, new Test.BookReader()); + assert.strictEqual(bookReader7.booksRead(), 7); + + var attacker = movie.characters(1, new Test.Attacker()); + assert.strictEqual(attacker.swordAttackDamage(), 5); + + var bookReader2 = movie.characters(2, new Test.BookReader()); + assert.strictEqual(bookReader2.booksRead(), 2); + + console.log('FlatBuffers union vector test: completed successfully'); +} + +main(); diff --git a/tests/TestAll.sh b/tests/TestAll.sh index b501838a8..463eb738d 100644 --- a/tests/TestAll.sh +++ b/tests/TestAll.sh @@ -13,6 +13,7 @@ sh PythonTest.sh echo "************************ JavaScript:" sh JavaScriptTest.sh +sh JavaScriptUnionVectorTest.sh echo "************************ TypeScript:" @@ -33,6 +34,7 @@ cd .. echo "************************ PHP:" php phpTest.php +sh phpUnionVectorTest.sh echo "************************ C:" diff --git a/tests/TypeScriptTest.sh b/tests/TypeScriptTest.sh index b033a3024..fa650a4da 100755 --- a/tests/TypeScriptTest.sh +++ b/tests/TypeScriptTest.sh @@ -15,9 +15,19 @@ # limitations under the License. pushd "$(dirname $0)" >/dev/null + +npm install @types/flatbuffers + ../flatc --ts --no-fb-import --gen-mutable -o ts -I include_test monster_test.fbs ../flatc -b -I include_test monster_test.fbs unicode_test.json -npm install @types/flatbuffers tsc --strict --noUnusedParameters --noUnusedLocals --noImplicitReturns --strictNullChecks ts/monster_test_generated.ts -npm uninstall @types/flatbuffers node JavaScriptTest ./ts/monster_test_generated + +../flatc --ts --js --no-fb-import -o ts union_vector/union_vector.fbs + +# test JS version first, then transpile and rerun for TS +node JavaScriptUnionVectorTest ./ts/union_vector_generated +tsc --strict --noUnusedParameters --noUnusedLocals --noImplicitReturns --strictNullChecks ts/union_vector_generated.ts +node JavaScriptUnionVectorTest ./ts/union_vector_generated + +npm uninstall @types/flatbuffers diff --git a/tests/generate_code.sh b/tests/generate_code.sh index ebc465e30..31f4dee5e 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -16,7 +16,7 @@ ../flatc --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json ../flatc --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs -../flatc --cpp --gen-mutable --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs +../flatc --cpp --js --ts --php --gen-mutable --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs ../flatc -b --schema --bfbs-comments -I include_test monster_test.fbs ../flatc --jsonschema --schema -I include_test monster_test.fbs cd ../samples diff --git a/tests/phpUnionVectorTest.php b/tests/phpUnionVectorTest.php new file mode 100644 index 000000000..4b5e25885 --- /dev/null +++ b/tests/phpUnionVectorTest.php @@ -0,0 +1,109 @@ +dataBuffer()->data()); + + $movie = Movie::getRootAsMovie($buf); + + $assert->strictEqual($movie->getCharactersTypeLength(), count($charTypes)); + $assert->strictEqual($movie->getCharactersLength(), $movie->getCharactersTypeLength()); + + for ($i = 0; $i < count($charTypes); ++$i) { + $assert->strictEqual($movie->getCharactersType($i), $charTypes[$i]); + } + + $bookReader7 = $movie->getCharacters(0, new BookReader()); + $assert->strictEqual($bookReader7->getBooksRead(), 7); + + $attacker = $movie->getCharacters(1, new Attacker()); + $assert->strictEqual($attacker->getSwordAttackDamage(), 5); + + $bookReader2 = $movie->getCharacters(2, new BookReader()); + $assert->strictEqual($bookReader2->getBooksRead(), 2); +} + +try { + main(); + exit(0); +} catch(Exception $e) { + printf("Fatal error: Uncaught exception '%s' with message '%s. in %s:%d\n", get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()); + printf("Stack trace:\n"); + echo $e->getTraceAsString() . PHP_EOL; + printf(" thrown in in %s:%d\n", $e->getFile(), $e->getLine()); + + die(-1); +} diff --git a/tests/phpUnionVectorTest.sh b/tests/phpUnionVectorTest.sh new file mode 100755 index 000000000..a6c3f2644 --- /dev/null +++ b/tests/phpUnionVectorTest.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +../flatc --php -o php union_vector/union_vector.fbs +php phpUnionVectorTest.php + +echo 'PHP union vector test passed' diff --git a/tests/union_vector/Attacker.php b/tests/union_vector/Attacker.php new file mode 100644 index 000000000..64cdc095b --- /dev/null +++ b/tests/union_vector/Attacker.php @@ -0,0 +1,94 @@ +init($bb->getInt($bb->getPosition()) + $bb->getPosition(), $bb)); + } + + public static function AttackerIdentifier() + { + return "MOVI"; + } + + public static function AttackerBufferHasIdentifier(ByteBuffer $buf) + { + return self::__has_identifier($buf, self::AttackerIdentifier()); + } + + /** + * @param int $_i offset + * @param ByteBuffer $_bb + * @return Attacker + **/ + public function init($_i, ByteBuffer $_bb) + { + $this->bb_pos = $_i; + $this->bb = $_bb; + return $this; + } + + /** + * @return int + */ + public function getSwordAttackDamage() + { + $o = $this->__offset(4); + return $o != 0 ? $this->bb->getInt($o + $this->bb_pos) : 0; + } + + /** + * @param FlatBufferBuilder $builder + * @return void + */ + public static function startAttacker(FlatBufferBuilder $builder) + { + $builder->StartObject(1); + } + + /** + * @param FlatBufferBuilder $builder + * @return Attacker + */ + public static function createAttacker(FlatBufferBuilder $builder, $sword_attack_damage) + { + $builder->startObject(1); + self::addSwordAttackDamage($builder, $sword_attack_damage); + $o = $builder->endObject(); + return $o; + } + + /** + * @param FlatBufferBuilder $builder + * @param int + * @return void + */ + public static function addSwordAttackDamage(FlatBufferBuilder $builder, $swordAttackDamage) + { + $builder->addIntX(0, $swordAttackDamage, 0); + } + + /** + * @param FlatBufferBuilder $builder + * @return int table offset + */ + public static function endAttacker(FlatBufferBuilder $builder) + { + $o = $builder->endObject(); + return $o; + } +} diff --git a/tests/union_vector/BookReader.php b/tests/union_vector/BookReader.php new file mode 100644 index 000000000..0d9e7f2fe --- /dev/null +++ b/tests/union_vector/BookReader.php @@ -0,0 +1,43 @@ +bb_pos = $_i; + $this->bb = $_bb; + return $this; + } + + /** + * @return int + */ + public function GetBooksRead() + { + return $this->bb->getInt($this->bb_pos + 0); + } + + + /** + * @return int offset + */ + public static function createBookReader(FlatBufferBuilder $builder, $booksRead) + { + $builder->prep(4, 4); + $builder->putInt($booksRead); + return $builder->offset(); + } +} diff --git a/tests/union_vector/Character.php b/tests/union_vector/Character.php new file mode 100644 index 000000000..052c1e4f6 --- /dev/null +++ b/tests/union_vector/Character.php @@ -0,0 +1,33 @@ +init($bb->getInt($bb->getPosition()) + $bb->getPosition(), $bb)); + } + + public static function MovieIdentifier() + { + return "MOVI"; + } + + public static function MovieBufferHasIdentifier(ByteBuffer $buf) + { + return self::__has_identifier($buf, self::MovieIdentifier()); + } + + /** + * @param int $_i offset + * @param ByteBuffer $_bb + * @return Movie + **/ + public function init($_i, ByteBuffer $_bb) + { + $this->bb_pos = $_i; + $this->bb = $_bb; + return $this; + } + + /** + * @return byte + */ + public function getMainCharacterType() + { + $o = $this->__offset(4); + return $o != 0 ? $this->bb->getByte($o + $this->bb_pos) : \Character::NONE; + } + + /** + * @returnint + */ + public function getMainCharacter($obj) + { + $o = $this->__offset(6); + return $o != 0 ? $this->__union($obj, $o) : null; + } + + /** + * @param int offset + * @return byte + */ + public function getCharactersType($j) + { + $o = $this->__offset(8); + return $o != 0 ? $this->bb->getByte($this->__vector($o) + $j * 1) : \Character::NONE; + } + + /** + * @return int + */ + public function getCharactersTypeLength() + { + $o = $this->__offset(8); + return $o != 0 ? $this->__vector_len($o) : 0; + } + + /** + * @param int offset + * @return Table + */ + public function getCharacters($j, $obj) + { + $o = $this->__offset(10); + return $o != 0 ? $this->__union($obj, $this->__vector($o) + $j * 4 - $this->bb_pos) : null; + } + + /** + * @return int + */ + public function getCharactersLength() + { + $o = $this->__offset(10); + return $o != 0 ? $this->__vector_len($o) : 0; + } + + /** + * @param FlatBufferBuilder $builder + * @return void + */ + public static function startMovie(FlatBufferBuilder $builder) + { + $builder->StartObject(4); + } + + /** + * @param FlatBufferBuilder $builder + * @return Movie + */ + public static function createMovie(FlatBufferBuilder $builder, $main_character_type, $main_character, $characters_type, $characters) + { + $builder->startObject(4); + self::addMainCharacterType($builder, $main_character_type); + self::addMainCharacter($builder, $main_character); + self::addCharactersType($builder, $characters_type); + self::addCharacters($builder, $characters); + $o = $builder->endObject(); + return $o; + } + + /** + * @param FlatBufferBuilder $builder + * @param byte + * @return void + */ + public static function addMainCharacterType(FlatBufferBuilder $builder, $mainCharacterType) + { + $builder->addByteX(0, $mainCharacterType, 0); + } + + public static function addMainCharacter(FlatBufferBuilder $builder, $offset) + { + $builder->addOffsetX(1, $offset, 0); + } + + /** + * @param FlatBufferBuilder $builder + * @param VectorOffset + * @return void + */ + public static function addCharactersType(FlatBufferBuilder $builder, $charactersType) + { + $builder->addOffsetX(2, $charactersType, 0); + } + + /** + * @param FlatBufferBuilder $builder + * @param array offset array + * @return int vector offset + */ + public static function createCharactersTypeVector(FlatBufferBuilder $builder, array $data) + { + $builder->startVector(1, count($data), 1); + for ($i = count($data) - 1; $i >= 0; $i--) { + $builder->addByte($data[$i]); + } + return $builder->endVector(); + } + + /** + * @param FlatBufferBuilder $builder + * @param int $numElems + * @return void + */ + public static function startCharactersTypeVector(FlatBufferBuilder $builder, $numElems) + { + $builder->startVector(1, $numElems, 1); + } + + /** + * @param FlatBufferBuilder $builder + * @param VectorOffset + * @return void + */ + public static function addCharacters(FlatBufferBuilder $builder, $characters) + { + $builder->addOffsetX(3, $characters, 0); + } + + /** + * @param FlatBufferBuilder $builder + * @param array offset array + * @return int vector offset + */ + public static function createCharactersVector(FlatBufferBuilder $builder, array $data) + { + $builder->startVector(4, count($data), 4); + for ($i = count($data) - 1; $i >= 0; $i--) { + $builder->addOffset($data[$i]); + } + return $builder->endVector(); + } + + /** + * @param FlatBufferBuilder $builder + * @param int $numElems + * @return void + */ + public static function startCharactersVector(FlatBufferBuilder $builder, $numElems) + { + $builder->startVector(4, $numElems, 4); + } + + /** + * @param FlatBufferBuilder $builder + * @return int table offset + */ + public static function endMovie(FlatBufferBuilder $builder) + { + $o = $builder->endObject(); + return $o; + } + + public static function finishMovieBuffer(FlatBufferBuilder $builder, $offset) + { + $builder->finish($offset, "MOVI"); + } +} diff --git a/tests/union_vector/Rapunzel.php b/tests/union_vector/Rapunzel.php new file mode 100644 index 000000000..2e2152313 --- /dev/null +++ b/tests/union_vector/Rapunzel.php @@ -0,0 +1,43 @@ +bb_pos = $_i; + $this->bb = $_bb; + return $this; + } + + /** + * @return int + */ + public function GetHairLength() + { + return $this->bb->getInt($this->bb_pos + 0); + } + + + /** + * @return int offset + */ + public static function createRapunzel(FlatBufferBuilder $builder, $hairLength) + { + $builder->prep(4, 4); + $builder->putInt($hairLength); + return $builder->offset(); + } +} diff --git a/tests/union_vector/union_vector_generated.js b/tests/union_vector/union_vector_generated.js new file mode 100644 index 000000000..21787cb5f --- /dev/null +++ b/tests/union_vector/union_vector_generated.js @@ -0,0 +1,437 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/** + * @enum + */ +var Character = { + NONE: 0, + MuLan: 1, + Rapunzel: 2, + Belle: 3, + BookFan: 4, + Other: 5, + Unused: 6 +}; + +/** + * @constructor + */ +function Attacker() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +} + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {Attacker} + */ +Attacker.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {Attacker=} obj + * @returns {Attacker} + */ +Attacker.getRootAsAttacker = function(bb, obj) { + return (obj || new Attacker).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @returns {number} + */ +Attacker.prototype.swordAttackDamage = function() { + var offset = this.bb.__offset(this.bb_pos, 4); + return offset ? this.bb.readInt32(this.bb_pos + offset) : 0; +}; + +/** + * @param {number} value + * @returns {boolean} + */ +Attacker.prototype.mutate_sword_attack_damage = function(value) { + var offset = this.bb.__offset(this.bb_pos, 4); + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Builder} builder + */ +Attacker.startAttacker = function(builder) { + builder.startObject(1); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} swordAttackDamage + */ +Attacker.addSwordAttackDamage = function(builder, swordAttackDamage) { + builder.addFieldInt32(0, swordAttackDamage, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +Attacker.endAttacker = function(builder) { + var offset = builder.endObject(); + return offset; +}; + +/** + * @constructor + */ +function Rapunzel() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +} + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {Rapunzel} + */ +Rapunzel.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @returns {number} + */ +Rapunzel.prototype.hairLength = function() { + return this.bb.readInt32(this.bb_pos); +}; + +/** + * @param {number} value + * @returns {boolean} + */ +Rapunzel.prototype.mutate_hair_length = function(value) { + var offset = this.bb.__offset(this.bb_pos, 0); + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} hair_length + * @returns {flatbuffers.Offset} + */ +Rapunzel.createRapunzel = function(builder, hair_length) { + builder.prep(4, 4); + builder.writeInt32(hair_length); + return builder.offset(); +}; + +/** + * @constructor + */ +function BookReader() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +} + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {BookReader} + */ +BookReader.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @returns {number} + */ +BookReader.prototype.booksRead = function() { + return this.bb.readInt32(this.bb_pos); +}; + +/** + * @param {number} value + * @returns {boolean} + */ +BookReader.prototype.mutate_books_read = function(value) { + var offset = this.bb.__offset(this.bb_pos, 0); + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} books_read + * @returns {flatbuffers.Offset} + */ +BookReader.createBookReader = function(builder, books_read) { + builder.prep(4, 4); + builder.writeInt32(books_read); + return builder.offset(); +}; + +/** + * @constructor + */ +function Movie() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +} + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {Movie} + */ +Movie.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {Movie=} obj + * @returns {Movie} + */ +Movie.getRootAsMovie = function(bb, obj) { + return (obj || new Movie).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @returns {boolean} + */ +Movie.bufferHasIdentifier = function(bb) { + return bb.__has_identifier('MOVI'); +}; + +/** + * @returns {Character} + */ +Movie.prototype.mainCharacterType = function() { + var offset = this.bb.__offset(this.bb_pos, 4); + return offset ? /** @type {Character} */ (this.bb.readUint8(this.bb_pos + offset)) : Character.NONE; +}; + +/** + * @param {Character} value + * @returns {boolean} + */ +Movie.prototype.mutate_main_character_type = function(value) { + var offset = this.bb.__offset(this.bb_pos, 4); + + if (offset === 0) { + return false; + } + + this.bb.writeUint8(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Table} obj + * @returns {?flatbuffers.Table} + */ +Movie.prototype.mainCharacter = function(obj) { + var offset = this.bb.__offset(this.bb_pos, 6); + return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; +}; + +/** + * @param {number} index + * @returns {Character} + */ +Movie.prototype.charactersType = function(index) { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? /** @type {Character} */ (this.bb.readUint8(this.bb.__vector(this.bb_pos + offset) + index)) : /** @type {Character} */ (0); +}; + +/** + * @returns {number} + */ +Movie.prototype.charactersTypeLength = function() { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @returns {Uint8Array} + */ +Movie.prototype.charactersTypeArray = function() { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? new Uint8Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null; +}; + +/** + * @param {number} index + * @param {flatbuffers.Table=} obj + * @returns {?flatbuffers.Table} + */ +Movie.prototype.characters = function(index) { + var offset = this.bb.__offset(this.bb_pos, 10); + return offset ? this.bb.__union(obj, this.bb.__vector(this.bb_pos + offset) + index * 4) : null; +}; + +/** + * @returns {number} + */ +Movie.prototype.charactersLength = function() { + var offset = this.bb.__offset(this.bb_pos, 10); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @param {flatbuffers.Builder} builder + */ +Movie.startMovie = function(builder) { + builder.startObject(4); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Character} mainCharacterType + */ +Movie.addMainCharacterType = function(builder, mainCharacterType) { + builder.addFieldInt8(0, mainCharacterType, Character.NONE); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} mainCharacterOffset + */ +Movie.addMainCharacter = function(builder, mainCharacterOffset) { + builder.addFieldOffset(1, mainCharacterOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} charactersTypeOffset + */ +Movie.addCharactersType = function(builder, charactersTypeOffset) { + builder.addFieldOffset(2, charactersTypeOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +Movie.createCharactersTypeVector = function(builder, data) { + builder.startVector(1, data.length, 1); + for (var i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +Movie.startCharactersTypeVector = function(builder, numElems) { + builder.startVector(1, numElems, 1); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} charactersOffset + */ +Movie.addCharacters = function(builder, charactersOffset) { + builder.addFieldOffset(3, charactersOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +Movie.createCharactersVector = function(builder, data) { + builder.startVector(4, data.length, 4); + for (var i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +Movie.startCharactersVector = function(builder, numElems) { + builder.startVector(4, numElems, 4); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +Movie.endMovie = function(builder) { + var offset = builder.endObject(); + return offset; +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} offset + */ +Movie.finishMovieBuffer = function(builder, offset) { + builder.finish(offset, 'MOVI'); +}; + +// Exports for Node.js and RequireJS +this.Character = Character; +this.Attacker = Attacker; +this.Rapunzel = Rapunzel; +this.BookReader = BookReader; +this.Movie = Movie; diff --git a/tests/union_vector/union_vector_generated.ts b/tests/union_vector/union_vector_generated.ts new file mode 100644 index 000000000..1fdd81130 --- /dev/null +++ b/tests/union_vector/union_vector_generated.ts @@ -0,0 +1,436 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/** + * @enum + */ +export namespace { +export enum Character{ + NONE= 0, + MuLan= 1, + Rapunzel= 2, + Belle= 3, + BookFan= 4, + Other= 5, + Unused= 6 +}}; + +/** + * @constructor + */ +export namespace { +export class Attacker { + /** + * @type {flatbuffers.ByteBuffer} + */ + bb: flatbuffers.ByteBuffer; + + /** + * @type {number} + */ + bb_pos:number = 0; +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {Attacker} + */ +__init(i:number, bb:flatbuffers.ByteBuffer):Attacker { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {Attacker=} obj + * @returns {Attacker} + */ +static getRootAsAttacker(bb:flatbuffers.ByteBuffer, obj?:Attacker):Attacker { + return (obj || new Attacker).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @returns {number} + */ +swordAttackDamage():number { + var offset = this.bb.__offset(this.bb_pos, 4); + return offset ? this.bb.readInt32(this.bb_pos + offset) : 0; +}; + +/** + * @param {number} value + * @returns {boolean} + */ +mutate_sword_attack_damage(value:number):boolean { + var offset = this.bb.__offset(this.bb_pos, 4); + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Builder} builder + */ +static startAttacker(builder:flatbuffers.Builder) { + builder.startObject(1); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} swordAttackDamage + */ +static addSwordAttackDamage(builder:flatbuffers.Builder, swordAttackDamage:number) { + builder.addFieldInt32(0, swordAttackDamage, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +static endAttacker(builder:flatbuffers.Builder):flatbuffers.Offset { + var offset = builder.endObject(); + return offset; +}; + +} +} +/** + * @constructor + */ +export namespace { +export class Rapunzel { + /** + * @type {flatbuffers.ByteBuffer} + */ + bb: flatbuffers.ByteBuffer; + + /** + * @type {number} + */ + bb_pos:number = 0; +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {Rapunzel} + */ +__init(i:number, bb:flatbuffers.ByteBuffer):Rapunzel { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @returns {number} + */ +hairLength():number { + return this.bb.readInt32(this.bb_pos); +}; + +/** + * @param {number} value + * @returns {boolean} + */ +mutate_hair_length(value:number):boolean { + var offset = this.bb.__offset(this.bb_pos, 0); + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} hair_length + * @returns {flatbuffers.Offset} + */ +static createRapunzel(builder:flatbuffers.Builder, hair_length: number):flatbuffers.Offset { + builder.prep(4, 4); + builder.writeInt32(hair_length); + return builder.offset(); +}; + +} +} +/** + * @constructor + */ +export namespace { +export class BookReader { + /** + * @type {flatbuffers.ByteBuffer} + */ + bb: flatbuffers.ByteBuffer; + + /** + * @type {number} + */ + bb_pos:number = 0; +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {BookReader} + */ +__init(i:number, bb:flatbuffers.ByteBuffer):BookReader { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @returns {number} + */ +booksRead():number { + return this.bb.readInt32(this.bb_pos); +}; + +/** + * @param {number} value + * @returns {boolean} + */ +mutate_books_read(value:number):boolean { + var offset = this.bb.__offset(this.bb_pos, 0); + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} books_read + * @returns {flatbuffers.Offset} + */ +static createBookReader(builder:flatbuffers.Builder, books_read: number):flatbuffers.Offset { + builder.prep(4, 4); + builder.writeInt32(books_read); + return builder.offset(); +}; + +} +} +/** + * @constructor + */ +export namespace { +export class Movie { + /** + * @type {flatbuffers.ByteBuffer} + */ + bb: flatbuffers.ByteBuffer; + + /** + * @type {number} + */ + bb_pos:number = 0; +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {Movie} + */ +__init(i:number, bb:flatbuffers.ByteBuffer):Movie { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {Movie=} obj + * @returns {Movie} + */ +static getRootAsMovie(bb:flatbuffers.ByteBuffer, obj?:Movie):Movie { + return (obj || new Movie).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @returns {boolean} + */ +static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean { + return bb.__has_identifier('MOVI'); +}; + +/** + * @returns {Character} + */ +mainCharacterType():Character { + var offset = this.bb.__offset(this.bb_pos, 4); + return offset ? /** @type {Character} */ (this.bb.readUint8(this.bb_pos + offset)) : Character.NONE; +}; + +/** + * @param {Character} value + * @returns {boolean} + */ +mutate_main_character_type(value:Character):boolean { + var offset = this.bb.__offset(this.bb_pos, 4); + + if (offset === 0) { + return false; + } + + this.bb.writeUint8(this.bb_pos + offset, value); + return true; +}; + +/** + * @param {flatbuffers.Table} obj + * @returns {?flatbuffers.Table} + */ +mainCharacter(obj:T):T|null { + var offset = this.bb.__offset(this.bb_pos, 6); + return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; +}; + +/** + * @param {number} index + * @returns {Character} + */ +charactersType(index: number):Character|null { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? /** @type {Character} */ (this.bb.readUint8(this.bb.__vector(this.bb_pos + offset) + index)) : /** @type {Character} */ (0); +}; + +/** + * @returns {number} + */ +charactersTypeLength():number { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @returns {Uint8Array} + */ +charactersTypeArray():Uint8Array|null { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? new Uint8Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null; +}; + +/** + * @param {number} index + * @param {flatbuffers.Table=} obj + * @returns {?flatbuffers.Table} + */ +characters(index: number, obj:T):T|null { + var offset = this.bb.__offset(this.bb_pos, 10); + return offset ? this.bb.__union(obj, this.bb.__vector(this.bb_pos + offset) + index * 4) : null; +}; + +/** + * @returns {number} + */ +charactersLength():number { + var offset = this.bb.__offset(this.bb_pos, 10); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @param {flatbuffers.Builder} builder + */ +static startMovie(builder:flatbuffers.Builder) { + builder.startObject(4); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Character} mainCharacterType + */ +static addMainCharacterType(builder:flatbuffers.Builder, mainCharacterType:Character) { + builder.addFieldInt8(0, mainCharacterType, Character.NONE); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} mainCharacterOffset + */ +static addMainCharacter(builder:flatbuffers.Builder, mainCharacterOffset:flatbuffers.Offset) { + builder.addFieldOffset(1, mainCharacterOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} charactersTypeOffset + */ +static addCharactersType(builder:flatbuffers.Builder, charactersTypeOffset:flatbuffers.Offset) { + builder.addFieldOffset(2, charactersTypeOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +static createCharactersTypeVector(builder:flatbuffers.Builder, data:Character[]):flatbuffers.Offset { + builder.startVector(1, data.length, 1); + for (var i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +static startCharactersTypeVector(builder:flatbuffers.Builder, numElems:number) { + builder.startVector(1, numElems, 1); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} charactersOffset + */ +static addCharacters(builder:flatbuffers.Builder, charactersOffset:flatbuffers.Offset) { + builder.addFieldOffset(3, charactersOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +static createCharactersVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset { + builder.startVector(4, data.length, 4); + for (var i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +static startCharactersVector(builder:flatbuffers.Builder, numElems:number) { + builder.startVector(4, numElems, 4); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +static endMovie(builder:flatbuffers.Builder):flatbuffers.Offset { + var offset = builder.endObject(); + return offset; +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} offset + */ +static finishMovieBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) { + builder.finish(offset, 'MOVI'); +}; + +} +}