[ObjC] Keep `GPBUnknownField` simpler.

Move some of the new logic out of `GPBUnknownField` so it will end up as a much
simpler "container", with all the serialization logic inside `GPBUnknownFields`
instead.

Also move some of the internal logic needed into static C functions to reduce the
ObjC class overhead of `GPBUnknownFields`.

This was all inspired by realizing during serialization related apis the `type` of
each field was being examined multiple times and reduces that in addition to reducing
the number of methods being invoked.

PiperOrigin-RevId: 650631975
diff --git a/objectivec/GPBUnknownFields.m b/objectivec/GPBUnknownFields.m
index ee33de1..e24e07b 100644
--- a/objectivec/GPBUnknownFields.m
+++ b/objectivec/GPBUnknownFields.m
@@ -24,14 +24,15 @@
     [NSException raise:NSInvalidArgumentException format:@"Not a valid field number."]; \
   }
 
-@interface GPBUnknownFields ()
-- (BOOL)mergeFromInputStream:(nonnull GPBCodedInputStream *)input endTag:(uint32_t)endTag;
-@end
+// TODO: Consider using on other functions to reduce bloat when
+// some compiler optimizations are enabled.
+#define GPB_NOINLINE __attribute__((noinline))
 
-@implementation GPBUnknownFields {
+@interface GPBUnknownFields () {
  @package
   NSMutableArray<GPBUnknownField *> *fields_;
 }
+@end
 
 // Direct access is use for speed, to avoid even internally declaring things
 // read/write, etc. The warning is enabled in the project to ensure code calling
@@ -39,6 +40,157 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdirect-ivar-access"
 
+GPB_NOINLINE
+static size_t ComputeSerializeSize(GPBUnknownFields *_Nonnull self) {
+  size_t result = 0;
+  for (GPBUnknownField *field in self->fields_) {
+    uint32_t fieldNumber = field->number_;
+    switch (field->type_) {
+      case GPBUnknownFieldTypeVarint:
+        result += GPBComputeUInt64Size(fieldNumber, field->storage_.intValue);
+        break;
+      case GPBUnknownFieldTypeFixed32:
+        result += GPBComputeFixed32Size(fieldNumber, (uint32_t)field->storage_.intValue);
+        break;
+      case GPBUnknownFieldTypeFixed64:
+        result += GPBComputeFixed64Size(fieldNumber, field->storage_.intValue);
+        break;
+      case GPBUnknownFieldTypeLengthDelimited:
+        result += GPBComputeBytesSize(fieldNumber, field->storage_.lengthDelimited);
+        break;
+      case GPBUnknownFieldTypeGroup:
+        result +=
+            (GPBComputeTagSize(fieldNumber) * 2) + ComputeSerializeSize(field->storage_.group);
+        break;
+      case GPBUnknownFieldTypeLegacy:
+#if defined(DEBUG) && DEBUG
+        NSCAssert(NO, @"Internal error within the library");
+#endif
+        break;
+    }
+  }
+  return result;
+}
+
+GPB_NOINLINE
+static void WriteToCoddedOutputStream(GPBUnknownFields *_Nonnull self,
+                                      GPBCodedOutputStream *_Nonnull output) {
+  for (GPBUnknownField *field in self->fields_) {
+    uint32_t fieldNumber = field->number_;
+    switch (field->type_) {
+      case GPBUnknownFieldTypeVarint:
+        [output writeUInt64:fieldNumber value:field->storage_.intValue];
+        break;
+      case GPBUnknownFieldTypeFixed32:
+        [output writeFixed32:fieldNumber value:(uint32_t)field->storage_.intValue];
+        break;
+      case GPBUnknownFieldTypeFixed64:
+        [output writeFixed64:fieldNumber value:field->storage_.intValue];
+        break;
+      case GPBUnknownFieldTypeLengthDelimited:
+        [output writeBytes:fieldNumber value:field->storage_.lengthDelimited];
+        break;
+      case GPBUnknownFieldTypeGroup:
+        [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatStartGroup)];
+        WriteToCoddedOutputStream(field->storage_.group, output);
+        [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)];
+        break;
+      case GPBUnknownFieldTypeLegacy:
+#if defined(DEBUG) && DEBUG
+        NSCAssert(NO, @"Internal error within the library");
+#endif
+        break;
+    }
+  }
+}
+
+GPB_NOINLINE
+static BOOL MergeFromInputStream(GPBUnknownFields *self, GPBCodedInputStream *input,
+                                 uint32_t endTag) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(endTag == 0 || GPBWireFormatGetTagWireType(endTag) == GPBWireFormatEndGroup,
+            @"Internal error:Invalid end tag: %u", endTag);
+#endif
+  GPBCodedInputStreamState *state = &input->state_;
+  NSMutableArray<GPBUnknownField *> *fields = self->fields_;
+  @try {
+    while (YES) {
+      uint32_t tag = GPBCodedInputStreamReadTag(state);
+      if (tag == endTag) {
+        return YES;
+      }
+      if (tag == 0) {
+        // Reached end of input without finding the end tag.
+        return NO;
+      }
+      GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
+      int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
+      switch (wireType) {
+        case GPBWireFormatVarint: {
+          uint64_t value = GPBCodedInputStreamReadInt64(state);
+          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
+                                                                    varint:value];
+          [fields addObject:field];
+          [field release];
+          break;
+        }
+        case GPBWireFormatFixed32: {
+          uint32_t value = GPBCodedInputStreamReadFixed32(state);
+          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
+                                                                   fixed32:value];
+          [fields addObject:field];
+          [field release];
+          break;
+        }
+        case GPBWireFormatFixed64: {
+          uint64_t value = GPBCodedInputStreamReadFixed64(state);
+          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
+                                                                   fixed64:value];
+          [fields addObject:field];
+          [field release];
+          break;
+        }
+        case GPBWireFormatLengthDelimited: {
+          NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
+          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
+                                                           lengthDelimited:data];
+          [fields addObject:field];
+          [field release];
+          [data release];
+          break;
+        }
+        case GPBWireFormatStartGroup: {
+          GPBUnknownFields *group = [[GPBUnknownFields alloc] init];
+          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group];
+          [fields addObject:field];
+          [field release];
+          [group release];  // Still will be held in the field/fields.
+          uint32_t endGroupTag = GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup);
+          if (MergeFromInputStream(group, input, endGroupTag)) {
+            GPBCodedInputStreamCheckLastTagWas(state, endGroupTag);
+          } else {
+            [NSException
+                 raise:NSInternalInconsistencyException
+                format:@"Internal error: Unknown field data for nested group was malformed."];
+          }
+          break;
+        }
+        case GPBWireFormatEndGroup:
+          [NSException raise:NSInternalInconsistencyException
+                      format:@"Unexpected end group tag: %u", tag];
+          break;
+      }
+    }
+  } @catch (NSException *exception) {
+#if defined(DEBUG) && DEBUG
+    NSLog(@"%@: Internal exception while parsing unknown data, this shouldn't happen!: %@",
+          [self class], exception);
+#endif
+  }
+}
+
+@implementation GPBUnknownFields
+
 - (instancetype)initFromMessage:(nonnull GPBMessage *)message {
   self = [super init];
   if (self) {
@@ -50,7 +202,7 @@
       GPBCodedInputStream *input =
           [[GPBCodedInputStream alloc] initWithData:[legacyUnknownFields data]];
       // Parse until the end of the data (tag will be zero).
-      if (![self mergeFromInputStream:input endTag:0]) {
+      if (!MergeFromInputStream(self, input, 0)) {
         [input release];
         [self release];
         [NSException raise:NSInternalInconsistencyException
@@ -177,29 +329,15 @@
 
 #pragma mark - Internal Methods
 
-- (size_t)serializedSize {
-  size_t result = 0;
-  for (GPBUnknownField *field in self->fields_) {
-    result += [field serializedSize];
-  }
-  return result;
-}
-
-- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
-  for (GPBUnknownField *field in fields_) {
-    [field writeToOutput:output];
-  }
-}
-
 - (NSData *)serializeAsData {
   if (fields_.count == 0) {
     return [NSData data];
   }
-  size_t expectedSize = [self serializedSize];
+  size_t expectedSize = ComputeSerializeSize(self);
   NSMutableData *data = [NSMutableData dataWithLength:expectedSize];
   GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data];
   @try {
-    [self writeToCodedOutputStream:stream];
+    WriteToCoddedOutputStream(self, stream);
     [stream flush];
   } @catch (NSException *exception) {
 #if defined(DEBUG) && DEBUG
@@ -213,88 +351,6 @@
   return data;
 }
 
-- (BOOL)mergeFromInputStream:(nonnull GPBCodedInputStream *)input endTag:(uint32_t)endTag {
-#if defined(DEBUG) && DEBUG
-  NSAssert(endTag == 0 || GPBWireFormatGetTagWireType(endTag) == GPBWireFormatEndGroup,
-           @"Internal error:Invalid end tag: %u", endTag);
-#endif
-  GPBCodedInputStreamState *state = &input->state_;
-  @try {
-    while (YES) {
-      uint32_t tag = GPBCodedInputStreamReadTag(state);
-      if (tag == endTag) {
-        return YES;
-      }
-      if (tag == 0) {
-        // Reached end of input without finding the end tag.
-        return NO;
-      }
-      GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
-      int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
-      switch (wireType) {
-        case GPBWireFormatVarint: {
-          uint64_t value = GPBCodedInputStreamReadInt64(state);
-          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
-                                                                    varint:value];
-          [fields_ addObject:field];
-          [field release];
-          break;
-        }
-        case GPBWireFormatFixed32: {
-          uint32_t value = GPBCodedInputStreamReadFixed32(state);
-          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
-                                                                   fixed32:value];
-          [fields_ addObject:field];
-          [field release];
-          break;
-        }
-        case GPBWireFormatFixed64: {
-          uint64_t value = GPBCodedInputStreamReadFixed64(state);
-          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
-                                                                   fixed64:value];
-          [fields_ addObject:field];
-          [field release];
-          break;
-        }
-        case GPBWireFormatLengthDelimited: {
-          NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
-          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
-                                                           lengthDelimited:data];
-          [fields_ addObject:field];
-          [field release];
-          [data release];
-          break;
-        }
-        case GPBWireFormatStartGroup: {
-          GPBUnknownFields *group = [[GPBUnknownFields alloc] init];
-          GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group];
-          [fields_ addObject:field];
-          [field release];
-          [group release];  // Still will be held in the field/fields_.
-          uint32_t endGroupTag = GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup);
-          if ([group mergeFromInputStream:input endTag:endGroupTag]) {
-            GPBCodedInputStreamCheckLastTagWas(state, endGroupTag);
-          } else {
-            [NSException
-                 raise:NSInternalInconsistencyException
-                format:@"Internal error: Unknown field data for nested group was malformed."];
-          }
-          break;
-        }
-        case GPBWireFormatEndGroup:
-          [NSException raise:NSInternalInconsistencyException
-                      format:@"Unexpected end group tag: %u", tag];
-          break;
-      }
-    }
-  } @catch (NSException *exception) {
-#if defined(DEBUG) && DEBUG
-    NSLog(@"%@: Internal exception while parsing unknown data, this shouldn't happen!: %@",
-          [self class], exception);
-#endif
-  }
-}
-
 @end
 
 @implementation GPBUnknownFields (AccessHelpers)
@@ -352,6 +408,6 @@
   return nil;
 }
 
-#pragma clang diagnostic pop
-
 @end
+
+#pragma clang diagnostic pop