forked from BigfootDev/flatbuffers
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
This commit is contained in:
committed by
Wouter van Oortmerssen
parent
c43a0beff0
commit
88912640d0
7
.gitignore
vendored
7
.gitignore
vendored
@@ -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/
|
||||
|
||||
1
BUILD
1
BUILD
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
233
dart/LICENSE
Normal file
233
dart/LICENSE
Normal file
@@ -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.
|
||||
13
dart/README.md
Normal file
13
dart/README.md
Normal file
@@ -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)
|
||||
155
dart/example/example.dart
Normal file
155
dart/example/example.dart
Normal file
@@ -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;
|
||||
}
|
||||
440
dart/example/monster_my_game.sample_generated.dart
Normal file
440
dart/example/monster_my_game.sample_generated.dart
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
1241
dart/lib/flat_buffers.dart
Normal file
1241
dart/lib/flat_buffers.dart
Normal file
File diff suppressed because it is too large
Load Diff
28
dart/publish.sh
Executable file
28
dart/publish.sh
Executable file
@@ -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
|
||||
18
dart/pubspec.yaml
Normal file
18
dart/pubspec.yaml
Normal file
@@ -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
|
||||
573
dart/test/flat_buffers_test.dart
Normal file
573
dart/test/flat_buffers_test.dart
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
60
dart/test/monster_test_my_game.example2_generated.dart
Normal file
60
dart/test/monster_test_my_game.example2_generated.dart
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
1332
dart/test/monster_test_my_game.example_generated.dart
Normal file
1332
dart/test/monster_test_my_game.example_generated.dart
Normal file
File diff suppressed because it is too large
Load Diff
60
dart/test/monster_test_my_game_generated.dart
Normal file
60
dart/test/monster_test_my_game_generated.dart
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
108
docs/source/DartUsage.md
Normal file
108
docs/source/DartUsage.md
Normal file
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -751,6 +751,7 @@ INPUT = "FlatBuffers.md" \
|
||||
"Schemas.md" \
|
||||
"CppUsage.md" \
|
||||
"CUsage.md" \
|
||||
"DartUsage.md" \
|
||||
"GoUsage.md" \
|
||||
"JavaCsharpUsage.md" \
|
||||
"JavaScriptUsage.md" \
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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,
|
||||
|
||||
52
samples/dart_sample.sh
Executable file
52
samples/dart_sample.sh
Executable file
@@ -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
|
||||
@@ -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 },
|
||||
|
||||
890
src/idl_gen_dart.cpp
Normal file
890
src/idl_gen_dart.cpp
Normal file
@@ -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
|
||||
34
tests/DartTest.sh
Executable file
34
tests/DartTest.sh
Executable file
@@ -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
|
||||
@@ -36,6 +36,10 @@ echo "************************ PHP:"
|
||||
php phpTest.php
|
||||
sh phpUnionVectorTest.sh
|
||||
|
||||
echo "************************ Dart:"
|
||||
|
||||
sh DartTest.sh
|
||||
|
||||
echo "************************ C:"
|
||||
|
||||
echo "(in a different repo)"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user