blob: 290c82a1bb8203a3bbe702ad4a3b525c40d967f1 [file] [log] [blame]
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001// 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"
Thomas Van Lenten36650a02016-03-07 12:07:03 -050036#import "GPBCodedOutputStream_PackagePrivate.h"
Thomas Van Lentend846b0b2015-06-08 16:24:57 -040037#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) {
Thomas Van Lentenc8a440d2016-05-25 13:46:00 -040048#pragma clang diagnostic push
49#pragma clang diagnostic ignored "-Wswitch-enum"
Thomas Van Lentend846b0b2015-06-08 16:24:57 -040050 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 }
Thomas Van Lentenc8a440d2016-05-25 13:46:00 -040064#pragma clang diagnostic pop
Thomas Van Lentend846b0b2015-06-08 16:24:57 -040065}
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
Thomas Van Lentenc8a440d2016-05-25 13:46:00 -0400267// 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
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400273void 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}
Thomas Van Lentenc8a440d2016-05-25 13:46:00 -0400390
391#pragma clang diagnostic pop