Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
649e1a1
Split protobuf package into libraries
osa1 Jul 18, 2025
35bb15a
Also make consts a lib
osa1 Jul 18, 2025
9a2207f
Make utils a lib
osa1 Jul 18, 2025
8b59a1d
Make PbFieldType stuff public
osa1 Jul 18, 2025
0a5d2b2
Sync web decoders
osa1 Jul 18, 2025
32911d8
Export ExtensionRegistry
osa1 Jul 18, 2025
e821977
Revert "Export ExtensionRegistry"
osa1 Jul 18, 2025
877adf8
Update GeneratedMessage.{writeToJson,mergeFromJson}
osa1 Jul 21, 2025
11eecbc
Remove unused member
osa1 Jul 21, 2025
83390ea
Fix js_interop usage in serialization
osa1 Jul 21, 2025
96b5dc0
Fix sint serialization
osa1 Jul 21, 2025
afd49a7
Remove a dynamic invo
osa1 Jul 21, 2025
9cb7ca2
Merge remote-tracking branch 'origin/master' into split_into_libs
osa1 Jul 22, 2025
9a49d17
Merge branch 'split_into_libs' into field_type_public
osa1 Jul 22, 2025
43a04ff
Merge branch 'field_type_public' into web_json_decoder
osa1 Jul 22, 2025
c5acfd3
Fix warning
osa1 Jul 22, 2025
be75e06
Revert some of the changes
osa1 Jul 22, 2025
1096ccc
Merge branch 'split_into_libs' into field_type_public
osa1 Jul 22, 2025
3f14114
Merge branch 'field_type_public' into web_json_decoder
osa1 Jul 22, 2025
34afcf8
Revert renaming
osa1 Jul 22, 2025
f302178
Revert renaming
osa1 Jul 22, 2025
85b6747
Merge branch 'split_into_libs' into field_type_public
osa1 Jul 22, 2025
a187a9d
Revert renaming
osa1 Jul 22, 2025
af07865
Merge branch 'field_type_public' into web_json_decoder
osa1 Jul 22, 2025
38e9653
Formatting
osa1 Jul 22, 2025
6763a79
Use the old library with Wasm
osa1 Jul 22, 2025
0184a4c
Merge branch 'split_into_libs' into field_type_public
osa1 Jul 23, 2025
72663fa
Merge branch 'field_type_public' into web_json_decoder
osa1 Jul 23, 2025
1423191
Merge branch 'master' into web_json_decoder
osa1 Jul 23, 2025
d7f09dc
Update CHANGELOG
osa1 Jul 23, 2025
fb94c55
Merge remote-tracking branch 'origin/master' into web_json_decoder
osa1 Jul 28, 2025
7df422f
Address some of the comments
osa1 Jul 28, 2025
22968ec
Make members private
osa1 Jul 28, 2025
7a72bf0
Merge remote-tracking branch 'origin/master' into web_json_decoder
osa1 Jul 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions protobuf/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
* Some of the private `PbFieldType` members are made public, to allow using
them in internal libraries. This type is for internal use only. ([#1027])

* Improve performance of `GeneratedMessage` members: `writeToJsonMap`,
`writeToJson`, `mergeFromJson`, `mergeFromJsonMap`. ([#1028])

[#1026]: https://github.com/google/protobuf.dart/pull/1026
[#1027]: https://github.com/google/protobuf.dart/pull/1027
[#1028]: https://github.com/google/protobuf.dart/pull/1028

## 4.1.1

Expand Down
18 changes: 4 additions & 14 deletions protobuf/lib/src/protobuf/generated_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ abstract class GeneratedMessage {
/// Unknown field data, data for which there is no metadata for the associated
/// field, will only be included if this message was deserialized from the
/// same wire format.
Map<String, dynamic> writeToJsonMap() => _writeToJsonMap(_fieldSet);
Map<String, dynamic> writeToJsonMap() => json_lib.writeToJsonMap(_fieldSet);

/// Returns a JSON string that encodes this message.
///
Expand All @@ -246,7 +246,7 @@ abstract class GeneratedMessage {
/// Unknown field data, data for which there is no metadata for the associated
/// field, will only be included if this message was deserialized from the
/// same wire format.
String writeToJson() => jsonEncode(writeToJsonMap());
String writeToJson() => json_lib.writeToJsonString(_fieldSet);

/// Returns an Object representing Proto3 JSON serialization of `this`.
///
Expand Down Expand Up @@ -318,27 +318,17 @@ abstract class GeneratedMessage {
String data, [
ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY,
]) {
/// Disable lazy creation of Dart objects for a dart2js speedup.
/// This is a slight regression on the Dart VM.
/// TODO(skybrian) we could skip the reviver if we're running
/// on the Dart VM for a slight speedup.
final Map<String, dynamic> jsonMap = jsonDecode(
data,
reviver: _emptyReviver,
);
_mergeFromJsonMap(_fieldSet, jsonMap, extensionRegistry);
json_lib.mergeFromJsonString(_fieldSet, data, extensionRegistry);
}

static Object? _emptyReviver(Object? k, Object? v) => v;

/// Merges field values from a JSON object represented as a Dart map.
///
/// The encoding is described in [GeneratedMessage.writeToJson].
void mergeFromJsonMap(
Map<String, dynamic> json, [
ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY,
]) {
_mergeFromJsonMap(_fieldSet, json, extensionRegistry);
json_lib.mergeFromJsonMap(_fieldSet, json, extensionRegistry);
}

/// Adds an extension field value to a repeated field.
Expand Down
11 changes: 2 additions & 9 deletions protobuf/lib/src/protobuf/internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,15 @@
library;

import 'dart:collection' show ListBase, MapBase;
import 'dart:convert'
show
Utf8Decoder,
Utf8Encoder,
base64Decode,
base64Encode,
jsonDecode,
jsonEncode;
import 'dart:convert' show Utf8Decoder, Utf8Encoder, base64Decode, base64Encode;
import 'dart:math' as math;
import 'dart:typed_data' show ByteData, Endian, Uint8List;

import 'package:fixnum/fixnum.dart' show Int64;
import 'package:meta/meta.dart' show UseResult;

import 'consts.dart';
import 'json/json.dart' as json_lib;
import 'json_parsing_context.dart';
import 'permissive_compare.dart';
import 'type_registry.dart';
Expand All @@ -45,7 +39,6 @@ part 'field_set.dart';
part 'field_type.dart';
part 'generated_message.dart';
part 'generated_service.dart';
part 'json.dart';
part 'message_set.dart';
part 'pb_list.dart';
part 'pb_map.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@
// 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.

part of 'internal.dart';
import 'dart:convert' show base64Decode, base64Encode;

Map<String, dynamic> _writeToJsonMap(FieldSet fs) {
import 'package:fixnum/fixnum.dart' show Int64;

import '../consts.dart';
import '../internal.dart';
import '../utils.dart';

// Use json_vm.dart with VM and dart2wasm, json_web.dart with dart2js.
// json_web.dart uses JS interop for parsing, and JS interop is too slow on
// Wasm. VM's patch performs better in Wasm.
export 'json_vm.dart' if (dart.library.html) 'json_web.dart';

Map<String, dynamic> writeToJsonMap(FieldSet fs) {
dynamic convertToMap(dynamic fieldValue, int fieldType) {
final baseType = PbFieldType.baseType(fieldType);

Expand Down Expand Up @@ -61,15 +72,15 @@ Map<String, dynamic> _writeToJsonMap(FieldSet fs) {
List writeMap(PbMap fieldValue, MapFieldInfo fi) => List.from(
fieldValue.entries.map(
(MapEntry e) => {
'${PbMap._keyFieldNumber}': convertToMap(e.key, fi.keyFieldType),
'${PbMap._valueFieldNumber}': convertToMap(e.value, fi.valueFieldType),
'$mapKeyFieldNumber': convertToMap(e.key, fi.keyFieldType),
'$mapValueFieldNumber': convertToMap(e.value, fi.valueFieldType),
},
),
);

final result = <String, dynamic>{};
for (final fi in fs._infosSortedByTag) {
final value = fs._values[fi.index!];
for (final fi in fs.infosSortedByTag) {
final value = fs.values[fi.index!];
if (value == null || (value is List && value.isEmpty)) {
continue; // It's missing, repeated, or an empty byte array.
}
Expand All @@ -82,18 +93,18 @@ Map<String, dynamic> _writeToJsonMap(FieldSet fs) {
}
result['${fi.tagNumber}'] = convertToMap(value, fi.type);
}
final extensions = fs._extensions;
final extensions = fs.extensions;
if (extensions != null) {
for (final tagNumber in sorted(extensions._tagNumbers)) {
final value = extensions._values[tagNumber];
for (final tagNumber in sorted(extensions.tagNumbers)) {
final value = extensions.values[tagNumber];
if (value is List && value.isEmpty) {
continue; // It's repeated or an empty byte array.
}
final fi = extensions._getInfoOrNull(tagNumber)!;
final fi = extensions.getInfoOrNull(tagNumber)!;
result['$tagNumber'] = convertToMap(value, fi.type);
}
}
final unknownJsonData = fs._unknownJsonData;
final unknownJsonData = fs.unknownJsonData;
if (unknownJsonData != null) {
unknownJsonData.forEach((key, value) {
result[key] = value;
Expand All @@ -104,20 +115,20 @@ Map<String, dynamic> _writeToJsonMap(FieldSet fs) {

// Merge fields from a previously decoded JSON object.
// (Called recursively on nested messages.)
void _mergeFromJsonMap(
void mergeFromJsonMap(
FieldSet fs,
Map<String, dynamic> json,
ExtensionRegistry? registry,
) {
fs._ensureWritable();
fs.ensureWritable();
final keys = json.keys;
final meta = fs._meta;
final meta = fs.meta;
for (final key in keys) {
var fi = meta.byTagAsString[key];
if (fi == null) {
fi = registry?.getExtension(fs._messageName, int.parse(key));
fi = registry?.getExtension(fs.messageName, int.parse(key));
if (fi == null) {
(fs._unknownJsonData ??= {})[key] = json[key];
(fs.unknownJsonData ??= {})[key] = json[key];
continue;
}
}
Expand All @@ -144,7 +155,7 @@ void _appendJsonList(
FieldInfo fi,
ExtensionRegistry? registry,
) {
final repeated = fi._ensureRepeatedField(meta, fs);
final repeated = fi.ensureRepeatedField(meta, fs);
// Micro optimization. Using "for in" generates the following and iterator
// alloc:
// for (t1 = J.get$iterator$ax(json), t2 = fi.tagNumber, t3 = fi.type,
Expand Down Expand Up @@ -175,23 +186,23 @@ void _appendJsonMap(
ExtensionRegistry? registry,
) {
final entryMeta = fi.mapEntryBuilderInfo;
final map = fi._ensureMapField(meta, fs);
final map = fi.ensureMapField(meta, fs);
for (final jsonEntryDynamic in jsonList) {
final jsonEntry = jsonEntryDynamic as Map<String, dynamic>;
final entryFieldSet = FieldSet(null, entryMeta);
final convertedKey = _convertJsonValue(
entryMeta,
entryFieldSet,
jsonEntry['${PbMap._keyFieldNumber}'],
PbMap._keyFieldNumber,
jsonEntry['$mapKeyFieldNumber'],
mapKeyFieldNumber,
fi.keyFieldType,
registry,
);
var convertedValue = _convertJsonValue(
entryMeta,
entryFieldSet,
jsonEntry['${PbMap._valueFieldNumber}'],
PbMap._valueFieldNumber,
jsonEntry['$mapValueFieldNumber'],
mapValueFieldNumber,
fi.valueFieldType,
registry,
);
Expand Down Expand Up @@ -223,10 +234,10 @@ void _setJsonField(
// Therefore we run _validateField for debug builds only to validate
// correctness of conversion.
assert(() {
fs._validateField(fi, value);
fs.validateField(fi, value);
return true;
}());
fs._setFieldUnchecked(meta, fi, value);
fs.setFieldUnchecked(meta, fi, value);
}

/// Converts [value] from the JSON format to the Dart data type suitable for
Expand Down Expand Up @@ -298,7 +309,7 @@ dynamic _convertJsonValue(
// The following call will return null if the enum value is unknown.
// In that case, we want the caller to ignore this value, so we return
// null from this method as well.
return meta._decodeEnum(tagNumber, registry, value);
return meta.decodeEnum(tagNumber, registry, value);
}
expectedType = 'int or stringified int';
break;
Expand Down Expand Up @@ -333,8 +344,8 @@ dynamic _convertJsonValue(
case PbFieldType.MESSAGE_BIT:
if (value is Map) {
final messageValue = value as Map<String, dynamic>;
final subMessage = meta._makeEmptyMessage(tagNumber, registry);
_mergeFromJsonMap(subMessage._fieldSet, messageValue, registry);
final subMessage = meta.makeEmptyMessage(tagNumber, registry);
mergeFromJsonMap(subMessage.fieldSet, messageValue, registry);
return subMessage;
}
expectedType = 'nested message or group';
Expand Down
23 changes: 23 additions & 0 deletions protobuf/lib/src/protobuf/json/json_vm.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2025, 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:convert' show jsonDecode, jsonEncode;

import '../internal.dart';
import 'json.dart';

String writeToJsonString(FieldSet fs) => jsonEncode(writeToJsonMap(fs));

/// Merge fields from a [json] string.
void mergeFromJsonString(
FieldSet fs,
String json,
ExtensionRegistry? registry,
) {
final jsonMap = jsonDecode(json);
if (jsonMap is! Map<String, dynamic>) {
throw ArgumentError.value(json, 'json', 'Does not parse to a JSON object.');
}
mergeFromJsonMap(fs, jsonMap, registry);
}
Loading
Loading