Dart - unpack() must use eager list reader (#6723)

This commit is contained in:
Ivan Dlugos
2021-07-09 18:55:34 +02:00
committed by GitHub
parent 838c93b843
commit 3f77dc9a0e
6 changed files with 98 additions and 47 deletions

View File

@@ -907,20 +907,34 @@ class Int8Reader extends Reader<int> {
int read(BufferContext bc, int offset) => bc._getInt8(offset); int read(BufferContext bc, int offset) => bc._getInt8(offset);
} }
/// The reader of lists of objects. /// The reader of lists of objects. Lazy by default - see [lazy].
///
/// The returned unmodifiable lists lazily read objects on access.
class ListReader<E> extends Reader<List<E>> { class ListReader<E> extends Reader<List<E>> {
final Reader<E> _elementReader; final Reader<E> _elementReader;
const ListReader(this._elementReader); /// Enables lazy reading of the list
///
/// If true, the returned unmodifiable list lazily reads objects on access.
/// Therefore, the underlying buffer must not change while accessing the list.
///
/// If false, reads the whole list immediately on access.
final bool lazy;
const ListReader(this._elementReader, {this.lazy = true});
@override @override
int get size => _sizeofUint32; int get size => _sizeofUint32;
@override @override
List<E> read(BufferContext bc, int offset) => List<E> read(BufferContext bc, int offset) {
new _FbGenericList<E>(_elementReader, bc, bc.derefObject(offset)); final listOffset = bc.derefObject(offset);
return lazy
? _FbGenericList<E>(_elementReader, bc, listOffset)
: List<E>.generate(
bc.buffer.getUint32(listOffset, Endian.little),
(int index) => _elementReader.read(
bc, listOffset + size + _elementReader.size * index),
growable: true);
}
} }
/// Object that can read a value at a [BufferContext]. /// Object that can read a value at a [BufferContext].

View File

