Skip to content

Commit 06f49b8

Browse files
committed
Add support for advanced union features
1 parent 95053e6 commit 06f49b8

File tree

9 files changed

+1748
-103
lines changed

9 files changed

+1748
-103
lines changed

dart/lib/flat_buffers.dart

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,42 @@ class ListReader<E> extends Reader<List<E>> {
10221022
}
10231023
}
10241024

1025+
/// The reader of lists of objects. Lazy by default - see [lazy].
1026+
class UnionListReader<E> extends Reader<List<E?>> {
1027+
final Reader<E>? Function(int index) _getElementReader;
1028+
1029+
/// Enables lazy reading of the list
1030+
///
1031+
/// If true, the returned unmodifiable list lazily reads objects on access.
1032+
/// Therefore, the underlying buffer must not change while accessing the list.
1033+
///
1034+
/// If false, reads the whole list immediately on access.
1035+
final bool lazy;
1036+
1037+
const UnionListReader(this._getElementReader, {this.lazy = true});
1038+
1039+
@override
1040+
@pragma('vm:prefer-inline')
1041+
int get size => _sizeofUint32;
1042+
1043+
@override
1044+
List<E?> read(BufferContext bc, int offset) {
1045+
final listOffset = bc.derefObject(offset);
1046+
return lazy
1047+
? _FbUnionList<E>(_getElementReader, bc, listOffset)
1048+
: List<E?>.generate(
1049+
bc.buffer.getUint32(listOffset, Endian.little),
1050+
(int index) {
1051+
final reader = _getElementReader(index);
1052+
if (reader == null) return null;
1053+
int offset = listOffset + size + _sizeofUint32 * index;
1054+
return reader.read(bc, offset);
1055+
},
1056+
growable: true,
1057+
);
1058+
}
1059+
}
1060+
10251061
/// Object that can read a value at a [BufferContext].
10261062
abstract class Reader<T> {
10271063
const Reader();
@@ -1121,6 +1157,25 @@ abstract class TableReader<T> extends Reader<T> {
11211157
}
11221158
}
11231159

1160+
/// A reader that wraps another reader if the type is a union.
1161+
///
1162+
/// This is useful for reading unions that can be stucts and need an extra
1163+
/// derefObject call.
1164+
class UnionReader<T> extends Reader<T?> {
1165+
final Reader<T>? reader;
1166+
1167+
const UnionReader(this.reader);
1168+
1169+
@override
1170+
int get size => 4;
1171+
1172+
@override
1173+
T? read(BufferContext bc, int offset) {
1174+
if (reader is StructReader) offset = bc.derefObject(offset);
1175+
return reader?.read(bc, offset);
1176+
}
1177+
}
1178+
11241179
/// Reader of lists of unsigned 32-bit integer values.
11251180
///
11261181
/// The returned unmodifiable lists lazily read values on access.
@@ -1311,6 +1366,29 @@ class _FbGenericList<E> extends _FbList<E> {
13111366
}
13121367
}
13131368

