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