From 4878307956b0cf20c063269e422724af0b69c164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Thu, 8 May 2025 10:41:22 +0100 Subject: [PATCH 01/12] Improve enum decoding -- alternative This is an alternative to #980 that doesn't make a big difference in terms of performance of AOT compiled benchmarks, but makes a big difference when compiling to Wasm, comapred to #980. When decoding an enum value, we call a callback in the enum field's `FieldInfo`. The callback then indexes a map mapping enum numbers to Dart values. When these conditions hold: - The known enum numbers are all positive. (so that we can use a list and index it with the number) - The known enum numbers are more than 70% of the large known enum number. (so that the list won't have a lot of `null` entries, wasting heap space) We now generate a list instead of a map to map enum numbers to Dart values. Note: similar to the map, the list is runtime allocated. No new code generated per message or enum type. Wasm benchmarks: - Before: `protobuf_PackedEnumDecoding(RunTimeRaw): 48200.0 us` - PR #980: `protobuf_PackedEnumDecoding(RunTimeRaw): 42120.0 us` - Diff: -12.6% - After: `protobuf_PackedEnumDecoding(RunTimeRaw): 35733.3 us` - Diff against PR #980: -15% - Diff against master: -25% AOT benchmarks: - Before: `protobuf_PackedEnumDecoding(RunTimeRaw): 49180.0 us` - PR #980: `protobuf_PackedEnumDecoding(RunTimeRaw): 45726.82 us` - Diff: -7% - This PR: `protobuf_PackedEnumDecoding(RunTimeRaw): 42929.7 us` - Diff against PR #980: -6% - Diff agianst master: -12% --- protobuf/lib/src/protobuf/protobuf_enum.dart | 13 ++- protoc_plugin/lib/protoc.dart | 1 + protoc_plugin/lib/src/enum_generator.dart | 35 ++++++-- .../lib/src/generated/dart_options.pb.dart | 6 +- .../lib/src/generated/descriptor.pb.dart | 76 +++++++++--------- .../lib/src/generated/descriptor.pbenum.dart | 79 +++++++++++++------ .../lib/src/generated/plugin.pb.dart | 10 ++- .../lib/src/generated/plugin.pbenum.dart | 11 ++- protoc_plugin/pubspec.yaml | 2 +- .../test/goldens/deprecations.pbenum | 7 +- .../test/goldens/doc_comments.pbenum | 7 +- protoc_plugin/test/goldens/enum | 4 +- .../test/goldens/messageGeneratorEnums | 4 +- .../test/goldens/topLevelEnum.pbenum | 4 +- 14 files changed, 162 insertions(+), 97 deletions(-) diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index b96c119a4..970d20fc3 100644 --- a/protobuf/lib/src/protobuf/protobuf_enum.dart +++ b/protobuf/lib/src/protobuf/protobuf_enum.dart @@ -37,9 +37,16 @@ class ProtobufEnum { /// Creates a new constant [ProtobufEnum] using [value] and [name]. const ProtobufEnum(this.value, this.name); - /// Creates a Map for all of the [ProtobufEnum]s in [byIndex], mapping each - /// [ProtobufEnum]'s [value] to the [ProtobufEnum]. - static Map initByValue(List byIndex) { + static List initByValueList(List byIndex) { + if (byIndex.isEmpty) return []; + final byValue = List.filled(byIndex.last.value + 1, null); + for (final enumValue in byIndex) { + byValue[enumValue.value] = enumValue; + } + return byValue; + } + + static Map initByValueMap(List byIndex) { final byValue = {}; for (final v in byIndex) { byValue[v.value] = v; diff --git a/protoc_plugin/lib/protoc.dart b/protoc_plugin/lib/protoc.dart index e6d175bd7..2ce0dd0af 100644 --- a/protoc_plugin/lib/protoc.dart +++ b/protoc_plugin/lib/protoc.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; import 'package:protobuf/protobuf.dart'; import 'const_generator.dart' show writeJsonConst; diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index a1cd8b5f7..c66c2f930 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -102,6 +102,9 @@ class EnumGenerator extends ProtobufContainer { static const int _enumValueTag = 2; void generate(IndentingWriter out) { + assert(_canonicalValues + .isSortedBy((EnumValueDescriptorProto a) => a.number)); + final commentBlock = fileGen?.commentBlock(fieldPath); if (commentBlock != null) { out.println(commentBlock); @@ -165,7 +168,6 @@ class EnumGenerator extends ProtobufContainer { } } out.println(); - out.println('static const $coreImportPrefix.List<$classname> values =' ' <$classname> ['); for (final val in _canonicalValues) { @@ -175,11 +177,32 @@ class EnumGenerator extends ProtobufContainer { out.println('];'); out.println(); - out.println( - 'static final $coreImportPrefix.Map<$coreImportPrefix.int, $classname> _byValue =' - ' $protobufImportPrefix.ProtobufEnum.initByValue(values);'); - out.println('static $classname? valueOf($coreImportPrefix.int value) =>' - ' _byValue[value];'); + var useList = _canonicalValues.isEmpty; + if (_canonicalValues.isNotEmpty) { + if (_canonicalValues.every((val) => !val.number.isNegative)) { + if (_canonicalValues.length / (_canonicalValues.last.number + 1) >= + 0.7) { + useList = true; + } + } + } + + if (useList) { + out.println( + 'static final $coreImportPrefix.List<$classname?> _byValue =' + ' $protobufImportPrefix.ProtobufEnum.initByValueList(values);'); + + out.println('static $classname? valueOf($coreImportPrefix.int value) =>' + ' value < 0 || value >= _byValue.length ? null : _byValue[value];'); + } else { + out.println( + 'static final $coreImportPrefix.Map<$coreImportPrefix.int, $classname> _byValue =' + ' $protobufImportPrefix.ProtobufEnum.initByValueMap(values);'); + + out.println('static $classname? valueOf($coreImportPrefix.int value) =>' + ' _byValue[value];'); + } + out.println(); out.println('const $classname._(super.v, super.n);'); diff --git a/protoc_plugin/lib/src/generated/dart_options.pb.dart b/protoc_plugin/lib/src/generated/dart_options.pb.dart index f045820bd..340247fca 100644 --- a/protoc_plugin/lib/src/generated/dart_options.pb.dart +++ b/protoc_plugin/lib/src/generated/dart_options.pb.dart @@ -2,7 +2,7 @@ // Generated code. Do not modify. // source: dart_options.proto // -// @dart = 2.12 +// @dart = 3.3 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes @@ -13,6 +13,8 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + /// A mixin that can be used in the 'with' clause of the generated Dart class /// for a proto message. class DartMixin extends $pb.GeneratedMessage { @@ -171,7 +173,7 @@ class Imports extends $pb.GeneratedMessage { /// so the generated code may contain errors. Therefore, running dartanalyzer /// on the generated file is a good idea. @$pb.TagNumber(1) - $core.List get mixins => $_getList(0); + $pb.PbList get mixins => $_getList(0); } class Dart_options { diff --git a/protoc_plugin/lib/src/generated/descriptor.pb.dart b/protoc_plugin/lib/src/generated/descriptor.pb.dart index d367d33e5..a4dfd4cb1 100644 --- a/protoc_plugin/lib/src/generated/descriptor.pb.dart +++ b/protoc_plugin/lib/src/generated/descriptor.pb.dart @@ -2,7 +2,7 @@ // Generated code. Do not modify. // source: descriptor.proto // -// @dart = 2.12 +// @dart = 3.3 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes @@ -16,6 +16,8 @@ import 'package:protobuf/protobuf.dart' as $pb; import 'descriptor.pbenum.dart'; +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + export 'descriptor.pbenum.dart'; /// The protocol compiler can output a FileDescriptorSet containing the .proto @@ -71,7 +73,7 @@ class FileDescriptorSet extends $pb.GeneratedMessage { static FileDescriptorSet? _defaultInstance; @$pb.TagNumber(1) - $core.List get file => $_getList(0); + $pb.PbList get file => $_getList(0); } /// Describes a complete .proto file. @@ -216,20 +218,20 @@ class FileDescriptorProto extends $pb.GeneratedMessage { /// Names of files imported by this file. @$pb.TagNumber(3) - $core.List<$core.String> get dependency => $_getList(2); + $pb.PbList<$core.String> get dependency => $_getList(2); /// All top-level definitions in this file. @$pb.TagNumber(4) - $core.List get messageType => $_getList(3); + $pb.PbList get messageType => $_getList(3); @$pb.TagNumber(5) - $core.List get enumType => $_getList(4); + $pb.PbList get enumType => $_getList(4); @$pb.TagNumber(6) - $core.List get service => $_getList(5); + $pb.PbList get service => $_getList(5); @$pb.TagNumber(7) - $core.List get extension => $_getList(6); + $pb.PbList get extension => $_getList(6); @$pb.TagNumber(8) FileOptions get options => $_getN(7); @@ -265,12 +267,12 @@ class FileDescriptorProto extends $pb.GeneratedMessage { /// Indexes of the public imported files in the dependency list above. @$pb.TagNumber(10) - $core.List<$core.int> get publicDependency => $_getList(9); + $pb.PbList<$core.int> get publicDependency => $_getList(9); /// Indexes of the weak imported files in the dependency list. /// For Google-internal migration only. Do not use. @$pb.TagNumber(11) - $core.List<$core.int> get weakDependency => $_getList(10); + $pb.PbList<$core.int> get weakDependency => $_getList(10); /// The syntax of the proto file. /// The supported values are "proto2" and "proto3". @@ -597,19 +599,19 @@ class DescriptorProto extends $pb.GeneratedMessage { void clearName() => $_clearField(1); @$pb.TagNumber(2) - $core.List get field => $_getList(1); + $pb.PbList get field => $_getList(1); @$pb.TagNumber(3) - $core.List get nestedType => $_getList(2); + $pb.PbList get nestedType => $_getList(2); @$pb.TagNumber(4) - $core.List get enumType => $_getList(3); + $pb.PbList get enumType => $_getList(3); @$pb.TagNumber(5) - $core.List get extensionRange => $_getList(4); + $pb.PbList get extensionRange => $_getList(4); @$pb.TagNumber(6) - $core.List get extension => $_getList(5); + $pb.PbList get extension => $_getList(5); @$pb.TagNumber(7) MessageOptions get options => $_getN(6); @@ -626,15 +628,15 @@ class DescriptorProto extends $pb.GeneratedMessage { MessageOptions ensureOptions() => $_ensure(6); @$pb.TagNumber(8) - $core.List get oneofDecl => $_getList(7); + $pb.PbList get oneofDecl => $_getList(7); @$pb.TagNumber(9) - $core.List get reservedRange => $_getList(8); + $pb.PbList get reservedRange => $_getList(8); /// Reserved field names, which may not be used by fields in the same message. /// A given name may only be reserved once. @$pb.TagNumber(10) - $core.List<$core.String> get reservedName => $_getList(9); + $pb.PbList<$core.String> get reservedName => $_getList(9); } class ExtensionRangeOptions extends $pb.GeneratedMessage { @@ -692,7 +694,7 @@ class ExtensionRangeOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(0); + $pb.PbList get uninterpretedOption => $_getList(0); } /// Describes a field within a message. @@ -1241,7 +1243,7 @@ class EnumDescriptorProto extends $pb.GeneratedMessage { void clearName() => $_clearField(1); @$pb.TagNumber(2) - $core.List get value => $_getList(1); + $pb.PbList get value => $_getList(1); @$pb.TagNumber(3) EnumOptions get options => $_getN(2); @@ -1261,13 +1263,13 @@ class EnumDescriptorProto extends $pb.GeneratedMessage { /// by enum values in the same enum declaration. Reserved ranges may not /// overlap. @$pb.TagNumber(4) - $core.List get reservedRange => + $pb.PbList get reservedRange => $_getList(3); /// Reserved enum value names, which may not be reused. A given name may only /// be reserved once. @$pb.TagNumber(5) - $core.List<$core.String> get reservedName => $_getList(4); + $pb.PbList<$core.String> get reservedName => $_getList(4); } /// Describes a value within an enum. @@ -1448,7 +1450,7 @@ class ServiceDescriptorProto extends $pb.GeneratedMessage { void clearName() => $_clearField(1); @$pb.TagNumber(2) - $core.List get method => $_getList(1); + $pb.PbList get method => $_getList(1); @$pb.TagNumber(3) ServiceOptions get options => $_getN(2); @@ -2087,7 +2089,7 @@ class FileOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. /// See the documentation for the "Options" section above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(20); + $pb.PbList get uninterpretedOption => $_getList(20); } class MessageOptions extends $pb.GeneratedMessage { @@ -2257,7 +2259,7 @@ class MessageOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(4); + $pb.PbList get uninterpretedOption => $_getList(4); } class FieldOptions extends $pb.GeneratedMessage { @@ -2476,7 +2478,7 @@ class FieldOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(6); + $pb.PbList get uninterpretedOption => $_getList(6); } class OneofOptions extends $pb.GeneratedMessage { @@ -2532,7 +2534,7 @@ class OneofOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(0); + $pb.PbList get uninterpretedOption => $_getList(0); } class EnumOptions extends $pb.GeneratedMessage { @@ -2627,7 +2629,7 @@ class EnumOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(2); + $pb.PbList get uninterpretedOption => $_getList(2); } class EnumValueOptions extends $pb.GeneratedMessage { @@ -2704,7 +2706,7 @@ class EnumValueOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(1); + $pb.PbList get uninterpretedOption => $_getList(1); } class ServiceOptions extends $pb.GeneratedMessage { @@ -2781,7 +2783,7 @@ class ServiceOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(1); + $pb.PbList get uninterpretedOption => $_getList(1); } class MethodOptions extends $pb.GeneratedMessage { @@ -2879,7 +2881,7 @@ class MethodOptions extends $pb.GeneratedMessage { /// The parser stores options it doesn't recognize here. See above. @$pb.TagNumber(999) - $core.List get uninterpretedOption => $_getList(2); + $pb.PbList get uninterpretedOption => $_getList(2); } /// The name of the uninterpreted option. Each string represents a segment in @@ -3061,7 +3063,7 @@ class UninterpretedOption extends $pb.GeneratedMessage { static UninterpretedOption? _defaultInstance; @$pb.TagNumber(2) - $core.List get name => $_getList(0); + $pb.PbList get name => $_getList(0); /// The value of the uninterpreted option, in whatever type the tokenizer /// identified it as during parsing. Exactly one of these should be set. @@ -3233,7 +3235,7 @@ class SourceCodeInfo_Location extends $pb.GeneratedMessage { /// this path refers to the whole field declaration (from the beginning /// of the label to the terminating semicolon). @$pb.TagNumber(1) - $core.List<$core.int> get path => $_getList(0); + $pb.PbList<$core.int> get path => $_getList(0); /// Always has exactly three or four elements: start line, start column, /// end line (optional, otherwise assumed same as start line), end column. @@ -3241,7 +3243,7 @@ class SourceCodeInfo_Location extends $pb.GeneratedMessage { /// and column numbers are zero-based -- typically you will want to add /// 1 to each before displaying to a user. @$pb.TagNumber(2) - $core.List<$core.int> get span => $_getList(1); + $pb.PbList<$core.int> get span => $_getList(1); /// If this SourceCodeInfo represents a complete declaration, these are any /// comments appearing before and after the declaration which appear to be @@ -3315,7 +3317,7 @@ class SourceCodeInfo_Location extends $pb.GeneratedMessage { void clearTrailingComments() => $_clearField(4); @$pb.TagNumber(6) - $core.List<$core.String> get leadingDetachedComments => $_getList(4); + $pb.PbList<$core.String> get leadingDetachedComments => $_getList(4); } /// Encapsulates information about the original source file from which a @@ -3415,7 +3417,7 @@ class SourceCodeInfo extends $pb.GeneratedMessage { /// ignore those that it doesn't understand, as more types of locations could /// be recorded in the future. @$pb.TagNumber(1) - $core.List get location => $_getList(0); + $pb.PbList get location => $_getList(0); } class GeneratedCodeInfo_Annotation extends $pb.GeneratedMessage { @@ -3489,7 +3491,7 @@ class GeneratedCodeInfo_Annotation extends $pb.GeneratedMessage { /// Identifies the element in the original source .proto file. This field /// is formatted the same as SourceCodeInfo.Location.path. @$pb.TagNumber(1) - $core.List<$core.int> get path => $_getList(0); + $pb.PbList<$core.int> get path => $_getList(0); /// Identifies the filesystem path to the original source .proto. @$pb.TagNumber(2) @@ -3591,7 +3593,7 @@ class GeneratedCodeInfo extends $pb.GeneratedMessage { /// An Annotation connects some span of text in generated code to an element /// of its generating .proto file. @$pb.TagNumber(1) - $core.List get annotation => $_getList(0); + $pb.PbList get annotation => $_getList(0); } const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); diff --git a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart index 8b92c04a8..0c205c756 100644 --- a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart +++ b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart @@ -2,7 +2,7 @@ // Generated code. Do not modify. // source: descriptor.proto // -// @dart = 2.12 +// @dart = 3.3 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes @@ -14,14 +14,22 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class FieldDescriptorProto_Type extends $pb.ProtobufEnum { + /// 0 is reserved for errors. + /// Order is weird for historical reasons. static const FieldDescriptorProto_Type TYPE_DOUBLE = FieldDescriptorProto_Type._(1, _omitEnumNames ? '' : 'TYPE_DOUBLE'); static const FieldDescriptorProto_Type TYPE_FLOAT = FieldDescriptorProto_Type._(2, _omitEnumNames ? '' : 'TYPE_FLOAT'); + + /// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + /// negative values are likely. static const FieldDescriptorProto_Type TYPE_INT64 = FieldDescriptorProto_Type._(3, _omitEnumNames ? '' : 'TYPE_INT64'); static const FieldDescriptorProto_Type TYPE_UINT64 = FieldDescriptorProto_Type._(4, _omitEnumNames ? '' : 'TYPE_UINT64'); + + /// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + /// negative values are likely. static const FieldDescriptorProto_Type TYPE_INT32 = FieldDescriptorProto_Type._(5, _omitEnumNames ? '' : 'TYPE_INT32'); static const FieldDescriptorProto_Type TYPE_FIXED64 = @@ -32,10 +40,17 @@ class FieldDescriptorProto_Type extends $pb.ProtobufEnum { FieldDescriptorProto_Type._(8, _omitEnumNames ? '' : 'TYPE_BOOL'); static const FieldDescriptorProto_Type TYPE_STRING = FieldDescriptorProto_Type._(9, _omitEnumNames ? '' : 'TYPE_STRING'); + + /// Tag-delimited aggregate. + /// Group type is deprecated and not supported in proto3. However, Proto3 + /// implementations should still be able to parse the group wire format and + /// treat group fields as unknown fields. static const FieldDescriptorProto_Type TYPE_GROUP = FieldDescriptorProto_Type._(10, _omitEnumNames ? '' : 'TYPE_GROUP'); static const FieldDescriptorProto_Type TYPE_MESSAGE = FieldDescriptorProto_Type._(11, _omitEnumNames ? '' : 'TYPE_MESSAGE'); + + /// New in version 2. static const FieldDescriptorProto_Type TYPE_BYTES = FieldDescriptorProto_Type._(12, _omitEnumNames ? '' : 'TYPE_BYTES'); static const FieldDescriptorProto_Type TYPE_UINT32 = @@ -73,14 +88,16 @@ class FieldDescriptorProto_Type extends $pb.ProtobufEnum { TYPE_SINT64, ]; - static final $core.Map<$core.int, FieldDescriptorProto_Type> _byValue = - $pb.ProtobufEnum.initByValue(values); - static FieldDescriptorProto_Type? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); + static FieldDescriptorProto_Type? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; - const FieldDescriptorProto_Type._($core.int v, $core.String n) : super(v, n); + const FieldDescriptorProto_Type._(super.v, super.n); } class FieldDescriptorProto_Label extends $pb.ProtobufEnum { + /// 0 is reserved for errors static const FieldDescriptorProto_Label LABEL_OPTIONAL = FieldDescriptorProto_Label._(1, _omitEnumNames ? '' : 'LABEL_OPTIONAL'); static const FieldDescriptorProto_Label LABEL_REQUIRED = @@ -95,18 +112,20 @@ class FieldDescriptorProto_Label extends $pb.ProtobufEnum { LABEL_REPEATED, ]; - static final $core.Map<$core.int, FieldDescriptorProto_Label> _byValue = - $pb.ProtobufEnum.initByValue(values); + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); static FieldDescriptorProto_Label? valueOf($core.int value) => - _byValue[value]; + value < 0 || value >= _byValue.length ? null : _byValue[value]; - const FieldDescriptorProto_Label._($core.int v, $core.String n) : super(v, n); + const FieldDescriptorProto_Label._(super.v, super.n); } /// Generated classes can be optimized for speed or code size. class FileOptions_OptimizeMode extends $pb.ProtobufEnum { static const FileOptions_OptimizeMode SPEED = FileOptions_OptimizeMode._(1, _omitEnumNames ? '' : 'SPEED'); + + /// etc. static const FileOptions_OptimizeMode CODE_SIZE = FileOptions_OptimizeMode._(2, _omitEnumNames ? '' : 'CODE_SIZE'); static const FileOptions_OptimizeMode LITE_RUNTIME = @@ -119,14 +138,16 @@ class FileOptions_OptimizeMode extends $pb.ProtobufEnum { LITE_RUNTIME, ]; - static final $core.Map<$core.int, FileOptions_OptimizeMode> _byValue = - $pb.ProtobufEnum.initByValue(values); - static FileOptions_OptimizeMode? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); + static FileOptions_OptimizeMode? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; - const FileOptions_OptimizeMode._($core.int v, $core.String n) : super(v, n); + const FileOptions_OptimizeMode._(super.v, super.n); } class FieldOptions_CType extends $pb.ProtobufEnum { + /// Default mode. static const FieldOptions_CType STRING = FieldOptions_CType._(0, _omitEnumNames ? '' : 'STRING'); static const FieldOptions_CType CORD = @@ -140,18 +161,24 @@ class FieldOptions_CType extends $pb.ProtobufEnum { STRING_PIECE, ]; - static final $core.Map<$core.int, FieldOptions_CType> _byValue = - $pb.ProtobufEnum.initByValue(values); - static FieldOptions_CType? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); + static FieldOptions_CType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; - const FieldOptions_CType._($core.int v, $core.String n) : super(v, n); + const FieldOptions_CType._(super.v, super.n); } class FieldOptions_JSType extends $pb.ProtobufEnum { + /// Use the default type. static const FieldOptions_JSType JS_NORMAL = FieldOptions_JSType._(0, _omitEnumNames ? '' : 'JS_NORMAL'); + + /// Use JavaScript strings. static const FieldOptions_JSType JS_STRING = FieldOptions_JSType._(1, _omitEnumNames ? '' : 'JS_STRING'); + + /// Use JavaScript numbers. static const FieldOptions_JSType JS_NUMBER = FieldOptions_JSType._(2, _omitEnumNames ? '' : 'JS_NUMBER'); @@ -161,11 +188,12 @@ class FieldOptions_JSType extends $pb.ProtobufEnum { JS_NUMBER, ]; - static final $core.Map<$core.int, FieldOptions_JSType> _byValue = - $pb.ProtobufEnum.initByValue(values); - static FieldOptions_JSType? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); + static FieldOptions_JSType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; - const FieldOptions_JSType._($core.int v, $core.String n) : super(v, n); + const FieldOptions_JSType._(super.v, super.n); } /// Is this method side-effect-free (or safe in HTTP parlance), or idempotent, @@ -188,13 +216,12 @@ class MethodOptions_IdempotencyLevel extends $pb.ProtobufEnum { IDEMPOTENT, ]; - static final $core.Map<$core.int, MethodOptions_IdempotencyLevel> _byValue = - $pb.ProtobufEnum.initByValue(values); + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); static MethodOptions_IdempotencyLevel? valueOf($core.int value) => - _byValue[value]; + value < 0 || value >= _byValue.length ? null : _byValue[value]; - const MethodOptions_IdempotencyLevel._($core.int v, $core.String n) - : super(v, n); + const MethodOptions_IdempotencyLevel._(super.v, super.n); } const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/protoc_plugin/lib/src/generated/plugin.pb.dart b/protoc_plugin/lib/src/generated/plugin.pb.dart index bad241898..e50ab709e 100644 --- a/protoc_plugin/lib/src/generated/plugin.pb.dart +++ b/protoc_plugin/lib/src/generated/plugin.pb.dart @@ -2,7 +2,7 @@ // Generated code. Do not modify. // source: plugin.proto // -// @dart = 2.12 +// @dart = 3.3 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes @@ -16,6 +16,8 @@ import 'package:protobuf/protobuf.dart' as $pb; import 'descriptor.pb.dart' as $0; +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + export 'plugin.pbenum.dart'; /// The version number of protocol compiler. @@ -204,7 +206,7 @@ class CodeGeneratorRequest extends $pb.GeneratedMessage { /// code generator should generate code only for these files. Each file's /// descriptor will be included in proto_file, below. @$pb.TagNumber(1) - $core.List<$core.String> get fileToGenerate => $_getList(0); + $pb.PbList<$core.String> get fileToGenerate => $_getList(0); /// The generator parameter passed on the command-line. @$pb.TagNumber(2) @@ -249,7 +251,7 @@ class CodeGeneratorRequest extends $pb.GeneratedMessage { /// Type names of fields and extensions in the FileDescriptorProto are always /// fully qualified. @$pb.TagNumber(15) - $core.List<$0.FileDescriptorProto> get protoFile => $_getList(3); + $pb.PbList<$0.FileDescriptorProto> get protoFile => $_getList(3); } /// Represents a single generated file. @@ -525,7 +527,7 @@ class CodeGeneratorResponse extends $pb.GeneratedMessage { void clearSupportedFeatures() => $_clearField(2); @$pb.TagNumber(15) - $core.List get file => $_getList(2); + $pb.PbList get file => $_getList(2); } const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); diff --git a/protoc_plugin/lib/src/generated/plugin.pbenum.dart b/protoc_plugin/lib/src/generated/plugin.pbenum.dart index f3082012f..23a359df2 100644 --- a/protoc_plugin/lib/src/generated/plugin.pbenum.dart +++ b/protoc_plugin/lib/src/generated/plugin.pbenum.dart @@ -2,7 +2,7 @@ // Generated code. Do not modify. // source: plugin.proto // -// @dart = 2.12 +// @dart = 3.3 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes @@ -27,13 +27,12 @@ class CodeGeneratorResponse_Feature extends $pb.ProtobufEnum { FEATURE_PROTO3_OPTIONAL, ]; - static final $core.Map<$core.int, CodeGeneratorResponse_Feature> _byValue = - $pb.ProtobufEnum.initByValue(values); + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); static CodeGeneratorResponse_Feature? valueOf($core.int value) => - _byValue[value]; + value < 0 || value >= _byValue.length ? null : _byValue[value]; - const CodeGeneratorResponse_Feature._($core.int v, $core.String n) - : super(v, n); + const CodeGeneratorResponse_Feature._(super.v, super.n); } const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml index 95c33d3b1..122eef003 100644 --- a/protoc_plugin/pubspec.yaml +++ b/protoc_plugin/pubspec.yaml @@ -7,12 +7,12 @@ environment: sdk: '>=3.3.0 <4.0.0' dependencies: + collection: ^1.15.0 fixnum: ^1.0.0 path: ^1.8.0 protobuf: ^4.0.0 dev_dependencies: - collection: ^1.15.0 dart_flutter_team_lints: ^1.0.0 matcher: ^0.12.10 test: ^1.16.0 diff --git a/protoc_plugin/test/goldens/deprecations.pbenum b/protoc_plugin/test/goldens/deprecations.pbenum index 30bc5d782..59d98c7de 100644 --- a/protoc_plugin/test/goldens/deprecations.pbenum +++ b/protoc_plugin/test/goldens/deprecations.pbenum @@ -24,9 +24,10 @@ class A extends $pb.ProtobufEnum { A2, ]; - static final $core.Map<$core.int, A> _byValue = - $pb.ProtobufEnum.initByValue(values); - static A? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); + static A? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; const A._(super.v, super.n); } diff --git a/protoc_plugin/test/goldens/doc_comments.pbenum b/protoc_plugin/test/goldens/doc_comments.pbenum index 7cde50e13..3a7e8189c 100644 --- a/protoc_plugin/test/goldens/doc_comments.pbenum +++ b/protoc_plugin/test/goldens/doc_comments.pbenum @@ -26,9 +26,10 @@ class A extends $pb.ProtobufEnum { A2, ]; - static final $core.Map<$core.int, A> _byValue = - $pb.ProtobufEnum.initByValue(values); - static A? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = + $pb.ProtobufEnum.initByValueList(values); + static A? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; const A._(super.v, super.n); } diff --git a/protoc_plugin/test/goldens/enum b/protoc_plugin/test/goldens/enum index 812247ddf..2cc22d612 100644 --- a/protoc_plugin/test/goldens/enum +++ b/protoc_plugin/test/goldens/enum @@ -11,8 +11,8 @@ class PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.Map<$core.int, PhoneType> _byValue = $pb.ProtobufEnum.initByValue(values); - static PhoneType? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = $pb.ProtobufEnum.initByValueList(values); + static PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneType._(super.v, super.n); } diff --git a/protoc_plugin/test/goldens/messageGeneratorEnums b/protoc_plugin/test/goldens/messageGeneratorEnums index 3469c7bab..ad35cc4cd 100644 --- a/protoc_plugin/test/goldens/messageGeneratorEnums +++ b/protoc_plugin/test/goldens/messageGeneratorEnums @@ -11,8 +11,8 @@ class PhoneNumber_PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.Map<$core.int, PhoneNumber_PhoneType> _byValue = $pb.ProtobufEnum.initByValue(values); - static PhoneNumber_PhoneType? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = $pb.ProtobufEnum.initByValueList(values); + static PhoneNumber_PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneNumber_PhoneType._(super.v, super.n); } diff --git a/protoc_plugin/test/goldens/topLevelEnum.pbenum b/protoc_plugin/test/goldens/topLevelEnum.pbenum index 30d81a24e..6ebd26f44 100644 --- a/protoc_plugin/test/goldens/topLevelEnum.pbenum +++ b/protoc_plugin/test/goldens/topLevelEnum.pbenum @@ -26,8 +26,8 @@ class PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.Map<$core.int, PhoneType> _byValue = $pb.ProtobufEnum.initByValue(values); - static PhoneType? valueOf($core.int value) => _byValue[value]; + static final $core.List _byValue = $pb.ProtobufEnum.initByValueList(values); + static PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneType._(super.v, super.n); } From 11cd8fd41923ae32bcffd7c01dfbed9fcb2b6583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Fri, 9 May 2025 15:42:50 +0100 Subject: [PATCH 02/12] Add sparse enum decoding benchmarks With upcoming change we'll improve decoding performance of enums, but there will be a difference between "sparse" and "dense" enum decoding performance even though they'll both be faster. To be able to measure the difference add a "sparse" enum type and a benchmark for decoding it. "Sparse" means the enum has large gaps between known enum values, or negative enum values. When decoding this kind of enums, the mapping from the wire `varint` to the Dart value for the enum needs to be done by binary search, map lookup, or similar. For "dense" enums, we can have a list of enum values and index the list directly with the `varint` value, after a range check. These changes will be done in the follow-up PR(s). --- benchmarks/bin/binary_decode_packed.dart | 20 ++++++++++++++++++++ benchmarks/protos/packed_fields.proto | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/benchmarks/bin/binary_decode_packed.dart b/benchmarks/bin/binary_decode_packed.dart index f640cca43..a09c8bc1a 100644 --- a/benchmarks/bin/binary_decode_packed.dart +++ b/benchmarks/bin/binary_decode_packed.dart @@ -181,6 +181,25 @@ class PackedEnumDecodingBenchmark extends BenchmarkBase { } } +class PackedSparseEnumDecodingBenchmark extends BenchmarkBase { + late final Uint8List encoded; + + PackedSparseEnumDecodingBenchmark() : super('PackedSparseEnumDecoding') { + final rand = Random(123); + final message = PackedFields(); + final numEnums = SparseEnum.values.length; + for (var i = 0; i < 1000000; i += 1) { + message.sparseEnum.add(SparseEnum.values[rand.nextInt(numEnums)]); + } + encoded = message.writeToBuffer(); + } + + @override + void run() { + sink = PackedFields()..mergeFromBuffer(encoded); + } +} + void main() { PackedInt32DecodingBenchmark().report(); PackedInt64DecodingBenchmark().report(); @@ -190,6 +209,7 @@ void main() { PackedSint64DecodingBenchmark().report(); PackedBoolDecodingBenchmark().report(); PackedEnumDecodingBenchmark().report(); + PackedSparseEnumDecodingBenchmark().report(); if (int.parse('1') == 0) print(sink); } diff --git a/benchmarks/protos/packed_fields.proto b/benchmarks/protos/packed_fields.proto index 09d32d3ba..18f763944 100644 --- a/benchmarks/protos/packed_fields.proto +++ b/benchmarks/protos/packed_fields.proto @@ -10,6 +10,7 @@ message PackedFields { repeated bool packedBool = 7 [packed = true]; repeated Enum1 packedEnum1 = 8 [packed = true]; repeated Enum2 packedEnum2 = 9 [packed = true]; + repeated SparseEnum sparseEnum = 10 [packed = true]; } enum Enum1 { @@ -27,3 +28,23 @@ enum Enum2 { ENUM_2_4 = 4; ENUM_2_5 = 5; } + +// An enum with large gaps between the known values, and with negative values. +// +// This will be slower to decode as the varint to enum value mapping needs to be +// done with binary search, or map lookup etc. +enum SparseEnum { + ENUM_ZERO = 0; + ENUM_MIN_INT = -2147483648; + ENUM_1 = -1000000000; + ENUM_2 = -100000000; + ENUM_3 = -10000000; + ENUM_4 = -1000000; + ENUM_5 = -100000; + ENUM_6 = 100000; + ENUM_7 = 1000000; + ENUM_8 = 10000000; + ENUM_9 = 100000000; + ENUM_10 = 1000000000; + ENUM_MAX_INT = 2147483647; +} From 2f9478c2a576e7f67dca1e9a7e286182644b2c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 12 May 2025 12:18:21 +0100 Subject: [PATCH 03/12] Update --- protobuf/lib/src/protobuf/protobuf_enum.dart | 29 +++++++++++++------ protoc_plugin/lib/src/enum_generator.dart | 7 ++--- .../lib/src/generated/descriptor.pbenum.dart | 12 ++++---- .../lib/src/generated/plugin.pbenum.dart | 2 +- .../test/goldens/deprecations.pbenum | 2 +- .../test/goldens/doc_comments.pbenum | 2 +- protoc_plugin/test/goldens/enum | 2 +- .../test/goldens/messageGeneratorEnums | 2 +- .../test/goldens/topLevelEnum.pbenum | 2 +- 9 files changed, 34 insertions(+), 26 deletions(-) diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index 970d20fc3..b095d35be 100644 --- a/protobuf/lib/src/protobuf/protobuf_enum.dart +++ b/protobuf/lib/src/protobuf/protobuf_enum.dart @@ -2,6 +2,8 @@ // 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. +// ignore_for_file: non_constant_identifier_names + part of '../../protobuf.dart'; /// A base class for all proto enum types. @@ -37,19 +39,28 @@ class ProtobufEnum { /// Creates a new constant [ProtobufEnum] using [value] and [name]. const ProtobufEnum(this.value, this.name); - static List initByValueList(List byIndex) { - if (byIndex.isEmpty) return []; - final byValue = List.filled(byIndex.last.value + 1, null); - for (final enumValue in byIndex) { - byValue[enumValue.value] = enumValue; + /// This function is for generated code. + /// + /// Creates a Map for all of the [ProtobufEnum]s in [byIndex], mapping each + /// [ProtobufEnum]'s [value] to the [ProtobufEnum]. + /// + /// @nodoc + static Map initByValue(List byIndex) { + final byValue = {}; + for (final v in byIndex) { + byValue[v.value] = v; } return byValue; } - static Map initByValueMap(List byIndex) { - final byValue = {}; - for (final v in byIndex) { - byValue[v.value] = v; + /// This function is for generated code. + /// + /// @nodoc + static List $_initByValueList(List byIndex) { + if (byIndex.isEmpty) return []; + final byValue = List.filled(byIndex.last.value + 1, null); + for (final enumValue in byIndex) { + byValue[enumValue.value] = enumValue; } return byValue; } diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index c66c2f930..ad7b691dd 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -102,9 +102,6 @@ class EnumGenerator extends ProtobufContainer { static const int _enumValueTag = 2; void generate(IndentingWriter out) { - assert(_canonicalValues - .isSortedBy((EnumValueDescriptorProto a) => a.number)); - final commentBlock = fileGen?.commentBlock(fieldPath); if (commentBlock != null) { out.println(commentBlock); @@ -190,14 +187,14 @@ class EnumGenerator extends ProtobufContainer { if (useList) { out.println( 'static final $coreImportPrefix.List<$classname?> _byValue =' - ' $protobufImportPrefix.ProtobufEnum.initByValueList(values);'); + ' $protobufImportPrefix.ProtobufEnum.\$_initByValueList(values);'); out.println('static $classname? valueOf($coreImportPrefix.int value) =>' ' value < 0 || value >= _byValue.length ? null : _byValue[value];'); } else { out.println( 'static final $coreImportPrefix.Map<$coreImportPrefix.int, $classname> _byValue =' - ' $protobufImportPrefix.ProtobufEnum.initByValueMap(values);'); + ' $protobufImportPrefix.ProtobufEnum.initByValue(values);'); out.println('static $classname? valueOf($coreImportPrefix.int value) =>' ' _byValue[value];'); diff --git a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart index 0c205c756..c9b1cb43f 100644 --- a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart +++ b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart @@ -89,7 +89,7 @@ class FieldDescriptorProto_Type extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static FieldDescriptorProto_Type? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -113,7 +113,7 @@ class FieldDescriptorProto_Label extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static FieldDescriptorProto_Label? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -139,7 +139,7 @@ class FileOptions_OptimizeMode extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static FileOptions_OptimizeMode? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -162,7 +162,7 @@ class FieldOptions_CType extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static FieldOptions_CType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -189,7 +189,7 @@ class FieldOptions_JSType extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static FieldOptions_JSType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -217,7 +217,7 @@ class MethodOptions_IdempotencyLevel extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static MethodOptions_IdempotencyLevel? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/lib/src/generated/plugin.pbenum.dart b/protoc_plugin/lib/src/generated/plugin.pbenum.dart index 23a359df2..86e0b2d80 100644 --- a/protoc_plugin/lib/src/generated/plugin.pbenum.dart +++ b/protoc_plugin/lib/src/generated/plugin.pbenum.dart @@ -28,7 +28,7 @@ class CodeGeneratorResponse_Feature extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static CodeGeneratorResponse_Feature? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/test/goldens/deprecations.pbenum b/protoc_plugin/test/goldens/deprecations.pbenum index 59d98c7de..d98728a7f 100644 --- a/protoc_plugin/test/goldens/deprecations.pbenum +++ b/protoc_plugin/test/goldens/deprecations.pbenum @@ -25,7 +25,7 @@ class A extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static A? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/test/goldens/doc_comments.pbenum b/protoc_plugin/test/goldens/doc_comments.pbenum index 3a7e8189c..5ad1b4bfb 100644 --- a/protoc_plugin/test/goldens/doc_comments.pbenum +++ b/protoc_plugin/test/goldens/doc_comments.pbenum @@ -27,7 +27,7 @@ class A extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values); static A? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/test/goldens/enum b/protoc_plugin/test/goldens/enum index 2cc22d612..a168024bb 100644 --- a/protoc_plugin/test/goldens/enum +++ b/protoc_plugin/test/goldens/enum @@ -11,7 +11,7 @@ class PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.List _byValue = $pb.ProtobufEnum.initByValueList(values); + static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values); static PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneType._(super.v, super.n); diff --git a/protoc_plugin/test/goldens/messageGeneratorEnums b/protoc_plugin/test/goldens/messageGeneratorEnums index ad35cc4cd..bb244bc9f 100644 --- a/protoc_plugin/test/goldens/messageGeneratorEnums +++ b/protoc_plugin/test/goldens/messageGeneratorEnums @@ -11,7 +11,7 @@ class PhoneNumber_PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.List _byValue = $pb.ProtobufEnum.initByValueList(values); + static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values); static PhoneNumber_PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneNumber_PhoneType._(super.v, super.n); diff --git a/protoc_plugin/test/goldens/topLevelEnum.pbenum b/protoc_plugin/test/goldens/topLevelEnum.pbenum index 6ebd26f44..5d455aea1 100644 --- a/protoc_plugin/test/goldens/topLevelEnum.pbenum +++ b/protoc_plugin/test/goldens/topLevelEnum.pbenum @@ -26,7 +26,7 @@ class PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.List _byValue = $pb.ProtobufEnum.initByValueList(values); + static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values); static PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneType._(super.v, super.n); From 7a833a0d4dab54b15317e88fe00ce6cbc3e30524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 12 May 2025 12:21:07 +0100 Subject: [PATCH 04/12] Update --- protoc_plugin/lib/protoc.dart | 1 - protoc_plugin/lib/src/enum_generator.dart | 13 ++++--------- protoc_plugin/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/protoc_plugin/lib/protoc.dart b/protoc_plugin/lib/protoc.dart index 2ce0dd0af..e6d175bd7 100644 --- a/protoc_plugin/lib/protoc.dart +++ b/protoc_plugin/lib/protoc.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:collection/collection.dart'; import 'package:protobuf/protobuf.dart'; import 'const_generator.dart' show writeJsonConst; diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index ad7b691dd..f2d048970 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -174,15 +174,10 @@ class EnumGenerator extends ProtobufContainer { out.println('];'); out.println(); - var useList = _canonicalValues.isEmpty; - if (_canonicalValues.isNotEmpty) { - if (_canonicalValues.every((val) => !val.number.isNegative)) { - if (_canonicalValues.length / (_canonicalValues.last.number + 1) >= - 0.7) { - useList = true; - } - } - } + final useList = _canonicalValues.isEmpty || + (_canonicalValues.every((val) => !val.number.isNegative) && + _canonicalValues.length / (_canonicalValues.last.number + 1) >= + 0.7); if (useList) { out.println( diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml index 122eef003..95c33d3b1 100644 --- a/protoc_plugin/pubspec.yaml +++ b/protoc_plugin/pubspec.yaml @@ -7,12 +7,12 @@ environment: sdk: '>=3.3.0 <4.0.0' dependencies: - collection: ^1.15.0 fixnum: ^1.0.0 path: ^1.8.0 protobuf: ^4.0.0 dev_dependencies: + collection: ^1.15.0 dart_flutter_team_lints: ^1.0.0 matcher: ^0.12.10 test: ^1.16.0 From 47b6f20b3641b9204ae89ad27c06895263de7783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 12 May 2025 12:23:22 +0100 Subject: [PATCH 05/12] Fix --- protoc_plugin/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml index 95c33d3b1..122eef003 100644 --- a/protoc_plugin/pubspec.yaml +++ b/protoc_plugin/pubspec.yaml @@ -7,12 +7,12 @@ environment: sdk: '>=3.3.0 <4.0.0' dependencies: + collection: ^1.15.0 fixnum: ^1.0.0 path: ^1.8.0 protobuf: ^4.0.0 dev_dependencies: - collection: ^1.15.0 dart_flutter_team_lints: ^1.0.0 matcher: ^0.12.10 test: ^1.16.0 From 5638bb8c5caf37a18193558c51db0b94b3801fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 12 May 2025 14:58:23 +0100 Subject: [PATCH 06/12] Fix a bug, add a test --- protobuf/lib/src/protobuf/protobuf_enum.dart | 23 +++++--- protoc_plugin/Makefile | 1 + .../test/generated_message_test.dart | 25 +++++++++ protoc_plugin/test/protos/enums.proto | 54 +++++++++++++++++++ 4 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 protoc_plugin/test/protos/enums.proto diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index b095d35be..993d9c6e7 100644 --- a/protobuf/lib/src/protobuf/protobuf_enum.dart +++ b/protobuf/lib/src/protobuf/protobuf_enum.dart @@ -41,13 +41,13 @@ class ProtobufEnum { /// This function is for generated code. /// - /// Creates a Map for all of the [ProtobufEnum]s in [byIndex], mapping each + /// Creates a Map for all of the [ProtobufEnum]s in [enumValues], mapping each /// [ProtobufEnum]'s [value] to the [ProtobufEnum]. /// /// @nodoc - static Map initByValue(List byIndex) { + static Map initByValue(List enumValues) { final byValue = {}; - for (final v in byIndex) { + for (final v in enumValues) { byValue[v.value] = v; } return byValue; @@ -56,10 +56,19 @@ class ProtobufEnum { /// This function is for generated code. /// /// @nodoc - static List $_initByValueList(List byIndex) { - if (byIndex.isEmpty) return []; - final byValue = List.filled(byIndex.last.value + 1, null); - for (final enumValue in byIndex) { + static List $_initByValueList(List enumValues) { + if (enumValues.isEmpty) return []; + + var maxValue = enumValues[0].value; + for (var i = 0; i < enumValues.length; i += 1) { + final value = enumValues[i].value; + if (value > maxValue) { + maxValue = value; + } + } + + final byValue = List.filled(maxValue + 1, null); + for (final enumValue in enumValues) { byValue[enumValue.value] = enumValue; } return byValue; diff --git a/protoc_plugin/Makefile b/protoc_plugin/Makefile index 0aa7a1312..de31d7edd 100644 --- a/protoc_plugin/Makefile +++ b/protoc_plugin/Makefile @@ -30,6 +30,7 @@ TEST_PROTO_LIST = \ entity \ enum_extension \ enum_name \ + enums \ extend_unittest \ ExtensionEnumNameConflict \ ExtensionNameConflict \ diff --git a/protoc_plugin/test/generated_message_test.dart b/protoc_plugin/test/generated_message_test.dart index a25f6a4d4..366bef2e1 100644 --- a/protoc_plugin/test/generated_message_test.dart +++ b/protoc_plugin/test/generated_message_test.dart @@ -10,6 +10,8 @@ import '../out/protos/constructor_args/google/protobuf/unittest.pb.dart' import '../out/protos/constructor_args/google/protobuf/unittest_import.pb.dart' as constructor_args_unittest_import; import '../out/protos/duplicate_names_import.pb.dart'; +import '../out/protos/enums.pb.dart'; +import '../out/protos/enums.pbenum.dart'; import '../out/protos/google/protobuf/unittest.pb.dart'; import '../out/protos/google/protobuf/unittest_import.pb.dart'; import '../out/protos/google/protobuf/unittest_optimize_for.pb.dart'; @@ -890,4 +892,27 @@ void main() { // constructor arguments, to be able to reuse `assertAllFieldsSet`. assertAllFieldsSet(TestAllTypes.fromBuffer(value.writeToBuffer())); }); + + test('Handling enums defined out of order', () { + final message = MessageWithEnums(); + for (final enum_ in DenseEnum.values) { + message.denseEnums.add(enum_); + } + for (final enum_ in DenseEnumOutOfOrder.values) { + message.denseOutOfOrderEnums.add(enum_); + } + for (final enum_ in SparseEnum.values) { + message.sparseEnums.add(enum_); + } + for (final enum_ in SparseEnumOutOfOrder.values) { + message.sparseOutOfOrderEnums.add(enum_); + } + + final encoded = message.writeToBuffer(); + final decoded = MessageWithEnums.fromBuffer(encoded); + expect(decoded.denseEnums, DenseEnum.values); + expect(decoded.denseOutOfOrderEnums, DenseEnumOutOfOrder.values); + expect(decoded.sparseEnums, SparseEnum.values); + expect(decoded.sparseOutOfOrderEnums, SparseEnumOutOfOrder.values); + }); } diff --git a/protoc_plugin/test/protos/enums.proto b/protoc_plugin/test/protos/enums.proto new file mode 100644 index 000000000..03647c9d2 --- /dev/null +++ b/protoc_plugin/test/protos/enums.proto @@ -0,0 +1,54 @@ +enum DenseEnum { + DENSE_ENUM_0 = 0; + DENSE_ENUM_1 = 1; + DENSE_ENUM_2 = 2; + DENSE_ENUM_3 = 3; + DENSE_ENUM_4 = 4; +} + +enum DenseEnumOutOfOrder { + DENSE_ENUM_OOO_0 = 0; + DENSE_ENUM_OOO_2 = 2; + DENSE_ENUM_OOO_4 = 4; + DENSE_ENUM_OOO_3 = 3; + DENSE_ENUM_OOO_1 = 1; +} + +enum SparseEnum { + SPARSE_ENUM_ZERO = 0; + SPARSE_ENUM_MIN_INT = -2147483648; + SPARSE_ENUM_1 = -1000000000; + SPARSE_ENUM_2 = -100000000; + SPARSE_ENUM_3 = -10000000; + SPARSE_ENUM_4 = -1000000; + SPARSE_ENUM_5 = -100000; + SPARSE_ENUM_6 = 100000; + SPARSE_ENUM_7 = 1000000; + SPARSE_ENUM_8 = 10000000; + SPARSE_ENUM_9 = 100000000; + SPARSE_ENUM_10 = 1000000000; + SPARSE_ENUM_MAX_INT = 2147483647; +} + +enum SparseEnumOutOfOrder { + SPARSE_ENUM_OOO_ZERO = 0; + SPARSE_ENUM_OOO_1 = -1000000000; + SPARSE_ENUM_OOO_MAX_INT = 2147483647; + SPARSE_ENUM_OOO_4 = -1000000; + SPARSE_ENUM_OOO_6 = 100000; + SPARSE_ENUM_OOO_MIN_INT = -2147483648; + SPARSE_ENUM_OOO_7 = 1000000; + SPARSE_ENUM_OOO_5 = -100000; + SPARSE_ENUM_OOO_8 = 10000000; + SPARSE_ENUM_OOO_3 = -10000000; + SPARSE_ENUM_OOO_10 = 1000000000; + SPARSE_ENUM_OOO_2 = -100000000; + SPARSE_ENUM_OOO_9 = 100000000; +} + +message MessageWithEnums { + repeated DenseEnum denseEnums = 1; + repeated DenseEnumOutOfOrder denseOutOfOrderEnums = 2; + repeated SparseEnum sparseEnums = 3; + repeated SparseEnumOutOfOrder sparseOutOfOrderEnums = 4; +} From f4ccab102da2e6f03fbd813f2c170450516a1d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 12 May 2025 15:23:51 +0100 Subject: [PATCH 07/12] Fix bug --- protobuf/lib/src/protobuf/protobuf_enum.dart | 3 ++- protoc_plugin/lib/src/enum_generator.dart | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index 993d9c6e7..6c838cb2d 100644 --- a/protobuf/lib/src/protobuf/protobuf_enum.dart +++ b/protobuf/lib/src/protobuf/protobuf_enum.dart @@ -56,7 +56,8 @@ class ProtobufEnum { /// This function is for generated code. /// /// @nodoc - static List $_initByValueList(List enumValues) { + static List $_initByValueList( + List enumValues) { if (enumValues.isEmpty) return []; var maxValue = enumValues[0].value; diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index f2d048970..b14bee341 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -174,10 +174,20 @@ class EnumGenerator extends ProtobufContainer { out.println('];'); out.println(); + var maxEnumValue = -1; + for (final valueDescriptor in _canonicalValues) { + if (valueDescriptor.number.isNegative) { + maxEnumValue = -1; // don't use list + break; + } + if (valueDescriptor.number > maxEnumValue) { + maxEnumValue = valueDescriptor.number; + } + } + final useList = _canonicalValues.isEmpty || - (_canonicalValues.every((val) => !val.number.isNegative) && - _canonicalValues.length / (_canonicalValues.last.number + 1) >= - 0.7); + (maxEnumValue >= 0 && + _canonicalValues.length / (maxEnumValue + 1) >= 0.7); if (useList) { out.println( From a9959c33264f5f2302e1b01d32103d7595e97e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 12 May 2025 15:25:25 +0100 Subject: [PATCH 08/12] Fix warning --- protoc_plugin/test/generated_message_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/protoc_plugin/test/generated_message_test.dart b/protoc_plugin/test/generated_message_test.dart index 366bef2e1..63f8f1e45 100644 --- a/protoc_plugin/test/generated_message_test.dart +++ b/protoc_plugin/test/generated_message_test.dart @@ -11,7 +11,6 @@ import '../out/protos/constructor_args/google/protobuf/unittest_import.pb.dart' as constructor_args_unittest_import; import '../out/protos/duplicate_names_import.pb.dart'; import '../out/protos/enums.pb.dart'; -import '../out/protos/enums.pbenum.dart'; import '../out/protos/google/protobuf/unittest.pb.dart'; import '../out/protos/google/protobuf/unittest_import.pb.dart'; import '../out/protos/google/protobuf/unittest_optimize_for.pb.dart'; From 09603ac9c967c22dc5d17bd9f2881bcedbdf6067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Tue, 13 May 2025 09:09:11 +0100 Subject: [PATCH 09/12] Address comments --- protobuf/lib/src/protobuf/protobuf_enum.dart | 15 +++------------ protoc_plugin/lib/src/enum_generator.dart | 2 +- .../lib/src/generated/descriptor.pbenum.dart | 12 ++++++------ .../lib/src/generated/plugin.pbenum.dart | 2 +- protoc_plugin/test/goldens/deprecations.pbenum | 2 +- protoc_plugin/test/goldens/doc_comments.pbenum | 2 +- protoc_plugin/test/goldens/enum | 2 +- protoc_plugin/test/goldens/messageGeneratorEnums | 2 +- protoc_plugin/test/goldens/topLevelEnum.pbenum | 2 +- 9 files changed, 16 insertions(+), 25 deletions(-) diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index 6c838cb2d..a5fdb515c 100644 --- a/protobuf/lib/src/protobuf/protobuf_enum.dart +++ b/protobuf/lib/src/protobuf/protobuf_enum.dart @@ -57,18 +57,9 @@ class ProtobufEnum { /// /// @nodoc static List $_initByValueList( - List enumValues) { - if (enumValues.isEmpty) return []; - - var maxValue = enumValues[0].value; - for (var i = 0; i < enumValues.length; i += 1) { - final value = enumValues[i].value; - if (value > maxValue) { - maxValue = value; - } - } - - final byValue = List.filled(maxValue + 1, null); + List enumValues, int maxEnumValue) { + if (enumValues.isEmpty) return List.filled(0, null); + final byValue = List.filled(maxEnumValue + 1, null); for (final enumValue in enumValues) { byValue[enumValue.value] = enumValue; } diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index b14bee341..bb8a632e9 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -192,7 +192,7 @@ class EnumGenerator extends ProtobufContainer { if (useList) { out.println( 'static final $coreImportPrefix.List<$classname?> _byValue =' - ' $protobufImportPrefix.ProtobufEnum.\$_initByValueList(values);'); + ' $protobufImportPrefix.ProtobufEnum.\$_initByValueList(values, $maxEnumValue);'); out.println('static $classname? valueOf($coreImportPrefix.int value) =>' ' value < 0 || value >= _byValue.length ? null : _byValue[value];'); diff --git a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart index c9b1cb43f..15909ed5a 100644 --- a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart +++ b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart @@ -89,7 +89,7 @@ class FieldDescriptorProto_Type extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 18); static FieldDescriptorProto_Type? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -113,7 +113,7 @@ class FieldDescriptorProto_Label extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 3); static FieldDescriptorProto_Label? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -139,7 +139,7 @@ class FileOptions_OptimizeMode extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 3); static FileOptions_OptimizeMode? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -162,7 +162,7 @@ class FieldOptions_CType extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 2); static FieldOptions_CType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -189,7 +189,7 @@ class FieldOptions_JSType extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 2); static FieldOptions_JSType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; @@ -217,7 +217,7 @@ class MethodOptions_IdempotencyLevel extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 2); static MethodOptions_IdempotencyLevel? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/lib/src/generated/plugin.pbenum.dart b/protoc_plugin/lib/src/generated/plugin.pbenum.dart index 86e0b2d80..2f427c9f2 100644 --- a/protoc_plugin/lib/src/generated/plugin.pbenum.dart +++ b/protoc_plugin/lib/src/generated/plugin.pbenum.dart @@ -28,7 +28,7 @@ class CodeGeneratorResponse_Feature extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 1); static CodeGeneratorResponse_Feature? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/test/goldens/deprecations.pbenum b/protoc_plugin/test/goldens/deprecations.pbenum index d98728a7f..c21797e77 100644 --- a/protoc_plugin/test/goldens/deprecations.pbenum +++ b/protoc_plugin/test/goldens/deprecations.pbenum @@ -25,7 +25,7 @@ class A extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 1); static A? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/test/goldens/doc_comments.pbenum b/protoc_plugin/test/goldens/doc_comments.pbenum index 5ad1b4bfb..1a35f2418 100644 --- a/protoc_plugin/test/goldens/doc_comments.pbenum +++ b/protoc_plugin/test/goldens/doc_comments.pbenum @@ -27,7 +27,7 @@ class A extends $pb.ProtobufEnum { ]; static final $core.List _byValue = - $pb.ProtobufEnum.$_initByValueList(values); + $pb.ProtobufEnum.$_initByValueList(values, 1); static A? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; diff --git a/protoc_plugin/test/goldens/enum b/protoc_plugin/test/goldens/enum index a168024bb..2dca720ef 100644 --- a/protoc_plugin/test/goldens/enum +++ b/protoc_plugin/test/goldens/enum @@ -11,7 +11,7 @@ class PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values); + static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values, 2); static PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneType._(super.v, super.n); diff --git a/protoc_plugin/test/goldens/messageGeneratorEnums b/protoc_plugin/test/goldens/messageGeneratorEnums index bb244bc9f..3d673dad4 100644 --- a/protoc_plugin/test/goldens/messageGeneratorEnums +++ b/protoc_plugin/test/goldens/messageGeneratorEnums @@ -11,7 +11,7 @@ class PhoneNumber_PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values); + static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values, 2); static PhoneNumber_PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneNumber_PhoneType._(super.v, super.n); diff --git a/protoc_plugin/test/goldens/topLevelEnum.pbenum b/protoc_plugin/test/goldens/topLevelEnum.pbenum index 5d455aea1..69cb70d28 100644 --- a/protoc_plugin/test/goldens/topLevelEnum.pbenum +++ b/protoc_plugin/test/goldens/topLevelEnum.pbenum @@ -26,7 +26,7 @@ class PhoneType extends $pb.ProtobufEnum { WORK, ]; - static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values); + static final $core.List _byValue = $pb.ProtobufEnum.$_initByValueList(values, 2); static PhoneType? valueOf($core.int value) => value < 0 || value >= _byValue.length ? null : _byValue[value]; const PhoneType._(super.v, super.n); From d09daf5f291fcf533a0a3e1f2b627eff49c8ce22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Tue, 13 May 2025 09:11:56 +0100 Subject: [PATCH 10/12] Remove redundnat isEmpty check --- protobuf/lib/src/protobuf/protobuf_enum.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index a5fdb515c..6ec5a5780 100644 --- a/protobuf/lib/src/protobuf/protobuf_enum.dart +++ b/protobuf/lib/src/protobuf/protobuf_enum.dart @@ -58,7 +58,6 @@ class ProtobufEnum { /// @nodoc static List $_initByValueList( List enumValues, int maxEnumValue) { - if (enumValues.isEmpty) return List.filled(0, null); final byValue = List.filled(maxEnumValue + 1, null); for (final enumValue in enumValues) { byValue[enumValue.value] = enumValue; From a330fc4cb25f832411d9c7c1cf306b5a23f9d936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Tue, 13 May 2025 09:37:51 +0100 Subject: [PATCH 11/12] Remove accidental whitespace change --- protoc_plugin/lib/src/enum_generator.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index bb8a632e9..287a4fe1e 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -165,6 +165,7 @@ class EnumGenerator extends ProtobufContainer { } } out.println(); + out.println('static const $coreImportPrefix.List<$classname> values =' ' <$classname> ['); for (final val in _canonicalValues) { From 2cc0faae3518a601f4980696f42dc4967836649a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Wed, 14 May 2025 15:50:49 +0100 Subject: [PATCH 12/12] Rename for consistency --- protobuf/lib/src/protobuf/protobuf_enum.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index 6ec5a5780..46c173e33 100644 --- a/protobuf/lib/src/protobuf/protobuf_enum.dart +++ b/protobuf/lib/src/protobuf/protobuf_enum.dart @@ -47,8 +47,8 @@ class ProtobufEnum { /// @nodoc static Map initByValue(List enumValues) { final byValue = {}; - for (final v in enumValues) { - byValue[v.value] = v; + for (final enumValue in enumValues) { + byValue[enumValue.value] = enumValue; } return byValue; }