Skip to content

Commit

Permalink
Add GPBMessageDropUnknownFieldsRecursively() and tests.
Browse files Browse the repository at this point in the history
GPBMessageDropUnknownFieldsRecursively() is a new helper to drop the
unknownFields from a message and all sub messages (in fields or extensions).
  • Loading branch information
thomasvl committed Feb 23, 2017
1 parent 17174b5 commit d071766
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 0 deletions.
5 changes: 5 additions & 0 deletions objectivec/GPBUtilities.h
Expand Up @@ -392,6 +392,11 @@ void GPBSetMessageMapField(GPBMessage *self,
**/
NSData *GPBEmptyNSData(void) __attribute__((pure));

/**
* Drops the `unknownFields` from the given message and from all sub message.
**/
void GPBMessageDropUnknownFieldsRecursively(GPBMessage *message);

NS_ASSUME_NONNULL_END

CF_EXTERN_C_END
Expand Down
119 changes: 119 additions & 0 deletions objectivec/GPBUtilities.m
Expand Up @@ -58,6 +58,125 @@ static void AppendTextFormatForMessage(GPBMessage *message,
return defaultNSData;
}

void GPBMessageDropUnknownFieldsRecursively(GPBMessage *initialMessage) {
if (!initialMessage) {
return;
}

// Use an array as a list to process to avoid recursion.
NSMutableArray *todo = [NSMutableArray arrayWithObject:initialMessage];

while (todo.count) {
GPBMessage *msg = todo.lastObject;
[todo removeLastObject];

// Clear unknowns.
msg.unknownFields = nil;

// Handle the message fields.
GPBDescriptor *descriptor = [[msg class] descriptor];
for (GPBFieldDescriptor *field in descriptor->fields_) {
if (!GPBFieldDataTypeIsMessage(field)) {
continue;
}
switch (field.fieldType) {
case GPBFieldTypeSingle:
if (GPBGetHasIvarField(msg, field)) {
GPBMessage *fieldMessage = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
[todo addObject:fieldMessage];
}
break;

case GPBFieldTypeRepeated: {
NSArray *fieldMessages = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
if (fieldMessages.count) {
[todo addObjectsFromArray:fieldMessages];
}
break;
}

case GPBFieldTypeMap: {
id rawFieldMap = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
switch (field.mapKeyDataType) {
case GPBDataTypeBool:
[(GPBBoolObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
BOOL key, id _Nonnull object, BOOL * _Nonnull stop) {
#pragma unused(key, stop)
[todo addObject:object];
}];
break;
case GPBDataTypeFixed32:
case GPBDataTypeUInt32:
[(GPBUInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
uint32_t key, id _Nonnull object, BOOL * _Nonnull stop) {
#pragma unused(key, stop)
[todo addObject:object];
}];
break;
case GPBDataTypeInt32:
case GPBDataTypeSFixed32:
case GPBDataTypeSInt32:
[(GPBInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
int32_t key, id _Nonnull object, BOOL * _Nonnull stop) {
#pragma unused(key, stop)
[todo addObject:object];
}];
break;
case GPBDataTypeFixed64:
case GPBDataTypeUInt64:
[(GPBUInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
uint64_t key, id _Nonnull object, BOOL * _Nonnull stop) {
#pragma unused(key, stop)
[todo addObject:object];
}];
break;
case GPBDataTypeInt64:
case GPBDataTypeSFixed64:
case GPBDataTypeSInt64:
[(GPBInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
int64_t key, id _Nonnull object, BOOL * _Nonnull stop) {
#pragma unused(key, stop)
[todo addObject:object];
}];
break;
case GPBDataTypeString:
[(NSDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
NSString * _Nonnull key, GPBMessage * _Nonnull obj, BOOL * _Nonnull stop) {
#pragma unused(key, stop)
[todo addObject:obj];
}];
break;
case GPBDataTypeFloat:
case GPBDataTypeDouble:
case GPBDataTypeEnum:
case GPBDataTypeBytes:
case GPBDataTypeGroup:
case GPBDataTypeMessage:
NSCAssert(NO, @"Aren't valid key types.");
}
break;
} // switch(field.mapKeyDataType)
} // switch(field.fieldType)
} // for(fields)

// Handle any extensions holding messages.
for (GPBExtensionDescriptor *extension in [msg extensionsCurrentlySet]) {
if (!GPBDataTypeIsMessage(extension.dataType)) {
continue;
}
if (extension.isRepeated) {
NSArray *extMessages = [msg getExtension:extension];
[todo addObjectsFromArray:extMessages];
} else {
GPBMessage *extMessage = [msg getExtension:extension];
[todo addObject:extMessage];
}
} // for(extensionsCurrentlySet)

} // while(todo.count)
}


// -- About Version Checks --
// There's actually 3 places these checks all come into play:
// 1. When the generated source is compile into .o files, the header check
Expand Down
200 changes: 200 additions & 0 deletions objectivec/Tests/GPBUtilitiesTests.m
Expand Up @@ -39,6 +39,7 @@
#import "GPBDescriptor.h"
#import "GPBDescriptor_PackagePrivate.h"
#import "GPBMessage.h"
#import "GPBUnknownField_PackagePrivate.h"

#import "google/protobuf/MapUnittest.pbobjc.h"
#import "google/protobuf/Unittest.pbobjc.h"
Expand Down Expand Up @@ -197,4 +198,203 @@ - (void)testSetRepeatedFields {
}
}

