blob: 0753a9485bb833c56715269ffb621d4a5059dca6 [file] [log] [blame]
Jakob Buchgraber8cc62f72018-07-09 16:45:04 +02001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
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
14// distribution.
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.
18//
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.
30
31#import "GPBDescriptor_PackagePrivate.h"
32
33#import <objc/runtime.h>
34
35#import "GPBUtilities_PackagePrivate.h"
36#import "GPBWireFormat.h"
37#import "GPBMessage_PackagePrivate.h"
38
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"
44
45// The addresses of these variables are used as keys for objc_getAssociatedObject.
46static const char kTextFormatExtraValueKey = 0;
47static const char kParentClassNameValueKey = 0;
48static const char kClassNameSuffixKey = 0;
49
50// Utility function to generate selectors on the fly.
51static 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);
55 }
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;
59 size_t totalLen =
60 prefixLen + middleLen + suffixLen + 1; // include space for null on end.
61 if (takesArg) {
62 totalLen += 1;
63 }
64 char buffer[totalLen];
65 if (prefix != NULL) {
66 memcpy(buffer, prefix, prefixLen);
67 memcpy(buffer + prefixLen, middle, middleLen);
68 buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
69 } else {
70 memcpy(buffer, middle, middleLen);
71 }
72 if (suffix != NULL) {
73 memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
74 }
75 if (takesArg) {
76 buffer[totalLen - 2] = ':';
77 }
78 // Always null terminate it.
79 buffer[totalLen - 1] = 0;
80
81 SEL result = sel_getUid(buffer);
82 return result;
83}
84
85static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
86 NSArray *allMessageFields)
87 __attribute__((ns_returns_retained));
88
89static 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];
95 }
96 }
97 return result;
98}
99
100@implementation GPBDescriptor {
101 Class messageClass_;
102 GPBFileDescriptor *file_;
103 BOOL wireFormat_;
104}
105
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_;
113
114+ (instancetype)
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.
124 (void)rootClass;
125 NSMutableArray *fields = nil;
126 GPBFileSyntax syntax = file.syntax;
127 BOOL fieldsIncludeDefault =
128 (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
129
130 void *desc;
131 for (uint32_t i = 0; i < fieldCount; ++i) {
132 if (fields == nil) {
133 fields = [[NSMutableArray alloc] initWithCapacity:fieldCount];
134 }
135 // Need correctly typed pointer for array indexing below to work.
136 if (fieldsIncludeDefault) {
137 GPBMessageFieldDescriptionWithDefault *fieldDescWithDefault = fieldDescriptions;
138 desc = &(fieldDescWithDefault[i]);
139 } else {
140 GPBMessageFieldDescription *fieldDesc = fieldDescriptions;
141 desc = &(fieldDesc[i]);
142 }
143 GPBFieldDescriptor *fieldDescriptor =
144 [[GPBFieldDescriptor alloc] initWithFieldDescription:desc
145 includesDefault:fieldsIncludeDefault
146 syntax:syntax];
147 [fields addObject:fieldDescriptor];
148 [fieldDescriptor release];
149 }
150
151 BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0;
152 GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
153 file:file
154 fields:fields
155 storageSize:storageSize
156 wireFormat:wireFormat];
157 [fields release];
158 return descriptor;
159}
160
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;
168 file_ = file;
169 fields_ = [fields retain];
170 storageSize_ = storageSize;
171 wireFormat_ = wireFormat;
172 }
173 return self;
174}
175
176- (void)dealloc {
177 [fields_ release];
178 [oneofs_ release];
179 [super dealloc];
180}
181
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];
197 }
198 oneofs_ = oneofs;
199}
200
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,
208 extraInfoValue,
209 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
210 }
211 }
212 }
213}
214
215- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count {
216 extensionRanges_ = ranges;
217 extensionRangesCount_ = count;
218}
219
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,
227 parentNameValue,
228 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
229}
230
231- (void)setupMessageClassNameSuffix:(NSString *)suffix {
232 if (suffix.length) {
233 objc_setAssociatedObject(self, &kClassNameSuffixKey,
234 suffix,
235 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
236 }
237}
238
239- (NSString *)name {
240 return NSStringFromClass(messageClass_);
241}
242
243- (GPBDescriptor *)containingType {
244 NSValue *parentNameValue =
245 objc_getAssociatedObject(self, &kParentClassNameValueKey);
246 if (!parentNameValue) {
247 return nil;
248 }
249 const char *parentName = [parentNameValue pointerValue];
250 Class parentClass = objc_getClass(parentName);
251 NSAssert(parentClass, @"Class %s not defined", parentName);
252 return [parentClass descriptor];
253}
254
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]) {
260 NSAssert(0,
261 @"Class didn't have correct prefix? (%@ - %@)",
262 className, objcPrefix);
263 return nil;
264 }
265 GPBDescriptor *parent = self.containingType;
266
267 NSString *name = nil;
268 if (parent) {
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);
272 if (suffix) {
273 if (![parentClassName hasSuffix:suffix]) {
274 NSAssert(0,
275 @"ParentMessage class didn't have correct suffix? (%@ - %@)",
276 className, suffix);
277 return nil;
278 }
279 parentClassName =
280 [parentClassName substringToIndex:(parentClassName.length - suffix.length)];
281 }
282 NSString *parentPrefix = [parentClassName stringByAppendingString:@"_"];
283 if (![className hasPrefix:parentPrefix]) {
284 NSAssert(0,
285 @"Class didn't have the correct parent name prefix? (%@ - %@)",
286 parentPrefix, className);
287 return nil;
288 }
289 name = [className substringFromIndex:parentPrefix.length];
290 } else {
291 name = [className substringFromIndex:objcPrefix.length];
292 }
293
294 // The generator will add _Class to avoid reserved words, drop it.
295 NSString *suffix = objc_getAssociatedObject(self, &kClassNameSuffixKey);
296 if (suffix) {
297 if (![name hasSuffix:suffix]) {
298 NSAssert(0,
299 @"Message class didn't have correct suffix? (%@ - %@)",
300 name, suffix);
301 return nil;
302 }
303 name = [name substringToIndex:(name.length - suffix.length)];
304 }
305
306 NSString *prefix = (parent != nil ? parent.fullName : file.package);
307 NSString *result;
308 if (prefix.length > 0) {
309 result = [NSString stringWithFormat:@"%@.%@", prefix, name];
310 } else {
311 result = name;
312 }
313 return result;
314}
315
316- (id)copyWithZone:(NSZone *)zone {
317#pragma unused(zone)
318 return [self retain];
319}
320
321- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
322 for (GPBFieldDescriptor *descriptor in fields_) {
323 if (GPBFieldNumber(descriptor) == fieldNumber) {
324 return descriptor;
325 }
326 }
327 return nil;
328}
329
330- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
331 for (GPBFieldDescriptor *descriptor in fields_) {
332 if ([descriptor.name isEqual:name]) {
333 return descriptor;
334 }
335 }
336 return nil;
337}
338
339- (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
340 for (GPBOneofDescriptor *descriptor in oneofs_) {
341 if ([descriptor.name isEqual:name]) {
342 return descriptor;
343 }
344 }
345 return nil;
346}
347
348@end
349
350@implementation GPBFileDescriptor {
351 NSString *package_;
352 NSString *objcPrefix_;
353 GPBFileSyntax syntax_;
354}
355
356@synthesize package = package_;
357@synthesize objcPrefix = objcPrefix_;
358@synthesize syntax = syntax_;
359
360- (instancetype)initWithPackage:(NSString *)package
361 objcPrefix:(NSString *)objcPrefix
362 syntax:(GPBFileSyntax)syntax {
363 self = [super init];
364 if (self) {
365 package_ = [package copy];
366 objcPrefix_ = [objcPrefix copy];
367 syntax_ = syntax;
368 }
369 return self;
370}
371
372- (instancetype)initWithPackage:(NSString *)package
373 syntax:(GPBFileSyntax)syntax {
374 self = [super init];
375 if (self) {
376 package_ = [package copy];
377 syntax_ = syntax;
378 }
379 return self;
380}
381
382- (void)dealloc {
383 [package_ release];
384 [objcPrefix_ release];
385 [super dealloc];
386}
387
388@end
389
390@implementation GPBOneofDescriptor
391
392@synthesize fields = fields_;
393
394- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields {
395 self = [super init];
396 if (self) {
397 name_ = name;
398 fields_ = [fields retain];
399 for (GPBFieldDescriptor *fieldDesc in fields) {
400 fieldDesc->containingOneof_ = self;
401 }
402
403 caseSel_ = SelFromStrings(NULL, name, "OneOfCase", NO);
404 }
405 return self;
406}
407
408- (void)dealloc {
409 [fields_ release];
410 [super dealloc];
411}
412
413- (NSString *)name {
414 return @(name_);
415}
416
417- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
418 for (GPBFieldDescriptor *descriptor in fields_) {
419 if (GPBFieldNumber(descriptor) == fieldNumber) {
420 return descriptor;
421 }
422 }
423 return nil;
424}
425
426- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
427 for (GPBFieldDescriptor *descriptor in fields_) {
428 if ([descriptor.name isEqual:name]) {
429 return descriptor;
430 }
431 }
432 return nil;
433}
434
435@end
436
437uint32_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);
443 } else {
444 format = GPBWireFormatForType(description->dataType,
445 ((description->flags & GPBFieldPacked) != 0));
446 }
447 return GPBWireFormatMakeTag(description->number, format);
448}
449
450uint32_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);
458}
459
460@implementation GPBFieldDescriptor {
461 GPBGenericValue defaultValue_;
462
463 // Message ivars
464 Class msgClass_;
465
466 // Enum ivars.
467 // If protos are generated with GenerateEnumDescriptors on then it will
468 // be a enumDescriptor, otherwise it will be a enumVerifier.
469 union {
470 GPBEnumDescriptor *enumDescriptor_;
471 GPBEnumValidationFunc enumVerifier_;
472 } enumHandling_;
473}
474
475@synthesize msgClass = msgClass_;
476@synthesize containingOneof = containingOneof_;
477
478- (instancetype)init {
479 // Throw an exception if people attempt to not use the designated initializer.
480 self = [super init];
481 if (self != nil) {
482 [self doesNotRecognizeSelector:_cmd];
483 self = nil;
484 }
485 return self;
486}
487
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);
495 } else {
496 coreDesc = description;
497 }
498 description_ = coreDesc;
499 getSel_ = sel_getUid(coreDesc->name);
500 setSel_ = SelFromStrings("set", coreDesc->name, NULL, YES);
501
502 GPBDataType dataType = coreDesc->dataType;
503 BOOL isMessage = GPBDataTypeIsMessage(dataType);
504 BOOL isMapOrArray = GPBFieldIsMapOrArray(self);
505
506 if (isMapOrArray) {
507 // map<>/repeated fields get a *Count property (inplace of a has*) to
508 // support checking if there are any entries without triggering
509 // autocreation.
510 hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO);
511 } else {
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);
522 }
523 }
524
525 // Extra type specific data.
526 if (isMessage) {
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();
537 } else {
538 enumHandling_.enumVerifier_ =
539 coreDesc->dataTypeSpecific.enumVerifier;
540 }
541 }
542
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;
550 if (bytes) {
551 uint32_t length = *((uint32_t *)bytes);
552 length = ntohl(length);
553 bytes += sizeof(length);
554 defaultValue_.valueData =
555 [[NSData alloc] initWithBytes:bytes length:length];
556 }
557 }
558 }
559 }
560 return self;
561}
562
563- (void)dealloc {
564 if (description_->dataType == GPBDataTypeBytes &&
565 !(description_->flags & GPBFieldRepeated)) {
566 [defaultValue_.valueData release];
567 }
568 [super dealloc];
569}
570
571- (GPBDataType)dataType {
572 return description_->dataType;
573}
574
575- (BOOL)hasDefaultValue {
576 return (description_->flags & GPBFieldHasDefaultValue) != 0;
577}
578
579- (uint32_t)number {
580 return description_->number;
581}
582
583- (NSString *)name {
584 return @(description_->name);
585}
586
587- (BOOL)isRequired {
588 return (description_->flags & GPBFieldRequired) != 0;
589}
590
591- (BOOL)isOptional {
592 return (description_->flags & GPBFieldOptional) != 0;
593}
594
595- (GPBFieldType)fieldType {
596 GPBFieldFlags flags = description_->flags;
597 if ((flags & GPBFieldRepeated) != 0) {
598 return GPBFieldTypeRepeated;
599 } else if ((flags & GPBFieldMapKeyMask) != 0) {
600 return GPBFieldTypeMap;
601 } else {
602 return GPBFieldTypeSingle;
603 }
604}
605
606- (GPBDataType)mapKeyDataType {
607 switch (description_->flags & GPBFieldMapKeyMask) {
608 case GPBFieldMapKeyInt32:
609 return GPBDataTypeInt32;
610 case GPBFieldMapKeyInt64:
611 return GPBDataTypeInt64;
612 case GPBFieldMapKeyUInt32:
613 return GPBDataTypeUInt32;
614 case GPBFieldMapKeyUInt64:
615 return GPBDataTypeUInt64;
616 case GPBFieldMapKeySInt32:
617 return GPBDataTypeSInt32;
618 case GPBFieldMapKeySInt64:
619 return GPBDataTypeSInt64;
620 case GPBFieldMapKeyFixed32:
621 return GPBDataTypeFixed32;
622 case GPBFieldMapKeyFixed64:
623 return GPBDataTypeFixed64;
624 case GPBFieldMapKeySFixed32:
625 return GPBDataTypeSFixed32;
626 case GPBFieldMapKeySFixed64:
627 return GPBDataTypeSFixed64;
628 case GPBFieldMapKeyBool:
629 return GPBDataTypeBool;
630 case GPBFieldMapKeyString:
631 return GPBDataTypeString;
632
633 default:
634 NSAssert(0, @"Not a map type");
635 return GPBDataTypeInt32; // For lack of anything better.
636 }
637}
638
639- (BOOL)isPackable {
640 return (description_->flags & GPBFieldPacked) != 0;
641}
642
643- (BOOL)isValidEnumValue:(int32_t)value {
644 NSAssert(description_->dataType == GPBDataTypeEnum,
645 @"Field Must be of type GPBDataTypeEnum");
646 if (description_->flags & GPBFieldHasEnumDescriptor) {
647 return enumHandling_.enumDescriptor_.enumVerifier(value);
648 } else {
649 return enumHandling_.enumVerifier_(value);
650 }
651}
652
653- (GPBEnumDescriptor *)enumDescriptor {
654 if (description_->flags & GPBFieldHasEnumDescriptor) {
655 return enumHandling_.enumDescriptor_;
656 } else {
657 return nil;
658 }
659}
660
661- (GPBGenericValue)defaultValue {
662 // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
663 // to an actual defaultValue in our initializer.
664 GPBGenericValue value = defaultValue_;
665
666 if (!(description_->flags & GPBFieldRepeated)) {
667 // We special handle data and strings. If they are nil, we replace them
668 // with empty string/empty data.
669 GPBDataType type = description_->dataType;
670 if (type == GPBDataTypeBytes && value.valueData == nil) {
671 value.valueData = GPBEmptyNSData();
672 } else if (type == GPBDataTypeString && value.valueString == nil) {
673 value.valueString = @"";
674 }
675 }
676 return value;
677}
678
679- (NSString *)textFormatName {
680 if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
681 NSValue *extraInfoValue =
682 objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
683 // Support can be left out at generation time.
684 if (!extraInfoValue) {
685 return nil;
686 }
687 const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
688 return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self),
689 self.name);
690 }
691
692 // The logic here has to match SetCommonFieldVariables() from
693 // objectivec_field.cc in the proto compiler.
694 NSString *name = self.name;
695 NSUInteger len = [name length];
696
697 // Remove the "_p" added to reserved names.
698 if ([name hasSuffix:@"_p"]) {
699 name = [name substringToIndex:(len - 2)];
700 len = [name length];
701 }
702
703 // Remove "Array" from the end for repeated fields.
704 if (((description_->flags & GPBFieldRepeated) != 0) &&
705 [name hasSuffix:@"Array"]) {
706 name = [name substringToIndex:(len - 5)];
707 len = [name length];
708 }
709
710 // Groups vs. other fields.
711 if (description_->dataType == GPBDataTypeGroup) {
712 // Just capitalize the first letter.
713 unichar firstChar = [name characterAtIndex:0];
714 if (firstChar >= 'a' && firstChar <= 'z') {
715 NSString *firstCharString =
716 [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
717 NSString *result =
718 [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
719 withString:firstCharString];
720 return result;
721 }
722 return name;
723
724 } else {
725 // Undo the CamelCase.
726 NSMutableString *result = [NSMutableString stringWithCapacity:len];
727 for (uint32_t i = 0; i < len; i++) {
728 unichar c = [name characterAtIndex:i];
729 if (c >= 'A' && c <= 'Z') {
730 if (i > 0) {
731 [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
732 } else {
733 [result appendFormat:@"%C", c];
734 }
735 } else {
736 [result appendFormat:@"%C", c];
737 }
738 }
739 return result;
740 }
741}
742
743@end
744
745@implementation GPBEnumDescriptor {
746 NSString *name_;
747 // valueNames_ is a single c string with all of the value names appended
748 // together, each null terminated. -calcValueNameOffsets fills in
749 // nameOffsets_ with the offsets to allow quicker access to the individual
750 // names.
751 const char *valueNames_;
752 const int32_t *values_;
753 GPBEnumValidationFunc enumVerifier_;
754 const uint8_t *extraTextFormatInfo_;
755 uint32_t *nameOffsets_;
756 uint32_t valueCount_;
757}
758
759@synthesize name = name_;
760@synthesize enumVerifier = enumVerifier_;
761
762+ (instancetype)
763 allocDescriptorForName:(NSString *)name
764 valueNames:(const char *)valueNames
765 values:(const int32_t *)values
766 count:(uint32_t)valueCount
767 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
768 GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
769 valueNames:valueNames
770 values:values
771 count:valueCount
772 enumVerifier:enumVerifier];
773 return descriptor;
774}
775
776+ (instancetype)
777 allocDescriptorForName:(NSString *)name
778 valueNames:(const char *)valueNames
779 values:(const int32_t *)values
780 count:(uint32_t)valueCount
781 enumVerifier:(GPBEnumValidationFunc)enumVerifier
782 extraTextFormatInfo:(const char *)extraTextFormatInfo {
783 // Call the common case.
784 GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
785 valueNames:valueNames
786 values:values
787 count:valueCount
788 enumVerifier:enumVerifier];
789 // Set the extra info.
790 descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
791 return descriptor;
792}
793
794- (instancetype)initWithName:(NSString *)name
795 valueNames:(const char *)valueNames
796 values:(const int32_t *)values
797 count:(uint32_t)valueCount
798 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
799 if ((self = [super init])) {
800 name_ = [name copy];
801 valueNames_ = valueNames;
802 values_ = values;
803 valueCount_ = valueCount;
804 enumVerifier_ = enumVerifier;
805 }
806 return self;
807}
808
809- (void)dealloc {
810 [name_ release];
811 if (nameOffsets_) free(nameOffsets_);
812 [super dealloc];
813}
814
815- (void)calcValueNameOffsets {
816 @synchronized(self) {
817 if (nameOffsets_ != NULL) {
818 return;
819 }
820 uint32_t *offsets = malloc(valueCount_ * sizeof(uint32_t));
821 const char *scan = valueNames_;
822 for (uint32_t i = 0; i < valueCount_; ++i) {
823 offsets[i] = (uint32_t)(scan - valueNames_);
824 while (*scan != '\0') ++scan;
825 ++scan; // Step over the null.
826 }
827 nameOffsets_ = offsets;
828 }
829}
830
831- (NSString *)enumNameForValue:(int32_t)number {
832 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
833
834 for (uint32_t i = 0; i < valueCount_; ++i) {
835 if (values_[i] == number) {
836 const char *valueName = valueNames_ + nameOffsets_[i];
837 NSString *fullName = [NSString stringWithFormat:@"%@_%s", name_, valueName];
838 return fullName;
839 }
840 }
841 return nil;
842}
843
844- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
845 // Must have the prefix.
846 NSUInteger prefixLen = name_.length + 1;
847 if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
848 ([name characterAtIndex:prefixLen - 1] != '_')) {
849 return NO;
850 }
851
852 // Skip over the prefix.
853 const char *nameAsCStr = [name UTF8String];
854 nameAsCStr += prefixLen;
855
856 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
857
858 // Find it.
859 for (uint32_t i = 0; i < valueCount_; ++i) {
860 const char *valueName = valueNames_ + nameOffsets_[i];
861 if (strcmp(nameAsCStr, valueName) == 0) {
862 if (outValue) {
863 *outValue = values_[i];
864 }
865 return YES;
866 }
867 }
868 return NO;
869}
870
871- (BOOL)getValue:(int32_t *)outValue forEnumTextFormatName:(NSString *)textFormatName {
872 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
873
874 for (uint32_t i = 0; i < valueCount_; ++i) {
875 int32_t value = values_[i];
876 NSString *valueTextFormatName = [self textFormatNameForValue:value];
877 if ([valueTextFormatName isEqual:textFormatName]) {
878 if (outValue) {
879 *outValue = value;
880 }
881 return YES;
882 }
883 }
884 return NO;
885}
886
887- (NSString *)textFormatNameForValue:(int32_t)number {
888 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
889
890 // Find the EnumValue descriptor and its index.
891 BOOL foundIt = NO;
892 uint32_t valueDescriptorIndex;
893 for (valueDescriptorIndex = 0; valueDescriptorIndex < valueCount_;
894 ++valueDescriptorIndex) {
895 if (values_[valueDescriptorIndex] == number) {
896 foundIt = YES;
897 break;
898 }
899 }
900
901 if (!foundIt) {
902 return nil;
903 }
904
905 NSString *result = nil;
906 // Naming adds an underscore between enum name and value name, skip that also.
907 const char *valueName = valueNames_ + nameOffsets_[valueDescriptorIndex];
908 NSString *shortName = @(valueName);
909
910 // See if it is in the map of special format handling.
911 if (extraTextFormatInfo_) {
912 result = GPBDecodeTextFormatName(extraTextFormatInfo_,
913 (int32_t)valueDescriptorIndex, shortName);
914 }
915 // Logic here needs to match what objectivec_enum.cc does in the proto
916 // compiler.
917 if (result == nil) {
918 NSUInteger len = [shortName length];
919 NSMutableString *worker = [NSMutableString stringWithCapacity:len];
920 for (NSUInteger i = 0; i < len; i++) {
921 unichar c = [shortName characterAtIndex:i];
922 if (i > 0 && c >= 'A' && c <= 'Z') {
923 [worker appendString:@"_"];
924 }
925 [worker appendFormat:@"%c", toupper((char)c)];
926 }
927 result = worker;
928 }
929 return result;
930}
931
932@end
933
934@implementation GPBExtensionDescriptor {
935 GPBGenericValue defaultValue_;
936}
937
938@synthesize containingMessageClass = containingMessageClass_;
939
940- (instancetype)initWithExtensionDescription:
941 (GPBExtensionDescription *)description {
942 if ((self = [super init])) {
943 description_ = description;
944
945#if defined(DEBUG) && DEBUG
946 const char *className = description->messageOrGroupClassName;
947 if (className) {
948 NSAssert(objc_lookUpClass(className) != Nil,
949 @"Class %s not defined", className);
950 }
951#endif
952
953 if (description->extendedClass) {
954 Class containingClass = objc_lookUpClass(description->extendedClass);
955 NSAssert(containingClass, @"Class %s not defined",
956 description->extendedClass);
957 containingMessageClass_ = containingClass;
958 }
959
960 GPBDataType type = description_->dataType;
961 if (type == GPBDataTypeBytes) {
962 // Data stored as a length prefixed c-string in descriptor records.
963 const uint8_t *bytes =
964 (const uint8_t *)description->defaultValue.valueData;
965 if (bytes) {
966 uint32_t length = *((uint32_t *)bytes);
967 // The length is stored in network byte order.
968 length = ntohl(length);
969 bytes += sizeof(length);
970 defaultValue_.valueData =
971 [[NSData alloc] initWithBytes:bytes length:length];
972 }
973 } else if (type == GPBDataTypeMessage || type == GPBDataTypeGroup) {
974 // The default is looked up in -defaultValue instead since extensions
975 // aren't common, we avoid the hit startup hit and it avoid initialization
976 // order issues.
977 } else {
978 defaultValue_ = description->defaultValue;
979 }
980 }
981 return self;
982}
983
984- (void)dealloc {
985 if ((description_->dataType == GPBDataTypeBytes) &&
986 !GPBExtensionIsRepeated(description_)) {
987 [defaultValue_.valueData release];
988 }
989 [super dealloc];
990}
991
992- (instancetype)copyWithZone:(NSZone *)zone {
993#pragma unused(zone)
994 // Immutable.
995 return [self retain];
996}
997
998- (NSString *)singletonName {
999 return @(description_->singletonName);
1000}
1001
1002- (const char *)singletonNameC {
1003 return description_->singletonName;
1004}
1005
1006- (uint32_t)fieldNumber {
1007 return description_->fieldNumber;
1008}
1009
1010- (GPBDataType)dataType {
1011 return description_->dataType;
1012}
1013
1014- (GPBWireFormat)wireType {
1015 return GPBWireFormatForType(description_->dataType,
1016 GPBExtensionIsPacked(description_));
1017}
1018
1019- (GPBWireFormat)alternateWireType {
1020 NSAssert(GPBExtensionIsRepeated(description_),
1021 @"Only valid on repeated extensions");
1022 return GPBWireFormatForType(description_->dataType,
1023 !GPBExtensionIsPacked(description_));
1024}
1025
1026- (BOOL)isRepeated {
1027 return GPBExtensionIsRepeated(description_);
1028}
1029
1030- (BOOL)isMap {
1031 return (description_->options & GPBFieldMapKeyMask) != 0;
1032}
1033
1034- (BOOL)isPackable {
1035 return GPBExtensionIsPacked(description_);
1036}
1037
1038- (Class)msgClass {
1039 return objc_getClass(description_->messageOrGroupClassName);
1040}
1041
1042- (GPBEnumDescriptor *)enumDescriptor {
1043 if (description_->dataType == GPBDataTypeEnum) {
1044 GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
1045 return enumDescriptor;
1046 }
1047 return nil;
1048}
1049
1050- (id)defaultValue {
1051 if (GPBExtensionIsRepeated(description_)) {
1052 return nil;
1053 }
1054
1055 switch (description_->dataType) {
1056 case GPBDataTypeBool:
1057 return @(defaultValue_.valueBool);
1058 case GPBDataTypeFloat:
1059 return @(defaultValue_.valueFloat);
1060 case GPBDataTypeDouble:
1061 return @(defaultValue_.valueDouble);
1062 case GPBDataTypeInt32:
1063 case GPBDataTypeSInt32:
1064 case GPBDataTypeEnum:
1065 case GPBDataTypeSFixed32:
1066 return @(defaultValue_.valueInt32);
1067 case GPBDataTypeInt64:
1068 case GPBDataTypeSInt64:
1069 case GPBDataTypeSFixed64:
1070 return @(defaultValue_.valueInt64);
1071 case GPBDataTypeUInt32:
1072 case GPBDataTypeFixed32:
1073 return @(defaultValue_.valueUInt32);
1074 case GPBDataTypeUInt64:
1075 case GPBDataTypeFixed64:
1076 return @(defaultValue_.valueUInt64);
1077 case GPBDataTypeBytes:
1078 // Like message fields, the default is zero length data.
1079 return (defaultValue_.valueData ? defaultValue_.valueData
1080 : GPBEmptyNSData());
1081 case GPBDataTypeString:
1082 // Like message fields, the default is zero length string.
1083 return (defaultValue_.valueString ? defaultValue_.valueString : @"");
1084 case GPBDataTypeGroup:
1085 case GPBDataTypeMessage:
1086 return nil;
1087 }
1088}
1089
1090- (NSComparisonResult)compareByFieldNumber:(GPBExtensionDescriptor *)other {
1091 int32_t selfNumber = description_->fieldNumber;
1092 int32_t otherNumber = other->description_->fieldNumber;
1093 if (selfNumber < otherNumber) {
1094 return NSOrderedAscending;
1095 } else if (selfNumber == otherNumber) {
1096 return NSOrderedSame;
1097 } else {
1098 return NSOrderedDescending;
1099 }
1100}
1101
1102@end
1103
1104#pragma clang diagnostic pop