Skip to content

Commit f72d16e

Browse files
authored
Add support for map fields. (flutter#140)
1 parent 836186a commit f72d16e

13 files changed

Lines changed: 274 additions & 5 deletions

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.10.5
2+
3+
* Added support for [map fields](https://developers.google.com/protocol-buffers/docs/proto3#maps).
4+
Map fields are now represented as Dart maps and are accessed through a getter with the same name as the map field.
5+
To use the map support, use Dart protoc_plugin version 11.0 or newer.
6+
7+
18
## 0.10.4
29

310
* Added separate getter for `BuilderInfo.qualifiedMessageName`.

lib/meta.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ const GeneratedMessage_reservedNames = const <String>[
4848
'GeneratedMessage',
4949
'Object',
5050
'eventPlugin',
51+
'createMapField',
5152
'createRepeatedField',
5253
'unknownFields',
5354
'clone',
5455
r'$_get',
5556
r'$_getI64',
5657
r'$_getList',
58+
r'$_getMap',
5759
r'$_getN',
5860
r'$_getS',
5961
r'$_has',

lib/protobuf.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
library protobuf;
66

77
import 'dart:async' show Future;
8-
import 'dart:collection' show ListBase;
8+
import 'dart:collection' show ListBase, MapBase;
99
import 'dart:convert'
1010
show base64Decode, base64Encode, jsonEncode, jsonDecode, Utf8Codec;
1111
import 'dart:math' as math;
@@ -30,6 +30,7 @@ part 'src/protobuf/generated_message.dart';
3030
part 'src/protobuf/generated_service.dart';
3131
part 'src/protobuf/json.dart';
3232
part 'src/protobuf/pb_list.dart';
33+
part 'src/protobuf/pb_map.dart';
3334
part 'src/protobuf/protobuf_enum.dart';
3435
part 'src/protobuf/readonly_message.dart';
3536
part 'src/protobuf/rpc_client.dart';

lib/src/protobuf/builder_info.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ class BuilderInfo {
3232
defaultOrMaker, subBuilder, valueOf, enumValues));
3333
}
3434

35+
void addMapField<K, V>(
36+
int tagNumber,
37+
String name,
38+
int keyFieldType,
39+
int valueFieldType,
40+
CreateBuilderFunc valueCreator,
41+
ValueOfFunc valueOf,
42+
List<ProtobufEnum> enumValues) {
43+
var index = byIndex.length;
44+
_addField(MapFieldInfo<K, V>.map(name, tagNumber, index, PbFieldType.M,
45+
keyFieldType, valueFieldType, valueCreator, valueOf, enumValues));
46+
}
47+
3548
void addRepeated<T>(
3649
int tagNumber,
3750
String name,
@@ -113,6 +126,15 @@ class BuilderInfo {
113126
tagNumber, name, fieldType, check, subBuilder, valueOf, enumValues);
114127
}
115128

129+
// Map field.
130+
void m<K, V>(int tagNumber, String name, int keyFieldType, int valueFieldType,
131+
[CreateBuilderFunc valueCreator,
132+
ValueOfFunc valueOf,
133+
List<ProtobufEnum> enumValues]) {
134+
addMapField<K, V>(tagNumber, name, keyFieldType, valueFieldType,
135+
valueCreator, valueOf, enumValues);
136+
}
137+
116138
bool containsTagNumber(int tagNumber) => fieldInfo.containsKey(tagNumber);
117139

118140
defaultValue(int tagNumber) {

lib/src/protobuf/coded_buffer.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ void _mergeFromCodedBufferReader(
217217
input.readMessage(subMessage, registry);
218218
fs._ensureRepeatedField(fi).add(subMessage);
219219
break;
220+
case PbFieldType._MAP:
221+
fs._ensureMapField(fi).add(input, registry);
222+
break;
220223
default:
221224
throw 'Unknown field type $fieldType';
222225
}

lib/src/protobuf/coded_buffer_writer.dart

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,30 @@ class CodedBufferWriter {
7979

8080
final int wireFormat = _wireTypes[_valueTypeIndex(valueType)];
8181

82+
if ((fieldType & PbFieldType._MAP_BIT) != 0) {
83+
final int keyWireFormat =
84+
_wireTypes[_valueTypeIndex(fieldValue.keyFieldType)];
85+
final int valueWireFormat =
86+
_wireTypes[_valueTypeIndex(fieldValue.valueFieldType)];
87+
88+
fieldValue.forEach((key, value) {
89+
_writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
90+
final mark = _startLengthDelimited();
91+
_writeValue(
92+
PbMap._keyFieldNumber, fieldValue.keyFieldType, key, keyWireFormat);
93+
_writeValue(PbMap._valueFieldNumber, fieldValue.valueFieldType, value,
94+
valueWireFormat);
95+
_endLengthDelimited(mark);
96+
});
97+
return;
98+
}
99+
82100
if ((fieldType & PbFieldType._REPEATED_BIT) != 0) {
83101
for (var i = 0; i < fieldValue.length; i++) {
84102
_writeValue(fieldNumber, valueType, fieldValue[i], wireFormat);
85103
}
86104
return;
87105
}
88-
89106
_writeValue(fieldNumber, valueType, fieldValue, wireFormat);
90107
}
91108

lib/src/protobuf/field_info.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ class FieldInfo<T> {
5555
assert(!_isEnum(type) || valueOf != null);
5656
}
5757

58+
FieldInfo._map(
59+
this.name, this.tagNumber, this.index, int type, this.makeDefault,
60+
[dynamic defaultOrMaker, this.subBuilder, this.valueOf, this.enumValues])
61+
: this.type = type,
62+
this.check = null {
63+
assert(name != null);
64+
assert(tagNumber != null);
65+
assert(_isMapField(type));
66+
}
67+
5868
static MakeDefaultFunc findMakeDefault(int type, dynamic defaultOrMaker) {
5969
if (defaultOrMaker == null) return PbFieldType._defaultForType(type);
6070
if (defaultOrMaker is MakeDefaultFunc) return defaultOrMaker;
@@ -65,6 +75,7 @@ class FieldInfo<T> {
6575
bool get isRepeated => _isRepeated(type);
6676
bool get isGroupOrMessage => _isGroupOrMessage(type);
6777
bool get isEnum => _isEnum(type);
78+
bool get isMapField => _isMapField(type);
6879

6980
/// Returns a read-only default value for a field.
7081
/// (Unlike getField, doesn't create a repeated field.)
@@ -147,3 +158,38 @@ class FieldInfo<T> {
147158

148159
String toString() => name;
149160
}
161+
162+
class MapFieldInfo<K, V> extends FieldInfo<PbMap<K, V>> {
163+
int keyFieldType;
164+
int valueFieldType;
165+
CreateBuilderFunc valueCreator;
166+
167+
MapFieldInfo.map(String name, int tagNumber, int index, int type,
168+
this.keyFieldType, this.valueFieldType,
169+
[this.valueCreator, ValueOfFunc valueOf, List<ProtobufEnum> enumValues])
170+
: super._map(
171+
name,
172+
tagNumber,
173+
index,
174+
type,
175+
() => PbMap<K, V>(keyFieldType, valueFieldType, valueCreator,
176+
valueOf, enumValues),
177+
null,
178+
null,
179+
valueOf,
180+
enumValues) {
181+
assert(name != null);
182+
assert(tagNumber != null);
183+
assert(_isMapField(type));
184+
assert(!_isEnum(type) || valueOf != null);
185+
}
186+
187+
Map<K, V> _ensureMapField(_FieldSet fs) {
188+
return fs._ensureMapField<K, V>(this);
189+
}
190+
191+
Map<K, V> _createMapField(GeneratedMessage m) {
192+
assert(isMapField);
193+
return m.createMapField<K, V>(tagNumber, this);
194+
}
195+
}

lib/src/protobuf/field_set.dart

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,17 @@ class _FieldSet {
187187
return value;
188188
}
189189

190+
Map<K, V> _getDefaultMap<K, V>(MapFieldInfo<K, V> fi) {
191+
assert(fi.isMapField);
192+
if (_isReadOnly)
193+
return PbMap<K, V>.unmodifiable(PbMap<K, V>(fi.keyFieldType,
194+
fi.valueFieldType, fi.valueCreator, fi.valueOf, fi.enumValues));
195+
196+
var value = fi._createMapField(_message);
197+
_setNonExtensionFieldUnchecked(fi, value);
198+
return value;
199+
}
200+
190201
_getFieldOrNullByTag(int tagNumber) {
191202
var fi = _getFieldInfoOrNull(tagNumber);
192203
if (fi == null) return null;
@@ -292,6 +303,19 @@ class _FieldSet {
292303
return newValue;
293304
}
294305

306+
PbMap<K, V> _ensureMapField<K, V>(MapFieldInfo<K, V> fi) {
307+
assert(!_isReadOnly);
308+
assert(fi.isMapField);
309+
assert(fi.index != null); // Map fields are not allowed to be extensions.
310+
311+
var value = _getFieldOrNull(fi);
312+
if (value != null) return value as Map<K, V>;
313+
314+
var newValue = fi._createMapField(_message);
315+
_setNonExtensionFieldUnchecked(fi, newValue);
316+
return newValue;
317+
}
318+
295319
/// Sets a non-extended field and fires events.
296320
void _setNonExtensionFieldUnchecked(FieldInfo fi, value) {
297321
if (_hasObservers) {
@@ -324,6 +348,13 @@ class _FieldSet {
324348
return _getDefaultList<T>(_nonExtensionInfoByIndex(index));
325349
}
326350

351+
/// The implementation of a generated getter for map fields.
352+
Map<K, V> _$getMap<K, V>(int index) {
353+
var value = _values[index];
354+
if (value != null) return value as Map<K, V>;
355+
return _getDefaultMap<K, V>(_nonExtensionInfoByIndex(index));
356+
}
357+
327358
/// The implementation of a generated getter for String fields.
328359
String _$getS(int index, String defaultValue) {
329360
var value = _values[index];
@@ -511,6 +542,8 @@ class _FieldSet {
511542
out.write('$indent$key: {\n');
512543
value._fieldSet.writeString(out, '$indent ');
513544
out.write('$indent}\n');
545+
} else if (value is MapEntry) {
546+
out.write('$indent$key: {${value.key} : ${value.value}} \n');
514547
} else {
515548
out.write('$indent$key: $value\n');
516549
}
@@ -527,6 +560,10 @@ class _FieldSet {
527560
for (var value in fieldValue) {
528561
renderValue(fi.name, value);
529562
}
563+
} else if (fieldValue is Map) {
564+
for (var entry in fieldValue.entries) {
565+
renderValue(fi.name, entry);
566+
}
530567
} else {
531568
renderValue(fi.name, fieldValue);
532569
}
@@ -581,6 +618,20 @@ class _FieldSet {
581618

582619
bool mustClone = _isGroupOrMessage(otherFi.type);
583620

621+
if (fi.isMapField) {
622+
MapFieldInfo f = fi;
623+
mustClone = _isGroupOrMessage(f.valueFieldType);
624+
PbMap map = f._ensureMapField(this);
625+
if (mustClone) {
626+
for (MapEntry entry in fieldValue.entries) {
627+
map[entry.key] = _cloneMessage(entry.value);
628+
}
629+
} else {
630+
map.addAll(fieldValue);
631+
}
632+
return;
633+
}
634+
584635
if (fi.isRepeated) {
585636
if (mustClone) {
586637
// fieldValue must be a PbListBase of GeneratedMessage.

lib/src/protobuf/field_type.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ bool _isEnum(int fieldType) =>
1414
bool _isGroupOrMessage(int fieldType) =>
1515
(fieldType & (PbFieldType._GROUP_BIT | PbFieldType._MESSAGE_BIT)) != 0;
1616

17+
bool _isMapField(int fieldType) => (fieldType & PbFieldType._MAP_BIT) != 0;
18+
1719
/// Defines constants and functions for dealing with fieldType bits.
1820
class PbFieldType {
1921
/// Returns the base field type without any of the required, repeated
2022
/// and packed bits.
2123
static int _baseType(int fieldType) =>
22-
fieldType & ~(_REQUIRED_BIT | _REPEATED_BIT | _PACKED_BIT);
24+
fieldType & ~(_REQUIRED_BIT | _REPEATED_BIT | _PACKED_BIT | _MAP_BIT);
2325

2426
static MakeDefaultFunc _defaultForType(int type) {
2527
switch (type) {
@@ -92,6 +94,7 @@ class PbFieldType {
9294
static const int _SFIXED32_BIT = 0x80000;
9395
static const int _SFIXED64_BIT = 0x100000;
9496
static const int _MESSAGE_BIT = 0x200000;
97+
static const int _MAP_BIT = 0x400000;
9598

9699
static const int _OPTIONAL_BOOL = _BOOL_BIT;
97100
static const int _OPTIONAL_BYTES = _BYTES_BIT;
@@ -167,6 +170,7 @@ class PbFieldType {
167170
static const int _PACKED_SFIXED64 =
168171
_REPEATED_BIT | _PACKED_BIT | _SFIXED64_BIT;
169172

173+
static const int _MAP = _MAP_BIT | _MESSAGE_BIT;
170174
// Short names for use in generated code.
171175

172176
// _O_ptional.
@@ -244,4 +248,6 @@ class PbFieldType {
244248
static const int KF6 = _PACKED_FIXED64;
245249
static const int KSF3 = _PACKED_SFIXED32;
246250
static const int KSF6 = _PACKED_SFIXED64;
251+
252+
static const int M = _MAP;
247253
}

lib/src/protobuf/generated_message.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,12 @@ abstract class GeneratedMessage {
252252
return new PbList<T>(check: fi.check);
253253
}
254254

255+
/// Creates a Map representing a map field.
256+
Map<K, V> createMapField<K, V>(int tagNumber, MapFieldInfo<K, V> fi) {
257+
return PbMap<K, V>(fi.keyFieldType, fi.valueFieldType, fi.valueCreator,
258+
fi.valueOf, fi.enumValues);
259+
}
260+
255261
/// Returns the value of a field, ignoring any defaults.
256262
///
257263
/// For unset or cleared fields, returns null.
@@ -321,6 +327,9 @@ abstract class GeneratedMessage {
321327
/// For generated code only.
322328
List<T> $_getList<T>(int index) => _fieldSet._$getList<T>(index);
323329

330+
/// For generated code only.
331+
Map<K, V> $_getMap<K, V>(int index) => _fieldSet._$getMap<K, V>(index);
332+
324333
/// For generated code only.
325334
String $_getS(int index, String defaultValue) =>
326335
_fieldSet._$getS(index, defaultValue);

0 commit comments

Comments
 (0)