Remove fold() closures from `FieldSet._hashCode`. (#554)

The fold() calls come with significant runtime cost, and make it more
difficult to hoist accesses to commonly used variables. Just rewriting
this pattern shows a significant performance improvement for both JS and
VM benchmarks.

Baseline

```
JS
HashCode(RunTime): 9833.333333333334 us.
HashCode(RunTime): 9891.625615763547 us.
HashCode(RunTime): 9607.655502392345 us.
HashCode(RunTime): 9661.835748792271 us.
HashCode(RunTime): 9765.853658536585 us.
VM
HashCode(RunTime): 4527.384615384615 us.
HashCode(RunTime): 4534.151583710407 us.
HashCode(RunTime): 4546.556818181818 us.
HashCode(RunTime): 4490.65470852017
```

Results

```
JS
HashCode(RunTime): 8004 us.
HashCode(RunTime): 7980.0796812749 us.
HashCode(RunTime): 7976.095617529881 us.
HashCode(RunTime): 7824.21875 us.
HashCode(RunTime): 7847.058823529412 us.
VM
HashCode(RunTime): 2474.5451174289246 us.
HashCode(RunTime): 2533.7037974683544 us.
HashCode(RunTime): 2532.4556962025317 us.
HashCode(RunTime): 2420.072551390568 us.
HashCode(RunTime): 2521.3198992443326
```

Co-authored-by: Loren Van Spronsen <lorenvs@google.com>
diff --git a/protobuf/lib/src/protobuf/field_set.dart b/protobuf/lib/src/protobuf/field_set.dart
index 9800563..9e702b6 100644
--- a/protobuf/lib/src/protobuf/field_set.dart
+++ b/protobuf/lib/src/protobuf/field_set.dart
@@ -678,26 +678,27 @@
       return hash;
     }
 
-    int hashEachField(int hash) {
-      //non-extension fields
-      hash = _infosSortedByTag.where((fi) => _values[fi.index!] != null).fold(
-          hash, (int h, FieldInfo fi) => hashField(h, fi, _values[fi.index!]));
-
-      if (!_hasExtensions) return hash;
-
-      hash =
-          _sorted(_extensions!._tagNumbers).fold(hash, (int h, int tagNumber) {
-        var fi = _extensions!._getInfoOrNull(tagNumber)!;
-        return hashField(h, fi, _extensions!._getFieldOrNull(fi));
-      });
-
-      return hash;
-    }
-
     // Hash with descriptor.
     var hash = _HashUtils._combine(0, _meta.hashCode);
-    // Hash with fields.
-    hash = hashEachField(hash);
+
+    // Hash with non-extension fields.
+    final values = _values;
+    for (final fi in _infosSortedByTag) {
+      final value = values[fi.index!];
+      if (value == null) continue;
+      hash = hashField(hash, fi, value);
+    }
+
+    // Hash with extension fields.
+    if (_hasExtensions) {
+      final extensions = _extensions!;
+      final sortedByTagNumbers = _sorted(extensions._tagNumbers);
+      for (final tagNumber in sortedByTagNumbers) {
+        final fi = extensions._getInfoOrNull(tagNumber)!;
+        hash = hashField(hash, fi, extensions._getFieldOrNull(fi));
+      }
+    }
+
     // Hash with unknown fields.
     if (_hasUnknownFields) {
       hash = _HashUtils._combine(hash, _unknownFields.hashCode);