1369+
/// Lazy list of union objects
1370+
class _FbUnionList<E> extends _FbList<E?> {
1371+
final Reader<E>? Function(int index) getElementReader;
1372+
1373+
List<E?>? _items;
1374+
1375+
_FbUnionList(this.getElementReader, BufferContext bp, int offset)
1376+
: super(bp, offset);
1377+
1378+
@override
1379+
@pragma('vm:prefer-inline')
1380+
E? operator [](int i) {
1381+
_items ??= List<E?>.filled(length, null);
1382+
var item = _items![i];
1383+
final reader = getElementReader(i);
1384+
if (item == null && reader != null) {
1385+
item = reader.read(bc, offset + 4 + 4 * i);
1386+
_items![i] = item;
1387+
}
1388+
return item!;
1389+
}
1390+
}
1391+
13141392
/// The base class for immutable lists read from flat buffers.
13151393
abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> {
13161394
final BufferContext bc;

dart/test/flat_buffers_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import './bool_structs_generated.dart' as example4;
1010
import './monster_test_my_game.example2_generated.dart' as example2;
1111
import './monster_test_my_game.example_generated.dart' as example;
1212
import 'enums_generated.dart' as example3;
13+
import 'union_vector_generated.dart' as example5;
1314

1415
main() {
1516
defineReflectiveSuite(() {
@@ -18,6 +19,7 @@ main() {
1819
defineReflectiveTests(CheckOtherLangaugesData);
1920
defineReflectiveTests(GeneratorTest);
2021
defineReflectiveTests(ListOfEnumsTest);
22+
defineReflectiveTests(UnionVectorTest);
2123
});
2224
}
2325

@@ -936,6 +938,46 @@ class ObjectAPITest {
936938
}
937939
}
938940

941+
@reflectiveTest
942+
class UnionVectorTest {
943+
void test_unionVector() {
944+
final movie = example5.MovieT(
945+
mainCharacterType: example5.CharacterTypeId.Rapunzel,
946+
mainCharacter: example5.RapunzelT(hairLength: 42),
947+
charactersType: [
948+
example5.CharacterTypeId.MuLan,
949+
example5.CharacterTypeId.Rapunzel,
950+
example5.CharacterTypeId.Belle,
951+
example5.CharacterTypeId.BookFan,
952+
example5.CharacterTypeId.Other,
953+
example5.CharacterTypeId.Unused,
954+
],
955+
characters: [
956+
example5.AttackerT(swordAttackDamage: 10),
957+
example5.RapunzelT(hairLength: 203),
958+
example5.BookReaderT(booksRead: 21),
959+
example5.BookReaderT(booksRead: 500),
960+
"Hello",
961+
"World",
962+
],
963+
);
964+
965+
final fbb = Builder();
966+
fbb.finish(movie.pack(fbb));
967+
final bytes = fbb.buffer;
968+
969+
final movie2 = example5.Movie(bytes);
970+
expect(
971+
movie2.toString().replaceAllMapped(
972+
RegExp('([a-zA-Z0-9]+){'), (match) => match.group(1)! + 'T{'),
973+
movie.toString(),
974+
);
975+
976+
final movie3 = movie2.unpack();
977+
expect(movie3.toString(), movie.toString());
978+
}
979+
}
980+
939981
class StringListWrapperImpl {
940982
final BufferContext bp;
941983
final int offset;

dart/test/keyword_test_keyword_test_generated.dart

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,18 @@ class _KeywordsInUnionTypeIdReader extends fb.Reader<KeywordsInUnionTypeId> {
111111
KeywordsInUnionTypeId.fromValue(const fb.Uint8Reader().read(bc, offset));
112112
}
113113

114+
class _KeywordsInUnionReader extends fb.UnionReader {
115+
_KeywordsInUnionReader(KeywordsInUnionTypeId? type) : super(_get(type));
116+
117+
static fb.Reader? _get(KeywordsInUnionTypeId? type) {
118+
switch (type?.value) {
119+
case 1: return KeywordsInTable.reader;
120+
case 2: return KeywordsInTable.reader;
121+
default: return null;
122+
}
123+
}
124+
}
125+
114126
class KeywordsInTable {
115127
KeywordsInTable._(this._bc, this._bcOffset);
116128
factory KeywordsInTable(List<int> bytes) {
@@ -261,13 +273,7 @@ class Table2 {
261273
final int _bcOffset;
262274

263275
KeywordsInUnionTypeId? get typeType => KeywordsInUnionTypeId._createOrNull(const fb.Uint8Reader().vTableGetNullable(_bc, _bcOffset, 4));
264-
dynamic get type {
265-
switch (typeType?.value) {
266-
case 1: return KeywordsInTable.reader.vTableGetNullable(_bc, _bcOffset, 6);
267-
case 2: return KeywordsInTable.reader.vTableGetNullable(_bc, _bcOffset, 6);
268-
default: return null;
269-
}
270-
}
276+
dynamic get type => _KeywordsInUnionReader(typeType).vTableGetNullable(_bc, _bcOffset, 6);
271277

272278
@override
273279
String toString() {
@@ -276,7 +282,7 @@ class Table2 {
276282

277283
Table2T unpack() => Table2T(
278284
typeType: typeType,
279-
type: type);
285+
type: type is String ? type : type?.unpack());
280286

281287
static int pack(fb.Builder fbBuilder, Table2T? object) {
282288
if (object == null) return 0;
@@ -294,7 +300,7 @@ class Table2T implements fb.Packable {
294300

295301
@override
296302
int pack(fb.Builder fbBuilder) {
297-
final int? typeOffset = type?.pack(fbBuilder);
303+
final int? typeOffset = type is String ? fbBuilder.writeString(type) : type?.pack(fbBuilder);
298304
fbBuilder.startTable(2);
299305
fbBuilder.addUint8(0, typeType?.value);
300306
fbBuilder.addOffset(1, typeOffset);
@@ -352,7 +358,7 @@ class Table2ObjectBuilder extends fb.ObjectBuilder {
352358
/// Finish building, and store into the [fbBuilder].
353359
@override
354360
int finish(fb.Builder fbBuilder) {
355-
final int? typeOffset = _type?.getOrCreateOffset(fbBuilder);
361+
final int? typeOffset = _type is String ? fbBuilder.writeString(_type) : _type?.getOrCreateOffset(fbBuilder);
356362
fbBuilder.startTable(2);
357363
fbBuilder.addUint8(0, _typeType?.value);
358364
fbBuilder.addOffset(1, typeOffset);

0 commit comments

Comments
 (0)