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