1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 #import "GPBExtensionInternals.h"
33 #import <objc/runtime.h>
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"
41 static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
42 GPBCodedInputStream *input,
43 GPBExtensionRegistry *extensionRegistry,
44 GPBMessage *existingValue)
45 __attribute__((ns_returns_retained));
47 GPB_INLINE size_t DataTypeSize(GPBDataType dataType) {
48 #pragma clang diagnostic push
49 #pragma clang diagnostic ignored "-Wswitch-enum"
53 case GPBDataTypeFixed32:
54 case GPBDataTypeSFixed32:
55 case GPBDataTypeFloat:
57 case GPBDataTypeFixed64:
58 case GPBDataTypeSFixed64:
59 case GPBDataTypeDouble:
64 #pragma clang diagnostic pop
67 static 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);
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)
98 static 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)
125 case GPBDataTypeMessage:
126 if (GPBExtensionIsWireFormat(description)) {
127 return GPBComputeMessageSetExtensionSize(description->fieldNumber,
130 return GPBComputeMessageSize(description->fieldNumber, object);
137 static size_t ComputeSerializedSizeIncludingTagOfArray(
138 GPBExtensionDescription *description, NSArray *values) {
139 if (GPBExtensionIsPacked(description)) {
141 size_t typeSize = DataTypeSize(description->dataType);
143 size = values.count * typeSize;
145 for (id value in values) {
147 ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
150 return size + GPBComputeTagSize(description->fieldNumber) +
151 GPBComputeRawVarint32SizeForInteger(size);
154 for (id value in values) {
155 size += ComputeSerializedSizeIncludingTagOfObject(description, value);
161 static 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]]; \
169 #define FIELD_CASE2(TYPE) \
170 case GPBDataType##TYPE: \
171 [output write##TYPE:description->fieldNumber value:object]; \
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)
191 case GPBDataTypeMessage:
192 if (GPBExtensionIsWireFormat(description)) {
193 [output writeMessageSetExtension:description->fieldNumber value:object];
195 [output writeMessage:description->fieldNumber value:object];
203 static 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]]; \
210 #define FIELD_CASE2(TYPE) \
211 case GPBDataType##TYPE: \
212 [output write##TYPE##NoTag:object]; \
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)
232 case GPBDataTypeGroup:
233 [output writeGroupNoTag:description->fieldNumber value:object];
240 static void WriteArrayIncludingTagsToCodedOutputStream(
241 NSArray *values, GPBExtensionDescription *description,
242 GPBCodedOutputStream *output) {
243 if (GPBExtensionIsPacked(description)) {
244 [output writeTag:description->fieldNumber
245 format:GPBWireFormatLengthDelimited];
247 size_t typeSize = DataTypeSize(description->dataType);
249 dataSize = values.count * typeSize;
251 for (id value in values) {
253 ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
256 [output writeRawVarintSizeTAs32:dataSize];
257 for (id value in values) {
258 WriteObjectNoTagToCodedOutputStream(value, description, output);
261 for (id value in values) {
262 WriteObjectIncludingTagToCodedOutputStream(value, description, output);
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"
273 void 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,
290 [message addExtension:extension value:value];
293 GPBCodedInputStreamPopLimit(state, limit);
295 id existingValue = nil;
296 BOOL isRepeated = GPBExtensionIsRepeated(description);
297 if (!isRepeated && GPBDataTypeIsMessage(description->dataType)) {
298 existingValue = [message getExistingExtension:extension];
300 id value = NewSingleValueFromInputStream(extension,
305 [message addExtension:extension value:value];
307 [message setExtension:extension value:value];
313 void GPBWriteExtensionValueToOutputStream(GPBExtensionDescriptor *extension,
315 GPBCodedOutputStream *output) {
316 GPBExtensionDescription *description = extension->description_;
317 if (GPBExtensionIsRepeated(description)) {
318 WriteArrayIncludingTagsToCodedOutputStream(value, description, output);
320 WriteObjectIncludingTagToCodedOutputStream(value, description, output);
324 size_t GPBComputeExtensionSerializedSizeIncludingTag(
325 GPBExtensionDescriptor *extension, id value) {
326 GPBExtensionDescription *description = extension->description_;
327 if (GPBExtensionIsRepeated(description)) {
328 return ComputeSerializedSizeIncludingTagOfArray(description, value);
330 return ComputeSerializedSizeIncludingTagOfObject(description, value);
334 // Note that this returns a retained value intentionally.
335 static 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: {
362 message = [existingValue retain];
364 GPBDescriptor *decriptor = [extension.msgClass descriptor];
365 message = [[decriptor.messageClass alloc] init];
368 if (description->dataType == GPBDataTypeGroup) {
369 [input readGroup:description->fieldNumber
371 extensionRegistry:extensionRegistry];
373 // description->dataType == GPBDataTypeMessage
374 if (GPBExtensionIsWireFormat(description)) {
375 // For MessageSet fields the message length will have already been
377 [message mergeFromCodedInputStream:input
378 extensionRegistry:extensionRegistry];
380 [input readMessage:message extensionRegistry:extensionRegistry];
391 #pragma clang diagnostic pop