1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file or at 6// https://developers.google.com/open-source/licenses/bsd 7 8#import "GPBUnknownFieldSet.h" 9#import "GPBUnknownFieldSet_PackagePrivate.h" 10 11#import "GPBCodedInputStream.h" 12#import "GPBCodedInputStream_PackagePrivate.h" 13#import "GPBCodedOutputStream.h" 14#import "GPBUnknownField.h" 15#import "GPBUnknownField_PackagePrivate.h" 16#import "GPBUtilities.h" 17#import "GPBWireFormat.h" 18 19#pragma clang diagnostic push 20#pragma clang diagnostic ignored "-Wdeprecated-declarations" 21#pragma clang diagnostic ignored "-Wdeprecated-implementations" 22 23#pragma mark Helpers 24 25static void checkNumber(int32_t number) { 26 if (number == 0) { 27 [NSException raise:NSInvalidArgumentException format:@"Zero is not a valid field number."]; 28 } 29} 30 31@implementation GPBUnknownFieldSet { 32 @package 33 CFMutableDictionaryRef fields_; 34} 35 36static void CopyWorker(__unused const void *key, const void *value, void *context) { 37 GPBUnknownField *field = value; 38 GPBUnknownFieldSet *result = context; 39 40 GPBUnknownField *copied = [field copy]; 41 [result addField:copied]; 42 [copied release]; 43} 44 45// Direct access is use for speed, to avoid even internally declaring things 46// read/write, etc. The warning is enabled in the project to ensure code calling 47// protos can turn on -Wdirect-ivar-access without issues. 48#pragma clang diagnostic push 49#pragma clang diagnostic ignored "-Wdirect-ivar-access" 50 51- (id)copyWithZone:(NSZone *)zone { 52 GPBUnknownFieldSet *result = [[GPBUnknownFieldSet allocWithZone:zone] init]; 53 if (fields_) { 54 CFDictionaryApplyFunction(fields_, CopyWorker, result); 55 } 56 return result; 57} 58 59- (void)dealloc { 60 if (fields_) { 61 CFRelease(fields_); 62 } 63 [super dealloc]; 64} 65 66- (BOOL)isEqual:(id)object { 67 BOOL equal = NO; 68 if ([object isKindOfClass:[GPBUnknownFieldSet class]]) { 69 GPBUnknownFieldSet *set = (GPBUnknownFieldSet *)object; 70 if ((fields_ == NULL) && (set->fields_ == NULL)) { 71 equal = YES; 72 } else if ((fields_ != NULL) && (set->fields_ != NULL)) { 73 equal = CFEqual(fields_, set->fields_); 74 } 75 } 76 return equal; 77} 78 79- (NSUInteger)hash { 80 // Return the hash of the fields dictionary (or just some value). 81 if (fields_) { 82 return CFHash(fields_); 83 } 84 return (NSUInteger)[GPBUnknownFieldSet class]; 85} 86 87#pragma mark - Public Methods 88 89- (BOOL)hasField:(int32_t)number { 90 ssize_t key = number; 91 return fields_ ? (CFDictionaryGetValue(fields_, (void *)key) != nil) : NO; 92} 93 94- (GPBUnknownField *)getField:(int32_t)number { 95 ssize_t key = number; 96 GPBUnknownField *result = fields_ ? CFDictionaryGetValue(fields_, (void *)key) : nil; 97 return result; 98} 99 100- (NSUInteger)countOfFields { 101 return fields_ ? CFDictionaryGetCount(fields_) : 0; 102} 103 104- (NSArray *)sortedFields { 105 if (!fields_) return [NSArray array]; 106 size_t count = CFDictionaryGetCount(fields_); 107 ssize_t keys[count]; 108 GPBUnknownField *values[count]; 109 CFDictionaryGetKeysAndValues(fields_, (const void **)keys, (const void **)values); 110 struct GPBFieldPair { 111 ssize_t key; 112 GPBUnknownField *value; 113 } pairs[count]; 114 for (size_t i = 0; i < count; ++i) { 115 pairs[i].key = keys[i]; 116 pairs[i].value = values[i]; 117 }; 118 qsort_b(pairs, count, sizeof(struct GPBFieldPair), ^(const void *first, const void *second) { 119 const struct GPBFieldPair *a = first; 120 const struct GPBFieldPair *b = second; 121 return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1); 122 }); 123 for (size_t i = 0; i < count; ++i) { 124 values[i] = pairs[i].value; 125 }; 126 return [NSArray arrayWithObjects:values count:count]; 127} 128 129#pragma mark - Internal Methods 130 131- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output { 132 if (!fields_) return; 133 size_t count = CFDictionaryGetCount(fields_); 134 ssize_t keys[count]; 135 GPBUnknownField *values[count]; 136 CFDictionaryGetKeysAndValues(fields_, (const void **)keys, (const void **)values); 137 if (count > 1) { 138 struct GPBFieldPair { 139 ssize_t key; 140 GPBUnknownField *value; 141 } pairs[count]; 142 143 for (size_t i = 0; i < count; ++i) { 144 pairs[i].key = keys[i]; 145 pairs[i].value = values[i]; 146 }; 147 qsort_b(pairs, count, sizeof(struct GPBFieldPair), ^(const void *first, const void *second) { 148 const struct GPBFieldPair *a = first; 149 const struct GPBFieldPair *b = second; 150 return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1); 151 }); 152 for (size_t i = 0; i < count; ++i) { 153 GPBUnknownField *value = pairs[i].value; 154 [value writeToOutput:output]; 155 } 156 } else { 157 [values[0] writeToOutput:output]; 158 } 159} 160 161- (NSString *)description { 162 NSMutableString *description = 163 [NSMutableString stringWithFormat:@"<%@ %p>: TextFormat: {\n", [self class], self]; 164 NSString *textFormat = GPBTextFormatForUnknownFieldSet(self, @" "); 165 [description appendString:textFormat]; 166 [description appendString:@"}"]; 167 return description; 168} 169 170static void GPBUnknownFieldSetSerializedSize(__unused const void *key, const void *value, 171 void *context) { 172 GPBUnknownField *field = value; 173 size_t *result = context; 174 *result += [field serializedSize]; 175} 176 177- (size_t)serializedSize { 178 size_t result = 0; 179 if (fields_) { 180 CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetSerializedSize, &result); 181 } 182 return result; 183} 184 185static void GPBUnknownFieldSetWriteAsMessageSetTo(__unused const void *key, const void *value, 186 void *context) { 187 GPBUnknownField *field = value; 188 GPBCodedOutputStream *output = context; 189 [field writeAsMessageSetExtensionToOutput:output]; 190} 191 192- (void)writeAsMessageSetTo:(GPBCodedOutputStream *)output { 193 if (fields_) { 194 CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetWriteAsMessageSetTo, output); 195 } 196} 197 198static void GPBUnknownFieldSetSerializedSizeAsMessageSet(__unused const void *key, 199 const void *value, void *context) { 200 GPBUnknownField *field = value; 201 size_t *result = context; 202 *result += [field serializedSizeAsMessageSetExtension]; 203} 204 205- (size_t)serializedSizeAsMessageSet { 206 size_t result = 0; 207 if (fields_) { 208 CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetSerializedSizeAsMessageSet, &result); 209 } 210 return result; 211} 212 213- (NSData *)data { 214 NSMutableData *data = [NSMutableData dataWithLength:self.serializedSize]; 215 GPBCodedOutputStream *output = [[GPBCodedOutputStream alloc] initWithData:data]; 216 [self writeToCodedOutputStream:output]; 217 [output flush]; 218 [output release]; 219 return data; 220} 221 222- (void)addField:(GPBUnknownField *)field { 223 int32_t number = [field number]; 224 checkNumber(number); 225 if (!fields_) { 226 // Use a custom dictionary here because the keys are numbers and conversion 227 // back and forth from NSNumber isn't worth the cost. 228 fields_ = 229 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); 230 } 231 ssize_t key = number; 232 CFDictionarySetValue(fields_, (const void *)key, field); 233} 234 235- (GPBUnknownField *)mutableFieldForNumber:(int32_t)number create:(BOOL)create { 236 ssize_t key = number; 237 GPBUnknownField *existing = fields_ ? CFDictionaryGetValue(fields_, (const void *)key) : nil; 238 if (!existing && create) { 239 existing = [[GPBUnknownField alloc] initWithNumber:number]; 240 // This retains existing. 241 [self addField:existing]; 242 [existing release]; 243 } 244 return existing; 245} 246 247static void GPBUnknownFieldSetMergeUnknownFields(__unused const void *key, const void *value, 248 void *context) { 249 GPBUnknownField *field = value; 250 GPBUnknownFieldSet *self = context; 251 252 int32_t number = [field number]; 253 checkNumber(number); 254 GPBUnknownField *oldField = [self mutableFieldForNumber:number create:NO]; 255 if (oldField) { 256 [oldField mergeFromField:field]; 257 } else { 258 // Merge only comes from GPBMessage's mergeFrom:, so it means we are on 259 // mutable message and are an mutable instance, so make sure we need 260 // mutable fields. 261 GPBUnknownField *fieldCopy = [field copy]; 262 [self addField:fieldCopy]; 263 [fieldCopy release]; 264 } 265} 266 267- (void)mergeUnknownFields:(GPBUnknownFieldSet *)other { 268 if (other && other->fields_) { 269 CFDictionaryApplyFunction(other->fields_, GPBUnknownFieldSetMergeUnknownFields, self); 270 } 271} 272 273- (void)mergeVarintField:(int32_t)number value:(int32_t)value { 274 checkNumber(number); 275 [[self mutableFieldForNumber:number create:YES] addVarint:value]; 276} 277 278- (void)mergeLengthDelimited:(int32_t)fieldNum value:(NSData *)value { 279 checkNumber(fieldNum); 280 [[self mutableFieldForNumber:fieldNum create:YES] addLengthDelimited:value]; 281} 282 283- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input { 284 NSAssert(GPBWireFormatIsValidTag(tag), @"Got passed an invalid tag"); 285 int32_t number = GPBWireFormatGetTagFieldNumber(tag); 286 GPBCodedInputStreamState *state = &input->state_; 287 switch (GPBWireFormatGetTagWireType(tag)) { 288 case GPBWireFormatVarint: { 289 GPBUnknownField *field = [self mutableFieldForNumber:number create:YES]; 290 [field addVarint:GPBCodedInputStreamReadInt64(state)]; 291 return YES; 292 } 293 case GPBWireFormatFixed64: { 294 GPBUnknownField *field = [self mutableFieldForNumber:number create:YES]; 295 [field addFixed64:GPBCodedInputStreamReadFixed64(state)]; 296 return YES; 297 } 298 case GPBWireFormatLengthDelimited: { 299 NSData *data = GPBCodedInputStreamReadRetainedBytes(state); 300 GPBUnknownField *field = [self mutableFieldForNumber:number create:YES]; 301 [field addLengthDelimited:data]; 302 [data release]; 303 return YES; 304 } 305 case GPBWireFormatStartGroup: { 306 GPBUnknownFieldSet *unknownFieldSet = [[GPBUnknownFieldSet alloc] init]; 307 GPBUnknownField *field = [self mutableFieldForNumber:number create:YES]; 308 [field addGroup:unknownFieldSet]; 309 // The field will now retain unknownFieldSet, so go ahead and release it in case 310 // -readUnknownGroup:message: throws so it won't be leaked. 311 [unknownFieldSet release]; 312 [input readUnknownGroup:number message:unknownFieldSet]; 313 return YES; 314 } 315 case GPBWireFormatEndGroup: 316 return NO; 317 case GPBWireFormatFixed32: { 318 GPBUnknownField *field = [self mutableFieldForNumber:number create:YES]; 319 [field addFixed32:GPBCodedInputStreamReadFixed32(state)]; 320 return YES; 321 } 322 } 323} 324 325- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input { 326 while (YES) { 327 int32_t tag = GPBCodedInputStreamReadTag(&input->state_); 328 if (tag == 0 || ![self mergeFieldFrom:tag input:input]) { 329 break; 330 } 331 } 332} 333 334- (void)getTags:(int32_t *)tags { 335 if (!fields_) return; 336 size_t count = CFDictionaryGetCount(fields_); 337 ssize_t keys[count]; 338 CFDictionaryGetKeysAndValues(fields_, (const void **)keys, NULL); 339 for (size_t i = 0; i < count; ++i) { 340 tags[i] = (int32_t)keys[i]; 341 } 342} 343 344#pragma clang diagnostic pop 345 346@end 347 348#pragma clang diagnostic pop // -Wdeprecated-declarations suppression 349