1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file or at 6// https://developers.google.com/open-source/licenses/bsd 7 8#import "GPBExtensionInternals.h" 9 10#import <objc/runtime.h> 11 12#import "GPBCodedInputStream.h" 13#import "GPBCodedInputStream_PackagePrivate.h" 14#import "GPBCodedOutputStream.h" 15#import "GPBCodedOutputStream_PackagePrivate.h" 16#import "GPBDescriptor.h" 17#import "GPBDescriptor_PackagePrivate.h" 18#import "GPBMessage.h" 19#import "GPBMessage_PackagePrivate.h" 20#import "GPBUtilities.h" 21#import "GPBUtilities_PackagePrivate.h" 22 23GPB_INLINE size_t DataTypeSize(GPBDataType dataType) { 24#pragma clang diagnostic push 25#pragma clang diagnostic ignored "-Wswitch-enum" 26 switch (dataType) { 27 case GPBDataTypeBool: 28 return 1; 29 case GPBDataTypeFixed32: 30 case GPBDataTypeSFixed32: 31 case GPBDataTypeFloat: 32 return 4; 33 case GPBDataTypeFixed64: 34 case GPBDataTypeSFixed64: 35 case GPBDataTypeDouble: 36 return 8; 37 default: 38 return 0; 39 } 40#pragma clang diagnostic pop 41} 42 43static size_t ComputePBSerializedSizeNoTagOfObject(GPBDataType dataType, id object) { 44#define FIELD_CASE(TYPE, ACCESSOR) \ 45 case GPBDataType##TYPE: \ 46 return GPBCompute##TYPE##SizeNoTag([(NSNumber *)object ACCESSOR]); 47#define FIELD_CASE2(TYPE) \ 48 case GPBDataType##TYPE: \ 49 return GPBCompute##TYPE##SizeNoTag(object); 50 switch (dataType) { 51 FIELD_CASE(Bool, boolValue) 52 FIELD_CASE(Float, floatValue) 53 FIELD_CASE(Double, doubleValue) 54 FIELD_CASE(Int32, intValue) 55 FIELD_CASE(SFixed32, intValue) 56 FIELD_CASE(SInt32, intValue) 57 FIELD_CASE(Enum, intValue) 58 FIELD_CASE(Int64, longLongValue) 59 FIELD_CASE(SInt64, longLongValue) 60 FIELD_CASE(SFixed64, longLongValue) 61 FIELD_CASE(UInt32, unsignedIntValue) 62 FIELD_CASE(Fixed32, unsignedIntValue) 63 FIELD_CASE(UInt64, unsignedLongLongValue) 64 FIELD_CASE(Fixed64, unsignedLongLongValue) 65 FIELD_CASE2(Bytes) 66 FIELD_CASE2(String) 67 FIELD_CASE2(Message) 68 FIELD_CASE2(Group) 69 } 70#undef FIELD_CASE 71#undef FIELD_CASE2 72} 73 74static size_t ComputeSerializedSizeIncludingTagOfObject(GPBExtensionDescription *description, 75 id object) { 76#define FIELD_CASE(TYPE, ACCESSOR) \ 77 case GPBDataType##TYPE: \ 78 return GPBCompute##TYPE##Size(description->fieldNumber, [(NSNumber *)object ACCESSOR]); 79#define FIELD_CASE2(TYPE) \ 80 case GPBDataType##TYPE: \ 81 return GPBCompute##TYPE##Size(description->fieldNumber, object); 82 switch (description->dataType) { 83 FIELD_CASE(Bool, boolValue) 84 FIELD_CASE(Float, floatValue) 85 FIELD_CASE(Double, doubleValue) 86 FIELD_CASE(Int32, intValue) 87 FIELD_CASE(SFixed32, intValue) 88 FIELD_CASE(SInt32, intValue) 89 FIELD_CASE(Enum, intValue) 90 FIELD_CASE(Int64, longLongValue) 91 FIELD_CASE(SInt64, longLongValue) 92 FIELD_CASE(SFixed64, longLongValue) 93 FIELD_CASE(UInt32, unsignedIntValue) 94 FIELD_CASE(Fixed32, unsignedIntValue) 95 FIELD_CASE(UInt64, unsignedLongLongValue) 96 FIELD_CASE(Fixed64, unsignedLongLongValue) 97 FIELD_CASE2(Bytes) 98 FIELD_CASE2(String) 99 FIELD_CASE2(Group) 100 case GPBDataTypeMessage: 101 if (GPBExtensionIsWireFormat(description)) { 102 return GPBComputeMessageSetExtensionSize(description->fieldNumber, object); 103 } else { 104 return GPBComputeMessageSize(description->fieldNumber, object); 105 } 106 } 107#undef FIELD_CASE 108#undef FIELD_CASE2 109} 110 111static size_t ComputeSerializedSizeIncludingTagOfArray(GPBExtensionDescription *description, 112 NSArray *values) { 113 if (GPBExtensionIsPacked(description)) { 114 size_t size = 0; 115 size_t typeSize = DataTypeSize(description->dataType); 116 if (typeSize != 0) { 117 size = values.count * typeSize; 118 } else { 119 for (id value in values) { 120 size += ComputePBSerializedSizeNoTagOfObject(description->dataType, value); 121 } 122 } 123 return size + GPBComputeTagSize(description->fieldNumber) + 124 GPBComputeRawVarint32SizeForInteger(size); 125 } else { 126 size_t size = 0; 127 for (id value in values) { 128 size += ComputeSerializedSizeIncludingTagOfObject(description, value); 129 } 130 return size; 131 } 132} 133 134static void WriteObjectIncludingTagToCodedOutputStream(id object, 135 GPBExtensionDescription *description, 136 GPBCodedOutputStream *output) { 137#define FIELD_CASE(TYPE, ACCESSOR) \ 138 case GPBDataType##TYPE: \ 139 [output write##TYPE:description->fieldNumber value:[(NSNumber *)object ACCESSOR]]; \ 140 return; 141#define FIELD_CASE2(TYPE) \ 142 case GPBDataType##TYPE: \ 143 [output write##TYPE:description->fieldNumber value:object]; \ 144 return; 145 switch (description->dataType) { 146 FIELD_CASE(Bool, boolValue) 147 FIELD_CASE(Float, floatValue) 148 FIELD_CASE(Double, doubleValue) 149 FIELD_CASE(Int32, intValue) 150 FIELD_CASE(SFixed32, intValue) 151 FIELD_CASE(SInt32, intValue) 152 FIELD_CASE(Enum, intValue) 153 FIELD_CASE(Int64, longLongValue) 154 FIELD_CASE(SInt64, longLongValue) 155 FIELD_CASE(SFixed64, longLongValue) 156 FIELD_CASE(UInt32, unsignedIntValue) 157 FIELD_CASE(Fixed32, unsignedIntValue) 158 FIELD_CASE(UInt64, unsignedLongLongValue) 159 FIELD_CASE(Fixed64, unsignedLongLongValue) 160 FIELD_CASE2(Bytes) 161 FIELD_CASE2(String) 162 FIELD_CASE2(Group) 163 case GPBDataTypeMessage: 164 if (GPBExtensionIsWireFormat(description)) { 165 [output writeMessageSetExtension:description->fieldNumber value:object]; 166 } else { 167 [output writeMessage:description->fieldNumber value:object]; 168 } 169 return; 170 } 171#undef FIELD_CASE 172#undef FIELD_CASE2 173} 174 175static void WriteObjectNoTagToCodedOutputStream(id object, GPBExtensionDescription *description, 176 GPBCodedOutputStream *output) { 177#define FIELD_CASE(TYPE, ACCESSOR) \ 178 case GPBDataType##TYPE: \ 179 [output write##TYPE##NoTag:[(NSNumber *)object ACCESSOR]]; \ 180 return; 181#define FIELD_CASE2(TYPE) \ 182 case GPBDataType##TYPE: \ 183 [output write##TYPE##NoTag:object]; \ 184 return; 185 switch (description->dataType) { 186 FIELD_CASE(Bool, boolValue) 187 FIELD_CASE(Float, floatValue) 188 FIELD_CASE(Double, doubleValue) 189 FIELD_CASE(Int32, intValue) 190 FIELD_CASE(SFixed32, intValue) 191 FIELD_CASE(SInt32, intValue) 192 FIELD_CASE(Enum, intValue) 193 FIELD_CASE(Int64, longLongValue) 194 FIELD_CASE(SInt64, longLongValue) 195 FIELD_CASE(SFixed64, longLongValue) 196 FIELD_CASE(UInt32, unsignedIntValue) 197 FIELD_CASE(Fixed32, unsignedIntValue) 198 FIELD_CASE(UInt64, unsignedLongLongValue) 199 FIELD_CASE(Fixed64, unsignedLongLongValue) 200 FIELD_CASE2(Bytes) 201 FIELD_CASE2(String) 202 FIELD_CASE2(Message) 203 case GPBDataTypeGroup: 204 [output writeGroupNoTag:description->fieldNumber value:object]; 205 return; 206 } 207#undef FIELD_CASE 208#undef FIELD_CASE2 209} 210 211static void WriteArrayIncludingTagsToCodedOutputStream(NSArray *values, 212 GPBExtensionDescription *description, 213 GPBCodedOutputStream *output) { 214 if (GPBExtensionIsPacked(description)) { 215 [output writeTag:description->fieldNumber format:GPBWireFormatLengthDelimited]; 216 size_t dataSize = 0; 217 size_t typeSize = DataTypeSize(description->dataType); 218 if (typeSize != 0) { 219 dataSize = values.count * typeSize; 220 } else { 221 for (id value in values) { 222 dataSize += ComputePBSerializedSizeNoTagOfObject(description->dataType, value); 223 } 224 } 225 [output writeRawVarintSizeTAs32:dataSize]; 226 for (id value in values) { 227 WriteObjectNoTagToCodedOutputStream(value, description, output); 228 } 229 } else { 230 for (id value in values) { 231 WriteObjectIncludingTagToCodedOutputStream(value, description, output); 232 } 233 } 234} 235 236// Direct access is use for speed, to avoid even internally declaring things 237// read/write, etc. The warning is enabled in the project to ensure code calling 238// protos can turn on -Wdirect-ivar-access without issues. 239#pragma clang diagnostic push 240#pragma clang diagnostic ignored "-Wdirect-ivar-access" 241 242void GPBWriteExtensionValueToOutputStream(GPBExtensionDescriptor *extension, id value, 243 GPBCodedOutputStream *output) { 244 GPBExtensionDescription *description = extension->description_; 245 if (GPBExtensionIsRepeated(description)) { 246 WriteArrayIncludingTagsToCodedOutputStream(value, description, output); 247 } else { 248 WriteObjectIncludingTagToCodedOutputStream(value, description, output); 249 } 250} 251 252size_t GPBComputeExtensionSerializedSizeIncludingTag(GPBExtensionDescriptor *extension, id value) { 253 GPBExtensionDescription *description = extension->description_; 254 if (GPBExtensionIsRepeated(description)) { 255 return ComputeSerializedSizeIncludingTagOfArray(description, value); 256 } else { 257 return ComputeSerializedSizeIncludingTagOfObject(description, value); 258 } 259} 260 261#pragma clang diagnostic pop 262