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