blob: ee33de18840c1bb48f8125088977310cfe1544ac [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 Lentenc2299862024-07-08 15:12:47 -07009
10#import <Foundation/Foundation.h>
11
12#import "GPBCodedInputStream_PackagePrivate.h"
Thomas Van Lenten6e5a8672024-07-03 10:40:29 -070013#import "GPBCodedOutputStream.h"
14#import "GPBCodedOutputStream_PackagePrivate.h"
Thomas Van Lentenc2299862024-07-08 15:12:47 -070015#import "GPBMessage.h"
16#import "GPBUnknownField.h"
17#import "GPBUnknownFieldSet_PackagePrivate.h"
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070018#import "GPBUnknownField_PackagePrivate.h"
Thomas Van Lentenc2299862024-07-08 15:12:47 -070019#import "GPBUnknownFields_PackagePrivate.h"
20#import "GPBWireFormat.h"
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070021
22#define CHECK_FIELD_NUMBER(number) \
23 if (number <= 0) { \
24 [NSException raise:NSInvalidArgumentException format:@"Not a valid field number."]; \
25 }
26
Thomas Van Lentenc2299862024-07-08 15:12:47 -070027@interface GPBUnknownFields ()
28- (BOOL)mergeFromInputStream:(nonnull GPBCodedInputStream *)input endTag:(uint32_t)endTag;
29@end
30
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070031@implementation GPBUnknownFields {
32 @package
33 NSMutableArray<GPBUnknownField *> *fields_;
34}
35
36// Direct access is use for speed, to avoid even internally declaring things
37// read/write, etc. The warning is enabled in the project to ensure code calling
38// protos can turn on -Wdirect-ivar-access without issues.
39#pragma clang diagnostic push
40#pragma clang diagnostic ignored "-Wdirect-ivar-access"
41
Thomas Van Lentenc2299862024-07-08 15:12:47 -070042- (instancetype)initFromMessage:(nonnull GPBMessage *)message {
43 self = [super init];
44 if (self) {
45 fields_ = [[NSMutableArray alloc] init];
46 // TODO: b/349146447 - Move off the legacy class and directly to the data once Message is
47 // updated.
48 GPBUnknownFieldSet *legacyUnknownFields = [message unknownFields];
49 if (legacyUnknownFields) {
50 GPBCodedInputStream *input =
51 [[GPBCodedInputStream alloc] initWithData:[legacyUnknownFields data]];
52 // Parse until the end of the data (tag will be zero).
53 if (![self mergeFromInputStream:input endTag:0]) {
54 [input release];
55 [self release];
56 [NSException raise:NSInternalInconsistencyException
57 format:@"Internal error: Unknown field data from message was malformed."];
58 }
59 [input release];
60 }
61 }
62 return self;
63}
64
Thomas Van Lenten47f633e2024-07-01 07:25:23 -070065- (instancetype)init {
66 self = [super init];
67 if (self) {
68 fields_ = [[NSMutableArray alloc] init];
69 }
70 return self;
71}
72
73- (id)copyWithZone:(NSZone *)zone {
74 GPBUnknownFields *copy = [[GPBUnknownFields alloc] init];
75 // Fields are r/o in this api, so just copy the array.
76 copy->fields_ = [fields_ mutableCopyWithZone:zone];
77 return copy;
78}
79
80- (void)dealloc {
81 [fields_ release];
82 [super dealloc];
83}
84
85- (BOOL)isEqual:(id)object {
86 if (![object isKindOfClass:[GPBUnknownFields class]]) {
87 return NO;
88 }
89 GPBUnknownFields *ufs = (GPBUnknownFields *)object;
90 // The type is defined with order of fields mattering, so just compare the arrays.
91 return [fields_ isEqual:ufs->fields_];
92}
93
94- (NSUInteger)hash {
95 return [fields_ hash];
96}
97
98- (NSString *)description {
99 return [NSString
100 stringWithFormat:@"<%@ %p>: %lu fields", [self class], self, (unsigned long)fields_.count];
101}
102
103#pragma mark - Public Methods
104
105- (NSUInteger)count {
106 return fields_.count;
107}
108
109- (BOOL)empty {
110 return fields_.count == 0;
111}
112
113- (void)clear {
114 [fields_ removeAllObjects];
115}
116
117- (NSArray<GPBUnknownField *> *)fields:(int32_t)fieldNumber {
118 CHECK_FIELD_NUMBER(fieldNumber);
119 NSMutableArray<GPBUnknownField *> *result = [[NSMutableArray alloc] init];
120 for (GPBUnknownField *field in fields_) {
121 if (field.number == fieldNumber) {
122 [result addObject:field];
123 }
124 }
125 if (result.count == 0) {
126 [result release];
127 return nil;
128 }
129 return [result autorelease];
130}
131
132- (void)addFieldNumber:(int32_t)fieldNumber varint:(uint64_t)value {
133 CHECK_FIELD_NUMBER(fieldNumber);
134 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber varint:value];
135 [fields_ addObject:field];
136 [field release];
137}
138
139- (void)addFieldNumber:(int32_t)fieldNumber fixed32:(uint32_t)value {
140 CHECK_FIELD_NUMBER(fieldNumber);
141 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber fixed32:value];
142 [fields_ addObject:field];
143 [field release];
144}
145
146- (void)addFieldNumber:(int32_t)fieldNumber fixed64:(uint64_t)value {
147 CHECK_FIELD_NUMBER(fieldNumber);
148 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber fixed64:value];
149 [fields_ addObject:field];
150 [field release];
151}
152
153- (void)addFieldNumber:(int32_t)fieldNumber lengthDelimited:(NSData *)value {
154 CHECK_FIELD_NUMBER(fieldNumber);
155 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
156 lengthDelimited:value];
157 [fields_ addObject:field];
158 [field release];
159}
160
161- (GPBUnknownFields *)addGroupWithFieldNumber:(int32_t)fieldNumber {
162 CHECK_FIELD_NUMBER(fieldNumber);
163 GPBUnknownFields *group = [[GPBUnknownFields alloc] init];
164 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group];
165 [fields_ addObject:field];
166 [field release];
167 return [group autorelease];
168}
169
170#pragma mark - NSFastEnumeration protocol
171
172- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
173 objects:(__unsafe_unretained id _Nonnull *)stackbuf
174 count:(NSUInteger)len {
175 return [fields_ countByEnumeratingWithState:state objects:stackbuf count:len];
176}
177
Thomas Van Lenten6e5a8672024-07-03 10:40:29 -0700178#pragma mark - Internal Methods
179
180- (size_t)serializedSize {
181 size_t result = 0;
182 for (GPBUnknownField *field in self->fields_) {
183 result += [field serializedSize];
184 }
185 return result;
186}
187
188- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
189 for (GPBUnknownField *field in fields_) {
190 [field writeToOutput:output];
191 }
192}
193
194- (NSData *)serializeAsData {
195 if (fields_.count == 0) {
196 return [NSData data];
197 }
198 size_t expectedSize = [self serializedSize];
199 NSMutableData *data = [NSMutableData dataWithLength:expectedSize];
200 GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data];
201 @try {
202 [self writeToCodedOutputStream:stream];
203 [stream flush];
204 } @catch (NSException *exception) {
205#if defined(DEBUG) && DEBUG
206 NSLog(@"Internal exception while building GPBUnknownFields serialized data: %@", exception);
207#endif
208 }
209#if defined(DEBUG) && DEBUG
210 NSAssert([stream bytesWritten] == expectedSize, @"Internal error within the library");
211#endif
212 [stream release];
213 return data;
214}
215
Thomas Van Lentenc2299862024-07-08 15:12:47 -0700216- (BOOL)mergeFromInputStream:(nonnull GPBCodedInputStream *)input endTag:(uint32_t)endTag {
217#if defined(DEBUG) && DEBUG
218 NSAssert(endTag == 0 || GPBWireFormatGetTagWireType(endTag) == GPBWireFormatEndGroup,
219 @"Internal error:Invalid end tag: %u", endTag);
220#endif
221 GPBCodedInputStreamState *state = &input->state_;
222 @try {
223 while (YES) {
224 uint32_t tag = GPBCodedInputStreamReadTag(state);
225 if (tag == endTag) {
226 return YES;
227 }
228 if (tag == 0) {
229 // Reached end of input without finding the end tag.
230 return NO;
231 }
232 GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
233 int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
234 switch (wireType) {
235 case GPBWireFormatVarint: {
236 uint64_t value = GPBCodedInputStreamReadInt64(state);
237 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
238 varint:value];
239 [fields_ addObject:field];
240 [field release];
241 break;
242 }
243 case GPBWireFormatFixed32: {
244 uint32_t value = GPBCodedInputStreamReadFixed32(state);
245 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
246 fixed32:value];
247 [fields_ addObject:field];
248 [field release];
249 break;
250 }
251 case GPBWireFormatFixed64: {
252 uint64_t value = GPBCodedInputStreamReadFixed64(state);
253 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
254 fixed64:value];
255 [fields_ addObject:field];
256 [field release];
257 break;
258 }
259 case GPBWireFormatLengthDelimited: {
260 NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
261 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber
262 lengthDelimited:data];
263 [fields_ addObject:field];
264 [field release];
265 [data release];
266 break;
267 }
268 case GPBWireFormatStartGroup: {
269 GPBUnknownFields *group = [[GPBUnknownFields alloc] init];
270 GPBUnknownField *field = [[GPBUnknownField alloc] initWithNumber:fieldNumber group:group];
271 [fields_ addObject:field];
272 [field release];
273 [group release]; // Still will be held in the field/fields_.
274 uint32_t endGroupTag = GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup);
275 if ([group mergeFromInputStream:input endTag:endGroupTag]) {
276 GPBCodedInputStreamCheckLastTagWas(state, endGroupTag);
277 } else {
278 [NSException
279 raise:NSInternalInconsistencyException
280 format:@"Internal error: Unknown field data for nested group was malformed."];
281 }
282 break;
283 }
284 case GPBWireFormatEndGroup:
285 [NSException raise:NSInternalInconsistencyException
286 format:@"Unexpected end group tag: %u", tag];
287 break;
288 }
289 }
290 } @catch (NSException *exception) {
291#if defined(DEBUG) && DEBUG
292 NSLog(@"%@: Internal exception while parsing unknown data, this shouldn't happen!: %@",
293 [self class], exception);
294#endif
295 }
296}
297
Thomas Van Lenten47f633e2024-07-01 07:25:23 -0700298@end
299
300@implementation GPBUnknownFields (AccessHelpers)
301
302- (BOOL)getFirst:(int32_t)fieldNumber varint:(nonnull uint64_t *)outValue {
303 CHECK_FIELD_NUMBER(fieldNumber);
304 for (GPBUnknownField *field in fields_) {
305 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeVarint) {
306 *outValue = field.varint;
307 return YES;
308 }
309 }
310 return NO;
311}
312
313- (BOOL)getFirst:(int32_t)fieldNumber fixed32:(nonnull uint32_t *)outValue {
314 CHECK_FIELD_NUMBER(fieldNumber);
315 for (GPBUnknownField *field in fields_) {
316 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeFixed32) {
317 *outValue = field.fixed32;
318 return YES;
319 }
320 }
321 return NO;
322}
323
324- (BOOL)getFirst:(int32_t)fieldNumber fixed64:(nonnull uint64_t *)outValue {
325 CHECK_FIELD_NUMBER(fieldNumber);
326 for (GPBUnknownField *field in fields_) {
327 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeFixed64) {
328 *outValue = field.fixed64;
329 return YES;
330 }
331 }
332 return NO;
333}
334
335- (nullable NSData *)firstLengthDelimited:(int32_t)fieldNumber {
336 CHECK_FIELD_NUMBER(fieldNumber);
337 for (GPBUnknownField *field in fields_) {
338 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeLengthDelimited) {
339 return field.lengthDelimited;
340 }
341 }
342 return nil;
343}
344
345- (nullable GPBUnknownFields *)firstGroup:(int32_t)fieldNumber {
346 CHECK_FIELD_NUMBER(fieldNumber);
347 for (GPBUnknownField *field in fields_) {
348 if (field.number == fieldNumber && field.type == GPBUnknownFieldTypeGroup) {
349 return field.group;
350 }
351 }
352 return nil;
353}
354
355#pragma clang diagnostic pop
356
357@end