1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #import "GPBDescriptor_PackagePrivate.h"
33 #import <objc/runtime.h>
35 #import "GPBUtilities_PackagePrivate.h"
36 #import "GPBWireFormat.h"
37 #import "GPBMessage_PackagePrivate.h"
39 // Direct access is use for speed, to avoid even internally declaring things
40 // read/write, etc. The warning is enabled in the project to ensure code calling
41 // protos can turn on -Wdirect-ivar-access without issues.
42 #pragma clang diagnostic push
43 #pragma clang diagnostic ignored "-Wdirect-ivar-access"
45 // The addresses of these variables are used as keys for objc_getAssociatedObject.
46 static const char kTextFormatExtraValueKey = 0;
47 static const char kParentClassNameValueKey = 0;
48 static const char kClassNameSuffixKey = 0;
50 // Utility function to generate selectors on the fly.
51 static SEL SelFromStrings(const char *prefix, const char *middle,
52 const char *suffix, BOOL takesArg) {
53 if (prefix == NULL && suffix == NULL && !takesArg) {
54 return sel_getUid(middle);
56 const size_t prefixLen = prefix != NULL ? strlen(prefix) : 0;
57 const size_t middleLen = strlen(middle);
58 const size_t suffixLen = suffix != NULL ? strlen(suffix) : 0;
60 prefixLen + middleLen + suffixLen + 1; // include space for null on end.
64 char buffer[totalLen];
66 memcpy(buffer, prefix, prefixLen);
67 memcpy(buffer + prefixLen, middle, middleLen);
68 buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
70 memcpy(buffer, middle, middleLen);
73 memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
76 buffer[totalLen - 2] = ':';
78 // Always null terminate it.
79 buffer[totalLen - 1] = 0;
81 SEL result = sel_getUid(buffer);
85 static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
86 NSArray *allMessageFields)
87 __attribute__((ns_returns_retained));
89 static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
90 NSArray *allMessageFields) {
91 NSMutableArray *result = [[NSMutableArray alloc] init];
92 for (GPBFieldDescriptor *fieldDesc in allMessageFields) {
93 if (fieldDesc->description_->hasIndex == hasIndex) {
94 [result addObject:fieldDesc];
100 @implementation GPBDescriptor {
102 GPBFileDescriptor *file_;
106 @synthesize messageClass = messageClass_;
107 @synthesize fields = fields_;
108 @synthesize oneofs = oneofs_;
109 @synthesize extensionRanges = extensionRanges_;
110 @synthesize extensionRangesCount = extensionRangesCount_;
111 @synthesize file = file_;
112 @synthesize wireFormat = wireFormat_;
115 allocDescriptorForClass:(Class)messageClass
116 rootClass:(Class)rootClass
117 file:(GPBFileDescriptor *)file
118 fields:(void *)fieldDescriptions
119 fieldCount:(uint32_t)fieldCount
120 storageSize:(uint32_t)storageSize
121 flags:(GPBDescriptorInitializationFlags)flags {
122 // The rootClass is no longer used, but it is passed in to ensure it
123 // was started up during initialization also.
125 NSMutableArray *fields = nil;
126 GPBFileSyntax syntax = file.syntax;
127 BOOL fieldsIncludeDefault =
128 (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
131 for (uint32_t i = 0; i < fieldCount; ++i) {
133 fields = [[NSMutableArray alloc] initWithCapacity:fieldCount];
135 // Need correctly typed pointer for array indexing below to work.
136 if (fieldsIncludeDefault) {
137 GPBMessageFieldDescriptionWithDefault *fieldDescWithDefault = fieldDescriptions;
138 desc = &(fieldDescWithDefault[i]);
140 GPBMessageFieldDescription *fieldDesc = fieldDescriptions;
141 desc = &(fieldDesc[i]);
143 GPBFieldDescriptor *fieldDescriptor =
144 [[GPBFieldDescriptor alloc] initWithFieldDescription:desc
145 includesDefault:fieldsIncludeDefault
147 [fields addObject:fieldDescriptor];
148 [fieldDescriptor release];
151 BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0;
152 GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
155 storageSize:storageSize
156 wireFormat:wireFormat];
161 - (instancetype)initWithClass:(Class)messageClass
162 file:(GPBFileDescriptor *)file
163 fields:(NSArray *)fields
164 storageSize:(uint32_t)storageSize
165 wireFormat:(BOOL)wireFormat {
166 if ((self = [super init])) {
167 messageClass_ = messageClass;
169 fields_ = [fields retain];
170 storageSize_ = storageSize;
171 wireFormat_ = wireFormat;
182 - (void)setupOneofs:(const char **)oneofNames
183 count:(uint32_t)count
184 firstHasIndex:(int32_t)firstHasIndex {
185 NSCAssert(firstHasIndex < 0, @"Should always be <0");
186 NSMutableArray *oneofs = [[NSMutableArray alloc] initWithCapacity:count];
187 for (uint32_t i = 0, hasIndex = firstHasIndex; i < count; ++i, --hasIndex) {
188 const char *name = oneofNames[i];
189 NSArray *fieldsForOneof = NewFieldsArrayForHasIndex(hasIndex, fields_);
190 NSCAssert(fieldsForOneof.count > 0,
191 @"No fields for this oneof? (%s:%d)", name, hasIndex);
192 GPBOneofDescriptor *oneofDescriptor =
193 [[GPBOneofDescriptor alloc] initWithName:name fields:fieldsForOneof];
194 [oneofs addObject:oneofDescriptor];
195 [oneofDescriptor release];
196 [fieldsForOneof release];
201 - (void)setupExtraTextInfo:(const char *)extraTextFormatInfo {
202 // Extra info is a compile time option, so skip the work if not needed.
203 if (extraTextFormatInfo) {
204 NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo];
205 for (GPBFieldDescriptor *fieldDescriptor in fields_) {
206 if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) {
207 objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey,
209 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
215 - (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count {
216 extensionRanges_ = ranges;
217 extensionRangesCount_ = count;
220 - (void)setupContainingMessageClassName:(const char *)msgClassName {
221 // Note: Only fetch the class here, can't send messages to it because
222 // that could cause cycles back to this class within +initialize if
223 // two messages have each other in fields (i.e. - they build a graph).
224 NSAssert(objc_getClass(msgClassName), @"Class %s not defined", msgClassName);
225 NSValue *parentNameValue = [NSValue valueWithPointer:msgClassName];
226 objc_setAssociatedObject(self, &kParentClassNameValueKey,
228 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
231 - (void)setupMessageClassNameSuffix:(NSString *)suffix {
233 objc_setAssociatedObject(self, &kClassNameSuffixKey,
235 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
240 return NSStringFromClass(messageClass_);
243 - (GPBDescriptor *)containingType {
244 NSValue *parentNameValue =
245 objc_getAssociatedObject(self, &kParentClassNameValueKey);
246 if (!parentNameValue) {
249 const char *parentName = [parentNameValue pointerValue];
250 Class parentClass = objc_getClass(parentName);
251 NSAssert(parentClass, @"Class %s not defined", parentName);
252 return [parentClass descriptor];
255 - (NSString *)fullName {
256 NSString *className = NSStringFromClass(self.messageClass);
257 GPBFileDescriptor *file = self.file;
258 NSString *objcPrefix = file.objcPrefix;
259 if (objcPrefix && ![className hasPrefix:objcPrefix]) {
261 @"Class didn't have correct prefix? (%@ - %@)",
262 className, objcPrefix);
265 GPBDescriptor *parent = self.containingType;
267 NSString *name = nil;
269 NSString *parentClassName = NSStringFromClass(parent.messageClass);
270 // The generator will add _Class to avoid reserved words, drop it.
271 NSString *suffix = objc_getAssociatedObject(parent, &kClassNameSuffixKey);
273 if (![parentClassName hasSuffix:suffix]) {
275 @"ParentMessage class didn't have correct suffix? (%@ - %@)",
280 [parentClassName substringToIndex:(parentClassName.length - suffix.length)];
282 NSString *parentPrefix = [parentClassName stringByAppendingString:@"_"];
283 if (![className hasPrefix:parentPrefix]) {
285 @"Class didn't have the correct parent name prefix? (%@ - %@)",
286 parentPrefix, className);
289 name = [className substringFromIndex:parentPrefix.length];
291 name = [className substringFromIndex:objcPrefix.length];
294 // The generator will add _Class to avoid reserved words, drop it.
295 NSString *suffix = objc_getAssociatedObject(self, &kClassNameSuffixKey);
297 if (![name hasSuffix:suffix]) {
299 @"Message class didn't have correct suffix? (%@ - %@)",
303 name = [name substringToIndex:(name.length - suffix.length)];
306 NSString *prefix = (parent != nil ? parent.fullName : file.package);
308 if (prefix.length > 0) {
309 result = [NSString stringWithFormat:@"%@.%@", prefix, name];
316 - (id)copyWithZone:(NSZone *)zone {
318 return [self retain];
321 - (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
322 for (GPBFieldDescriptor *descriptor in fields_) {
323 if (GPBFieldNumber(descriptor) == fieldNumber) {
330 - (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
331 for (GPBFieldDescriptor *descriptor in fields_) {
332 if ([descriptor.name isEqual:name]) {
339 - (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
340 for (GPBOneofDescriptor *descriptor in oneofs_) {
341 if ([descriptor.name isEqual:name]) {
350 @implementation GPBFileDescriptor {
352 NSString *objcPrefix_;
353 GPBFileSyntax syntax_;
356 @synthesize package = package_;
357 @synthesize objcPrefix = objcPrefix_;
358 @synthesize syntax = syntax_;
360 - (instancetype)initWithPackage:(NSString *)package
361 objcPrefix:(NSString *)objcPrefix
362 syntax:(GPBFileSyntax)syntax {
365 package_ = [package copy];
366 objcPrefix_ = [objcPrefix copy];
372 - (instancetype)initWithPackage:(NSString *)package
373 syntax:(GPBFileSyntax)syntax {
376 package_ = [package copy];
384 [objcPrefix_ release];
390 @implementation GPBOneofDescriptor
392 @synthesize fields = fields_;
394 - (instancetype)initWithName:(const char *)name fields:(NSArray *)fields {
398 fields_ = [fields retain];
399 for (GPBFieldDescriptor *fieldDesc in fields) {
400 fieldDesc->containingOneof_ = self;
403 caseSel_ = SelFromStrings(NULL, name, "OneOfCase", NO);
414 return (NSString * _Nonnull)@(name_);
417 - (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
418 for (GPBFieldDescriptor *descriptor in fields_) {
419 if (GPBFieldNumber(descriptor) == fieldNumber) {
426 - (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
427 for (GPBFieldDescriptor *descriptor in fields_) {
428 if ([descriptor.name isEqual:name]) {
437 uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
438 GPBMessageFieldDescription *description = self->description_;
439 GPBWireFormat format;
440 if ((description->flags & GPBFieldMapKeyMask) != 0) {
441 // Maps are repeated messages on the wire.
442 format = GPBWireFormatForType(GPBDataTypeMessage, NO);
444 format = GPBWireFormatForType(description->dataType,
445 ((description->flags & GPBFieldPacked) != 0));
447 return GPBWireFormatMakeTag(description->number, format);
450 uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) {
451 GPBMessageFieldDescription *description = self->description_;
452 NSCAssert((description->flags & GPBFieldRepeated) != 0,
453 @"Only valid on repeated fields");
454 GPBWireFormat format =
455 GPBWireFormatForType(description->dataType,
456 ((description->flags & GPBFieldPacked) == 0));
457 return GPBWireFormatMakeTag(description->number, format);
460 @implementation GPBFieldDescriptor {
461 GPBGenericValue defaultValue_;
467 // If protos are generated with GenerateEnumDescriptors on then it will
468 // be a enumDescriptor, otherwise it will be a enumVerifier.
470 GPBEnumDescriptor *enumDescriptor_;
471 GPBEnumValidationFunc enumVerifier_;
475 @synthesize msgClass = msgClass_;
476 @synthesize containingOneof = containingOneof_;
478 - (instancetype)init {
479 // Throw an exception if people attempt to not use the designated initializer.
482 [self doesNotRecognizeSelector:_cmd];
488 - (instancetype)initWithFieldDescription:(void *)description
489 includesDefault:(BOOL)includesDefault
490 syntax:(GPBFileSyntax)syntax {
491 if ((self = [super init])) {
492 GPBMessageFieldDescription *coreDesc;
493 if (includesDefault) {
494 coreDesc = &(((GPBMessageFieldDescriptionWithDefault *)description)->core);
496 coreDesc = description;
498 description_ = coreDesc;
499 getSel_ = sel_getUid(coreDesc->name);
500 setSel_ = SelFromStrings("set", coreDesc->name, NULL, YES);
502 GPBDataType dataType = coreDesc->dataType;
503 BOOL isMessage = GPBDataTypeIsMessage(dataType);
504 BOOL isMapOrArray = GPBFieldIsMapOrArray(self);
507 // map<>/repeated fields get a *Count property (inplace of a has*) to
508 // support checking if there are any entries without triggering
510 hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO);
512 // If there is a positive hasIndex, then:
513 // - All fields types for proto2 messages get has* selectors.
514 // - Only message fields for proto3 messages get has* selectors.
515 // Note: the positive check is to handle oneOfs, we can't check
516 // containingOneof_ because it isn't set until after initialization.
517 if ((coreDesc->hasIndex >= 0) &&
518 (coreDesc->hasIndex != GPBNoHasBit) &&
519 ((syntax != GPBFileSyntaxProto3) || isMessage)) {
520 hasOrCountSel_ = SelFromStrings("has", coreDesc->name, NULL, NO);
521 setHasSel_ = SelFromStrings("setHas", coreDesc->name, NULL, YES);
525 // Extra type specific data.
527 const char *className = coreDesc->dataTypeSpecific.className;
528 // Note: Only fetch the class here, can't send messages to it because
529 // that could cause cycles back to this class within +initialize if
530 // two messages have each other in fields (i.e. - they build a graph).
531 msgClass_ = objc_getClass(className);
532 NSAssert(msgClass_, @"Class %s not defined", className);
533 } else if (dataType == GPBDataTypeEnum) {
534 if ((coreDesc->flags & GPBFieldHasEnumDescriptor) != 0) {
535 enumHandling_.enumDescriptor_ =
536 coreDesc->dataTypeSpecific.enumDescFunc();
538 enumHandling_.enumVerifier_ =
539 coreDesc->dataTypeSpecific.enumVerifier;
543 // Non map<>/repeated fields can have defaults in proto2 syntax.
544 if (!isMapOrArray && includesDefault) {
545 defaultValue_ = ((GPBMessageFieldDescriptionWithDefault *)description)->defaultValue;
546 if (dataType == GPBDataTypeBytes) {
547 // Data stored as a length prefixed (network byte order) c-string in
548 // descriptor structure.
549 const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData;
552 memcpy(&length, bytes, sizeof(length));
553 length = ntohl(length);
554 bytes += sizeof(length);
555 defaultValue_.valueData =
556 [[NSData alloc] initWithBytes:bytes length:length];
565 if (description_->dataType == GPBDataTypeBytes &&
566 !(description_->flags & GPBFieldRepeated)) {
567 [defaultValue_.valueData release];
572 - (GPBDataType)dataType {
573 return description_->dataType;
576 - (BOOL)hasDefaultValue {
577 return (description_->flags & GPBFieldHasDefaultValue) != 0;
581 return description_->number;
585 return (NSString * _Nonnull)@(description_->name);
589 return (description_->flags & GPBFieldRequired) != 0;
593 return (description_->flags & GPBFieldOptional) != 0;
596 - (GPBFieldType)fieldType {
597 GPBFieldFlags flags = description_->flags;
598 if ((flags & GPBFieldRepeated) != 0) {
599 return GPBFieldTypeRepeated;
600 } else if ((flags & GPBFieldMapKeyMask) != 0) {
601 return GPBFieldTypeMap;
603 return GPBFieldTypeSingle;
607 - (GPBDataType)mapKeyDataType {
608 switch (description_->flags & GPBFieldMapKeyMask) {
609 case GPBFieldMapKeyInt32:
610 return GPBDataTypeInt32;
611 case GPBFieldMapKeyInt64:
612 return GPBDataTypeInt64;
613 case GPBFieldMapKeyUInt32:
614 return GPBDataTypeUInt32;
615 case GPBFieldMapKeyUInt64:
616 return GPBDataTypeUInt64;
617 case GPBFieldMapKeySInt32:
618 return GPBDataTypeSInt32;
619 case GPBFieldMapKeySInt64:
620 return GPBDataTypeSInt64;
621 case GPBFieldMapKeyFixed32:
622 return GPBDataTypeFixed32;
623 case GPBFieldMapKeyFixed64:
624 return GPBDataTypeFixed64;
625 case GPBFieldMapKeySFixed32:
626 return GPBDataTypeSFixed32;
627 case GPBFieldMapKeySFixed64:
628 return GPBDataTypeSFixed64;
629 case GPBFieldMapKeyBool:
630 return GPBDataTypeBool;
631 case GPBFieldMapKeyString:
632 return GPBDataTypeString;
635 NSAssert(0, @"Not a map type");
636 return GPBDataTypeInt32; // For lack of anything better.
641 return (description_->flags & GPBFieldPacked) != 0;
644 - (BOOL)isValidEnumValue:(int32_t)value {
645 NSAssert(description_->dataType == GPBDataTypeEnum,
646 @"Field Must be of type GPBDataTypeEnum");
647 if (description_->flags & GPBFieldHasEnumDescriptor) {
648 return enumHandling_.enumDescriptor_.enumVerifier(value);
650 return enumHandling_.enumVerifier_(value);
654 - (GPBEnumDescriptor *)enumDescriptor {
655 if (description_->flags & GPBFieldHasEnumDescriptor) {
656 return enumHandling_.enumDescriptor_;
662 - (GPBGenericValue)defaultValue {
663 // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
664 // to an actual defaultValue in our initializer.
665 GPBGenericValue value = defaultValue_;
667 if (!(description_->flags & GPBFieldRepeated)) {
668 // We special handle data and strings. If they are nil, we replace them
669 // with empty string/empty data.
670 GPBDataType type = description_->dataType;
671 if (type == GPBDataTypeBytes && value.valueData == nil) {
672 value.valueData = GPBEmptyNSData();
673 } else if (type == GPBDataTypeString && value.valueString == nil) {
674 value.valueString = @"";
680 - (NSString *)textFormatName {
681 if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
682 NSValue *extraInfoValue =
683 objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
684 // Support can be left out at generation time.
685 if (!extraInfoValue) {
688 const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
689 return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self),
693 // The logic here has to match SetCommonFieldVariables() from
694 // objectivec_field.cc in the proto compiler.
695 NSString *name = self.name;
696 NSUInteger len = [name length];
698 // Remove the "_p" added to reserved names.
699 if ([name hasSuffix:@"_p"]) {
700 name = [name substringToIndex:(len - 2)];
704 // Remove "Array" from the end for repeated fields.
705 if (((description_->flags & GPBFieldRepeated) != 0) &&
706 [name hasSuffix:@"Array"]) {
707 name = [name substringToIndex:(len - 5)];
711 // Groups vs. other fields.
712 if (description_->dataType == GPBDataTypeGroup) {
713 // Just capitalize the first letter.
714 unichar firstChar = [name characterAtIndex:0];
715 if (firstChar >= 'a' && firstChar <= 'z') {
716 NSString *firstCharString =
717 [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
719 [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
720 withString:firstCharString];
726 // Undo the CamelCase.
727 NSMutableString *result = [NSMutableString stringWithCapacity:len];
728 for (uint32_t i = 0; i < len; i++) {
729 unichar c = [name characterAtIndex:i];
730 if (c >= 'A' && c <= 'Z') {
732 [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
734 [result appendFormat:@"%C", c];
737 [result appendFormat:@"%C", c];
746 @implementation GPBEnumDescriptor {
748 // valueNames_ is a single c string with all of the value names appended
749 // together, each null terminated. -calcValueNameOffsets fills in
750 // nameOffsets_ with the offsets to allow quicker access to the individual
752 const char *valueNames_;
753 const int32_t *values_;
754 GPBEnumValidationFunc enumVerifier_;
755 const uint8_t *extraTextFormatInfo_;
756 uint32_t *nameOffsets_;
757 uint32_t valueCount_;
760 @synthesize name = name_;
761 @synthesize enumVerifier = enumVerifier_;
764 allocDescriptorForName:(NSString *)name
765 valueNames:(const char *)valueNames
766 values:(const int32_t *)values
767 count:(uint32_t)valueCount
768 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
769 GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
770 valueNames:valueNames
773 enumVerifier:enumVerifier];
778 allocDescriptorForName:(NSString *)name
779 valueNames:(const char *)valueNames
780 values:(const int32_t *)values
781 count:(uint32_t)valueCount
782 enumVerifier:(GPBEnumValidationFunc)enumVerifier
783 extraTextFormatInfo:(const char *)extraTextFormatInfo {
784 // Call the common case.
785 GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
786 valueNames:valueNames
789 enumVerifier:enumVerifier];
790 // Set the extra info.
791 descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
795 - (instancetype)initWithName:(NSString *)name
796 valueNames:(const char *)valueNames
797 values:(const int32_t *)values
798 count:(uint32_t)valueCount
799 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
800 if ((self = [super init])) {
802 valueNames_ = valueNames;
804 valueCount_ = valueCount;
805 enumVerifier_ = enumVerifier;
812 if (nameOffsets_) free(nameOffsets_);
816 - (void)calcValueNameOffsets {
817 @synchronized(self) {
818 if (nameOffsets_ != NULL) {
821 uint32_t *offsets = malloc(valueCount_ * sizeof(uint32_t));
822 const char *scan = valueNames_;
823 for (uint32_t i = 0; i < valueCount_; ++i) {
824 offsets[i] = (uint32_t)(scan - valueNames_);
825 while (*scan != '\0') ++scan;
826 ++scan; // Step over the null.
828 nameOffsets_ = offsets;
832 - (NSString *)enumNameForValue:(int32_t)number {
833 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
835 for (uint32_t i = 0; i < valueCount_; ++i) {
836 if (values_[i] == number) {
837 const char *valueName = valueNames_ + nameOffsets_[i];
838 NSString *fullName = [NSString stringWithFormat:@"%@_%s", name_, valueName];
845 - (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
846 // Must have the prefix.
847 NSUInteger prefixLen = name_.length + 1;
848 if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
849 ([name characterAtIndex:prefixLen - 1] != '_')) {
853 // Skip over the prefix.
854 const char *nameAsCStr = [name UTF8String];
855 nameAsCStr += prefixLen;
857 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
860 for (uint32_t i = 0; i < valueCount_; ++i) {
861 const char *valueName = valueNames_ + nameOffsets_[i];
862 if (strcmp(nameAsCStr, valueName) == 0) {
864 *outValue = values_[i];
872 - (BOOL)getValue:(int32_t *)outValue forEnumTextFormatName:(NSString *)textFormatName {
873 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
875 for (uint32_t i = 0; i < valueCount_; ++i) {
876 int32_t value = values_[i];
877 NSString *valueTextFormatName = [self textFormatNameForValue:value];
878 if ([valueTextFormatName isEqual:textFormatName]) {
888 - (NSString *)textFormatNameForValue:(int32_t)number {
889 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
891 // Find the EnumValue descriptor and its index.
893 uint32_t valueDescriptorIndex;
894 for (valueDescriptorIndex = 0; valueDescriptorIndex < valueCount_;
895 ++valueDescriptorIndex) {
896 if (values_[valueDescriptorIndex] == number) {
906 NSString *result = nil;
907 // Naming adds an underscore between enum name and value name, skip that also.
908 const char *valueName = valueNames_ + nameOffsets_[valueDescriptorIndex];
909 NSString *shortName = @(valueName);
911 // See if it is in the map of special format handling.
912 if (extraTextFormatInfo_) {
913 result = GPBDecodeTextFormatName(extraTextFormatInfo_,
914 (int32_t)valueDescriptorIndex, shortName);
916 // Logic here needs to match what objectivec_enum.cc does in the proto
919 NSUInteger len = [shortName length];
920 NSMutableString *worker = [NSMutableString stringWithCapacity:len];
921 for (NSUInteger i = 0; i < len; i++) {
922 unichar c = [shortName characterAtIndex:i];
923 if (i > 0 && c >= 'A' && c <= 'Z') {
924 [worker appendString:@"_"];
926 [worker appendFormat:@"%c", toupper((char)c)];
935 @implementation GPBExtensionDescriptor {
936 GPBGenericValue defaultValue_;
939 @synthesize containingMessageClass = containingMessageClass_;
941 - (instancetype)initWithExtensionDescription:
942 (GPBExtensionDescription *)description {
943 if ((self = [super init])) {
944 description_ = description;
946 #if defined(DEBUG) && DEBUG
947 const char *className = description->messageOrGroupClassName;
949 NSAssert(objc_lookUpClass(className) != Nil,
950 @"Class %s not defined", className);
954 if (description->extendedClass) {
955 Class containingClass = objc_lookUpClass(description->extendedClass);
956 NSAssert(containingClass, @"Class %s not defined",
957 description->extendedClass);
958 containingMessageClass_ = containingClass;
961 GPBDataType type = description_->dataType;
962 if (type == GPBDataTypeBytes) {
963 // Data stored as a length prefixed c-string in descriptor records.
964 const uint8_t *bytes =
965 (const uint8_t *)description->defaultValue.valueData;
968 memcpy(&length, bytes, sizeof(length));
969 // The length is stored in network byte order.
970 length = ntohl(length);
971 bytes += sizeof(length);
972 defaultValue_.valueData =
973 [[NSData alloc] initWithBytes:bytes length:length];
975 } else if (type == GPBDataTypeMessage || type == GPBDataTypeGroup) {
976 // The default is looked up in -defaultValue instead since extensions
977 // aren't common, we avoid the hit startup hit and it avoid initialization
980 defaultValue_ = description->defaultValue;
987 if ((description_->dataType == GPBDataTypeBytes) &&
988 !GPBExtensionIsRepeated(description_)) {
989 [defaultValue_.valueData release];
994 - (instancetype)copyWithZone:(NSZone *)zone {
997 return [self retain];
1000 - (NSString *)singletonName {
1001 return (NSString * _Nonnull)@(description_->singletonName);
1004 - (const char *)singletonNameC {
1005 return description_->singletonName;
1008 - (uint32_t)fieldNumber {
1009 return description_->fieldNumber;
1012 - (GPBDataType)dataType {
1013 return description_->dataType;
1016 - (GPBWireFormat)wireType {
1017 return GPBWireFormatForType(description_->dataType,
1018 GPBExtensionIsPacked(description_));
1021 - (GPBWireFormat)alternateWireType {
1022 NSAssert(GPBExtensionIsRepeated(description_),
1023 @"Only valid on repeated extensions");
1024 return GPBWireFormatForType(description_->dataType,
1025 !GPBExtensionIsPacked(description_));
1028 - (BOOL)isRepeated {
1029 return GPBExtensionIsRepeated(description_);
1032 - (BOOL)isPackable {
1033 return GPBExtensionIsPacked(description_);
1037 return objc_getClass(description_->messageOrGroupClassName);
1040 - (GPBEnumDescriptor *)enumDescriptor {
1041 if (description_->dataType == GPBDataTypeEnum) {
1042 GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
1043 return enumDescriptor;
1048 - (id)defaultValue {
1049 if (GPBExtensionIsRepeated(description_)) {
1053 switch (description_->dataType) {
1054 case GPBDataTypeBool:
1055 return @(defaultValue_.valueBool);
1056 case GPBDataTypeFloat:
1057 return @(defaultValue_.valueFloat);
1058 case GPBDataTypeDouble:
1059 return @(defaultValue_.valueDouble);
1060 case GPBDataTypeInt32:
1061 case GPBDataTypeSInt32:
1062 case GPBDataTypeEnum:
1063 case GPBDataTypeSFixed32:
1064 return @(defaultValue_.valueInt32);
1065 case GPBDataTypeInt64:
1066 case GPBDataTypeSInt64:
1067 case GPBDataTypeSFixed64:
1068 return @(defaultValue_.valueInt64);
1069 case GPBDataTypeUInt32:
1070 case GPBDataTypeFixed32:
1071 return @(defaultValue_.valueUInt32);
1072 case GPBDataTypeUInt64:
1073 case GPBDataTypeFixed64:
1074 return @(defaultValue_.valueUInt64);
1075 case GPBDataTypeBytes:
1076 // Like message fields, the default is zero length data.
1077 return (defaultValue_.valueData ? defaultValue_.valueData
1078 : GPBEmptyNSData());
1079 case GPBDataTypeString:
1080 // Like message fields, the default is zero length string.
1081 return (defaultValue_.valueString ? defaultValue_.valueString : @"");
1082 case GPBDataTypeGroup:
1083 case GPBDataTypeMessage:
1088 - (NSComparisonResult)compareByFieldNumber:(GPBExtensionDescriptor *)other {
1089 int32_t selfNumber = description_->fieldNumber;
1090 int32_t otherNumber = other->description_->fieldNumber;
1091 if (selfNumber < otherNumber) {
1092 return NSOrderedAscending;
1093 } else if (selfNumber == otherNumber) {
1094 return NSOrderedSame;
1096 return NSOrderedDescending;
1102 #pragma clang diagnostic pop