diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart index b96c119a4..46c173e33 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,12 +39,28 @@ 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 + /// This function is for generated code. + /// + /// Creates a Map for all of the [ProtobufEnum]s in [enumValues], mapping each /// [ProtobufEnum]'s [value] to the [ProtobufEnum]. - static Map initByValue(List byIndex) { + /// + /// @nodoc + static Map initByValue(List enumValues) { final byValue = {}; - for (final v in byIndex) { - byValue[v.value] = v; + for (final enumValue in enumValues) { + byValue[enumValue.value] = enumValue; + } + return byValue; + } + + /// This function is for generated code. + /// + /// @nodoc + static List $_initByValueList( + List enumValues, int maxEnumValue) { + final byValue = List.filled(maxEnumValue + 1, null); + for (final enumValue in enumValues) { + byValue[enumValue.value] = enumValue; } return byValue; } diff --git a/protoc_plugin/Makefile b/protoc_plugin/Makefile index bab82e37d..6c04fa5cc 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/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index a1cd8b5f7..287a4fe1e 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -175,11 +175,37 @@ 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 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 || + (maxEnumValue >= 0 && + _canonicalValues.length / (maxEnumValue + 1) >= 0.7); + + if (useList) { + out.println( + 'static final $coreImportPrefix.List<$classname?> _byValue =' + ' $protobufImportPrefix.ProtobufEnum.\$_initByValueList(values, $maxEnumValue);'); + + 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.initByValue(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/descriptor.pbenum.dart b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart index dc6f349d3..15909ed5a 100644 --- a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart +++ b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart @@ -88,9 +88,10 @@ 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, 18); + static FieldDescriptorProto_Type? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; const FieldDescriptorProto_Type._(super.v, super.n); } @@ -111,10 +112,10 @@ 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, 3); static FieldDescriptorProto_Label? valueOf($core.int value) => - _byValue[value]; + value < 0 || value >= _byValue.length ? null : _byValue[value]; const FieldDescriptorProto_Label._(super.v, super.n); } @@ -137,9 +138,10 @@ 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, 3); + static FileOptions_OptimizeMode? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; const FileOptions_OptimizeMode._(super.v, super.n); } @@ -159,9 +161,10 @@ 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, 2); + static FieldOptions_CType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; const FieldOptions_CType._(super.v, super.n); } @@ -185,9 +188,10 @@ 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, 2); + static FieldOptions_JSType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; const FieldOptions_JSType._(super.v, super.n); } @@ -212,10 +216,10 @@ 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, 2); static MethodOptions_IdempotencyLevel? valueOf($core.int value) => - _byValue[value]; + value < 0 || value >= _byValue.length ? null : _byValue[value]; const MethodOptions_IdempotencyLevel._(super.v, super.n); } diff --git a/protoc_plugin/lib/src/generated/plugin.pbenum.dart b/protoc_plugin/lib/src/generated/plugin.pbenum.dart index 399158c55..2f427c9f2 100644 --- a/protoc_plugin/lib/src/generated/plugin.pbenum.dart +++ b/protoc_plugin/lib/src/generated/plugin.pbenum.dart @@ -27,10 +27,10 @@ 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, 1); static CodeGeneratorResponse_Feature? valueOf($core.int value) => - _byValue[value]; + value < 0 || value >= _byValue.length ? null : _byValue[value]; const CodeGeneratorResponse_Feature._(super.v, super.n); } diff --git a/protoc_plugin/test/generated_message_test.dart b/protoc_plugin/test/generated_message_test.dart index a25f6a4d4..63f8f1e45 100644 --- a/protoc_plugin/test/generated_message_test.dart +++ b/protoc_plugin/test/generated_message_test.dart @@ -10,6 +10,7 @@ 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/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 +891,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/goldens/deprecations.pbenum b/protoc_plugin/test/goldens/deprecations.pbenum index 30bc5d782..c21797e77 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, 1); + 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..1a35f2418 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, 1); + 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..2dca720ef 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, 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 3469c7bab..3d673dad4 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, 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 30d81a24e..69cb70d28 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, 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/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; +}