@@ -763,6 +763,28 @@ class ObjectAPITest {
// final monster3 = monster2.unpack(); // MonsterT // final monster3 = monster2.unpack(); // MonsterT
// expect(monster3.toString(), monster.toString()); // expect(monster3.toString(), monster.toString());
} }
void test_Lists() {
// Ensure unpack() reads lists eagerly by reusing the same builder and
// overwriting data. Why: because standard reader reads lists lazily...
final fbb = Builder();
final object1 = example.TypeAliasesT(v8: [1, 2, 3], vf64: [5, 6]);
final data1 = fbb.finish(object1.pack(fbb));
final object1Read = example.TypeAliases(data1).unpack();
// overwrite the original buffer by writing to the same builder
fbb.reset();
final object2 = example.TypeAliasesT(v8: [7, 8, 9], vf64: [10, 11]);
final data2 = fbb.finish(object2.pack(fbb));
final object2Read = example.TypeAliases(data2).unpack();
// this is fine even with lazy lists:
expect(object2.toString(), object2Read.toString());
// this fails with lazy lists:
expect(object1.toString(), object1Read.toString());
}
} }
class StringListWrapperImpl { class StringListWrapperImpl {

View File

@@ -1117,15 +1117,15 @@ class Monster {
mana: mana, mana: mana,
hp: hp, hp: hp,
name: name, name: name,
inventory: inventory, inventory: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 14),
color: color, color: color,
testType: testType, testType: testType,
test: test, test: test,
test4: test4?.map((e) => e.unpack()).toList(), test4: test4?.map((e) => e.unpack()).toList(),
testarrayofstring: testarrayofstring, testarrayofstring: const fb.ListReader<String>(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24),
testarrayoftables: testarrayoftables?.map((e) => e.unpack()).toList(), testarrayoftables: testarrayoftables?.map((e) => e.unpack()).toList(),
enemy: enemy?.unpack(), enemy: enemy?.unpack(),
testnestedflatbuffer: testnestedflatbuffer, testnestedflatbuffer: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 30),
testempty: testempty?.unpack(), testempty: testempty?.unpack(),
testbool: testbool, testbool: testbool,
testhashs32Fnv1: testhashs32Fnv1, testhashs32Fnv1: testhashs32Fnv1,
@@ -1136,32 +1136,32 @@ class Monster {
testhashu32Fnv1a: testhashu32Fnv1a, testhashu32Fnv1a: testhashu32Fnv1a,
testhashs64Fnv1a: testhashs64Fnv1a, testhashs64Fnv1a: testhashs64Fnv1a,
testhashu64Fnv1a: testhashu64Fnv1a, testhashu64Fnv1a: testhashu64Fnv1a,
testarrayofbools: testarrayofbools, testarrayofbools: const fb.ListReader<bool>(const fb.BoolReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 52),
testf: testf, testf: testf,
testf2: testf2, testf2: testf2,
testf3: testf3, testf3: testf3,
testarrayofstring2: testarrayofstring2, testarrayofstring2: const fb.ListReader<String>(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 60),
testarrayofsortedstruct: testarrayofsortedstruct?.map((e) => e.unpack()).toList(), testarrayofsortedstruct: testarrayofsortedstruct?.map((e) => e.unpack()).toList(),
flex: flex, flex: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 64),
test5: test5?.map((e) => e.unpack()).toList(), test5: test5?.map((e) => e.unpack()).toList(),
vectorOfLongs: vectorOfLongs, vectorOfLongs: const fb.ListReader<int>(const fb.Int64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 68),
vectorOfDoubles: vectorOfDoubles, vectorOfDoubles: const fb.ListReader<double>(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 70),
parentNamespaceTest: parentNamespaceTest?.unpack(), parentNamespaceTest: parentNamespaceTest?.unpack(),
vectorOfReferrables: vectorOfReferrables?.map((e) => e.unpack()).toList(), vectorOfReferrables: vectorOfReferrables?.map((e) => e.unpack()).toList(),
singleWeakReference: singleWeakReference, singleWeakReference: singleWeakReference,
vectorOfWeakReferences: vectorOfWeakReferences, vectorOfWeakReferences: const fb.ListReader<int>(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 78),
vectorOfStrongReferrables: vectorOfStrongReferrables?.map((e) => e.unpack()).toList(), vectorOfStrongReferrables: vectorOfStrongReferrables?.map((e) => e.unpack()).toList(),
coOwningReference: coOwningReference, coOwningReference: coOwningReference,
vectorOfCoOwningReferences: vectorOfCoOwningReferences, vectorOfCoOwningReferences: const fb.ListReader<int>(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 84),
nonOwningReference: nonOwningReference, nonOwningReference: nonOwningReference,
vectorOfNonOwningReferences: vectorOfNonOwningReferences, vectorOfNonOwningReferences: const fb.ListReader<int>(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 88),
anyUniqueType: anyUniqueType, anyUniqueType: anyUniqueType,
anyUnique: anyUnique, anyUnique: anyUnique,
anyAmbiguousType: anyAmbiguousType, anyAmbiguousType: anyAmbiguousType,
anyAmbiguous: anyAmbiguous, anyAmbiguous: anyAmbiguous,
vectorOfEnums: vectorOfEnums, vectorOfEnums: const fb.ListReader<Color>(Color.reader, lazy: false).vTableGetNullable(_bc, _bcOffset, 98),
signedEnum: signedEnum, signedEnum: signedEnum,
testrequirednestedflatbuffer: testrequirednestedflatbuffer, testrequirednestedflatbuffer: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 102),
scalarKeySortedTables: scalarKeySortedTables?.map((e) => e.unpack()).toList()); scalarKeySortedTables: scalarKeySortedTables?.map((e) => e.unpack()).toList());
static int pack(fb.Builder fbBuilder, MonsterT? object) { static int pack(fb.Builder fbBuilder, MonsterT? object) {
@@ -1966,8 +1966,8 @@ class TypeAliases {
u64: u64, u64: u64,
f32: f32, f32: f32,
f64: f64, f64: f64,
v8: v8, v8: const fb.ListReader<int>(const fb.Int8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24),
vf64: vf64); vf64: const fb.ListReader<double>(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 26));
static int pack(fb.Builder fbBuilder, TypeAliasesT? object) { static int pack(fb.Builder fbBuilder, TypeAliasesT? object) {
if (object == null) return 0; if (object == null) return 0;

View File

@@ -330,7 +330,8 @@ class DartGenerator : public BaseGenerator {
std::string GenReaderTypeName(const Type &type, Namespace *current_namespace, std::string GenReaderTypeName(const Type &type, Namespace *current_namespace,
const FieldDef &def, const FieldDef &def,
bool parent_is_vector = false) { bool parent_is_vector = false,
bool lazy = true) {
if (type.base_type == BASE_TYPE_BOOL) { if (type.base_type == BASE_TYPE_BOOL) {
return "const " + _kFb + ".BoolReader()"; return "const " + _kFb + ".BoolReader()";
} else if (IsVector(type)) { } else if (IsVector(type)) {
@@ -338,7 +339,7 @@ class DartGenerator : public BaseGenerator {
GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" + GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" +
GenReaderTypeName(type.VectorType(), current_namespace, def, GenReaderTypeName(type.VectorType(), current_namespace, def,
true) + true) +
")"; (lazy ? ")" : ", lazy: false)");
} else if (IsString(type)) { } else if (IsString(type)) {
return "const " + _kFb + ".StringReader()"; return "const " + _kFb + ".StringReader()";
} }
@@ -556,18 +557,32 @@ class DartGenerator : public BaseGenerator {
std::string field_name = MakeCamel(field.name, false); std::string field_name = MakeCamel(field.name, false);
if (!constructor_args.empty()) constructor_args += ",\n"; if (!constructor_args.empty()) constructor_args += ",\n";
constructor_args += " " + field_name + ": " + field_name; constructor_args += " " + field_name + ": ";
const Type &type = field.value.type; const Type &type = field.value.type;
bool isNullable = std::string defaultValue = getDefaultValue(field.value);
getDefaultValue(field.value).empty() && !struct_def.fixed; bool isNullable = defaultValue.empty() && !struct_def.fixed;
std::string nullableValueAccessOperator = isNullable ? "?" : ""; std::string nullableValueAccessOperator = isNullable ? "?" : "";
if (type.base_type == BASE_TYPE_STRUCT) { if (type.base_type == BASE_TYPE_STRUCT) {
constructor_args += nullableValueAccessOperator + ".unpack()";
} else if (type.base_type == BASE_TYPE_VECTOR &&
type.VectorType().base_type == BASE_TYPE_STRUCT) {
constructor_args += constructor_args +=
nullableValueAccessOperator + ".map((e) => e.unpack()).toList()"; field_name + nullableValueAccessOperator + ".unpack()";
} else if (type.base_type == BASE_TYPE_VECTOR) {
if (type.VectorType().base_type == BASE_TYPE_STRUCT) {
constructor_args += field_name + nullableValueAccessOperator +
".map((e) => e.unpack()).toList()";
} else {
constructor_args +=
GenReaderTypeName(field.value.type, struct_def.defined_namespace,
field, false, false);
constructor_args += ".vTableGet";
std::string offset = NumToString(field.value.offset);
constructor_args +=
isNullable
? "Nullable(_bc, _bcOffset, " + offset + ")"
: "(_bc, _bcOffset, " + offset + ", " + defaultValue + ")";
}
} else {
constructor_args += field_name;
} }
} }

View File

@@ -44,8 +44,8 @@ class MonsterExtra {
f1: f1, f1: f1,
f2: f2, f2: f2,
f3: f3, f3: f3,
dvec: dvec, dvec: const fb.ListReader<double>(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 20),
fvec: fvec); fvec: const fb.ListReader<double>(const fb.Float32Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 22));
static int pack(fb.Builder fbBuilder, MonsterExtraT? object) { static int pack(fb.Builder fbBuilder, MonsterExtraT? object) {
if (object == null) return 0; if (object == null) return 0;

View File

@@ -1117,15 +1117,15 @@ class Monster {
mana: mana, mana: mana,
hp: hp, hp: hp,
name: name, name: name,
inventory: inventory, inventory: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 14),
color: color, color: color,
testType: testType, testType: testType,
test: test, test: test,
test4: test4?.map((e) => e.unpack()).toList(), test4: test4?.map((e) => e.unpack()).toList(),
testarrayofstring: testarrayofstring, testarrayofstring: const fb.ListReader<String>(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24),
testarrayoftables: testarrayoftables?.map((e) => e.unpack()).toList(), testarrayoftables: testarrayoftables?.map((e) => e.unpack()).toList(),
enemy: enemy?.unpack(), enemy: enemy?.unpack(),
testnestedflatbuffer: testnestedflatbuffer, testnestedflatbuffer: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 30),
testempty: testempty?.unpack(), testempty: testempty?.unpack(),
testbool: testbool, testbool: testbool,
testhashs32Fnv1: testhashs32Fnv1, testhashs32Fnv1: testhashs32Fnv1,
@@ -1136,32 +1136,32 @@ class Monster {
testhashu32Fnv1a: testhashu32Fnv1a, testhashu32Fnv1a: testhashu32Fnv1a,
testhashs64Fnv1a: testhashs64Fnv1a, testhashs64Fnv1a: testhashs64Fnv1a,
testhashu64Fnv1a: testhashu64Fnv1a, testhashu64Fnv1a: testhashu64Fnv1a,
testarrayofbools: testarrayofbools, testarrayofbools: const fb.ListReader<bool>(const fb.BoolReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 52),
testf: testf, testf: testf,
testf2: testf2, testf2: testf2,
testf3: testf3, testf3: testf3,
testarrayofstring2: testarrayofstring2, testarrayofstring2: const fb.ListReader<String>(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 60),
testarrayofsortedstruct: testarrayofsortedstruct?.map((e) => e.unpack()).toList(), testarrayofsortedstruct: testarrayofsortedstruct?.map((e) => e.unpack()).toList(),
flex: flex, flex: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 64),
test5: test5?.map((e) => e.unpack()).toList(), test5: test5?.map((e) => e.unpack()).toList(),
vectorOfLongs: vectorOfLongs, vectorOfLongs: const fb.ListReader<int>(const fb.Int64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 68),
vectorOfDoubles: vectorOfDoubles, vectorOfDoubles: const fb.ListReader<double>(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 70),
parentNamespaceTest: parentNamespaceTest?.unpack(), parentNamespaceTest: parentNamespaceTest?.unpack(),
vectorOfReferrables: vectorOfReferrables?.map((e) => e.unpack()).toList(), vectorOfReferrables: vectorOfReferrables?.map((e) => e.unpack()).toList(),
singleWeakReference: singleWeakReference, singleWeakReference: singleWeakReference,
vectorOfWeakReferences: vectorOfWeakReferences, vectorOfWeakReferences: const fb.ListReader<int>(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 78),
vectorOfStrongReferrables: vectorOfStrongReferrables?.map((e) => e.unpack()).toList(), vectorOfStrongReferrables: vectorOfStrongReferrables?.map((e) => e.unpack()).toList(),
coOwningReference: coOwningReference, coOwningReference: coOwningReference,
vectorOfCoOwningReferences: vectorOfCoOwningReferences, vectorOfCoOwningReferences: const fb.ListReader<int>(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 84),
nonOwningReference: nonOwningReference, nonOwningReference: nonOwningReference,
vectorOfNonOwningReferences: vectorOfNonOwningReferences, vectorOfNonOwningReferences: const fb.ListReader<int>(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 88),
anyUniqueType: anyUniqueType, anyUniqueType: anyUniqueType,
anyUnique: anyUnique, anyUnique: anyUnique,
anyAmbiguousType: anyAmbiguousType, anyAmbiguousType: anyAmbiguousType,
anyAmbiguous: anyAmbiguous, anyAmbiguous: anyAmbiguous,
vectorOfEnums: vectorOfEnums, vectorOfEnums: const fb.ListReader<Color>(Color.reader, lazy: false).vTableGetNullable(_bc, _bcOffset, 98),
signedEnum: signedEnum, signedEnum: signedEnum,
testrequirednestedflatbuffer: testrequirednestedflatbuffer, testrequirednestedflatbuffer: const fb.ListReader<int>(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 102),
scalarKeySortedTables: scalarKeySortedTables?.map((e) => e.unpack()).toList()); scalarKeySortedTables: scalarKeySortedTables?.map((e) => e.unpack()).toList());
static int pack(fb.Builder fbBuilder, MonsterT? object) { static int pack(fb.Builder fbBuilder, MonsterT? object) {
@@ -1966,8 +1966,8 @@ class TypeAliases {
u64: u64, u64: u64,
f32: f32, f32: f32,
f64: f64, f64: f64,
v8: v8, v8: const fb.ListReader<int>(const fb.Int8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24),
vf64: vf64); vf64: const fb.ListReader<double>(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 26));
static int pack(fb.Builder fbBuilder, TypeAliasesT? object) { static int pack(fb.Builder fbBuilder, TypeAliasesT? object) {
if (object == null) return 0; if (object == null) return 0;