[ObjC] Add initializing a `GPBUnknownFields` from a message. PiperOrigin-RevId: 650388684
diff --git a/objectivec/GPBUnknownFields.m b/objectivec/GPBUnknownFields.m index 8856ac5..ee33de1 100644 --- a/objectivec/GPBUnknownFields.m +++ b/objectivec/GPBUnknownFields.m
@@ -6,15 +6,28 @@ // https://developers.google.com/open-source/licenses/bsd #import "GPBUnknownFields.h" + +#import <Foundation/Foundation.h> + +#import "GPBCodedInputStream_PackagePrivate.h" #import "GPBCodedOutputStream.h" #import "GPBCodedOutputStream_PackagePrivate.h" +#import "GPBMessage.h" +#import "GPBUnknownField.h" +#import "GPBUnknownFieldSet_PackagePrivate.h" #import "GPBUnknownField_PackagePrivate.h" +#import "GPBUnknownFields_PackagePrivate.h" +#import "GPBWireFormat.h" #define CHECK_FIELD_NUMBER(number) \ if (number <= 0) { \ [NSException raise:NSInvalidArgumentException format:@"Not a valid field number."]; \ } +@interface GPBUnknownFields () +- (BOOL)mergeFromInputStream:(nonnull GPBCodedInputStream *)input endTag:(uint32_t)endTag; +@end + @implementation GPBUnknownFields { @package NSMutableArray<GPBUnknownField *> *fields_; @@ -26,6 +39,29 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdirect-ivar-access" +- (instancetype)initFromMessage:(nonnull GPBMessage *)message { + self = [super init]; + if (self) { + fields_ = [[NSMutableArray alloc] init]; + // TODO: b/349146447 - Move off the legacy class and directly to the data once Message is + // updated. + GPBUnknownFieldSet *legacyUnknownFields = [message unknownFields]; + if (legacyUnknownFields) { + GPBCodedInputStream *input = + [[GPBCodedInputStream alloc] initWithData:[legacyUnknownFields data]]; + // Parse until the end of the data (tag will be zero). + if (![self mergeFromInputStream:input endTag:0]) { + [input release]; + [self release]; + [NSException raise:NSInternalInconsistencyException + format:@"Internal error: Unknown field data from message was malformed."]; + } + [input release]; + } + } + return self; +} + - (instancetype)init { self = [super init]; if (self) { @@ -177,6 +213,88 @@ 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)