diff options
Diffstat (limited to 'third_party/protobuf/3.4.0/objectivec/GPBMessage.m')
-rw-r--r-- | third_party/protobuf/3.4.0/objectivec/GPBMessage.m | 3256 |
1 files changed, 3256 insertions, 0 deletions
diff --git a/third_party/protobuf/3.4.0/objectivec/GPBMessage.m b/third_party/protobuf/3.4.0/objectivec/GPBMessage.m new file mode 100644 index 0000000000..37cff6ceff --- /dev/null +++ b/third_party/protobuf/3.4.0/objectivec/GPBMessage.m @@ -0,0 +1,3256 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import "GPBMessage_PackagePrivate.h" + +#import <objc/runtime.h> +#import <objc/message.h> + +#import "GPBArray_PackagePrivate.h" +#import "GPBCodedInputStream_PackagePrivate.h" +#import "GPBCodedOutputStream_PackagePrivate.h" +#import "GPBDescriptor_PackagePrivate.h" +#import "GPBDictionary_PackagePrivate.h" +#import "GPBExtensionInternals.h" +#import "GPBExtensionRegistry.h" +#import "GPBRootObject_PackagePrivate.h" +#import "GPBUnknownFieldSet_PackagePrivate.h" +#import "GPBUtilities_PackagePrivate.h" + +// 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 +// protos can turn on -Wdirect-ivar-access without issues. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdirect-ivar-access" + +NSString *const GPBMessageErrorDomain = + GPBNSStringifySymbol(GPBMessageErrorDomain); + +NSString *const GPBErrorReasonKey = @"Reason"; + +static NSString *const kGPBDataCoderKey = @"GPBData"; + +// +// PLEASE REMEMBER: +// +// This is the base class for *all* messages generated, so any selector defined, +// *public* or *private* could end up colliding with a proto message field. So +// avoid using selectors that could match a property, use C functions to hide +// them, etc. +// + +@interface GPBMessage () { + @package + GPBUnknownFieldSet *unknownFields_; + NSMutableDictionary *extensionMap_; + NSMutableDictionary *autocreatedExtensionMap_; + + // If the object was autocreated, we remember the creator so that if we get + // mutated, we can inform the creator to make our field visible. + GPBMessage *autocreator_; + GPBFieldDescriptor *autocreatorField_; + GPBExtensionDescriptor *autocreatorExtension_; +} +@end + +static id CreateArrayForField(GPBFieldDescriptor *field, + GPBMessage *autocreator) + __attribute__((ns_returns_retained)); +static id GetOrCreateArrayIvarWithField(GPBMessage *self, + GPBFieldDescriptor *field, + GPBFileSyntax syntax); +static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); +static id CreateMapForField(GPBFieldDescriptor *field, + GPBMessage *autocreator) + __attribute__((ns_returns_retained)); +static id GetOrCreateMapIvarWithField(GPBMessage *self, + GPBFieldDescriptor *field, + GPBFileSyntax syntax); +static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); +static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, + NSZone *zone) + __attribute__((ns_returns_retained)); + +#ifdef DEBUG +static NSError *MessageError(NSInteger code, NSDictionary *userInfo) { + return [NSError errorWithDomain:GPBMessageErrorDomain + code:code + userInfo:userInfo]; +} +#endif + +static NSError *ErrorFromException(NSException *exception) { + NSError *error = nil; + + if ([exception.name isEqual:GPBCodedInputStreamException]) { + NSDictionary *exceptionInfo = exception.userInfo; + error = exceptionInfo[GPBCodedInputStreamUnderlyingErrorKey]; + } + + if (!error) { + NSString *reason = exception.reason; + NSDictionary *userInfo = nil; + if ([reason length]) { + userInfo = @{ GPBErrorReasonKey : reason }; + } + + error = [NSError errorWithDomain:GPBMessageErrorDomain + code:GPBMessageErrorCodeOther + userInfo:userInfo]; + } + return error; +} + +static void CheckExtension(GPBMessage *self, + GPBExtensionDescriptor *extension) { + if (![self isKindOfClass:extension.containingMessageClass]) { + [NSException + raise:NSInvalidArgumentException + format:@"Extension %@ used on wrong class (%@ instead of %@)", + extension.singletonName, + [self class], extension.containingMessageClass]; + } +} + +static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, + NSZone *zone) { + if (extensionMap.count == 0) { + return nil; + } + NSMutableDictionary *result = [[NSMutableDictionary allocWithZone:zone] + initWithCapacity:extensionMap.count]; + + for (GPBExtensionDescriptor *extension in extensionMap) { + id value = [extensionMap objectForKey:extension]; + BOOL isMessageExtension = GPBExtensionIsMessage(extension); + + if (extension.repeated) { + if (isMessageExtension) { + NSMutableArray *list = + [[NSMutableArray alloc] initWithCapacity:[value count]]; + for (GPBMessage *listValue in value) { + GPBMessage *copiedValue = [listValue copyWithZone:zone]; + [list addObject:copiedValue]; + [copiedValue release]; + } + [result setObject:list forKey:extension]; + [list release]; + } else { + NSMutableArray *copiedValue = [value mutableCopyWithZone:zone]; + [result setObject:copiedValue forKey:extension]; + [copiedValue release]; + } + } else { + if (isMessageExtension) { + GPBMessage *copiedValue = [value copyWithZone:zone]; + [result setObject:copiedValue forKey:extension]; + [copiedValue release]; + } else { + [result setObject:value forKey:extension]; + } + } + } + + return result; +} + +static id CreateArrayForField(GPBFieldDescriptor *field, + GPBMessage *autocreator) { + id result; + GPBDataType fieldDataType = GPBGetFieldDataType(field); + switch (fieldDataType) { + case GPBDataTypeBool: + result = [[GPBBoolArray alloc] init]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + result = [[GPBUInt32Array alloc] init]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + result = [[GPBInt32Array alloc] init]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + result = [[GPBUInt64Array alloc] init]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + result = [[GPBInt64Array alloc] init]; + break; + case GPBDataTypeFloat: + result = [[GPBFloatArray alloc] init]; + break; + case GPBDataTypeDouble: + result = [[GPBDoubleArray alloc] init]; + break; + + case GPBDataTypeEnum: + result = [[GPBEnumArray alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + + case GPBDataTypeBytes: + case GPBDataTypeGroup: + case GPBDataTypeMessage: + case GPBDataTypeString: + if (autocreator) { + result = [[GPBAutocreatedArray alloc] init]; + } else { + result = [[NSMutableArray alloc] init]; + } + break; + } + + if (autocreator) { + if (GPBDataTypeIsObject(fieldDataType)) { + GPBAutocreatedArray *autoArray = result; + autoArray->_autocreator = autocreator; + } else { + GPBInt32Array *gpbArray = result; + gpbArray->_autocreator = autocreator; + } + } + + return result; +} + +static id CreateMapForField(GPBFieldDescriptor *field, + GPBMessage *autocreator) { + id result; + GPBDataType keyDataType = field.mapKeyDataType; + GPBDataType valueDataType = GPBGetFieldDataType(field); + switch (keyDataType) { + case GPBDataTypeBool: + switch (valueDataType) { + case GPBDataTypeBool: + result = [[GPBBoolBoolDictionary alloc] init]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + result = [[GPBBoolUInt32Dictionary alloc] init]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + result = [[GPBBoolInt32Dictionary alloc] init]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + result = [[GPBBoolUInt64Dictionary alloc] init]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + result = [[GPBBoolInt64Dictionary alloc] init]; + break; + case GPBDataTypeFloat: + result = [[GPBBoolFloatDictionary alloc] init]; + break; + case GPBDataTypeDouble: + result = [[GPBBoolDoubleDictionary alloc] init]; + break; + case GPBDataTypeEnum: + result = [[GPBBoolEnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBDataTypeBytes: + case GPBDataTypeMessage: + case GPBDataTypeString: + result = [[GPBBoolObjectDictionary alloc] init]; + break; + case GPBDataTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + switch (valueDataType) { + case GPBDataTypeBool: + result = [[GPBUInt32BoolDictionary alloc] init]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + result = [[GPBUInt32UInt32Dictionary alloc] init]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + result = [[GPBUInt32Int32Dictionary alloc] init]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + result = [[GPBUInt32UInt64Dictionary alloc] init]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + result = [[GPBUInt32Int64Dictionary alloc] init]; + break; + case GPBDataTypeFloat: + result = [[GPBUInt32FloatDictionary alloc] init]; + break; + case GPBDataTypeDouble: + result = [[GPBUInt32DoubleDictionary alloc] init]; + break; + case GPBDataTypeEnum: + result = [[GPBUInt32EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBDataTypeBytes: + case GPBDataTypeMessage: + case GPBDataTypeString: + result = [[GPBUInt32ObjectDictionary alloc] init]; + break; + case GPBDataTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + switch (valueDataType) { + case GPBDataTypeBool: + result = [[GPBInt32BoolDictionary alloc] init]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + result = [[GPBInt32UInt32Dictionary alloc] init]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + result = [[GPBInt32Int32Dictionary alloc] init]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + result = [[GPBInt32UInt64Dictionary alloc] init]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + result = [[GPBInt32Int64Dictionary alloc] init]; + break; + case GPBDataTypeFloat: + result = [[GPBInt32FloatDictionary alloc] init]; + break; + case GPBDataTypeDouble: + result = [[GPBInt32DoubleDictionary alloc] init]; + break; + case GPBDataTypeEnum: + result = [[GPBInt32EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBDataTypeBytes: + case GPBDataTypeMessage: + case GPBDataTypeString: + result = [[GPBInt32ObjectDictionary alloc] init]; + break; + case GPBDataTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + switch (valueDataType) { + case GPBDataTypeBool: + result = [[GPBUInt64BoolDictionary alloc] init]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + result = [[GPBUInt64UInt32Dictionary alloc] init]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + result = [[GPBUInt64Int32Dictionary alloc] init]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + result = [[GPBUInt64UInt64Dictionary alloc] init]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + result = [[GPBUInt64Int64Dictionary alloc] init]; + break; + case GPBDataTypeFloat: + result = [[GPBUInt64FloatDictionary alloc] init]; + break; + case GPBDataTypeDouble: + result = [[GPBUInt64DoubleDictionary alloc] init]; + break; + case GPBDataTypeEnum: + result = [[GPBUInt64EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBDataTypeBytes: + case GPBDataTypeMessage: + case GPBDataTypeString: + result = [[GPBUInt64ObjectDictionary alloc] init]; + break; + case GPBDataTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + switch (valueDataType) { + case GPBDataTypeBool: + result = [[GPBInt64BoolDictionary alloc] init]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + result = [[GPBInt64UInt32Dictionary alloc] init]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + result = [[GPBInt64Int32Dictionary alloc] init]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + result = [[GPBInt64UInt64Dictionary alloc] init]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + result = [[GPBInt64Int64Dictionary alloc] init]; + break; + case GPBDataTypeFloat: + result = [[GPBInt64FloatDictionary alloc] init]; + break; + case GPBDataTypeDouble: + result = [[GPBInt64DoubleDictionary alloc] init]; + break; + case GPBDataTypeEnum: + result = [[GPBInt64EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBDataTypeBytes: + case GPBDataTypeMessage: + case GPBDataTypeString: + result = [[GPBInt64ObjectDictionary alloc] init]; + break; + case GPBDataTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBDataTypeString: + switch (valueDataType) { + case GPBDataTypeBool: + result = [[GPBStringBoolDictionary alloc] init]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + result = [[GPBStringUInt32Dictionary alloc] init]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + result = [[GPBStringInt32Dictionary alloc] init]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + result = [[GPBStringUInt64Dictionary alloc] init]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + result = [[GPBStringInt64Dictionary alloc] init]; + break; + case GPBDataTypeFloat: + result = [[GPBStringFloatDictionary alloc] init]; + break; + case GPBDataTypeDouble: + result = [[GPBStringDoubleDictionary alloc] init]; + break; + case GPBDataTypeEnum: + result = [[GPBStringEnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBDataTypeBytes: + case GPBDataTypeMessage: + case GPBDataTypeString: + if (autocreator) { + result = [[GPBAutocreatedDictionary alloc] init]; + } else { + result = [[NSMutableDictionary alloc] init]; + } + break; + case GPBDataTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + + case GPBDataTypeFloat: + case GPBDataTypeDouble: + case GPBDataTypeEnum: + case GPBDataTypeBytes: + case GPBDataTypeGroup: + case GPBDataTypeMessage: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + + if (autocreator) { + if ((keyDataType == GPBDataTypeString) && + GPBDataTypeIsObject(valueDataType)) { + GPBAutocreatedDictionary *autoDict = result; + autoDict->_autocreator = autocreator; + } else { + GPBInt32Int32Dictionary *gpbDict = result; + gpbDict->_autocreator = autocreator; + } + } + + return result; +} + +#if !defined(__clang_analyzer__) +// These functions are blocked from the analyzer because the analyzer sees the +// GPBSetRetainedObjectIvarWithFieldInternal() call as consuming the array/map, +// so use of the array/map after the call returns is flagged as a use after +// free. +// But GPBSetRetainedObjectIvarWithFieldInternal() is "consuming" the retain +// count be holding onto the object (it is transfering it), the object is +// still valid after returning from the call. The other way to avoid this +// would be to add a -retain/-autorelease, but that would force every +// repeated/map field parsed into the autorelease pool which is both a memory +// and performance hit. + +static id GetOrCreateArrayIvarWithField(GPBMessage *self, + GPBFieldDescriptor *field, + GPBFileSyntax syntax) { + id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!array) { + // No lock needed, this is called from places expecting to mutate + // so no threading protection is needed. + array = CreateArrayForField(field, nil); + GPBSetRetainedObjectIvarWithFieldInternal(self, field, array, syntax); + } + return array; +} + +// This is like GPBGetObjectIvarWithField(), but for arrays, it should +// only be used to wire the method into the class. +static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { + id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!array) { + // Check again after getting the lock. + GPBPrepareReadOnlySemaphore(self); + dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER); + array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!array) { + array = CreateArrayForField(field, self); + GPBSetAutocreatedRetainedObjectIvarWithField(self, field, array); + } + dispatch_semaphore_signal(self->readOnlySemaphore_); + } + return array; +} + +static id GetOrCreateMapIvarWithField(GPBMessage *self, + GPBFieldDescriptor *field, + GPBFileSyntax syntax) { + id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!dict) { + // No lock needed, this is called from places expecting to mutate + // so no threading protection is needed. + dict = CreateMapForField(field, nil); + GPBSetRetainedObjectIvarWithFieldInternal(self, field, dict, syntax); + } + return dict; +} + +// This is like GPBGetObjectIvarWithField(), but for maps, it should +// only be used to wire the method into the class. +static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { + id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!dict) { + // Check again after getting the lock. + GPBPrepareReadOnlySemaphore(self); + dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER); + dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!dict) { + dict = CreateMapForField(field, self); + GPBSetAutocreatedRetainedObjectIvarWithField(self, field, dict); + } + dispatch_semaphore_signal(self->readOnlySemaphore_); + } + return dict; +} + +#endif // !defined(__clang_analyzer__) + +GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass, + GPBMessage *autocreator, + GPBFieldDescriptor *field) { + GPBMessage *message = [[msgClass alloc] init]; + message->autocreator_ = autocreator; + message->autocreatorField_ = [field retain]; + return message; +} + +static GPBMessage *CreateMessageWithAutocreatorForExtension( + Class msgClass, GPBMessage *autocreator, GPBExtensionDescriptor *extension) + __attribute__((ns_returns_retained)); + +static GPBMessage *CreateMessageWithAutocreatorForExtension( + Class msgClass, GPBMessage *autocreator, + GPBExtensionDescriptor *extension) { + GPBMessage *message = [[msgClass alloc] init]; + message->autocreator_ = autocreator; + message->autocreatorExtension_ = [extension retain]; + return message; +} + +BOOL GPBWasMessageAutocreatedBy(GPBMessage *message, GPBMessage *parent) { + return (message->autocreator_ == parent); +} + +void GPBBecomeVisibleToAutocreator(GPBMessage *self) { + // Message objects that are implicitly created by accessing a message field + // are initially not visible via the hasX selector. This method makes them + // visible. + if (self->autocreator_) { + // This will recursively make all parent messages visible until it reaches a + // super-creator that's visible. + if (self->autocreatorField_) { + GPBFileSyntax syntax = [self->autocreator_ descriptor].file.syntax; + GPBSetObjectIvarWithFieldInternal(self->autocreator_, + self->autocreatorField_, self, syntax); + } else { + [self->autocreator_ setExtension:self->autocreatorExtension_ value:self]; + } + } +} + +void GPBAutocreatedArrayModified(GPBMessage *self, id array) { + // When one of our autocreated arrays adds elements, make it visible. + GPBDescriptor *descriptor = [[self class] descriptor]; + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (field.fieldType == GPBFieldTypeRepeated) { + id curArray = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (curArray == array) { + if (GPBFieldDataTypeIsObject(field)) { + GPBAutocreatedArray *autoArray = array; + autoArray->_autocreator = nil; + } else { + GPBInt32Array *gpbArray = array; + gpbArray->_autocreator = nil; + } + GPBBecomeVisibleToAutocreator(self); + return; + } + } + } + NSCAssert(NO, @"Unknown autocreated %@ for %@.", [array class], self); +} + +void GPBAutocreatedDictionaryModified(GPBMessage *self, id dictionary) { + // When one of our autocreated dicts adds elements, make it visible. + GPBDescriptor *descriptor = [[self class] descriptor]; + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (field.fieldType == GPBFieldTypeMap) { + id curDict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (curDict == dictionary) { + if ((field.mapKeyDataType == GPBDataTypeString) && + GPBFieldDataTypeIsObject(field)) { + GPBAutocreatedDictionary *autoDict = dictionary; + autoDict->_autocreator = nil; + } else { + GPBInt32Int32Dictionary *gpbDict = dictionary; + gpbDict->_autocreator = nil; + } + GPBBecomeVisibleToAutocreator(self); + return; + } + } + } + NSCAssert(NO, @"Unknown autocreated %@ for %@.", [dictionary class], self); +} + +void GPBClearMessageAutocreator(GPBMessage *self) { + if ((self == nil) || !self->autocreator_) { + return; + } + +#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) + // Either the autocreator must have its "has" flag set to YES, or it must be + // NO and not equal to ourselves. + BOOL autocreatorHas = + (self->autocreatorField_ + ? GPBGetHasIvarField(self->autocreator_, self->autocreatorField_) + : [self->autocreator_ hasExtension:self->autocreatorExtension_]); + GPBMessage *autocreatorFieldValue = + (self->autocreatorField_ + ? GPBGetObjectIvarWithFieldNoAutocreate(self->autocreator_, + self->autocreatorField_) + : [self->autocreator_->autocreatedExtensionMap_ + objectForKey:self->autocreatorExtension_]); + NSCAssert(autocreatorHas || autocreatorFieldValue != self, + @"Cannot clear autocreator because it still refers to self, self: %@.", + self); + +#endif // DEBUG && !defined(NS_BLOCK_ASSERTIONS) + + self->autocreator_ = nil; + [self->autocreatorField_ release]; + self->autocreatorField_ = nil; + [self->autocreatorExtension_ release]; + self->autocreatorExtension_ = nil; +} + +// Call this before using the readOnlySemaphore_. This ensures it is created only once. +void GPBPrepareReadOnlySemaphore(GPBMessage *self) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdirect-ivar-access" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + + // Create the semaphore on demand (rather than init) as developers might not cause them + // to be needed, and the heap usage can add up. The atomic swap is used to avoid needing + // another lock around creating it. + if (self->readOnlySemaphore_ == nil) { + dispatch_semaphore_t worker = dispatch_semaphore_create(1); + if (!OSAtomicCompareAndSwapPtrBarrier(NULL, worker, (void * volatile *)&(self->readOnlySemaphore_))) { + dispatch_release(worker); + } + } + +#pragma clang diagnostic pop +} + +static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { + if (!self->unknownFields_) { + self->unknownFields_ = [[GPBUnknownFieldSet alloc] init]; + GPBBecomeVisibleToAutocreator(self); + } + return self->unknownFields_; +} + +@implementation GPBMessage + ++ (void)initialize { + Class pbMessageClass = [GPBMessage class]; + if ([self class] == pbMessageClass) { + // This is here to start up the "base" class descriptor. + [self descriptor]; + // Message shares extension method resolving with GPBRootObject so insure + // it is started up at the same time. + (void)[GPBRootObject class]; + } else if ([self superclass] == pbMessageClass) { + // This is here to start up all the "message" subclasses. Just needs to be + // done for the messages, not any of the subclasses. + // This must be done in initialize to enforce thread safety of start up of + // the protocol buffer library. + // Note: The generated code for -descriptor calls + // +[GPBDescriptor allocDescriptorForClass:...], passing the GPBRootObject + // subclass for the file. That call chain is what ensures that *Root class + // is started up to support extension resolution off the message class + // (+resolveClassMethod: below) in a thread safe manner. + [self descriptor]; + } +} + ++ (instancetype)allocWithZone:(NSZone *)zone { + // Override alloc to allocate our classes with the additional storage + // required for the instance variables. + GPBDescriptor *descriptor = [self descriptor]; + return NSAllocateObject(self, descriptor->storageSize_, zone); +} + ++ (instancetype)alloc { + return [self allocWithZone:nil]; +} + ++ (GPBDescriptor *)descriptor { + // This is thread safe because it is called from +initialize. + static GPBDescriptor *descriptor = NULL; + static GPBFileDescriptor *fileDescriptor = NULL; + if (!descriptor) { + // Use a dummy file that marks it as proto2 syntax so when used generically + // it supports unknowns/etc. + fileDescriptor = + [[GPBFileDescriptor alloc] initWithPackage:@"internal" + syntax:GPBFileSyntaxProto2]; + + descriptor = [GPBDescriptor allocDescriptorForClass:[GPBMessage class] + rootClass:Nil + file:fileDescriptor + fields:NULL + fieldCount:0 + storageSize:0 + flags:0]; + } + return descriptor; +} + ++ (instancetype)message { + return [[[self alloc] init] autorelease]; +} + +- (instancetype)init { + if ((self = [super init])) { + messageStorage_ = (GPBMessage_StoragePtr)( + ((uint8_t *)self) + class_getInstanceSize([self class])); + } + + return self; +} + +- (instancetype)initWithData:(NSData *)data error:(NSError **)errorPtr { + return [self initWithData:data extensionRegistry:nil error:errorPtr]; +} + +- (instancetype)initWithData:(NSData *)data + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { + if ((self = [self init])) { + @try { + [self mergeFromData:data extensionRegistry:extensionRegistry]; + if (errorPtr) { + *errorPtr = nil; + } + } + @catch (NSException *exception) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = ErrorFromException(exception); + } + } +#ifdef DEBUG + if (self && !self.initialized) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil); + } + } +#endif + } + return self; +} + +- (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input + extensionRegistry: + (GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { + if ((self = [self init])) { + @try { + [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; + if (errorPtr) { + *errorPtr = nil; + } + } + @catch (NSException *exception) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = ErrorFromException(exception); + } + } +#ifdef DEBUG + if (self && !self.initialized) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil); + } + } +#endif + } + return self; +} + +- (void)dealloc { + [self internalClear:NO]; + NSCAssert(!autocreator_, @"Autocreator was not cleared before dealloc."); + if (readOnlySemaphore_) { + dispatch_release(readOnlySemaphore_); + } + [super dealloc]; +} + +- (void)copyFieldsInto:(GPBMessage *)message + zone:(NSZone *)zone + descriptor:(GPBDescriptor *)descriptor { + // Copy all the storage... + memcpy(message->messageStorage_, messageStorage_, descriptor->storageSize_); + + GPBFileSyntax syntax = descriptor.file.syntax; + + // Loop over the fields doing fixup... + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (GPBFieldIsMapOrArray(field)) { + id value = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (value) { + // We need to copy the array/map, but the catch is for message fields, + // we also need to ensure all the messages as those need copying also. + id newValue; + if (GPBFieldDataTypeIsMessage(field)) { + if (field.fieldType == GPBFieldTypeRepeated) { + NSArray *existingArray = (NSArray *)value; + NSMutableArray *newArray = + [[NSMutableArray alloc] initWithCapacity:existingArray.count]; + newValue = newArray; + for (GPBMessage *msg in existingArray) { + GPBMessage *copiedMsg = [msg copyWithZone:zone]; + [newArray addObject:copiedMsg]; + [copiedMsg release]; + } + } else { + if (field.mapKeyDataType == GPBDataTypeString) { + // Map is an NSDictionary. + NSDictionary *existingDict = value; + NSMutableDictionary *newDict = [[NSMutableDictionary alloc] + initWithCapacity:existingDict.count]; + newValue = newDict; + [existingDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, + GPBMessage *msg, + BOOL *stop) { +#pragma unused(stop) + GPBMessage *copiedMsg = [msg copyWithZone:zone]; + [newDict setObject:copiedMsg forKey:key]; + [copiedMsg release]; + }]; + } else { + // Is one of the GPB*ObjectDictionary classes. Type doesn't + // matter, just need one to invoke the selector. + GPBInt32ObjectDictionary *existingDict = value; + newValue = [existingDict deepCopyWithZone:zone]; + } + } + } else { + // Not messages (but is a map/array)... + if (field.fieldType == GPBFieldTypeRepeated) { + if (GPBFieldDataTypeIsObject(field)) { + // NSArray + newValue = [value mutableCopyWithZone:zone]; + } else { + // GPB*Array + newValue = [value copyWithZone:zone]; + } + } else { + if (field.mapKeyDataType == GPBDataTypeString) { + // NSDictionary + newValue = [value mutableCopyWithZone:zone]; + } else { + // Is one of the GPB*Dictionary classes. Type doesn't matter, + // just need one to invoke the selector. + GPBInt32Int32Dictionary *existingDict = value; + newValue = [existingDict copyWithZone:zone]; + } + } + } + // We retain here because the memcpy picked up the pointer value and + // the next call to SetRetainedObject... will release the current value. + [value retain]; + GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue, + syntax); + } + } else if (GPBFieldDataTypeIsMessage(field)) { + // For object types, if we have a value, copy it. If we don't, + // zero it to remove the pointer to something that was autocreated + // (and the ptr just got memcpyed). + if (GPBGetHasIvarField(self, field)) { + GPBMessage *value = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + GPBMessage *newValue = [value copyWithZone:zone]; + // We retain here because the memcpy picked up the pointer value and + // the next call to SetRetainedObject... will release the current value. + [value retain]; + GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue, + syntax); + } else { + uint8_t *storage = (uint8_t *)message->messageStorage_; + id *typePtr = (id *)&storage[field->description_->offset]; + *typePtr = NULL; + } + } else if (GPBFieldDataTypeIsObject(field) && + GPBGetHasIvarField(self, field)) { + // A set string/data value (message picked off above), copy it. + id value = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + id newValue = [value copyWithZone:zone]; + // We retain here because the memcpy picked up the pointer value and + // the next call to SetRetainedObject... will release the current value. + [value retain]; + GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue, + syntax); + } else { + // memcpy took care of the rest of the primitive fields if they were set. + } + } // for (field in descriptor->fields_) +} + +- (id)copyWithZone:(NSZone *)zone { + GPBDescriptor *descriptor = [self descriptor]; + GPBMessage *result = [[descriptor.messageClass allocWithZone:zone] init]; + + [self copyFieldsInto:result zone:zone descriptor:descriptor]; + // Make immutable copies of the extra bits. + result->unknownFields_ = [unknownFields_ copyWithZone:zone]; + result->extensionMap_ = CloneExtensionMap(extensionMap_, zone); + return result; +} + +- (void)clear { + [self internalClear:YES]; +} + +- (void)internalClear:(BOOL)zeroStorage { + GPBDescriptor *descriptor = [self descriptor]; + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (GPBFieldIsMapOrArray(field)) { + id arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (arrayOrMap) { + if (field.fieldType == GPBFieldTypeRepeated) { + if (GPBFieldDataTypeIsObject(field)) { + if ([arrayOrMap isKindOfClass:[GPBAutocreatedArray class]]) { + GPBAutocreatedArray *autoArray = arrayOrMap; + if (autoArray->_autocreator == self) { + autoArray->_autocreator = nil; + } + } + } else { + // Type doesn't matter, it is a GPB*Array. + GPBInt32Array *gpbArray = arrayOrMap; + if (gpbArray->_autocreator == self) { + gpbArray->_autocreator = nil; + } + } + } else { + if ((field.mapKeyDataType == GPBDataTypeString) && + GPBFieldDataTypeIsObject(field)) { + if ([arrayOrMap isKindOfClass:[GPBAutocreatedDictionary class]]) { + GPBAutocreatedDictionary *autoDict = arrayOrMap; + if (autoDict->_autocreator == self) { + autoDict->_autocreator = nil; + } + } + } else { + // Type doesn't matter, it is a GPB*Dictionary. + GPBInt32Int32Dictionary *gpbDict = arrayOrMap; + if (gpbDict->_autocreator == self) { + gpbDict->_autocreator = nil; + } + } + } + [arrayOrMap release]; + } + } else if (GPBFieldDataTypeIsMessage(field)) { + GPBClearAutocreatedMessageIvarWithField(self, field); + GPBMessage *value = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [value release]; + } else if (GPBFieldDataTypeIsObject(field) && + GPBGetHasIvarField(self, field)) { + id value = GPBGetObjectIvarWithField(self, field); + [value release]; + } + } + + // GPBClearMessageAutocreator() expects that its caller has already been + // removed from autocreatedExtensionMap_ so we set to nil first. + NSArray *autocreatedValues = [autocreatedExtensionMap_ allValues]; + [autocreatedExtensionMap_ release]; + autocreatedExtensionMap_ = nil; + + // Since we're clearing all of our extensions, make sure that we clear the + // autocreator on any that we've created so they no longer refer to us. + for (GPBMessage *value in autocreatedValues) { + NSCAssert(GPBWasMessageAutocreatedBy(value, self), + @"Autocreated extension does not refer back to self."); + GPBClearMessageAutocreator(value); + } + + [extensionMap_ release]; + extensionMap_ = nil; + [unknownFields_ release]; + unknownFields_ = nil; + + // Note that clearing does not affect autocreator_. If we are being cleared + // because of a dealloc, then autocreator_ should be nil anyway. If we are + // being cleared because someone explicitly clears us, we don't want to + // sever our relationship with our autocreator. + + if (zeroStorage) { + memset(messageStorage_, 0, descriptor->storageSize_); + } +} + +- (BOOL)isInitialized { + GPBDescriptor *descriptor = [self descriptor]; + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (field.isRequired) { + if (!GPBGetHasIvarField(self, field)) { + return NO; + } + } + if (GPBFieldDataTypeIsMessage(field)) { + GPBFieldType fieldType = field.fieldType; + if (fieldType == GPBFieldTypeSingle) { + if (field.isRequired) { + GPBMessage *message = GPBGetMessageMessageField(self, field); + if (!message.initialized) { + return NO; + } + } else { + NSAssert(field.isOptional, + @"%@: Single message field %@ not required or optional?", + [self class], field.name); + if (GPBGetHasIvarField(self, field)) { + GPBMessage *message = GPBGetMessageMessageField(self, field); + if (!message.initialized) { + return NO; + } + } + } + } else if (fieldType == GPBFieldTypeRepeated) { + NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + for (GPBMessage *message in array) { + if (!message.initialized) { + return NO; + } + } + } else { // fieldType == GPBFieldTypeMap + if (field.mapKeyDataType == GPBDataTypeString) { + NSDictionary *map = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (map && !GPBDictionaryIsInitializedInternalHelper(map, field)) { + return NO; + } + } else { + // Real type is GPB*ObjectDictionary, exact type doesn't matter. + GPBInt32ObjectDictionary *map = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (map && ![map isInitialized]) { + return NO; + } + } + } + } + } + + __block BOOL result = YES; + [extensionMap_ + enumerateKeysAndObjectsUsingBlock:^(GPBExtensionDescriptor *extension, + id obj, + BOOL *stop) { + if (GPBExtensionIsMessage(extension)) { + if (extension.isRepeated) { + for (GPBMessage *msg in obj) { + if (!msg.initialized) { + result = NO; + *stop = YES; + break; + } + } + } else { + GPBMessage *asMsg = obj; + if (!asMsg.initialized) { + result = NO; + *stop = YES; + } + } + } + }]; + return result; +} + +- (GPBDescriptor *)descriptor { + return [[self class] descriptor]; +} + +- (NSData *)data { +#ifdef DEBUG + if (!self.initialized) { + return nil; + } +#endif + NSMutableData *data = [NSMutableData dataWithLength:[self serializedSize]]; + GPBCodedOutputStream *stream = + [[GPBCodedOutputStream alloc] initWithData:data]; + @try { + [self writeToCodedOutputStream:stream]; + } + @catch (NSException *exception) { + // This really shouldn't happen. The only way writeToCodedOutputStream: + // could throw is if something in the library has a bug and the + // serializedSize was wrong. +#ifdef DEBUG + NSLog(@"%@: Internal exception while building message data: %@", + [self class], exception); +#endif + data = nil; + } + [stream release]; + return data; +} + +- (NSData *)delimitedData { + size_t serializedSize = [self serializedSize]; + size_t varintSize = GPBComputeRawVarint32SizeForInteger(serializedSize); + NSMutableData *data = + [NSMutableData dataWithLength:(serializedSize + varintSize)]; + GPBCodedOutputStream *stream = + [[GPBCodedOutputStream alloc] initWithData:data]; + @try { + [self writeDelimitedToCodedOutputStream:stream]; + } + @catch (NSException *exception) { + // This really shouldn't happen. The only way writeToCodedOutputStream: + // could throw is if something in the library has a bug and the + // serializedSize was wrong. +#ifdef DEBUG + NSLog(@"%@: Internal exception while building message delimitedData: %@", + [self class], exception); +#endif + // If it happens, truncate. + data.length = 0; + } + [stream release]; + return data; +} + +- (void)writeToOutputStream:(NSOutputStream *)output { + GPBCodedOutputStream *stream = + [[GPBCodedOutputStream alloc] initWithOutputStream:output]; + [self writeToCodedOutputStream:stream]; + [stream release]; +} + +- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output { + GPBDescriptor *descriptor = [self descriptor]; + NSArray *fieldsArray = descriptor->fields_; + NSUInteger fieldCount = fieldsArray.count; + const GPBExtensionRange *extensionRanges = descriptor.extensionRanges; + NSUInteger extensionRangesCount = descriptor.extensionRangesCount; + for (NSUInteger i = 0, j = 0; i < fieldCount || j < extensionRangesCount;) { + if (i == fieldCount) { + [self writeExtensionsToCodedOutputStream:output + range:extensionRanges[j++]]; + } else if (j == extensionRangesCount || + GPBFieldNumber(fieldsArray[i]) < extensionRanges[j].start) { + [self writeField:fieldsArray[i++] toCodedOutputStream:output]; + } else { + [self writeExtensionsToCodedOutputStream:output + range:extensionRanges[j++]]; + } + } + if (descriptor.isWireFormat) { + [unknownFields_ writeAsMessageSetTo:output]; + } else { + [unknownFields_ writeToCodedOutputStream:output]; + } +} + +- (void)writeDelimitedToOutputStream:(NSOutputStream *)output { + GPBCodedOutputStream *codedOutput = + [[GPBCodedOutputStream alloc] initWithOutputStream:output]; + [self writeDelimitedToCodedOutputStream:codedOutput]; + [codedOutput release]; +} + +- (void)writeDelimitedToCodedOutputStream:(GPBCodedOutputStream *)output { + [output writeRawVarintSizeTAs32:[self serializedSize]]; + [self writeToCodedOutputStream:output]; +} + +- (void)writeField:(GPBFieldDescriptor *)field + toCodedOutputStream:(GPBCodedOutputStream *)output { + GPBFieldType fieldType = field.fieldType; + if (fieldType == GPBFieldTypeSingle) { + BOOL has = GPBGetHasIvarField(self, field); + if (!has) { + return; + } + } + uint32_t fieldNumber = GPBFieldNumber(field); + +//%PDDM-DEFINE FIELD_CASE(TYPE, REAL_TYPE) +//%FIELD_CASE_FULL(TYPE, REAL_TYPE, REAL_TYPE) +//%PDDM-DEFINE FIELD_CASE_FULL(TYPE, REAL_TYPE, ARRAY_TYPE) +//% case GPBDataType##TYPE: +//% if (fieldType == GPBFieldTypeRepeated) { +//% uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; +//% GPB##ARRAY_TYPE##Array *array = +//% GPBGetObjectIvarWithFieldNoAutocreate(self, field); +//% [output write##TYPE##Array:fieldNumber values:array tag:tag]; +//% } else if (fieldType == GPBFieldTypeSingle) { +//% [output write##TYPE:fieldNumber +//% TYPE$S value:GPBGetMessage##REAL_TYPE##Field(self, field)]; +//% } else { // fieldType == GPBFieldTypeMap +//% // Exact type here doesn't matter. +//% GPBInt32##ARRAY_TYPE##Dictionary *dict = +//% GPBGetObjectIvarWithFieldNoAutocreate(self, field); +//% [dict writeToCodedOutputStream:output asField:field]; +//% } +//% break; +//% +//%PDDM-DEFINE FIELD_CASE2(TYPE) +//% case GPBDataType##TYPE: +//% if (fieldType == GPBFieldTypeRepeated) { +//% NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); +//% [output write##TYPE##Array:fieldNumber values:array]; +//% } else if (fieldType == GPBFieldTypeSingle) { +//% // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check +//% // again. +//% [output write##TYPE:fieldNumber +//% TYPE$S value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)]; +//% } else { // fieldType == GPBFieldTypeMap +//% // Exact type here doesn't matter. +//% id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); +//% GPBDataType mapKeyDataType = field.mapKeyDataType; +//% if (mapKeyDataType == GPBDataTypeString) { +//% GPBDictionaryWriteToStreamInternalHelper(output, dict, field); +//% } else { +//% [dict writeToCodedOutputStream:output asField:field]; +//% } +//% } +//% break; +//% + + switch (GPBGetFieldDataType(field)) { + +//%PDDM-EXPAND FIELD_CASE(Bool, Bool) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeBool: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBBoolArray *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeBoolArray:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeBool:fieldNumber + value:GPBGetMessageBoolField(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32BoolDictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(Fixed32, UInt32) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeFixed32: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBUInt32Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeFixed32Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeFixed32:fieldNumber + value:GPBGetMessageUInt32Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32UInt32Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(SFixed32, Int32) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeSFixed32: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBInt32Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeSFixed32Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeSFixed32:fieldNumber + value:GPBGetMessageInt32Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32Int32Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(Float, Float) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeFloat: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBFloatArray *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeFloatArray:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeFloat:fieldNumber + value:GPBGetMessageFloatField(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32FloatDictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(Fixed64, UInt64) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeFixed64: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBUInt64Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeFixed64Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeFixed64:fieldNumber + value:GPBGetMessageUInt64Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32UInt64Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(SFixed64, Int64) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeSFixed64: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBInt64Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeSFixed64Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeSFixed64:fieldNumber + value:GPBGetMessageInt64Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32Int64Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(Double, Double) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeDouble: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBDoubleArray *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeDoubleArray:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeDouble:fieldNumber + value:GPBGetMessageDoubleField(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32DoubleDictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(Int32, Int32) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeInt32: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBInt32Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeInt32Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeInt32:fieldNumber + value:GPBGetMessageInt32Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32Int32Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(Int64, Int64) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeInt64: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBInt64Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeInt64Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeInt64:fieldNumber + value:GPBGetMessageInt64Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32Int64Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(SInt32, Int32) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeSInt32: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBInt32Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeSInt32Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeSInt32:fieldNumber + value:GPBGetMessageInt32Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32Int32Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(SInt64, Int64) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeSInt64: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBInt64Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeSInt64Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeSInt64:fieldNumber + value:GPBGetMessageInt64Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32Int64Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(UInt32, UInt32) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeUInt32: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBUInt32Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeUInt32Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeUInt32:fieldNumber + value:GPBGetMessageUInt32Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32UInt32Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE(UInt64, UInt64) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeUInt64: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBUInt64Array *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeUInt64Array:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeUInt64:fieldNumber + value:GPBGetMessageUInt64Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32UInt64Dictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE_FULL(Enum, Int32, Enum) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeEnum: + if (fieldType == GPBFieldTypeRepeated) { + uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0; + GPBEnumArray *array = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeEnumArray:fieldNumber values:array tag:tag]; + } else if (fieldType == GPBFieldTypeSingle) { + [output writeEnum:fieldNumber + value:GPBGetMessageInt32Field(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + GPBInt32EnumDictionary *dict = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [dict writeToCodedOutputStream:output asField:field]; + } + break; + +//%PDDM-EXPAND FIELD_CASE2(Bytes) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeBytes: + if (fieldType == GPBFieldTypeRepeated) { + NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeBytesArray:fieldNumber values:array]; + } else if (fieldType == GPBFieldTypeSingle) { + // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check + // again. + [output writeBytes:fieldNumber + value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + GPBDataType mapKeyDataType = field.mapKeyDataType; + if (mapKeyDataType == GPBDataTypeString) { + GPBDictionaryWriteToStreamInternalHelper(output, dict, field); + } else { + [dict writeToCodedOutputStream:output asField:field]; + } + } + break; + +//%PDDM-EXPAND FIELD_CASE2(String) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeString: + if (fieldType == GPBFieldTypeRepeated) { + NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeStringArray:fieldNumber values:array]; + } else if (fieldType == GPBFieldTypeSingle) { + // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check + // again. + [output writeString:fieldNumber + value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + GPBDataType mapKeyDataType = field.mapKeyDataType; + if (mapKeyDataType == GPBDataTypeString) { + GPBDictionaryWriteToStreamInternalHelper(output, dict, field); + } else { + [dict writeToCodedOutputStream:output asField:field]; + } + } + break; + +//%PDDM-EXPAND FIELD_CASE2(Message) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeMessage: + if (fieldType == GPBFieldTypeRepeated) { + NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeMessageArray:fieldNumber values:array]; + } else if (fieldType == GPBFieldTypeSingle) { + // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check + // again. + [output writeMessage:fieldNumber + value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + GPBDataType mapKeyDataType = field.mapKeyDataType; + if (mapKeyDataType == GPBDataTypeString) { + GPBDictionaryWriteToStreamInternalHelper(output, dict, field); + } else { + [dict writeToCodedOutputStream:output asField:field]; + } + } + break; + +//%PDDM-EXPAND FIELD_CASE2(Group) +// This block of code is generated, do not edit it directly. + + case GPBDataTypeGroup: + if (fieldType == GPBFieldTypeRepeated) { + NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [output writeGroupArray:fieldNumber values:array]; + } else if (fieldType == GPBFieldTypeSingle) { + // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check + // again. + [output writeGroup:fieldNumber + value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)]; + } else { // fieldType == GPBFieldTypeMap + // Exact type here doesn't matter. + id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + GPBDataType mapKeyDataType = field.mapKeyDataType; + if (mapKeyDataType == GPBDataTypeString) { + GPBDictionaryWriteToStreamInternalHelper(output, dict, field); + } else { + [dict writeToCodedOutputStream:output asField:field]; + } + } + break; + +//%PDDM-EXPAND-END (18 expansions) + } +} + +#pragma mark - Extensions + +- (id)getExtension:(GPBExtensionDescriptor *)extension { + CheckExtension(self, extension); + id value = [extensionMap_ objectForKey:extension]; + if (value != nil) { + return value; + } + + // No default for repeated. + if (extension.isRepeated) { + return nil; + } + // Non messages get their default. + if (!GPBExtensionIsMessage(extension)) { + return extension.defaultValue; + } + + // Check for an autocreated value. + GPBPrepareReadOnlySemaphore(self); + dispatch_semaphore_wait(readOnlySemaphore_, DISPATCH_TIME_FOREVER); + value = [autocreatedExtensionMap_ objectForKey:extension]; + if (!value) { + // Auto create the message extensions to match normal fields. + value = CreateMessageWithAutocreatorForExtension(extension.msgClass, self, + extension); + + if (autocreatedExtensionMap_ == nil) { + autocreatedExtensionMap_ = [[NSMutableDictionary alloc] init]; + } + + // We can't simply call setExtension here because that would clear the new + // value's autocreator. + [autocreatedExtensionMap_ setObject:value forKey:extension]; + [value release]; + } + + dispatch_semaphore_signal(readOnlySemaphore_); + return value; +} + +- (id)getExistingExtension:(GPBExtensionDescriptor *)extension { + // This is an internal method so we don't need to call CheckExtension(). + return [extensionMap_ objectForKey:extension]; +} + +- (BOOL)hasExtension:(GPBExtensionDescriptor *)extension { +#if defined(DEBUG) && DEBUG + CheckExtension(self, extension); +#endif // DEBUG + return nil != [extensionMap_ objectForKey:extension]; +} + +- (NSArray *)extensionsCurrentlySet { + return [extensionMap_ allKeys]; +} + +- (void)writeExtensionsToCodedOutputStream:(GPBCodedOutputStream *)output + range:(GPBExtensionRange)range { + NSArray *sortedExtensions = [[extensionMap_ allKeys] + sortedArrayUsingSelector:@selector(compareByFieldNumber:)]; + uint32_t start = range.start; + uint32_t end = range.end; + for (GPBExtensionDescriptor *extension in sortedExtensions) { + uint32_t fieldNumber = extension.fieldNumber; + if (fieldNumber >= start && fieldNumber < end) { + id value = [extensionMap_ objectForKey:extension]; + GPBWriteExtensionValueToOutputStream(extension, value, output); + } + } +} + +- (void)setExtension:(GPBExtensionDescriptor *)extension value:(id)value { + if (!value) { + [self clearExtension:extension]; + return; + } + + CheckExtension(self, extension); + + if (extension.repeated) { + [NSException raise:NSInvalidArgumentException + format:@"Must call addExtension() for repeated types."]; + } + + if (extensionMap_ == nil) { + extensionMap_ = [[NSMutableDictionary alloc] init]; + } + + // This pointless cast is for CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION. + // Without it, the compiler complains we're passing an id nullable when + // setObject:forKey: requires a id nonnull for the value. The check for + // !value at the start of the method ensures it isn't nil, but the check + // isn't smart enough to realize that. + [extensionMap_ setObject:(id)value forKey:extension]; + + GPBExtensionDescriptor *descriptor = extension; + + if (GPBExtensionIsMessage(descriptor) && !descriptor.isRepeated) { + GPBMessage *autocreatedValue = + [[autocreatedExtensionMap_ objectForKey:extension] retain]; + // Must remove from the map before calling GPBClearMessageAutocreator() so + // that GPBClearMessageAutocreator() knows its safe to clear. + [autocreatedExtensionMap_ removeObjectForKey:extension]; + GPBClearMessageAutocreator(autocreatedValue); + [autocreatedValue release]; + } + + GPBBecomeVisibleToAutocreator(self); +} + +- (void)addExtension:(GPBExtensionDescriptor *)extension value:(id)value { + CheckExtension(self, extension); + + if (!extension.repeated) { + [NSException raise:NSInvalidArgumentException + format:@"Must call setExtension() for singular types."]; + } + + if (extensionMap_ == nil) { + extensionMap_ = [[NSMutableDictionary alloc] init]; + } + NSMutableArray *list = [extensionMap_ objectForKey:extension]; + if (list == nil) { + list = [NSMutableArray array]; + [extensionMap_ setObject:list forKey:extension]; + } + + [list addObject:value]; + GPBBecomeVisibleToAutocreator(self); +} + +- (void)setExtension:(GPBExtensionDescriptor *)extension + index:(NSUInteger)idx + value:(id)value { + CheckExtension(self, extension); + + if (!extension.repeated) { + [NSException raise:NSInvalidArgumentException + format:@"Must call setExtension() for singular types."]; + } + + if (extensionMap_ == nil) { + extensionMap_ = [[NSMutableDictionary alloc] init]; + } + + NSMutableArray *list = [extensionMap_ objectForKey:extension]; + + [list replaceObjectAtIndex:idx withObject:value]; + GPBBecomeVisibleToAutocreator(self); +} + +- (void)clearExtension:(GPBExtensionDescriptor *)extension { + CheckExtension(self, extension); + + // Only become visible if there was actually a value to clear. + if ([extensionMap_ objectForKey:extension]) { + [extensionMap_ removeObjectForKey:extension]; + GPBBecomeVisibleToAutocreator(self); + } +} + +#pragma mark - mergeFrom + +- (void)mergeFromData:(NSData *)data + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { + GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data]; + [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; + [input checkLastTagWas:0]; + [input release]; +} + +#pragma mark - mergeDelimitedFrom + +- (void)mergeDelimitedFromCodedInputStream:(GPBCodedInputStream *)input + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { + GPBCodedInputStreamState *state = &input->state_; + if (GPBCodedInputStreamIsAtEnd(state)) { + return; + } + NSData *data = GPBCodedInputStreamReadRetainedBytesNoCopy(state); + if (data == nil) { + return; + } + [self mergeFromData:data extensionRegistry:extensionRegistry]; + [data release]; +} + +#pragma mark - Parse From Data Support + ++ (instancetype)parseFromData:(NSData *)data error:(NSError **)errorPtr { + return [self parseFromData:data extensionRegistry:nil error:errorPtr]; +} + ++ (instancetype)parseFromData:(NSData *)data + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { + return [[[self alloc] initWithData:data + extensionRegistry:extensionRegistry + error:errorPtr] autorelease]; +} + ++ (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { + return + [[[self alloc] initWithCodedInputStream:input + extensionRegistry:extensionRegistry + error:errorPtr] autorelease]; +} + +#pragma mark - Parse Delimited From Data Support + ++ (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input + extensionRegistry: + (GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { + GPBMessage *message = [[[self alloc] init] autorelease]; + @try { + [message mergeDelimitedFromCodedInputStream:input + extensionRegistry:extensionRegistry]; + if (errorPtr) { + *errorPtr = nil; + } + } + @catch (NSException *exception) { + message = nil; + if (errorPtr) { + *errorPtr = ErrorFromException(exception); + } + } +#ifdef DEBUG + if (message && !message.initialized) { + message = nil; + if (errorPtr) { + *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil); + } + } +#endif + return message; +} + +#pragma mark - Unknown Field Support + +- (GPBUnknownFieldSet *)unknownFields { + return unknownFields_; +} + +- (void)setUnknownFields:(GPBUnknownFieldSet *)unknownFields { + if (unknownFields != unknownFields_) { + [unknownFields_ release]; + unknownFields_ = [unknownFields copy]; + GPBBecomeVisibleToAutocreator(self); + } +} + +- (void)parseMessageSet:(GPBCodedInputStream *)input + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { + uint32_t typeId = 0; + NSData *rawBytes = nil; + GPBExtensionDescriptor *extension = nil; + GPBCodedInputStreamState *state = &input->state_; + while (true) { + uint32_t tag = GPBCodedInputStreamReadTag(state); + if (tag == 0) { + break; + } + + if (tag == GPBWireFormatMessageSetTypeIdTag) { + typeId = GPBCodedInputStreamReadUInt32(state); + if (typeId != 0) { + extension = [extensionRegistry extensionForDescriptor:[self descriptor] + fieldNumber:typeId]; + } + } else if (tag == GPBWireFormatMessageSetMessageTag) { + rawBytes = + [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease]; + } else { + if (![input skipField:tag]) { + break; + } + } + } + + [input checkLastTagWas:GPBWireFormatMessageSetItemEndTag]; + + if (rawBytes != nil && typeId != 0) { + if (extension != nil) { + GPBCodedInputStream *newInput = + [[GPBCodedInputStream alloc] initWithData:rawBytes]; + GPBExtensionMergeFromInputStream(extension, + extension.packable, + newInput, + extensionRegistry, + self); + [newInput release]; + } else { + GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); + [unknownFields mergeMessageSetMessage:typeId data:rawBytes]; + } + } +} + +- (BOOL)parseUnknownField:(GPBCodedInputStream *)input + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry + tag:(uint32_t)tag { + GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag); + int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag); + + GPBDescriptor *descriptor = [self descriptor]; + GPBExtensionDescriptor *extension = + [extensionRegistry extensionForDescriptor:descriptor + fieldNumber:fieldNumber]; + if (extension == nil) { + if (descriptor.wireFormat && GPBWireFormatMessageSetItemTag == tag) { + [self parseMessageSet:input extensionRegistry:extensionRegistry]; + return YES; + } + } else { + if (extension.wireType == wireType) { + GPBExtensionMergeFromInputStream(extension, + extension.packable, + input, + extensionRegistry, + self); + return YES; + } + // Primitive, repeated types can be packed on unpacked on the wire, and are + // parsed either way. + if ([extension isRepeated] && + !GPBDataTypeIsObject(extension->description_->dataType) && + (extension.alternateWireType == wireType)) { + GPBExtensionMergeFromInputStream(extension, + !extension.packable, + input, + extensionRegistry, + self); + return YES; + } + } + if ([GPBUnknownFieldSet isFieldTag:tag]) { + GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); + return [unknownFields mergeFieldFrom:tag input:input]; + } else { + return NO; + } +} + +- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data { + GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); + [unknownFields addUnknownMapEntry:fieldNum value:data]; +} + +#pragma mark - MergeFromCodedInputStream Support + +static void MergeSingleFieldFromCodedInputStream( + GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax, + GPBCodedInputStream *input, GPBExtensionRegistry *extensionRegistry) { + GPBDataType fieldDataType = GPBGetFieldDataType(field); + switch (fieldDataType) { +#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE) \ + case GPBDataType##NAME: { \ + TYPE val = GPBCodedInputStreamRead##NAME(&input->state_); \ + GPBSet##FUNC_TYPE##IvarWithFieldInternal(self, field, val, syntax); \ + break; \ + } +#define CASE_SINGLE_OBJECT(NAME) \ + case GPBDataType##NAME: { \ + id val = GPBCodedInputStreamReadRetained##NAME(&input->state_); \ + GPBSetRetainedObjectIvarWithFieldInternal(self, field, val, syntax); \ + break; \ + } + CASE_SINGLE_POD(Bool, BOOL, Bool) + CASE_SINGLE_POD(Fixed32, uint32_t, UInt32) + CASE_SINGLE_POD(SFixed32, int32_t, Int32) + CASE_SINGLE_POD(Float, float, Float) + CASE_SINGLE_POD(Fixed64, uint64_t, UInt64) + CASE_SINGLE_POD(SFixed64, int64_t, Int64) + CASE_SINGLE_POD(Double, double, Double) + CASE_SINGLE_POD(Int32, int32_t, Int32) + CASE_SINGLE_POD(Int64, int64_t, Int64) + CASE_SINGLE_POD(SInt32, int32_t, Int32) + CASE_SINGLE_POD(SInt64, int64_t, Int64) + CASE_SINGLE_POD(UInt32, uint32_t, UInt32) + CASE_SINGLE_POD(UInt64, uint64_t, UInt64) + CASE_SINGLE_OBJECT(Bytes) + CASE_SINGLE_OBJECT(String) +#undef CASE_SINGLE_POD +#undef CASE_SINGLE_OBJECT + + case GPBDataTypeMessage: { + if (GPBGetHasIvarField(self, field)) { + // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has + // check again. + GPBMessage *message = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [input readMessage:message extensionRegistry:extensionRegistry]; + } else { + GPBMessage *message = [[field.msgClass alloc] init]; + [input readMessage:message extensionRegistry:extensionRegistry]; + GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, syntax); + } + break; + } + + case GPBDataTypeGroup: { + if (GPBGetHasIvarField(self, field)) { + // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has + // check again. + GPBMessage *message = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [input readGroup:GPBFieldNumber(field) + message:message + extensionRegistry:extensionRegistry]; + } else { + GPBMessage *message = [[field.msgClass alloc] init]; + [input readGroup:GPBFieldNumber(field) + message:message + extensionRegistry:extensionRegistry]; + GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, syntax); + } + break; + } + + case GPBDataTypeEnum: { + int32_t val = GPBCodedInputStreamReadEnum(&input->state_); + if (GPBHasPreservingUnknownEnumSemantics(syntax) || + [field isValidEnumValue:val]) { + GPBSetInt32IvarWithFieldInternal(self, field, val, syntax); + } else { + GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); + [unknownFields mergeVarintField:GPBFieldNumber(field) value:val]; + } + } + } // switch +} + +static void MergeRepeatedPackedFieldFromCodedInputStream( + GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax, + GPBCodedInputStream *input) { + GPBDataType fieldDataType = GPBGetFieldDataType(field); + GPBCodedInputStreamState *state = &input->state_; + id genericArray = GetOrCreateArrayIvarWithField(self, field, syntax); + int32_t length = GPBCodedInputStreamReadInt32(state); + size_t limit = GPBCodedInputStreamPushLimit(state, length); + while (GPBCodedInputStreamBytesUntilLimit(state) > 0) { + switch (fieldDataType) { +#define CASE_REPEATED_PACKED_POD(NAME, TYPE, ARRAY_TYPE) \ + case GPBDataType##NAME: { \ + TYPE val = GPBCodedInputStreamRead##NAME(state); \ + [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val]; \ + break; \ + } + CASE_REPEATED_PACKED_POD(Bool, BOOL, Bool) + CASE_REPEATED_PACKED_POD(Fixed32, uint32_t, UInt32) + CASE_REPEATED_PACKED_POD(SFixed32, int32_t, Int32) + CASE_REPEATED_PACKED_POD(Float, float, Float) + CASE_REPEATED_PACKED_POD(Fixed64, uint64_t, UInt64) + CASE_REPEATED_PACKED_POD(SFixed64, int64_t, Int64) + CASE_REPEATED_PACKED_POD(Double, double, Double) + CASE_REPEATED_PACKED_POD(Int32, int32_t, Int32) + CASE_REPEATED_PACKED_POD(Int64, int64_t, Int64) + CASE_REPEATED_PACKED_POD(SInt32, int32_t, Int32) + CASE_REPEATED_PACKED_POD(SInt64, int64_t, Int64) + CASE_REPEATED_PACKED_POD(UInt32, uint32_t, UInt32) + CASE_REPEATED_PACKED_POD(UInt64, uint64_t, UInt64) +#undef CASE_REPEATED_PACKED_POD + + case GPBDataTypeBytes: + case GPBDataTypeString: + case GPBDataTypeMessage: + case GPBDataTypeGroup: + NSCAssert(NO, @"Non primitive types can't be packed"); + break; + + case GPBDataTypeEnum: { + int32_t val = GPBCodedInputStreamReadEnum(state); + if (GPBHasPreservingUnknownEnumSemantics(syntax) || + [field isValidEnumValue:val]) { + [(GPBEnumArray*)genericArray addRawValue:val]; + } else { + GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); + [unknownFields mergeVarintField:GPBFieldNumber(field) value:val]; + } + break; + } + } // switch + } // while(BytesUntilLimit() > 0) + GPBCodedInputStreamPopLimit(state, limit); +} + +static void MergeRepeatedNotPackedFieldFromCodedInputStream( + GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax, + GPBCodedInputStream *input, GPBExtensionRegistry *extensionRegistry) { + GPBCodedInputStreamState *state = &input->state_; + id genericArray = GetOrCreateArrayIvarWithField(self, field, syntax); + switch (GPBGetFieldDataType(field)) { +#define CASE_REPEATED_NOT_PACKED_POD(NAME, TYPE, ARRAY_TYPE) \ + case GPBDataType##NAME: { \ + TYPE val = GPBCodedInputStreamRead##NAME(state); \ + [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val]; \ + break; \ + } +#define CASE_REPEATED_NOT_PACKED_OBJECT(NAME) \ + case GPBDataType##NAME: { \ + id val = GPBCodedInputStreamReadRetained##NAME(state); \ + [(NSMutableArray*)genericArray addObject:val]; \ + [val release]; \ + break; \ + } + CASE_REPEATED_NOT_PACKED_POD(Bool, BOOL, Bool) + CASE_REPEATED_NOT_PACKED_POD(Fixed32, uint32_t, UInt32) + CASE_REPEATED_NOT_PACKED_POD(SFixed32, int32_t, Int32) + CASE_REPEATED_NOT_PACKED_POD(Float, float, Float) + CASE_REPEATED_NOT_PACKED_POD(Fixed64, uint64_t, UInt64) + CASE_REPEATED_NOT_PACKED_POD(SFixed64, int64_t, Int64) + CASE_REPEATED_NOT_PACKED_POD(Double, double, Double) + CASE_REPEATED_NOT_PACKED_POD(Int32, int32_t, Int32) + CASE_REPEATED_NOT_PACKED_POD(Int64, int64_t, Int64) + CASE_REPEATED_NOT_PACKED_POD(SInt32, int32_t, Int32) + CASE_REPEATED_NOT_PACKED_POD(SInt64, int64_t, Int64) + CASE_REPEATED_NOT_PACKED_POD(UInt32, uint32_t, UInt32) + CASE_REPEATED_NOT_PACKED_POD(UInt64, uint64_t, UInt64) + CASE_REPEATED_NOT_PACKED_OBJECT(Bytes) + CASE_REPEATED_NOT_PACKED_OBJECT(String) +#undef CASE_REPEATED_NOT_PACKED_POD +#undef CASE_NOT_PACKED_OBJECT + case GPBDataTypeMessage: { + GPBMessage *message = [[field.msgClass alloc] init]; + [input readMessage:message extensionRegistry:extensionRegistry]; + [(NSMutableArray*)genericArray addObject:message]; + [message release]; + break; + } + case GPBDataTypeGroup: { + GPBMessage *message = [[field.msgClass alloc] init]; + [input readGroup:GPBFieldNumber(field) + message:message + extensionRegistry:extensionRegistry]; + [(NSMutableArray*)genericArray addObject:message]; + [message release]; + break; + } + case GPBDataTypeEnum: { + int32_t val = GPBCodedInputStreamReadEnum(state); + if (GPBHasPreservingUnknownEnumSemantics(syntax) || + [field isValidEnumValue:val]) { + [(GPBEnumArray*)genericArray addRawValue:val]; + } else { + GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); + [unknownFields mergeVarintField:GPBFieldNumber(field) value:val]; + } + break; + } + } // switch +} + +- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { + GPBDescriptor *descriptor = [self descriptor]; + GPBFileSyntax syntax = descriptor.file.syntax; + GPBCodedInputStreamState *state = &input->state_; + uint32_t tag = 0; + NSUInteger startingIndex = 0; + NSArray *fields = descriptor->fields_; + NSUInteger numFields = fields.count; + while (YES) { + BOOL merged = NO; + tag = GPBCodedInputStreamReadTag(state); + if (tag == 0) { + break; // Reached end. + } + for (NSUInteger i = 0; i < numFields; ++i) { + if (startingIndex >= numFields) startingIndex = 0; + GPBFieldDescriptor *fieldDescriptor = fields[startingIndex]; + if (GPBFieldTag(fieldDescriptor) == tag) { + GPBFieldType fieldType = fieldDescriptor.fieldType; + if (fieldType == GPBFieldTypeSingle) { + MergeSingleFieldFromCodedInputStream(self, fieldDescriptor, syntax, + input, extensionRegistry); + // Well formed protos will only have a single field once, advance + // the starting index to the next field. + startingIndex += 1; + } else if (fieldType == GPBFieldTypeRepeated) { + if (fieldDescriptor.isPackable) { + MergeRepeatedPackedFieldFromCodedInputStream( + self, fieldDescriptor, syntax, input); + // Well formed protos will only have a repeated field that is + // packed once, advance the starting index to the next field. + startingIndex += 1; + } else { + MergeRepeatedNotPackedFieldFromCodedInputStream( + self, fieldDescriptor, syntax, input, extensionRegistry); + } + } else { // fieldType == GPBFieldTypeMap + // GPB*Dictionary or NSDictionary, exact type doesn't matter at this + // point. + id map = GetOrCreateMapIvarWithField(self, fieldDescriptor, syntax); + [input readMapEntry:map + extensionRegistry:extensionRegistry + field:fieldDescriptor + parentMessage:self]; + } + merged = YES; + break; + } else { + startingIndex += 1; + } + } // for(i < numFields) + + if (!merged && (tag != 0)) { + // Primitive, repeated types can be packed on unpacked on the wire, and + // are parsed either way. The above loop covered tag in the preferred + // for, so this need to check the alternate form. + for (NSUInteger i = 0; i < numFields; ++i) { + if (startingIndex >= numFields) startingIndex = 0; + GPBFieldDescriptor *fieldDescriptor = fields[startingIndex]; + if ((fieldDescriptor.fieldType == GPBFieldTypeRepeated) && + !GPBFieldDataTypeIsObject(fieldDescriptor) && + (GPBFieldAlternateTag(fieldDescriptor) == tag)) { + BOOL alternateIsPacked = !fieldDescriptor.isPackable; + if (alternateIsPacked) { + MergeRepeatedPackedFieldFromCodedInputStream( + self, fieldDescriptor, syntax, input); + // Well formed protos will only have a repeated field that is + // packed once, advance the starting index to the next field. + startingIndex += 1; + } else { + MergeRepeatedNotPackedFieldFromCodedInputStream( + self, fieldDescriptor, syntax, input, extensionRegistry); + } + merged = YES; + break; + } else { + startingIndex += 1; + } + } + } + + if (!merged) { + if (tag == 0) { + // zero signals EOF / limit reached + return; + } else { + if (![self parseUnknownField:input + extensionRegistry:extensionRegistry + tag:tag]) { + // it's an endgroup tag + return; + } + } + } // if(!merged) + + } // while(YES) +} + +#pragma mark - MergeFrom Support + +- (void)mergeFrom:(GPBMessage *)other { + Class selfClass = [self class]; + Class otherClass = [other class]; + if (!([selfClass isSubclassOfClass:otherClass] || + [otherClass isSubclassOfClass:selfClass])) { + [NSException raise:NSInvalidArgumentException + format:@"Classes must match %@ != %@", selfClass, otherClass]; + } + + // We assume something will be done and become visible. + GPBBecomeVisibleToAutocreator(self); + + GPBDescriptor *descriptor = [[self class] descriptor]; + GPBFileSyntax syntax = descriptor.file.syntax; + + for (GPBFieldDescriptor *field in descriptor->fields_) { + GPBFieldType fieldType = field.fieldType; + if (fieldType == GPBFieldTypeSingle) { + int32_t hasIndex = GPBFieldHasIndex(field); + uint32_t fieldNumber = GPBFieldNumber(field); + if (!GPBGetHasIvar(other, hasIndex, fieldNumber)) { + // Other doesn't have the field set, on to the next. + continue; + } + GPBDataType fieldDataType = GPBGetFieldDataType(field); + switch (fieldDataType) { + case GPBDataTypeBool: + GPBSetBoolIvarWithFieldInternal( + self, field, GPBGetMessageBoolField(other, field), syntax); + break; + case GPBDataTypeSFixed32: + case GPBDataTypeEnum: + case GPBDataTypeInt32: + case GPBDataTypeSInt32: + GPBSetInt32IvarWithFieldInternal( + self, field, GPBGetMessageInt32Field(other, field), syntax); + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + GPBSetUInt32IvarWithFieldInternal( + self, field, GPBGetMessageUInt32Field(other, field), syntax); + break; + case GPBDataTypeSFixed64: + case GPBDataTypeInt64: + case GPBDataTypeSInt64: + GPBSetInt64IvarWithFieldInternal( + self, field, GPBGetMessageInt64Field(other, field), syntax); + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + GPBSetUInt64IvarWithFieldInternal( + self, field, GPBGetMessageUInt64Field(other, field), syntax); + break; + case GPBDataTypeFloat: + GPBSetFloatIvarWithFieldInternal( + self, field, GPBGetMessageFloatField(other, field), syntax); + break; + case GPBDataTypeDouble: + GPBSetDoubleIvarWithFieldInternal( + self, field, GPBGetMessageDoubleField(other, field), syntax); + break; + case GPBDataTypeBytes: + case GPBDataTypeString: { + id otherVal = GPBGetObjectIvarWithFieldNoAutocreate(other, field); + GPBSetObjectIvarWithFieldInternal(self, field, otherVal, syntax); + break; + } + case GPBDataTypeMessage: + case GPBDataTypeGroup: { + id otherVal = GPBGetObjectIvarWithFieldNoAutocreate(other, field); + if (GPBGetHasIvar(self, hasIndex, fieldNumber)) { + GPBMessage *message = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + [message mergeFrom:otherVal]; + } else { + GPBMessage *message = [otherVal copy]; + GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, + syntax); + } + break; + } + } // switch() + } else if (fieldType == GPBFieldTypeRepeated) { + // In the case of a list, they need to be appended, and there is no + // _hasIvar to worry about setting. + id otherArray = + GPBGetObjectIvarWithFieldNoAutocreate(other, field); + if (otherArray) { + GPBDataType fieldDataType = field->description_->dataType; + if (GPBDataTypeIsObject(fieldDataType)) { + NSMutableArray *resultArray = + GetOrCreateArrayIvarWithField(self, field, syntax); + [resultArray addObjectsFromArray:otherArray]; + } else if (fieldDataType == GPBDataTypeEnum) { + GPBEnumArray *resultArray = + GetOrCreateArrayIvarWithField(self, field, syntax); + [resultArray addRawValuesFromArray:otherArray]; + } else { + // The array type doesn't matter, that all implment + // -addValuesFromArray:. + GPBInt32Array *resultArray = + GetOrCreateArrayIvarWithField(self, field, syntax); + [resultArray addValuesFromArray:otherArray]; + } + } + } else { // fieldType = GPBFieldTypeMap + // In the case of a map, they need to be merged, and there is no + // _hasIvar to worry about setting. + id otherDict = GPBGetObjectIvarWithFieldNoAutocreate(other, field); + if (otherDict) { + GPBDataType keyDataType = field.mapKeyDataType; + GPBDataType valueDataType = field->description_->dataType; + if (GPBDataTypeIsObject(keyDataType) && + GPBDataTypeIsObject(valueDataType)) { + NSMutableDictionary *resultDict = + GetOrCreateMapIvarWithField(self, field, syntax); + [resultDict addEntriesFromDictionary:otherDict]; + } else if (valueDataType == GPBDataTypeEnum) { + // The exact type doesn't matter, just need to know it is a + // GPB*EnumDictionary. + GPBInt32EnumDictionary *resultDict = + GetOrCreateMapIvarWithField(self, field, syntax); + [resultDict addRawEntriesFromDictionary:otherDict]; + } else { + // The exact type doesn't matter, they all implement + // -addEntriesFromDictionary:. + GPBInt32Int32Dictionary *resultDict = + GetOrCreateMapIvarWithField(self, field, syntax); + [resultDict addEntriesFromDictionary:otherDict]; + } + } + } // if (fieldType)..else if...else + } // for(fields) + + // Unknown fields. + if (!unknownFields_) { + [self setUnknownFields:other.unknownFields]; + } else { + [unknownFields_ mergeUnknownFields:other.unknownFields]; + } + + // Extensions + + if (other->extensionMap_.count == 0) { + return; + } + + if (extensionMap_ == nil) { + extensionMap_ = + CloneExtensionMap(other->extensionMap_, NSZoneFromPointer(self)); + } else { + for (GPBExtensionDescriptor *extension in other->extensionMap_) { + id otherValue = [other->extensionMap_ objectForKey:extension]; + id value = [extensionMap_ objectForKey:extension]; + BOOL isMessageExtension = GPBExtensionIsMessage(extension); + + if (extension.repeated) { + NSMutableArray *list = value; + if (list == nil) { + list = [[NSMutableArray alloc] init]; + [extensionMap_ setObject:list forKey:extension]; + [list release]; + } + if (isMessageExtension) { + for (GPBMessage *otherListValue in otherValue) { + GPBMessage *copiedValue = [otherListValue copy]; + [list addObject:copiedValue]; + [copiedValue release]; + } + } else { + [list addObjectsFromArray:otherValue]; + } + } else { + if (isMessageExtension) { + if (value) { + [(GPBMessage *)value mergeFrom:(GPBMessage *)otherValue]; + } else { + GPBMessage *copiedValue = [otherValue copy]; + [extensionMap_ setObject:copiedValue forKey:extension]; + [copiedValue release]; + } + } else { + [extensionMap_ setObject:otherValue forKey:extension]; + } + } + + if (isMessageExtension && !extension.isRepeated) { + GPBMessage *autocreatedValue = + [[autocreatedExtensionMap_ objectForKey:extension] retain]; + // Must remove from the map before calling GPBClearMessageAutocreator() + // so that GPBClearMessageAutocreator() knows its safe to clear. + [autocreatedExtensionMap_ removeObjectForKey:extension]; + GPBClearMessageAutocreator(autocreatedValue); + [autocreatedValue release]; + } + } + } +} + +#pragma mark - isEqual: & hash Support + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (![other isKindOfClass:[self class]] && + ![self isKindOfClass:[other class]]) { + return NO; + } + + GPBMessage *otherMsg = other; + GPBDescriptor *descriptor = [[self class] descriptor]; + uint8_t *selfStorage = (uint8_t *)messageStorage_; + uint8_t *otherStorage = (uint8_t *)otherMsg->messageStorage_; + + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (GPBFieldIsMapOrArray(field)) { + // In the case of a list or map, there is no _hasIvar to worry about. + // NOTE: These are NSArray/GPB*Array or NSDictionary/GPB*Dictionary, but + // the type doesn't really matter as the objects all support -count and + // -isEqual:. + NSArray *resultMapOrArray = + GPBGetObjectIvarWithFieldNoAutocreate(self, field); + NSArray *otherMapOrArray = + GPBGetObjectIvarWithFieldNoAutocreate(other, field); + // nil and empty are equal + if (resultMapOrArray.count != 0 || otherMapOrArray.count != 0) { + if (![resultMapOrArray isEqual:otherMapOrArray]) { + return NO; + } + } + } else { // Single field + int32_t hasIndex = GPBFieldHasIndex(field); + uint32_t fieldNum = GPBFieldNumber(field); + BOOL selfHas = GPBGetHasIvar(self, hasIndex, fieldNum); + BOOL otherHas = GPBGetHasIvar(other, hasIndex, fieldNum); + if (selfHas != otherHas) { + return NO; // Differing has values, not equal. + } + if (!selfHas) { + // Same has values, was no, nothing else to check for this field. + continue; + } + // Now compare the values. + GPBDataType fieldDataType = GPBGetFieldDataType(field); + size_t fieldOffset = field->description_->offset; + switch (fieldDataType) { + case GPBDataTypeBool: { + // Bools are stored in has_bits to avoid needing explicit space in + // the storage structure. + // (the field number passed to the HasIvar helper doesn't really + // matter since the offset is never negative) + BOOL selfValue = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0); + BOOL otherValue = GPBGetHasIvar(other, (int32_t)(fieldOffset), 0); + if (selfValue != otherValue) { + return NO; + } + break; + } + case GPBDataTypeSFixed32: + case GPBDataTypeInt32: + case GPBDataTypeSInt32: + case GPBDataTypeEnum: + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + case GPBDataTypeFloat: { + GPBInternalCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits); + // These are all 32bit, signed/unsigned doesn't matter for equality. + uint32_t *selfValPtr = (uint32_t *)&selfStorage[fieldOffset]; + uint32_t *otherValPtr = (uint32_t *)&otherStorage[fieldOffset]; + if (*selfValPtr != *otherValPtr) { + return NO; + } + break; + } + case GPBDataTypeSFixed64: + case GPBDataTypeInt64: + case GPBDataTypeSInt64: + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + case GPBDataTypeDouble: { + GPBInternalCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits); + // These are all 64bit, signed/unsigned doesn't matter for equality. + uint64_t *selfValPtr = (uint64_t *)&selfStorage[fieldOffset]; + uint64_t *otherValPtr = (uint64_t *)&otherStorage[fieldOffset]; + if (*selfValPtr != *otherValPtr) { + return NO; + } + break; + } + case GPBDataTypeBytes: + case GPBDataTypeString: + case GPBDataTypeMessage: + case GPBDataTypeGroup: { + // Type doesn't matter here, they all implement -isEqual:. + id *selfValPtr = (id *)&selfStorage[fieldOffset]; + id *otherValPtr = (id *)&otherStorage[fieldOffset]; + if (![*selfValPtr isEqual:*otherValPtr]) { + return NO; + } + break; + } + } // switch() + } // if(mapOrArray)...else + } // for(fields) + + // nil and empty are equal + if (extensionMap_.count != 0 || otherMsg->extensionMap_.count != 0) { + if (![extensionMap_ isEqual:otherMsg->extensionMap_]) { + return NO; + } + } + + // nil and empty are equal + GPBUnknownFieldSet *otherUnknowns = otherMsg->unknownFields_; + if ([unknownFields_ countOfFields] != 0 || + [otherUnknowns countOfFields] != 0) { + if (![unknownFields_ isEqual:otherUnknowns]) { + return NO; + } + } + + return YES; +} + +// It is very difficult to implement a generic hash for ProtoBuf messages that +// will perform well. If you need hashing on your ProtoBufs (eg you are using +// them as dictionary keys) you will probably want to implement a ProtoBuf +// message specific hash as a category on your protobuf class. Do not make it a +// category on GPBMessage as you will conflict with this hash, and will possibly +// override hash for all generated protobufs. A good implementation of hash will +// be really fast, so we would recommend only hashing protobufs that have an +// identifier field of some kind that you can easily hash. If you implement +// hash, we would strongly recommend overriding isEqual: in your category as +// well, as the default implementation of isEqual: is extremely slow, and may +// drastically affect performance in large sets. +- (NSUInteger)hash { + GPBDescriptor *descriptor = [[self class] descriptor]; + const NSUInteger prime = 19; + uint8_t *storage = (uint8_t *)messageStorage_; + + // Start with the descriptor and then mix it with some instance info. + // Hopefully that will give a spread based on classes and what fields are set. + NSUInteger result = (NSUInteger)descriptor; + + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (GPBFieldIsMapOrArray(field)) { + // Exact type doesn't matter, just check if there are any elements. + NSArray *mapOrArray = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + NSUInteger count = mapOrArray.count; + if (count) { + // NSArray/NSDictionary use count, use the field number and the count. + result = prime * result + GPBFieldNumber(field); + result = prime * result + count; + } + } else if (GPBGetHasIvarField(self, field)) { + // Just using the field number seemed simple/fast, but then a small + // message class where all the same fields are always set (to different + // things would end up all with the same hash, so pull in some data). + GPBDataType fieldDataType = GPBGetFieldDataType(field); + size_t fieldOffset = field->description_->offset; + switch (fieldDataType) { + case GPBDataTypeBool: { + // Bools are stored in has_bits to avoid needing explicit space in + // the storage structure. + // (the field number passed to the HasIvar helper doesn't really + // matter since the offset is never negative) + BOOL value = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0); + result = prime * result + value; + break; + } + case GPBDataTypeSFixed32: + case GPBDataTypeInt32: + case GPBDataTypeSInt32: + case GPBDataTypeEnum: + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + case GPBDataTypeFloat: { + GPBInternalCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits); + // These are all 32bit, just mix it in. + uint32_t *valPtr = (uint32_t *)&storage[fieldOffset]; + result = prime * result + *valPtr; + break; + } + case GPBDataTypeSFixed64: + case GPBDataTypeInt64: + case GPBDataTypeSInt64: + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + case GPBDataTypeDouble: { + GPBInternalCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits); + // These are all 64bit, just mix what fits into an NSUInteger in. + uint64_t *valPtr = (uint64_t *)&storage[fieldOffset]; + result = prime * result + (NSUInteger)(*valPtr); + break; + } + case GPBDataTypeBytes: + case GPBDataTypeString: { + // Type doesn't matter here, they both implement -hash:. + id *valPtr = (id *)&storage[fieldOffset]; + result = prime * result + [*valPtr hash]; + break; + } + + case GPBDataTypeMessage: + case GPBDataTypeGroup: { + GPBMessage **valPtr = (GPBMessage **)&storage[fieldOffset]; + // Could call -hash on the sub message, but that could recurse pretty + // deep; follow the lead of NSArray/NSDictionary and don't really + // recurse for hash, instead use the field number and the descriptor + // of the sub message. Yes, this could suck for a bunch of messages + // where they all only differ in the sub messages, but if you are + // using a message with sub messages for something that needs -hash, + // odds are you are also copying them as keys, and that deep copy + // will also suck. + result = prime * result + GPBFieldNumber(field); + result = prime * result + (NSUInteger)[[*valPtr class] descriptor]; + break; + } + } // switch() + } + } + + // Unknowns and extensions are not included. + + return result; +} + +#pragma mark - Description Support + +- (NSString *)description { + NSString *textFormat = GPBTextFormatForMessage(self, @" "); + NSString *description = [NSString + stringWithFormat:@"<%@ %p>: {\n%@}", [self class], self, textFormat]; + return description; +} + +#if defined(DEBUG) && DEBUG + +// Xcode 5.1 added support for custom quick look info. +// https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/CustomClassDisplay_in_QuickLook/CH01-quick_look_for_custom_objects/CH01-quick_look_for_custom_objects.html#//apple_ref/doc/uid/TP40014001-CH2-SW1 +- (id)debugQuickLookObject { + return GPBTextFormatForMessage(self, nil); +} + +#endif // DEBUG + +#pragma mark - SerializedSize + +- (size_t)serializedSize { + GPBDescriptor *descriptor = [[self class] descriptor]; + size_t result = 0; + + // Has check is done explicitly, so GPBGetObjectIvarWithFieldNoAutocreate() + // avoids doing the has check again. + + // Fields. + for (GPBFieldDescriptor *fieldDescriptor in descriptor->fields_) { + GPBFieldType fieldType = fieldDescriptor.fieldType; + GPBDataType fieldDataType = GPBGetFieldDataType(fieldDescriptor); + + // Single Fields + if (fieldType == GPBFieldTypeSingle) { + BOOL selfHas = GPBGetHasIvarField(self, fieldDescriptor); + if (!selfHas) { + continue; // Nothing to do. + } + + uint32_t fieldNumber = GPBFieldNumber(fieldDescriptor); + + switch (fieldDataType) { +#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE) \ + case GPBDataType##NAME: { \ + TYPE fieldVal = GPBGetMessage##FUNC_TYPE##Field(self, fieldDescriptor); \ + result += GPBCompute##NAME##Size(fieldNumber, fieldVal); \ + break; \ + } +#define CASE_SINGLE_OBJECT(NAME) \ + case GPBDataType##NAME: { \ + id fieldVal = GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor); \ + result += GPBCompute##NAME##Size(fieldNumber, fieldVal); \ + break; \ + } + CASE_SINGLE_POD(Bool, BOOL, Bool) + CASE_SINGLE_POD(Fixed32, uint32_t, UInt32) + CASE_SINGLE_POD(SFixed32, int32_t, Int32) + CASE_SINGLE_POD(Float, float, Float) + CASE_SINGLE_POD(Fixed64, uint64_t, UInt64) + CASE_SINGLE_POD(SFixed64, int64_t, Int64) + CASE_SINGLE_POD(Double, double, Double) + CASE_SINGLE_POD(Int32, int32_t, Int32) + CASE_SINGLE_POD(Int64, int64_t, Int64) + CASE_SINGLE_POD(SInt32, int32_t, Int32) + CASE_SINGLE_POD(SInt64, int64_t, Int64) + CASE_SINGLE_POD(UInt32, uint32_t, UInt32) + CASE_SINGLE_POD(UInt64, uint64_t, UInt64) + CASE_SINGLE_OBJECT(Bytes) + CASE_SINGLE_OBJECT(String) + CASE_SINGLE_OBJECT(Message) + CASE_SINGLE_OBJECT(Group) + CASE_SINGLE_POD(Enum, int32_t, Int32) +#undef CASE_SINGLE_POD +#undef CASE_SINGLE_OBJECT + } + + // Repeated Fields + } else if (fieldType == GPBFieldTypeRepeated) { + id genericArray = + GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor); + NSUInteger count = [genericArray count]; + if (count == 0) { + continue; // Nothing to add. + } + __block size_t dataSize = 0; + + switch (fieldDataType) { +#define CASE_REPEATED_POD(NAME, TYPE, ARRAY_TYPE) \ + CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, ) +#define CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, ARRAY_ACCESSOR_NAME) \ + case GPBDataType##NAME: { \ + GPB##ARRAY_TYPE##Array *array = genericArray; \ + [array enumerate##ARRAY_ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) { \ + _Pragma("unused(idx, stop)"); \ + dataSize += GPBCompute##NAME##SizeNoTag(value); \ + }]; \ + break; \ + } +#define CASE_REPEATED_OBJECT(NAME) \ + case GPBDataType##NAME: { \ + for (id value in genericArray) { \ + dataSize += GPBCompute##NAME##SizeNoTag(value); \ + } \ + break; \ + } + CASE_REPEATED_POD(Bool, BOOL, Bool) + CASE_REPEATED_POD(Fixed32, uint32_t, UInt32) + CASE_REPEATED_POD(SFixed32, int32_t, Int32) + CASE_REPEATED_POD(Float, float, Float) + CASE_REPEATED_POD(Fixed64, uint64_t, UInt64) + CASE_REPEATED_POD(SFixed64, int64_t, Int64) + CASE_REPEATED_POD(Double, double, Double) + CASE_REPEATED_POD(Int32, int32_t, Int32) + CASE_REPEATED_POD(Int64, int64_t, Int64) + CASE_REPEATED_POD(SInt32, int32_t, Int32) + CASE_REPEATED_POD(SInt64, int64_t, Int64) + CASE_REPEATED_POD(UInt32, uint32_t, UInt32) + CASE_REPEATED_POD(UInt64, uint64_t, UInt64) + CASE_REPEATED_OBJECT(Bytes) + CASE_REPEATED_OBJECT(String) + CASE_REPEATED_OBJECT(Message) + CASE_REPEATED_OBJECT(Group) + CASE_REPEATED_POD_EXTRA(Enum, int32_t, Enum, Raw) +#undef CASE_REPEATED_POD +#undef CASE_REPEATED_POD_EXTRA +#undef CASE_REPEATED_OBJECT + } // switch + result += dataSize; + size_t tagSize = GPBComputeTagSize(GPBFieldNumber(fieldDescriptor)); + if (fieldDataType == GPBDataTypeGroup) { + // Groups have both a start and an end tag. + tagSize *= 2; + } + if (fieldDescriptor.isPackable) { + result += tagSize; + result += GPBComputeSizeTSizeAsInt32NoTag(dataSize); + } else { + result += count * tagSize; + } + + // Map<> Fields + } else { // fieldType == GPBFieldTypeMap + if (GPBDataTypeIsObject(fieldDataType) && + (fieldDescriptor.mapKeyDataType == GPBDataTypeString)) { + // If key type was string, then the map is an NSDictionary. + NSDictionary *map = + GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor); + if (map) { + result += GPBDictionaryComputeSizeInternalHelper(map, fieldDescriptor); + } + } else { + // Type will be GPB*GroupDictionary, exact type doesn't matter. + GPBInt32Int32Dictionary *map = + GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor); + result += [map computeSerializedSizeAsField:fieldDescriptor]; + } + } + } // for(fields) + + // Add any unknown fields. + if (descriptor.wireFormat) { + result += [unknownFields_ serializedSizeAsMessageSet]; + } else { + result += [unknownFields_ serializedSize]; + } + + // Add any extensions. + for (GPBExtensionDescriptor *extension in extensionMap_) { + id value = [extensionMap_ objectForKey:extension]; + result += GPBComputeExtensionSerializedSizeIncludingTag(extension, value); + } + + return result; +} + +#pragma mark - Resolve Methods Support + +typedef struct ResolveIvarAccessorMethodResult { + IMP impToAdd; + SEL encodingSelector; +} ResolveIvarAccessorMethodResult; + +static void ResolveIvarGet(GPBFieldDescriptor *field, + ResolveIvarAccessorMethodResult *result) { + GPBDataType fieldDataType = GPBGetFieldDataType(field); + switch (fieldDataType) { +#define CASE_GET(NAME, TYPE, TRUE_NAME) \ + case GPBDataType##NAME: { \ + result->impToAdd = imp_implementationWithBlock(^(id obj) { \ + return GPBGetMessage##TRUE_NAME##Field(obj, field); \ + }); \ + result->encodingSelector = @selector(get##NAME); \ + break; \ + } +#define CASE_GET_OBJECT(NAME, TYPE, TRUE_NAME) \ + case GPBDataType##NAME: { \ + result->impToAdd = imp_implementationWithBlock(^(id obj) { \ + return GPBGetObjectIvarWithField(obj, field); \ + }); \ + result->encodingSelector = @selector(get##NAME); \ + break; \ + } + CASE_GET(Bool, BOOL, Bool) + CASE_GET(Fixed32, uint32_t, UInt32) + CASE_GET(SFixed32, int32_t, Int32) + CASE_GET(Float, float, Float) + CASE_GET(Fixed64, uint64_t, UInt64) + CASE_GET(SFixed64, int64_t, Int64) + CASE_GET(Double, double, Double) + CASE_GET(Int32, int32_t, Int32) + CASE_GET(Int64, int64_t, Int64) + CASE_GET(SInt32, int32_t, Int32) + CASE_GET(SInt64, int64_t, Int64) + CASE_GET(UInt32, uint32_t, UInt32) + CASE_GET(UInt64, uint64_t, UInt64) + CASE_GET_OBJECT(Bytes, id, Object) + CASE_GET_OBJECT(String, id, Object) + CASE_GET_OBJECT(Message, id, Object) + CASE_GET_OBJECT(Group, id, Object) + CASE_GET(Enum, int32_t, Enum) +#undef CASE_GET + } +} + +static void ResolveIvarSet(GPBFieldDescriptor *field, + GPBFileSyntax syntax, + ResolveIvarAccessorMethodResult *result) { + GPBDataType fieldDataType = GPBGetFieldDataType(field); + switch (fieldDataType) { +#define CASE_SET(NAME, TYPE, TRUE_NAME) \ + case GPBDataType##NAME: { \ + result->impToAdd = imp_implementationWithBlock(^(id obj, TYPE value) { \ + return GPBSet##TRUE_NAME##IvarWithFieldInternal(obj, field, value, syntax); \ + }); \ + result->encodingSelector = @selector(set##NAME:); \ + break; \ + } + CASE_SET(Bool, BOOL, Bool) + CASE_SET(Fixed32, uint32_t, UInt32) + CASE_SET(SFixed32, int32_t, Int32) + CASE_SET(Float, float, Float) + CASE_SET(Fixed64, uint64_t, UInt64) + CASE_SET(SFixed64, int64_t, Int64) + CASE_SET(Double, double, Double) + CASE_SET(Int32, int32_t, Int32) + CASE_SET(Int64, int64_t, Int64) + CASE_SET(SInt32, int32_t, Int32) + CASE_SET(SInt64, int64_t, Int64) + CASE_SET(UInt32, uint32_t, UInt32) + CASE_SET(UInt64, uint64_t, UInt64) + CASE_SET(Bytes, id, Object) + CASE_SET(String, id, Object) + CASE_SET(Message, id, Object) + CASE_SET(Group, id, Object) + CASE_SET(Enum, int32_t, Enum) +#undef CASE_SET + } +} + ++ (BOOL)resolveInstanceMethod:(SEL)sel { + const GPBDescriptor *descriptor = [self descriptor]; + if (!descriptor) { + return [super resolveInstanceMethod:sel]; + } + + // NOTE: hasOrCountSel_/setHasSel_ will be NULL if the field for the given + // message should not have has support (done in GPBDescriptor.m), so there is + // no need for checks here to see if has*/setHas* are allowed. + + ResolveIvarAccessorMethodResult result = {NULL, NULL}; + for (GPBFieldDescriptor *field in descriptor->fields_) { + BOOL isMapOrArray = GPBFieldIsMapOrArray(field); + if (!isMapOrArray) { + // Single fields. + if (sel == field->getSel_) { + ResolveIvarGet(field, &result); + break; + } else if (sel == field->setSel_) { + ResolveIvarSet(field, descriptor.file.syntax, &result); + break; + } else if (sel == field->hasOrCountSel_) { + int32_t index = GPBFieldHasIndex(field); + uint32_t fieldNum = GPBFieldNumber(field); + result.impToAdd = imp_implementationWithBlock(^(id obj) { + return GPBGetHasIvar(obj, index, fieldNum); + }); + result.encodingSelector = @selector(getBool); + break; + } else if (sel == field->setHasSel_) { + result.impToAdd = imp_implementationWithBlock(^(id obj, BOOL value) { + if (value) { + [NSException raise:NSInvalidArgumentException + format:@"%@: %@ can only be set to NO (to clear field).", + [obj class], + NSStringFromSelector(field->setHasSel_)]; + } + GPBClearMessageField(obj, field); + }); + result.encodingSelector = @selector(setBool:); + break; + } else { + GPBOneofDescriptor *oneof = field->containingOneof_; + if (oneof && (sel == oneof->caseSel_)) { + int32_t index = GPBFieldHasIndex(field); + result.impToAdd = imp_implementationWithBlock(^(id obj) { + return GPBGetHasOneof(obj, index); + }); + result.encodingSelector = @selector(getEnum); + break; + } + } + } else { + // map<>/repeated fields. + if (sel == field->getSel_) { + if (field.fieldType == GPBFieldTypeRepeated) { + result.impToAdd = imp_implementationWithBlock(^(id obj) { + return GetArrayIvarWithField(obj, field); + }); + } else { + result.impToAdd = imp_implementationWithBlock(^(id obj) { + return GetMapIvarWithField(obj, field); + }); + } + result.encodingSelector = @selector(getArray); + break; + } else if (sel == field->setSel_) { + // Local for syntax so the block can directly capture it and not the + // full lookup. + const GPBFileSyntax syntax = descriptor.file.syntax; + result.impToAdd = imp_implementationWithBlock(^(id obj, id value) { + return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax); + }); + result.encodingSelector = @selector(setArray:); + break; + } else if (sel == field->hasOrCountSel_) { + result.impToAdd = imp_implementationWithBlock(^(id obj) { + // Type doesn't matter, all *Array and *Dictionary types support + // -count. + NSArray *arrayOrMap = + GPBGetObjectIvarWithFieldNoAutocreate(obj, field); + return [arrayOrMap count]; + }); + result.encodingSelector = @selector(getArrayCount); + break; + } + } + } + if (result.impToAdd) { + const char *encoding = + GPBMessageEncodingForSelector(result.encodingSelector, YES); + Class msgClass = descriptor.messageClass; + BOOL methodAdded = class_addMethod(msgClass, sel, result.impToAdd, encoding); + // class_addMethod() is documented as also failing if the method was already + // added; so we check if the method is already there and return success so + // the method dispatch will still happen. Why would it already be added? + // Two threads could cause the same method to be bound at the same time, + // but only one will actually bind it; the other still needs to return true + // so things will dispatch. + if (!methodAdded) { + methodAdded = GPBClassHasSel(msgClass, sel); + } + return methodAdded; + } + return [super resolveInstanceMethod:sel]; +} + ++ (BOOL)resolveClassMethod:(SEL)sel { + // Extensions scoped to a Message and looked up via class methods. + if (GPBResolveExtensionClassMethod([self descriptor].messageClass, sel)) { + return YES; + } + return [super resolveClassMethod:sel]; +} + +#pragma mark - NSCoding Support + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [self init]; + if (self) { + NSData *data = + [aDecoder decodeObjectOfClass:[NSData class] forKey:kGPBDataCoderKey]; + if (data.length) { + [self mergeFromData:data extensionRegistry:nil]; + } + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + NSData *data = [self data]; + if (data.length) { + [aCoder encodeObject:data forKey:kGPBDataCoderKey]; + } +} + +#pragma mark - KVC Support + ++ (BOOL)accessInstanceVariablesDirectly { + // Make sure KVC doesn't use instance variables. + return NO; +} + +@end + +#pragma mark - Messages from GPBUtilities.h but defined here for access to helpers. + +// Only exists for public api, no core code should use this. +id GPBGetMessageRepeatedField(GPBMessage *self, GPBFieldDescriptor *field) { +#if defined(DEBUG) && DEBUG + if (field.fieldType != GPBFieldTypeRepeated) { + [NSException raise:NSInvalidArgumentException + format:@"%@.%@ is not a repeated field.", + [self class], field.name]; + } +#endif + GPBDescriptor *descriptor = [[self class] descriptor]; + GPBFileSyntax syntax = descriptor.file.syntax; + return GetOrCreateArrayIvarWithField(self, field, syntax); +} + +// Only exists for public api, no core code should use this. +id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field) { +#if defined(DEBUG) && DEBUG + if (field.fieldType != GPBFieldTypeMap) { + [NSException raise:NSInvalidArgumentException + format:@"%@.%@ is not a map<> field.", + [self class], field.name]; + } +#endif + GPBDescriptor *descriptor = [[self class] descriptor]; + GPBFileSyntax syntax = descriptor.file.syntax; + return GetOrCreateMapIvarWithField(self, field, syntax); +} + +#pragma clang diagnostic pop |