• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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