// Helper to make an unknown field set with something in it.
static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) {
GPBUnknownFieldSet *result =
[[[GPBUnknownFieldSet alloc] init] autorelease];

GPBUnknownField *field =
[[[GPBUnknownField alloc] initWithNumber:num] autorelease];
[field addVarint:num];
[result addField:field];

return result;
}

- (void)testDropMessageUnknownFieldsRecursively {
TestAllExtensions *message = [TestAllExtensions message];

// Give it unknownFields.
message.unknownFields = UnknownFieldsSetHelper(777);

// Given it extensions that include a message with unknown fields of its own.
{
// Int
[message setExtension:[UnittestRoot optionalInt32Extension] value:@5];

// Group
OptionalGroup_extension *optionalGroup = [OptionalGroup_extension message];
optionalGroup.a = 123;
optionalGroup.unknownFields = UnknownFieldsSetHelper(779);
[message setExtension:[UnittestRoot optionalGroupExtension]
value:optionalGroup];

// Message
TestAllTypes_NestedMessage *nestedMessage =
[TestAllTypes_NestedMessage message];
nestedMessage.bb = 456;
nestedMessage.unknownFields = UnknownFieldsSetHelper(778);
[message setExtension:[UnittestRoot optionalNestedMessageExtension]
value:nestedMessage];

// Repeated Group
RepeatedGroup_extension *repeatedGroup =
[[RepeatedGroup_extension alloc] init];
repeatedGroup.a = 567;
repeatedGroup.unknownFields = UnknownFieldsSetHelper(780);
[message addExtension:[UnittestRoot repeatedGroupExtension]
value:repeatedGroup];
[repeatedGroup release];

// Repeated Message
nestedMessage = [[TestAllTypes_NestedMessage alloc] init];
nestedMessage.bb = 678;
nestedMessage.unknownFields = UnknownFieldsSetHelper(781);
[message addExtension:[UnittestRoot repeatedNestedMessageExtension]
value:nestedMessage];
[nestedMessage release];
}

// Confirm everything is there.

XCTAssertNotNil(message);
XCTAssertNotNil(message.unknownFields);
XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]);

{
XCTAssertTrue([message hasExtension:[UnittestRoot optionalGroupExtension]]);
OptionalGroup_extension *optionalGroup =
[message getExtension:[UnittestRoot optionalGroupExtension]];
XCTAssertNotNil(optionalGroup);
XCTAssertEqual(optionalGroup.a, 123);
XCTAssertNotNil(optionalGroup.unknownFields);
}

{
XCTAssertTrue([message hasExtension:[UnittestRoot optionalNestedMessageExtension]]);
TestAllTypes_NestedMessage *nestedMessage =
[message getExtension:[UnittestRoot optionalNestedMessageExtension]];
XCTAssertNotNil(nestedMessage);
XCTAssertEqual(nestedMessage.bb, 456);
XCTAssertNotNil(nestedMessage.unknownFields);
}

