Merge remote-tracking branch 'origin/master' into improve_enum_decoding_2
diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart
index b96c119..970d20f 100644
--- a/protobuf/lib/src/protobuf/protobuf_enum.dart
+++ b/protobuf/lib/src/protobuf/protobuf_enum.dart
@@ -37,9 +37,16 @@
   /// 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<int, T> initByValue<T extends ProtobufEnum>(List<T> byIndex) {
+  static List<T?> initByValueList<T extends ProtobufEnum>(List<T> byIndex) {
+    if (byIndex.isEmpty) return [];
+    final byValue = List<T?>.filled(byIndex.last.value + 1, null);
+    for (final enumValue in byIndex) {
+      byValue[enumValue.value] = enumValue;
+    }
+    return byValue;
+  }
+
+  static Map<int, T> initByValueMap<T extends ProtobufEnum>(List<T> byIndex) {
     final byValue = <int, T>{};
     for (final v in byIndex) {
       byValue[v.value] = v;
diff --git a/protoc_plugin/lib/protoc.dart b/protoc_plugin/lib/protoc.dart
index e6d175b..2ce0dd0 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 a1cd8b5..c66c2f9 100644
--- a/protoc_plugin/lib/src/enum_generator.dart
+++ b/protoc_plugin/lib/src/enum_generator.dart
@@ -102,6 +102,9 @@
   static const int _enumValueTag = 2;
 
   void generate(IndentingWriter out) {
+    assert(_canonicalValues
+        .isSortedBy<num>((EnumValueDescriptorProto a) => a.number));
+
     final commentBlock = fileGen?.commentBlock(fieldPath);
     if (commentBlock != null) {
       out.println(commentBlock);
@@ -165,7 +168,6 @@
         }
       }
       out.println();
-
       out.println('static const $coreImportPrefix.List<$classname> values ='
           ' <$classname> [');
       for (final val in _canonicalValues) {
@@ -175,11 +177,32 @@
       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/descriptor.pbenum.dart b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart
index dc6f349..0c205c7 100644
--- a/protoc_plugin/lib/src/generated/descriptor.pbenum.dart
+++ b/protoc_plugin/lib/src/generated/descriptor.pbenum.dart
@@ -88,9 +88,10 @@
     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<FieldDescriptorProto_Type?> _byValue =
+      $pb.ProtobufEnum.initByValueList(values);
+  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 @@
     LABEL_REPEATED,
   ];
 
-  static final $core.Map<$core.int, FieldDescriptorProto_Label> _byValue =
-      $pb.ProtobufEnum.initByValue(values);
+  static final $core.List<FieldDescriptorProto_Label?> _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._(super.v, super.n);
 }
@@ -137,9 +138,10 @@
     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<FileOptions_OptimizeMode?> _byValue =
+      $pb.ProtobufEnum.initByValueList(values);
+  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 @@
     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<FieldOptions_CType?> _byValue =
+      $pb.ProtobufEnum.initByValueList(values);
+  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 @@
     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<FieldOptions_JSType?> _byValue =
+      $pb.ProtobufEnum.initByValueList(values);
+  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 @@
     IDEMPOTENT,
   ];
 
-  static final $core.Map<$core.int, MethodOptions_IdempotencyLevel> _byValue =
-      $pb.ProtobufEnum.initByValue(values);
+  static final $core.List<MethodOptions_IdempotencyLevel?> _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._(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 399158c..23a359d 100644
--- a/protoc_plugin/lib/src/generated/plugin.pbenum.dart
+++ b/protoc_plugin/lib/src/generated/plugin.pbenum.dart
@@ -27,10 +27,10 @@
     FEATURE_PROTO3_OPTIONAL,
   ];
 
-  static final $core.Map<$core.int, CodeGeneratorResponse_Feature> _byValue =
-      $pb.ProtobufEnum.initByValue(values);
+  static final $core.List<CodeGeneratorResponse_Feature?> _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._(super.v, super.n);
 }
diff --git a/protoc_plugin/test/goldens/deprecations.pbenum b/protoc_plugin/test/goldens/deprecations.pbenum
index 30bc5d7..59d98c7 100644
--- a/protoc_plugin/test/goldens/deprecations.pbenum
+++ b/protoc_plugin/test/goldens/deprecations.pbenum
@@ -24,9 +24,10 @@
     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<A?> _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 7cde50e..3a7e818 100644
--- a/protoc_plugin/test/goldens/doc_comments.pbenum
+++ b/protoc_plugin/test/goldens/doc_comments.pbenum
@@ -26,9 +26,10 @@
     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<A?> _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 812247d..2cc22d6 100644
--- a/protoc_plugin/test/goldens/enum
+++ b/protoc_plugin/test/goldens/enum
@@ -11,8 +11,8 @@
     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<PhoneType?> _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 3469c7b..ad35cc4 100644
--- a/protoc_plugin/test/goldens/messageGeneratorEnums
+++ b/protoc_plugin/test/goldens/messageGeneratorEnums
@@ -11,8 +11,8 @@
     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<PhoneNumber_PhoneType?> _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 30d81a2..6ebd26f 100644
--- a/protoc_plugin/test/goldens/topLevelEnum.pbenum
+++ b/protoc_plugin/test/goldens/topLevelEnum.pbenum
@@ -26,8 +26,8 @@
     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<PhoneType?> _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);
 }