1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2024 Google Inc. All rights reserved.
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
8 #import "GPBUnknownFields.h"
9 #import "GPBUnknownFields_PackagePrivate.h"
11 #import <Foundation/Foundation.h>
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 "GPBUnknownField_PackagePrivate.h"
22 #import "GPBWireFormat.h"
24 #define CHECK_FIELD_NUMBER(number) \
26 [NSException raise:NSInvalidArgumentException format:@"Not a valid field number."]; \
29 // TODO: Consider using on other functions to reduce bloat when
30 // some compiler optimizations are enabled.
31 #define GPB_NOINLINE __attribute__((noinline))
33 @interface GPBUnknownFields () {
35 NSMutableArray<GPBUnknownField *> *fields_;
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"
46 static size_t ComputeSerializeSize(GPBUnknownFields *_Nonnull self) {
48 for (GPBUnknownField *field in self->fields_) {
49 uint32_t fieldNumber = field->number_;
50 switch (field->type_) {
51 case GPBUnknownFieldTypeVarint:
52 result += GPBComputeUInt64Size(fieldNumber, field->storage_.intValue);
54 case GPBUnknownFieldTypeFixed32:
55 result += GPBComputeFixed32Size(fieldNumber, (uint32_t)field->storage_.intValue);
57 case GPBUnknownFieldTypeFixed64:
58 result += GPBComputeFixed64Size(fieldNumber, field->storage_.intValue);
60 case GPBUnknownFieldTypeLengthDelimited:
61 result += GPBComputeBytesSize(fieldNumber, field->storage_.lengthDelimited);
63 case GPBUnknownFieldTypeGroup:
65 (GPBComputeTagSize(fieldNumber) * 2) + ComputeSerializeSize(field->storage_.group);
73 static void WriteToCoddedOutputStream(GPBUnknownFields *_Nonnull self,
74 GPBCodedOutputStream *_Nonnull output) {
75 for (GPBUnknownField *field in self->fields_) {
76 uint32_t fieldNumber = field->number_;
77 switch (field->type_) {
78 case GPBUnknownFieldTypeVarint:
79 [output writeUInt64:fieldNumber value:field->storage_.intValue];
81 case GPBUnknownFieldTypeFixed32:
82 [output writeFixed32:fieldNumber value:(uint32_t)field->storage_.intValue];
84 case GPBUnknownFieldTypeFixed64:
85 [output writeFixed64:fieldNumber value:field->storage_.intValue];
87 case GPBUnknownFieldTypeLengthDelimited:
88 [output writeBytes:fieldNumber value:field->storage_.lengthDelimited];
90 case GPBUnknownFieldTypeGroup:
91 [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatStartGroup)];
92 WriteToCoddedOutputStream(field->storage_.group, output);
93 [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)];
100 static BOOL MergeFromInputStream(GPBUnknownFields *self, GPBCodedInputStream *input,
102 #if defined(DEBUG) && DEBUG
103 NSCAssert(endTag == 0 || GPBWireFormatGetTagWireType(endTag) == GPBWireFormatEndGroup,
104 @"Internal error:Invalid end tag: %u", endTag);
106 GPBCodedInputStreamState *state = &input->state_;
107 NSMutableArray<GPBUnknownField *> *fields = self->fields_;
110 uint32_t tag = GPBCodedInputStreamReadTag(state);
115 // Reached end of input without finding the end tag.
118 GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
119 int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
121 case GPBWireFormatVarint: {
122 uint64_t value = GPBCodedInputStreamReadInt64(state);
123 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
125 [fields addObject:field];
129 case GPBWireFormatFixed32: {
130 uint32_t value = GPBCodedInputStreamReadFixed32(state);
131 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
133 [fields addObject:field];
137 case GPBWireFormatFixed64: {
138 uint64_t value = GPBCodedInputStreamReadFixed64(state);
139 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
141 [fields addObject:field];
145 case GPBWireFormatLengthDelimited: {
146 NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
147 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
148 lengthDelimited:data];
149 [fields addObject:field];
154 case GPBWireFormatStartGroup: {
155 GPBUnknownFields *group = [[GPBUnknownFields alloc] init];
156 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group];
157 [fields addObject:field];
159 [group release]; // Still will be held in the field/fields.
160 uint32_t endGroupTag = GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup);
161 if (MergeFromInputStream(group, input, endGroupTag)) {
162 GPBCodedInputStreamCheckLastTagWas(state, endGroupTag);
165 raise:NSInternalInconsistencyException
166 format:@"Internal error: Unknown field data for nested group was malformed."];
170 case GPBWireFormatEndGroup:
171 [NSException raise:NSInternalInconsistencyException
172 format:@"Unexpected end group tag: %u", tag];
176 } @catch (NSException *exception) {
177 #if defined(DEBUG) && DEBUG
178 NSLog(@"%@: Internal exception while parsing unknown data, this shouldn't happen!: %@",
179 [self class], exception);
184 @implementation GPBUnknownFields
186 - (instancetype)initFromMessage:(nonnull GPBMessage *)message {
189 fields_ = [[NSMutableArray alloc] init];
190 // This shouldn't happen with the annotations, but just incase something claiming nonnull
191 // does return nil, block it.
194 [NSException raise:NSInvalidArgumentException format:@"Message cannot be nil"];
196 NSData *data = GPBMessageUnknownFieldsData(message);
198 GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
199 // Parse until the end of the data (tag will be zero).
200 if (!MergeFromInputStream(self, input, 0)) {
203 [NSException raise:NSInternalInconsistencyException
204 format:@"Internal error: Unknown field data from message was malformed."];
212 - (instancetype)init {
215 fields_ = [[NSMutableArray alloc] init];
220 - (id)copyWithZone:(NSZone *)zone {
221 GPBUnknownFields *copy = [[GPBUnknownFields allocWithZone:zone] init];
222 copy->fields_ = [[NSMutableArray allocWithZone:zone] initWithArray:fields_ copyItems:YES];
231 - (BOOL)isEqual:(id)object {
232 if (![object isKindOfClass:[GPBUnknownFields class]]) {
235 GPBUnknownFields *ufs = (GPBUnknownFields *)object;
236 // The type is defined with order of fields mattering, so just compare the arrays.
237 return [fields_ isEqual:ufs->fields_];
241 return [fields_ hash];
244 - (NSString *)description {
246 stringWithFormat:@"<%@ %p>: %lu fields", [self class], self, (unsigned long)fields_.count];
249 #pragma mark - Public Methods
251 - (NSUInteger)count {
252 return fields_.count;
256 return fields_.count == 0;
260 [fields_ removeAllObjects];
263 - (NSArray<GPBUnknownField *> *)fields:(int32_t)fieldNumber {
264 CHECK_FIELD_NUMBER(fieldNumber);
265 NSMutableArray<GPBUnknownField *> *result = [[NSMutableArray alloc] init];
266 for (GPBUnknownField *field in fields_) {
267 if (field.number == fieldNumber) {
268 [result addObject:field];
271 if (result.count == 0) {
275 return [result autorelease];
278 - (void)addFieldNumber:(int32_t)fieldNumber varint:(uint64_t)value {
279 CHECK_FIELD_NUMBER(fieldNumber);
280 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber varint:value];
281 [fields_ addObject:field];
285 - (void)addFieldNumber:(int32_t)fieldNumber fixed32:(uint32_t)value {
286 CHECK_FIELD_NUMBER(fieldNumber);
287 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber fixed32:value];
288 [fields_ addObject:field];
292 - (void)addFieldNumber:(int32_t)fieldNumber fixed64:(uint64_t)value {
293 CHECK_FIELD_NUMBER(fieldNumber);
294 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber fixed64:value];
295 [fields_ addObject:field];
299 - (void)addFieldNumber:(int32_t)fieldNumber lengthDelimited:(NSData *)value {
300 CHECK_FIELD_NUMBER(fieldNumber);
301 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
302 lengthDelimited:value];
303 [fields_ addObject:field];
307 - (GPBUnknownFields *)addGroupWithFieldNumber:(int32_t)fieldNumber {
308 CHECK_FIELD_NUMBER(fieldNumber);
309 GPBUnknownFields *group = [[GPBUnknownFields alloc] init];
310 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group];
311 [fields_ addObject:field];
313 return [group autorelease];
316 - (GPBUnknownField *)addCopyOfField:(nonnull GPBUnknownField *)field {
317 GPBUnknownField *result = [field copy];
318 [fields_ addObject:result];
319 return [result autorelease];
322 - (void)removeField:(nonnull GPBUnknownField *)field {
323 NSUInteger count = fields_.count;
324 [fields_ removeObjectIdenticalTo:field];
325 if (count == fields_.count) {
326 [NSException raise:NSInvalidArgumentException format:@"The field was not present."];
330 - (void)clearFieldNumber:(int32_t)fieldNumber {
331 CHECK_FIELD_NUMBER(fieldNumber);
332 NSMutableIndexSet *toRemove = nil;
334 for (GPBUnknownField *field in fields_) {
335 if (field->number_ == fieldNumber) {
336 if (toRemove == nil) {
337 toRemove = [[NSMutableIndexSet alloc] initWithIndex:idx];
339 [toRemove addIndex:idx];
345 [fields_ removeObjectsAtIndexes:toRemove];
350 #pragma mark - NSFastEnumeration protocol
352 - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
353 objects:(__unsafe_unretained id _Nonnull *)stackbuf
354 count:(NSUInteger)len {
355 return [fields_ countByEnumeratingWithState:state objects:stackbuf count:len];
358 #pragma mark - Internal Methods
360 - (NSData *)serializeAsData {
361 if (fields_.count == 0) {
362 return [NSData data];
364 size_t expectedSize = ComputeSerializeSize(self);
365 NSMutableData *data = [NSMutableData dataWithLength:expectedSize];
366 GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data];
368 WriteToCoddedOutputStream(self, stream);
370 } @catch (NSException *exception) {
371 #if defined(DEBUG) && DEBUG
372 NSLog(@"Internal exception while building GPBUnknownFields serialized data: %@", exception);
375 #if defined(DEBUG) && DEBUG
376 NSAssert([stream bytesWritten] == expectedSize, @"Internal error within the library");
384 @implementation GPBUnknownFields (AccessHelpers)
386 - (BOOL)getFirst:(int32_t)fieldNumber varint:(nonnull uint64_t *)outValue {
387 CHECK_FIELD_NUMBER(fieldNumber);
388 for (GPBUnknownField *field in fields_) {
389 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeVarint) {
390 *outValue = field.varint;
397 - (BOOL)getFirst:(int32_t)fieldNumber fixed32:(nonnull uint32_t *)outValue {
398 CHECK_FIELD_NUMBER(fieldNumber);
399 for (GPBUnknownField *field in fields_) {
400 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeFixed32) {
401 *outValue = field.fixed32;
408 - (BOOL)getFirst:(int32_t)fieldNumber fixed64:(nonnull uint64_t *)outValue {
409 CHECK_FIELD_NUMBER(fieldNumber);
410 for (GPBUnknownField *field in fields_) {
411 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeFixed64) {
412 *outValue = field.fixed64;
419 - (nullable NSData *)firstLengthDelimited:(int32_t)fieldNumber {
420 CHECK_FIELD_NUMBER(fieldNumber);
421 for (GPBUnknownField *field in fields_) {
422 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeLengthDelimited) {
423 return field.lengthDelimited;
429 - (nullable GPBUnknownFields *)firstGroup:(int32_t)fieldNumber {
430 CHECK_FIELD_NUMBER(fieldNumber);
431 for (GPBUnknownField *field in fields_) {
432 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeGroup) {
441 #pragma clang diagnostic pop