• 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 // 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.Binary;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.InteropServices;
37 using System.Security;
38 using System.Text;
39 
40 namespace Google.Protobuf
41 {
42     /// <summary>
43     /// Primitives for encoding protobuf wire format.
44     /// </summary>
45     [SecuritySafeCritical]
46     internal static class WritingPrimitives
47     {
48         // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.)
49         internal static readonly Encoding Utf8Encoding = Encoding.UTF8;
50 
51         #region Writing of values (not including tags)
52 
53         /// <summary>
54         /// Writes a double field value, without a tag, to the stream.
55         /// </summary>
WriteDouble(ref Span<byte> buffer, ref WriterInternalState state, double value)56         public static void WriteDouble(ref Span<byte> buffer, ref WriterInternalState state, double value)
57         {
58             WriteRawLittleEndian64(ref buffer, ref state, (ulong)BitConverter.DoubleToInt64Bits(value));
59         }
60 
61         /// <summary>
62         /// Writes a float field value, without a tag, to the stream.
63         /// </summary>
WriteFloat(ref Span<byte> buffer, ref WriterInternalState state, float value)64         public static unsafe void WriteFloat(ref Span<byte> buffer, ref WriterInternalState state, float value)
65         {
66             const int length = sizeof(float);
67             if (buffer.Length - state.position >= length)
68             {
69                 // if there's enough space in the buffer, write the float directly into the buffer
70                 var floatSpan = buffer.Slice(state.position, length);
71                 Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(floatSpan), value);
72 
73                 if (!BitConverter.IsLittleEndian)
74                 {
75                     floatSpan.Reverse();
76                 }
77                 state.position += length;
78             }
79             else
80             {
81                 WriteFloatSlowPath(ref buffer, ref state, value);
82             }
83         }
84 
85         [MethodImpl(MethodImplOptions.NoInlining)]
WriteFloatSlowPath(ref Span<byte> buffer, ref WriterInternalState state, float value)86         private static unsafe void WriteFloatSlowPath(ref Span<byte> buffer, ref WriterInternalState state, float value)
87         {
88             const int length = sizeof(float);
89 
90             // TODO(jtattermusch): deduplicate the code. Populating the span is the same as for the fastpath.
91             Span<byte> floatSpan = stackalloc byte[length];
92             Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(floatSpan), value);
93             if (!BitConverter.IsLittleEndian)
94             {
95                 floatSpan.Reverse();
96             }
97 
98             WriteRawByte(ref buffer, ref state, floatSpan[0]);
99             WriteRawByte(ref buffer, ref state, floatSpan[1]);
100             WriteRawByte(ref buffer, ref state, floatSpan[2]);
101             WriteRawByte(ref buffer, ref state, floatSpan[3]);
102         }
103 
104         /// <summary>
105         /// Writes a uint64 field value, without a tag, to the stream.
106         /// </summary>
WriteUInt64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)107         public static void WriteUInt64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
108         {
109             WriteRawVarint64(ref buffer, ref state, value);
110         }
111 
112         /// <summary>
113         /// Writes an int64 field value, without a tag, to the stream.
114         /// </summary>
WriteInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)115         public static void WriteInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)
116         {
117             WriteRawVarint64(ref buffer, ref state, (ulong)value);
118         }
119 
120         /// <summary>
121         /// Writes an int32 field value, without a tag, to the stream.
122         /// </summary>
WriteInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)123         public static void WriteInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)
124         {
125             if (value >= 0)
126             {
127                 WriteRawVarint32(ref buffer, ref state, (uint)value);
128             }
129             else
130             {
131                 // Must sign-extend.
132                 WriteRawVarint64(ref buffer, ref state, (ulong)value);
133             }
134         }
135 
136         /// <summary>
137         /// Writes a fixed64 field value, without a tag, to the stream.
138         /// </summary>
WriteFixed64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)139         public static void WriteFixed64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
140         {
141             WriteRawLittleEndian64(ref buffer, ref state, value);
142         }
143 
144         /// <summary>
145         /// Writes a fixed32 field value, without a tag, to the stream.
146         /// </summary>
WriteFixed32(ref Span<byte> buffer, ref WriterInternalState state, uint value)147         public static void WriteFixed32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
148         {
149             WriteRawLittleEndian32(ref buffer, ref state, value);
150         }
151 
152         /// <summary>
153         /// Writes a bool field value, without a tag, to the stream.
154         /// </summary>
WriteBool(ref Span<byte> buffer, ref WriterInternalState state, bool value)155         public static void WriteBool(ref Span<byte> buffer, ref WriterInternalState state, bool value)
156         {
157             WriteRawByte(ref buffer, ref state, value ? (byte)1 : (byte)0);
158         }
159 
160         /// <summary>
161         /// Writes a string field value, without a tag, to the stream.
162         /// The data is length-prefixed.
163         /// </summary>
WriteString(ref Span<byte> buffer, ref WriterInternalState state, string value)164         public static void WriteString(ref Span<byte> buffer, ref WriterInternalState state, string value)
165         {
166             // Optimise the case where we have enough space to write
167             // the string directly to the buffer, which should be common.
168             int length = Utf8Encoding.GetByteCount(value);
169             WriteLength(ref buffer, ref state, length);
170             if (buffer.Length - state.position >= length)
171             {
172                 if (length == value.Length) // Must be all ASCII...
173                 {
174                     for (int i = 0; i < length; i++)
175                     {
176                         buffer[state.position + i] = (byte)value[i];
177                     }
178                     state.position += length;
179                 }
180                 else
181                 {
182 #if NETSTANDARD1_1
183                     // slowpath when Encoding.GetBytes(Char*, Int32, Byte*, Int32) is not available
184                     byte[] bytes = Utf8Encoding.GetBytes(value);
185                     WriteRawBytes(ref buffer, ref state, bytes);
186 #else
187                     ReadOnlySpan<char> source = value.AsSpan();
188                     int bytesUsed;
189                     unsafe
190                     {
191                         fixed (char* sourceChars = &MemoryMarshal.GetReference(source))
192                         fixed (byte* destinationBytes = &MemoryMarshal.GetReference(buffer.Slice(state.position)))
193                         {
194                             bytesUsed = Utf8Encoding.GetBytes(sourceChars, source.Length, destinationBytes, buffer.Length);
195                         }
196                     }
197                     state.position += bytesUsed;
198 #endif
199                 }
200             }
201             else
202             {
203                 // Opportunity for future optimization:
204                 // Large strings that don't fit into the current buffer segment
205                 // can probably be optimized by using Utf8Encoding.GetEncoder()
206                 // but more benchmarks would need to be added as evidence.
207                 byte[] bytes = Utf8Encoding.GetBytes(value);
208                 WriteRawBytes(ref buffer, ref state, bytes);
209             }
210         }
211 
212         /// <summary>
213         /// Write a byte string, without a tag, to the stream.
214         /// The data is length-prefixed.
215         /// </summary>
WriteBytes(ref Span<byte> buffer, ref WriterInternalState state, ByteString value)216         public static void WriteBytes(ref Span<byte> buffer, ref WriterInternalState state, ByteString value)
217         {
218             WriteLength(ref buffer, ref state, value.Length);
219             WriteRawBytes(ref buffer, ref state, value.Span);
220         }
221 
222         /// <summary>
223         /// Writes a uint32 value, without a tag, to the stream.
224         /// </summary>
WriteUInt32(ref Span<byte> buffer, ref WriterInternalState state, uint value)225         public static void WriteUInt32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
226         {
227             WriteRawVarint32(ref buffer, ref state, value);
228         }
229 
230         /// <summary>
231         /// Writes an enum value, without a tag, to the stream.
232         /// </summary>
WriteEnum(ref Span<byte> buffer, ref WriterInternalState state, int value)233         public static void WriteEnum(ref Span<byte> buffer, ref WriterInternalState state, int value)
234         {
235             WriteInt32(ref buffer, ref state, value);
236         }
237 
238         /// <summary>
239         /// Writes an sfixed32 value, without a tag, to the stream.
240         /// </summary>
WriteSFixed32(ref Span<byte> buffer, ref WriterInternalState state, int value)241         public static void WriteSFixed32(ref Span<byte> buffer, ref WriterInternalState state, int value)
242         {
243             WriteRawLittleEndian32(ref buffer, ref state, (uint)value);
244         }
245 
246         /// <summary>
247         /// Writes an sfixed64 value, without a tag, to the stream.
248         /// </summary>
WriteSFixed64(ref Span<byte> buffer, ref WriterInternalState state, long value)249         public static void WriteSFixed64(ref Span<byte> buffer, ref WriterInternalState state, long value)
250         {
251             WriteRawLittleEndian64(ref buffer, ref state, (ulong)value);
252         }
253 
254         /// <summary>
255         /// Writes an sint32 value, without a tag, to the stream.
256         /// </summary>
WriteSInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)257         public static void WriteSInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)
258         {
259             WriteRawVarint32(ref buffer, ref state, EncodeZigZag32(value));
260         }
261 
262         /// <summary>
263         /// Writes an sint64 value, without a tag, to the stream.
264         /// </summary>
WriteSInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)265         public static void WriteSInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)
266         {
267             WriteRawVarint64(ref buffer, ref state, EncodeZigZag64(value));
268         }
269 
270         /// <summary>
271         /// Writes a length (in bytes) for length-delimited data.
272         /// </summary>
273         /// <remarks>
274         /// This method simply writes a rawint, but exists for clarity in calling code.
275         /// </remarks>
WriteLength(ref Span<byte> buffer, ref WriterInternalState state, int length)276         public static void WriteLength(ref Span<byte> buffer, ref WriterInternalState state, int length)
277         {
278             WriteRawVarint32(ref buffer, ref state, (uint)length);
279         }
280 
281         #endregion
282 
283         #region Writing primitives
284         /// <summary>
285         /// Writes a 32 bit value as a varint. The fast route is taken when
286         /// there's enough buffer space left to whizz through without checking
287         /// for each byte; otherwise, we resort to calling WriteRawByte each time.
288         /// </summary>
WriteRawVarint32(ref Span<byte> buffer, ref WriterInternalState state, uint value)289         public static void WriteRawVarint32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
290         {
291             // Optimize for the common case of a single byte value
292             if (value < 128 && state.position < buffer.Length)
293             {
294                 buffer[state.position++] = (byte)value;
295                 return;
296             }
297 
298             // Fast path when capacity is available
299             while (state.position < buffer.Length)
300             {
301                 if (value > 127)
302                 {
303                     buffer[state.position++] = (byte)((value & 0x7F) | 0x80);
304                     value >>= 7;
305                 }
306                 else
307                 {
308                     buffer[state.position++] = (byte)value;
309                     return;
310                 }
311             }
312 
313             while (value > 127)
314             {
315                 WriteRawByte(ref buffer, ref state, (byte)((value & 0x7F) | 0x80));
316                 value >>= 7;
317             }
318 
319             WriteRawByte(ref buffer, ref state, (byte)value);
320         }
321 
WriteRawVarint64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)322         public static void WriteRawVarint64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
323         {
324             // Optimize for the common case of a single byte value
325             if (value < 128 && state.position < buffer.Length)
326             {
327                 buffer[state.position++] = (byte)value;
328                 return;
329             }
330 
331             // Fast path when capacity is available
332             while (state.position < buffer.Length)
333             {
334                 if (value > 127)
335                 {
336                     buffer[state.position++] = (byte)((value & 0x7F) | 0x80);
337                     value >>= 7;
338                 }
339                 else
340                 {
341                     buffer[state.position++] = (byte)value;
342                     return;
343                 }
344             }
345 
346             while (value > 127)
347             {
348                 WriteRawByte(ref buffer, ref state, (byte)((value & 0x7F) | 0x80));
349                 value >>= 7;
350             }
351 
352             WriteRawByte(ref buffer, ref state, (byte)value);
353         }
354 
WriteRawLittleEndian32(ref Span<byte> buffer, ref WriterInternalState state, uint value)355         public static void WriteRawLittleEndian32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
356         {
357             const int length = sizeof(uint);
358             if (state.position + length > buffer.Length)
359             {
360                 WriteRawLittleEndian32SlowPath(ref buffer, ref state, value);
361             }
362             else
363             {
364                 BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(state.position), value);
365                 state.position += length;
366             }
367         }
368 
369         [MethodImpl(MethodImplOptions.NoInlining)]
WriteRawLittleEndian32SlowPath(ref Span<byte> buffer, ref WriterInternalState state, uint value)370         private static void WriteRawLittleEndian32SlowPath(ref Span<byte> buffer, ref WriterInternalState state, uint value)
371         {
372             WriteRawByte(ref buffer, ref state, (byte)value);
373             WriteRawByte(ref buffer, ref state, (byte)(value >> 8));
374             WriteRawByte(ref buffer, ref state, (byte)(value >> 16));
375             WriteRawByte(ref buffer, ref state, (byte)(value >> 24));
376         }
377 
WriteRawLittleEndian64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)378         public static void WriteRawLittleEndian64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
379         {
380             const int length = sizeof(ulong);
381             if (state.position + length > buffer.Length)
382             {
383                 WriteRawLittleEndian64SlowPath(ref buffer, ref state, value);
384             }
385             else
386             {
387                 BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(state.position), value);
388                 state.position += length;
389             }
390         }
391 
392         [MethodImpl(MethodImplOptions.NoInlining)]
WriteRawLittleEndian64SlowPath(ref Span<byte> buffer, ref WriterInternalState state, ulong value)393         public static void WriteRawLittleEndian64SlowPath(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
394         {
395             WriteRawByte(ref buffer, ref state, (byte)value);
396             WriteRawByte(ref buffer, ref state, (byte)(value >> 8));
397             WriteRawByte(ref buffer, ref state, (byte)(value >> 16));
398             WriteRawByte(ref buffer, ref state, (byte)(value >> 24));
399             WriteRawByte(ref buffer, ref state, (byte)(value >> 32));
400             WriteRawByte(ref buffer, ref state, (byte)(value >> 40));
401             WriteRawByte(ref buffer, ref state, (byte)(value >> 48));
402             WriteRawByte(ref buffer, ref state, (byte)(value >> 56));
403         }
404 
WriteRawByte(ref Span<byte> buffer, ref WriterInternalState state, byte value)405         private static void WriteRawByte(ref Span<byte> buffer, ref WriterInternalState state, byte value)
406         {
407             if (state.position == buffer.Length)
408             {
409                 WriteBufferHelper.RefreshBuffer(ref buffer, ref state);
410             }
411 
412             buffer[state.position++] = value;
413         }
414 
415         /// <summary>
416         /// Writes out an array of bytes.
417         /// </summary>
WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value)418         public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value)
419         {
420             WriteRawBytes(ref buffer, ref state, new ReadOnlySpan<byte>(value));
421         }
422 
423         /// <summary>
424         /// Writes out part of an array of bytes.
425         /// </summary>
WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value, int offset, int length)426         public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value, int offset, int length)
427         {
428             WriteRawBytes(ref buffer, ref state, new ReadOnlySpan<byte>(value, offset, length));
429         }
430 
431         /// <summary>
432         /// Writes out part of an array of bytes.
433         /// </summary>
WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, ReadOnlySpan<byte> value)434         public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, ReadOnlySpan<byte> value)
435         {
436             if (buffer.Length - state.position >= value.Length)
437             {
438                 // We have room in the current buffer.
439                 value.CopyTo(buffer.Slice(state.position, value.Length));
440                 state.position += value.Length;
441             }
442             else
443             {
444                 // When writing to a CodedOutputStream backed by a Stream, we could avoid
445                 // copying the data twice (first copying to the current buffer and
446                 // and later writing from the current buffer to the underlying Stream)
447                 // in some circumstances by writing the data directly to the underlying Stream.
448                 // Current this is not being done to avoid specialcasing the code for
449                 // CodedOutputStream vs IBufferWriter<byte>.
450                 int bytesWritten = 0;
451                 while (buffer.Length - state.position < value.Length - bytesWritten)
452                 {
453                     int length = buffer.Length - state.position;
454                     value.Slice(bytesWritten, length).CopyTo(buffer.Slice(state.position, length));
455                     bytesWritten += length;
456                     state.position += length;
457                     WriteBufferHelper.RefreshBuffer(ref buffer, ref state);
458                 }
459 
460                 // copy the remaining data
461                 int remainderLength = value.Length - bytesWritten;
462                 value.Slice(bytesWritten, remainderLength).CopyTo(buffer.Slice(state.position, remainderLength));
463                 state.position += remainderLength;
464             }
465         }
466         #endregion
467 
468         #region Raw tag writing
469         /// <summary>
470         /// Encodes and writes a tag.
471         /// </summary>
WriteTag(ref Span<byte> buffer, ref WriterInternalState state, int fieldNumber, WireFormat.WireType type)472         public static void WriteTag(ref Span<byte> buffer, ref WriterInternalState state, int fieldNumber, WireFormat.WireType type)
473         {
474             WriteRawVarint32(ref buffer, ref state, WireFormat.MakeTag(fieldNumber, type));
475         }
476 
477         /// <summary>
478         /// Writes an already-encoded tag.
479         /// </summary>
WriteTag(ref Span<byte> buffer, ref WriterInternalState state, uint tag)480         public static void WriteTag(ref Span<byte> buffer, ref WriterInternalState state, uint tag)
481         {
482             WriteRawVarint32(ref buffer, ref state, tag);
483         }
484 
485         /// <summary>
486         /// Writes the given single-byte tag directly to the stream.
487         /// </summary>
WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1)488         public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1)
489         {
490             WriteRawByte(ref buffer, ref state, b1);
491         }
492 
493         /// <summary>
494         /// Writes the given two-byte tag directly to the stream.
495         /// </summary>
WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)496         public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)
497         {
498             if (state.position + 2 > buffer.Length)
499             {
500                 WriteRawTagSlowPath(ref buffer, ref state, b1, b2);
501             }
502             else
503             {
504                 buffer[state.position++] = b1;
505                 buffer[state.position++] = b2;
506             }
507         }
508 
509         [MethodImpl(MethodImplOptions.NoInlining)]
WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)510         private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)
511         {
512             WriteRawByte(ref buffer, ref state, b1);
513             WriteRawByte(ref buffer, ref state, b2);
514         }
515 
516         /// <summary>
517         /// Writes the given three-byte tag directly to the stream.
518         /// </summary>
WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)519         public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)
520         {
521             if (state.position + 3 > buffer.Length)
522             {
523                 WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3);
524             }
525             else
526             {
527                 buffer[state.position++] = b1;
528                 buffer[state.position++] = b2;
529                 buffer[state.position++] = b3;
530             }
531         }
532 
533         [MethodImpl(MethodImplOptions.NoInlining)]
WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)534         private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)
535         {
536             WriteRawByte(ref buffer, ref state, b1);
537             WriteRawByte(ref buffer, ref state, b2);
538             WriteRawByte(ref buffer, ref state, b3);
539         }
540 
541         /// <summary>
542         /// Writes the given four-byte tag directly to the stream.
543         /// </summary>
WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)544         public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)
545         {
546             if (state.position + 4 > buffer.Length)
547             {
548                 WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3, b4);
549             }
550             else
551             {
552                 buffer[state.position++] = b1;
553                 buffer[state.position++] = b2;
554                 buffer[state.position++] = b3;
555                 buffer[state.position++] = b4;
556             }
557         }
558 
559         [MethodImpl(MethodImplOptions.NoInlining)]
560 
WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)561         private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)
562         {
563             WriteRawByte(ref buffer, ref state, b1);
564             WriteRawByte(ref buffer, ref state, b2);
565             WriteRawByte(ref buffer, ref state, b3);
566             WriteRawByte(ref buffer, ref state, b4);
567         }
568 
569         /// <summary>
570         /// Writes the given five-byte tag directly to the stream.
571         /// </summary>
WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)572         public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)
573         {
574             if (state.position + 5 > buffer.Length)
575             {
576                 WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3, b4, b5);
577             }
578             else
579             {
580                 buffer[state.position++] = b1;
581                 buffer[state.position++] = b2;
582                 buffer[state.position++] = b3;
583                 buffer[state.position++] = b4;
584                 buffer[state.position++] = b5;
585             }
586         }
587 
588         [MethodImpl(MethodImplOptions.NoInlining)]
WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)589         private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)
590         {
591             WriteRawByte(ref buffer, ref state, b1);
592             WriteRawByte(ref buffer, ref state, b2);
593             WriteRawByte(ref buffer, ref state, b3);
594             WriteRawByte(ref buffer, ref state, b4);
595             WriteRawByte(ref buffer, ref state, b5);
596         }
597         #endregion
598 
599         /// <summary>
600         /// Encode a 32-bit value with ZigZag encoding.
601         /// </summary>
602         /// <remarks>
603         /// ZigZag encodes signed integers into values that can be efficiently
604         /// encoded with varint.  (Otherwise, negative values must be
605         /// sign-extended to 64 bits to be varint encoded, thus always taking
606         /// 10 bytes on the wire.)
607         /// </remarks>
EncodeZigZag32(int n)608         public static uint EncodeZigZag32(int n)
609         {
610             // Note:  the right-shift must be arithmetic
611             return (uint)((n << 1) ^ (n >> 31));
612         }
613 
614         /// <summary>
615         /// Encode a 64-bit value with ZigZag encoding.
616         /// </summary>
617         /// <remarks>
618         /// ZigZag encodes signed integers into values that can be efficiently
619         /// encoded with varint.  (Otherwise, negative values must be
620         /// sign-extended to 64 bits to be varint encoded, thus always taking
621         /// 10 bytes on the wire.)
622         /// </remarks>
EncodeZigZag64(long n)623         public static ulong EncodeZigZag64(long n)
624         {
625             return (ulong)((n << 1) ^ (n >> 63));
626         }
627     }
628 }