1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#import "GPBUnknownFieldSet_PackagePrivate.h"
32
33#import "GPBCodedInputStream_PackagePrivate.h"
34#import "GPBCodedOutputStream.h"
35#import "GPBUnknownField_PackagePrivate.h"
36#import "GPBUtilities.h"
37#import "GPBWireFormat.h"
38
39#pragma mark CFDictionaryKeyCallBacks
40
41// We use a custom dictionary here because our keys are numbers and
42// conversion back and forth from NSNumber was costing us performance.
43// If/when we move to C++ this could be done using a std::map and some
44// careful retain/release calls.
45
46static const void *GPBUnknownFieldSetKeyRetain(CFAllocatorRef allocator,
47                                               const void *value) {
48#pragma unused(allocator)
49  return value;
50}
51
52static void GPBUnknownFieldSetKeyRelease(CFAllocatorRef allocator,
53                                         const void *value) {
54#pragma unused(allocator)
55#pragma unused(value)
56}
57
58static CFStringRef GPBUnknownFieldSetCopyKeyDescription(const void *value) {
59  return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"),
60                                  (int)value);
61}
62
63static Boolean GPBUnknownFieldSetKeyEqual(const void *value1,
64                                          const void *value2) {
65  return value1 == value2;
66}
67
68static CFHashCode GPBUnknownFieldSetKeyHash(const void *value) {
69  return (CFHashCode)value;
70}
71
72#pragma mark Helpers
73
74static void checkNumber(int32_t number) {
75  if (number == 0) {
76    [NSException raise:NSInvalidArgumentException
77                format:@"Zero is not a valid field number."];
78  }
79}
80
81@implementation GPBUnknownFieldSet {
82 @package
83  CFMutableDictionaryRef fields_;
84}
85
86static void CopyWorker(const void *key, const void *value, void *context) {
87#pragma unused(key)
88  GPBUnknownField *field = value;
89  GPBUnknownFieldSet *result = context;
90
91  GPBUnknownField *copied = [field copy];
92  [result addField:copied];
93  [copied release];
94}
95
96// Direct access is use for speed, to avoid even internally declaring things
97// read/write, etc. The warning is enabled in the project to ensure code calling
98// protos can turn on -Wdirect-ivar-access without issues.
99#pragma clang diagnostic push
100#pragma clang diagnostic ignored "-Wdirect-ivar-access"
101
102- (id)copyWithZone:(NSZone *)zone {
103  GPBUnknownFieldSet *result = [[GPBUnknownFieldSet allocWithZone:zone] init];
104  if (fields_) {
105    CFDictionaryApplyFunction(fields_, CopyWorker, result);
106  }
107  return result;
108}
109
110- (void)dealloc {
111  if (fields_) {
112    CFRelease(fields_);
113  }
114  [super dealloc];
115}
116
117- (BOOL)isEqual:(id)object {
118  BOOL equal = NO;
119  if ([object isKindOfClass:[GPBUnknownFieldSet class]]) {
120    GPBUnknownFieldSet *set = (GPBUnknownFieldSet *)object;
121    if ((fields_ == NULL) && (set->fields_ == NULL)) {
122      equal = YES;
123    } else if ((fields_ != NULL) && (set->fields_ != NULL)) {
124      equal = CFEqual(fields_, set->fields_);
125    }
126  }
127  return equal;
128}
129
130- (NSUInteger)hash {
131  // Return the hash of the fields dictionary (or just some value).
132  if (fields_) {
133    return CFHash(fields_);
134  }
135  return (NSUInteger)[GPBUnknownFieldSet class];
136}
137
138#pragma mark - Public Methods
139
140- (BOOL)hasField:(int32_t)number {
141  ssize_t key = number;
142  return fields_ ? (CFDictionaryGetValue(fields_, (void *)key) != nil) : NO;
143}
144
145- (GPBUnknownField *)getField:(int32_t)number {
146  ssize_t key = number;
147  GPBUnknownField *result =
148      fields_ ? CFDictionaryGetValue(fields_, (void *)key) : nil;
149  return result;
150}
151
152- (NSUInteger)countOfFields {
153  return fields_ ? CFDictionaryGetCount(fields_) : 0;
154}
155
156- (NSArray *)sortedFields {
157  if (!fields_) return [NSArray array];
158  size_t count = CFDictionaryGetCount(fields_);
159  ssize_t keys[count];
160  GPBUnknownField *values[count];
161  CFDictionaryGetKeysAndValues(fields_, (const void **)keys,
162                               (const void **)values);
163  struct GPBFieldPair {
164    ssize_t key;
165    GPBUnknownField *value;
166  } pairs[count];
167  for (size_t i = 0; i < count; ++i) {
168    pairs[i].key = keys[i];
169    pairs[i].value = values[i];
170  };
171  qsort_b(pairs, count, sizeof(struct GPBFieldPair),
172          ^(const void *first, const void *second) {
173            const struct GPBFieldPair *a = first;
174            const struct GPBFieldPair *b = second;
175            return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
176          });
177  for (size_t i = 0; i < count; ++i) {
178    values[i] = pairs[i].value;
179  };
180  return [NSArray arrayWithObjects:values count:count];
181}
182
183#pragma mark - Internal Methods
184
185- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
186  if (!fields_) return;
187  size_t count = CFDictionaryGetCount(fields_);
188  ssize_t keys[count];
189  GPBUnknownField *values[count];
190  CFDictionaryGetKeysAndValues(fields_, (const void **)keys,
191                               (const void **)values);
192  if (count > 1) {
193    struct GPBFieldPair {
194      ssize_t key;
195      GPBUnknownField *value;
196    } pairs[count];
197
198    for (size_t i = 0; i < count; ++i) {
199      pairs[i].key = keys[i];
200      pairs[i].value = values[i];
201    };
202    qsort_b(pairs, count, sizeof(struct GPBFieldPair),
203            ^(const void *first, const void *second) {
204              const struct GPBFieldPair *a = first;
205              const struct GPBFieldPair *b = second;
206              return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
207            });
208    for (size_t i = 0; i < count; ++i) {
209      GPBUnknownField *value = pairs[i].value;
210      [value writeToOutput:output];
211    }
212  } else {
213    [values[0] writeToOutput:output];
214  }
215}
216
217- (NSString *)description {
218  NSMutableString *description = [NSMutableString
219      stringWithFormat:@"<%@ %p>: TextFormat: {\n", [self class], self];
220  NSString *textFormat = GPBTextFormatForUnknownFieldSet(self, @"  ");
221  [description appendString:textFormat];
222  [description appendString:@"}"];
223  return description;
224}
225
226static void GPBUnknownFieldSetSerializedSize(const void *key, const void *value,
227                                             void *context) {
228#pragma unused(key)
229  GPBUnknownField *field = value;
230  size_t *result = context;
231  *result += [field serializedSize];
232}
233
234- (size_t)serializedSize {
235  size_t result = 0;
236  if (fields_) {
237    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetSerializedSize,
238                              &result);
239  }
240  return result;
241}
242
243static void GPBUnknownFieldSetWriteAsMessageSetTo(const void *key,
244                                                  const void *value,
245                                                  void *context) {
246#pragma unused(key)
247  GPBUnknownField *field = value;
248  GPBCodedOutputStream *output = context;
249  [field writeAsMessageSetExtensionToOutput:output];
250}
251
252- (void)writeAsMessageSetTo:(GPBCodedOutputStream *)output {
253  if (fields_) {
254    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetWriteAsMessageSetTo,
255                              output);
256  }
257}
258
259static void GPBUnknownFieldSetSerializedSizeAsMessageSet(const void *key,
260                                                         const void *value,
261                                                         void *context) {
262#pragma unused(key)
263  GPBUnknownField *field = value;
264  size_t *result = context;
265  *result += [field serializedSizeAsMessageSetExtension];
266}
267
268- (size_t)serializedSizeAsMessageSet {
269  size_t result = 0;
270  if (fields_) {
271    CFDictionaryApplyFunction(
272        fields_, GPBUnknownFieldSetSerializedSizeAsMessageSet, &result);
273  }
274  return result;
275}
276
277- (NSData *)data {
278  NSMutableData *data = [NSMutableData dataWithLength:self.serializedSize];
279  GPBCodedOutputStream *output =
280      [[GPBCodedOutputStream alloc] initWithData:data];
281  [self writeToCodedOutputStream:output];
282  [output release];
283  return data;
284}
285
286+ (BOOL)isFieldTag:(int32_t)tag {
287  return GPBWireFormatGetTagWireType(tag) != GPBWireFormatEndGroup;
288}
289
290- (void)addField:(GPBUnknownField *)field {
291  int32_t number = [field number];
292  checkNumber(number);
293  if (!fields_) {
294    CFDictionaryKeyCallBacks keyCallBacks = {
295        // See description above for reason for using custom dictionary.
296        0, GPBUnknownFieldSetKeyRetain, GPBUnknownFieldSetKeyRelease,
297        GPBUnknownFieldSetCopyKeyDescription, GPBUnknownFieldSetKeyEqual,
298        GPBUnknownFieldSetKeyHash,
299    };
300    fields_ = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallBacks,
301                                        &kCFTypeDictionaryValueCallBacks);
302  }
303  ssize_t key = number;
304  CFDictionarySetValue(fields_, (const void *)key, field);
305}
306
307- (GPBUnknownField *)mutableFieldForNumber:(int32_t)number create:(BOOL)create {
308  ssize_t key = number;
309  GPBUnknownField *existing =
310      fields_ ? CFDictionaryGetValue(fields_, (const void *)key) : nil;
311  if (!existing && create) {
312    existing = [[GPBUnknownField alloc] initWithNumber:number];
313    // This retains existing.
314    [self addField:existing];
315    [existing release];
316  }
317  return existing;
318}
319
320static void GPBUnknownFieldSetMergeUnknownFields(const void *key,
321                                                 const void *value,
322                                                 void *context) {
323#pragma unused(key)
324  GPBUnknownField *field = value;
325  GPBUnknownFieldSet *self = context;
326
327  int32_t number = [field number];
328  checkNumber(number);
329  GPBUnknownField *oldField = [self mutableFieldForNumber:number create:NO];
330  if (oldField) {
331    [oldField mergeFromField:field];
332  } else {
333    // Merge only comes from GPBMessage's mergeFrom:, so it means we are on
334    // mutable message and are an mutable instance, so make sure we need
335    // mutable fields.
336    GPBUnknownField *fieldCopy = [field copy];
337    [self addField:fieldCopy];
338    [fieldCopy release];
339  }
340}
341
342- (void)mergeUnknownFields:(GPBUnknownFieldSet *)other {
343  if (other && other->fields_) {
344    CFDictionaryApplyFunction(other->fields_,
345                              GPBUnknownFieldSetMergeUnknownFields, self);
346  }
347}
348
349- (void)mergeFromData:(NSData *)data {
350  GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
351  [self mergeFromCodedInputStream:input];
352  [input checkLastTagWas:0];
353  [input release];
354}
355
356- (void)mergeVarintField:(int32_t)number value:(int32_t)value {
357  checkNumber(number);
358  [[self mutableFieldForNumber:number create:YES] addVarint:value];
359}
360
361- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input {
362  NSAssert(GPBWireFormatIsValidTag(tag), @"Got passed an invalid tag");
363  int32_t number = GPBWireFormatGetTagFieldNumber(tag);
364  GPBCodedInputStreamState *state = &input->state_;
365  switch (GPBWireFormatGetTagWireType(tag)) {
366    case GPBWireFormatVarint: {
367      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
368      [field addVarint:GPBCodedInputStreamReadInt64(state)];
369      return YES;
370    }
371    case GPBWireFormatFixed64: {
372      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
373      [field addFixed64:GPBCodedInputStreamReadFixed64(state)];
374      return YES;
375    }
376    case GPBWireFormatLengthDelimited: {
377      NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
378      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
379      [field addLengthDelimited:data];
380      [data release];
381      return YES;
382    }
383    case GPBWireFormatStartGroup: {
384      GPBUnknownFieldSet *unknownFieldSet = [[GPBUnknownFieldSet alloc] init];
385      [input readUnknownGroup:number message:unknownFieldSet];
386      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
387      [field addGroup:unknownFieldSet];
388      [unknownFieldSet release];
389      return YES;
390    }
391    case GPBWireFormatEndGroup:
392      return NO;
393    case GPBWireFormatFixed32: {
394      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
395      [field addFixed32:GPBCodedInputStreamReadFixed32(state)];
396      return YES;
397    }
398  }
399}
400
401- (void)mergeMessageSetMessage:(int32_t)number data:(NSData *)messageData {
402  [[self mutableFieldForNumber:number create:YES]
403      addLengthDelimited:messageData];
404}
405
406- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data {
407  GPBUnknownField *field = [self mutableFieldForNumber:fieldNum create:YES];
408  [field addLengthDelimited:data];
409}
410
411- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input {
412  while (YES) {
413    int32_t tag = GPBCodedInputStreamReadTag(&input->state_);
414    if (tag == 0 || ![self mergeFieldFrom:tag input:input]) {
415      break;
416    }
417  }
418}
419
420- (void)getTags:(int32_t *)tags {
421  if (!fields_) return;
422  size_t count = CFDictionaryGetCount(fields_);
423  ssize_t keys[count];
424  CFDictionaryGetKeysAndValues(fields_, (const void **)keys, NULL);
425  for (size_t i = 0; i < count; ++i) {
426    tags[i] = (int32_t)keys[i];
427  }
428}
429
430#pragma clang diagnostic pop
431
432@end
433