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