blob: 7b5c515c5093a353d43c2584964d1c2e24669db6 [file] [log] [blame]
Thomas Van Lenten47f633e2024-07-01 07:25:23 -07001// 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"
Thomas Van Lenten5710bfc2024-09-05 08:49:15 -07009#import "GPBUnknownFields_PackagePrivate.h"
Thomas Van Lentenc2299862024-07-08 15:12:47 -070010
11#import <Foundation/Foundation.h>
12
Thomas Van Lenten5710bfc2024-09-05 08:49:15 -070013#import "GPBCodedInputStream.h"
Thomas Van Lentenc2299862024-07-08 15:12:47 -070014#import "GPBCodedInputStream_PackagePrivate.h"
Thomas Van Lenten6e5a8672024-07-03 10:40:29 -070015#import "GPBCodedOutputStream.h"
16#import "GPBCodedOutputStream_PackagePrivate.h"
Thomas Van Lentenac844112024-07-16 10:22:10 -070017#import "GPBDescriptor.h"
Thomas Van Lentenc2299862024-07-08 15:12:47 -070018#import "GPBMessage.h"
Thomas Van Lenten224573d2024-07-23 09:55:21 -070019#import "GPBMessage_PackagePrivate.h"
Thomas Van Lentenc2299862024-07-08 15:12:47 -070020#import "GPBUnknownField.h"
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070021#import "GPBUnknownField_PackagePrivate.h"
Thomas Van Lentenc2299862024-07-08 15:12:47 -070022#import "GPBWireFormat.h"
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070023
24#define CHECK_FIELD_NUMBER(number) \
25 if (number <= 0) { \
26 [NSException raise:NSInvalidArgumentException format:@"Not a valid field number."]; \
27 }
28
Thomas Van Lentene759a392024-07-09 08:08:54 -070029// TODO: Consider using on other functions to reduce bloat when
30// some compiler optimizations are enabled.
31#define GPB_NOINLINE __attribute__((noinline))
Thomas Van Lentenc2299862024-07-08 15:12:47 -070032
Thomas Van Lentene759a392024-07-09 08:08:54 -070033@interface GPBUnknownFields () {
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070034 @package
35 NSMutableArray<GPBUnknownField *> *fields_;
36}
Thomas Van Lentene759a392024-07-09 08:08:54 -070037@end
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070038
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
Thomas Van Lentene759a392024-07-09 08:08:54 -070045GPB_NOINLINE
46static size_t ComputeSerializeSize(GPBUnknownFields *_Nonnull self) {
47 size_t result = 0;
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);
53 break;
54 case GPBUnknownFieldTypeFixed32:
55 result += GPBComputeFixed32Size(fieldNumber, (uint32_t)field->storage_.intValue);
56 break;
57 case GPBUnknownFieldTypeFixed64:
58 result += GPBComputeFixed64Size(fieldNumber, field->storage_.intValue);
59 break;
60 case GPBUnknownFieldTypeLengthDelimited:
61 result += GPBComputeBytesSize(fieldNumber, field->storage_.lengthDelimited);
62 break;
63 case GPBUnknownFieldTypeGroup:
64 result +=
65 (GPBComputeTagSize(fieldNumber) * 2) + ComputeSerializeSize(field->storage_.group);
66 break;
Thomas Van Lentene759a392024-07-09 08:08:54 -070067 }
68 }
69 return result;
70}
71
72GPB_NOINLINE
73static 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];
80 break;
81 case GPBUnknownFieldTypeFixed32:
82 [output writeFixed32:fieldNumber value:(uint32_t)field->storage_.intValue];
83 break;
84 case GPBUnknownFieldTypeFixed64:
85 [output writeFixed64:fieldNumber value:field->storage_.intValue];
86 break;
87 case GPBUnknownFieldTypeLengthDelimited:
88 [output writeBytes:fieldNumber value:field->storage_.lengthDelimited];
89 break;
90 case GPBUnknownFieldTypeGroup:
91 [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatStartGroup)];
92 WriteToCoddedOutputStream(field->storage_.group, output);
93 [output writeRawVarint32:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)];
94 break;
Thomas Van Lentene759a392024-07-09 08:08:54 -070095 }
96 }
97}
98
99GPB_NOINLINE
100static BOOL MergeFromInputStream(GPBUnknownFields *self, GPBCodedInputStream *input,
101 uint32_t endTag) {
102#if defined(DEBUG) && DEBUG
103 NSCAssert(endTag == 0 || GPBWireFormatGetTagWireType(endTag) == GPBWireFormatEndGroup,
104 @"Internal error:Invalid end tag: %u", endTag);
105#endif
106 GPBCodedInputStreamState *state = &input->state_;
107 NSMutableArray<GPBUnknownField *> *fields = self->fields_;
108 @try {
109 while (YES) {
110 uint32_t tag = GPBCodedInputStreamReadTag(state);
111 if (tag == endTag) {
112 return YES;
113 }
114 if (tag == 0) {
115 // Reached end of input without finding the end tag.
116 return NO;
117 }
118 GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
119 int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
120 switch (wireType) {
121 case GPBWireFormatVarint: {
122 uint64_t value = GPBCodedInputStreamReadInt64(state);
123 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
124 varint:value];
125 [fields addObject:field];
126 [field release];
127 break;
128 }
129 case GPBWireFormatFixed32: {
130 uint32_t value = GPBCodedInputStreamReadFixed32(state);
131 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
132 fixed32:value];
133 [fields addObject:field];
134 [field release];
135 break;
136 }
137 case GPBWireFormatFixed64: {
138 uint64_t value = GPBCodedInputStreamReadFixed64(state);
139 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
140 fixed64:value];
141 [fields addObject:field];
142 [field release];
143 break;
144 }
145 case GPBWireFormatLengthDelimited: {
146 NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
147 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
148 lengthDelimited:data];
149 [fields addObject:field];
150 [field release];
151 [data release];
152 break;
153 }
154 case GPBWireFormatStartGroup: {
155 GPBUnknownFields *group = [[GPBUnknownFields alloc] init];
156 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group];
157 [fields addObject:field];
158 [field release];
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);
163 } else {
164 [NSException
165 raise:NSInternalInconsistencyException
166 format:@"Internal error: Unknown field data for nested group was malformed."];
167 }
168 break;
169 }
170 case GPBWireFormatEndGroup:
171 [NSException raise:NSInternalInconsistencyException
172 format:@"Unexpected end group tag: %u", tag];
173 break;
174 }
175 }
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);
180#endif
181 }
182}
183
184@implementation GPBUnknownFields
185
Thomas Van Lentenc2299862024-07-08 15:12:47 -0700186- (instancetype)initFromMessage:(nonnull GPBMessage *)message {
187 self = [super init];
188 if (self) {
189 fields_ = [[NSMutableArray alloc] init];
Thomas Van Lentend2287772024-08-14 08:49:00 -0700190 // This shouldn't happen with the annotations, but just incase something claiming nonnull
191 // does return nil, block it.
192 if (!message) {
193 [self release];
194 [NSException raise:NSInvalidArgumentException format:@"Message cannot be nil"];
195 }
Thomas Van Lenten224573d2024-07-23 09:55:21 -0700196 NSData *data = GPBMessageUnknownFieldsData(message);
197 if (data) {
Thomas Van Lentenac844112024-07-16 10:22:10 -0700198 GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
Thomas Van Lentenc2299862024-07-08 15:12:47 -0700199 // Parse until the end of the data (tag will be zero).
Thomas Van Lentene759a392024-07-09 08:08:54 -0700200 if (!MergeFromInputStream(self, input, 0)) {
Thomas Van Lentenc2299862024-07-08 15:12:47 -0700201 [input release];
202 [self release];
203 [NSException raise:NSInternalInconsistencyException
204 format:@"Internal error: Unknown field data from message was malformed."];
205 }
206 [input release];
207 }
208 }
209 return self;
210}
211
Thomas Van Lenten47f633e2024-07-01 07:25:23 -0700212- (instancetype)init {
213 self = [super init];
214 if (self) {
215 fields_ = [[NSMutableArray alloc] init];
216 }
217 return self;
218}
219
220- (id)copyWithZone:(NSZone *)zone {
Thomas Van Lenten7155a7e2024-08-15 08:58:29 -0700221 GPBUnknownFields *copy = [[GPBUnknownFields allocWithZone:zone] init];
222 copy->fields_ = [[NSMutableArray allocWithZone:zone] initWithArray:fields_ copyItems:YES];
Thomas Van Lenten47f633e2024-07-01 07:25:23 -0700223 return copy;
224}
225
226- (void)dealloc {
227 [fields_ release];
228 [super dealloc];
229}
230
231- (BOOL)isEqual:(id)object {
232 if (![object isKindOfClass:[GPBUnknownFields class]]) {
233 return NO;
234 }
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_];
238}
239
240- (NSUInteger)hash {
241 return [fields_ hash];
242}
243
244- (NSString *)description {
245 return [NSString
246 stringWithFormat:@"<%@ %p>: %lu fields", [self class], self, (unsigned long)fields_.count];
247}
248
249#pragma mark - Public Methods
250
251- (NSUInteger)count {
252 return fields_.count;
253}
254
255- (BOOL)empty {
256 return fields_.count == 0;
257}
258
259- (void)clear {
260 [fields_ removeAllObjects];
261}
262
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];
269 }
270 }
271 if (result.count == 0) {
272 [result release];
273 return nil;
274 }
275 return [result autorelease];
276}
277
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];
282 [field release];
283}
284
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];
289 [field release];
290}
291
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];
296 [field release];
297}
298
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];
304 [field release];
305}
306
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];
312 [field release];
313 return [group autorelease];
314}
315
Thomas Van Lentene98e8ed2024-08-15 13:14:15 -0700316- (GPBUnknownField *)addCopyOfField:(nonnull GPBUnknownField *)field {
Thomas Van Lentene98e8ed2024-08-15 13:14:15 -0700317 GPBUnknownField *result = [field copy];
318 [fields_ addObject:result];
319 return [result autorelease];
320}
321
Thomas Van Lentenfe7b64e2024-08-01 14:25:07 -0700322- (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."];
327 }
328}
329
330- (void)clearFieldNumber:(int32_t)fieldNumber {
331 CHECK_FIELD_NUMBER(fieldNumber);
332 NSMutableIndexSet *toRemove = nil;
333 NSUInteger idx = 0;
334 for (GPBUnknownField *field in fields_) {
335 if (field->number_ == fieldNumber) {
336 if (toRemove == nil) {
337 toRemove = [[NSMutableIndexSet alloc] initWithIndex:idx];
338 } else {
339 [toRemove addIndex:idx];
340 }
341 }
342 ++idx;
343 }
344 if (toRemove) {
345 [fields_ removeObjectsAtIndexes:toRemove];
346 [toRemove release];
347 }
348}
349
Thomas Van Lenten47f633e2024-07-01 07:25:23 -0700350#pragma mark - NSFastEnumeration protocol
351
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];
356}
357
Thomas Van Lenten6e5a8672024-07-03 10:40:29 -0700358#pragma mark - Internal Methods
359
Thomas Van Lenten6e5a8672024-07-03 10:40:29 -0700360- (NSData *)serializeAsData {
361 if (fields_.count == 0) {
362 return [NSData data];
363 }
Thomas Van Lentene759a392024-07-09 08:08:54 -0700364 size_t expectedSize = ComputeSerializeSize(self);
Thomas Van Lenten6e5a8672024-07-03 10:40:29 -0700365 NSMutableData *data = [NSMutableData dataWithLength:expectedSize];
366 GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data];
367 @try {
Thomas Van Lentene759a392024-07-09 08:08:54 -0700368 WriteToCoddedOutputStream(self, stream);
Thomas Van Lenten6e5a8672024-07-03 10:40:29 -0700369 [stream flush];
370 } @catch (NSException *exception) {
371#if defined(DEBUG) && DEBUG
372 NSLog(@"Internal exception while building GPBUnknownFields serialized data: %@", exception);
373#endif
374 }
375#if defined(DEBUG) && DEBUG
376 NSAssert([stream bytesWritten] == expectedSize, @"Internal error within the library");
377#endif
378 [stream release];
379 return data;
380}
381
Thomas Van Lenten47f633e2024-07-01 07:25:23 -0700382@end
383
384@implementation GPBUnknownFields (AccessHelpers)
385
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;
391 return YES;
392 }
393 }
394 return NO;
395}
396
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;
402 return YES;
403 }
404 }
405 return NO;
406}
407
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;
413 return YES;
414 }
415 }
416 return NO;
417}
418
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;
424 }
425 }
426 return nil;
427}
428
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) {
433 return field.group;
434 }
435 }
436 return nil;
437}
438
Thomas Van Lenten47f633e2024-07-01 07:25:23 -0700439@end
Thomas Van Lentene759a392024-07-09 08:08:54 -0700440
441#pragma clang diagnostic pop