1// Protocol Buffers - Google's data interchange format 2// Copyright 2024 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 "GPBUnknownFields.h" 9#import "GPBUnknownFields_PackagePrivate.h" 10 11#import <Foundation/Foundation.h> 12 13#import "GPBCodedInputStream.h" 14#import "GPBCodedInputStream_PackagePrivate.h" 15#import "GPBCodedOutputStream.h" 16#import "GPBCodedOutputStream_PackagePrivate.h" 17#import "GPBDescriptor.h" 18#import "GPBMessage.h" 19#import "GPBMessage_PackagePrivate.h" 20#import "GPBUnknownField.h" 21#import "GPBUnknownFieldSet.h" 22#import "GPBUnknownFieldSet_PackagePrivate.h" 23#import "GPBUnknownField_PackagePrivate.h" 24#import "GPBWireFormat.h" 25 26#define CHECK_FIELD_NUMBER(number) \ 27 if (number <= 0) { \ 28 [NSException raise:NSInvalidArgumentException format:@"Not a valid field number."]; \ 29 } 30 31// TODO: Consider using on other functions to reduce bloat when 32// some compiler optimizations are enabled. 33#define GPB_NOINLINE __attribute__((noinline)) 34 35@interface GPBUnknownFields () { 36 @package 37 NSMutableArray<GPBUnknownField *> *fields_; 38} 39@end 40 41// Direct access is use for speed, to avoid even internally declaring things 42// read/write, etc. The warning is enabled in the project to ensure code calling 43// protos can turn on -Wdirect-ivar-access without issues. 44#pragma clang diagnostic push 45#pragma clang diagnostic ignored "-Wdirect-ivar-access" 46 47GPB_NOINLINE 48static size_t ComputeSerializeSize(GPBUnknownFields *_Nonnull self) { 49 size_t result = 0; 50 for (GPBUnknownField *field in self->fields_) { 51 uint32_t fieldNumber = field->number_; 52 switch (field->type_) { 53 case GPBUnknownFieldTypeVarint: 54 result += GPBComputeUInt64Size(fieldNumber, field->storage_.intValue); 55 break; 56 case GPBUnknownFieldTypeFixed32: 57 result += GPBComputeFixed32Size(fieldNumber, (uint32_t)field->storage_.intValue); 58 break; 59 case GPBUnknownFieldTypeFixed64: 60 result += GPBComputeFixed64Size(fieldNumber, field->storage_.intValue); 61 break; 62 case GPBUnknownFieldTypeLengthDelimited: 63 result += GPBComputeBytesSize(fieldNumber, field->storage_.lengthDelimited); 64 break; 65 case GPBUnknownFieldTypeGroup: 66 result += 67 (GPBComputeTagSize(fieldNumber) * 2) + ComputeSerializeSize(field->storage_.group); 68 break; 69 case GPBUnknownFieldTypeLegacy: 70#if defined(DEBUG) && DEBUG 71 NSCAssert(NO, @"Internal error within the library"); 72#endif 73 break; 74 } 75 } 76 return result; 77} 78 79GPB_NOINLINE 80static void WriteToCoddedOutputStream(GPBUnknownFields *_Nonnull self, 81 GPBCodedOutputStream *_Nonnull output) { 82 for (GPBUnknownField *field in self->fields_) { 83 uint32_t fieldNumber = field->number_; 84 switch (field->type_) { 85 case GPBUnknownFieldTypeVarint: 86 [output writeUInt64:fieldNumber value:field->storage_.intValue]; 87 break; 88 case GPBUnknownFieldTypeFixed32: 89 [output writeFixed32:fieldNumber value:(uint32_t)field->storage_.intValue]; 90 break; 91 case GPBUnknownFieldTypeFixed64: 92 [output writeFixed64:fieldNumber value:field->storage_.intValue]; 93 break; 94 case GPBUnknownFieldTypeLengthDelimited: 95 [output writeBytes:fieldNumber value:field->storage_.lengthDelimited]; 96 break; 97 case GPBUnknownFieldTypeGroup: 98 [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatStartGroup)]; 99 WriteToCoddedOutputStream(field->storage_.group, output); 100 [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)]; 101 break; 102 case GPBUnknownFieldTypeLegacy: 103#if defined(DEBUG) && DEBUG 104 NSCAssert(NO, @"Internal error within the library"); 105#endif 106 break; 107 } 108 } 109} 110 111GPB_NOINLINE 112static BOOL MergeFromInputStream(GPBUnknownFields *self, GPBCodedInputStream *input, 113 uint32_t endTag) { 114#if defined(DEBUG) && DEBUG 115 NSCAssert(endTag == 0 || GPBWireFormatGetTagWireType(endTag) == GPBWireFormatEndGroup, 116 @"Internal error:Invalid end tag: %u", endTag); 117#endif 118 GPBCodedInputStreamState *state = &input->state_; 119 NSMutableArray<GPBUnknownField *> *fields = self->fields_; 120 @try { 121 while (YES) { 122 uint32_t tag = GPBCodedInputStreamReadTag(state); 123 if (tag == endTag) { 124 return YES; 125 } 126 if (tag == 0) { 127 // Reached end of input without finding the end tag. 128 return NO; 129 } 130 GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag); 131 int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag); 132 switch (wireType) { 133 case GPBWireFormatVarint: { 134 uint64_t value = GPBCodedInputStreamReadInt64(state); 135 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber 136 varint:value]; 137 [fields addObject:field]; 138 [field release]; 139 break; 140 } 141 case GPBWireFormatFixed32: { 142 uint32_t value = GPBCodedInputStreamReadFixed32(state); 143 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber 144 fixed32:value]; 145 [fields addObject:field]; 146 [field release]; 147 break; 148 } 149 case GPBWireFormatFixed64: { 150 uint64_t value = GPBCodedInputStreamReadFixed64(state); 151 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber 152 fixed64:value]; 153 [fields addObject:field]; 154 [field release]; 155 break; 156 } 157 case GPBWireFormatLengthDelimited: { 158 NSData *data = GPBCodedInputStreamReadRetainedBytes(state); 159 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber 160 lengthDelimited:data]; 161 [fields addObject:field]; 162 [field release]; 163 [data release]; 164 break; 165 } 166 case GPBWireFormatStartGroup: { 167 GPBUnknownFields *group = [[GPBUnknownFields alloc] init]; 168 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group]; 169 [fields addObject:field]; 170 [field release]; 171 [group release]; // Still will be held in the field/fields. 172 uint32_t endGroupTag = GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup); 173 if (MergeFromInputStream(group, input, endGroupTag)) { 174 GPBCodedInputStreamCheckLastTagWas(state, endGroupTag); 175 } else { 176 [NSException 177 raise:NSInternalInconsistencyException 178 format:@"Internal error: Unknown field data for nested group was malformed."]; 179 } 180 break; 181 } 182 case GPBWireFormatEndGroup: 183 [NSException raise:NSInternalInconsistencyException 184 format:@"Unexpected end group tag: %u", tag]; 185 break; 186 } 187 } 188 } @catch (NSException *exception) { 189#if defined(DEBUG) && DEBUG 190 NSLog(@"%@: Internal exception while parsing unknown data, this shouldn't happen!: %@", 191 [self class], exception); 192#endif 193 } 194} 195 196@implementation GPBUnknownFields 197 198- (instancetype)initFromMessage:(nonnull GPBMessage *)message { 199 self = [super init]; 200 if (self) { 201 fields_ = [[NSMutableArray alloc] init]; 202 // This shouldn't happen with the annotations, but just incase something claiming nonnull 203 // does return nil, block it. 204 if (!message) { 205 [self release]; 206 [NSException raise:NSInvalidArgumentException format:@"Message cannot be nil"]; 207 } 208 NSData *data = GPBMessageUnknownFieldsData(message); 209 if (data) { 210 GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data]; 211 // Parse until the end of the data (tag will be zero). 212 if (!MergeFromInputStream(self, input, 0)) { 213 [input release]; 214 [self release]; 215 [NSException raise:NSInternalInconsistencyException 216 format:@"Internal error: Unknown field data from message was malformed."]; 217 } 218 [input release]; 219 } 220 } 221 return self; 222} 223 224- (instancetype)init { 225 self = [super init]; 226 if (self) { 227 fields_ = [[NSMutableArray alloc] init]; 228 } 229 return self; 230} 231 232- (id)copyWithZone:(NSZone *)zone { 233 GPBUnknownFields *copy = [[GPBUnknownFields allocWithZone:zone] init]; 234 copy->fields_ = [[NSMutableArray allocWithZone:zone] initWithArray:fields_ copyItems:YES]; 235 return copy; 236} 237 238- (void)dealloc { 239 [fields_ release]; 240 [super dealloc]; 241} 242 243- (BOOL)isEqual:(id)object { 244 if (![object isKindOfClass:[GPBUnknownFields class]]) { 245 return NO; 246 } 247 GPBUnknownFields *ufs = (GPBUnknownFields *)object; 248 // The type is defined with order of fields mattering, so just compare the arrays. 249 return [fields_ isEqual:ufs->fields_]; 250} 251 252- (NSUInteger)hash { 253 return [fields_ hash]; 254} 255 256- (NSString *)description { 257 return [NSString 258 stringWithFormat:@"<%@ %p>: %lu fields", [self class], self, (unsigned long)fields_.count]; 259} 260 261#pragma mark - Public Methods 262 263- (NSUInteger)count { 264 return fields_.count; 265} 266 267- (BOOL)empty { 268 return fields_.count == 0; 269} 270 271- (void)clear { 272 [fields_ removeAllObjects]; 273} 274 275- (NSArray<GPBUnknownField *> *)fields:(int32_t)fieldNumber { 276 CHECK_FIELD_NUMBER(fieldNumber); 277 NSMutableArray<GPBUnknownField *> *result = [[NSMutableArray alloc] init]; 278 for (GPBUnknownField *field in fields_) { 279 if (field.number == fieldNumber) { 280 [result addObject:field]; 281 } 282 } 283 if (result.count == 0) { 284 [result release]; 285 return nil; 286 } 287 return [result autorelease]; 288} 289 290- (void)addFieldNumber:(int32_t)fieldNumber varint:(uint64_t)value { 291 CHECK_FIELD_NUMBER(fieldNumber); 292 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber varint:value]; 293 [fields_ addObject:field]; 294 [field release]; 295} 296 297- (void)addFieldNumber:(int32_t)fieldNumber fixed32:(uint32_t)value { 298 CHECK_FIELD_NUMBER(fieldNumber); 299 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber fixed32:value]; 300 [fields_ addObject:field]; 301 [field release]; 302} 303 304- (void)addFieldNumber:(int32_t)fieldNumber fixed64:(uint64_t)value { 305 CHECK_FIELD_NUMBER(fieldNumber); 306 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber fixed64:value]; 307 [fields_ addObject:field]; 308 [field release]; 309} 310 311- (void)addFieldNumber:(int32_t)fieldNumber lengthDelimited:(NSData *)value { 312 CHECK_FIELD_NUMBER(fieldNumber); 313 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber 314 lengthDelimited:value]; 315 [fields_ addObject:field]; 316 [field release]; 317} 318 319- (GPBUnknownFields *)addGroupWithFieldNumber:(int32_t)fieldNumber { 320 CHECK_FIELD_NUMBER(fieldNumber); 321 GPBUnknownFields *group = [[GPBUnknownFields alloc] init]; 322 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group]; 323 [fields_ addObject:field]; 324 [field release]; 325 return [group autorelease]; 326} 327 328- (GPBUnknownField *)addCopyOfField:(nonnull GPBUnknownField *)field { 329 if (field->type_ == GPBUnknownFieldTypeLegacy) { 330 [NSException raise:NSInternalInconsistencyException 331 format:@"GPBUnknownField is the wrong type"]; 332 } 333 GPBUnknownField *result = [field copy]; 334 [fields_ addObject:result]; 335 return [result autorelease]; 336} 337 338- (void)removeField:(nonnull GPBUnknownField *)field { 339 NSUInteger count = fields_.count; 340 [fields_ removeObjectIdenticalTo:field]; 341 if (count == fields_.count) { 342 [NSException raise:NSInvalidArgumentException format:@"The field was not present."]; 343 } 344} 345 346- (void)clearFieldNumber:(int32_t)fieldNumber { 347 CHECK_FIELD_NUMBER(fieldNumber); 348 NSMutableIndexSet *toRemove = nil; 349 NSUInteger idx = 0; 350 for (GPBUnknownField *field in fields_) { 351 if (field->number_ == fieldNumber) { 352 if (toRemove == nil) { 353 toRemove = [[NSMutableIndexSet alloc] initWithIndex:idx]; 354 } else { 355 [toRemove addIndex:idx]; 356 } 357 } 358 ++idx; 359 } 360 if (toRemove) { 361 [fields_ removeObjectsAtIndexes:toRemove]; 362 [toRemove release]; 363 } 364} 365 366#pragma mark - NSFastEnumeration protocol 367 368- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state 369 objects:(__unsafe_unretained id _Nonnull *)stackbuf 370 count:(NSUInteger)len { 371 return [fields_ countByEnumeratingWithState:state objects:stackbuf count:len]; 372} 373 374#pragma mark - Internal Methods 375 376- (NSData *)serializeAsData { 377 if (fields_.count == 0) { 378 return [NSData data]; 379 } 380 size_t expectedSize = ComputeSerializeSize(self); 381 NSMutableData *data = [NSMutableData dataWithLength:expectedSize]; 382 GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data]; 383 @try { 384 WriteToCoddedOutputStream(self, stream); 385 [stream flush]; 386 } @catch (NSException *exception) { 387#if defined(DEBUG) && DEBUG 388 NSLog(@"Internal exception while building GPBUnknownFields serialized data: %@", exception); 389#endif 390 } 391#if defined(DEBUG) && DEBUG 392 NSAssert([stream bytesWritten] == expectedSize, @"Internal error within the library"); 393#endif 394 [stream release]; 395 return data; 396} 397 398@end 399 400@implementation GPBUnknownFields (AccessHelpers) 401 402- (BOOL)getFirst:(int32_t)fieldNumber varint:(nonnull uint64_t *)outValue { 403 CHECK_FIELD_NUMBER(fieldNumber); 404 for (GPBUnknownField *field in fields_) { 405 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeVarint) { 406 *outValue = field.varint; 407 return YES; 408 } 409 } 410 return NO; 411} 412 413- (BOOL)getFirst:(int32_t)fieldNumber fixed32:(nonnull uint32_t *)outValue { 414 CHECK_FIELD_NUMBER(fieldNumber); 415 for (GPBUnknownField *field in fields_) { 416 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeFixed32) { 417 *outValue = field.fixed32; 418 return YES; 419 } 420 } 421 return NO; 422} 423 424- (BOOL)getFirst:(int32_t)fieldNumber fixed64:(nonnull uint64_t *)outValue { 425 CHECK_FIELD_NUMBER(fieldNumber); 426 for (GPBUnknownField *field in fields_) { 427 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeFixed64) { 428 *outValue = field.fixed64; 429 return YES; 430 } 431 } 432 return NO; 433} 434 435- (nullable NSData *)firstLengthDelimited:(int32_t)fieldNumber { 436 CHECK_FIELD_NUMBER(fieldNumber); 437 for (GPBUnknownField *field in fields_) { 438 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeLengthDelimited) { 439 return field.lengthDelimited; 440 } 441 } 442 return nil; 443} 444 445- (nullable GPBUnknownFields *)firstGroup:(int32_t)fieldNumber { 446 CHECK_FIELD_NUMBER(fieldNumber); 447 for (GPBUnknownField *field in fields_) { 448 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeGroup) { 449 return field.group; 450 } 451 } 452 return nil; 453} 454 455@end 456 457#pragma clang diagnostic pop 458