diff options
author | Dan Field <dfield@gmail.com> | 2018-05-18 14:06:15 -0400 |
---|---|---|
committer | Wouter van Oortmerssen <aardappel@gmail.com> | 2018-05-18 11:06:15 -0700 |
commit | 88912640d08bab0588c5804d2990173006b57207 (patch) | |
tree | 5d451dd37682d66d40b85e4c3eeadd78e1a2a385 | |
parent | c43a0beff0ab4e1d5c5fe5044dfb30f24974eaff (diff) | |
download | flatbuffers-88912640d08bab0588c5804d2990173006b57207.tar.gz |
Add [Dart] support (#4676)
* Add [Dart] support
* fix enum vectors
* Allow for opt out of string interning
* fix comment style, make interning opt in
* remove Offset<T>, prefer int
* avoid creating unnecessary vtable objects
* start work on tests - do not generate builder if struct has 0 fields - add int64
* support reading structs properly
* correctly handle reading vectors of structs, dartfmt
* support structs, fix unnecessary prepares
* fix bool customizations
* undo unintentional removal of file
* docs updates, complete tutorial, bug fix for codegen
* more documentation
* Update docs, add to doxygen file
* update package structure, add samples script/code
* rearrange sample
* Tests
* Add readme for pub
* cleanup package for pub
* update docs for renamed file
* remove custom matcher, use `closeTo` instead
* remove unintentional file
* remove unintended file checkin
* use auto, move method, cleanup
* refactor to ObjectBuilders, add Builders
* Update tests, examples
* Add files missing from previous commit
* documentation and example updates
* Update LICENSE, make dartanalyzer happy, fix minor bugs, get rid of duplicate files, publish script
* fix sample for slightly different schema
* Update pubspec.yaml
27 files changed, 5547 insertions, 20 deletions
@@ -45,6 +45,7 @@ grpctest grpctest.exe snapshot.sh tags +tests/dart_gen tests/go_gen tests/monsterdata_java_wire.mon tests/monsterdata_java_wire_sp.mon @@ -94,3 +95,9 @@ js/flatbuffers.mjs build.ninja rules.ninja .vscode +dart/.pub/ +dart/.packages +dart/pubspec.lock +dart/.dart_tool/ +dart/build/ +dart/doc/api/ @@ -86,6 +86,7 @@ cc_binary( "grpc/src/compiler/schema_interface.h", "src/flatc_main.cpp", "src/idl_gen_cpp.cpp", + "src/idl_gen_dart.cpp", "src/idl_gen_general.cpp", "src/idl_gen_go.cpp", "src/idl_gen_grpc.cpp", diff --git a/CMakeLists.txt b/CMakeLists.txt index a3388ddc..90860c35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ set(FlatBuffers_Library_SRCS set(FlatBuffers_Compiler_SRCS ${FlatBuffers_Library_SRCS} src/idl_gen_cpp.cpp + src/idl_gen_dart.cpp src/idl_gen_general.cpp src/idl_gen_go.cpp src/idl_gen_js.cpp diff --git a/dart/LICENSE b/dart/LICENSE new file mode 100644 index 00000000..b2ae013b --- /dev/null +++ b/dart/LICENSE @@ -0,0 +1,233 @@ +The code in lib/flat_buffers.dart is based on code that was releases under the +following license: + +Copyright 2012, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +To the extent permissible, the changes to that code and the other assets in +this package are licensed under the Apache2 license: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014 Google Inc. + + 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. diff --git a/dart/README.md b/dart/README.md new file mode 100644 index 00000000..11bc0c48 --- /dev/null +++ b/dart/README.md @@ -0,0 +1,13 @@ +# FlatBuffers for Dart + +This package is used to read and write FlatBuffer files in Dart. + +Most consumers will want to use the [`flatc`](https://github.com/google/flatbuffers) +compiler to generate Dart code from a FlatBuffers IDL schema. For example, the +`monster_my_game.sample_generated.dart` was generated with `flatc` from +`monster.fbs` in the example folder. The generated classes can be used to read +or write binary files that are interoperable with other languages and platforms +supported by FlatBuffers, as illustrated in the `example.dart` in the +examples folder. + +Additional documentation and examples are available [at the FlatBuffers site](https://google.github.io/flatbuffers/index.html)
\ No newline at end of file diff --git a/dart/example/example.dart b/dart/example/example.dart new file mode 100644 index 00000000..d95bb31f --- /dev/null +++ b/dart/example/example.dart @@ -0,0 +1,155 @@ +/* + * Copyright 2018 Dan Field. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flat_buffers/flat_buffers.dart' as fb; +import './monster_my_game.sample_generated.dart' as myGame; + +// Example how to use FlatBuffers to create and read binary buffers. + +void main() { + builderTest(); + objectBuilderTest(); +} + +void builderTest() { + final builder = new fb.Builder(initialSize: 1024); + final int weaponOneName = builder.writeString("Sword"); + final int weaponOneDamage = 3; + + final int weaponTwoName = builder.writeString("Axe"); + final int weaponTwoDamage = 5; + + final swordBuilder = new myGame.WeaponBuilder(builder) + ..begin() + ..addNameOffset(weaponOneName) + ..addDamage(weaponOneDamage); + final int sword = swordBuilder.finish(); + + final axeBuilder = new myGame.WeaponBuilder(builder) + ..begin() + ..addNameOffset(weaponTwoName) + ..addDamage(weaponTwoDamage); + final int axe = axeBuilder.finish(); + + // Serialize a name for our monster, called "Orc". + final int name = builder.writeString('Orc'); + + // Create a list representing the inventory of the Orc. Each number + // could correspond to an item that can be claimed after he is slain. + final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + final inventory = builder.writeListUint8(treasure); + final weapons = builder.writeList([sword, axe]); + + // Struct builders are very easy to reuse. + final vec3Builder = new myGame.Vec3Builder(builder); + + vec3Builder.finish(4.0, 5.0, 6.0); + vec3Builder.finish(1.0, 2.0, 3.0); + // Set his hit points to 300 and his mana to 150. + final int hp = 300; + final int mana = 150; + + final monster = new myGame.MonsterBuilder(builder) + ..begin() + ..addNameOffset(name) + ..addInventoryOffset(inventory) + ..addWeaponsOffset(weapons) + ..addEquippedType(myGame.EquipmentTypeId.Weapon) + ..addEquippedOffset(axe) + ..addHp(hp) + ..addMana(mana) + ..addPos(vec3Builder.finish(1.0, 2.0, 3.0)) + ..addColor(myGame.Color.Red); + + final int monsteroff = monster.finish(); + final buffer = builder.finish(monsteroff); + if (verify(buffer)) { + print( + "The FlatBuffer was successfully created with a builder and verified!"); + } +} + +void objectBuilderTest() { + // Create the builder here so we can use it for both weapons and equipped + // the actual data will only be written to the buffer once. + var axe = new myGame.WeaponObjectBuilder(name: 'Axe', damage: 5); + + var monsterBuilder = new myGame.MonsterObjectBuilder( + pos: new myGame.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0), + mana: 150, + hp: 300, + name: 'Orc', + inventory: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + color: myGame.Color.Red, + weapons: [new myGame.WeaponObjectBuilder(name: 'Sword', damage: 3), axe], + equippedType: myGame.EquipmentTypeId.Weapon, + equipped: axe, + ); + + var buffer = monsterBuilder.toBytes(); + + // We now have a FlatBuffer we can store on disk or send over a network. + + // ** file/network code goes here :) ** + + // Instead, we're going to access it right away (as if we just received it). + if (verify(buffer)) { + print( + "The FlatBuffer was successfully created with an object builder and verified!"); + } +} + +bool verify(List<int> buffer) { + // Get access to the root: + var monster = new myGame.Monster(buffer); + + // Get and test some scalar types from the FlatBuffer. + assert(monster.hp == 80); + assert(monster.mana == 150); // default + assert(monster.name == "MyMonster"); + + // Get and test a field of the FlatBuffer's `struct`. + var pos = monster.pos; + assert(pos != null); + assert(pos.z == 3.0); + + // Get a test an element from the `inventory` FlatBuffer's `vector`. + var inv = monster.inventory; + assert(inv != null); + assert(inv.length == 10); + assert(inv[9] == 9); + + // Get and test the `weapons` FlatBuffers's `vector`. + var expected_weapon_names = ["Sword", "Axe"]; + var expected_weapon_damages = [3, 5]; + var weps = monster.weapons; + for (int i = 0; i < weps.length; i++) { + assert(weps[i].name == expected_weapon_names[i]); + assert(weps[i].damage == expected_weapon_damages[i]); + } + + // Get and test the `Equipment` union (`equipped` field). + assert(monster.equippedType.value == myGame.EquipmentTypeId.Weapon.value); + assert(monster.equippedType == myGame.EquipmentTypeId.Weapon); + + assert(monster.equipped is myGame.Weapon); + var equipped = monster.equipped as myGame.Weapon; + assert(equipped.name == "Axe"); + assert(equipped.damage == 5); + + print(monster); + return true; +} diff --git a/dart/example/monster_my_game.sample_generated.dart b/dart/example/monster_my_game.sample_generated.dart new file mode 100644 index 00000000..2c7c10d2 --- /dev/null +++ b/dart/example/monster_my_game.sample_generated.dart @@ -0,0 +1,440 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// ignore_for_file: unused_import, non_constant_identifier_names + +library my_game.sample; + +import 'dart:typed_data' show Uint8List; +import 'package:flat_buffers/flat_buffers.dart' as fb; + + +class Color { + final int value; + const Color._(this.value); + + factory Color.fromValue(int value) { + if (value == null) return null; + if (!values.containsKey(value)) { + throw new StateError('Invalid value $value for bit flag enum Color'); + } + return values[value]; + } + + static const int minValue = 0; + static const int maxValue = 2; + static bool containsValue(int value) => values.containsKey(value); + + static const Color Red = const Color._(0); + static const Color Green = const Color._(1); + static const Color Blue = const Color._(2); + static get values => {0: Red,1: Green,2: Blue,}; + + static const fb.Reader<Color> reader = const _ColorReader(); + + @override + String toString() { + return 'Color{value: $value}'; + } +} + +class _ColorReader extends fb.Reader<Color> { + const _ColorReader(); + + @override + int get size => 1; + + @override + Color read(fb.BufferContext bc, int offset) => + new Color.fromValue(const fb.Int8Reader().read(bc, offset)); +} + +class EquipmentTypeId { + final int value; + const EquipmentTypeId._(this.value); + + factory EquipmentTypeId.fromValue(int value) { + if (value == null) return null; + if (!values.containsKey(value)) { + throw new StateError('Invalid value $value for bit flag enum EquipmentTypeId'); + } + return values[value]; + } + + static const int minValue = 0; + static const int maxValue = 1; + static bool containsValue(int value) => values.containsKey(value); + + static const EquipmentTypeId NONE = const EquipmentTypeId._(0); + static const EquipmentTypeId Weapon = const EquipmentTypeId._(1); + static get values => {0: NONE,1: Weapon,}; + + static const fb.Reader<EquipmentTypeId> reader = const _EquipmentTypeIdReader(); + + @override + String toString() { + return 'EquipmentTypeId{value: $value}'; + } +} + +class _EquipmentTypeIdReader extends fb.Reader<EquipmentTypeId> { + const _EquipmentTypeIdReader(); + + @override + int get size => 1; + + @override + EquipmentTypeId read(fb.BufferContext bc, int offset) => + new EquipmentTypeId.fromValue(const fb.Uint8Reader().read(bc, offset)); +} + +class Vec3 { + Vec3._(this._bc, this._bcOffset); + + static const fb.Reader<Vec3> reader = const _Vec3Reader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + double get x => const fb.Float32Reader().read(_bc, _bcOffset + 0); + double get y => const fb.Float32Reader().read(_bc, _bcOffset + 4); + double get z => const fb.Float32Reader().read(_bc, _bcOffset + 8); + + @override + String toString() { + return 'Vec3{x: $x, y: $y, z: $z}'; + } +} + +class _Vec3Reader extends fb.StructReader<Vec3> { + const _Vec3Reader(); + + @override + int get size => 12; + + @override + Vec3 createObject(fb.BufferContext bc, int offset) => + new Vec3._(bc, offset); +} + +class Vec3Builder { + Vec3Builder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + int finish(double x, double y, double z) { + fbBuilder.putFloat32(z); + fbBuilder.putFloat32(y); + fbBuilder.putFloat32(x); + return fbBuilder.offset; + } + +} + +class Vec3ObjectBuilder extends fb.ObjectBuilder { + final double _x; + final double _y; + final double _z; + + Vec3ObjectBuilder({ + double x, + double y, + double z, + }) + : _x = x, + _y = y, + _z = z; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.putFloat32(_z); + fbBuilder.putFloat32(_y); + fbBuilder.putFloat32(_x); + return fbBuilder.offset; + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class Monster { + Monster._(this._bc, this._bcOffset); + factory Monster(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<Monster> reader = const _MonsterReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + Vec3 get pos => Vec3.reader.vTableGet(_bc, _bcOffset, 4, null); + int get mana => const fb.Int16Reader().vTableGet(_bc, _bcOffset, 6, 150); + int get hp => const fb.Int16Reader().vTableGet(_bc, _bcOffset, 8, 100); + String get name => const fb.StringReader().vTableGet(_bc, _bcOffset, 10, null); + List<int> get inventory => const fb.ListReader<int>(const fb.Uint8Reader()).vTableGet(_bc, _bcOffset, 14, null); + Color get color => new Color.fromValue(const fb.Int8Reader().vTableGet(_bc, _bcOffset, 16, 2)); + List<Weapon> get weapons => const fb.ListReader<Weapon>(Weapon.reader).vTableGet(_bc, _bcOffset, 18, null); + EquipmentTypeId get equippedType => new EquipmentTypeId.fromValue(const fb.Uint8Reader().vTableGet(_bc, _bcOffset, 20, null)); + dynamic get equipped { + switch (equippedType?.value) { + case 1: return Weapon.reader.vTableGet(_bc, _bcOffset, 22, null); + default: return null; + } + } + List<Vec3> get path => const fb.ListReader<Vec3>(Vec3.reader).vTableGet(_bc, _bcOffset, 24, null); + + @override + String toString() { + return 'Monster{pos: $pos, mana: $mana, hp: $hp, name: $name, inventory: $inventory, color: $color, weapons: $weapons, equippedType: $equippedType, equipped: $equipped, path: $path}'; + } +} + +class _MonsterReader extends fb.TableReader<Monster> { + const _MonsterReader(); + + @override + Monster createObject(fb.BufferContext bc, int offset) => + new Monster._(bc, offset); +} + +class MonsterBuilder { + MonsterBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(); + } + + int addPos(int offset) { + fbBuilder.addStruct(0, offset); + return fbBuilder.offset; + } + int addMana(int mana) { + fbBuilder.addInt16(1, mana); + return fbBuilder.offset; + } + int addHp(int hp) { + fbBuilder.addInt16(2, hp); + return fbBuilder.offset; + } + int addNameOffset(int offset) { + fbBuilder.addOffset(3, offset); + return fbBuilder.offset; + } + int addInventoryOffset(int offset) { + fbBuilder.addOffset(5, offset); + return fbBuilder.offset; + } + int addColor(Color color) { + fbBuilder.addInt8(6, color?.value); + return fbBuilder.offset; + } + int addWeaponsOffset(int offset) { + fbBuilder.addOffset(7, offset); + return fbBuilder.offset; + } + int addEquippedType(EquipmentTypeId equippedType) { + fbBuilder.addUint8(8, equippedType?.value); + return fbBuilder.offset; + } + int addEquippedOffset(int offset) { + fbBuilder.addOffset(9, offset); + return fbBuilder.offset; + } + int addPathOffset(int offset) { + fbBuilder.addOffset(10, offset); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class MonsterObjectBuilder extends fb.ObjectBuilder { + final Vec3ObjectBuilder _pos; + final int _mana; + final int _hp; + final String _name; + final List<int> _inventory; + final Color _color; + final List<WeaponObjectBuilder> _weapons; + final EquipmentTypeId _equippedType; + final dynamic _equipped; + final List<Vec3ObjectBuilder> _path; + + MonsterObjectBuilder({ + Vec3ObjectBuilder pos, + int mana, + int hp, + String name, + List<int> inventory, + Color color, + List<WeaponObjectBuilder> weapons, + EquipmentTypeId equippedType, + dynamic equipped, + List<Vec3ObjectBuilder> path, + }) + : _pos = pos, + _mana = mana, + _hp = hp, + _name = name, + _inventory = inventory, + _color = color, + _weapons = weapons, + _equippedType = equippedType, + _equipped = equipped, + _path = path; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + final int nameOffset = fbBuilder.writeString(_name); + final int inventoryOffset = _inventory?.isNotEmpty == true + ? fbBuilder.writeListUint8(_inventory) + : null; + final int weaponsOffset = _weapons?.isNotEmpty == true + ? fbBuilder.writeList(_weapons.map((b) => b.getOrCreateOffset(fbBuilder)).toList()) + : null; + final int equippedOffset = _equipped?.getOrCreateOffset(fbBuilder); + final int pathOffset = _path?.isNotEmpty == true + ? fbBuilder.writeListOfStructs(_path) + : null; + + fbBuilder.startTable(); + if (_pos != null) { + fbBuilder.addStruct(0, _pos.finish(fbBuilder)); + } + fbBuilder.addInt16(1, _mana); + fbBuilder.addInt16(2, _hp); + if (nameOffset != null) { + fbBuilder.addOffset(3, nameOffset); + } + if (inventoryOffset != null) { + fbBuilder.addOffset(5, inventoryOffset); + } + fbBuilder.addInt8(6, _color?.value); + if (weaponsOffset != null) { + fbBuilder.addOffset(7, weaponsOffset); + } + fbBuilder.addUint8(8, _equippedType?.value); + if (equippedOffset != null) { + fbBuilder.addOffset(9, equippedOffset); + } + if (pathOffset != null) { + fbBuilder.addOffset(10, pathOffset); + } + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class Weapon { + Weapon._(this._bc, this._bcOffset); + factory Weapon(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<Weapon> reader = const _WeaponReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + String get name => const fb.StringReader().vTableGet(_bc, _bcOffset, 4, null); + int get damage => const fb.Int16Reader().vTableGet(_bc, _bcOffset, 6, null); + + @override + String toString() { + return 'Weapon{name: $name, damage: $damage}'; + } +} + +class _WeaponReader extends fb.TableReader<Weapon> { + const _WeaponReader(); + + @override + Weapon createObject(fb.BufferContext bc, int offset) => + new Weapon._(bc, offset); +} + +class WeaponBuilder { + WeaponBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(); + } + + int addNameOffset(int offset) { + fbBuilder.addOffset(0, offset); + return fbBuilder.offset; + } + int addDamage(int damage) { + fbBuilder.addInt16(1, damage); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class WeaponObjectBuilder extends fb.ObjectBuilder { + final String _name; + final int _damage; + + WeaponObjectBuilder({ + String name, + int damage, + }) + : _name = name, + _damage = damage; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + final int nameOffset = fbBuilder.writeString(_name); + + fbBuilder.startTable(); + if (nameOffset != null) { + fbBuilder.addOffset(0, nameOffset); + } + fbBuilder.addInt16(1, _damage); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} diff --git a/dart/lib/flat_buffers.dart b/dart/lib/flat_buffers.dart new file mode 100644 index 00000000..baef5a35 --- /dev/null +++ b/dart/lib/flat_buffers.dart @@ -0,0 +1,1241 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:collection'; +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +const int _sizeofUint8 = 1; +const int _sizeofUint16 = 2; +const int _sizeofUint32 = 4; +const int _sizeofUint64 = 8; +const int _sizeofInt8 = 1; +const int _sizeofInt16 = 2; +const int _sizeofInt32 = 4; +const int _sizeofInt64 = 8; +const int _sizeofFloat32 = 4; +const int _sizeofFloat64 = 8; + +/// Callback used to invoke a struct builder's finish method. +/// +/// This callback is used by other struct's `finish` methods to write the nested +/// struct's fields inline. +typedef void StructBuilder(); + +/// Buffer with data and some context about it. +class BufferContext { + final ByteData _buffer; + + factory BufferContext.fromBytes(List<int> byteList) { + Uint8List uint8List = _asUint8List(byteList); + ByteData buf = new ByteData.view(uint8List.buffer, uint8List.offsetInBytes); + return new BufferContext._(buf); + } + + BufferContext._(this._buffer); + + int derefObject(int offset) { + return offset + _getUint32(offset); + } + + Uint8List _asUint8LIst(int offset, int length) => + _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length); + + double _getFloat64(int offset) => + _buffer.getFloat64(offset, Endianness.LITTLE_ENDIAN); + + double _getFloat32(int offset) => + _buffer.getFloat32(offset, Endianness.LITTLE_ENDIAN); + + int _getInt64(int offset) => + _buffer.getInt64(offset, Endianness.LITTLE_ENDIAN); + + int _getInt32(int offset) => + _buffer.getInt32(offset, Endianness.LITTLE_ENDIAN); + + int _getInt16(int offset) => + _buffer.getInt16(offset, Endianness.LITTLE_ENDIAN); + + int _getInt8(int offset) => _buffer.getInt8(offset); + + int _getUint64(int offset) => + _buffer.getUint64(offset, Endianness.LITTLE_ENDIAN); + + int _getUint32(int offset) => + _buffer.getUint32(offset, Endianness.LITTLE_ENDIAN); + + int _getUint16(int offset) => + _buffer.getUint16(offset, Endianness.LITTLE_ENDIAN); + + int _getUint8(int offset) => _buffer.getUint8(offset); + + /// If the [byteList] is already a [Uint8List] return it. + /// Otherwise return a [Uint8List] copy of the [byteList]. + static Uint8List _asUint8List(List<int> byteList) { + if (byteList is Uint8List) { + return byteList; + } else { + return new Uint8List.fromList(byteList); + } + } +} + +/// Class implemented by typed builders generated by flatc. +abstract class ObjectBuilder { + int _firstOffset; + + /// Can be used to write the data represented by this builder to the [Builder] + /// and reuse the offset created in multiple tables. + /// + /// Note that this method assumes you call it using the same [Builder] instance + /// every time. The returned offset is only good for the [Builder] used in the + /// first call to this method. + int getOrCreateOffset(Builder fbBuilder) { + _firstOffset ??= finish(fbBuilder); + return _firstOffset; + } + + /// Writes the data in this helper to the [Builder]. + int finish(Builder fbBuilder); + + /// Convenience method that will create a new [Builder], [finish]es the data, + /// and returns the buffer as a [Uint8List] of bytes. + Uint8List toBytes(); +} + +/// Class that helps building flat buffers. +class Builder { + final int initialSize; + + /// The list of existing VTable(s). + //final List<_VTable> _vTables = <_VTable>[]; + final List<int> _vTables = <int>[]; + + ByteData _buf; + + /// The maximum alignment that has been seen so far. If [_buf] has to be + /// reallocated in the future (to insert room at its start for more bytes) the + /// reallocation will need to be a multiple of this many bytes. + int _maxAlign; + + /// The number of bytes that have been written to the buffer so far. The + /// most recently written byte is this many bytes from the end of [_buf]. + int _tail; + + /// The location of the end of the current table, measured in bytes from the + /// end of [_buf], or `null` if a table is not currently being built. + int _currentTableEndTail; + + _VTable _currentVTable; + + /// Map containing all strings that have been written so far. This allows us + /// to avoid duplicating strings. + /// + /// Allocated only if `internStrings` is set to true on the constructor. + Map<String, int> _strings; + + /// Creates a new FlatBuffers Builder. + /// + /// `initialSize` is the initial array size in bytes. The [Builder] will + /// automatically grow the array if/as needed. `internStrings`, if set to + /// true, will cause [writeString] to pool strings in the buffer so that + /// identical strings will always use the same offset in tables. + Builder({this.initialSize: 1024, bool internStrings = false}) { + if (internStrings == true) { + _strings = new Map<String, int>(); + } + reset(); + } + + /// Add the [field] with the given boolean [value]. The field is not added if + /// the [value] is equal to [def]. Booleans are stored as 8-bit fields with + /// `0` for `false` and `1` for `true`. + void addBool(int field, bool value, [bool def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofUint8, 1); + _trackField(field); + _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0); + } + } + + /// Add the [field] with the given 32-bit signed integer [value]. The field is + /// not added if the [value] is equal to [def]. + void addInt32(int field, int value, [int def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofInt32, 1); + _trackField(field); + _setInt32AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 32-bit signed integer [value]. The field is + /// not added if the [value] is equal to [def]. + void addInt16(int field, int value, [int def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofInt16, 1); + _trackField(field); + _setInt16AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 8-bit signed integer [value]. The field is + /// not added if the [value] is equal to [def]. + void addInt8(int field, int value, [int def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofInt8, 1); + _trackField(field); + _setInt8AtTail(_buf, _tail, value); + } + } + + void addStruct(int field, int offset) { + _ensureCurrentVTable(); + _trackField(field); + _currentVTable.addField(field, offset); + } + + /// Add the [field] referencing an object with the given [offset]. + void addOffset(int field, int offset) { + _ensureCurrentVTable(); + if (offset != null) { + _prepare(_sizeofUint32, 1); + _trackField(field); + _setUint32AtTail(_buf, _tail, _tail - offset); + } + } + + /// Add the [field] with the given 32-bit unsigned integer [value]. The field + /// is not added if the [value] is equal to [def]. + void addUint32(int field, int value, [int def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofUint32, 1); + _trackField(field); + _setUint32AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 32-bit unsigned integer [value]. The field + /// is not added if the [value] is equal to [def]. + void addUint16(int field, int value, [int def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofUint16, 1); + _trackField(field); + _setUint16AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 8-bit unsigned integer [value]. The field + /// is not added if the [value] is equal to [def]. + void addUint8(int field, int value, [int def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofUint8, 1); + _trackField(field); + _setUint8AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 32-bit float [value]. The field + /// is not added if the [value] is equal to [def]. + void addFloat32(int field, double value, [double def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofFloat32, 1); + _trackField(field); + _setFloat32AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 64-bit double [value]. The field + /// is not added if the [value] is equal to [def]. + void addFloat64(int field, double value, [double def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofFloat64, 1); + _trackField(field); + _setFloat64AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 64-bit unsigned integer [value]. The field + /// is not added if the [value] is equal to [def]. + void addUint64(int field, int value, [double def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofUint64, 1); + _trackField(field); + _setUint64AtTail(_buf, _tail, value); + } + } + + /// Add the [field] with the given 64-bit unsigned integer [value]. The field + /// is not added if the [value] is equal to [def]. + void addInt64(int field, int value, [double def]) { + _ensureCurrentVTable(); + if (value != null && value != def) { + _prepare(_sizeofInt64, 1); + _trackField(field); + _setInt64AtTail(_buf, _tail, value); + } + } + + /// End the current table and return its offset. + int endTable() { + if (_currentVTable == null) { + throw new StateError('Start a table before ending it.'); + } + // Prepare for writing the VTable. + _prepare(_sizeofInt32, 1); + int tableTail = _tail; + // Prepare the size of the current table. + _currentVTable.tableSize = tableTail - _currentTableEndTail; + // Prepare the VTable to use for the current table. + int vTableTail; + { + _currentVTable.computeFieldOffsets(tableTail); + // Try to find an existing compatible VTable. + // Search backward - more likely to have recently used one + for (int i = _vTables.length - 1; i >= 0; i--) { + final int vt2Offset = _vTables[i]; + final int vt2Start = _buf.lengthInBytes - vt2Offset; + final int vt2Size = _buf.getUint16(vt2Start, Endianness.LITTLE_ENDIAN); + + if (_currentVTable._vTableSize == vt2Size && + _currentVTable._offsetsMatch(vt2Start, _buf)) { + vTableTail = vt2Offset; + break; + } + } + // Write a new VTable. + if (vTableTail == null) { + _prepare(_sizeofUint16, _currentVTable.numOfUint16); + vTableTail = _tail; + _currentVTable.tail = vTableTail; + _currentVTable.output(_buf, _buf.lengthInBytes - _tail); + _vTables.add(_currentVTable.tail); + } + } + // Set the VTable offset. + _setInt32AtTail(_buf, tableTail, vTableTail - tableTail); + // Done with this table. + _currentVTable = null; + return tableTail; + } + + /// This method low level method can be used to return a raw piece of the buffer + /// after using the the put* methods. + /// + /// Most clients should prefer calling [finish]. + Uint8List lowFinish() { + int alignedTail = _tail + ((-_tail) % _maxAlign); + return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail); + } + + /// Finish off the creation of the buffer. The given [offset] is used as the + /// root object offset, and usually references directly or indirectly every + /// written object. If [fileIdentifier] is specified (and not `null`), it is + /// interpreted as a 4-byte Latin-1 encoded string that should be placed at + /// bytes 4-7 of the file. + Uint8List finish(int offset, [String fileIdentifier]) { + _prepare(max(_sizeofUint32, _maxAlign), fileIdentifier == null ? 1 : 2); + int alignedTail = _tail + ((-_tail) % _maxAlign); + _setUint32AtTail(_buf, alignedTail, alignedTail - offset); + if (fileIdentifier != null) { + for (int i = 0; i < 4; i++) { + _setUint8AtTail(_buf, alignedTail - _sizeofUint32 - i, + fileIdentifier.codeUnitAt(i)); + } + } + return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail); + } + + /// Writes a Float64 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putFloat64(double value) { + _prepare(_sizeofFloat64, 1); + _setFloat32AtTail(_buf, _tail, value); + } + + /// Writes a Float32 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putFloat32(double value) { + _prepare(_sizeofFloat32, 1); + _setFloat32AtTail(_buf, _tail, value); + } + + /// Writes a Int64 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putInt64(int value) { + _prepare(_sizeofInt64, 1); + _setInt64AtTail(_buf, _tail, value); + } + + /// Writes a Uint32 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putInt32(int value) { + _prepare(_sizeofInt32, 1); + _setInt32AtTail(_buf, _tail, value); + } + + /// Writes a Uint16 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putInt16(int value) { + _prepare(_sizeofInt16, 1); + _setInt16AtTail(_buf, _tail, value); + } + + /// Writes a Uint8 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putInt8(int value) { + _prepare(_sizeofInt8, 1); + _buf.setInt8(_buf.lengthInBytes - _tail, value); + } + + /// Writes a Uint64 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putUint64(int value) { + _prepare(_sizeofUint64, 1); + _setUint64AtTail(_buf, _tail, value); + } + + /// Writes a Uint32 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putUint32(int value) { + _prepare(_sizeofUint32, 1); + _setUint32AtTail(_buf, _tail, value); + } + + /// Writes a Uint16 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putUint16(int value) { + _prepare(_sizeofUint16, 1); + _setUint16AtTail(_buf, _tail, value); + } + + /// Writes a Uint8 to the tail of the buffer after preparing space for it. + /// + /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer. + void putUint8(int value) { + _prepare(_sizeofUint8, 1); + _buf.setUint8(_buf.lengthInBytes - _tail, value); + } + + /// Reset the builder and make it ready for filling a new buffer. + void reset() { + _buf = new ByteData(initialSize); + _maxAlign = 1; + _tail = 0; + _currentVTable = null; + if (_strings != null) { + _strings = new Map<String, int>(); + } + } + + /// Start a new table. Must be finished with [endTable] invocation. + void startTable() { + if (_currentVTable != null) { + throw new StateError('Inline tables are not supported.'); + } + _currentVTable = new _VTable(); + _currentTableEndTail = _tail; + } + + /// Finish a Struct vector. Most callers should preferto use [writeListOfStructs]. + /// + /// Most callers should prefer [writeListOfStructs]. + int endStructVector(int count) { + putUint32(count); + return _tail; + } + + /// Writes a list of Structs to the buffer, returning the offset + int writeListOfStructs(List<ObjectBuilder> structBuilders) { + _ensureNoVTable(); + for (int i = structBuilders.length - 1; i >= 0; i--) { + structBuilders[i].finish(this); + } + return endStructVector(structBuilders.length); + } + + /// Write the given list of [values]. + int writeList(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 1 + values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setUint32AtTail(_buf, tail, tail - value); + tail -= _sizeofUint32; + } + return result; + } + + /// Write the given list of 64-bit float [values]. + int writeListFloat64(List<double> values) { + _ensureNoVTable(); + _prepare(4, 1 + (2 * values.length)); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (double value in values) { + _setFloat64AtTail(_buf, tail, value); + tail -= _sizeofFloat64; + } + return result; + } + + /// Write the given list of 32-bit float [values]. + int writeListFloat32(List<double> values) { + _ensureNoVTable(); + _prepare(_sizeofFloat32, 1 + values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (double value in values) { + _setFloat32AtTail(_buf, tail, value); + tail -= _sizeofFloat32; + } + return result; + } + + /// Write the given list of signed 64-bit integer [values]. + int writeListInt64(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 2 * values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setInt64AtTail(_buf, tail, value); + tail -= _sizeofInt64; + } + return result; + } + + /// Write the given list of signed 64-bit integer [values]. + int writeListUint64(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 2 * values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setUint64AtTail(_buf, tail, value); + tail -= _sizeofUint64; + } + return result; + } + + /// Write the given list of signed 32-bit integer [values]. + int writeListInt32(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 1 + values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setInt32AtTail(_buf, tail, value); + tail -= _sizeofInt32; + } + return result; + } + + /// Write the given list of unsigned 32-bit integer [values]. + int writeListUint32(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 1 + values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setUint32AtTail(_buf, tail, value); + tail -= _sizeofUint32; + } + return result; + } + + /// Write the given list of signed 16-bit integer [values]. + int writeListInt16(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setInt16AtTail(_buf, tail, value); + tail -= _sizeofInt16; + } + return result; + } + + /// Write the given list of unsigned 16-bit integer [values]. + int writeListUint16(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setUint16AtTail(_buf, tail, value); + tail -= _sizeofUint16; + } + return result; + } + + /// Write the given list of bools as unsigend 8-bit integer [values]. + int writeListBool(List<bool> values) { + return writeListUint8(values?.map((b) => b ? 1 : 0)?.toList()); + } + + /// Write the given list of signed 8-bit integer [values]. + int writeListInt8(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 1, additionalBytes: values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setInt8AtTail(_buf, tail, value); + tail -= _sizeofUint8; + } + return result; + } + + /// Write the given list of unsigned 8-bit integer [values]. + int writeListUint8(List<int> values) { + _ensureNoVTable(); + _prepare(_sizeofUint32, 1, additionalBytes: values.length); + final int result = _tail; + int tail = _tail; + _setUint32AtTail(_buf, tail, values.length); + tail -= _sizeofUint32; + for (int value in values) { + _setUint8AtTail(_buf, tail, value); + tail -= _sizeofUint8; + } + return result; + } + + /// Write the given string [value] and return its offset, or `null` if + /// the [value] is `null`. + int writeString(String value) { + _ensureNoVTable(); + if (value != null) { + if (_strings != null) { + return _strings.putIfAbsent(value, () => _writeString(value)); + } else { + return _writeString(value); + } + } + return null; + } + + int _writeString(String value) { + // TODO(scheglov) optimize for ASCII strings + List<int> bytes = UTF8.encode(value); + int length = bytes.length; + _prepare(4, 1, additionalBytes: length); + final int result = _tail; + _setUint32AtTail(_buf, _tail, length); + int offset = _buf.lengthInBytes - _tail + 4; + for (int i = 0; i < length; i++) { + _buf.setUint8(offset++, bytes[i]); + } + return result; + } + + /// Throw an exception if there is not currently a vtable. + void _ensureCurrentVTable() { + if (_currentVTable == null) { + throw new StateError('Start a table before adding values.'); + } + } + + /// Throw an exception if there is currently a vtable. + void _ensureNoVTable() { + if (_currentVTable != null) { + throw new StateError( + 'Cannot write a non-scalar value while writing a table.'); + } + } + + /// The number of bytes that have been written to the buffer so far. The + /// most recently written byte is this many bytes from the end of the buffer. + int get offset => _tail; + + /// Zero-pads the buffer, which may be required for some struct layouts. + void pad(int howManyBytes) { + for (int i = 0; i < howManyBytes; i++) putUint8(0); + } + + /// Prepare for writing the given `count` of scalars of the given `size`. + /// Additionally allocate the specified `additionalBytes`. Update the current + /// tail pointer to point at the allocated space. + void _prepare(int size, int count, {int additionalBytes = 0}) { + // Update the alignment. + if (_maxAlign < size) { + _maxAlign = size; + } + // Prepare amount of required space. + int dataSize = size * count + additionalBytes; + int alignDelta = (-(_tail + dataSize)) % size; + int bufSize = alignDelta + dataSize; + // Ensure that we have the required amount of space. + { + int oldCapacity = _buf.lengthInBytes; + if (_tail + bufSize > oldCapacity) { + int desiredNewCapacity = (oldCapacity + bufSize) * 2; + int deltaCapacity = desiredNewCapacity - oldCapacity; + deltaCapacity += (-deltaCapacity) % _maxAlign; + int newCapacity = oldCapacity + deltaCapacity; + ByteData newBuf = new ByteData(newCapacity); + newBuf.buffer + .asUint8List() + .setAll(deltaCapacity, _buf.buffer.asUint8List()); + _buf = newBuf; + } + } + // Update the tail pointer. + _tail += bufSize; + } + + /// Record the offset of the given [field]. + void _trackField(int field) { + _currentVTable.addField(field, _tail); + } + + static void _setFloat64AtTail(ByteData _buf, int tail, double x) { + _buf.setFloat64(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setFloat32AtTail(ByteData _buf, int tail, double x) { + _buf.setFloat32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setUint64AtTail(ByteData _buf, int tail, int x) { + _buf.setUint64(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setInt64AtTail(ByteData _buf, int tail, int x) { + _buf.setInt64(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setInt32AtTail(ByteData _buf, int tail, int x) { + _buf.setInt32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setUint32AtTail(ByteData _buf, int tail, int x) { + _buf.setUint32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setInt16AtTail(ByteData _buf, int tail, int x) { + _buf.setInt16(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setUint16AtTail(ByteData _buf, int tail, int x) { + _buf.setUint16(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); + } + + static void _setInt8AtTail(ByteData _buf, int tail, int x) { + _buf.setInt8(_buf.lengthInBytes - tail, x); + } + + static void _setUint8AtTail(ByteData _buf, int tail, int x) { + _buf.setUint8(_buf.lengthInBytes - tail, x); + } +} + +/// Reader of lists of boolean values. +/// +/// The returned unmodifiable lists lazily read values on access. +class BoolListReader extends Reader<List<bool>> { + const BoolListReader(); + + @override + int get size => _sizeofUint32; + + @override + List<bool> read(BufferContext bc, int offset) => + new _FbBoolList(bc, bc.derefObject(offset)); +} + +/// The reader of booleans. +class BoolReader extends Reader<bool> { + const BoolReader() : super(); + + @override + int get size => _sizeofUint8; + + @override + bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0; +} + +/// The reader of lists of 64-bit float values. +/// +/// The returned unmodifiable lists lazily read values on access. +class Float64ListReader extends Reader<List<double>> { + const Float64ListReader(); + + @override + int get size => _sizeofFloat64; + + @override + List<double> read(BufferContext bc, int offset) => + new _FbFloat64List(bc, bc.derefObject(offset)); +} + +class Float32ListReader extends Reader<List<double>> { + const Float32ListReader(); + + @override + int get size => _sizeofFloat32; + + @override + List<double> read(BufferContext bc, int offset) => + new _FbFloat32List(bc, bc.derefObject(offset)); +} + +class Float64Reader extends Reader<double> { + const Float64Reader(); + + @override + int get size => _sizeofFloat64; + + @override + double read(BufferContext bc, int offset) => bc._getFloat64(offset); +} + +class Float32Reader extends Reader<double> { + const Float32Reader(); + + @override + int get size => _sizeofFloat32; + + @override + double read(BufferContext bc, int offset) => bc._getFloat32(offset); +} + +class Int64Reader extends Reader<int> { + const Int64Reader() : super(); + @override + int get size => _sizeofInt64; + + @override + int read(BufferContext bc, int offset) => bc._getInt64(offset); +} + +/// The reader of signed 32-bit integers. +class Int32Reader extends Reader<int> { + const Int32Reader() : super(); + + @override + int get size => _sizeofInt32; + + @override + int read(BufferContext bc, int offset) => bc._getInt32(offset); +} + +/// The reader of signed 32-bit integers. +class Int16Reader extends Reader<int> { + const Int16Reader() : super(); + + @override + int get size => _sizeofInt16; + + @override + int read(BufferContext bc, int offset) => bc._getInt16(offset); +} + +/// The reader of 8-bit signed integers. +class Int8Reader extends Reader<int> { + const Int8Reader() : super(); + + @override + int get size => _sizeofInt8; + + @override + int read(BufferContext bc, int offset) => bc._getInt8(offset); +} + +/// The reader of lists of objects. +/// +/// The returned unmodifiable lists lazily read objects on access. +class ListReader<E> extends Reader<List<E>> { + final Reader<E> _elementReader; + + const ListReader(this._elementReader); + + @override + int get size => _sizeofUint32; + + @override + List<E> read(BufferContext bc, int offset) => + new _FbGenericList<E>(_elementReader, bc, bc.derefObject(offset)); +} + +/// Object that can read a value at a [BufferContext]. +abstract class Reader<T> { + const Reader(); + + /// The size of the value in bytes. + int get size; + + /// Read the value at the given [offset] in [bc]. + T read(BufferContext bc, int offset); + + /// Read the value of the given [field] in the given [object]. + T vTableGet(BufferContext object, int offset, int field, [T defaultValue]) { + int vTableSOffset = object._getInt32(offset); + int vTableOffset = offset - vTableSOffset; + int vTableSize = object._getUint16(vTableOffset); + int vTableFieldOffset = field; + if (vTableFieldOffset < vTableSize) { + int fieldOffsetInObject = + object._getUint16(vTableOffset + vTableFieldOffset); + if (fieldOffsetInObject != 0) { + return read(object, offset + fieldOffsetInObject); + } + } + return defaultValue; + } +} + +/// The reader of string values. +class StringReader extends Reader<String> { + const StringReader() : super(); + + @override + int get size => 4; + + @override + String read(BufferContext bc, int offset) { + int strOffset = bc.derefObject(offset); + int length = bc._getUint32(strOffset); + Uint8List bytes = bc._asUint8LIst(strOffset + 4, length); + if (_isLatin(bytes)) { + return new String.fromCharCodes(bytes); + } + return UTF8.decode(bytes); + } + + static bool _isLatin(Uint8List bytes) { + int length = bytes.length; + for (int i = 0; i < length; i++) { + if (bytes[i] > 127) { + return false; + } + } + return true; + } +} + +/// An abstract reader for structs. +abstract class StructReader<T> extends Reader<T> { + const StructReader(); + + /// Return the object at `offset`. + T createObject(BufferContext bc, int offset); + + T read(BufferContext bp, int offset) { + return createObject(bp, offset); + } +} + +/// An abstract reader for tables. +abstract class TableReader<T> extends Reader<T> { + const TableReader(); + + @override + int get size => 4; + + /// Return the object at [offset]. + T createObject(BufferContext bc, int offset); + + @override + T read(BufferContext bp, int offset) { + int objectOffset = bp.derefObject(offset); + return createObject(bp, objectOffset); + } +} + +/// Reader of lists of unsigned 32-bit integer values. +/// +/// The returned unmodifiable lists lazily read values on access. +class Uint32ListReader extends Reader<List<int>> { + const Uint32ListReader(); + + @override + int get size => _sizeofUint32; + + @override + List<int> read(BufferContext bc, int offset) => + new _FbUint32List(bc, bc.derefObject(offset)); +} + +/// The reader of unsigned 64-bit integers. +/// +/// WARNING: May have compatibility issues with JavaScript +class Uint64Reader extends Reader<int> { + const Uint64Reader() : super(); + + @override + int get size => _sizeofUint64; + + @override + int read(BufferContext bc, int offset) => bc._getUint64(offset); +} + +/// The reader of unsigned 32-bit integers. +class Uint32Reader extends Reader<int> { + const Uint32Reader() : super(); + + @override + int get size => _sizeofUint32; + + @override + int read(BufferContext bc, int offset) => bc._getUint32(offset); +} + +/// Reader of lists of unsigned 32-bit integer values. +/// +/// The returned unmodifiable lists lazily read values on access. +class Uint16ListReader extends Reader<List<int>> { + const Uint16ListReader(); + + @override + int get size => _sizeofUint32; + + @override + List<int> read(BufferContext bc, int offset) => + new _FbUint16List(bc, bc.derefObject(offset)); +} + +/// The reader of unsigned 32-bit integers. +class Uint16Reader extends Reader<int> { + const Uint16Reader() : super(); + + @override + int get size => _sizeofUint16; + + @override + int read(BufferContext bc, int offset) => bc._getUint16(offset); +} + +/// Reader of lists of unsigned 8-bit integer values. +/// +/// The returned unmodifiable lists lazily read values on access. +class Uint8ListReader extends Reader<List<int>> { + const Uint8ListReader(); + + @override + int get size => _sizeofUint32; + + @override + List<int> read(BufferContext bc, int offset) => + new _FbUint8List(bc, bc.derefObject(offset)); +} + +/// The reader of unsigned 8-bit integers. +class Uint8Reader extends Reader<int> { + const Uint8Reader() : super(); + + @override + int get size => _sizeofUint8; + + @override + int read(BufferContext bc, int offset) => bc._getUint8(offset); +} + +/// The list backed by 64-bit values - Uint64 length and Float64. +class _FbFloat64List extends _FbList<double> { + _FbFloat64List(BufferContext bc, int offset) : super(bc, offset); + + @override + double operator [](int i) { + return bc._getFloat64(offset + 4 + 8 * i); + } +} + +/// The list backed by 32-bit values - Float32. +class _FbFloat32List extends _FbList<double> { + _FbFloat32List(BufferContext bc, int offset) : super(bc, offset); + + @override + double operator [](int i) { + return bc._getFloat32(offset + 4 + 4 * i); + } +} + +/// List backed by a generic object which may have any size. +class _FbGenericList<E> extends _FbList<E> { + final Reader<E> elementReader; + + List<E> _items; + + _FbGenericList(this.elementReader, BufferContext bp, int offset) + : super(bp, offset); + + @override + E operator [](int i) { + _items ??= new List<E>(length); + E item = _items[i]; + if (item == null) { + item = elementReader.read(bc, offset + 4 + elementReader.size * i); + _items[i] = item; + } + return item; + } +} + +/// The base class for immutable lists read from flat buffers. +abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> { + final BufferContext bc; + final int offset; + int _length; + + _FbList(this.bc, this.offset); + + @override + int get length { + _length ??= bc._getUint32(offset); + return _length; + } + + @override + void set length(int i) => + throw new StateError('Attempt to modify immutable list'); + + @override + void operator []=(int i, E e) => + throw new StateError('Attempt to modify immutable list'); +} + +/// List backed by 32-bit unsigned integers. +class _FbUint32List extends _FbList<int> { + _FbUint32List(BufferContext bc, int offset) : super(bc, offset); + + @override + int operator [](int i) { + return bc._getUint32(offset + 4 + 4 * i); + } +} + +/// List backed by 16-bit unsigned integers. +class _FbUint16List extends _FbList<int> { + _FbUint16List(BufferContext bc, int offset) : super(bc, offset); + + @override + int operator [](int i) { + return bc._getUint16(offset + 4 + 2 * i); + } +} + +/// List backed by 8-bit unsigned integers. +class _FbUint8List extends _FbList<int> { + _FbUint8List(BufferContext bc, int offset) : super(bc, offset); + + @override + int operator [](int i) { + return bc._getUint8(offset + 4 + i); + } +} + +/// List backed by 8-bit unsigned integers. +class _FbBoolList extends _FbList<bool> { + _FbBoolList(BufferContext bc, int offset) : super(bc, offset); + + @override + bool operator [](int i) { + return bc._getUint8(offset + 4 + i) == 1 ? true : false; + } +} + +/// Class that describes the structure of a table. +class _VTable { + static const int _metadataLength = 4; + + final List<int> fieldTails = <int>[]; + final List<int> fieldOffsets = <int>[]; + + /// The size of the table that uses this VTable. + int tableSize; + + /// The tail of this VTable. It is used to share the same VTable between + /// multiple tables of identical structure. + int tail; + + int get _vTableSize => numOfUint16 * _sizeofUint16; + + int get numOfUint16 => 1 + 1 + fieldTails.length; + + void addField(int field, int offset) { + while (fieldTails.length <= field) { + fieldTails.add(null); + } + fieldTails[field] = offset; + } + + bool _offsetsMatch(int vt2Start, ByteData buf) { + for (int i = 0; i < fieldOffsets.length; i++) { + if (fieldOffsets[i] != + buf.getUint16( + vt2Start + _metadataLength + (2 * i), Endianness.LITTLE_ENDIAN)) { + return false; + } + } + return true; + } + + /// Fill the [fieldOffsets] field. + void computeFieldOffsets(int tableTail) { + assert(fieldOffsets.isEmpty); + for (int fieldTail in fieldTails) { + int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail; + fieldOffsets.add(fieldOffset); + } + } + + /// Outputs this VTable to [buf], which is is expected to be aligned to 16-bit + /// and have at least [numOfUint16] 16-bit words available. + void output(ByteData buf, int bufOffset) { + // VTable size. + buf.setUint16(bufOffset, numOfUint16 * 2, Endianness.LITTLE_ENDIAN); + bufOffset += 2; + // Table size. + buf.setUint16(bufOffset, tableSize, Endianness.LITTLE_ENDIAN); + bufOffset += 2; + // Field offsets. + for (int fieldOffset in fieldOffsets) { + buf.setUint16(bufOffset, fieldOffset, Endianness.LITTLE_ENDIAN); + bufOffset += 2; + } + } +} diff --git a/dart/publish.sh b/dart/publish.sh new file mode 100755 index 00000000..167a4a34 --- /dev/null +++ b/dart/publish.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright 2018 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Note to pub consumers: this file is used to assist with publishing the +# pub package from the flatbuffers repository and is not meant for general use. +# As pub does not currently provide a way to exclude files, it is included here. + +command -v pub >/dev/null 2>&1 || { echo >&2 "Require `pub` but it's not installed. Aborting."; exit 1; } + +cp ../samples/monster.fbs example/ +cp ../tests/monster_test.fbs test/ +pub publish + +rm example/monster.fbs +rm test/monster_test.fbs
\ No newline at end of file diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml new file mode 100644 index 00000000..c71f831d --- /dev/null +++ b/dart/pubspec.yaml @@ -0,0 +1,18 @@ +name: flat_buffers +version: 1.9.0 +description: > + FlatBuffers reading and writing library for Dart. Use the flatc compiler to + generate Dart classes for a FlatBuffers schema, and this library to assist with + reading and writing the binary format. + + Based on original work by Konstantin Scheglov and Paul Berry of the Dart SDK team. +authors: +- Dan Field <dfield@gmail.com> +- Konstantin Scheglov +- Paul Berry +homepage: https://github.com/google/flatbuffers +documentation: https://google.github.io/flatbuffers/index.html +dev_dependencies: + test: ^0.12.33 + test_reflective_loader: ^0.1.4 + path: ^1.5.1 diff --git a/dart/test/flat_buffers_test.dart b/dart/test/flat_buffers_test.dart new file mode 100644 index 00000000..9fbbf8ae --- /dev/null +++ b/dart/test/flat_buffers_test.dart @@ -0,0 +1,573 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:typed_data'; +import 'dart:io' as io; + +import 'package:path/path.dart' as path; + +import 'package:flat_buffers/flat_buffers.dart'; +import 'package:test/test.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import './monster_test_my_game.example_generated.dart' as example; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(BuilderTest); + defineReflectiveTests(CheckOtherLangaugesData); + }); +} + +int indexToField(int index) { + return (1 + 1 + index) * 2; +} + +@reflectiveTest +class CheckOtherLangaugesData { + test_cppData() async { + List<int> data = await new io.File(path.join( + path.dirname(io.Platform.script.path), + 'monsterdata_test.mon', + )) + .readAsBytes(); + example.Monster mon = new example.Monster(data); + expect(mon.hp, 80); + expect(mon.mana, 150); + expect(mon.name, 'MyMonster'); + expect(mon.pos.x, 1.0); + expect(mon.pos.y, 2.0); + expect(mon.pos.z, 3.0); + expect(mon.pos.test1, 3.0); + expect(mon.pos.test2.value, 2.0); + expect(mon.pos.test3.a, 5); + expect(mon.pos.test3.b, 6); + expect(mon.testType.value, example.AnyTypeId.Monster.value); + expect(mon.test is example.Monster, true); + final monster2 = mon.test as example.Monster; + expect(monster2.name, "Fred"); + + expect(mon.inventory.length, 5); + expect(mon.inventory.reduce((cur, next) => cur + next), 10); + expect(mon.test4.length, 2); + expect( + mon.test4[0].a + mon.test4[0].b + mon.test4[1].a + mon.test4[1].b, 100); + expect(mon.testarrayofstring.length, 2); + expect(mon.testarrayofstring[0], "test1"); + expect(mon.testarrayofstring[1], "test2"); + + // this will fail if accessing any field fails. + expect(mon.toString(), + 'Monster{pos: Vec3{x: 1.0, y: 2.0, z: 3.0, test1: 3.0, test2: Color{value: 2}, test3: Test{a: 5, b: 6}}, mana: 150, hp: 80, name: MyMonster, inventory: [0, 1, 2, 3, 4], color: Color{value: 8}, testType: AnyTypeId{value: 1}, test: Monster{pos: null, mana: 150, hp: 100, name: Fred, inventory: null, color: Color{value: 8}, testType: null, test: null, test4: null, testarrayofstring: null, testarrayoftables: null, enemy: null, testnestedflatbuffer: null, testempty: null, testbool: null, testhashs32Fnv1: null, testhashu32Fnv1: null, testhashs64Fnv1: null, testhashu64Fnv1: null, testhashs32Fnv1a: null, testhashu32Fnv1a: null, testhashs64Fnv1a: null, testhashu64Fnv1a: null, testarrayofbools: null, testf: 3.14159, testf2: 3.0, testf3: 0.0, testarrayofstring2: null, testarrayofsortedstruct: null, flex: null, test5: null, vectorOfLongs: null, vectorOfDoubles: null, parentNamespaceTest: null, vectorOfReferrables: null, singleWeakReference: null, vectorOfWeakReferences: null, vectorOfStrongReferrables: null, coOwningReference: null, vectorOfCoOwningReferences: null, nonOwningReference: null, vectorOfNonOwningReferences: null}, test4: [Test{a: 10, b: 20}, Test{a: 30, b: 40}], testarrayofstring: [test1, test2], testarrayoftables: null, enemy: Monster{pos: null, mana: 150, hp: 100, name: Fred, inventory: null, color: Color{value: 8}, testType: null, test: null, test4: null, testarrayofstring: null, testarrayoftables: null, enemy: null, testnestedflatbuffer: null, testempty: null, testbool: null, testhashs32Fnv1: null, testhashu32Fnv1: null, testhashs64Fnv1: null, testhashu64Fnv1: null, testhashs32Fnv1a: null, testhashu32Fnv1a: null, testhashs64Fnv1a: null, testhashu64Fnv1a: null, testarrayofbools: null, testf: 3.14159, testf2: 3.0, testf3: 0.0, testarrayofstring2: null, testarrayofsortedstruct: null, flex: null, test5: null, vectorOfLongs: null, vectorOfDoubles: null, parentNamespaceTest: null, vectorOfReferrables: null, singleWeakReference: null, vectorOfWeakReferences: null, vectorOfStrongReferrables: null, coOwningReference: null, vectorOfCoOwningReferences: null, nonOwningReference: null, vectorOfNonOwningReferences: null}, testnestedflatbuffer: null, testempty: null, testbool: null, testhashs32Fnv1: -579221183, testhashu32Fnv1: 3715746113, testhashs64Fnv1: 7930699090847568257, testhashu64Fnv1: 7930699090847568257, testhashs32Fnv1a: -1904106383, testhashu32Fnv1a: 2390860913, testhashs64Fnv1a: 4898026182817603057, testhashu64Fnv1a: 4898026182817603057, testarrayofbools: [true, false, true], testf: 3.14159, testf2: 3.0, testf3: 0.0, testarrayofstring2: null, testarrayofsortedstruct: null, flex: null, test5: [Test{a: 10, b: 20}, Test{a: 30, b: 40}], vectorOfLongs: [1, 100, 10000, 1000000, 100000000], vectorOfDoubles: [-1.7976931348623157e+308, 0.0, 1.7976931348623157e+308], parentNamespaceTest: null, vectorOfReferrables: null, singleWeakReference: null, vectorOfWeakReferences: null, vectorOfStrongReferrables: null, coOwningReference: null, vectorOfCoOwningReferences: null, nonOwningReference: null, vectorOfNonOwningReferences: null}'); + } +} + +@reflectiveTest +class BuilderTest { + void test_monsterBuilder() { + final fbBuilder = new Builder(); + final str = fbBuilder.writeString('MyMonster'); + + fbBuilder.writeString('test1'); + fbBuilder.writeString('test2'); + final testArrayOfString = fbBuilder.endStructVector(2); + + final fred = fbBuilder.writeString('Fred'); + + final List<int> treasure = [0, 1, 2, 3, 4]; + final inventory = fbBuilder.writeListUint8(treasure); + + final monBuilder = new example.MonsterBuilder(fbBuilder) + ..begin() + ..addNameOffset(fred); + final mon2 = monBuilder.finish(); + + final testBuilder = new example.TestBuilder(fbBuilder); + testBuilder.finish(10, 20); + testBuilder.finish(30, 40); + final test4 = fbBuilder.endStructVector(2); + + + monBuilder + ..begin() + ..addPos( + new example.Vec3Builder(fbBuilder).finish( + 1.0, + 2.0, + 3.0, + 3.0, + example.Color.Green, + () => testBuilder.finish(5, 6), + ), + ) + ..addHp(80) + ..addNameOffset(str) + ..addInventoryOffset(inventory) + ..addTestType(example.AnyTypeId.Monster) + ..addTestOffset(mon2) + ..addTest4Offset(test4) + ..addTestarrayofstringOffset(testArrayOfString); + final mon = monBuilder.finish(); + fbBuilder.finish(mon); + } + + void test_error_addInt32_withoutStartTable() { + Builder builder = new Builder(); + expect(() { + builder.addInt32(0, 0); + }, throwsStateError); + } + + void test_error_addOffset_withoutStartTable() { + Builder builder = new Builder(); + expect(() { + builder.addOffset(0, 0); + }, throwsStateError); + } + + void test_error_endTable_withoutStartTable() { + Builder builder = new Builder(); + expect(() { + builder.endTable(); + }, throwsStateError); + } + + void test_error_startTable_duringTable() { + Builder builder = new Builder(); + builder.startTable(); + expect(() { + builder.startTable(); + }, throwsStateError); + } + + void test_error_writeString_duringTable() { + Builder builder = new Builder(); + builder.startTable(); + expect(() { + builder.writeString('12345'); + }, throwsStateError); + } + + void test_file_identifier() { + Uint8List byteList; + { + Builder builder = new Builder(initialSize: 0); + builder.startTable(); + int offset = builder.endTable(); + byteList = builder.finish(offset, 'Az~ÿ'); + } + // Convert byteList to a ByteData so that we can read data from it. + ByteData byteData = byteList.buffer.asByteData(byteList.offsetInBytes); + // First 4 bytes are an offset to the table data. + int tableDataLoc = byteData.getUint32(0, Endianness.LITTLE_ENDIAN); + // Next 4 bytes are the file identifier. + expect(byteData.getUint8(4), 65); // 'a' + expect(byteData.getUint8(5), 122); // 'z' + expect(byteData.getUint8(6), 126); // '~' + expect(byteData.getUint8(7), 255); // 'ÿ' + // First 4 bytes of the table data are a backwards offset to the vtable. + int vTableLoc = tableDataLoc - + byteData.getInt32(tableDataLoc, Endianness.LITTLE_ENDIAN); + // First 2 bytes of the vtable are the size of the vtable in bytes, which + // should be 4. + expect(byteData.getUint16(vTableLoc, Endianness.LITTLE_ENDIAN), 4); + // Next 2 bytes are the size of the object in bytes (including the vtable + // pointer), which should be 4. + expect(byteData.getUint16(vTableLoc + 2, Endianness.LITTLE_ENDIAN), 4); + } + + void test_low() { + Builder builder = new Builder(initialSize: 0); + expect((builder..putUint8(1)).lowFinish(), [1]); + expect((builder..putUint32(2)).lowFinish(), [2, 0, 0, 0, 0, 0, 0, 1]); + expect((builder..putUint8(3)).lowFinish(), + [0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 1]); + expect((builder..putUint8(4)).lowFinish(), + [0, 0, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); + expect((builder..putUint8(5)).lowFinish(), + [0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); + expect((builder..putUint32(6)).lowFinish(), + [6, 0, 0, 0, 0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); + } + + void test_table_default() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + builder.startTable(); + builder.addInt32(0, 10, 10); + builder.addInt32(1, 20, 10); + int offset = builder.endTable(); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buffer = new BufferContext.fromBytes(byteList); + int objectOffset = buffer.derefObject(0); + // was not written, so uses the new default value + expect( + const Int32Reader() + .vTableGet(buffer, objectOffset, indexToField(0), 15), + 15); + // has the written value + expect( + const Int32Reader() + .vTableGet(buffer, objectOffset, indexToField(1), 15), + 20); + } + + void test_table_format() { + Uint8List byteList; + { + Builder builder = new Builder(initialSize: 0); + builder.startTable(); + builder.addInt32(0, 10); + builder.addInt32(1, 20); + builder.addInt32(2, 30); + byteList = builder.finish(builder.endTable()); + } + // Convert byteList to a ByteData so that we can read data from it. + ByteData byteData = byteList.buffer.asByteData(byteList.offsetInBytes); + // First 4 bytes are an offset to the table data. + int tableDataLoc = byteData.getUint32(0, Endianness.LITTLE_ENDIAN); + // First 4 bytes of the table data are a backwards offset to the vtable. + int vTableLoc = tableDataLoc - + byteData.getInt32(tableDataLoc, Endianness.LITTLE_ENDIAN); + // First 2 bytes of the vtable are the size of the vtable in bytes, which + // should be 10. + expect(byteData.getUint16(vTableLoc, Endianness.LITTLE_ENDIAN), 10); + // Next 2 bytes are the size of the object in bytes (including the vtable + // pointer), which should be 16. + expect(byteData.getUint16(vTableLoc + 2, Endianness.LITTLE_ENDIAN), 16); + // Remaining 6 bytes are the offsets within the object where the ints are + // located. + for (int i = 0; i < 3; i++) { + int offset = + byteData.getUint16(vTableLoc + 4 + 2 * i, Endianness.LITTLE_ENDIAN); + expect(byteData.getInt32(tableDataLoc + offset, Endianness.LITTLE_ENDIAN), + 10 + 10 * i); + } + } + + void test_table_string() { + String latinString = 'test'; + String unicodeString = 'Проба пера'; + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int latinStringOffset = builder.writeString(latinString); + int unicodeStringOffset = builder.writeString(unicodeString); + builder.startTable(); + builder.addOffset(0, latinStringOffset); + builder.addOffset(1, unicodeStringOffset); + int offset = builder.endTable(); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + int objectOffset = buf.derefObject(0); + expect(const StringReader().vTableGet(buf, objectOffset, indexToField(0)), + latinString); + expect(const StringReader().vTableGet(buf, objectOffset, indexToField(1)), + unicodeString); + } + + void test_table_types() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int stringOffset = builder.writeString('12345'); + builder.startTable(); + builder.addBool(0, true); + builder.addInt8(1, 10); + builder.addInt32(2, 20); + builder.addOffset(3, stringOffset); + builder.addInt32(4, 40); + builder.addUint32(5, 0x9ABCDEF0); + builder.addUint8(6, 0x9A); + int offset = builder.endTable(); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + int objectOffset = buf.derefObject(0); + expect( + const BoolReader().vTableGet(buf, objectOffset, indexToField(0)), true); + expect( + const Int8Reader().vTableGet(buf, objectOffset, indexToField(1)), 10); + expect( + const Int32Reader().vTableGet(buf, objectOffset, indexToField(2)), 20); + expect(const StringReader().vTableGet(buf, objectOffset, indexToField(3)), + '12345'); + expect( + const Int32Reader().vTableGet(buf, objectOffset, indexToField(4)), 40); + expect(const Uint32Reader().vTableGet(buf, objectOffset, indexToField(5)), + 0x9ABCDEF0); + expect(const Uint8Reader().vTableGet(buf, objectOffset, indexToField(6)), + 0x9A); + } + + void test_writeList_of_Uint32() { + List<int> values = <int>[10, 100, 12345, 0x9abcdef0]; + // write + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int offset = builder.writeListUint32(values); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<int> items = const Uint32ListReader().read(buf, 0); + expect(items, hasLength(4)); + expect(items, orderedEquals(values)); + } + + void test_writeList_ofBool() { + void verifyListBooleans(int len, List<int> trueBits) { + // write + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + List<bool> values = new List<bool>.filled(len, false); + for (int bit in trueBits) { + values[bit] = true; + } + int offset = builder.writeListBool(values); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<bool> items = const BoolListReader().read(buf, 0); + expect(items, hasLength(len)); + for (int i = 0; i < items.length; i++) { + expect(items[i], trueBits.contains(i), reason: 'bit $i of $len'); + } + } + + verifyListBooleans(0, <int>[]); + verifyListBooleans(1, <int>[]); + verifyListBooleans(1, <int>[0]); + verifyListBooleans(31, <int>[0, 1]); + verifyListBooleans(31, <int>[1, 2, 24, 25, 30]); + verifyListBooleans(31, <int>[0, 30]); + verifyListBooleans(32, <int>[1, 2, 24, 25, 31]); + verifyListBooleans(33, <int>[1, 2, 24, 25, 32]); + verifyListBooleans(33, <int>[1, 2, 24, 25, 31, 32]); + verifyListBooleans(63, <int>[]); + verifyListBooleans(63, <int>[0, 1, 2, 61, 62]); + verifyListBooleans(63, new List<int>.generate(63, (i) => i)); + verifyListBooleans(64, <int>[]); + verifyListBooleans(64, <int>[0, 1, 2, 61, 62, 63]); + verifyListBooleans(64, <int>[1, 2, 62]); + verifyListBooleans(64, <int>[0, 1, 2, 63]); + verifyListBooleans(64, new List<int>.generate(64, (i) => i)); + verifyListBooleans(100, <int>[0, 3, 30, 60, 90, 99]); + } + + void test_writeList_ofInt32() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int offset = builder.writeListInt32(<int>[1, 2, 3, 4, 5]); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<int> items = const ListReader<int>(const Int32Reader()).read(buf, 0); + expect(items, hasLength(5)); + expect(items, orderedEquals(<int>[1, 2, 3, 4, 5])); + } + + void test_writeList_ofFloat64() { + List<double> values = <double>[-1.234567, 3.4E+9, -5.6E-13, 7.8, 12.13]; + // write + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int offset = builder.writeListFloat64(values); + byteList = builder.finish(offset); + } + + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<double> items = const Float64ListReader().read(buf, 0); + + expect(items, hasLength(values.length)); + for (int i = 0; i < values.length; i++) { + expect(values[i], closeTo(items[i], .001)); + } + } + + void test_writeList_ofFloat32() { + List<double> values = [1.0, 2.23, -3.213, 7.8, 12.13]; + // write + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int offset = builder.writeListFloat32(values); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<double> items = const Float32ListReader().read(buf, 0); + expect(items, hasLength(5)); + for (int i = 0; i < values.length; i++) { + expect(values[i], closeTo(items[i], .001)); + } + } + + void test_writeList_ofObjects() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + // write the object #1 + int object1; + { + builder.startTable(); + builder.addInt32(0, 10); + builder.addInt32(1, 20); + object1 = builder.endTable(); + } + // write the object #1 + int object2; + { + builder.startTable(); + builder.addInt32(0, 100); + builder.addInt32(1, 200); + object2 = builder.endTable(); + } + // write the list + int offset = builder.writeList([object1, object2]); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<TestPointImpl> items = + const ListReader<TestPointImpl>(const TestPointReader()).read(buf, 0); + expect(items, hasLength(2)); + expect(items[0].x, 10); + expect(items[0].y, 20); + expect(items[1].x, 100); + expect(items[1].y, 200); + } + + void test_writeList_ofStrings_asRoot() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int str1 = builder.writeString('12345'); + int str2 = builder.writeString('ABC'); + int offset = builder.writeList([str1, str2]); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<String> items = + const ListReader<String>(const StringReader()).read(buf, 0); + expect(items, hasLength(2)); + expect(items, contains('12345')); + expect(items, contains('ABC')); + } + + void test_writeList_ofStrings_inObject() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int listOffset = builder.writeList( + [builder.writeString('12345'), builder.writeString('ABC')]); + builder.startTable(); + builder.addOffset(0, listOffset); + int offset = builder.endTable(); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + StringListWrapperImpl reader = new StringListWrapperReader().read(buf, 0); + List<String> items = reader.items; + expect(items, hasLength(2)); + expect(items, contains('12345')); + expect(items, contains('ABC')); + } + + void test_writeList_ofUint32() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int offset = builder.writeListUint32(<int>[1, 2, 0x9ABCDEF0]); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<int> items = const Uint32ListReader().read(buf, 0); + expect(items, hasLength(3)); + expect(items, orderedEquals(<int>[1, 2, 0x9ABCDEF0])); + } + + void test_writeList_ofUint16() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int offset = builder.writeListUint16(<int>[1, 2, 60000]); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<int> items = const Uint16ListReader().read(buf, 0); + expect(items, hasLength(3)); + expect(items, orderedEquals(<int>[1, 2, 60000])); + } + + void test_writeList_ofUint8() { + List<int> byteList; + { + Builder builder = new Builder(initialSize: 0); + int offset = builder.writeListUint8(<int>[1, 2, 3, 4, 0x9A]); + byteList = builder.finish(offset); + } + // read and verify + BufferContext buf = new BufferContext.fromBytes(byteList); + List<int> items = const Uint8ListReader().read(buf, 0); + expect(items, hasLength(5)); + expect(items, orderedEquals(<int>[1, 2, 3, 4, 0x9A])); + } +} + +class StringListWrapperImpl { + final BufferContext bp; + final int offset; + + StringListWrapperImpl(this.bp, this.offset); + + List<String> get items => const ListReader<String>(const StringReader()) + .vTableGet(bp, offset, indexToField(0)); +} + +class StringListWrapperReader extends TableReader<StringListWrapperImpl> { + const StringListWrapperReader(); + + @override + StringListWrapperImpl createObject(BufferContext object, int offset) { + return new StringListWrapperImpl(object, offset); + } +} + +class TestPointImpl { + final BufferContext bp; + final int offset; + + TestPointImpl(this.bp, this.offset); + + int get x => const Int32Reader().vTableGet(bp, offset, indexToField(0), 0); + + int get y => const Int32Reader().vTableGet(bp, offset, indexToField(1), 0); +} + +class TestPointReader extends TableReader<TestPointImpl> { + const TestPointReader(); + + @override + TestPointImpl createObject(BufferContext object, int offset) { + return new TestPointImpl(object, offset); + } +} diff --git a/dart/test/monster_test_my_game.example2_generated.dart b/dart/test/monster_test_my_game.example2_generated.dart new file mode 100644 index 00000000..eed14bc2 --- /dev/null +++ b/dart/test/monster_test_my_game.example2_generated.dart @@ -0,0 +1,60 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// ignore_for_file: unused_import, unused_field, unused_local_variable + +library my_game.example2; + +import 'dart:typed_data' show Uint8List; +import 'package:flat_buffers/flat_buffers.dart' as fb; + +import './monster_test_my_game_generated.dart' as my_game; +import './monster_test_my_game.example_generated.dart' as my_game_example; + +class Monster { + Monster._(this._bc, this._bcOffset); + factory Monster(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<Monster> reader = const _MonsterReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + + @override + String toString() { + return 'Monster{}'; + } +} + +class _MonsterReader extends fb.TableReader<Monster> { + const _MonsterReader(); + + @override + Monster createObject(fb.BufferContext bc, int offset) => + new Monster._(bc, offset); +} + +class MonsterObjectBuilder extends fb.ObjectBuilder { + + MonsterObjectBuilder(); + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.startTable(); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} diff --git a/dart/test/monster_test_my_game.example_generated.dart b/dart/test/monster_test_my_game.example_generated.dart new file mode 100644 index 00000000..d85b5919 --- /dev/null +++ b/dart/test/monster_test_my_game.example_generated.dart @@ -0,0 +1,1332 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// ignore_for_file: unused_import, unused_field, unused_local_variable + +library my_game.example; + +import 'dart:typed_data' show Uint8List; +import 'package:flat_buffers/flat_buffers.dart' as fb; + +import './monster_test_my_game.example2_generated.dart' as my_game_example2; +import './monster_test_my_game_generated.dart' as my_game; + +class Color { + final int value; + const Color._(this.value); + + factory Color.fromValue(int value) { + if (value == null) return null; + if (!values.containsKey(value)) { + throw new StateError('Invalid value $value for bit flag enum Color'); + } + return values[value]; + } + + static bool containsValue(int value) => values.containsKey(value); + + static const Color Red = const Color._(1); + static const Color Green = const Color._(2); + static const Color Blue = const Color._(8); + static get values => {1: Red,2: Green,8: Blue,}; + + static const fb.Reader<Color> reader = const _ColorReader(); + + @override + String toString() { + return 'Color{value: $value}'; + } +} + +class _ColorReader extends fb.Reader<Color> { + const _ColorReader(); + + @override + int get size => 1; + + @override + Color read(fb.BufferContext bc, int offset) => + new Color.fromValue(const fb.Int8Reader().read(bc, offset)); +} + +class AnyTypeId { + final int value; + const AnyTypeId._(this.value); + + factory AnyTypeId.fromValue(int value) { + if (value == null) return null; + if (!values.containsKey(value)) { + throw new StateError('Invalid value $value for bit flag enum AnyTypeId'); + } + return values[value]; + } + + static const int minValue = 0; + static const int maxValue = 3; + static bool containsValue(int value) => values.containsKey(value); + + static const AnyTypeId NONE = const AnyTypeId._(0); + static const AnyTypeId Monster = const AnyTypeId._(1); + static const AnyTypeId TestSimpleTableWithEnum = const AnyTypeId._(2); + static const AnyTypeId MyGame_Example2_Monster = const AnyTypeId._(3); + static get values => {0: NONE,1: Monster,2: TestSimpleTableWithEnum,3: MyGame_Example2_Monster,}; + + static const fb.Reader<AnyTypeId> reader = const _AnyTypeIdReader(); + + @override + String toString() { + return 'AnyTypeId{value: $value}'; + } +} + +class _AnyTypeIdReader extends fb.Reader<AnyTypeId> { + const _AnyTypeIdReader(); + + @override + int get size => 1; + + @override + AnyTypeId read(fb.BufferContext bc, int offset) => + new AnyTypeId.fromValue(const fb.Uint8Reader().read(bc, offset)); +} + +class Test { + Test._(this._bc, this._bcOffset); + + static const fb.Reader<Test> reader = const _TestReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + int get a => const fb.Int16Reader().read(_bc, _bcOffset + 0); + int get b => const fb.Int8Reader().read(_bc, _bcOffset + 2); + + @override + String toString() { + return 'Test{a: $a, b: $b}'; + } +} + +class _TestReader extends fb.StructReader<Test> { + const _TestReader(); + + @override + int get size => 4; + + @override + Test createObject(fb.BufferContext bc, int offset) => + new Test._(bc, offset); +} + +class TestBuilder { + TestBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + int finish(int a, int b) { + fbBuilder.pad(1); + fbBuilder.putInt8(b); + fbBuilder.putInt16(a); + return fbBuilder.offset; + } + +} + +class TestObjectBuilder extends fb.ObjectBuilder { + final int _a; + final int _b; + + TestObjectBuilder({ + int a, + int b, + }) + : _a = a, + _b = b; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.pad(1); + fbBuilder.putInt8(_b); + fbBuilder.putInt16(_a); + return fbBuilder.offset; + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class TestSimpleTableWithEnum { + TestSimpleTableWithEnum._(this._bc, this._bcOffset); + factory TestSimpleTableWithEnum(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<TestSimpleTableWithEnum> reader = const _TestSimpleTableWithEnumReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + Color get color => new Color.fromValue(const fb.Int8Reader().vTableGet(_bc, _bcOffset, 4, 2)); + + @override + String toString() { + return 'TestSimpleTableWithEnum{color: $color}'; + } +} + +class _TestSimpleTableWithEnumReader extends fb.TableReader<TestSimpleTableWithEnum> { + const _TestSimpleTableWithEnumReader(); + + @override + TestSimpleTableWithEnum createObject(fb.BufferContext bc, int offset) => + new TestSimpleTableWithEnum._(bc, offset); +} + +class TestSimpleTableWithEnumBuilder { + TestSimpleTableWithEnumBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(); + } + + int addColor(Color color) { + fbBuilder.addInt8(0, color?.value); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class TestSimpleTableWithEnumObjectBuilder extends fb.ObjectBuilder { + final Color _color; + + TestSimpleTableWithEnumObjectBuilder({ + Color color, + }) + : _color = color; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.startTable(); + fbBuilder.addInt8(0, _color?.value); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class Vec3 { + Vec3._(this._bc, this._bcOffset); + + static const fb.Reader<Vec3> reader = const _Vec3Reader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + double get x => const fb.Float32Reader().read(_bc, _bcOffset + 0); + double get y => const fb.Float32Reader().read(_bc, _bcOffset + 4); + double get z => const fb.Float32Reader().read(_bc, _bcOffset + 8); + double get test1 => const fb.Float64Reader().read(_bc, _bcOffset + 16); + Color get test2 => new Color.fromValue(const fb.Int8Reader().read(_bc, _bcOffset + 24)); + Test get test3 => Test.reader.read(_bc, _bcOffset + 26); + + @override + String toString() { + return 'Vec3{x: $x, y: $y, z: $z, test1: $test1, test2: $test2, test3: $test3}'; + } +} + +class _Vec3Reader extends fb.StructReader<Vec3> { + const _Vec3Reader(); + + @override + int get size => 32; + + @override + Vec3 createObject(fb.BufferContext bc, int offset) => + new Vec3._(bc, offset); +} + +class Vec3Builder { + Vec3Builder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + int finish(double x, double y, double z, double test1, Color test2, fb.StructBuilder test3) { + fbBuilder.pad(2); + test3(); + fbBuilder.pad(1); + fbBuilder.putInt8(test2?.value); + fbBuilder.putFloat64(test1); + fbBuilder.pad(4); + fbBuilder.putFloat32(z); + fbBuilder.putFloat32(y); + fbBuilder.putFloat32(x); + return fbBuilder.offset; + } + +} + +class Vec3ObjectBuilder extends fb.ObjectBuilder { + final double _x; + final double _y; + final double _z; + final double _test1; + final Color _test2; + final TestObjectBuilder _test3; + + Vec3ObjectBuilder({ + double x, + double y, + double z, + double test1, + Color test2, + TestObjectBuilder test3, + }) + : _x = x, + _y = y, + _z = z, + _test1 = test1, + _test2 = test2, + _test3 = test3; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.pad(2); + _test3.finish(fbBuilder); + fbBuilder.pad(1); + fbBuilder.putInt8(_test2?.value); + fbBuilder.putFloat64(_test1); + fbBuilder.pad(4); + fbBuilder.putFloat32(_z); + fbBuilder.putFloat32(_y); + fbBuilder.putFloat32(_x); + return fbBuilder.offset; + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class Ability { + Ability._(this._bc, this._bcOffset); + + static const fb.Reader<Ability> reader = const _AbilityReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + int get id => const fb.Uint32Reader().read(_bc, _bcOffset + 0); + int get distance => const fb.Uint32Reader().read(_bc, _bcOffset + 4); + + @override + String toString() { + return 'Ability{id: $id, distance: $distance}'; + } +} + +class _AbilityReader extends fb.StructReader<Ability> { + const _AbilityReader(); + + @override + int get size => 8; + + @override + Ability createObject(fb.BufferContext bc, int offset) => + new Ability._(bc, offset); +} + +class AbilityBuilder { + AbilityBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + int finish(int id, int distance) { + fbBuilder.putUint32(distance); + fbBuilder.putUint32(id); + return fbBuilder.offset; + } + +} + +class AbilityObjectBuilder extends fb.ObjectBuilder { + final int _id; + final int _distance; + + AbilityObjectBuilder({ + int id, + int distance, + }) + : _id = id, + _distance = distance; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.putUint32(_distance); + fbBuilder.putUint32(_id); + return fbBuilder.offset; + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class Stat { + Stat._(this._bc, this._bcOffset); + factory Stat(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<Stat> reader = const _StatReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + String get id => const fb.StringReader().vTableGet(_bc, _bcOffset, 4, null); + int get val => const fb.Int64Reader().vTableGet(_bc, _bcOffset, 6, null); + int get count => const fb.Uint16Reader().vTableGet(_bc, _bcOffset, 8, null); + + @override + String toString() { + return 'Stat{id: $id, val: $val, count: $count}'; + } +} + +class _StatReader extends fb.TableReader<Stat> { + const _StatReader(); + + @override + Stat createObject(fb.BufferContext bc, int offset) => + new Stat._(bc, offset); +} + +class StatBuilder { + StatBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(); + } + + int addIdOffset(int offset) { + fbBuilder.addOffset(0, offset); + return fbBuilder.offset; + } + int addVal(int val) { + fbBuilder.addInt64(1, val); + return fbBuilder.offset; + } + int addCount(int count) { + fbBuilder.addUint16(2, count); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class StatObjectBuilder extends fb.ObjectBuilder { + final String _id; + final int _val; + final int _count; + + StatObjectBuilder({ + String id, + int val, + int count, + }) + : _id = id, + _val = val, + _count = count; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + final int idOffset = fbBuilder.writeString(_id); + + fbBuilder.startTable(); + if (idOffset != null) { + fbBuilder.addOffset(0, idOffset); + } + fbBuilder.addInt64(1, _val); + fbBuilder.addUint16(2, _count); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class Referrable { + Referrable._(this._bc, this._bcOffset); + factory Referrable(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<Referrable> reader = const _ReferrableReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + int get id => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 4, null); + + @override + String toString() { + return 'Referrable{id: $id}'; + } +} + +class _ReferrableReader extends fb.TableReader<Referrable> { + const _ReferrableReader(); + + @override + Referrable createObject(fb.BufferContext bc, int offset) => + new Referrable._(bc, offset); +} + +class ReferrableBuilder { + ReferrableBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(); + } + + int addId(int id) { + fbBuilder.addUint64(0, id); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class ReferrableObjectBuilder extends fb.ObjectBuilder { + final int _id; + + ReferrableObjectBuilder({ + int id, + }) + : _id = id; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.startTable(); + fbBuilder.addUint64(0, _id); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +/// an example documentation comment: monster object +class Monster { + Monster._(this._bc, this._bcOffset); + factory Monster(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<Monster> reader = const _MonsterReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + Vec3 get pos => Vec3.reader.vTableGet(_bc, _bcOffset, 4, null); + int get mana => const fb.Int16Reader().vTableGet(_bc, _bcOffset, 6, 150); + int get hp => const fb.Int16Reader().vTableGet(_bc, _bcOffset, 8, 100); + String get name => const fb.StringReader().vTableGet(_bc, _bcOffset, 10, null); + List<int> get inventory => const fb.ListReader<int>(const fb.Uint8Reader()).vTableGet(_bc, _bcOffset, 14, null); + Color get color => new Color.fromValue(const fb.Int8Reader().vTableGet(_bc, _bcOffset, 16, 8)); + AnyTypeId get testType => new AnyTypeId.fromValue(const fb.Uint8Reader().vTableGet(_bc, _bcOffset, 18, null)); + dynamic get test { + switch (testType?.value) { + case 1: return Monster.reader.vTableGet(_bc, _bcOffset, 20, null); + case 2: return TestSimpleTableWithEnum.reader.vTableGet(_bc, _bcOffset, 20, null); + case 3: return my_game_example2.Monster.reader.vTableGet(_bc, _bcOffset, 20, null); + default: return null; + } + } + List<Test> get test4 => const fb.ListReader<Test>(Test.reader).vTableGet(_bc, _bcOffset, 22, null); + List<String> get testarrayofstring => const fb.ListReader<String>(const fb.StringReader()).vTableGet(_bc, _bcOffset, 24, null); +/// an example documentation comment: this will end up in the generated code +/// multiline too + List<Monster> get testarrayoftables => const fb.ListReader<Monster>(Monster.reader).vTableGet(_bc, _bcOffset, 26, null); + Monster get enemy => Monster.reader.vTableGet(_bc, _bcOffset, 28, null); + List<int> get testnestedflatbuffer => const fb.ListReader<int>(const fb.Uint8Reader()).vTableGet(_bc, _bcOffset, 30, null); + Stat get testempty => Stat.reader.vTableGet(_bc, _bcOffset, 32, null); + bool get testbool => const fb.BoolReader().vTableGet(_bc, _bcOffset, 34, null); + int get testhashs32Fnv1 => const fb.Int32Reader().vTableGet(_bc, _bcOffset, 36, null); + int get testhashu32Fnv1 => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 38, null); + int get testhashs64Fnv1 => const fb.Int64Reader().vTableGet(_bc, _bcOffset, 40, null); + int get testhashu64Fnv1 => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 42, null); + int get testhashs32Fnv1a => const fb.Int32Reader().vTableGet(_bc, _bcOffset, 44, null); + int get testhashu32Fnv1a => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 46, null); + int get testhashs64Fnv1a => const fb.Int64Reader().vTableGet(_bc, _bcOffset, 48, null); + int get testhashu64Fnv1a => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 50, null); + List<bool> get testarrayofbools => const fb.ListReader<bool>(const fb.BoolReader()).vTableGet(_bc, _bcOffset, 52, null); + double get testf => const fb.Float32Reader().vTableGet(_bc, _bcOffset, 54, 3.14159); + double get testf2 => const fb.Float32Reader().vTableGet(_bc, _bcOffset, 56, 3.0); + double get testf3 => const fb.Float32Reader().vTableGet(_bc, _bcOffset, 58, 0.0); + List<String> get testarrayofstring2 => const fb.ListReader<String>(const fb.StringReader()).vTableGet(_bc, _bcOffset, 60, null); + List<Ability> get testarrayofsortedstruct => const fb.ListReader<Ability>(Ability.reader).vTableGet(_bc, _bcOffset, 62, null); + List<int> get flex => const fb.ListReader<int>(const fb.Uint8Reader()).vTableGet(_bc, _bcOffset, 64, null); + List<Test> get test5 => const fb.ListReader<Test>(Test.reader).vTableGet(_bc, _bcOffset, 66, null); + List<int> get vectorOfLongs => const fb.ListReader<int>(const fb.Int64Reader()).vTableGet(_bc, _bcOffset, 68, null); + List<double> get vectorOfDoubles => const fb.ListReader<double>(const fb.Float64Reader()).vTableGet(_bc, _bcOffset, 70, null); + my_game.InParentNamespace get parentNamespaceTest => my_game.InParentNamespace.reader.vTableGet(_bc, _bcOffset, 72, null); + List<Referrable> get vectorOfReferrables => const fb.ListReader<Referrable>(Referrable.reader).vTableGet(_bc, _bcOffset, 74, null); + int get singleWeakReference => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 76, null); + List<int> get vectorOfWeakReferences => const fb.ListReader<int>(const fb.Uint64Reader()).vTableGet(_bc, _bcOffset, 78, null); + List<Referrable> get vectorOfStrongReferrables => const fb.ListReader<Referrable>(Referrable.reader).vTableGet(_bc, _bcOffset, 80, null); + int get coOwningReference => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 82, null); + List<int> get vectorOfCoOwningReferences => const fb.ListReader<int>(const fb.Uint64Reader()).vTableGet(_bc, _bcOffset, 84, null); + int get nonOwningReference => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 86, null); + List<int> get vectorOfNonOwningReferences => const fb.ListReader<int>(const fb.Uint64Reader()).vTableGet(_bc, _bcOffset, 88, null); + + @override + String toString() { + return 'Monster{pos: $pos, mana: $mana, hp: $hp, name: $name, inventory: $inventory, color: $color, testType: $testType, test: $test, test4: $test4, testarrayofstring: $testarrayofstring, testarrayoftables: $testarrayoftables, enemy: $enemy, testnestedflatbuffer: $testnestedflatbuffer, testempty: $testempty, testbool: $testbool, testhashs32Fnv1: $testhashs32Fnv1, testhashu32Fnv1: $testhashu32Fnv1, testhashs64Fnv1: $testhashs64Fnv1, testhashu64Fnv1: $testhashu64Fnv1, testhashs32Fnv1a: $testhashs32Fnv1a, testhashu32Fnv1a: $testhashu32Fnv1a, testhashs64Fnv1a: $testhashs64Fnv1a, testhashu64Fnv1a: $testhashu64Fnv1a, testarrayofbools: $testarrayofbools, testf: $testf, testf2: $testf2, testf3: $testf3, testarrayofstring2: $testarrayofstring2, testarrayofsortedstruct: $testarrayofsortedstruct, flex: $flex, test5: $test5, vectorOfLongs: $vectorOfLongs, vectorOfDoubles: $vectorOfDoubles, parentNamespaceTest: $parentNamespaceTest, vectorOfReferrables: $vectorOfReferrables, singleWeakReference: $singleWeakReference, vectorOfWeakReferences: $vectorOfWeakReferences, vectorOfStrongReferrables: $vectorOfStrongReferrables, coOwningReference: $coOwningReference, vectorOfCoOwningReferences: $vectorOfCoOwningReferences, nonOwningReference: $nonOwningReference, vectorOfNonOwningReferences: $vectorOfNonOwningReferences}'; + } +} + +class _MonsterReader extends fb.TableReader<Monster> { + const _MonsterReader(); + + @override + Monster createObject(fb.BufferContext bc, int offset) => + new Monster._(bc, offset); +} + +class MonsterBuilder { + MonsterBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(); + } + + int addPos(int offset) { + fbBuilder.addStruct(0, offset); + return fbBuilder.offset; + } + int addMana(int mana) { + fbBuilder.addInt16(1, mana); + return fbBuilder.offset; + } + int addHp(int hp) { + fbBuilder.addInt16(2, hp); + return fbBuilder.offset; + } + int addNameOffset(int offset) { + fbBuilder.addOffset(3, offset); + return fbBuilder.offset; + } + int addInventoryOffset(int offset) { + fbBuilder.addOffset(5, offset); + return fbBuilder.offset; + } + int addColor(Color color) { + fbBuilder.addInt8(6, color?.value); + return fbBuilder.offset; + } + int addTestType(AnyTypeId testType) { + fbBuilder.addUint8(7, testType?.value); + return fbBuilder.offset; + } + int addTestOffset(int offset) { + fbBuilder.addOffset(8, offset); + return fbBuilder.offset; + } + int addTest4Offset(int offset) { + fbBuilder.addOffset(9, offset); + return fbBuilder.offset; + } + int addTestarrayofstringOffset(int offset) { + fbBuilder.addOffset(10, offset); + return fbBuilder.offset; + } + int addTestarrayoftablesOffset(int offset) { + fbBuilder.addOffset(11, offset); + return fbBuilder.offset; + } + int addEnemyOffset(int offset) { + fbBuilder.addOffset(12, offset); + return fbBuilder.offset; + } + int addTestnestedflatbufferOffset(int offset) { + fbBuilder.addOffset(13, offset); + return fbBuilder.offset; + } + int addTestemptyOffset(int offset) { + fbBuilder.addOffset(14, offset); + return fbBuilder.offset; + } + int addTestbool(bool testbool) { + fbBuilder.addBool(15, testbool); + return fbBuilder.offset; + } + int addTesthashs32Fnv1(int testhashs32Fnv1) { + fbBuilder.addInt32(16, testhashs32Fnv1); + return fbBuilder.offset; + } + int addTesthashu32Fnv1(int testhashu32Fnv1) { + fbBuilder.addUint32(17, testhashu32Fnv1); + return fbBuilder.offset; + } + int addTesthashs64Fnv1(int testhashs64Fnv1) { + fbBuilder.addInt64(18, testhashs64Fnv1); + return fbBuilder.offset; + } + int addTesthashu64Fnv1(int testhashu64Fnv1) { + fbBuilder.addUint64(19, testhashu64Fnv1); + return fbBuilder.offset; + } + int addTesthashs32Fnv1a(int testhashs32Fnv1a) { + fbBuilder.addInt32(20, testhashs32Fnv1a); + return fbBuilder.offset; + } + int addTesthashu32Fnv1a(int testhashu32Fnv1a) { + fbBuilder.addUint32(21, testhashu32Fnv1a); + return fbBuilder.offset; + } + int addTesthashs64Fnv1a(int testhashs64Fnv1a) { + fbBuilder.addInt64(22, testhashs64Fnv1a); + return fbBuilder.offset; + } + int addTesthashu64Fnv1a(int testhashu64Fnv1a) { + fbBuilder.addUint64(23, testhashu64Fnv1a); + return fbBuilder.offset; + } + int addTestarrayofboolsOffset(int offset) { + fbBuilder.addOffset(24, offset); + return fbBuilder.offset; + } + int addTestf(double testf) { + fbBuilder.addFloat32(25, testf); + return fbBuilder.offset; + } + int addTestf2(double testf2) { + fbBuilder.addFloat32(26, testf2); + return fbBuilder.offset; + } + int addTestf3(double testf3) { + fbBuilder.addFloat32(27, testf3); + return fbBuilder.offset; + } + int addTestarrayofstring2Offset(int offset) { + fbBuilder.addOffset(28, offset); + return fbBuilder.offset; + } + int addTestarrayofsortedstructOffset(int offset) { + fbBuilder.addOffset(29, offset); + return fbBuilder.offset; + } + int addFlexOffset(int offset) { + fbBuilder.addOffset(30, offset); + return fbBuilder.offset; + } + int addTest5Offset(int offset) { + fbBuilder.addOffset(31, offset); + return fbBuilder.offset; + } + int addVectorOfLongsOffset(int offset) { + fbBuilder.addOffset(32, offset); + return fbBuilder.offset; + } + int addVectorOfDoublesOffset(int offset) { + fbBuilder.addOffset(33, offset); + return fbBuilder.offset; + } + int addParentNamespaceTestOffset(int offset) { + fbBuilder.addOffset(34, offset); + return fbBuilder.offset; + } + int addVectorOfReferrablesOffset(int offset) { + fbBuilder.addOffset(35, offset); + return fbBuilder.offset; + } + int addSingleWeakReference(int singleWeakReference) { + fbBuilder.addUint64(36, singleWeakReference); + return fbBuilder.offset; + } + int addVectorOfWeakReferencesOffset(int offset) { + fbBuilder.addOffset(37, offset); + return fbBuilder.offset; + } + int addVectorOfStrongReferrablesOffset(int offset) { + fbBuilder.addOffset(38, offset); + return fbBuilder.offset; + } + int addCoOwningReference(int coOwningReference) { + fbBuilder.addUint64(39, coOwningReference); + return fbBuilder.offset; + } + int addVectorOfCoOwningReferencesOffset(int offset) { + fbBuilder.addOffset(40, offset); + return fbBuilder.offset; + } + int addNonOwningReference(int nonOwningReference) { + fbBuilder.addUint64(41, nonOwningReference); + return fbBuilder.offset; + } + int addVectorOfNonOwningReferencesOffset(int offset) { + fbBuilder.addOffset(42, offset); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class MonsterObjectBuilder extends fb.ObjectBuilder { + final Vec3ObjectBuilder _pos; + final int _mana; + final int _hp; + final String _name; + final List<int> _inventory; + final Color _color; + final AnyTypeId _testType; + final dynamic _test; + final List<TestObjectBuilder> _test4; + final List<String> _testarrayofstring; + final List<MonsterObjectBuilder> _testarrayoftables; + final MonsterObjectBuilder _enemy; + final List<int> _testnestedflatbuffer; + final StatObjectBuilder _testempty; + final bool _testbool; + final int _testhashs32Fnv1; + final int _testhashu32Fnv1; + final int _testhashs64Fnv1; + final int _testhashu64Fnv1; + final int _testhashs32Fnv1a; + final int _testhashu32Fnv1a; + final int _testhashs64Fnv1a; + final int _testhashu64Fnv1a; + final List<bool> _testarrayofbools; + final double _testf; + final double _testf2; + final double _testf3; + final List<String> _testarrayofstring2; + final List<AbilityObjectBuilder> _testarrayofsortedstruct; + final List<int> _flex; + final List<TestObjectBuilder> _test5; + final List<int> _vectorOfLongs; + final List<double> _vectorOfDoubles; + final my_game.InParentNamespaceObjectBuilder _parentNamespaceTest; + final List<ReferrableObjectBuilder> _vectorOfReferrables; + final int _singleWeakReference; + final List<int> _vectorOfWeakReferences; + final List<ReferrableObjectBuilder> _vectorOfStrongReferrables; + final int _coOwningReference; + final List<int> _vectorOfCoOwningReferences; + final int _nonOwningReference; + final List<int> _vectorOfNonOwningReferences; + + MonsterObjectBuilder({ + Vec3ObjectBuilder pos, + int mana, + int hp, + String name, + List<int> inventory, + Color color, + AnyTypeId testType, + dynamic test, + List<TestObjectBuilder> test4, + List<String> testarrayofstring, + List<MonsterObjectBuilder> testarrayoftables, + MonsterObjectBuilder enemy, + List<int> testnestedflatbuffer, + StatObjectBuilder testempty, + bool testbool, + int testhashs32Fnv1, + int testhashu32Fnv1, + int testhashs64Fnv1, + int testhashu64Fnv1, + int testhashs32Fnv1a, + int testhashu32Fnv1a, + int testhashs64Fnv1a, + int testhashu64Fnv1a, + List<bool> testarrayofbools, + double testf, + double testf2, + double testf3, + List<String> testarrayofstring2, + List<AbilityObjectBuilder> testarrayofsortedstruct, + List<int> flex, + List<TestObjectBuilder> test5, + List<int> vectorOfLongs, + List<double> vectorOfDoubles, + my_game.InParentNamespaceObjectBuilder parentNamespaceTest, + List<ReferrableObjectBuilder> vectorOfReferrables, + int singleWeakReference, + List<int> vectorOfWeakReferences, + List<ReferrableObjectBuilder> vectorOfStrongReferrables, + int coOwningReference, + List<int> vectorOfCoOwningReferences, + int nonOwningReference, + List<int> vectorOfNonOwningReferences, + }) + : _pos = pos, + _mana = mana, + _hp = hp, + _name = name, + _inventory = inventory, + _color = color, + _testType = testType, + _test = test, + _test4 = test4, + _testarrayofstring = testarrayofstring, + _testarrayoftables = testarrayoftables, + _enemy = enemy, + _testnestedflatbuffer = testnestedflatbuffer, + _testempty = testempty, + _testbool = testbool, + _testhashs32Fnv1 = testhashs32Fnv1, + _testhashu32Fnv1 = testhashu32Fnv1, + _testhashs64Fnv1 = testhashs64Fnv1, + _testhashu64Fnv1 = testhashu64Fnv1, + _testhashs32Fnv1a = testhashs32Fnv1a, + _testhashu32Fnv1a = testhashu32Fnv1a, + _testhashs64Fnv1a = testhashs64Fnv1a, + _testhashu64Fnv1a = testhashu64Fnv1a, + _testarrayofbools = testarrayofbools, + _testf = testf, + _testf2 = testf2, + _testf3 = testf3, + _testarrayofstring2 = testarrayofstring2, + _testarrayofsortedstruct = testarrayofsortedstruct, + _flex = flex, + _test5 = test5, + _vectorOfLongs = vectorOfLongs, + _vectorOfDoubles = vectorOfDoubles, + _parentNamespaceTest = parentNamespaceTest, + _vectorOfReferrables = vectorOfReferrables, + _singleWeakReference = singleWeakReference, + _vectorOfWeakReferences = vectorOfWeakReferences, + _vectorOfStrongReferrables = vectorOfStrongReferrables, + _coOwningReference = coOwningReference, + _vectorOfCoOwningReferences = vectorOfCoOwningReferences, + _nonOwningReference = nonOwningReference, + _vectorOfNonOwningReferences = vectorOfNonOwningReferences; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + final int nameOffset = fbBuilder.writeString(_name); + final int inventoryOffset = _inventory?.isNotEmpty == true + ? fbBuilder.writeListUint8(_inventory) + : null; + final int testOffset = _test?.getOrCreateOffset(fbBuilder); + final int test4Offset = _test4?.isNotEmpty == true + ? fbBuilder.writeListOfStructs(_test4) + : null; + final int testarrayofstringOffset = _testarrayofstring?.isNotEmpty == true + ? fbBuilder.writeList(_testarrayofstring.map((b) => fbBuilder.writeString(b)).toList()) + : null; + final int testarrayoftablesOffset = _testarrayoftables?.isNotEmpty == true + ? fbBuilder.writeList(_testarrayoftables.map((b) => b.getOrCreateOffset(fbBuilder)).toList()) + : null; + final int enemyOffset = _enemy?.getOrCreateOffset(fbBuilder); + final int testnestedflatbufferOffset = _testnestedflatbuffer?.isNotEmpty == true + ? fbBuilder.writeListUint8(_testnestedflatbuffer) + : null; + final int testemptyOffset = _testempty?.getOrCreateOffset(fbBuilder); + final int testarrayofboolsOffset = _testarrayofbools?.isNotEmpty == true + ? fbBuilder.writeListBool(_testarrayofbools) + : null; + final int testarrayofstring2Offset = _testarrayofstring2?.isNotEmpty == true + ? fbBuilder.writeList(_testarrayofstring2.map((b) => fbBuilder.writeString(b)).toList()) + : null; + final int testarrayofsortedstructOffset = _testarrayofsortedstruct?.isNotEmpty == true + ? fbBuilder.writeListOfStructs(_testarrayofsortedstruct) + : null; + final int flexOffset = _flex?.isNotEmpty == true + ? fbBuilder.writeListUint8(_flex) + : null; + final int test5Offset = _test5?.isNotEmpty == true + ? fbBuilder.writeListOfStructs(_test5) + : null; + final int vectorOfLongsOffset = _vectorOfLongs?.isNotEmpty == true + ? fbBuilder.writeListInt64(_vectorOfLongs) + : null; + final int vectorOfDoublesOffset = _vectorOfDoubles?.isNotEmpty == true + ? fbBuilder.writeListFloat64(_vectorOfDoubles) + : null; + final int parentNamespaceTestOffset = _parentNamespaceTest?.getOrCreateOffset(fbBuilder); + final int vectorOfReferrablesOffset = _vectorOfReferrables?.isNotEmpty == true + ? fbBuilder.writeList(_vectorOfReferrables.map((b) => b.getOrCreateOffset(fbBuilder)).toList()) + : null; + final int vectorOfWeakReferencesOffset = _vectorOfWeakReferences?.isNotEmpty == true + ? fbBuilder.writeListUint64(_vectorOfWeakReferences) + : null; + final int vectorOfStrongReferrablesOffset = _vectorOfStrongReferrables?.isNotEmpty == true + ? fbBuilder.writeList(_vectorOfStrongReferrables.map((b) => b.getOrCreateOffset(fbBuilder)).toList()) + : null; + final int vectorOfCoOwningReferencesOffset = _vectorOfCoOwningReferences?.isNotEmpty == true + ? fbBuilder.writeListUint64(_vectorOfCoOwningReferences) + : null; + final int vectorOfNonOwningReferencesOffset = _vectorOfNonOwningReferences?.isNotEmpty == true + ? fbBuilder.writeListUint64(_vectorOfNonOwningReferences) + : null; + + fbBuilder.startTable(); + if (_pos != null) { + fbBuilder.addStruct(0, _pos.finish(fbBuilder)); + } + fbBuilder.addInt16(1, _mana); + fbBuilder.addInt16(2, _hp); + if (nameOffset != null) { + fbBuilder.addOffset(3, nameOffset); + } + if (inventoryOffset != null) { + fbBuilder.addOffset(5, inventoryOffset); + } + fbBuilder.addInt8(6, _color?.value); + fbBuilder.addUint8(7, _testType?.value); + if (testOffset != null) { + fbBuilder.addOffset(8, testOffset); + } + if (test4Offset != null) { + fbBuilder.addOffset(9, test4Offset); + } + if (testarrayofstringOffset != null) { + fbBuilder.addOffset(10, testarrayofstringOffset); + } + if (testarrayoftablesOffset != null) { + fbBuilder.addOffset(11, testarrayoftablesOffset); + } + if (enemyOffset != null) { + fbBuilder.addOffset(12, enemyOffset); + } + if (testnestedflatbufferOffset != null) { + fbBuilder.addOffset(13, testnestedflatbufferOffset); + } + if (testemptyOffset != null) { + fbBuilder.addOffset(14, testemptyOffset); + } + fbBuilder.addBool(15, _testbool); + fbBuilder.addInt32(16, _testhashs32Fnv1); + fbBuilder.addUint32(17, _testhashu32Fnv1); + fbBuilder.addInt64(18, _testhashs64Fnv1); + fbBuilder.addUint64(19, _testhashu64Fnv1); + fbBuilder.addInt32(20, _testhashs32Fnv1a); + fbBuilder.addUint32(21, _testhashu32Fnv1a); + fbBuilder.addInt64(22, _testhashs64Fnv1a); + fbBuilder.addUint64(23, _testhashu64Fnv1a); + if (testarrayofboolsOffset != null) { + fbBuilder.addOffset(24, testarrayofboolsOffset); + } + fbBuilder.addFloat32(25, _testf); + fbBuilder.addFloat32(26, _testf2); + fbBuilder.addFloat32(27, _testf3); + if (testarrayofstring2Offset != null) { + fbBuilder.addOffset(28, testarrayofstring2Offset); + } + if (testarrayofsortedstructOffset != null) { + fbBuilder.addOffset(29, testarrayofsortedstructOffset); + } + if (flexOffset != null) { + fbBuilder.addOffset(30, flexOffset); + } + if (test5Offset != null) { + fbBuilder.addOffset(31, test5Offset); + } + if (vectorOfLongsOffset != null) { + fbBuilder.addOffset(32, vectorOfLongsOffset); + } + if (vectorOfDoublesOffset != null) { + fbBuilder.addOffset(33, vectorOfDoublesOffset); + } + if (parentNamespaceTestOffset != null) { + fbBuilder.addOffset(34, parentNamespaceTestOffset); + } + if (vectorOfReferrablesOffset != null) { + fbBuilder.addOffset(35, vectorOfReferrablesOffset); + } + fbBuilder.addUint64(36, _singleWeakReference); + if (vectorOfWeakReferencesOffset != null) { + fbBuilder.addOffset(37, vectorOfWeakReferencesOffset); + } + if (vectorOfStrongReferrablesOffset != null) { + fbBuilder.addOffset(38, vectorOfStrongReferrablesOffset); + } + fbBuilder.addUint64(39, _coOwningReference); + if (vectorOfCoOwningReferencesOffset != null) { + fbBuilder.addOffset(40, vectorOfCoOwningReferencesOffset); + } + fbBuilder.addUint64(41, _nonOwningReference); + if (vectorOfNonOwningReferencesOffset != null) { + fbBuilder.addOffset(42, vectorOfNonOwningReferencesOffset); + } + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} +class TypeAliases { + TypeAliases._(this._bc, this._bcOffset); + factory TypeAliases(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<TypeAliases> reader = const _TypeAliasesReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + int get i8 => const fb.Int8Reader().vTableGet(_bc, _bcOffset, 4, null); + int get u8 => const fb.Uint8Reader().vTableGet(_bc, _bcOffset, 6, null); + int get i16 => const fb.Int16Reader().vTableGet(_bc, _bcOffset, 8, null); + int get u16 => const fb.Uint16Reader().vTableGet(_bc, _bcOffset, 10, null); + int get i32 => const fb.Int32Reader().vTableGet(_bc, _bcOffset, 12, null); + int get u32 => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 14, null); + int get i64 => const fb.Int64Reader().vTableGet(_bc, _bcOffset, 16, null); + int get u64 => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 18, null); + double get f32 => const fb.Float32Reader().vTableGet(_bc, _bcOffset, 20, 0.0); + double get f64 => const fb.Float64Reader().vTableGet(_bc, _bcOffset, 22, 0.0); + List<int> get v8 => const fb.ListReader<int>(const fb.Int8Reader()).vTableGet(_bc, _bcOffset, 24, null); + List<double> get vf64 => const fb.ListReader<double>(const fb.Float64Reader()).vTableGet(_bc, _bcOffset, 26, null); + + @override + String toString() { + return 'TypeAliases{i8: $i8, u8: $u8, i16: $i16, u16: $u16, i32: $i32, u32: $u32, i64: $i64, u64: $u64, f32: $f32, f64: $f64, v8: $v8, vf64: $vf64}'; + } +} + +class _TypeAliasesReader extends fb.TableReader<TypeAliases> { + const _TypeAliasesReader(); + + @override + TypeAliases createObject(fb.BufferContext bc, int offset) => + new TypeAliases._(bc, offset); +} + +class TypeAliasesBuilder { + TypeAliasesBuilder(this.fbBuilder) { + assert(fbBuilder != null); + } + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(); + } + + int addI8(int i8) { + fbBuilder.addInt8(0, i8); + return fbBuilder.offset; + } + int addU8(int u8) { + fbBuilder.addUint8(1, u8); + return fbBuilder.offset; + } + int addI16(int i16) { + fbBuilder.addInt16(2, i16); + return fbBuilder.offset; + } + int addU16(int u16) { + fbBuilder.addUint16(3, u16); + return fbBuilder.offset; + } + int addI32(int i32) { + fbBuilder.addInt32(4, i32); + return fbBuilder.offset; + } + int addU32(int u32) { + fbBuilder.addUint32(5, u32); + return fbBuilder.offset; + } + int addI64(int i64) { + fbBuilder.addInt64(6, i64); + return fbBuilder.offset; + } + int addU64(int u64) { + fbBuilder.addUint64(7, u64); + return fbBuilder.offset; + } + int addF32(double f32) { + fbBuilder.addFloat32(8, f32); + return fbBuilder.offset; + } + int addF64(double f64) { + fbBuilder.addFloat64(9, f64); + return fbBuilder.offset; + } + int addV8Offset(int offset) { + fbBuilder.addOffset(10, offset); + return fbBuilder.offset; + } + int addVf64Offset(int offset) { + fbBuilder.addOffset(11, offset); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class TypeAliasesObjectBuilder extends fb.ObjectBuilder { + final int _i8; + final int _u8; + final int _i16; + final int _u16; + final int _i32; + final int _u32; + final int _i64; + final int _u64; + final double _f32; + final double _f64; + final List<int> _v8; + final List<double> _vf64; + + TypeAliasesObjectBuilder({ + int i8, + int u8, + int i16, + int u16, + int i32, + int u32, + int i64, + int u64, + double f32, + double f64, + List<int> v8, + List<double> vf64, + }) + : _i8 = i8, + _u8 = u8, + _i16 = i16, + _u16 = u16, + _i32 = i32, + _u32 = u32, + _i64 = i64, + _u64 = u64, + _f32 = f32, + _f64 = f64, + _v8 = v8, + _vf64 = vf64; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + final int v8Offset = _v8?.isNotEmpty == true + ? fbBuilder.writeListInt8(_v8) + : null; + final int vf64Offset = _vf64?.isNotEmpty == true + ? fbBuilder.writeListFloat64(_vf64) + : null; + + fbBuilder.startTable(); + fbBuilder.addInt8(0, _i8); + fbBuilder.addUint8(1, _u8); + fbBuilder.addInt16(2, _i16); + fbBuilder.addUint16(3, _u16); + fbBuilder.addInt32(4, _i32); + fbBuilder.addUint32(5, _u32); + fbBuilder.addInt64(6, _i64); + fbBuilder.addUint64(7, _u64); + fbBuilder.addFloat32(8, _f32); + fbBuilder.addFloat64(9, _f64); + if (v8Offset != null) { + fbBuilder.addOffset(10, v8Offset); + } + if (vf64Offset != null) { + fbBuilder.addOffset(11, vf64Offset); + } + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} diff --git a/dart/test/monster_test_my_game_generated.dart b/dart/test/monster_test_my_game_generated.dart new file mode 100644 index 00000000..425bae33 --- /dev/null +++ b/dart/test/monster_test_my_game_generated.dart @@ -0,0 +1,60 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// ignore_for_file: unused_import, unused_field, unused_local_variable + +library my_game; + +import 'dart:typed_data' show Uint8List; +import 'package:flat_buffers/flat_buffers.dart' as fb; + +import './monster_test_my_game.example2_generated.dart' as my_game_example2; +import './monster_test_my_game.example_generated.dart' as my_game_example; + +class InParentNamespace { + InParentNamespace._(this._bc, this._bcOffset); + factory InParentNamespace(List<int> bytes) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader<InParentNamespace> reader = const _InParentNamespaceReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + + @override + String toString() { + return 'InParentNamespace{}'; + } +} + +class _InParentNamespaceReader extends fb.TableReader<InParentNamespace> { + const _InParentNamespaceReader(); + + @override + InParentNamespace createObject(fb.BufferContext bc, int offset) => + new InParentNamespace._(bc, offset); +} + +class InParentNamespaceObjectBuilder extends fb.ObjectBuilder { + + InParentNamespaceObjectBuilder(); + + /// Finish building, and store into the [fbBuilder]. + @override + int finish( + fb.Builder fbBuilder) { + assert(fbBuilder != null); + + fbBuilder.startTable(); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String fileIdentifier]) { + fb.Builder fbBuilder = new fb.Builder(); + int offset = finish(fbBuilder); + return fbBuilder.finish(offset, fileIdentifier); + } +} diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index 1c0b50e6..6401c44d 100644 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -35,6 +35,8 @@ For any schema input files, one or more generators can be specified: - `--grpc`: Generate RPC stub code for GRPC. +- `--dart`: Generate Dart code. + For any data input files: - `--binary`, `-b` : If data is contained in this file, generate a diff --git a/docs/source/DartUsage.md b/docs/source/DartUsage.md new file mode 100644 index 00000000..6be97726 --- /dev/null +++ b/docs/source/DartUsage.md @@ -0,0 +1,108 @@ +Use in Dart {#flatbuffers_guide_use_dart} +=========== + +## Before you get started + +Before diving into the FlatBuffers usage in Dart, it should be noted that +the [Tutorial](@ref flatbuffers_guide_tutorial) page has a complete guide +to general FlatBuffers usage in all of the supported languages (including Dart). +This page is designed to cover the nuances of FlatBuffers usage, specific to +Dart. + +You should also have read the [Building](@ref flatbuffers_guide_building) +documentation to build `flatc` and should be familiar with +[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler) and +[Writing a schema](@ref flatbuffers_guide_writing_schema). + +## FlatBuffers Dart library code location + +The code for the FlatBuffers Go library can be found at +`flatbuffers/dart`. You can browse the library code on the [FlatBuffers +GitHub page](https://github.com/google/flatbuffers/tree/master/dart). + +## Testing the FlatBuffers Dart library + +The code to test the Dart library can be found at `flatbuffers/tests`. +The test code itself is located in [dart_test.dart](https://github.com/google/ +flatbuffers/blob/master/tests/dart_test.dart). + +To run the tests, use the [DartTest.sh](https://github.com/google/flatbuffers/ +blob/master/tests/DartTest.sh) shell script. + +*Note: The shell script requires the [Dart SDK](https://www.dartlang.org/tools/sdk) +to be installed.* + +## Using the FlatBuffers Dart library + +*Note: See [Tutorial](@ref flatbuffers_guide_tutorial) for a more in-depth +example of how to use FlatBuffers in Dart.* + +FlatBuffers supports reading and writing binary FlatBuffers in Dart. + +To use FlatBuffers in your own code, first generate Dart classes from your +schema with the `--dart` option to `flatc`. Then you can include both FlatBuffers +and the generated code to read or write a FlatBuffer. + +For example, here is how you would read a FlatBuffer binary file in Dart: First, +include the library and generated code. Then read a FlatBuffer binary file into +a `List<int>`, which you pass to the factory constructor for `Monster`: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.dart} +import 'dart:io' as io; + +import 'package:flat_buffers/flat_buffers.dart' as fb; +import './monster_my_game.sample_generated.dart' as myGame; + +List<int> data = await new io.File('monster.dat').readAsBytes(); +var monster = new myGame.Monster(data); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now you can access values like this: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.dart} +var hp = monster.hp; +var pos = monster.pos; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## Differences from the Dart SDK Front End flat_buffers + +The work in this repository is signfiicantly based on the implementation used +internally by the Dart SDK in the front end/analyzer package. Several +significant changes have been made. + +1. Support for packed boolean lists has been removed. This is not standard + in other implementations and is not compatible with them. Do note that, + like in the JavaScript implementation, __null values in boolean lists + will be treated as false__. It is also still entirely possible to pack data + in a single scalar field, but that would have to be done on the application + side. +2. The SDK implementation supports enums with regular Dart enums, which + works if enums are always indexed at 1; however, FlatBuffers does not + require that. This implementation uses specialized enum-like classes to + ensure proper mapping from FlatBuffers to Dart and other platforms. +3. The SDK implementation does not appear to support FlatBuffer structs or + vectors of structs - it treated everything as a built-in scalar or a table. + This implementation treats structs in a way that is compatible with other + non-Dart implementations, and properly handles vectors of structs. Many of + the methods prefixed with 'low' have been prepurposed to support this. +4. The SDK implementation treats int64 and uint64 as float64s. This + implementation does not. This may cause problems with JavaScript + compatibility - however, it should be possible to use the JavaScript + implementation, or to do a customized implementation that treats all 64 bit + numbers as floats. Supporting the Dart VM and Flutter was a more important + goal of this implementation. Support for 16 bit integers was also added. +5. The code generation in this offers an "ObjectBuilder", which generates code + very similar to the SDK classes that consume FlatBuffers, as well as Builder + classes, which produces code which more closely resembles the builders in + other languages. The ObjectBuilder classes are easier to use, at the cost of + additional references allocated. + +## Text Parsing + +There currently is no support for parsing text (Schema's and JSON) directly +from Dart, though you could use the C++ parser through Dart Native Extensions. +Please see the C++ documentation for more on text parsing (note that this is +not currently an option in Flutter - follow [this issue](https://github.com/flutter/flutter/issues/7053) +for the latest). + +<br> diff --git a/docs/source/Support.md b/docs/source/Support.md index e1fc5633..d41a8dd8 100644 --- a/docs/source/Support.md +++ b/docs/source/Support.md @@ -18,23 +18,24 @@ In general: NOTE: this table is a start, it needs to be extended. -Feature | C++ | Java | C# | Go | Python | JS | TS | C | PHP | Ruby ------------------------------- | ------ | ------ | ------ | ------ | ------ | --------- | --------- | ------ | --- | ---- -Codegen for all basic features | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | WiP | WiP -JSON parsing | Yes | No | No | No | No | No | No | Yes | No | No -Simple mutation | Yes | Yes | Yes | Yes | No | No | No | No | No | No -Reflection | Yes | No | No | No | No | No | No | Basic | No | No -Buffer verifier | Yes | No | No | No | No | No | No | Yes | No | No -Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | ? -Testing: fuzz | Yes | No | No | Yes | Yes | No | No | No | ? | ? -Performance: | Superb | Great | Great | Great | Ok | ? | ? | Superb | ? | ? -Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | Yes | VS2010 | ? | ? -Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | Yes | ? | ? -Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | Yes | ? | ? -Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ? | ? -Platform: iOS | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? -Engine: Unity | ? | ? | Yes | ? | ? | ? | ? | ? | ? | ? -Primary authors (github) | gwvo | gwvo | ev*/js*| rw | rw | evanw/ev* | kr | mik* | ch* | rw +Feature | C++ | Java | C# | Go | Python | JS | TS | C | PHP | Ruby | Dart +------------------------------ | ------ | ------ | ------ | ------ | ------ | --------- | --------- | ------ | --- | ---- | ---- +Codegen for all basic features | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | WiP | WiP | Yes +JSON parsing | Yes | No | No | No | No | No | No | Yes | No | No | No +Simple mutation | Yes | Yes | Yes | Yes | No | No | No | No | No | No | No +Reflection | Yes | No | No | No | No | No | No | Basic | No | No | No +Buffer verifier | Yes | No | No | No | No | No | No | Yes | No | No | No +Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | ? | Yes +Testing: fuzz | Yes | No | No | Yes | Yes | No | No | No | ? | ? | No +Performance: | Superb | Great | Great | Great | Ok | ? | ? | Superb | ? | ? | ? +Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | Yes | VS2010 | ? | ? | Yes +Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | Yes | ? | ? | Yes +Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | Yes | ? | ? | Yes +Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ? | ? | Flutter +Platform: iOS | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | Flutter +Engine: Unity | ? | ? | Yes | ? | ? | ? | ? | ? | ? | ? | ? +Primary authors (github) | gwvo | gwvo | ev*/js*| rw | rw | evanw/ev* | kr | mik* | ch* | rw | dnfield + * ev = evolutional * js = jonsimantov diff --git a/docs/source/Tutorial.md b/docs/source/Tutorial.md index 1c54829f..1424e82c 100644 --- a/docs/source/Tutorial.md +++ b/docs/source/Tutorial.md @@ -30,6 +30,7 @@ Please select your desired language for our quest: <input type="radio" name="language" value="typescript">TypeScript</input> <input type="radio" name="language" value="php">PHP</input> <input type="radio" name="language" value="c">C</input> + <input type="radio" name="language" value="dart">Dart</input> </form> \endhtmlonly @@ -132,6 +133,9 @@ For your chosen language, please cross-reference with: <div class="language-c"> [monster.c](https://github.com/dvidelabs/flatcc/blob/master/samples/monster/monster.c) </div> +<div class="language-dart"> +[example.dart](https://github.com/google/flatbuffers/blob/master/dart/example/example.dart) +</div> ## Writing the Monsters' FlatBuffer Schema @@ -312,6 +316,12 @@ Please be aware of the difference between `flatc` and `flatcc` tools. flatcc/samples/monster/build.sh ~~~ </div> +<div class="language-dart"> +~~~{.sh} + cd flatbuffers/sample + ./../flatc --dart samples/monster.fbs +~~~ +</div> For a more complete guide to using the `flatc` compiler, please read the [Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler) @@ -421,6 +431,14 @@ The first step is to import/include the library, generated files, etc. #define c_vec_len(V) (sizeof(V)/sizeof((V)[0])) ~~~ </div> +<div class="language-dart"> +~~~{.dart} + import 'package:flat_buffers/flat_buffers.dart' as fb; + + // Generated by `flatc`. + import 'monster_my_game.sample_generated.dart' as myGame; +~~~ +</div> Now we are ready to start building some buffers. In order to start, we need to create an instance of the `FlatBufferBuilder`, which will contain the buffer @@ -491,6 +509,15 @@ which will grow automatically if needed: flatcc_builder_init(B); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // Create the fb.Builder object that will be used by our generated builders + // Note that if you are only planning to immediately get the byte array this builder would create, + // you can use the convenience method `toBytes()` on the generated builders. + // For example, you could do something like `new myGame.MonsterBuilder(...).toBytes()` + var builder = new fb.Builder(initialSize: 1024); +~~~ +</div> After creating the `builder`, we can start serializing our data. Before we make our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`. @@ -633,6 +660,51 @@ our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`. ns(Weapon_ref_t) axe = ns(Weapon_create(B, weapon_two_name, weapon_two_damage)); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // The generated Builder classes work much like in other languages, + final int weaponOneName = builder.writeString("Sword"); + final int weaponOneDamage = 3; + + final int weaponTwoName = builder.writeString("Axe"); + final int weaponTwoDamage = 5; + + final swordBuilder = new myGame.WeaponBuilder(builder) + ..begin() + ..addNameOffset(weaponOneName) + ..addDamage(weaponOneDamage); + final int sword = swordBuilder.finish(); + + final axeBuilder = new myGame.WeaponBuilder(builder) + ..begin() + ..addNameOffset(weaponTwoName) + ..addDamage(weaponTwoDamage); + final int axe = axeBuilder.finish(); + + + + // The genearted ObjectBuilder classes offer an easier to use alternative + // at the cost of requiring some additional reference allocations. If memory + // usage is critical, or if you'll be working with especially large messages + // or tables, you should prefer using the generated Builder classes. + // The following code would produce an identical buffer as above. + final String weaponOneName = "Sword"; + final int weaponOneDamage = 3; + + final String weaponTwoName = "Axe"; + final int weaponTwoDamage = 5; + + final myGame.WeaponBuilder sword = new myGame.WeaponObjectBuilder( + name: weaponOneName, + damage: weaponOneDamage, + ); + + final myGame.WeaponBuilder axe = new myGame.WeaponObjectBuilder( + name: weaponTwoName, + damage: weaponTwoDamage, + ); +~~~ +</div> Now let's create our monster, the `orc`. For this `orc`, lets make him `red` with rage, positioned at `(1.0, 2.0, 3.0)`, and give him @@ -760,6 +832,26 @@ traversal. This is generally easy to do on any tree structures. inventory = flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure)); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // Serialize a name for our monster, called "Orc". + final int name = builder.writeString('Orc'); + + // Create a list representing the inventory of the Orc. Each number + // could correspond to an item that can be claimed after he is slain. + final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + final inventory = builder.writeListUint8(treasure); + + // The following code should be used instead if you intend to use the + // ObjectBuilder classes: + // Serialize a name for our monster, called "Orc". + final String name = 'Orc'; + + // Create a list representing the inventory of the Orc. Each number + // could correspond to an item that can be claimed after he is slain. + final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +~~~ +</div> We serialized two built-in data types (`string` and `vector`) and captured their return values. These values are offsets into the serialized data, @@ -863,6 +955,15 @@ offsets. ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B)); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // If using the Builder classes, serialize the `[sword,axe]` + final weapons = builder.writeList([sword, axe]); + + // If using the ObjectBuilders, just create an array from the two `Weapon`s + final List<myGame.WeaponBuilder> weaps = [sword, axe]; +~~~ +</div> <div class="language-cpp"> <br> @@ -943,6 +1044,25 @@ for the `path` field above: // TBD ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // Using the Builder classes, you can write a list of structs like so: + // Note that the intended order should be reversed if order is important. + final vec3Builder = new myGame.Vec3Builder(builder); + vec3Builder.finish(4.0, 5.0, 6.0); + vec3Builder.finish(1.0, 2.0, 3.0); + final int path = builder.endStructVector(2); // the lenght of the vector + + // Otherwise, using the ObjectBuilder classes: + // The dart implementation provides a simple interface for writing vectors + // of structs, in `writeListOfStructs`. This method takes + // `List<ObjectBuilder>` and is used by the generated builder classes. + final List<myGame.Vec3ObjectBuilder> path = [ + new myGame.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0), + new myGame.Vec3ObjectBuilder(x: 4.0, y: 5.0, z: 6.0) + ]; +~~~ +</div> We have now serialized the non-scalar components of the orc, so we can serialize the monster itself: @@ -1095,6 +1215,58 @@ can serialize the monster itself: weapons, equipped, path)); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // Using the Builder API: + // Set his hit points to 300 and his mana to 150. + final int hp = 300; + final int mana = 150; + + final monster = new myGame.MonsterBuilder(builder) + ..begin() + ..addNameOffset(name) + ..addInventoryOffset(inventory) + ..addWeaponsOffset(weapons) + ..addEquippedType(myGame.EquipmentTypeId.Weapon) + ..addEquippedOffset(axe) + ..addHp(hp) + ..addMana(mana) + ..addPos(vec3Builder.finish(1.0, 2.0, 3.0)) + ..addPathOffset(path) + ..addColor(myGame.Color.Red); + + final int orc = monster.finish(); + + // -Or- using the ObjectBuilder API: + // Set his hit points to 300 and his mana to 150. + final int hp = 300; + final int mana = 150; + + // Note that these parameters are optional - it is not necessary to set + // all of them. + // Also note that it is not necessary to `finish` the builder helpers above + // - the generated code will automatically reuse offsets if the same object + // is used in more than one place (e.g. the axe appearing in `weapons` and + // `equipped`). + final myGame.MonsterBuilder orcBuilder = new myGame.MonsterBuilder( + name: name, + inventory: treasure, + weapons: weaps, + equippedType: myGame.EquipmentTypeId.Weapon, + equipped: axe, + path: path, + hp: hp, + mana: mana, + pos: new myGame.Vec3Builder(x: 1.0, y: 2.0, z: 3.0), + color: myGame.Color.Red, + path: [ + new myGame.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0), + new myGame.Vec3ObjectBuilder(x: 4.0, y: 5.0, z: 6.0) + ]); + + final int orc = orcBuilder.finish(builder); +~~~ +</div> Note how we create `Vec3` struct in-line in the table. Unlike tables, structs are simple combinations of scalars that are always stored inline, just like @@ -1226,6 +1398,17 @@ Here is a repetition these lines, to help highlight them more clearly: ns(Monster_equipped_Weapon_add(B, axe)); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // using the builder API: + ..addEquippedType(myGame.EquipmentTypeId.Weapon) + ..addEquippedOffset(axe) + + // in the ObjectBuilder API: + equippedTypeId: myGame.EquipmentTypeId.Weapon, // Union type + equipped: axe, // Union data +~~~ +</div> After you have created your buffer, you will have the offset to the root of the data in the `orc` variable, so you can finish the buffer by calling the @@ -1291,6 +1474,12 @@ appropriate `finish` method. // Because we used `Monster_create_as_root`, we do not need a `finish` call in C`. ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // Call `finish()` to instruct the builder that this monster is complete. + // See the next code section, as in Dart `finish` will also return the byte array. +~~~ +</div> The buffer is now ready to be stored somewhere, sent over the network, be compressed, or whatever you'd like to do with it. You can access the buffer @@ -1383,6 +1572,11 @@ like so: flatcc_builder_clear(B); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + final Uint8List buf = builder.finish(orc); +~~~ +</div> Now you can write the bytes to a file, send them over the network.. **Make sure your file mode (or tranfer protocol) is set to BINARY, not text.** @@ -1490,6 +1684,12 @@ before: #define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema. ~~~ </div> +<div class="language-dart"> +~~~{.dart} +import 'package:flat_buffers/flat_buffers.dart' as fb; +import './monster_my_game.sample_generated.dart' as myGame; +~~~ +</div> Then, assuming you have a buffer of bytes received from disk, network, etc., you can create start accessing the buffer like so: @@ -1591,6 +1791,13 @@ won't work** // Note: root object pointers are NOT the same as the `buffer` pointer. ~~~ </div> +<div class="language-dart"> +~~~{.dart} +List<int> data = ... // the data, e.g. from file or network +// A generated factory constructor that will read the data. +myGame.Monster monster = new myGame.Monster(data); +~~~ +</div> If you look in the generated files from the schema compiler, you will see it generated accessors for all non-`deprecated` fields. For example: @@ -1611,7 +1818,7 @@ accessors for all non-`deprecated` fields. For example: </div> <div class="language-csharp"> ~~~{.cs} - // For C#, unlike other languages support by FlatBuffers, most values (except for + // For C#, unlike most other languages support by FlatBuffers, most values (except for // vectors and unions) are available as propreties instead of asccessor methods. var hp = monster.Hp var mana = monster.Mana @@ -1660,6 +1867,15 @@ accessors for all non-`deprecated` fields. For example: flatbuffers_string_t name = ns(Monster_name(monster)); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + // For Dart, unlike other languages support by FlatBuffers, most values + // are available as propreties instead of asccessor methods. + var hp = monster.hp; + var mana = monster.mana; + var name = monster.name; +~~~ +</div> These should hold `300`, `150`, and `"Orc"` respectively. @@ -1745,6 +1961,14 @@ To access sub-objects, in the case of our `pos`, which is a `Vec3`: float z = ns(Vec3_z(pos)); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + myGame.Vec3 pos = monster.pos; + double x = pos.x; + double y = pos.y; + double z = pos.z; +~~~ +</div> `x`, `y`, and `z` will contain `1.0`, `2.0`, and `3.0`, respectively. @@ -1811,6 +2035,12 @@ FlatBuffers `vector`. size_t inv_len = flatbuffers_uint8_vec_len(inv); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + int invLength = monster.inventory.length; + var thirdItem = monster.inventory[2]; +~~~ +</div> For `vector`s of `table`s, you can access the elements like any other vector, except your need to handle the result as a FlatBuffer `table`: @@ -1885,6 +2115,13 @@ except your need to handle the result as a FlatBuffer `table`: uint16_t second_weapon_damage = ns(Weapon_damage(ns(Weapon_vec_at(weapons, 1)))); ~~~ </div> +<div class="language-dart"> +~~~{.dart} + int weaponsLength = monster.weapons.length; + var secondWeaponName = monster.weapons[1].name; + var secondWeaponDamage = monster.Weapons[1].damage; +~~~ +</div> Last, we can access our `Equipped` FlatBuffer `union`. Just like when we created the `union`, we need to get both parts of the `union`: the type and the data. @@ -2008,6 +2245,18 @@ We can access the type to dynamically cast the data as needed (since the } ~~~ </div> +<div class="language-dart"> +~~~{.dart} + var unionType = monster.equippedType.value; + + if (unionType == myGame.EquipmentTypeId.Weapon.value) { + myGame.Weapon weapon = mon.equipped as myGame.Weapon; + + var weaponName = weapon.name; // "Axe" + var weaponDamage = weapon.damage; // 5 + } +~~~ +</div> ## Mutating FlatBuffers @@ -2083,6 +2332,11 @@ mutators like so: (except in-place vector sorting is possible).> ~~~ </div> +<div class="language-dart"> +~~~{.dart} + <API for mutating FlatBuffers not yet available in Dart.> +~~~ +</div> We use the somewhat verbose term `mutate` instead of `set` to indicate that this is a special use case, not to be confused with the default way of constructing @@ -2192,5 +2446,8 @@ For your chosen language, see: <div class="language-c"> [Use in C](@ref flatbuffers_guide_use_c) </div> +<div class="language-dart"> +[Use in Dart](@ref flatbuffers_guide_use_dart) +</div> <br> diff --git a/docs/source/doxyfile b/docs/source/doxyfile index 64af6710..fb9b695c 100644 --- a/docs/source/doxyfile +++ b/docs/source/doxyfile @@ -751,6 +751,7 @@ INPUT = "FlatBuffers.md" \ "Schemas.md" \ "CppUsage.md" \ "CUsage.md" \ + "DartUsage.md" \ "GoUsage.md" \ "JavaCsharpUsage.md" \ "JavaScriptUsage.md" \ diff --git a/docs/source/doxygen_layout.xml b/docs/source/doxygen_layout.xml index 1096cfff..c7233143 100644 --- a/docs/source/doxygen_layout.xml +++ b/docs/source/doxygen_layout.xml @@ -39,6 +39,8 @@ title="Use in PHP"/> <tab type="user" url="@ref flatbuffers_guide_use_python" title="Use in Python"/> + <tab type="user" url="@ref flatbuffers_guide_use_dart" + title="Use in Dart"/> <tab type="user" url="@ref flexbuffers" title="Schema-less version"/> <tab type="usergroup" url="" title="gRPC"> diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index c261b74a..a40c5c19 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -405,6 +405,7 @@ struct IDLOptions { kBinary = 1 << 8, kTs = 1 << 9, kJsonSchema = 1 << 10, + kDart = 1 << 11, kMAX }; @@ -767,6 +768,10 @@ extern bool GenerateCPP(const Parser &parser, const std::string &path, const std::string &file_name); +extern bool GenerateDart(const Parser &parser, + const std::string &path, + const std::string &file_name); + // Generate JavaScript or TypeScript code from the definitions in the Parser object. // See idl_gen_js. extern bool GenerateJS(const Parser &parser, @@ -823,6 +828,12 @@ extern std::string CPPMakeRule(const Parser &parser, const std::string &path, const std::string &file_name); +// Generate a make rule for the generated Dart code +// see idl_gen_dart.cpp +extern std::string DartMakeRule(const Parser &parser, + const std::string &path, + const std::string &file_name); + // Generate a make rule for the generated Java/C#/... files. // See idl_gen_general.cpp. extern std::string GeneralMakeRule(const Parser &parser, diff --git a/samples/dart_sample.sh b/samples/dart_sample.sh new file mode 100755 index 00000000..56160666 --- /dev/null +++ b/samples/dart_sample.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# +# Copyright 2018 Dan Field. 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. +# +# Note: This script runs on Mac and Linux. It requires `Node.js` to be installed +# and `flatc` to be built (using `cmake` in the root directory). + +sampledir=$(cd $(dirname $BASH_SOURCE) && pwd) +rootdir=$(cd $sampledir/.. && pwd) +currentdir=$(pwd) + +if [[ "$sampledir" != "$currentdir" ]]; then + echo Error: This script must be run from inside the $sampledir directory. + echo You executed it from the $currentdir directory. + exit 1 +fi + +cd ../dart/example + +# Run `flatc`. Note: This requires you to compile using `cmake` from the +# root `/flatbuffers` directory. +if [ -e ../../flatc ]; then + ../../flatc --dart ../../samples/monster.fbs +elif [ -e ../../Debug/flatc ]; then + ../../Debug/flatc --dart ../../samples/monster.fbs +else + echo 'flatc' could not be found. Make sure to build FlatBuffers from the \ + $rootdir directory. + exit 1 +fi + +echo Running the Dart sample. + +# Execute the sample. +dart example.dart + +# Cleanup temporary files. +git checkout monster_my_game.sample_generated.dart + +cd ../../samples diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp index d093020e..1fed8952 100644 --- a/src/flatc_main.cpp +++ b/src/flatc_main.cpp @@ -58,6 +58,9 @@ int main(int argc, const char *argv[]) { { flatbuffers::GenerateJS, "-s", "--js", "JavaScript", true, nullptr, flatbuffers::IDLOptions::kJs, "Generate JavaScript code for tables/structs", flatbuffers::JSMakeRule }, + { flatbuffers::GenerateDart, "-d", "--dart", "Dart", true, nullptr, + flatbuffers::IDLOptions::kDart, + "Generate Dart classes for tables/structs", flatbuffers::DartMakeRule }, { flatbuffers::GenerateJS, "-T", "--ts", "TypeScript", true, nullptr, flatbuffers::IDLOptions::kTs, "Generate TypeScript code for tables/structs", flatbuffers::JSMakeRule }, diff --git a/src/idl_gen_dart.cpp b/src/idl_gen_dart.cpp new file mode 100644 index 00000000..41343d76 --- /dev/null +++ b/src/idl_gen_dart.cpp @@ -0,0 +1,890 @@ +/* + * Copyright 2018 Dan Field + * + * 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 <cassert> +#include <unordered_map> + +#include "flatbuffers/code_generators.h" +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +namespace flatbuffers { + +static std::string GeneratedFileName(const std::string &path, + const std::string &file_name) { + return path + file_name + "_generated.dart"; +} + +namespace dart { + +const std::string _kFb = "fb"; +// see https://www.dartlang.org/guides/language/language-tour#keywords +// yeild*, async*, and sync* shouldn't be problems anyway but keeping them in +static const char *keywords[] = { + "abstract", "deferred", "if", "super", "as", "do", + "implements", "switch", "assert", "dynamic", "import", "sync*", + "async", "else", "in", "this", "async*", "enum", + "is", "throw", "await", "export", "library", "true", + "break", "external", "new", "try", "case", "extends", + "null", "typedef", "catch", "factory", "operator", "var", + "class", "false", "part", "void", "const", "final", + "rethrow", "while", "continue", "finally", "return", "with", + "covariant", "for", "set", "yield", "default", "get", + "static", "yield*" +}; + +// Iterate through all definitions we haven't generate code for (enums, structs, +// and tables) and output them to a single file. +class DartGenerator : public BaseGenerator { + public: + typedef std::unordered_map<std::string, std::string> namespace_code_map; + + DartGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "", "."){}; + // Iterate through all definitions we haven't generate code for (enums, + // structs, and tables) and output them to a single file. + bool generate() { + std::string code; + namespace_code_map namespace_code; + GenerateEnums(&namespace_code); + GenerateStructs(&namespace_code); + + for (auto kv = namespace_code.begin(); kv != namespace_code.end(); ++kv) { + code.clear(); + code = code + "// " + FlatBuffersGeneratedWarning() + "\n"; + code = code + + "// ignore_for_file: unused_import, unused_field, " + "unused_local_variable\n\n"; + + code += "library " + kv->first + ";\n\n"; + + code += "import 'dart:typed_data' show Uint8List;\n"; + code += "import 'package:flat_buffers/flat_buffers.dart' as " + _kFb + + ";\n\n"; + + for (auto kv2 = namespace_code.begin(); kv2 != namespace_code.end(); + ++kv2) { + if (kv2->first != kv->first) { + code += "import '" + + GeneratedFileName("./", file_name_ + "_" + kv2->first) + + "' as " + ImportAliasName(kv2->first) + ";\n"; + } + } + code += "\n"; + code += kv->second; + + if (!SaveFile( + GeneratedFileName(path_, file_name_ + "_" + kv->first).c_str(), + code, false)) { + return false; + } + } + return true; + } + + private: + static std::string ImportAliasName(const std::string &ns) { + std::string ret; + ret.assign(ns); + size_t pos = ret.find("."); + while (pos != std::string::npos) { + ret.replace(pos, 1, "_"); + pos = ret.find(".", pos + 1); + } + + return ret; + } + + static std::string BuildNamespaceName(const Namespace &ns) { + std::stringstream sstream; + std::copy(ns.components.begin(), ns.components.end() - 1, + std::ostream_iterator<std::string>(sstream, ".")); + + auto ret = sstream.str() + ns.components.back(); + for (int i = 0; ret[i]; i++) { + auto lower = tolower(ret[i]); + if (lower != ret[i]) { + ret[i] = static_cast<char>(lower); + if (i != 0 && ret[i - 1] != '.') { + ret.insert(i, "_"); + i++; + } + } + } + // std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; + } + static std::string EscapeKeyword(const std::string &name) { + for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { + if (name == keywords[i]) { return MakeCamel(name + "_", false); } + } + + return MakeCamel(name, false); + } + + void GenerateEnums(namespace_code_map *namespace_code) { + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + auto &enum_def = **it; + GenEnum(enum_def, namespace_code); // enum_code_ptr); + } + } + + void GenerateStructs(namespace_code_map *namespace_code) { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + GenStruct(struct_def, namespace_code); + } + } + + // Generate a documentation comment, if available. + static void GenDocComment(const std::vector<std::string> &dc, + std::string *code_ptr, + const std::string &extra_lines, + const char *indent = nullptr) { + if (dc.empty() && extra_lines.empty()) { + // Don't output empty comment blocks with 0 lines of comment content. + return; + } + + auto &code = *code_ptr; + if (indent) code += indent; + + for (auto it = dc.begin(); it != dc.end(); ++it) { + if (indent) code += indent; + code += "/// " + *it + "\n"; + } + if (!extra_lines.empty()) { + if (!dc.empty()) { + if (indent) code += indent; + code += "///\n"; + } + if (indent) code += indent; + std::string::size_type start = 0; + for (;;) { + auto end = extra_lines.find('\n', start); + if (end != std::string::npos) { + code += "/// " + extra_lines.substr(start, end - start) + "\n"; + start = end + 1; + } else { + code += "/// " + extra_lines.substr(start) + "\n"; + break; + } + } + } + } + + static void GenDocComment(std::string *code_ptr, + const std::string &extra_lines) { + GenDocComment(std::vector<std::string>(), code_ptr, extra_lines); + } + + // Generate an enum declaration and an enum string lookup table. + void GenEnum(EnumDef &enum_def, namespace_code_map *namespace_code) { + if (enum_def.generated) return; + auto ns = BuildNamespaceName(*enum_def.defined_namespace); + std::string code; + GenDocComment(enum_def.doc_comment, &code, ""); + + auto name = enum_def.is_union ? enum_def.name + "TypeId" : enum_def.name; + auto is_bit_flags = enum_def.attributes.Lookup("bit_flags"); + + code += "class " + name + " {\n"; + code += " final int value;\n"; + code += " const " + name + "._(this.value);\n\n"; + code += " factory " + name + ".fromValue(int value) {\n"; + code += " if (value == null) return null;\n"; + + code += " if (!values.containsKey(value)) {\n"; + code += + " throw new StateError('Invalid value $value for bit flag enum "; + code += name + "');\n"; + code += " }\n"; + + code += " return values[value];\n"; + code += " }\n\n"; + + // this is meaningless for bit_flags + // however, note that unlike "regular" dart enums this enum can still have + // holes. + if (!is_bit_flags) { + code += " static const int minValue = " + + NumToString(enum_def.vals.vec.front()->value) + ";\n"; + code += " static const int maxValue = " + + NumToString(enum_def.vals.vec.back()->value) + ";\n"; + } + + code += + " static bool containsValue(int value) =>" + " values.containsKey(value);\n\n"; + + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + + if (!ev.doc_comment.empty()) { + if (it != enum_def.vals.vec.begin()) { code += '\n'; } + GenDocComment(ev.doc_comment, &code, "", " "); + } + code += " static const " + name + " " + ev.name + " = "; + code += "const " + name + "._(" + NumToString(ev.value) + ");\n"; + } + + code += " static get values => {"; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + code += NumToString(ev.value) + ": " + ev.name + ","; + } + code += "};\n\n"; + + code += " static const " + _kFb + ".Reader<" + name + + "> reader = const _" + name + "Reader();\n\n"; + code += " @override\n"; + code += " String toString() {\n"; + code += " return '" + name + "{value: $value}';\n"; + code += " }\n"; + code += "}\n\n"; + + GenEnumReader(enum_def, name, &code); + (*namespace_code)[ns] += code; + } + + void GenEnumReader(EnumDef &enum_def, const std::string &name, + std::string *code_ptr) { + auto &code = *code_ptr; + + code += "class _" + name + "Reader extends " + _kFb + ".Reader<" + name + + "> {\n"; + code += " const _" + name + "Reader();\n\n"; + code += " @override\n"; + code += " int get size => 1;\n\n"; + code += " @override\n"; + code += + " " + name + " read(" + _kFb + ".BufferContext bc, int offset) =>\n"; + code += " new " + name + ".fromValue(const " + _kFb + "." + + GenType(enum_def.underlying_type) + "Reader().read(bc, offset));\n"; + code += "}\n\n"; + } + + static std::string GenType(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_BOOL: return "Bool"; + case BASE_TYPE_CHAR: return "Int8"; + case BASE_TYPE_UTYPE: + case BASE_TYPE_UCHAR: return "Uint8"; + case BASE_TYPE_SHORT: return "Int16"; + case BASE_TYPE_USHORT: return "Uint16"; + case BASE_TYPE_INT: return "Int32"; + case BASE_TYPE_UINT: return "Uint32"; + case BASE_TYPE_LONG: return "Int64"; + case BASE_TYPE_ULONG: return "Uint64"; + case BASE_TYPE_FLOAT: return "Float32"; + case BASE_TYPE_DOUBLE: return "Float64"; + case BASE_TYPE_STRING: return "String"; + case BASE_TYPE_VECTOR: return GenType(type.VectorType()); + case BASE_TYPE_STRUCT: return type.struct_def->name; + case BASE_TYPE_UNION: return type.enum_def->name + "TypeId"; + default: return "Table"; + } + } + + std::string GenReaderTypeName(const Type &type, Namespace *current_namespace, + const FieldDef &def, + bool parent_is_vector = false) { + if (type.base_type == BASE_TYPE_BOOL) { + return "const " + _kFb + ".BoolReader()"; + } else if (type.base_type == BASE_TYPE_VECTOR) { + return "const " + _kFb + ".ListReader<" + + GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" + + GenReaderTypeName(type.VectorType(), current_namespace, def, + true) + + ")"; + } else if (type.base_type == BASE_TYPE_STRING) { + return "const " + _kFb + ".StringReader()"; + } + if (IsScalar(type.base_type)) { + if (type.enum_def && parent_is_vector) { + return GenDartTypeName(type, current_namespace, def) + ".reader"; + } + return "const " + _kFb + "." + GenType(type) + "Reader()"; + } else { + return GenDartTypeName(type, current_namespace, def) + ".reader"; + } + } + + std::string GenDartTypeName(const Type &type, Namespace *current_namespace, + const FieldDef &def, bool addBuilder = false) { + if (type.enum_def) { + if (type.enum_def->is_union && type.base_type != BASE_TYPE_UNION) { + return type.enum_def->name + "TypeId"; + } else if (type.enum_def->is_union) { + return "dynamic"; + } else if (type.base_type != BASE_TYPE_VECTOR) { + return type.enum_def->name; + } + } + + switch (type.base_type) { + case BASE_TYPE_BOOL: return "bool"; + case BASE_TYPE_LONG: + case BASE_TYPE_ULONG: + case BASE_TYPE_INT: + case BASE_TYPE_UINT: + case BASE_TYPE_SHORT: + case BASE_TYPE_USHORT: + case BASE_TYPE_CHAR: + case BASE_TYPE_UCHAR: return "int"; + case BASE_TYPE_FLOAT: + case BASE_TYPE_DOUBLE: return "double"; + case BASE_TYPE_STRING: return "String"; + case BASE_TYPE_STRUCT: + return MaybeWrapNamespace( + type.struct_def->name + (addBuilder ? "ObjectBuilder" : ""), + current_namespace, def); + case BASE_TYPE_VECTOR: + return "List<" + + GenDartTypeName(type.VectorType(), current_namespace, def, + addBuilder) + + ">"; + default: assert(0); return "dynamic"; + } + } + + static const std::string MaybeWrapNamespace(const std::string &type_name, + Namespace *current_ns, + const FieldDef &field) { + auto curr_ns_str = BuildNamespaceName(*current_ns); + std::string field_ns_str = ""; + if (field.value.type.struct_def) { + field_ns_str += + BuildNamespaceName(*field.value.type.struct_def->defined_namespace); + } else if (field.value.type.enum_def) { + field_ns_str += + BuildNamespaceName(*field.value.type.enum_def->defined_namespace); + } + + if (field_ns_str != "" && field_ns_str != curr_ns_str) { + return ImportAliasName(field_ns_str) + "." + type_name; + } else { + return type_name; + } + } + + // Generate an accessor struct with constructor for a flatbuffers struct. + void GenStruct(const StructDef &struct_def, + namespace_code_map *namespace_code) { + if (struct_def.generated) return; + + auto object_namespace = BuildNamespaceName(*struct_def.defined_namespace); + std::string code; + + auto object_name = struct_def.name; + + // Emit constructor + + GenDocComment(struct_def.doc_comment, &code, ""); + + auto reader_name = "_" + struct_def.name + "Reader"; + auto builder_name = struct_def.name + "Builder"; + auto object_builder_name = struct_def.name + "ObjectBuilder"; + + std::string reader_code, builder_code; + + code += "class " + struct_def.name + " {\n"; + + code += " " + struct_def.name + "._(this._bc, this._bcOffset);\n"; + if (!struct_def.fixed) { + code += " factory " + struct_def.name + "(List<int> bytes) {\n"; + code += " " + _kFb + ".BufferContext rootRef = new " + _kFb + + ".BufferContext.fromBytes(bytes);\n"; + code += " return reader.read(rootRef, 0);\n"; + code += " }\n"; + } + + code += "\n"; + code += " static const " + _kFb + ".Reader<" + struct_def.name + + "> reader = const " + reader_name + "();\n\n"; + + code += " final " + _kFb + ".BufferContext _bc;\n"; + code += " final int _bcOffset;\n\n"; + + GenImplementationGetters(struct_def, &code); + + code += "}\n\n"; + + GenReader(struct_def, &reader_name, &reader_code); + GenBuilder(struct_def, &builder_name, &builder_code); + GenObjectBuilder(struct_def, &object_builder_name, &builder_code); + + code += reader_code; + code += builder_code; + + (*namespace_code)[object_namespace] += code; + } + + std::string NamespaceAliasFromUnionType(const std::string &in) { + if (in.find("_") == std::string::npos) { return in; } + + std::stringstream ss(in); + std::string item; + std::vector<std::string> parts; + std::string ns; + + while (std::getline(ss, item, '_')) { parts.push_back(item); } + + for (auto it = parts.begin(); it != parts.end() - 1; ++it) { + auto &part = *it; + + for (size_t i = 0; i < part.length(); i++) { + if (i && !isdigit(part[i]) && + part[i] == static_cast<char>(toupper(part[i]))) { + ns += "_"; + ns += static_cast<char>(tolower(part[i])); + } else { + ns += static_cast<char>(tolower(part[i])); + } + } + if (it != parts.end() - 2) { ns += "_"; } + } + + return ns + "." + parts.back(); + } + + void GenImplementationGetters(const StructDef &struct_def, + std::string *code_ptr) { + auto &code = *code_ptr; + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + std::string field_name = MakeCamel(field.name, false); + std::string type_name = GenDartTypeName( + field.value.type, struct_def.defined_namespace, field, false); + + GenDocComment(field.doc_comment, &code, ""); + + code += " " + type_name + " get " + field_name; + if (field.value.type.base_type == BASE_TYPE_UNION) { + code += " {\n"; + code += " switch (" + field_name + "Type?.value) {\n"; + for (auto en_it = field.value.type.enum_def->vals.vec.begin() + 1; + en_it != field.value.type.enum_def->vals.vec.end(); ++en_it) { + auto &ev = **en_it; + + auto enum_name = NamespaceAliasFromUnionType(ev.name); + code += " case " + NumToString(ev.value) + ": return " + + enum_name + ".reader.vTableGet(_bc, _bcOffset, " + + NumToString(field.value.offset) + ", null);\n"; + } + code += " default: return null;\n"; + code += " }\n"; + code += " }\n"; + } else { + code += " => "; + if (field.value.type.enum_def && + field.value.type.base_type != BASE_TYPE_VECTOR) { + code += "new " + + GenDartTypeName(field.value.type, + struct_def.defined_namespace, field) + + ".fromValue("; + } + + code += GenReaderTypeName(field.value.type, + struct_def.defined_namespace, field); + if (struct_def.fixed) { + code += + ".read(_bc, _bcOffset + " + NumToString(field.value.offset) + ")"; + } else { + code += ".vTableGet(_bc, _bcOffset, " + + NumToString(field.value.offset) + ", "; + if (!field.value.constant.empty() && field.value.constant != "0") { + code += field.value.constant; + } else { + code += "null"; + } + code += ")"; + } + if (field.value.type.enum_def && + field.value.type.base_type != BASE_TYPE_VECTOR) { + code += ")"; + } + code += ";\n"; + } + } + + code += "\n"; + + code += " @override\n"; + code += " String toString() {\n"; + code += " return '" + struct_def.name + "{"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + code += + MakeCamel(field.name, false) + ": $" + MakeCamel(field.name, false); + if (it != struct_def.fields.vec.end() - 1) { code += ", "; } + } + code += "}';\n"; + code += " }\n"; + } + + void GenReader(const StructDef &struct_def, std::string *reader_name_ptr, + std::string *code_ptr) { + auto &code = *code_ptr; + auto &reader_name = *reader_name_ptr; + auto &impl_name = struct_def.name; + + code += "class " + reader_name + " extends " + _kFb; + if (struct_def.fixed) { + code += ".StructReader<"; + } else { + code += ".TableReader<"; + } + code += impl_name + "> {\n"; + code += " const " + reader_name + "();\n\n"; + + if (struct_def.fixed) { + code += " @override\n"; + code += " int get size => " + NumToString(struct_def.bytesize) + ";\n\n"; + } + code += " @override\n"; + code += " " + impl_name + + " createObject(fb.BufferContext bc, int offset) => \n new " + + impl_name + "._(bc, offset);\n"; + code += "}\n\n"; + } + + void GenBuilder(const StructDef &struct_def, std::string *builder_name_ptr, + std::string *code_ptr) { + if (struct_def.fields.vec.size() == 0) { return; } + auto &code = *code_ptr; + auto &builder_name = *builder_name_ptr; + + code += "class " + builder_name + " {\n"; + code += " " + builder_name + "(this.fbBuilder) {\n"; + code += " assert(fbBuilder != null);\n"; + code += " }\n\n"; + code += " final " + _kFb + ".Builder fbBuilder;\n\n"; + + if (struct_def.fixed) { + StructBuilderBody(struct_def, code_ptr); + } else { + TableBuilderBody(struct_def, code_ptr); + } + + code += "}\n\n"; + } + + void StructBuilderBody(const StructDef &struct_def, std::string *code_ptr) { + auto &code = *code_ptr; + + code += " int finish("; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + if (IsStruct(field.value.type)) { + code += "fb.StructBuilder"; + } else { + code += GenDartTypeName(field.value.type, struct_def.defined_namespace, + field); + } + code += " " + field.name; + if (it != struct_def.fields.vec.end() - 1) { code += ", "; } + } + code += ") {\n"; + + for (auto it = struct_def.fields.vec.rbegin(); + it != struct_def.fields.vec.rend(); ++it) { + auto &field = **it; + + if (field.deprecated) continue; + + if (field.padding) { + code += " fbBuilder.pad(" + NumToString(field.padding) + ");\n"; + } + + if (IsStruct(field.value.type)) { + code += " " + field.name + "();\n"; + } else { + code += " fbBuilder.put" + GenType(field.value.type) + "("; + code += field.name; + if (field.value.type.enum_def) { code += "?.value"; } + code += ");\n"; + } + } + code += " return fbBuilder.offset;\n"; + code += " }\n\n"; + } + + void TableBuilderBody(const StructDef &struct_def, std::string *code_ptr) { + auto &code = *code_ptr; + + code += " void begin() {\n"; + code += " fbBuilder.startTable();\n"; + code += " }\n\n"; + + 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(); + + if (IsScalar(field.value.type.base_type)) { + code += " int add" + MakeCamel(field.name) + "("; + code += GenDartTypeName(field.value.type, struct_def.defined_namespace, + field); + code += " " + MakeCamel(field.name, false) + ") {\n"; + code += " fbBuilder.add" + GenType(field.value.type) + "(" + + NumToString(offset) + ", "; + code += MakeCamel(field.name, false); + if (field.value.type.enum_def) { code += "?.value"; } + code += ");\n"; + } else if (IsStruct(field.value.type)) { + code += " int add" + MakeCamel(field.name) + "(int offset) {\n"; + code += + " fbBuilder.addStruct(" + NumToString(offset) + ", offset);\n"; + } else { + code += " int add" + MakeCamel(field.name) + "Offset(int offset) {\n"; + code += + " fbBuilder.addOffset(" + NumToString(offset) + ", offset);\n"; + } + code += " return fbBuilder.offset;\n"; + code += " }\n"; + } + + code += "\n"; + code += " int finish() {\n"; + code += " return fbBuilder.endTable();\n"; + code += " }\n"; + } + + void GenObjectBuilder(const StructDef &struct_def, + std::string *builder_name_ptr, std::string *code_ptr) { + auto &code = *code_ptr; + auto &builder_name = *builder_name_ptr; + + code += "class " + builder_name + " extends " + _kFb + ".ObjectBuilder {\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + code += " final " + + GenDartTypeName(field.value.type, struct_def.defined_namespace, + field, true) + + " _" + MakeCamel(field.name, false) + ";\n"; + } + code += "\n"; + code += " " + builder_name + "("; + if (struct_def.fields.vec.size() != 0) { + code += + + "{\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + code += " " + + GenDartTypeName(field.value.type, struct_def.defined_namespace, + field, true) + + " " + MakeCamel(field.name, false) + ",\n"; + } + code += " })\n"; + code += " : "; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + code += "_" + MakeCamel(field.name, false) + " = " + + MakeCamel(field.name, false); + if (it == struct_def.fields.vec.end() - 1) { + code += ";\n\n"; + } else { + code += ",\n "; + } + } + } else { + code += ");\n\n"; + } + + code += " /// Finish building, and store into the [fbBuilder].\n"; + code += " @override\n"; + code += " int finish(\n"; + code += " " + _kFb + ".Builder fbBuilder) {\n"; + code += " assert(fbBuilder != null);\n"; + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type)) + continue; + + code += " final int " + MakeCamel(field.name, false) + "Offset"; + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + code += + " = _" + MakeCamel(field.name, false) + "?.isNotEmpty == true\n"; + code += " ? fbBuilder.writeList"; + switch (field.value.type.VectorType().base_type) { + case BASE_TYPE_STRING: + code += "(_" + MakeCamel(field.name, false) + + ".map((b) => fbBuilder.writeString(b)).toList())"; + break; + case BASE_TYPE_STRUCT: + if (field.value.type.struct_def->fixed) { + code += "OfStructs(_" + MakeCamel(field.name, false) + ")"; + } else { + code += "(_" + MakeCamel(field.name, false) + + ".map((b) => b.getOrCreateOffset(fbBuilder)).toList())"; + } + break; + default: + code += GenType(field.value.type.VectorType()) + "(_" + + MakeCamel(field.name, false); + if (field.value.type.enum_def) { code += ".map((f) => f.value)"; } + code += ")"; + } + code += "\n : null;\n"; + } else if (field.value.type.base_type == BASE_TYPE_STRING) { + code += " = fbBuilder.writeString(_" + field.name + ");\n"; + } else { + code += " = _" + MakeCamel(field.name, false) + + "?.getOrCreateOffset(fbBuilder);\n"; + } + } + + code += "\n"; + if (struct_def.fixed) { + StructObjectBuilderBody(struct_def, code_ptr); + } else { + TableObjectBuilderBody(struct_def, code_ptr); + } + code += " }\n\n"; + + code += " /// Convenience method to serialize to byte list.\n"; + code += " @override\n"; + code += " Uint8List toBytes([String fileIdentifier]) {\n"; + code += " " + _kFb + ".Builder fbBuilder = new "; + code += _kFb + ".Builder();\n"; + code += " int offset = finish(fbBuilder);\n"; + code += " return fbBuilder.finish(offset, fileIdentifier);\n"; + code += " }\n"; + code += "}\n"; + } + + void StructObjectBuilderBody(const StructDef &struct_def, + std::string *code_ptr, + bool prependUnderscore = true) { + auto &code = *code_ptr; + + for (auto it = struct_def.fields.vec.rbegin(); + it != struct_def.fields.vec.rend(); ++it) { + auto &field = **it; + + if (field.deprecated) continue; + + if (field.padding) { + code += " fbBuilder.pad(" + NumToString(field.padding) + ");\n"; + } + + if (IsStruct(field.value.type)) { + code += " "; + if (prependUnderscore) { code += "_"; } + code += field.name + ".finish(fbBuilder);\n"; + } else { + code += " fbBuilder.put" + GenType(field.value.type) + "("; + if (prependUnderscore) { code += "_"; } + code += field.name; + if (field.value.type.enum_def) { code += "?.value"; } + code += ");\n"; + } + } + + code += " return fbBuilder.offset;\n"; + } + + void TableObjectBuilderBody(const StructDef &struct_def, + std::string *code_ptr, + bool prependUnderscore = true) { + std::string &code = *code_ptr; + code += " fbBuilder.startTable();\n"; + + 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(); + if (IsScalar(field.value.type.base_type)) { + code += " fbBuilder.add" + GenType(field.value.type) + "(" + + NumToString(offset) + ", "; + if (prependUnderscore) { code += "_"; } + code += MakeCamel(field.name, false); + if (field.value.type.enum_def) { code += "?.value"; } + code += ");\n"; + } else if (IsStruct(field.value.type)) { + code += " if ("; + if (prependUnderscore) { code += "_"; } + code += MakeCamel(field.name, false) + " != null) {\n"; + code += " fbBuilder.addStruct(" + NumToString(offset) + ", "; + code += "_" + MakeCamel(field.name, false) + ".finish(fbBuilder));\n"; + code += " }\n"; + } else { + code += + " if (" + MakeCamel(field.name, false) + "Offset != null) {\n"; + code += " fbBuilder.addOffset(" + NumToString(offset) + ", " + + MakeCamel(field.name, false) + "Offset);\n"; + code += " }\n"; + } + } + code += " return fbBuilder.endTable();\n"; + } +}; +} // namespace dart + +bool GenerateDart(const Parser &parser, const std::string &path, + const std::string &file_name) { + dart::DartGenerator generator(parser, path, file_name); + return generator.generate(); +} + +std::string DartMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name) { + assert(parser.opts.lang <= IDLOptions::kMAX); + + auto filebase = + flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); + auto make_rule = GeneratedFileName(path, filebase) + ": "; + + auto included_files = parser.GetIncludedFilesRecursive(file_name); + for (auto it = included_files.begin(); it != included_files.end(); ++it) { + make_rule += " " + *it; + } + return make_rule; +} + +} // namespace flatbuffers diff --git a/tests/DartTest.sh b/tests/DartTest.sh new file mode 100755 index 00000000..1ec5ce4c --- /dev/null +++ b/tests/DartTest.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 2016 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. + +pushd "$(dirname $0)" >/dev/null + +command -v pub >/dev/null 2>&1 || { echo >&2 "Dart tests require `pub` but it's not installed. Aborting."; exit 1; } +command -v dart >/dev/null 2>&1 || { echo >&2 "Dart tests require dart to be in path but it's not installed. Aborting."; exit 1; } +# output required files to the dart folder so that pub will be able to +# distrubte them and more people can more easily run the dart tests +../flatc --dart -I include_test -o ../dart/test monster_test.fbs +cp monsterdata_test.mon ../dart/test + +cd ../dart + +# update packages +pub get +# Execute the sample. +dart test/flat_buffers_test.dart + +# cleanup +rm ../dart/test/monsterdata_test.mon
\ No newline at end of file diff --git a/tests/TestAll.sh b/tests/TestAll.sh index 463eb738..d2de3e90 100644 --- a/tests/TestAll.sh +++ b/tests/TestAll.sh @@ -36,6 +36,10 @@ echo "************************ PHP:" php phpTest.php sh phpUnionVectorTest.sh +echo "************************ Dart:" + +sh DartTest.sh + echo "************************ C:" echo "(in a different repo)" diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 4b6db29b..c5304b69 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -14,8 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -../flatc --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json -../flatc --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs +../flatc --cpp --java --csharp --dart --go --binary --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json +../flatc --cpp --java --csharp --dart --go --binary --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs ../flatc -b --schema --bfbs-comments -I include_test monster_test.fbs ../flatc --jsonschema --schema -I include_test monster_test.fbs |