[ObjC] Add initializing a `GPBUnknownFields` from a message.

PiperOrigin-RevId: 650388684
pull/17370/head
Thomas Van Lenten 7 months ago committed by Copybara-Service
parent fba399ad35
commit c2299860fd
  1. 11
      objectivec/GPBUnknownFields.h
  2. 118
      objectivec/GPBUnknownFields.m
  3. 30
      objectivec/Tests/GPBUnknownFieldsTest.m

@ -7,6 +7,7 @@
#import <Foundation/Foundation.h>
@class GPBMessage;
@class GPBUnknownField;
NS_ASSUME_NONNULL_BEGIN
@ -26,6 +27,16 @@ NS_ASSUME_NONNULL_BEGIN
__attribute__((objc_subclassing_restricted))
@interface GPBUnknownFields : NSObject<NSCopying, NSFastEnumeration>
/**
* Initializes a new instance with the data from the unknown fields from the given
* message.
*
* Note: The instance is not linked to the message, any change will not be
* reflected on the message the changes have to be pushed back to the message
* with `-[GPBMessage mergeUnknownFields:error:]`.
**/
- (instancetype)initFromMessage:(nonnull GPBMessage *)message;
/**
* Initializes a new empty instance.
**/

@ -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)

@ -566,6 +566,8 @@
[ufs addFieldNumber:TestAllTypes_FieldNumber_OptionalBytes lengthDelimited:DataFromCStr("foo")];
GPBUnknownFields* group = [ufs addGroupWithFieldNumber:TestAllTypes_FieldNumber_OptionalGroup];
[group addFieldNumber:TestAllTypes_OptionalGroup_FieldNumber_A varint:55];
[ufs addFieldNumber:123456 varint:4321];
[group addFieldNumber:123456 varint:5432];
TestAllTypes* msg = [TestAllTypes message];
[msg mergeUnknownFields:ufs extensionRegistry:nil];
@ -574,6 +576,34 @@
XCTAssertEqual(msg.optionalFixed64, 300);
XCTAssertEqualObjects(msg.optionalBytes, DataFromCStr("foo"));
XCTAssertEqual(msg.optionalGroup.a, 55);
GPBUnknownFields* ufs2 = [[[GPBUnknownFields alloc] initFromMessage:msg] autorelease];
XCTAssertEqual(ufs2.count, 1); // The unknown at the root
uint64_t varint = 0;
XCTAssertTrue([ufs2 getFirst:123456 varint:&varint]);
XCTAssertEqual(varint, 4321);
GPBUnknownFields* ufs2group =
[[[GPBUnknownFields alloc] initFromMessage:msg.optionalGroup] autorelease];
XCTAssertEqual(ufs2group.count, 1); // The unknown at in group
XCTAssertTrue([ufs2group getFirst:123456 varint:&varint]);
XCTAssertEqual(varint, 5432);
TestEmptyMessage* emptyMessage = [TestEmptyMessage message];
[emptyMessage mergeUnknownFields:ufs extensionRegistry:nil];
GPBUnknownFields* ufs3 = [[[GPBUnknownFields alloc] initFromMessage:emptyMessage] autorelease];
XCTAssertEqualObjects(ufs3, ufs); // Round trip through an empty message got us same fields back.
XCTAssertTrue(ufs3 != ufs); // But they are different objects.
}
- (void)testRoundTripLotsOfFields {
// Usage a message with everything, into an empty message to get a lot of unknown fields,
// and confirm it comes back to match.
TestAllTypes* allFields = [self allSetRepeatedCount:kGPBDefaultRepeatCount];
NSData* allFieldsData = [allFields data];
TestEmptyMessage* emptyMessage = [TestEmptyMessage parseFromData:allFieldsData error:NULL];
GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:emptyMessage] autorelease];
TestAllTypes* allFields2 = [TestAllTypes message];
[allFields2 mergeUnknownFields:ufs extensionRegistry:nil];
XCTAssertEqualObjects(allFields2, allFields);
}
@end

Loading…
Cancel
Save