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 "GPBCodedInputStream_PackagePrivate.h" 32 33#import "GPBDictionary_PackagePrivate.h" 34#import "GPBMessage_PackagePrivate.h" 35#import "GPBUnknownFieldSet_PackagePrivate.h" 36#import "GPBUtilities_PackagePrivate.h" 37#import "GPBWireFormat.h" 38 39NSString *const GPBCodedInputStreamException = 40 GPBNSStringifySymbol(GPBCodedInputStreamException); 41 42NSString *const GPBCodedInputStreamUnderlyingErrorKey = 43 GPBNSStringifySymbol(GPBCodedInputStreamUnderlyingErrorKey); 44 45NSString *const GPBCodedInputStreamErrorDomain = 46 GPBNSStringifySymbol(GPBCodedInputStreamErrorDomain); 47 48// Matching: 49// https://github.com/protocolbuffers/protobuf/blob/master/java/core/src/main/java/com/google/protobuf/CodedInputStream.java#L62 50// private static final int DEFAULT_RECURSION_LIMIT = 100; 51// https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/io/coded_stream.cc#L86 52// int CodedInputStream::default_recursion_limit_ = 100; 53static const NSUInteger kDefaultRecursionLimit = 100; 54 55static void RaiseException(NSInteger code, NSString *reason) { 56 NSDictionary *errorInfo = nil; 57 if ([reason length]) { 58 errorInfo = @{ GPBErrorReasonKey: reason }; 59 } 60 NSError *error = [NSError errorWithDomain:GPBCodedInputStreamErrorDomain 61 code:code 62 userInfo:errorInfo]; 63 64 NSDictionary *exceptionInfo = 65 @{ GPBCodedInputStreamUnderlyingErrorKey: error }; 66 [[NSException exceptionWithName:GPBCodedInputStreamException 67 reason:reason 68 userInfo:exceptionInfo] raise]; 69} 70 71static void CheckRecursionLimit(GPBCodedInputStreamState *state) { 72 if (state->recursionDepth >= kDefaultRecursionLimit) { 73 RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil); 74 } 75} 76 77static void CheckSize(GPBCodedInputStreamState *state, size_t size) { 78 size_t newSize = state->bufferPos + size; 79 if (newSize > state->bufferSize) { 80 RaiseException(GPBCodedInputStreamErrorInvalidSize, nil); 81 } 82 if (newSize > state->currentLimit) { 83 // Fast forward to end of currentLimit; 84 state->bufferPos = state->currentLimit; 85 RaiseException(GPBCodedInputStreamErrorSubsectionLimitReached, nil); 86 } 87} 88 89static int8_t ReadRawByte(GPBCodedInputStreamState *state) { 90 CheckSize(state, sizeof(int8_t)); 91 return ((int8_t *)state->bytes)[state->bufferPos++]; 92} 93 94static int32_t ReadRawLittleEndian32(GPBCodedInputStreamState *state) { 95 CheckSize(state, sizeof(int32_t)); 96 // Not using OSReadLittleInt32 because it has undocumented dependency 97 // on reads being aligned. 98 int32_t value; 99 memcpy(&value, state->bytes + state->bufferPos, sizeof(int32_t)); 100 value = OSSwapLittleToHostInt32(value); 101 state->bufferPos += sizeof(int32_t); 102 return value; 103} 104 105static int64_t ReadRawLittleEndian64(GPBCodedInputStreamState *state) { 106 CheckSize(state, sizeof(int64_t)); 107 // Not using OSReadLittleInt64 because it has undocumented dependency 108 // on reads being aligned. 109 int64_t value; 110 memcpy(&value, state->bytes + state->bufferPos, sizeof(int64_t)); 111 value = OSSwapLittleToHostInt64(value); 112 state->bufferPos += sizeof(int64_t); 113 return value; 114} 115 116static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) { 117 int32_t shift = 0; 118 int64_t result = 0; 119 while (shift < 64) { 120 int8_t b = ReadRawByte(state); 121 result |= (int64_t)((uint64_t)(b & 0x7F) << shift); 122 if ((b & 0x80) == 0) { 123 return result; 124 } 125 shift += 7; 126 } 127 RaiseException(GPBCodedInputStreamErrorInvalidVarInt, @"Invalid VarInt64"); 128 return 0; 129} 130 131static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) { 132 return (int32_t)ReadRawVarint64(state); 133} 134 135static void SkipRawData(GPBCodedInputStreamState *state, size_t size) { 136 CheckSize(state, size); 137 state->bufferPos += size; 138} 139 140double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state) { 141 int64_t value = ReadRawLittleEndian64(state); 142 return GPBConvertInt64ToDouble(value); 143} 144 145float GPBCodedInputStreamReadFloat(GPBCodedInputStreamState *state) { 146 int32_t value = ReadRawLittleEndian32(state); 147 return GPBConvertInt32ToFloat(value); 148} 149 150uint64_t GPBCodedInputStreamReadUInt64(GPBCodedInputStreamState *state) { 151 uint64_t value = ReadRawVarint64(state); 152 return value; 153} 154 155uint32_t GPBCodedInputStreamReadUInt32(GPBCodedInputStreamState *state) { 156 uint32_t value = ReadRawVarint32(state); 157 return value; 158} 159 160int64_t GPBCodedInputStreamReadInt64(GPBCodedInputStreamState *state) { 161 int64_t value = ReadRawVarint64(state); 162 return value; 163} 164 165int32_t GPBCodedInputStreamReadInt32(GPBCodedInputStreamState *state) { 166 int32_t value = ReadRawVarint32(state); 167 return value; 168} 169 170uint64_t GPBCodedInputStreamReadFixed64(GPBCodedInputStreamState *state) { 171 uint64_t value = ReadRawLittleEndian64(state); 172 return value; 173} 174 175uint32_t GPBCodedInputStreamReadFixed32(GPBCodedInputStreamState *state) { 176 uint32_t value = ReadRawLittleEndian32(state); 177 return value; 178} 179 180int32_t GPBCodedInputStreamReadEnum(GPBCodedInputStreamState *state) { 181 int32_t value = ReadRawVarint32(state); 182 return value; 183} 184 185int32_t GPBCodedInputStreamReadSFixed32(GPBCodedInputStreamState *state) { 186 int32_t value = ReadRawLittleEndian32(state); 187 return value; 188} 189 190int64_t GPBCodedInputStreamReadSFixed64(GPBCodedInputStreamState *state) { 191 int64_t value = ReadRawLittleEndian64(state); 192 return value; 193} 194 195int32_t GPBCodedInputStreamReadSInt32(GPBCodedInputStreamState *state) { 196 int32_t value = GPBDecodeZigZag32(ReadRawVarint32(state)); 197 return value; 198} 199 200int64_t GPBCodedInputStreamReadSInt64(GPBCodedInputStreamState *state) { 201 int64_t value = GPBDecodeZigZag64(ReadRawVarint64(state)); 202 return value; 203} 204 205BOOL GPBCodedInputStreamReadBool(GPBCodedInputStreamState *state) { 206 return ReadRawVarint64(state) != 0; 207} 208 209int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) { 210 if (GPBCodedInputStreamIsAtEnd(state)) { 211 state->lastTag = 0; 212 return 0; 213 } 214 215 state->lastTag = ReadRawVarint32(state); 216 // Tags have to include a valid wireformat. 217 if (!GPBWireFormatIsValidTag(state->lastTag)) { 218 RaiseException(GPBCodedInputStreamErrorInvalidTag, 219 @"Invalid wireformat in tag."); 220 } 221 // Zero is not a valid field number. 222 if (GPBWireFormatGetTagFieldNumber(state->lastTag) == 0) { 223 RaiseException(GPBCodedInputStreamErrorInvalidTag, 224 @"A zero field number on the wire is invalid."); 225 } 226 return state->lastTag; 227} 228 229NSString *GPBCodedInputStreamReadRetainedString( 230 GPBCodedInputStreamState *state) { 231 int32_t size = ReadRawVarint32(state); 232 NSString *result; 233 if (size == 0) { 234 result = @""; 235 } else { 236 CheckSize(state, size); 237 result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos] 238 length:size 239 encoding:NSUTF8StringEncoding]; 240 state->bufferPos += size; 241 if (!result) { 242#ifdef DEBUG 243 // https://developers.google.com/protocol-buffers/docs/proto#scalar 244 NSLog(@"UTF-8 failure, is some field type 'string' when it should be " 245 @"'bytes'?"); 246#endif 247 RaiseException(GPBCodedInputStreamErrorInvalidUTF8, nil); 248 } 249 } 250 return result; 251} 252 253NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) { 254 int32_t size = ReadRawVarint32(state); 255 if (size < 0) return nil; 256 CheckSize(state, size); 257 NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos 258 length:size]; 259 state->bufferPos += size; 260 return result; 261} 262 263NSData *GPBCodedInputStreamReadRetainedBytesNoCopy( 264 GPBCodedInputStreamState *state) { 265 int32_t size = ReadRawVarint32(state); 266 if (size < 0) return nil; 267 CheckSize(state, size); 268 // Cast is safe because freeWhenDone is NO. 269 NSData *result = [[NSData alloc] 270 initWithBytesNoCopy:(void *)(state->bytes + state->bufferPos) 271 length:size 272 freeWhenDone:NO]; 273 state->bufferPos += size; 274 return result; 275} 276 277size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state, 278 size_t byteLimit) { 279 byteLimit += state->bufferPos; 280 size_t oldLimit = state->currentLimit; 281 if (byteLimit > oldLimit) { 282 RaiseException(GPBCodedInputStreamErrorInvalidSubsectionLimit, nil); 283 } 284 state->currentLimit = byteLimit; 285 return oldLimit; 286} 287 288void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state, 289 size_t oldLimit) { 290 state->currentLimit = oldLimit; 291} 292 293size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state) { 294 return state->currentLimit - state->bufferPos; 295} 296 297BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) { 298 return (state->bufferPos == state->bufferSize) || 299 (state->bufferPos == state->currentLimit); 300} 301 302void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, 303 int32_t value) { 304 if (state->lastTag != value) { 305 RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Unexpected tag read"); 306 } 307} 308 309@implementation GPBCodedInputStream 310 311+ (instancetype)streamWithData:(NSData *)data { 312 return [[[self alloc] initWithData:data] autorelease]; 313} 314 315- (instancetype)initWithData:(NSData *)data { 316 if ((self = [super init])) { 317#ifdef DEBUG 318 NSCAssert([self class] == [GPBCodedInputStream class], 319 @"Subclassing of GPBCodedInputStream is not allowed."); 320#endif 321 buffer_ = [data retain]; 322 state_.bytes = (const uint8_t *)[data bytes]; 323 state_.bufferSize = [data length]; 324 state_.currentLimit = state_.bufferSize; 325 } 326 return self; 327} 328 329- (void)dealloc { 330 [buffer_ release]; 331 [super dealloc]; 332} 333 334// Direct access is use for speed, to avoid even internally declaring things 335// read/write, etc. The warning is enabled in the project to ensure code calling 336// protos can turn on -Wdirect-ivar-access without issues. 337#pragma clang diagnostic push 338#pragma clang diagnostic ignored "-Wdirect-ivar-access" 339 340- (int32_t)readTag { 341 return GPBCodedInputStreamReadTag(&state_); 342} 343 344- (void)checkLastTagWas:(int32_t)value { 345 GPBCodedInputStreamCheckLastTagWas(&state_, value); 346} 347 348- (BOOL)skipField:(int32_t)tag { 349 NSAssert(GPBWireFormatIsValidTag(tag), @"Invalid tag"); 350 switch (GPBWireFormatGetTagWireType(tag)) { 351 case GPBWireFormatVarint: 352 GPBCodedInputStreamReadInt32(&state_); 353 return YES; 354 case GPBWireFormatFixed64: 355 SkipRawData(&state_, sizeof(int64_t)); 356 return YES; 357 case GPBWireFormatLengthDelimited: 358 SkipRawData(&state_, ReadRawVarint32(&state_)); 359 return YES; 360 case GPBWireFormatStartGroup: 361 [self skipMessage]; 362 GPBCodedInputStreamCheckLastTagWas( 363 &state_, GPBWireFormatMakeTag(GPBWireFormatGetTagFieldNumber(tag), 364 GPBWireFormatEndGroup)); 365 return YES; 366 case GPBWireFormatEndGroup: 367 return NO; 368 case GPBWireFormatFixed32: 369 SkipRawData(&state_, sizeof(int32_t)); 370 return YES; 371 } 372} 373 374- (void)skipMessage { 375 while (YES) { 376 int32_t tag = GPBCodedInputStreamReadTag(&state_); 377 if (tag == 0 || ![self skipField:tag]) { 378 return; 379 } 380 } 381} 382 383- (BOOL)isAtEnd { 384 return GPBCodedInputStreamIsAtEnd(&state_); 385} 386 387- (size_t)position { 388 return state_.bufferPos; 389} 390 391- (size_t)pushLimit:(size_t)byteLimit { 392 return GPBCodedInputStreamPushLimit(&state_, byteLimit); 393} 394 395- (void)popLimit:(size_t)oldLimit { 396 GPBCodedInputStreamPopLimit(&state_, oldLimit); 397} 398 399- (double)readDouble { 400 return GPBCodedInputStreamReadDouble(&state_); 401} 402 403- (float)readFloat { 404 return GPBCodedInputStreamReadFloat(&state_); 405} 406 407- (uint64_t)readUInt64 { 408 return GPBCodedInputStreamReadUInt64(&state_); 409} 410 411- (int64_t)readInt64 { 412 return GPBCodedInputStreamReadInt64(&state_); 413} 414 415- (int32_t)readInt32 { 416 return GPBCodedInputStreamReadInt32(&state_); 417} 418 419- (uint64_t)readFixed64 { 420 return GPBCodedInputStreamReadFixed64(&state_); 421} 422 423- (uint32_t)readFixed32 { 424 return GPBCodedInputStreamReadFixed32(&state_); 425} 426 427- (BOOL)readBool { 428 return GPBCodedInputStreamReadBool(&state_); 429} 430 431- (NSString *)readString { 432 return [GPBCodedInputStreamReadRetainedString(&state_) autorelease]; 433} 434 435- (void)readGroup:(int32_t)fieldNumber 436 message:(GPBMessage *)message 437 extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { 438 CheckRecursionLimit(&state_); 439 ++state_.recursionDepth; 440 [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry]; 441 GPBCodedInputStreamCheckLastTagWas( 442 &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)); 443 --state_.recursionDepth; 444} 445 446- (void)readUnknownGroup:(int32_t)fieldNumber 447 message:(GPBUnknownFieldSet *)message { 448 CheckRecursionLimit(&state_); 449 ++state_.recursionDepth; 450 [message mergeFromCodedInputStream:self]; 451 GPBCodedInputStreamCheckLastTagWas( 452 &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)); 453 --state_.recursionDepth; 454} 455 456- (void)readMessage:(GPBMessage *)message 457 extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { 458 CheckRecursionLimit(&state_); 459 int32_t length = ReadRawVarint32(&state_); 460 size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length); 461 ++state_.recursionDepth; 462 [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry]; 463 GPBCodedInputStreamCheckLastTagWas(&state_, 0); 464 --state_.recursionDepth; 465 GPBCodedInputStreamPopLimit(&state_, oldLimit); 466} 467 468- (void)readMapEntry:(id)mapDictionary 469 extensionRegistry:(GPBExtensionRegistry *)extensionRegistry 470 field:(GPBFieldDescriptor *)field 471 parentMessage:(GPBMessage *)parentMessage { 472 CheckRecursionLimit(&state_); 473 int32_t length = ReadRawVarint32(&state_); 474 size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length); 475 ++state_.recursionDepth; 476 GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field, 477 parentMessage); 478 GPBCodedInputStreamCheckLastTagWas(&state_, 0); 479 --state_.recursionDepth; 480 GPBCodedInputStreamPopLimit(&state_, oldLimit); 481} 482 483- (NSData *)readBytes { 484 return [GPBCodedInputStreamReadRetainedBytes(&state_) autorelease]; 485} 486 487- (uint32_t)readUInt32 { 488 return GPBCodedInputStreamReadUInt32(&state_); 489} 490 491- (int32_t)readEnum { 492 return GPBCodedInputStreamReadEnum(&state_); 493} 494 495- (int32_t)readSFixed32 { 496 return GPBCodedInputStreamReadSFixed32(&state_); 497} 498 499- (int64_t)readSFixed64 { 500 return GPBCodedInputStreamReadSFixed64(&state_); 501} 502 503- (int32_t)readSInt32 { 504 return GPBCodedInputStreamReadSInt32(&state_); 505} 506 507- (int64_t)readSInt64 { 508 return GPBCodedInputStreamReadSInt64(&state_); 509} 510 511#pragma clang diagnostic pop 512 513@end 514