• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 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 "GPBUnknownFieldSet.h"
9#import "GPBUnknownFieldSet_PackagePrivate.h"
10
11#import "GPBCodedInputStream.h"
12#import "GPBCodedInputStream_PackagePrivate.h"
13#import "GPBCodedOutputStream.h"
14#import "GPBUnknownField.h"
15#import "GPBUnknownField_PackagePrivate.h"
16#import "GPBUtilities.h"
17#import "GPBWireFormat.h"
18
19#pragma clang diagnostic push
20#pragma clang diagnostic ignored "-Wdeprecated-declarations"
21#pragma clang diagnostic ignored "-Wdeprecated-implementations"
22
23#pragma mark Helpers
24
25static void checkNumber(int32_t number) {
26  if (number == 0) {
27    [NSException raise:NSInvalidArgumentException format:@"Zero is not a valid field number."];
28  }
29}
30
31@implementation GPBUnknownFieldSet {
32 @package
33  CFMutableDictionaryRef fields_;
34}
35
36static void CopyWorker(__unused const void *key, const void *value, void *context) {
37  GPBUnknownField *field = value;
38  GPBUnknownFieldSet *result = context;
39
40  GPBUnknownField *copied = [field copy];
41  [result addField:copied];
42  [copied release];
43}
44
45// Direct access is use for speed, to avoid even internally declaring things
46// read/write, etc. The warning is enabled in the project to ensure code calling
47// protos can turn on -Wdirect-ivar-access without issues.
48#pragma clang diagnostic push
49#pragma clang diagnostic ignored "-Wdirect-ivar-access"
50
51- (id)copyWithZone:(NSZone *)zone {
52  GPBUnknownFieldSet *result = [[GPBUnknownFieldSet allocWithZone:zone] init];
53  if (fields_) {
54    CFDictionaryApplyFunction(fields_, CopyWorker, result);
55  }
56  return result;
57}
58
59- (void)dealloc {
60  if (fields_) {
61    CFRelease(fields_);
62  }
63  [super dealloc];
64}
65
66- (BOOL)isEqual:(id)object {
67  BOOL equal = NO;
68  if ([object isKindOfClass:[GPBUnknownFieldSet class]]) {
69    GPBUnknownFieldSet *set = (GPBUnknownFieldSet *)object;
70    if ((fields_ == NULL) && (set->fields_ == NULL)) {
71      equal = YES;
72    } else if ((fields_ != NULL) && (set->fields_ != NULL)) {
73      equal = CFEqual(fields_, set->fields_);
74    }
75  }
76  return equal;
77}
78
79- (NSUInteger)hash {
80  // Return the hash of the fields dictionary (or just some value).
81  if (fields_) {
82    return CFHash(fields_);
83  }
84  return (NSUInteger)[GPBUnknownFieldSet class];
85}
86
87#pragma mark - Public Methods
88
89- (BOOL)hasField:(int32_t)number {
90  ssize_t key = number;
91  return fields_ ? (CFDictionaryGetValue(fields_, (void *)key) != nil) : NO;
92}
93
94- (GPBUnknownField *)getField:(int32_t)number {
95  ssize_t key = number;
96  GPBUnknownField *result = fields_ ? CFDictionaryGetValue(fields_, (void *)key) : nil;
97  return result;
98}
99
100- (NSUInteger)countOfFields {
101  return fields_ ? CFDictionaryGetCount(fields_) : 0;
102}
103
104- (NSArray *)sortedFields {
105  if (!fields_) return [NSArray array];
106  size_t count = CFDictionaryGetCount(fields_);
107  ssize_t keys[count];
108  GPBUnknownField *values[count];
109  CFDictionaryGetKeysAndValues(fields_, (const void **)keys, (const void **)values);
110  struct GPBFieldPair {
111    ssize_t key;
112    GPBUnknownField *value;
113  } pairs[count];
114  for (size_t i = 0; i < count; ++i) {
115    pairs[i].key = keys[i];
116    pairs[i].value = values[i];
117  };
118  qsort_b(pairs, count, sizeof(struct GPBFieldPair), ^(const void *first, const void *second) {
119    const struct GPBFieldPair *a = first;
120    const struct GPBFieldPair *b = second;
121    return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
122  });
123  for (size_t i = 0; i < count; ++i) {
124    values[i] = pairs[i].value;
125  };
126  return [NSArray arrayWithObjects:values count:count];
127}
128
129#pragma mark - Internal Methods
130
131- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
132  if (!fields_) return;
133  size_t count = CFDictionaryGetCount(fields_);
134  ssize_t keys[count];
135  GPBUnknownField *values[count];
136  CFDictionaryGetKeysAndValues(fields_, (const void **)keys, (const void **)values);
137  if (count > 1) {
138    struct GPBFieldPair {
139      ssize_t key;
140      GPBUnknownField *value;
141    } pairs[count];
142
143    for (size_t i = 0; i < count; ++i) {
144      pairs[i].key = keys[i];
145      pairs[i].value = values[i];
146    };
147    qsort_b(pairs, count, sizeof(struct GPBFieldPair), ^(const void *first, const void *second) {
148      const struct GPBFieldPair *a = first;
149      const struct GPBFieldPair *b = second;
150      return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
151    });
152    for (size_t i = 0; i < count; ++i) {
153      GPBUnknownField *value = pairs[i].value;
154      [value writeToOutput:output];
155    }
156  } else {
157    [values[0] writeToOutput:output];
158  }
159}
160
161- (NSString *)description {
162  NSMutableString *description =
163      [NSMutableString stringWithFormat:@"<%@ %p>: TextFormat: {\n", [self class], self];
164  NSString *textFormat = GPBTextFormatForUnknownFieldSet(self, @"  ");
165  [description appendString:textFormat];
166  [description appendString:@"}"];
167  return description;
168}
169
170static void GPBUnknownFieldSetSerializedSize(__unused const void *key, const void *value,
171                                             void *context) {
172  GPBUnknownField *field = value;
173  size_t *result = context;
174  *result += [field serializedSize];
175}
176
177- (size_t)serializedSize {
178  size_t result = 0;
179  if (fields_) {
180    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetSerializedSize, &result);
181  }
182  return result;
183}
184
185static void GPBUnknownFieldSetWriteAsMessageSetTo(__unused const void *key, const void *value,
186                                                  void *context) {
187  GPBUnknownField *field = value;
188  GPBCodedOutputStream *output = context;
189  [field writeAsMessageSetExtensionToOutput:output];
190}
191
192- (void)writeAsMessageSetTo:(GPBCodedOutputStream *)output {
193  if (fields_) {
194    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetWriteAsMessageSetTo, output);
195  }
196}
197
198static void GPBUnknownFieldSetSerializedSizeAsMessageSet(__unused const void *key,
199                                                         const void *value, void *context) {
200  GPBUnknownField *field = value;
201  size_t *result = context;
202  *result += [field serializedSizeAsMessageSetExtension];
203}
204
205- (size_t)serializedSizeAsMessageSet {
206  size_t result = 0;
207  if (fields_) {
208    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetSerializedSizeAsMessageSet, &result);
209  }
210  return result;
211}
212
213- (NSData *)data {
214  NSMutableData *data = [NSMutableData dataWithLength:self.serializedSize];
215  GPBCodedOutputStream *output = [[GPBCodedOutputStream alloc] initWithData:data];
216  [self writeToCodedOutputStream:output];
217  [output flush];
218  [output release];
219  return data;
220}
221
222- (void)addField:(GPBUnknownField *)field {
223  int32_t number = [field number];
224  checkNumber(number);
225  if (!fields_) {
226    // Use a custom dictionary here because the keys are numbers and conversion
227    // back and forth from NSNumber isn't worth the cost.
228    fields_ =
229        CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
230  }
231  ssize_t key = number;
232  CFDictionarySetValue(fields_, (const void *)key, field);
233}
234
235- (GPBUnknownField *)mutableFieldForNumber:(int32_t)number create:(BOOL)create {
236  ssize_t key = number;
237  GPBUnknownField *existing = fields_ ? CFDictionaryGetValue(fields_, (const void *)key) : nil;
238  if (!existing && create) {
239    existing = [[GPBUnknownField alloc] initWithNumber:number];
240    // This retains existing.
241    [self addField:existing];
242    [existing release];
243  }
244  return existing;
245}
246
247static void GPBUnknownFieldSetMergeUnknownFields(__unused const void *key, const void *value,
248                                                 void *context) {
249  GPBUnknownField *field = value;
250  GPBUnknownFieldSet *self = context;
251
252  int32_t number = [field number];
253  checkNumber(number);
254  GPBUnknownField *oldField = [self mutableFieldForNumber:number create:NO];
255  if (oldField) {
256    [oldField mergeFromField:field];
257  } else {
258    // Merge only comes from GPBMessage's mergeFrom:, so it means we are on
259    // mutable message and are an mutable instance, so make sure we need
260    // mutable fields.
261    GPBUnknownField *fieldCopy = [field copy];
262    [self addField:fieldCopy];
263    [fieldCopy release];
264  }
265}
266
267- (void)mergeUnknownFields:(GPBUnknownFieldSet *)other {
268  if (other && other->fields_) {
269    CFDictionaryApplyFunction(other->fields_, GPBUnknownFieldSetMergeUnknownFields, self);
270  }
271}
272
273- (void)mergeVarintField:(int32_t)number value:(int32_t)value {
274  checkNumber(number);
275  [[self mutableFieldForNumber:number create:YES] addVarint:value];
276}
277
278- (void)mergeLengthDelimited:(int32_t)fieldNum value:(NSData *)value {
279  checkNumber(fieldNum);
280  [[self mutableFieldForNumber:fieldNum create:YES] addLengthDelimited:value];
281}
282
283- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input {
284  NSAssert(GPBWireFormatIsValidTag(tag), @"Got passed an invalid tag");
285  int32_t number = GPBWireFormatGetTagFieldNumber(tag);
286  GPBCodedInputStreamState *state = &input->state_;
287  switch (GPBWireFormatGetTagWireType(tag)) {
288    case GPBWireFormatVarint: {
289      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
290      [field addVarint:GPBCodedInputStreamReadInt64(state)];
291      return YES;
292    }
293    case GPBWireFormatFixed64: {
294      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
295      [field addFixed64:GPBCodedInputStreamReadFixed64(state)];
296      return YES;
297    }
298    case GPBWireFormatLengthDelimited: {
299      NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
300      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
301      [field addLengthDelimited:data];
302      [data release];
303      return YES;
304    }
305    case GPBWireFormatStartGroup: {
306      GPBUnknownFieldSet *unknownFieldSet = [[GPBUnknownFieldSet alloc] init];
307      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
308      [field addGroup:unknownFieldSet];
309      // The field will now retain unknownFieldSet, so go ahead and release it in case
310      // -readUnknownGroup:message: throws so it won't be leaked.
311      [unknownFieldSet release];
312      [input readUnknownGroup:number message:unknownFieldSet];
313      return YES;
314    }
315    case GPBWireFormatEndGroup:
316      return NO;
317    case GPBWireFormatFixed32: {
318      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
319      [field addFixed32:GPBCodedInputStreamReadFixed32(state)];
320      return YES;
321    }
322  }
323}
324
325- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input {
326  while (YES) {
327    int32_t tag = GPBCodedInputStreamReadTag(&input->state_);
328    if (tag == 0 || ![self mergeFieldFrom:tag input:input]) {
329      break;
330    }
331  }
332}
333
334- (void)getTags:(int32_t *)tags {
335  if (!fields_) return;
336  size_t count = CFDictionaryGetCount(fields_);
337  ssize_t keys[count];
338  CFDictionaryGetKeysAndValues(fields_, (const void **)keys, NULL);
339  for (size_t i = 0; i < count; ++i) {
340    tags[i] = (int32_t)keys[i];
341  }
342}
343
344#pragma clang diagnostic pop
345
346@end
347
348#pragma clang diagnostic pop  // -Wdeprecated-declarations suppression
349