Simplify generated string_view APIs.
[google-protobuf.git] / objectivec / GPBUnknownFields.m
blob7b5c515c5093a353d43c2584964d1c2e24669db6
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
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)                                                      \
25   if (number <= 0) {                                                                    \
26     [NSException raise:NSInvalidArgumentException format:@"Not a valid field number."]; \
27   }
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 () {
34  @package
35   NSMutableArray<GPBUnknownField *> *fields_;
37 @end
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"
45 GPB_NOINLINE
46 static 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;
67     }
68   }
69   return result;
72 GPB_NOINLINE
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];
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;
95     }
96   }
99 GPB_NOINLINE
100 static 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   }
184 @implementation GPBUnknownFields
186 - (instancetype)initFromMessage:(nonnull GPBMessage *)message {
187   self = [super init];
188   if (self) {
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.
192     if (!message) {
193       [self release];
194       [NSException raise:NSInvalidArgumentException format:@"Message cannot be nil"];
195     }
196     NSData *data = GPBMessageUnknownFieldsData(message);
197     if (data) {
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)) {
201         [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;
212 - (instancetype)init {
213   self = [super init];
214   if (self) {
215     fields_ = [[NSMutableArray alloc] init];
216   }
217   return self;
220 - (id)copyWithZone:(NSZone *)zone {
221   GPBUnknownFields *copy = [[GPBUnknownFields allocWithZone:zone] init];
222   copy->fields_ = [[NSMutableArray allocWithZone:zone] initWithArray:fields_ copyItems:YES];
223   return copy;
226 - (void)dealloc {
227   [fields_ release];
228   [super dealloc];
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_];
240 - (NSUInteger)hash {
241   return [fields_ hash];
244 - (NSString *)description {
245   return [NSString
246       stringWithFormat:@"<%@ %p>: %lu fields", [self class], self, (unsigned long)fields_.count];
249 #pragma mark - Public Methods
251 - (NSUInteger)count {
252   return fields_.count;
255 - (BOOL)empty {
256   return fields_.count == 0;
259 - (void)clear {
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];
269     }
270   }
271   if (result.count == 0) {
272     [result release];
273     return nil;
274   }
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];
282   [field release];
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];
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];
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];
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];
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."];
327   }
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   }
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];
363   }
364   size_t expectedSize = ComputeSerializeSize(self);
365   NSMutableData *data = [NSMutableData dataWithLength:expectedSize];
366   GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data];
367   @try {
368     WriteToCoddedOutputStream(self, stream);
369     [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;
382 @end
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;
391       return YES;
392     }
393   }
394   return NO;
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;
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;
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;
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;
439 @end
441 #pragma clang diagnostic pop