{
XCTAssertTrue([message hasExtension:[UnittestRoot repeatedGroupExtension]]);
NSArray *repeatedGroups = [message getExtension:[UnittestRoot repeatedGroupExtension]];
XCTAssertEqual(repeatedGroups.count, (NSUInteger)1);
RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject;
XCTAssertNotNil(repeatedGroup);
XCTAssertEqual(repeatedGroup.a, 567);
XCTAssertNotNil(repeatedGroup.unknownFields);
}

{
XCTAssertTrue([message hasExtension:[UnittestRoot repeatedNestedMessageExtension]]);
NSArray *repeatedNestedMessages = [message getExtension:[UnittestRoot repeatedNestedMessageExtension]];
XCTAssertEqual(repeatedNestedMessages.count, (NSUInteger)1);
TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject;
XCTAssertNotNil(repeatedNestedMessage);
XCTAssertEqual(repeatedNestedMessage.bb, 678);
XCTAssertNotNil(repeatedNestedMessage.unknownFields);
}

// Drop them.
GPBMessageDropUnknownFieldsRecursively(message);

// Confirm unknowns are gone from within the messages.

XCTAssertNotNil(message);
XCTAssertNil(message.unknownFields);
XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]);

{
XCTAssertTrue([message hasExtension:[UnittestRoot optionalGroupExtension]]);
OptionalGroup_extension *optionalGroup =
[message getExtension:[UnittestRoot optionalGroupExtension]];
XCTAssertNotNil(optionalGroup);
XCTAssertEqual(optionalGroup.a, 123);
XCTAssertNil(optionalGroup.unknownFields);
}

{
XCTAssertTrue([message hasExtension:[UnittestRoot optionalNestedMessageExtension]]);
TestAllTypes_NestedMessage *nestedMessage =
[message getExtension:[UnittestRoot optionalNestedMessageExtension]];
XCTAssertNotNil(nestedMessage);
XCTAssertEqual(nestedMessage.bb, 456);
XCTAssertNil(nestedMessage.unknownFields);
}

{
XCTAssertTrue([message hasExtension:[UnittestRoot repeatedGroupExtension]]);
NSArray *repeatedGroups = [message getExtension:[UnittestRoot repeatedGroupExtension]];
XCTAssertEqual(repeatedGroups.count, (NSUInteger)1);
RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject;
XCTAssertNotNil(repeatedGroup);
XCTAssertEqual(repeatedGroup.a, 567);
XCTAssertNil(repeatedGroup.unknownFields);
}

{
XCTAssertTrue([message hasExtension:[UnittestRoot repeatedNestedMessageExtension]]);
NSArray *repeatedNestedMessages = [message getExtension:[UnittestRoot repeatedNestedMessageExtension]];
XCTAssertEqual(repeatedNestedMessages.count, (NSUInteger)1);
TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject;
XCTAssertNotNil(repeatedNestedMessage);
XCTAssertEqual(repeatedNestedMessage.bb, 678);
XCTAssertNil(repeatedNestedMessage.unknownFields);
}

}

- (void)testDropMessageUnknownFieldsRecursively_Maps {
TestMap *message = [TestMap message];

{
ForeignMessage *foreignMessage = [ForeignMessage message];
foreignMessage.unknownFields = UnknownFieldsSetHelper(100);
[message.mapInt32ForeignMessage setObject:foreignMessage forKey:100];

foreignMessage = [ForeignMessage message];
foreignMessage.unknownFields = UnknownFieldsSetHelper(101);
[message.mapStringForeignMessage setObject:foreignMessage forKey:@"101"];
}

// Confirm everything is there.

XCTAssertNotNil(message);

{
ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100];
XCTAssertNotNil(foreignMessage);
XCTAssertNotNil(foreignMessage.unknownFields);
}

{
ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"];
XCTAssertNotNil(foreignMessage);
XCTAssertNotNil(foreignMessage.unknownFields);
}

GPBMessageDropUnknownFieldsRecursively(message);

// Confirm unknowns are gone from within the messages.

XCTAssertNotNil(message);

{
ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100];
XCTAssertNotNil(foreignMessage);
XCTAssertNil(foreignMessage.unknownFields);
}

{
ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"];
XCTAssertNotNil(foreignMessage);
XCTAssertNil(foreignMessage.unknownFields);
}

}

@end

0 comments on commit d071766

Please sign in to comment.