Skip to content
This repository has been archived by the owner on Feb 4, 2025. It is now read-only.

Commit

Permalink
Extend typed_map to allow 9-16 fields. (#113)
Browse files Browse the repository at this point in the history
* Extend typed_map to allow 9-16 fields.

* Address review comments.
  • Loading branch information
davidmorgan authored Oct 24, 2024
1 parent 0a2df87 commit 4d3f286
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 30 deletions.
134 changes: 104 additions & 30 deletions pkgs/dart_model/lib/src/json_buffer/typed_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,24 @@ class TypedMapSchema {
/// [Type#_sizeInBytes] for all present values.
///
/// Additionally returns a [bool]: whether the map is filled.
(int, bool) _valueSizeOf(
[Object? v0,
Object? v1,
Object? v2,
Object? v3,
Object? v4,
Object? v5,
Object? v6,
Object? v7]) {
(int, bool) _valueSizeOf([
Object? v0,
Object? v1,
Object? v2,
Object? v3,
Object? v4,
Object? v5,
Object? v6,
Object? v7,
Object? v8,
Object? v9,
Object? v10,
Object? v11,
Object? v12,
Object? v13,
Object? v14,
Object? v15,
]) {
if (_isAllBooleans) {
// All fields take up one bit, count then compute how many bytes.
var bits = 0;
Expand All @@ -91,6 +100,14 @@ class TypedMapSchema {
if (v5 != null) ++bits;
if (v6 != null) ++bits;
if (v7 != null) ++bits;
if (v8 != null) ++bits;
if (v9 != null) ++bits;
if (v10 != null) ++bits;
if (v11 != null) ++bits;
if (v12 != null) ++bits;
if (v13 != null) ++bits;
if (v14 != null) ++bits;
if (v15 != null) ++bits;
return ((bits + 7) ~/ 8, bits == length);
} else {
// Sum the sizes of present values.
Expand All @@ -103,6 +120,14 @@ class TypedMapSchema {
if (v5 != null) result += _valueTypes[5]._sizeInBytes;
if (v6 != null) result += _valueTypes[6]._sizeInBytes;
if (v7 != null) result += _valueTypes[7]._sizeInBytes;
if (v8 != null) result += _valueTypes[8]._sizeInBytes;
if (v9 != null) result += _valueTypes[9]._sizeInBytes;
if (v10 != null) result += _valueTypes[10]._sizeInBytes;
if (v11 != null) result += _valueTypes[11]._sizeInBytes;
if (v12 != null) result += _valueTypes[12]._sizeInBytes;
if (v13 != null) result += _valueTypes[13]._sizeInBytes;
if (v14 != null) result += _valueTypes[14]._sizeInBytes;
if (v15 != null) result += _valueTypes[15]._sizeInBytes;
return (result, result == _filledValueSize);
}
}
Expand Down Expand Up @@ -134,22 +159,32 @@ extension TypedMaps on JsonBufferBuilder {
/// createdTypedMap2, etc) are not faster in the JIT VM but they are about
/// 5-10% faster in the AOT VM, consider adding specialized methods. This
/// will presumably matter more when if larger schemas are supported.
Map<String, Object?> createTypedMap(TypedMapSchema schema,
[Object? v0,
Object? v1,
Object? v2,
Object? v3,
Object? v4,
Object? v5,
Object? v6,
Object? v7]) {
Map<String, Object?> createTypedMap(
TypedMapSchema schema, [
Object? v0,
Object? v1,
Object? v2,
Object? v3,
Object? v4,
Object? v5,
Object? v6,
Object? v7,
Object? v8,
Object? v9,
Object? v10,
Object? v11,
Object? v12,
Object? v13,
Object? v14,
Object? v15,
]) {
_explanations?.push('addTypedMap $schema');

// Compute how much space the values need, and whether the map is filled.
// If the map is filled this is marked with the high bit of the schema
// pointer, then the field set is omitted.
final (valuesSize, filled) =
schema._valueSizeOf(v0, v1, v2, v3, v4, v5, v6, v7);
final (valuesSize, filled) = schema._valueSizeOf(
v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15);

// Layout is: pointer to schema, field set (unless filled!), values.
final pointer = _reserve(
Expand All @@ -165,16 +200,30 @@ extension TypedMaps on JsonBufferBuilder {
// If not filled, write the field set: a bit vector indicating which fields
// are present.
if (!filled) {
_setByte(
pointer + _pointerSize,
(v0 == null ? 0 : 0x01) +
(v1 == null ? 0 : 0x02) +
(v2 == null ? 0 : 0x04) +
(v3 == null ? 0 : 0x08) +
(v4 == null ? 0 : 0x10) +
(v5 == null ? 0 : 0x20) +
(v6 == null ? 0 : 0x40) +
(v7 == null ? 0 : 0x80));
if (schema._fieldSetSize >= 1) {
_setByte(
pointer + _pointerSize,
(v0 == null ? 0 : 0x01) +
(v1 == null ? 0 : 0x02) +
(v2 == null ? 0 : 0x04) +
(v3 == null ? 0 : 0x08) +
(v4 == null ? 0 : 0x10) +
(v5 == null ? 0 : 0x20) +
(v6 == null ? 0 : 0x40) +
(v7 == null ? 0 : 0x80));
}
if (schema._fieldSetSize >= 2) {
_setByte(
pointer + _pointerSize + 1,
(v8 == null ? 0 : 0x01) +
(v9 == null ? 0 : 0x02) +
(v10 == null ? 0 : 0x04) +
(v11 == null ? 0 : 0x08) +
(v12 == null ? 0 : 0x10) +
(v13 == null ? 0 : 0x20) +
(v14 == null ? 0 : 0x40) +
(v15 == null ? 0 : 0x80));
}
}

// Write the values.
Expand All @@ -187,6 +236,15 @@ extension TypedMaps on JsonBufferBuilder {
void addBit(bool bit) {
if (bit) byte += bitmask;
bitmask <<= 1;

// On reaching bit 9 of `byte`, write the byte and reset `byte` and
// `bitmask`.
if (bitmask == 0x100) {
_setByte(valuePointer, byte);
byte = 0;
bitmask = 0x01;
++valuePointer;
}
}

if (v0 != null) addBit(v0 == true);
Expand All @@ -197,6 +255,14 @@ extension TypedMaps on JsonBufferBuilder {
if (v5 != null) addBit(v5 == true);
if (v6 != null) addBit(v6 == true);
if (v7 != null) addBit(v7 == true);
if (v8 != null) addBit(v8 == true);
if (v9 != null) addBit(v9 == true);
if (v10 != null) addBit(v10 == true);
if (v11 != null) addBit(v11 == true);
if (v12 != null) addBit(v12 == true);
if (v13 != null) addBit(v13 == true);
if (v14 != null) addBit(v14 == true);
if (v15 != null) addBit(v15 == true);

// Only write the byte if at least one bit was written.
if (bitmask != 0x01) {
Expand All @@ -221,6 +287,14 @@ extension TypedMaps on JsonBufferBuilder {
if (v5 != null) addValue(5, v5);
if (v6 != null) addValue(6, v6);
if (v7 != null) addValue(7, v7);
if (v8 != null) addValue(8, v8);
if (v9 != null) addValue(9, v9);
if (v10 != null) addValue(10, v10);
if (v11 != null) addValue(11, v11);
if (v12 != null) addValue(12, v12);
if (v13 != null) addValue(13, v13);
if (v14 != null) addValue(14, v14);
if (v15 != null) addValue(15, v15);
}

_explanations?.pop();
Expand Down
110 changes: 110 additions & 0 deletions pkgs/dart_model/test/json_buffer/typed_map_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -213,5 +213,115 @@ void main() {
'g': growableMap
});
});

test('value with sixteen booleans', () {
final schema = TypedMapSchema({
for (var i = 0; i != 16; ++i) 'k$i': Type.boolean,
});

final map = builder.createTypedMap(
schema,
true,
false,
true,
false,
true,
false,
true,
false,
true,
false,
true,
false,
true,
false,
true,
false,
);

expectFullyEquivalentMaps(map, {
for (var i = 0; i != 16; ++i) 'k$i': i.isEven,
});

final mapWithNulls = builder.createTypedMap(
schema,
true,
false,
null,
true,
false,
null,
true,
false,
null,
true,
false,
null,
true,
false,
null,
true,
);

expectFullyEquivalentMaps(mapWithNulls, {
for (var i = 0; i != 16; ++i)
if (i % 3 != 2) 'k$i': i % 3 == 0,
});
});

test('value with sixteen mixed fields', () {
final schema = TypedMapSchema({
for (var i = 0; i != 16; ++i) 'k$i': Type.stringPointer,
});

final map = builder.createTypedMap(
schema,
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
);

expectFullyEquivalentMaps(map, {
for (var i = 0; i != 16; ++i) 'k$i': '$i',
});

final mapWithNulls = builder.createTypedMap(
schema,
null,
'1',
'2',
'3',
'4',
null,
'6',
'7',
'8',
'9',
null,
'11',
'12',
'13',
'14',
null,
);

expectFullyEquivalentMaps(mapWithNulls, {
for (var i = 0; i != 15; ++i)
if (i % 5 != 0) 'k$i': '$i',
});
});
});
}

0 comments on commit 4d3f286

Please sign in to comment.