1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using System; 11 using System.Buffers; 12 using System.Buffers.Binary; 13 using System.Collections.Generic; 14 using System.IO; 15 using System.Runtime.CompilerServices; 16 using System.Runtime.InteropServices; 17 using System.Security; 18 19 namespace Google.Protobuf 20 { 21 /// <summary> 22 /// Primitives for parsing protobuf wire format. 23 /// </summary> 24 [SecuritySafeCritical] 25 internal static class ParsingPrimitives 26 { 27 private const int StackallocThreshold = 256; 28 29 /// <summary> 30 /// Reads a length for length-delimited data. 31 /// </summary> 32 /// <remarks> 33 /// This is internally just reading a varint, but this method exists 34 /// to make the calling code clearer. 35 /// </remarks> 36 [MethodImpl(MethodImplOptions.AggressiveInlining)] ParseLength(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)37 public static int ParseLength(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 38 { 39 return (int)ParseRawVarint32(ref buffer, ref state); 40 } 41 42 /// <summary> 43 /// Parses the next tag. 44 /// If the end of logical stream was reached, an invalid tag of 0 is returned. 45 /// </summary> ParseTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)46 public static uint ParseTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 47 { 48 // The "nextTag" logic is there only as an optimization for reading non-packed repeated / map 49 // fields and is strictly speaking not necessary. 50 // TODO: look into simplifying the ParseTag logic. 51 if (state.hasNextTag) 52 { 53 state.lastTag = state.nextTag; 54 state.hasNextTag = false; 55 return state.lastTag; 56 } 57 58 // Optimize for the incredibly common case of having at least two bytes left in the buffer, 59 // and those two bytes being enough to get the tag. This will be true for fields up to 4095. 60 if (state.bufferPos + 2 <= state.bufferSize) 61 { 62 int tmp = buffer[state.bufferPos++]; 63 if (tmp < 128) 64 { 65 state.lastTag = (uint)tmp; 66 } 67 else 68 { 69 int result = tmp & 0x7f; 70 if ((tmp = buffer[state.bufferPos++]) < 128) 71 { 72 result |= tmp << 7; 73 state.lastTag = (uint) result; 74 } 75 else 76 { 77 // Nope, rewind and go the potentially slow route. 78 state.bufferPos -= 2; 79 state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 80 } 81 } 82 } 83 else 84 { 85 if (SegmentedBufferHelper.IsAtEnd(ref buffer, ref state)) 86 { 87 state.lastTag = 0; 88 return 0; 89 } 90 91 state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 92 } 93 if (WireFormat.GetTagFieldNumber(state.lastTag) == 0) 94 { 95 // If we actually read a tag with a field of 0, that's not a valid tag. 96 throw InvalidProtocolBufferException.InvalidTag(); 97 } 98 return state.lastTag; 99 } 100 101 /// <summary> 102 /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>, 103 /// the tag is consumed and the method returns <c>true</c>; otherwise, the 104 /// stream is left in the original position and the method returns <c>false</c>. 105 /// </summary> MaybeConsumeTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, uint tag)106 public static bool MaybeConsumeTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, uint tag) 107 { 108 if (PeekTag(ref buffer, ref state) == tag) 109 { 110 state.hasNextTag = false; 111 return true; 112 } 113 return false; 114 } 115 116 /// <summary> 117 /// Peeks at the next field tag. This is like calling <see cref="ParseTag"/>, but the 118 /// tag is not consumed. (So a subsequent call to <see cref="ParseTag"/> will return the 119 /// same value.) 120 /// </summary> PeekTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)121 public static uint PeekTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 122 { 123 if (state.hasNextTag) 124 { 125 return state.nextTag; 126 } 127 128 uint savedLast = state.lastTag; 129 state.nextTag = ParseTag(ref buffer, ref state); 130 state.hasNextTag = true; 131 state.lastTag = savedLast; // Undo the side effect of ReadTag 132 return state.nextTag; 133 } 134 135 /// <summary> 136 /// Parses a raw varint. 137 /// </summary> ParseRawVarint64(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)138 public static ulong ParseRawVarint64(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 139 { 140 if (state.bufferPos + 10 > state.bufferSize) 141 { 142 return ParseRawVarint64SlowPath(ref buffer, ref state); 143 } 144 145 ulong result = buffer[state.bufferPos++]; 146 if (result < 128) 147 { 148 return result; 149 } 150 result &= 0x7f; 151 int shift = 7; 152 do 153 { 154 byte b = buffer[state.bufferPos++]; 155 result |= (ulong)(b & 0x7F) << shift; 156 if (b < 0x80) 157 { 158 return result; 159 } 160 shift += 7; 161 } 162 while (shift < 64); 163 164 throw InvalidProtocolBufferException.MalformedVarint(); 165 } 166 ParseRawVarint64SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)167 private static ulong ParseRawVarint64SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 168 { 169 int shift = 0; 170 ulong result = 0; 171 do 172 { 173 byte b = ReadRawByte(ref buffer, ref state); 174 result |= (ulong)(b & 0x7F) << shift; 175 if (b < 0x80) 176 { 177 return result; 178 } 179 shift += 7; 180 } 181 while (shift < 64); 182 183 throw InvalidProtocolBufferException.MalformedVarint(); 184 } 185 186 /// <summary> 187 /// Parses a raw Varint. If larger than 32 bits, discard the upper bits. 188 /// This method is optimised for the case where we've got lots of data in the buffer. 189 /// That means we can check the size just once, then just read directly from the buffer 190 /// without constant rechecking of the buffer length. 191 /// </summary> ParseRawVarint32(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)192 public static uint ParseRawVarint32(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 193 { 194 if (state.bufferPos + 5 > state.bufferSize) 195 { 196 return ParseRawVarint32SlowPath(ref buffer, ref state); 197 } 198 199 int tmp = buffer[state.bufferPos++]; 200 if (tmp < 128) 201 { 202 return (uint)tmp; 203 } 204 int result = tmp & 0x7f; 205 if ((tmp = buffer[state.bufferPos++]) < 128) 206 { 207 result |= tmp << 7; 208 } 209 else 210 { 211 result |= (tmp & 0x7f) << 7; 212 if ((tmp = buffer[state.bufferPos++]) < 128) 213 { 214 result |= tmp << 14; 215 } 216 else 217 { 218 result |= (tmp & 0x7f) << 14; 219 if ((tmp = buffer[state.bufferPos++]) < 128) 220 { 221 result |= tmp << 21; 222 } 223 else 224 { 225 result |= (tmp & 0x7f) << 21; 226 result |= (tmp = buffer[state.bufferPos++]) << 28; 227 if (tmp >= 128) 228 { 229 // Discard upper 32 bits. 230 // Note that this has to use ReadRawByte() as we only ensure we've 231 // got at least 5 bytes at the start of the method. This lets us 232 // use the fast path in more cases, and we rarely hit this section of code. 233 for (int i = 0; i < 5; i++) 234 { 235 if (ReadRawByte(ref buffer, ref state) < 128) 236 { 237 return (uint) result; 238 } 239 } 240 throw InvalidProtocolBufferException.MalformedVarint(); 241 } 242 } 243 } 244 } 245 return (uint)result; 246 } 247 ParseRawVarint32SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)248 private static uint ParseRawVarint32SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 249 { 250 int tmp = ReadRawByte(ref buffer, ref state); 251 if (tmp < 128) 252 { 253 return (uint) tmp; 254 } 255 int result = tmp & 0x7f; 256 if ((tmp = ReadRawByte(ref buffer, ref state)) < 128) 257 { 258 result |= tmp << 7; 259 } 260 else 261 { 262 result |= (tmp & 0x7f) << 7; 263 if ((tmp = ReadRawByte(ref buffer, ref state)) < 128) 264 { 265 result |= tmp << 14; 266 } 267 else 268 { 269 result |= (tmp & 0x7f) << 14; 270 if ((tmp = ReadRawByte(ref buffer, ref state)) < 128) 271 { 272 result |= tmp << 21; 273 } 274 else 275 { 276 result |= (tmp & 0x7f) << 21; 277 result |= (tmp = ReadRawByte(ref buffer, ref state)) << 28; 278 if (tmp >= 128) 279 { 280 // Discard upper 32 bits. 281 for (int i = 0; i < 5; i++) 282 { 283 if (ReadRawByte(ref buffer, ref state) < 128) 284 { 285 return (uint) result; 286 } 287 } 288 throw InvalidProtocolBufferException.MalformedVarint(); 289 } 290 } 291 } 292 } 293 return (uint) result; 294 } 295 296 /// <summary> 297 /// Parses a 32-bit little-endian integer. 298 /// </summary> ParseRawLittleEndian32(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)299 public static uint ParseRawLittleEndian32(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 300 { 301 const int uintLength = sizeof(uint); 302 const int ulongLength = sizeof(ulong); 303 if (state.bufferPos + ulongLength > state.bufferSize) 304 { 305 return ParseRawLittleEndian32SlowPath(ref buffer, ref state); 306 } 307 // ReadUInt32LittleEndian is many times slower than ReadUInt64LittleEndian (at least on some runtimes) 308 // so it's faster better to use ReadUInt64LittleEndian and truncate the result. 309 uint result = (uint) BinaryPrimitives.ReadUInt64LittleEndian(buffer.Slice(state.bufferPos, ulongLength)); 310 state.bufferPos += uintLength; 311 return result; 312 } 313 ParseRawLittleEndian32SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)314 private static uint ParseRawLittleEndian32SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 315 { 316 uint b1 = ReadRawByte(ref buffer, ref state); 317 uint b2 = ReadRawByte(ref buffer, ref state); 318 uint b3 = ReadRawByte(ref buffer, ref state); 319 uint b4 = ReadRawByte(ref buffer, ref state); 320 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); 321 } 322 323 /// <summary> 324 /// Parses a 64-bit little-endian integer. 325 /// </summary> ParseRawLittleEndian64(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)326 public static ulong ParseRawLittleEndian64(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 327 { 328 const int length = sizeof(ulong); 329 if (state.bufferPos + length > state.bufferSize) 330 { 331 return ParseRawLittleEndian64SlowPath(ref buffer, ref state); 332 } 333 ulong result = BinaryPrimitives.ReadUInt64LittleEndian(buffer.Slice(state.bufferPos, length)); 334 state.bufferPos += length; 335 return result; 336 } 337 ParseRawLittleEndian64SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)338 private static ulong ParseRawLittleEndian64SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 339 { 340 ulong b1 = ReadRawByte(ref buffer, ref state); 341 ulong b2 = ReadRawByte(ref buffer, ref state); 342 ulong b3 = ReadRawByte(ref buffer, ref state); 343 ulong b4 = ReadRawByte(ref buffer, ref state); 344 ulong b5 = ReadRawByte(ref buffer, ref state); 345 ulong b6 = ReadRawByte(ref buffer, ref state); 346 ulong b7 = ReadRawByte(ref buffer, ref state); 347 ulong b8 = ReadRawByte(ref buffer, ref state); 348 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) 349 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); 350 } 351 352 /// <summary> 353 /// Parses a double value. 354 /// </summary> ParseDouble(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)355 public static double ParseDouble(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 356 { 357 const int length = sizeof(double); 358 if (!BitConverter.IsLittleEndian || state.bufferPos + length > state.bufferSize) 359 { 360 return BitConverter.Int64BitsToDouble((long)ParseRawLittleEndian64(ref buffer, ref state)); 361 } 362 // ReadUnaligned uses processor architecture for endianness. 363 double result = Unsafe.ReadUnaligned<double>(ref MemoryMarshal.GetReference(buffer.Slice(state.bufferPos, length))); 364 state.bufferPos += length; 365 return result; 366 } 367 368 /// <summary> 369 /// Parses a float value. 370 /// </summary> ParseFloat(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)371 public static float ParseFloat(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 372 { 373 const int length = sizeof(float); 374 if (!BitConverter.IsLittleEndian || state.bufferPos + length > state.bufferSize) 375 { 376 return ParseFloatSlow(ref buffer, ref state); 377 } 378 // ReadUnaligned uses processor architecture for endianness. 379 float result = Unsafe.ReadUnaligned<float>(ref MemoryMarshal.GetReference(buffer.Slice(state.bufferPos, length))); 380 state.bufferPos += length; 381 return result; 382 } 383 ParseFloatSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)384 private static unsafe float ParseFloatSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 385 { 386 const int length = sizeof(float); 387 byte* stackBuffer = stackalloc byte[length]; 388 Span<byte> tempSpan = new Span<byte>(stackBuffer, length); 389 for (int i = 0; i < length; i++) 390 { 391 tempSpan[i] = ReadRawByte(ref buffer, ref state); 392 } 393 394 // Content is little endian. Reverse if needed to match endianness of architecture. 395 if (!BitConverter.IsLittleEndian) 396 { 397 tempSpan.Reverse(); 398 } 399 return Unsafe.ReadUnaligned<float>(ref MemoryMarshal.GetReference(tempSpan)); 400 } 401 402 /// <summary> 403 /// Reads a fixed size of bytes from the input. 404 /// </summary> 405 /// <exception cref="InvalidProtocolBufferException"> 406 /// the end of the stream or the current limit was reached 407 /// </exception> ReadRawBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size)408 public static byte[] ReadRawBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size) 409 { 410 if (size < 0) 411 { 412 throw InvalidProtocolBufferException.NegativeSize(); 413 } 414 415 if (size <= state.bufferSize - state.bufferPos) 416 { 417 // We have all the bytes we need already. 418 byte[] bytes = new byte[size]; 419 buffer.Slice(state.bufferPos, size).CopyTo(bytes); 420 state.bufferPos += size; 421 return bytes; 422 } 423 424 return ReadRawBytesSlow(ref buffer, ref state, size); 425 } 426 ReadRawBytesSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size)427 private static byte[] ReadRawBytesSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size) 428 { 429 ValidateCurrentLimit(ref buffer, ref state, size); 430 431 if ((!state.segmentedBufferHelper.TotalLength.HasValue && size < buffer.Length) || 432 IsDataAvailableInSource(ref state, size)) 433 { 434 // Reading more bytes than are in the buffer, but not an excessive number 435 // of bytes. We can safely allocate the resulting array ahead of time. 436 437 byte[] bytes = new byte[size]; 438 ReadRawBytesIntoSpan(ref buffer, ref state, size, bytes); 439 return bytes; 440 } 441 else 442 { 443 // The size is very large. For security reasons, we can't allocate the 444 // entire byte array yet. The size comes directly from the input, so a 445 // maliciously-crafted message could provide a bogus very large size in 446 // order to trick the app into allocating a lot of memory. We avoid this 447 // by allocating and reading only a small chunk at a time, so that the 448 // malicious message must actually *be* extremely large to cause 449 // problems. Meanwhile, we limit the allowed size of a message elsewhere. 450 451 List<byte[]> chunks = new List<byte[]>(); 452 453 int pos = state.bufferSize - state.bufferPos; 454 byte[] firstChunk = new byte[pos]; 455 buffer.Slice(state.bufferPos, pos).CopyTo(firstChunk); 456 chunks.Add(firstChunk); 457 state.bufferPos = state.bufferSize; 458 459 // Read all the rest of the bytes we need. 460 int sizeLeft = size - pos; 461 while (sizeLeft > 0) 462 { 463 state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true); 464 byte[] chunk = new byte[Math.Min(sizeLeft, state.bufferSize)]; 465 466 buffer.Slice(0, chunk.Length) 467 .CopyTo(chunk); 468 state.bufferPos += chunk.Length; 469 sizeLeft -= chunk.Length; 470 chunks.Add(chunk); 471 } 472 473 // OK, got everything. Now concatenate it all into one buffer. 474 byte[] bytes = new byte[size]; 475 int newPos = 0; 476 foreach (byte[] chunk in chunks) 477 { 478 Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length); 479 newPos += chunk.Length; 480 } 481 482 // Done. 483 return bytes; 484 } 485 } 486 487 /// <summary> 488 /// Reads and discards <paramref name="size"/> bytes. 489 /// </summary> 490 /// <exception cref="InvalidProtocolBufferException">the end of the stream 491 /// or the current limit was reached</exception> SkipRawBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size)492 public static void SkipRawBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size) 493 { 494 if (size < 0) 495 { 496 throw InvalidProtocolBufferException.NegativeSize(); 497 } 498 499 ValidateCurrentLimit(ref buffer, ref state, size); 500 501 if (size <= state.bufferSize - state.bufferPos) 502 { 503 // We have all the bytes we need already. 504 state.bufferPos += size; 505 } 506 else 507 { 508 // Skipping more bytes than are in the buffer. First skip what we have. 509 int pos = state.bufferSize - state.bufferPos; 510 state.bufferPos = state.bufferSize; 511 512 // TODO: If our segmented buffer is backed by a Stream that is seekable, we could skip the bytes more efficiently 513 // by simply updating stream's Position property. This used to be supported in the past, but the support was dropped 514 // because it would make the segmentedBufferHelper more complex. Support can be reintroduced if needed. 515 state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true); 516 517 while (size - pos > state.bufferSize) 518 { 519 pos += state.bufferSize; 520 state.bufferPos = state.bufferSize; 521 state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true); 522 } 523 524 state.bufferPos = size - pos; 525 } 526 } 527 528 /// <summary> 529 /// Reads a string field value from the input. 530 /// </summary> 531 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadString(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)532 public static string ReadString(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 533 { 534 int length = ParsingPrimitives.ParseLength(ref buffer, ref state); 535 return ParsingPrimitives.ReadRawString(ref buffer, ref state, length); 536 } 537 538 /// <summary> 539 /// Reads a bytes field value from the input. 540 /// </summary> 541 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)542 public static ByteString ReadBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 543 { 544 int length = ParsingPrimitives.ParseLength(ref buffer, ref state); 545 return ByteString.AttachBytes(ParsingPrimitives.ReadRawBytes(ref buffer, ref state, length)); 546 } 547 548 /// <summary> 549 /// Reads a UTF-8 string from the next "length" bytes. 550 /// </summary> 551 /// <exception cref="InvalidProtocolBufferException"> 552 /// the end of the stream or the current limit was reached 553 /// </exception> 554 [SecuritySafeCritical] ReadRawString(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int length)555 public static string ReadRawString(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int length) 556 { 557 // No need to read any data for an empty string. 558 if (length == 0) 559 { 560 return string.Empty; 561 } 562 563 if (length < 0) 564 { 565 throw InvalidProtocolBufferException.NegativeSize(); 566 } 567 568 #if GOOGLE_PROTOBUF_SUPPORT_FAST_STRING 569 if (length <= state.bufferSize - state.bufferPos) 570 { 571 // Fast path: all bytes to decode appear in the same span. 572 ReadOnlySpan<byte> data = buffer.Slice(state.bufferPos, length); 573 574 string value; 575 unsafe 576 { 577 fixed (byte* sourceBytes = &MemoryMarshal.GetReference(data)) 578 { 579 value = WritingPrimitives.Utf8Encoding.GetString(sourceBytes, length); 580 } 581 } 582 583 state.bufferPos += length; 584 return value; 585 } 586 #endif 587 588 return ReadStringSlow(ref buffer, ref state, length); 589 } 590 591 /// <summary> 592 /// Reads a string assuming that it is spread across multiple spans in a <see cref="ReadOnlySequence{T}"/>. 593 /// </summary> ReadStringSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int length)594 private static string ReadStringSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int length) 595 { 596 ValidateCurrentLimit(ref buffer, ref state, length); 597 598 #if GOOGLE_PROTOBUF_SUPPORT_FAST_STRING 599 if (IsDataAvailable(ref state, length)) 600 { 601 // Read string data into a temporary buffer, either stackalloc'ed or from ArrayPool 602 // Once all data is read then call Encoding.GetString on buffer and return to pool if needed. 603 604 byte[] byteArray = null; 605 Span<byte> byteSpan = length <= StackallocThreshold ? 606 stackalloc byte[length] : 607 (byteArray = ArrayPool<byte>.Shared.Rent(length)); 608 609 try 610 { 611 unsafe 612 { 613 fixed (byte* pByteSpan = &MemoryMarshal.GetReference(byteSpan)) 614 { 615 // Compiler doesn't like that a potentially stackalloc'd Span<byte> is being used 616 // in a method with a "ref Span<byte> buffer" argument. If the stackalloc'd span was assigned 617 // to the ref argument then bad things would happen. We'll never do that so it is ok. 618 // Make compiler happy by passing a new span created from pointer. 619 var tempSpan = new Span<byte>(pByteSpan, byteSpan.Length); 620 ReadRawBytesIntoSpan(ref buffer, ref state, length, tempSpan); 621 622 return WritingPrimitives.Utf8Encoding.GetString(pByteSpan, length); 623 } 624 } 625 } 626 finally 627 { 628 if (byteArray != null) 629 { 630 ArrayPool<byte>.Shared.Return(byteArray); 631 } 632 } 633 } 634 #endif 635 636 // Slow path: Build a byte array first then copy it. 637 // This will be called when reading from a Stream because we don't know the length of the stream, 638 // or there is not enough data in the sequence. If there is not enough data then ReadRawBytes will 639 // throw an exception. 640 return WritingPrimitives.Utf8Encoding.GetString(ReadRawBytes(ref buffer, ref state, length), 0, length); 641 } 642 643 /// <summary> 644 /// Validates that the specified size doesn't exceed the current limit. If it does then remaining bytes 645 /// are skipped and an error is thrown. 646 /// </summary> ValidateCurrentLimit(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size)647 private static void ValidateCurrentLimit(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size) 648 { 649 if (state.totalBytesRetired + state.bufferPos + size > state.currentLimit) 650 { 651 // Read to the end of the stream (up to the current limit) anyway. 652 SkipRawBytes(ref buffer, ref state, state.currentLimit - state.totalBytesRetired - state.bufferPos); 653 // Then fail. 654 throw InvalidProtocolBufferException.TruncatedMessage(); 655 } 656 } 657 658 [SecuritySafeCritical] ReadRawByte(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)659 private static byte ReadRawByte(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 660 { 661 if (state.bufferPos == state.bufferSize) 662 { 663 state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true); 664 } 665 return buffer[state.bufferPos++]; 666 } 667 668 /// <summary> 669 /// Reads a varint from the input one byte at a time, so that it does not 670 /// read any bytes after the end of the varint. If you simply wrapped the 671 /// stream in a CodedInputStream and used ReadRawVarint32(Stream) 672 /// then you would probably end up reading past the end of the varint since 673 /// CodedInputStream buffers its input. 674 /// </summary> 675 /// <param name="input"></param> 676 /// <returns></returns> ReadRawVarint32(Stream input)677 public static uint ReadRawVarint32(Stream input) 678 { 679 int result = 0; 680 int offset = 0; 681 for (; offset < 32; offset += 7) 682 { 683 int b = input.ReadByte(); 684 if (b == -1) 685 { 686 throw InvalidProtocolBufferException.TruncatedMessage(); 687 } 688 result |= (b & 0x7f) << offset; 689 if ((b & 0x80) == 0) 690 { 691 return (uint) result; 692 } 693 } 694 // Keep reading up to 64 bits. 695 for (; offset < 64; offset += 7) 696 { 697 int b = input.ReadByte(); 698 if (b == -1) 699 { 700 throw InvalidProtocolBufferException.TruncatedMessage(); 701 } 702 if ((b & 0x80) == 0) 703 { 704 return (uint) result; 705 } 706 } 707 throw InvalidProtocolBufferException.MalformedVarint(); 708 } 709 710 /// <summary> 711 /// Decode a 32-bit value with ZigZag encoding. 712 /// </summary> 713 /// <remarks> 714 /// ZigZag encodes signed integers into values that can be efficiently 715 /// encoded with varint. (Otherwise, negative values must be 716 /// sign-extended to 32 bits to be varint encoded, thus always taking 717 /// 5 bytes on the wire.) 718 /// </remarks> DecodeZigZag32(uint n)719 public static int DecodeZigZag32(uint n) 720 { 721 return (int)(n >> 1) ^ -(int)(n & 1); 722 } 723 724 /// <summary> 725 /// Decode a 64-bit value with ZigZag encoding. 726 /// </summary> 727 /// <remarks> 728 /// ZigZag encodes signed integers into values that can be efficiently 729 /// encoded with varint. (Otherwise, negative values must be 730 /// sign-extended to 64 bits to be varint encoded, thus always taking 731 /// 10 bytes on the wire.) 732 /// </remarks> DecodeZigZag64(ulong n)733 public static long DecodeZigZag64(ulong n) 734 { 735 return (long)(n >> 1) ^ -(long)(n & 1); 736 } 737 738 /// <summary> 739 /// Checks whether there is known data available of the specified size remaining to parse. 740 /// When parsing from a Stream this can return false because we have no knowledge of the amount 741 /// of data remaining in the stream until it is read. 742 /// </summary> IsDataAvailable(ref ParserInternalState state, int size)743 public static bool IsDataAvailable(ref ParserInternalState state, int size) 744 { 745 // Data fits in remaining buffer 746 if (size <= state.bufferSize - state.bufferPos) 747 { 748 return true; 749 } 750 751 return IsDataAvailableInSource(ref state, size); 752 } 753 754 /// <summary> 755 /// Checks whether there is known data available of the specified size remaining to parse 756 /// in the underlying data source. 757 /// When parsing from a Stream this will return false because we have no knowledge of the amount 758 /// of data remaining in the stream until it is read. 759 /// </summary> IsDataAvailableInSource(ref ParserInternalState state, int size)760 private static bool IsDataAvailableInSource(ref ParserInternalState state, int size) 761 { 762 // Data fits in remaining source data. 763 // Note that this will never be true when reading from a stream as the total length is unknown. 764 return size <= state.segmentedBufferHelper.TotalLength - state.totalBytesRetired - state.bufferPos; 765 } 766 767 /// <summary> 768 /// Read raw bytes of the specified length into a span. The amount of data available and the current limit should 769 /// be checked before calling this method. 770 /// </summary> ReadRawBytesIntoSpan(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int length, Span<byte> byteSpan)771 private static void ReadRawBytesIntoSpan(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int length, Span<byte> byteSpan) 772 { 773 int remainingByteLength = length; 774 while (remainingByteLength > 0) 775 { 776 if (state.bufferSize - state.bufferPos == 0) 777 { 778 state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true); 779 } 780 781 ReadOnlySpan<byte> unreadSpan = buffer.Slice(state.bufferPos, Math.Min(remainingByteLength, state.bufferSize - state.bufferPos)); 782 unreadSpan.CopyTo(byteSpan.Slice(length - remainingByteLength)); 783 784 remainingByteLength -= unreadSpan.Length; 785 state.bufferPos += unreadSpan.Length; 786 } 787 } 788 } 789 } 790