• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GPBExtensionInternals.h"
32
33#import <objc/runtime.h>
34
35#import "GPBCodedInputStream_PackagePrivate.h"
36#import "GPBCodedOutputStream_PackagePrivate.h"
37#import "GPBDescriptor_PackagePrivate.h"
38#import "GPBMessage_PackagePrivate.h"
39#import "GPBUtilities_PackagePrivate.h"
40
41static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
42                                        GPBCodedInputStream *input,
43                                        GPBExtensionRegistry *extensionRegistry,
44                                        GPBMessage *existingValue)
45    __attribute__((ns_returns_retained));
46
47GPB_INLINE size_t DataTypeSize(GPBDataType dataType) {
48#pragma clang diagnostic push
49#pragma clang diagnostic ignored "-Wswitch-enum"
50  switch (dataType) {
51    case GPBDataTypeBool:
52      return 1;
53    case GPBDataTypeFixed32:
54    case GPBDataTypeSFixed32:
55    case GPBDataTypeFloat:
56      return 4;
57    case GPBDataTypeFixed64:
58    case GPBDataTypeSFixed64:
59    case GPBDataTypeDouble:
60      return 8;
61    default:
62      return 0;
63  }
64#pragma clang diagnostic pop
65}
66
67static size_t ComputePBSerializedSizeNoTagOfObject(GPBDataType dataType, id object) {
68#define FIELD_CASE(TYPE, ACCESSOR)                                     \
69  case GPBDataType##TYPE:                                              \
70    return GPBCompute##TYPE##SizeNoTag([(NSNumber *)object ACCESSOR]);
71#define FIELD_CASE2(TYPE)                                              \
72  case GPBDataType##TYPE:                                              \
73    return GPBCompute##TYPE##SizeNoTag(object);
74  switch (dataType) {
75    FIELD_CASE(Bool, boolValue)
76    FIELD_CASE(Float, floatValue)
77    FIELD_CASE(Double, doubleValue)
78    FIELD_CASE(Int32, intValue)
79    FIELD_CASE(SFixed32, intValue)
80    FIELD_CASE(SInt32, intValue)
81    FIELD_CASE(Enum, intValue)
82    FIELD_CASE(Int64, longLongValue)
83    FIELD_CASE(SInt64, longLongValue)
84    FIELD_CASE(SFixed64, longLongValue)
85    FIELD_CASE(UInt32, unsignedIntValue)
86    FIELD_CASE(Fixed32, unsignedIntValue)
87    FIELD_CASE(UInt64, unsignedLongLongValue)
88    FIELD_CASE(Fixed64, unsignedLongLongValue)
89    FIELD_CASE2(Bytes)
90    FIELD_CASE2(String)
91    FIELD_CASE2(Message)
92    FIELD_CASE2(Group)
93  }
94#undef FIELD_CASE
95#undef FIELD_CASE2
96}
97
98static size_t ComputeSerializedSizeIncludingTagOfObject(
99    GPBExtensionDescription *description, id object) {
100#define FIELD_CASE(TYPE, ACCESSOR)                                   \
101  case GPBDataType##TYPE:                                            \
102    return GPBCompute##TYPE##Size(description->fieldNumber,          \
103                                  [(NSNumber *)object ACCESSOR]);
104#define FIELD_CASE2(TYPE)                                            \
105  case GPBDataType##TYPE:                                            \
106    return GPBCompute##TYPE##Size(description->fieldNumber, object);
107  switch (description->dataType) {
108    FIELD_CASE(Bool, boolValue)
109    FIELD_CASE(Float, floatValue)
110    FIELD_CASE(Double, doubleValue)
111    FIELD_CASE(Int32, intValue)
112    FIELD_CASE(SFixed32, intValue)
113    FIELD_CASE(SInt32, intValue)
114    FIELD_CASE(Enum, intValue)
115    FIELD_CASE(Int64, longLongValue)
116    FIELD_CASE(SInt64, longLongValue)
117    FIELD_CASE(SFixed64, longLongValue)
118    FIELD_CASE(UInt32, unsignedIntValue)
119    FIELD_CASE(Fixed32, unsignedIntValue)
120    FIELD_CASE(UInt64, unsignedLongLongValue)
121    FIELD_CASE(Fixed64, unsignedLongLongValue)
122    FIELD_CASE2(Bytes)
123    FIELD_CASE2(String)
124    FIELD_CASE2(Group)
125    case GPBDataTypeMessage:
126      if (GPBExtensionIsWireFormat(description)) {
127        return GPBComputeMessageSetExtensionSize(description->fieldNumber,
128                                                 object);
129      } else {
130        return GPBComputeMessageSize(description->fieldNumber, object);
131      }
132  }
133#undef FIELD_CASE
134#undef FIELD_CASE2
135}
136
137static size_t ComputeSerializedSizeIncludingTagOfArray(
138    GPBExtensionDescription *description, NSArray *values) {
139  if (GPBExtensionIsPacked(description)) {
140    size_t size = 0;
141    size_t typeSize = DataTypeSize(description->dataType);
142    if (typeSize != 0) {
143      size = values.count * typeSize;
144    } else {
145      for (id value in values) {
146        size +=
147            ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
148      }
149    }
150    return size + GPBComputeTagSize(description->fieldNumber) +
151           GPBComputeRawVarint32SizeForInteger(size);
152  } else {
153    size_t size = 0;
154    for (id value in values) {
155      size += ComputeSerializedSizeIncludingTagOfObject(description, value);
156    }
157    return size;
158  }
159}
160
161static void WriteObjectIncludingTagToCodedOutputStream(
162    id object, GPBExtensionDescription *description,
163    GPBCodedOutputStream *output) {
164#define FIELD_CASE(TYPE, ACCESSOR)                      \
165  case GPBDataType##TYPE:                               \
166    [output write##TYPE:description->fieldNumber        \
167                  value:[(NSNumber *)object ACCESSOR]]; \
168    return;
169#define FIELD_CASE2(TYPE)                                       \
170  case GPBDataType##TYPE:                                       \
171    [output write##TYPE:description->fieldNumber value:object]; \
172    return;
173  switch (description->dataType) {
174    FIELD_CASE(Bool, boolValue)
175    FIELD_CASE(Float, floatValue)
176    FIELD_CASE(Double, doubleValue)
177    FIELD_CASE(Int32, intValue)
178    FIELD_CASE(SFixed32, intValue)
179    FIELD_CASE(SInt32, intValue)
180    FIELD_CASE(Enum, intValue)
181    FIELD_CASE(Int64, longLongValue)
182    FIELD_CASE(SInt64, longLongValue)
183    FIELD_CASE(SFixed64, longLongValue)
184    FIELD_CASE(UInt32, unsignedIntValue)
185    FIELD_CASE(Fixed32, unsignedIntValue)
186    FIELD_CASE(UInt64, unsignedLongLongValue)
187    FIELD_CASE(Fixed64, unsignedLongLongValue)
188    FIELD_CASE2(Bytes)
189    FIELD_CASE2(String)
190    FIELD_CASE2(Group)
191    case GPBDataTypeMessage:
192      if (GPBExtensionIsWireFormat(description)) {
193        [output writeMessageSetExtension:description->fieldNumber value:object];
194      } else {
195        [output writeMessage:description->fieldNumber value:object];
196      }
197      return;
198  }
199#undef FIELD_CASE
200#undef FIELD_CASE2
201}
202
203static void WriteObjectNoTagToCodedOutputStream(
204    id object, GPBExtensionDescription *description,
205    GPBCodedOutputStream *output) {
206#define FIELD_CASE(TYPE, ACCESSOR)                             \
207  case GPBDataType##TYPE:                                      \
208    [output write##TYPE##NoTag:[(NSNumber *)object ACCESSOR]]; \
209    return;
210#define FIELD_CASE2(TYPE)               \
211  case GPBDataType##TYPE:               \
212    [output write##TYPE##NoTag:object]; \
213    return;
214  switch (description->dataType) {
215    FIELD_CASE(Bool, boolValue)
216    FIELD_CASE(Float, floatValue)
217    FIELD_CASE(Double, doubleValue)
218    FIELD_CASE(Int32, intValue)
219    FIELD_CASE(SFixed32, intValue)
220    FIELD_CASE(SInt32, intValue)
221    FIELD_CASE(Enum, intValue)
222    FIELD_CASE(Int64, longLongValue)
223    FIELD_CASE(SInt64, longLongValue)
224    FIELD_CASE(SFixed64, longLongValue)
225    FIELD_CASE(UInt32, unsignedIntValue)
226    FIELD_CASE(Fixed32, unsignedIntValue)
227    FIELD_CASE(UInt64, unsignedLongLongValue)
228    FIELD_CASE(Fixed64, unsignedLongLongValue)
229    FIELD_CASE2(Bytes)
230    FIELD_CASE2(String)
231    FIELD_CASE2(Message)
232    case GPBDataTypeGroup:
233      [output writeGroupNoTag:description->fieldNumber value:object];
234      return;
235  }
236#undef FIELD_CASE
237#undef FIELD_CASE2
238}
239
240static void WriteArrayIncludingTagsToCodedOutputStream(
241    NSArray *values, GPBExtensionDescription *description,
242    GPBCodedOutputStream *output) {
243  if (GPBExtensionIsPacked(description)) {
244    [output writeTag:description->fieldNumber
245              format:GPBWireFormatLengthDelimited];
246    size_t dataSize = 0;
247    size_t typeSize = DataTypeSize(description->dataType);
248    if (typeSize != 0) {
249      dataSize = values.count * typeSize;
250    } else {
251      for (id value in values) {
252        dataSize +=
253            ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
254      }
255    }
256    [output writeRawVarintSizeTAs32:dataSize];
257    for (id value in values) {
258      WriteObjectNoTagToCodedOutputStream(value, description, output);
259    }
260  } else {
261    for (id value in values) {
262      WriteObjectIncludingTagToCodedOutputStream(value, description, output);
263    }
264  }
265}
266
267// Direct access is use for speed, to avoid even internally declaring things
268// read/write, etc. The warning is enabled in the project to ensure code calling
269// protos can turn on -Wdirect-ivar-access without issues.
270#pragma clang diagnostic push
271#pragma clang diagnostic ignored "-Wdirect-ivar-access"
272
273void GPBExtensionMergeFromInputStream(GPBExtensionDescriptor *extension,
274                                      BOOL isPackedOnStream,
275                                      GPBCodedInputStream *input,
276                                      GPBExtensionRegistry *extensionRegistry,
277                                      GPBMessage *message) {
278  GPBExtensionDescription *description = extension->description_;
279  GPBCodedInputStreamState *state = &input->state_;
280  if (isPackedOnStream) {
281    NSCAssert(GPBExtensionIsRepeated(description),
282              @"How was it packed if it isn't repeated?");
283    int32_t length = GPBCodedInputStreamReadInt32(state);
284    size_t limit = GPBCodedInputStreamPushLimit(state, length);
285    while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
286      id value = NewSingleValueFromInputStream(extension,
287                                               input,
288                                               extensionRegistry,
289                                               nil);
290      [message addExtension:extension value:value];
291      [value release];
292    }
293    GPBCodedInputStreamPopLimit(state, limit);
294  } else {
295    id existingValue = nil;
296    BOOL isRepeated = GPBExtensionIsRepeated(description);
297    if (!isRepeated && GPBDataTypeIsMessage(description->dataType)) {
298      existingValue = [message getExistingExtension:extension];
299    }
300    id value = NewSingleValueFromInputStream(extension,
301                                             input,
302                                             extensionRegistry,
303                                             existingValue);
304    if (isRepeated) {
305      [message addExtension:extension value:value];
306    } else {
307      [message setExtension:extension value:value];
308    }
309    [value release];
310  }
311}
312
313void GPBWriteExtensionValueToOutputStream(GPBExtensionDescriptor *extension,
314                                          id value,
315                                          GPBCodedOutputStream *output) {
316  GPBExtensionDescription *description = extension->description_;
317  if (GPBExtensionIsRepeated(description)) {
318    WriteArrayIncludingTagsToCodedOutputStream(value, description, output);
319  } else {
320    WriteObjectIncludingTagToCodedOutputStream(value, description, output);
321  }
322}
323
324size_t GPBComputeExtensionSerializedSizeIncludingTag(
325    GPBExtensionDescriptor *extension, id value) {
326  GPBExtensionDescription *description = extension->description_;
327  if (GPBExtensionIsRepeated(description)) {
328    return ComputeSerializedSizeIncludingTagOfArray(description, value);
329  } else {
330    return ComputeSerializedSizeIncludingTagOfObject(description, value);
331  }
332}
333
334// Note that this returns a retained value intentionally.
335static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
336                                        GPBCodedInputStream *input,
337                                        GPBExtensionRegistry *extensionRegistry,
338                                        GPBMessage *existingValue) {
339  GPBExtensionDescription *description = extension->description_;
340  GPBCodedInputStreamState *state = &input->state_;
341  switch (description->dataType) {
342    case GPBDataTypeBool:     return [[NSNumber alloc] initWithBool:GPBCodedInputStreamReadBool(state)];
343    case GPBDataTypeFixed32:  return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadFixed32(state)];
344    case GPBDataTypeSFixed32: return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSFixed32(state)];
345    case GPBDataTypeFloat:    return [[NSNumber alloc] initWithFloat:GPBCodedInputStreamReadFloat(state)];
346    case GPBDataTypeFixed64:  return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadFixed64(state)];
347    case GPBDataTypeSFixed64: return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSFixed64(state)];
348    case GPBDataTypeDouble:   return [[NSNumber alloc] initWithDouble:GPBCodedInputStreamReadDouble(state)];
349    case GPBDataTypeInt32:    return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadInt32(state)];
350    case GPBDataTypeInt64:    return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadInt64(state)];
351    case GPBDataTypeSInt32:   return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSInt32(state)];
352    case GPBDataTypeSInt64:   return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSInt64(state)];
353    case GPBDataTypeUInt32:   return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadUInt32(state)];
354    case GPBDataTypeUInt64:   return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadUInt64(state)];
355    case GPBDataTypeBytes:    return GPBCodedInputStreamReadRetainedBytes(state);
356    case GPBDataTypeString:   return GPBCodedInputStreamReadRetainedString(state);
357    case GPBDataTypeEnum:     return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadEnum(state)];
358    case GPBDataTypeGroup:
359    case GPBDataTypeMessage: {
360      GPBMessage *message;
361      if (existingValue) {
362        message = [existingValue retain];
363      } else {
364        GPBDescriptor *decriptor = [extension.msgClass descriptor];
365        message = [[decriptor.messageClass alloc] init];
366      }
367
368      if (description->dataType == GPBDataTypeGroup) {
369        [input readGroup:description->fieldNumber
370                 message:message
371            extensionRegistry:extensionRegistry];
372      } else {
373        // description->dataType == GPBDataTypeMessage
374        if (GPBExtensionIsWireFormat(description)) {
375          // For MessageSet fields the message length will have already been
376          // read.
377          [message mergeFromCodedInputStream:input
378                           extensionRegistry:extensionRegistry];
379        } else {
380          [input readMessage:message extensionRegistry:extensionRegistry];
381        }
382      }
383
384      return message;
385    }
386  }
387
388  return nil;
389}
390
391#pragma clang diagnostic pop
392